c++ - Vulkan UBO 传递错误

标签 c++ glsl vulkan

我想将包含两个 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。我尝试将“投影”声明为 float ,但问题仍然存在。

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

/** 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> bindings{perRenderUBOBinding};

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::MemoryPropertyFlags{vk::MemoryPropertyFlagBits::eHostVisible} | vk::MemoryPropertyFlags{vk::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();

我已经多次检查缓冲区大小和偏移量,一切看起来都很好。此外,由于每个绘制命令都绑定(bind)了相同的描述符集并且某些对象正确呈现,我相信缓冲区中的数据本身一定是正确的。 我错过了什么?

最佳答案

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

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

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

关于c++ - Vulkan UBO 传递错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47228204/

相关文章:

glsl - 在统一分支内有一个非绑定(bind)采样器

c++ - GLSL 中的 KD 树

vulkan - 是否可以在 Vulkan 中不使用 Surface 进行屏幕外渲染?

batch-file - 将 GLSL 编译为相应的 Spir V,以便在 Vulkan 应用程序中使用

c++ - 如何使用 uniform_int_distribution 作为构造函数中的类字段

c++ - 在 char 数组 C++ 中查找值

c++ - 创建不带 WinMain 函数的 Win32 窗口

javascript - 在 Three.js 中使用 Javascript 更新每帧的数据纹理

arrays - 索引第二个数组元素始终返回零

c++ - 使用具有同名方法的派生类的对象访问基类的方法