Vulkan UBO 传递错误

Posted

技术标签:

【中文标题】Vulkan UBO 传递错误【英文标题】:Vulkan UBO passed incorrectly 【发布时间】:2017-11-10 17:30:18 【问题描述】:

我想将包含两个 4x4 矩阵的统一缓冲区对象传递给顶点着色器。我在 C++ 中声明了如下结构:

struct PerRenderUBO

    glm::mat4 viewProjection;
    glm::mat4 projection; //unused
;

在 GLSL 中:

layout(std140, set = 0, binding = 0) uniform UBO 
    mat4 viewProjection;
    mat4 projection; //unused
 perRenderUBO;

但是,一旦我在 UBO 结构中声明了多个成员,由于 viewProjection 矩阵错误,某些对象会被错误地绘制。如果我在 PerRenderUBO 结构和 GLSL 声明中注释掉“投影”数据成员,一切都会正确呈现(尽管我什至没有在着色器中使用投影)。 这让我相信数据对齐一定有问题。我已经用 std140 注释声明了布局。 sizeof(PerRenderUBO) 返回 128。我尝试将“投影”声明为浮点数,但问题仍然存在。

以下是描述符集创建中最重要的部分:

/** Create Layout **/
vk::DescriptorSetLayoutBinding perRenderUBOBinding;
perRenderUBOBinding.binding = 0;
perRenderUBOBinding.descriptorCount = 1;
perRenderUBOBinding.descriptorType = vk::DescriptorType::eUniformBuffer;
perRenderUBOBinding.stageFlags = vk::ShaderStageFlagBits::eVertex;
perRenderUBOBinding.pImmutableSamplers = nullptr;

std::vector<vk::DescriptorSetLayoutBinding> bindingsperRenderUBOBinding;

vk::DescriptorSetLayoutCreateInfo createInfo;
createInfo.bindingCount = bindings.size();
createInfo.pBindings = bindings.data();
this->perRenderUBOLayout = vkDevice->createDescriptorSetLayout(createInfo);

/** Create host-visible and host-coherent buffer **/
vk::BufferCreateInfo bufferCreateInfo;
bufferCreateInfo.size = sizeof(PerRenderUBO);
bufferCreateInfo.usage = vk::BufferUsageFlagBits::eUniformBuffer;
bufferCreateInfo.sharingMode = vk::SharingMode::eExclusive;
bufferCreateInfo.queueFamilyIndexCount = queueFamilyIndices.size();
bufferCreateInfo.pQueueFamilyIndices = queueFamilyIndices.data();
this->buffer = vkDevice->createBuffer(bufferCreateInfo);

vk::MemoryRequirements memoryRequirements = vkDevice->getBufferMemoryRequirements(this->buffer);
//allocates device memory as proposed in the specification (10.2 Device Memory)
this->bufferDeviceMemory = allocate(memoryRequirements, vk::MemoryPropertyFlagsvk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagsvk::MemoryPropertyFlagBits::eHostCoherent);
vkDevice->bindBufferMemory(this->buffer, this->bufferDeviceMemory, 0);


this->descriptorPool = ...//create a descriptor pool for 1 uniform buffer
this->descriptorSet = ...//allocate descriptor set for above layout

vk::DescriptorBufferInfo bufferInfo;
bufferInfo.buffer = this->buffer;
bufferInfo.offset = 0;
bufferInfo.range = sizeof(PerRenderUBO);

vk::WriteDescriptorSet writeDescriptorSet;
writeDescriptorSet.dstSet = *this->descriptorSet;
writeDescriptorSet.dstBinding = 0;
writeDescriptorSet.dstArrayElement = 0;
writeDescriptorSet.descriptorType = vk::DescriptorType::eUniformBuffer;
writeDescriptorSet.descriptorCount = 1;
writeDescriptorSet.pBufferInfo = &bufferInfo;
writeDescriptorSet.pImageInfo = nullptr;
writeDescriptorSet.pTexelBufferView = nullptr;

vkDevice->updateDescriptorSets(writeDescriptorSet, );

在执行主绘制命令缓冲区之前,我更新 PerRenderUBO 的缓冲区如下:

std::vector<PerRenderUBO> data; //contains 1 instance of PerRenderUBO
vk::DeviceSize offset = 0;
vk::DeviceSize size = data.size() * sizeof(PerRenderUBO);

void* memory = vkDevice->mapMemory(this->bufferDeviceMemory, offset, size);
std::memcpy(memory, data.data(), size);
this->deviceMemory->unmap();

我已经检查了几次缓冲区大小和偏移量,一切看起来都很好。另外,由于每个绘图命令都绑定了相同的描述符集,并且某些对象正确渲染,我相信缓冲区本身的数据必须是正确的。 我错过了什么?

【问题讨论】:

能不能也加上实际上传ubo数据的部分? 我已经添加了缓冲区创建和缓冲区更新的代码。 您只有一个 PerRenderUBO 实例,但您谈论渲染多个对象。每个命令缓冲区是否只有一次绘制(因为您在提交命令缓冲区之前映射/memcpy/unmap)?如果是这样,什么可以确保您在第 N 次抽签完成之前不会覆盖第 N+1 次抽签的 UBO 内容? 【参考方案1】:

某些对象绘制不正确和正确绘制的事实可能表明存在同步问题或缓冲区未按时更新。

首先,您需要通知驱动程序缓冲区的哪些部分已被主机更新。我没有看到这样的代码,你也没有提到任何关于它的内容。这是通过刷新内存来完成的 - 您需要调用 vkFlushMappedMemoryRanges() 函数。

您可能还需要设置一个屏障,通知驱动程序主机访问了缓冲区。但据我记得,这种障碍是在命令缓冲区提交时隐式设置的。

【讨论】:

非常感谢!我应该更清楚地指定我正在分配一个主机一致的内存,这不需要调用 vkFlushMappedMemoryRanges()。但是,同步可能存在问题。我尝试只为缓冲区设置一次数据,以便在创建缓冲区后不执行任何写操作。不幸的是,这并没有解决问题。我还尝试通过推送常量传递 PerRenderUBO,但得到了同样奇怪的行为。

以上是关于Vulkan UBO 传递错误的主要内容,如果未能解决你的问题,请参考以下文章

vulkan API 复制了啥?

Vulkan 构建错误

Vulkan Tutorial 04 理解Validation layers

脱离时的 ImGui Vulkan 验证层错误

Vulkan的分层设计

不取消映射 vulkan 内存