简体   繁体   English

Vulkan-统一缓冲区未发送到着色器

[英]Vulkan - Uniform Buffers Not Sent To Shader

While following a Vulkan tutorial using C# I reached the point where I should start using Uniform buffers (you can find the tutorial here ) First let me show the related code, and then explain the problem. 在遵循使用C#的Vulkan教程的同时,我到达了应该开始使用Uniform缓冲区的地步(您可以在此处找到该教程)首先,让我展示相关的代码,然后说明问题。 I have those few structures that are related: 我有一些相关的结构:

unsafe struct Buffer
{
    public SharpVulkan.Buffer buffer;
    public DeviceMemory memory;
    public readonly ulong size;

    public Buffer(ulong size) : this()
    {
        this.size = size;
    }

    public void Destroy(Device device)
    {
        device.DestroyBuffer(buffer);
        device.FreeMemory(memory);
    }
}

struct MVPMatrices
{
    public static MVPMatrices Identity { get; } = new MVPMatrices { model = Matrix4x4.Identity, view = Matrix4x4.Identity, projection = Matrix4x4.Identity };

    public Matrix4x4 model;
    public Matrix4x4 view;
    public Matrix4x4 projection;
}

struct UniformBuffer
{
    public Buffer buffer;
    public DescriptorSet descriptorSet;

    public void Destroy(Device device)
    {
        buffer.Destroy(device);
    }
}

I think they all are very self-explanatory, but if you need more information I can update the question. 我认为它们都是非常不言自明的,但是如果您需要更多信息,我可以更新问题。 Next, in the main program there are a few fields I have created that are related: 接下来,在主程序中,我创建了一些相关的字段:

static DescriptorSetLayout descriptorSetLayout;
static DescriptorPool descriptorPool;
static UniformBuffer mvpMatricesBuffer;

static readonly MVPMatrices[] mvpMatricesArray = new MVPMatrices[1];
static ref MVPMatrices MVPMatrices => ref mvpMatricesArray[0];

Those are some utility methods I have for buffers: 这些是我用于缓冲区的一些实用程序方法:

static Buffer CreateBuffer(ulong size, BufferUsageFlags usage, MemoryPropertyFlags memoryPropertyFlags = MemoryPropertyFlags.HostVisible | MemoryPropertyFlags.HostCoherent)
    {
        Buffer buffer = new Buffer(size);
        BufferCreateInfo createInfo = new BufferCreateInfo
        {
            StructureType = StructureType.BufferCreateInfo,
            SharingMode = SharingMode.Exclusive,
            Size = size,
            Usage = usage,
        };
        buffer.buffer = logicalDevice.CreateBuffer(ref createInfo);

        logicalDevice.GetBufferMemoryRequirements(buffer.buffer, out MemoryRequirements memoryRequirements);
        physicalDevice.GetMemoryProperties(out PhysicalDeviceMemoryProperties memoryProperties);

        uint memoryTypeIndex = 0;
        for (uint i = 0; i < memoryProperties.MemoryTypeCount; i++)
        {
            MemoryType* memoryType = &memoryProperties.MemoryTypes.Value0 + i;
            if ((memoryRequirements.MemoryTypeBits & (1 << (int)i)) != 0 && memoryType->PropertyFlags.HasFlag(memoryPropertyFlags))
            {
                memoryTypeIndex = i;
                break;
            }
        }

        MemoryAllocateInfo allocateInfo = new MemoryAllocateInfo
        {
            StructureType = StructureType.MemoryAllocateInfo,
            AllocationSize = memoryRequirements.Size,
            MemoryTypeIndex = memoryTypeIndex,
        };
        buffer.memory = logicalDevice.AllocateMemory(ref allocateInfo);
        logicalDevice.BindBufferMemory(buffer.buffer, buffer.memory, 0);
        return buffer;
    }

    static void SetBufferData<T>(this Buffer buffer, T[] data)
        where T : struct
    {
        ulong size = (ulong)(Marshal.SizeOf<T>() * data.Length);
        if (size != buffer.size)
            throw new ArgumentException("Size of buffer data must be the same as the size of the buffer!");

        IntPtr memory = logicalDevice.MapMemory(buffer.memory, 0, size, MemoryMapFlags.None);
        System.Buffer.MemoryCopy(Marshal.UnsafeAddrOfPinnedArrayElement(data, 0).ToPointer(), memory.ToPointer(), (uint)size, (uint)size);
        logicalDevice.UnmapMemory(buffer.memory);
    }

    static T[] GetBufferData<T>(this Buffer buffer)
        where T : struct
    {
        T[] result = new T[(int)buffer.size / Marshal.SizeOf<T>()];
        IntPtr memory = logicalDevice.MapMemory(buffer.memory, 0, buffer.size, MemoryMapFlags.None);
        System.Buffer.MemoryCopy(memory.ToPointer(), Marshal.UnsafeAddrOfPinnedArrayElement(result, 0).ToPointer(), (uint)buffer.size, (uint)buffer.size);
        logicalDevice.UnmapMemory(buffer.memory);
        return result;
    }

    static DescriptorSet AllocateDescriptorSet()
    {
        DescriptorSetLayout* setLayout = stackalloc DescriptorSetLayout[1];
        *setLayout = descriptorSetLayout;

        DescriptorSetAllocateInfo allocateInfo = new DescriptorSetAllocateInfo
        {
            StructureType = StructureType.DescriptorSetAllocateInfo,
            DescriptorPool = descriptorPool,
            DescriptorSetCount = 1,
            SetLayouts = (IntPtr)setLayout,
        };
        DescriptorSet set = DescriptorSet.Null;
        logicalDevice.AllocateDescriptorSets(ref allocateInfo, &set);
        return set;
    }

    static UniformBuffer CreateUniformBuffer(ulong size, uint binding)
    {
        UniformBuffer buffer = new UniformBuffer
        {
            buffer = CreateBuffer(size, BufferUsageFlags.UniformBuffer),
            descriptorSet = AllocateDescriptorSet(),
        };

        DescriptorBufferInfo bufferInfo = new DescriptorBufferInfo
        {
            Buffer = buffer.buffer.buffer,
            Offset = 0,
            Range = buffer.buffer.size,
        };

        WriteDescriptorSet descriptorWrite = new WriteDescriptorSet
        {
            StructureType = StructureType.WriteDescriptorSet,
            BufferInfo = new IntPtr(&bufferInfo),
            DescriptorCount = 1,
            DescriptorType = DescriptorType.UniformBuffer,
            DestinationArrayElement = 0,
            DestinationBinding = binding,
            DestinationSet = buffer.descriptorSet,
            ImageInfo = IntPtr.Zero,
            TexelBufferView = IntPtr.Zero,
        };
        logicalDevice.UpdateDescriptorSets(1, &descriptorWrite, 0, null);
        return buffer;
    }

At the start I also initialized it with a value, but instead I made the Get/SetBufferData methods, so I can update the data every frame. 在开始时,我还用一个值对其进行了初始化,但是我改用了Get / SetBufferData方法,因此我可以每帧更新数据。 Here is the function that's called in the main loop right before rendering: 这是渲染之前在主循环中调用的函数:

static void UpdateApplication()
{
    MVPMatrices.model = Matrix4x4.Identity;
    MVPMatrices.view = Matrix4x4.Identity;
    MVPMatrices.projection = Matrix4x4.Identity;

    mvpMatricesBuffer.buffer.SetBufferData(mvpMatricesArray);
    Console.WriteLine(mvpMatricesBuffer.buffer.GetBufferData<MVPMatrices>()[0].model);
    Console.WriteLine(mvpMatricesBuffer.buffer.GetBufferData<MVPMatrices>()[0].view);
    Console.WriteLine(mvpMatricesBuffer.buffer.GetBufferData<MVPMatrices>()[0].projection);
}

There is also the function for that allocates the command buffers. 还有用于分配命令缓冲区的功能。 There is really no need to show the whole thing, but basically: I allocate the buffer, bind some buffers and draw. 确实没有必要显示全部内容,但基本上是:我分配缓冲区,绑定一些缓冲区并绘制。 Right before drawing I bind the descriptor set to the command buffer, so this is the code I use for that (all you need to know is that "buffer" a pointer to the command buffer): 在绘制之前,我将描述符集绑定到命令缓冲区,因此这是我使用的代码(您需要知道的是“缓冲区”指向命令缓冲区的指针):

DescriptorSet* descriptorSets = stackalloc DescriptorSet[1];
*descriptorSets = mvpMatricesBuffer.descriptorSet;
buffer->BindDescriptorSets(PipelineBindPoint.Graphics, pipelineLayout, 0, 1, descriptorSets, 0, null);

Now there are the two shaders. 现在有两个着色器。 The only relevant shader is the vertex shader, so here it is: 唯一相关的着色器是顶点着色器,因此它是:

#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(binding = 0) uniform MVPMatrices
{
    mat4 model;
    mat4 view;
    mat4 projection;
} mvpMatrices;

layout(location = 0) in vec3 position;
layout(location = 1) in vec4 color;

layout(location = 0) out vec4 vertexColor;

void main()
{
    gl_Position = mvpMatrices.projection * mvpMatrices.view * mvpMatrices.model * vec4(position, 1);
    vertexColor = color;
}

So what is the problem? 那是什么问题呢?
As always with graphics: It doesn't render. 与图形一样:不渲染。 Whenever I delete the part where I multiply by the matrices it works perfectly fine. 每当我删除要乘以矩阵的部分时,它都可以正常工作。 I also tried to set the color to the second row of the model matrix (which should be green), and it didn't work. 我还尝试将颜色设置为模型矩阵的第二行(应为绿色),但该行不起作用。

The logs I showed in the update method print the data of the matrices buffer, and they all are the identity matrices (just like they should be). 我在update方法中显示的日志将打印矩阵缓冲区的数据,它们都是身份矩阵(就像它们应该那样)。
That means the data of the buffer is fine. 这意味着缓冲区的数据很好。

The descriptor sets are a different story, though. 但是,描述符集是一个不同的故事。 When I don't bind the set layout to the pipeline layout, I get an error saying the shader uses a descriptor slot but there it's not declared in the pipeline layout. 当我不将set布局绑定到管道布局时,我收到一条错误消息,指出着色器使用描述符插槽,但在管道布局中未声明该插槽。
That means the set layout is being bound correctly. 这意味着设置的布局已正确绑定。

If I don't create the descriptor set then I get an error when I bind it to the command buffer. 如果不创建描述符集,则将其绑定到命令缓冲区时会出现错误。
That means the descriptor set is being created correctly, and thus the descriptor pool is also fine (I would get an error otherwise). 这意味着描述符集正在正确创建,因此描述符池也很好(否则我会得到一个错误)。

So the buffers and descriptor sets both work. 因此,缓冲区和描述符集都可以工作。
That leaves me with one conclusion: The buffer and descriptor set are not linked correctly. 这给了我一个结论:缓冲区和描述符集未正确链接。 The weird thing is that the code linking the two is the Device.UpdateDescriptorSets call. 奇怪的是,链接两者的代码是Device.UpdateDescriptorSets调用。 The problem is that if I don't pass the buffer info the the DescriptorWrite variable, or if I pass a null handle to the buffer info, I get an error saying the descriptor set has never been updated. 问题是,如果我不将缓冲区信息传递给DescriptorWrite变量,或者如果我将空句柄传递给缓冲区信息,则会收到一条错误消息,说明描述符集从未更新过。
Same thing if I don't pass the descriptor set to the DescriptorWrite variable. 如果我不将描述符集传递给DescriptorWrite变量,也是一样。
That means it's aware of the fact that I update it, and it's aware of the fact that I am sending a buffer, but the data is still somehow not being sent to the shader. 这意味着它知道我对其进行了更新,并且知道了我正在发送缓冲区的事实,但是仍然以某种方式未将数据发送到着色器。

After trying to debug it for over a week already, I can say that everything looks perfect to me, and I have no idea what the problem is. 在尝试调试了一个多星期之后,我可以说一切对我来说都很完美,我也不知道问题出在哪里。 This made me pretty hopeless, so I came here. 这让我非常绝望,所以我来到了这里。

This is my first post on the website, and I have no idea if I gave you all of the information you need (even though I feel like I gave too many), so if there is anything wrong with the post, please tell me and I will try to fix it. 这是我在网站上的第一篇文章,我不知道是否向您提供了所需的所有信息(即使我觉得我提供了太多信息),因此,如果该帖子有任何问题,请告诉我,我将尝试修复它。

Apparently all of the code related to the uniforms is ok. 显然所有与制服有关的代码都可以。 For some reason the problem was with the debug report. 由于某种原因,问题出在调试报告上。 I really have no idea what it has to do with the uniforms, but when I disable it everything is perfect. 我真的不知道与制服有什么关系,但是当我禁用它时,一切都完美。

What I decided to do is just disable the debug report for the most part, but be able to bring it back (when I need some feedback from Vulkan about stuff) by simply defining something using #define. 我决定要做的只是在大多数情况下禁用调试报告,但是只要使用#define定义某些内容,就可以将其重新带回来(当我需要Vulkan关于某些内容的反馈时)。 It might not be a good solution, but it's the best I could come up with. 这可能不是一个好的解决方案,但这是我能想到的最好的解决方案。

I faced something similar and am still trying to chase down the reason. 我遇到了类似的情况,但仍在努力寻找原因。 If I enable the "VK_LAYER_LUNARG_core_validation" validation layer in my Android app, my uniform buffers are zero'd in the shaders. 如果在我的Android应用中启用了“ VK_LAYER_LUNARG_core_validation”验证层,则我的统一缓冲区在着色器中为零。

Don't have a workaround yet, but I wonder if this is a bug in the validation layer or in my descriptor sets. 还没有解决方法,但是我想知道这是验证层还是描述符集中的错误。

I am able to enable the other validation layers without this issue. 我可以启用其他验证层而不会出现此问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM