ETH官方钱包

前往
大廳
主題

Vulkan 學(xué)習(xí)筆記 Uniform buffer

%%鼠 拒收病婿 | 2024-12-25 21:08:05 | 巴幣 1126 | 人氣 67

Uniform buffer

uniform buffer用於存儲(chǔ)和傳遞每幀變化的常數(shù)數(shù)據(jù),供Shader訪問(wèn)。這些數(shù)據(jù)通常包括變換矩陣、光照參數(shù)、材質(zhì)屬性等。uniform buffer的主要特點(diǎn)是,它能夠高效地向著色器提供小塊數(shù)據(jù),而不需要重新編譯著色器程序。
備註: 更快的作法是使用push constant

成果

每frame更新rotation,並推上uniform buffer,使shader能表現(xiàn)出來(lái)。

原理:把資料放在buffer,用descriptor告訴shader去哪裡讀取。 (The descriptor layout specifies the types of resources that are going to be accessed by the pipeline)
步驟:
  1. Specify a descriptor layout during pipeline creation
  2. Allocate a descriptor set from a descriptor pool
  3. Bind the descriptor set during rendering

定義UBO架構(gòu)

假設(shè)我們有MVP矩陣。
    struct UniformBufferObject {
        glm::mat4 model;
        glm::mat4 view;
        glm::mat4 proj;
    };

Vertex Shader使用範(fàn)例

我們接著創(chuàng)descriptor的binding layout指示我們只能從vertex shader stage讀取它(VK_SHADER_STAGE_VERTEX_BIT)
#version 450
// descriptor
layout(binding = 0) uniform UniformBufferObject {
    mat4 model;
    mat4 view;
    mat4 proj;
} ubo;
// attr
layout(location = 0) in vec2 inPosition;
layout(location = 1) in vec3 inColor;

layout(location = 0) out vec3 fragColor;

void main() {
    //gl_Position = vec4(inPosition, 0.0, 1.0);
    gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
    fragColor = inColor;
}


建立 descriptor set , pool , layout

由於swap chain有2~3個(gè)(MAX_FRAMES_IN_FLIGHT)frame buffer,我們?yōu)槊總€(gè)都準(zhǔn)備一個(gè)uniform buffer,因此pool size要是 static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT)

void ltn::TransformObject::createDescriptorSetLayout()
{
    // MVP uniform data
    VkDescriptorSetLayoutBinding uboLayoutBinding{};
    uboLayoutBinding.binding = 0;
    uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    uboLayoutBinding.descriptorCount = 1; //Change this if you have multiple objects.


    uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
    uboLayoutBinding.pImmutableSamplers = nullptr; // Optional // only relevant for image sampling related descriptors

    VkDescriptorSetLayoutCreateInfo layoutInfo{};
    layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
    layoutInfo.bindingCount = 1;
    layoutInfo.pBindings = &uboLayoutBinding;

    if (vkCreateDescriptorSetLayout(m_core_instance.get_device(), &layoutInfo, nullptr, &m_descriptorSetLayout) != VK_SUCCESS) {
        throw std::runtime_error("failed to create descriptor set layout!");
    }
}

void ltn::TransformObject::createDescriptorPool()
{
    VkDescriptorPoolSize poolSize{};
    poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    poolSize.descriptorCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
    VkDescriptorPoolCreateInfo poolInfo{};
    poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
    poolInfo.poolSizeCount = 1;
    poolInfo.pPoolSizes = &poolSize;
    poolInfo.maxSets = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);

    if (vkCreateDescriptorPool(m_core_instance.get_device(), &poolInfo, nullptr, &m_descriptorPool) != VK_SUCCESS) {
        throw std::runtime_error("failed to create descriptor pool!");
    }
}

void ltn::TransformObject::createDescriptorSets()
{
    std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT, m_descriptorSetLayout);
    VkDescriptorSetAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
    allocInfo.descriptorPool = m_descriptorPool;
    allocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
    allocInfo.pSetLayouts = layouts.data();
    m_descriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
    if (vkAllocateDescriptorSets(m_core_instance.get_device(), &allocInfo, m_descriptorSets.data()) != VK_SUCCESS) {
        throw std::runtime_error("failed to allocate descriptor sets!");
    }
}



更新buffer

透過(guò)vkUpdateDescriptorSets更新buffer資料。
void ltn::TransformObject::updateUniformBuffer(FrameUpdateData& framedata)
{
    // dummy test
    static auto startTime = std::chrono::high_resolution_clock::now();
    auto currentTime = std::chrono::high_resolution_clock::now();
    float time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count();


    UniformBufferObject ubo{};
    ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
    ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
    ubo.proj = glm::perspective(glm::radians(45.0f), framedata.aspect_ratio , 0.1f, 10.0f);
    ubo.proj[1][1] *= -1;
    memcpy(m_uniformBuffersMapped[framedata.current_image], &ubo, sizeof(ubo));

    // update desc set
    VkDescriptorBufferInfo bufferInfo{};
    bufferInfo.buffer = m_uniformBuffers[framedata.current_image];
    bufferInfo.offset = 0;
    bufferInfo.range = sizeof(UniformBufferObject);

    VkWriteDescriptorSet descriptorWrite{};
    descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
    descriptorWrite.dstSet = m_descriptorSets[framedata.current_image];
    descriptorWrite.dstBinding = 0;
    descriptorWrite.dstArrayElement = 0;
    descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    descriptorWrite.descriptorCount = 1;
    descriptorWrite.pBufferInfo = &bufferInfo;
    descriptorWrite.pImageInfo = nullptr; // Optional
    descriptorWrite.pTexelBufferView = nullptr; // Optional
    vkUpdateDescriptorSets(m_core_instance.get_device(), 1, &descriptorWrite, 0, nullptr);
}

Binding

vkCmdBindPipeline(
    forward_renderer_pass->get_current_cmdbuffer(),
    VK_PIPELINE_BIND_POINT_GRAPHICS,
    pipeline->get_pipeline());

vkCmdBindDescriptorSets(
    forward_renderer_pass->get_current_cmdbuffer(),
    VK_PIPELINE_BIND_POINT_GRAPHICS,
    pipeline->get_layout(), 0, 1, &transform.m_descriptorSets[swapchain->current_frame()], 0, nullptr);

model.bind(forward_renderer_pass->get_current_cmdbuffer());
model.draw(forward_renderer_pass->get_current_cmdbuffer());


Alignment 問(wèn)題

通常16 bytes的 (vec4)比較不會(huì)有問(wèn)題,如果你的結(jié)構(gòu)有非vec4的物件,請(qǐng)看

為甚麼創(chuàng)uniform buffer需要descriptorset但vertex buffer不需要?

Uniform buffer 用於傳遞小塊的常數(shù)數(shù)據(jù),例如變換矩陣、光照參數(shù)等,這些數(shù)據(jù)需要在著色器中頻繁訪問(wèn)。因此,descriptor set 用來(lái)管理這些緩衝區(qū)並將它們綁定到著色器中,使得著色器可以有效地訪問(wèn)和使用這些數(shù)據(jù);Vertex buffer 存儲(chǔ)頂點(diǎn)數(shù)據(jù),例如頂點(diǎn)的位置、法線、顏色等,這些數(shù)據(jù)通常在繪製命令執(zhí)行前一次性傳輸?shù)紾PU。因?yàn)檫@些數(shù)據(jù)是靜態(tài)的或很少改變的,使用描述符集來(lái)管理它們並不必要。

不同用途的Descriptor Set可以共用一個(gè)DescriptorSetPool嗎?

可以,例如圖片跟UBO的descriptor共用。
VkDescriptorPoolSize poolSizes[] = {
    { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 10 },
    { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 10 }
};

VkDescriptorPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = 2;
poolInfo.pPoolSizes = poolSizes;
poolInfo.maxSets = 10;

VkDescriptorPool descriptorPool;
if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
    throw std::runtime_error("failed to create descriptor pool!");
}


醜醜關(guān)聯(lián)圖:



送禮物贊助創(chuàng)作者 !
0
留言

創(chuàng)作回應(yīng)

追蹤 創(chuàng)作集

作者相關(guān)創(chuàng)作

相關(guān)創(chuàng)作

更多創(chuàng)作