簡體   English   中英

Vulkan-統一緩沖區未發送到着色器

[英]Vulkan - Uniform Buffers Not Sent To Shader

在遵循使用C#的Vulkan教程的同時,我到達了應該開始使用Uniform緩沖區的地步(您可以在此處找到該教程)首先,讓我展示相關的代碼,然后說明問題。 我有一些相關的結構:

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);
    }
}

我認為它們都是非常不言自明的,但是如果您需要更多信息,我可以更新問題。 接下來,在主程序中,我創建了一些相關的字段:

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

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

這些是我用於緩沖區的一些實用程序方法:

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;
    }

在開始時,我還用一個值對其進行了初始化,但是我改用了Get / SetBufferData方法,因此我可以每幀更新數據。 這是渲染之前在主循環中調用的函數:

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);
}

還有用於分配命令緩沖區的功能。 確實沒有必要顯示全部內容,但基本上是:我分配緩沖區,綁定一些緩沖區並繪制。 在繪制之前,我將描述符集綁定到命令緩沖區,因此這是我使用的代碼(您需要知道的是“緩沖區”指向命令緩沖區的指針):

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

現在有兩個着色器。 唯一相關的着色器是頂點着色器,因此它是:

#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;
}

那是什么問題呢?
與圖形一樣:不渲染。 每當我刪除要乘以矩陣的部分時,它都可以正常工作。 我還嘗試將顏色設置為模型矩陣的第二行(應為綠色),但該行不起作用。

我在update方法中顯示的日志將打印矩陣緩沖區的數據,它們都是身份矩陣(就像它們應該那樣)。
這意味着緩沖區的數據很好。

但是,描述符集是一個不同的故事。 當我不將set布局綁定到管道布局時,我收到一條錯誤消息,指出着色器使用描述符插槽,但在管道布局中未聲明該插槽。
這意味着設置的布局已正確綁定。

如果不創建描述符集,則將其綁定到命令緩沖區時會出現錯誤。
這意味着描述符集正在正確創建,因此描述符池也很好(否則我會得到一個錯誤)。

因此,緩沖區和描述符集都可以工作。
這給了我一個結論:緩沖區和描述符集未正確鏈接。 奇怪的是,鏈接兩者的代碼是Device.UpdateDescriptorSets調用。 問題是,如果我不將緩沖區信息傳遞給DescriptorWrite變量,或者如果我將空句柄傳遞給緩沖區信息,則會收到一條錯誤消息,說明描述符集從未更新過。
如果我不將描述符集傳遞給DescriptorWrite變量,也是一樣。
這意味着它知道我對其進行了更新,並且知道了我正在發送緩沖區的事實,但是仍然以某種方式未將數據發送到着色器。

在嘗試調試了一個多星期之后,我可以說一切對我來說都很完美,我也不知道問題出在哪里。 這讓我非常絕望,所以我來到了這里。

這是我在網站上的第一篇文章,我不知道是否向您提供了所需的所有信息(即使我覺得我提供了太多信息),因此,如果該帖子有任何問題,請告訴我,我將嘗試修復它。

顯然所有與制服有關的代碼都可以。 由於某種原因,問題出在調試報告上。 我真的不知道與制服有什么關系,但是當我禁用它時,一切都完美。

我決定要做的只是在大多數情況下禁用調試報告,但是只要使用#define定義某些內容,就可以將其重新帶回來(當我需要Vulkan關於某些內容的反饋時)。 這可能不是一個好的解決方案,但這是我能想到的最好的解決方案。

我遇到了類似的情況,但仍在努力尋找原因。 如果在我的Android應用中啟用了“ VK_LAYER_LUNARG_core_validation”驗證層,則我的統一緩沖區在着色器中為零。

還沒有解決方法,但是我想知道這是驗證層還是描述符集中的錯誤。

我可以啟用其他驗證層而不會出現此問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM