GLSL Compute Shader только частично пишет в буфер в Vulkan - PullRequest
0 голосов
/ 12 мая 2018

Я создал этот GLSL Compute Shader и скомпилировал его, используя "glslangValidator.exe".Тем не менее, он будет обновлять только значения «Particles [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-шейдер

#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)

        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 голосов
/ 14 мая 2018

Посмотрите на вопрос StackOverflow:

Распределение памяти с квалификатором std430

ЗАКЛЮЧИТЕЛЬНЫЙ, ИСПРАВЛЕННЫЙ ОТВЕТ:

В вашем случае самым большим членом вашей структуры является vec3 (3-элементный вектор чисел с плавающей точкой). Базовое выравнивание vec3 такое же, как выравнивание vec4. Таким образом, базовое выравнивание элементов Вашего массива равно 16 байтам. Это означает, что каждый элемент вашего массива должен начинаться с адреса, кратного 16 .

Но правила выравнивания должны применяться рекурсивно к каждому члену структуры. Трехэлементные векторы имеют такое же выравнивание, что и четырехэлементные векторы. Это означает, что:

  • Position элемент начинается с того же выравнивания, что и каждый элемент массива
  • Velocity, IPosition и IVelocity члены должны начинаться с кратных 16 байт после начала данного элемента массива.
  • LifeTime и ILifeTime имеют выравнивание по 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 </strong>). Это означает, что каждый элемент вашего массива должен начинаться с адреса, кратного 12 ( нет, в этом случае он должен начинаться с кратного 16 ).

Но правила выравнивания должны применяться рекурсивно к каждому члену структуры. Трехэлементные векторы имеют такое же выравнивание, что и четырехэлементные векторы. Это означает, что:

  • Position элемент начинается с того же выравнивания, что и каждый элемент массива
  • Velocity, IPosition и IVelocity члены должны начинаться с кратных 16 байт после начала данного элемента массива.
  • LifeTime и ILifeTime имеют выравнивание по 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.

...