GLSL Compute Shader仅部分写入Vulkan中的Buffer

时间:2018-05-12 17:56:09

标签: graphics glsl vulkan compute-shader

我已经创建了这个GLSL Compute Shader并使用" glslangValidator.exe"编译它。但是,它只会更新"粒子[i] .Velocity"值而不是任何其他值,这仅在某些情况下发生。我已经检查过使用" RenderDoc"发送了正确的输入值。

缓冲区使用标记位

  

VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT

和财产标志位

  

VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT

GLSL Shader

#version 450
#extension GL_ARB_separate_shader_objects : enable

struct Particle
{
  vec3 Position;
  vec3 Velocity;
  vec3 IPosition;
  vec3 IVelocity;

  float LifeTime;
  float ILifetime;
};

layout(binding = 0) buffer Source
{
   Particle Particles[ ];
};

layout(binding = 1) uniform UBO
{
  mat4 model;
  mat4 view;
  mat4 proj;
  float time;
};

vec3 Gravity = vec3(0.0f,-0.98f,0.0f);
float dampeningFactor = 0.5;

void main(){
  uint i = gl_GlobalInvocationID.x;
  if(Particles[i].LifeTime > 0.0f){
    Particles[i].Velocity = Particles[i].Velocity + Gravity * dampeningFactor * time;
    Particles[i].Position = Particles[i].Position + Particles[i].Velocity * time;
    Particles[i].LifeTime = Particles[i].LifeTime - time;
  }else{
    Particles[i].Velocity = Particles[i].IVelocity;
    Particles[i].Position = Particles[i].IPosition;
    Particles[i].LifeTime = Particles[i].ILifetime;
  }
}

描述符集布局绑定

        VkDescriptorSetLayoutBinding descriptorSetLayoutBindings[2] = {
            { 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, 0 },
        { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, 0 }
        };

命令调度

vkCmdDispatch(computeCommandBuffers, MAX_PARTICLES , 1, 1);

提交队列

            VkSubmitInfo cSubmitInfo = {};
            cSubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;

            cSubmitInfo.commandBufferCount = 1;
            cSubmitInfo.pCommandBuffers = &computeCommandBuffers;

            if (vkQueueSubmit(computeQueue.getQueue(), 1, &cSubmitInfo, computeFence) != VK_SUCCESS) {
                throw std::runtime_error("failed to submit compute command buffer!");
            }

            vkWaitForFences(device.getDevice(), 1, &computeFence, VK_TRUE, UINT64_MAX);

更新时间:13/05/2017(更多信息已添加)

CPP中的粒子结构定义

struct Particle {
    glm::vec3 location;
    glm::vec3 velocity;
    glm::vec3 initLocation;
    glm::vec3 initVelocity;

    float lifeTime;
    float initLifetime;
}

数据映射到存储缓冲区

            void* data;
            vkMapMemory(device.getDevice(), stagingBufferMemory, 0, bufferSize, 0, &data);
            memcpy(data, particles, (size_t)bufferSize);
            vkUnmapMemory(device.getDevice(), stagingBufferMemory);

            copyBuffer(stagingBuffer, computeBuffer, bufferSize);

复制缓冲区功能(来自vulkan-tutorial.com的Alexander Overvoorde)

        void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
            VkCommandBufferAllocateInfo allocInfo = {};
            allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
            allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
            allocInfo.commandPool = commandPool.getCommandPool();
            allocInfo.commandBufferCount = 1;

            VkCommandBuffer commandBuffer;
            vkAllocateCommandBuffers(device.getDevice(), &allocInfo, &commandBuffer);

            VkCommandBufferBeginInfo beginInfo = {};
            beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
            beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;

            vkBeginCommandBuffer(commandBuffer, &beginInfo);

            VkBufferCopy copyRegion = {};
            copyRegion.size = size;
            vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);

            vkEndCommandBuffer(commandBuffer);

            VkSubmitInfo submitInfo = {};
            submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
            submitInfo.commandBufferCount = 1;
            submitInfo.pCommandBuffers = &commandBuffer;

            vkQueueSubmit(graphicsQueue.getQueue(), 1, &submitInfo, VK_NULL_HANDLE);
            vkQueueWaitIdle(graphicsQueue.getQueue());

            vkFreeCommandBuffers(device.getDevice(), commandPool.getCommandPool(), 1, &commandBuffer);

        }

1 个答案:

答案 0 :(得分:2)

看看这个StackOverflow问题:

Memory allocation with std430 qualifier

最终,纠正的答案:

在你的情况下,你的结构中最大的成员是vec3(浮动的3元素向量)。 vec3的碱基比对与vec4的比对相同。因此,数组元素的基本对齐方式等于16个字节。这意味着数组的每个元素都必须从16 的倍数开始。

但必须以递归方式为每个结构成员应用对齐规则。 3元素向量具有与4元素向量相同的比对。这意味着:

  • Position成员以与每个数组成员相同的对齐方式开始
  • VelocityIPositionIVelocity成员必须在给定数组元素开头后以16字节的倍数开始。
  • LifeTimeILifeTime成员的对齐方式为4个字节。

所以你的结构的总大小(以字节为单位)等于:

  • Position - 16个字节(Position本身需要12个字节,但下一个成员有16个字节的对齐方式)
  • Velocity - 16个字节
  • IPosition - 16个字节
  • IVelocity + LifeTime - 16个字节
  • ILifeTime - 4个字节

,它给出了68个字节。所以,据我所知,你需要在结构的末尾添加一个12字节的填充(数组元素之间需要12个字节),因为每个数组元素必须从16的倍数开始。

因此第一个数组元素从绑定到存储缓冲区的内存的偏移量0开始。但是第二个数组元素必须从内存的乞讨偏移80开始(最接近16的倍数大于68),依此类推。

或者,正如@NicolBolas评论的那样,为了让生活更轻松,只需将所有内容打包成vec4成员; - )。

更好,但没有完全正确的回答:

在你的情况下,你的结构中最大的成员是vec3(浮动的3元素向量)。因此,数组元素的基本对齐方式等于12个字节(对于std430布局中的结构数组,基本对齐不必舍入到4元素向量的马赫对齐。< - 我错了。我们不需要对结构的基本对齐进行舍入,但是其成员的对齐是正常计算的,vec3对齐与vec4对齐相同。这意味着您的数组的每个元素都必须从12的倍数开始(不,在这种情况下它应该以16的倍数开始)。

但必须以递归方式为每个结构成员应用对齐规则。 3元素向量具有与4元素向量相同的比对。这意味着:

  • Position成员以与每个数组成员相同的对齐方式开始
  • VelocityIPositionIVelocity成员必须在给定数组元素开头后以16字节的倍数开始。
  • LifeTimeILifeTime成员的对齐方式为4个字节。

所以你的结构的总大小(以字节为单位)等于:

  • Position - 16个字节(Position本身需要12个字节,但下一个成员有16个字节的对齐方式)
  • Velocity - 16个字节
  • IPosition - 16个字节
  • IVelocity + LifeTime - 16个字节
  • ILifeTime - 4个字节

,它给出了68个字节。所以,据我所知,你需要在你的结构的末尾填充一个4字节的填充(数组元素之间额外的4个字节),因为每个数组元素必须从12的倍数开始(再次,我们需要12字节填充,所以下一个数组元素从16的倍数开始,而不是12

因此第一个数组元素从绑定到存储缓冲区的内存的偏移量0开始。但是第二个数组元素必须从内存的乞讨偏移72开始(最接近12的倍数大于68),依此类推。

以前,错误的回答:

在你的情况下,最大的成员是vec3(浮动的3元素向量)。它的对齐等于12个字节(在结构数组的情况下,我们不必将3元素向量的圆对齐到4元素向量的马赫对齐)。您的结构的大小(以字节为单位)等于56.因此,据我所知,您需要在结构的末尾填充4字节(数组元素之间另外4个字节)因为每个数组元素必须从地址开始是12的倍数。