前言
动态统一缓冲区用于渲染具有存储在单个统一缓冲区对象中的多个矩阵的多个对象。各个矩阵在描述符绑定时间上动态寻址,最大限度地减少所需描述符集的数量。
为每个对象使用一个统一缓冲区,而是分配一个大的统一缓冲区。
关于设备通过 minUniformBufferOffsetAlignment 报告的对准包含场景中对象的所有矩阵。
使用的描述符类型VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC然后允许设置动态偏移量,用于将数据从单个统一缓冲区传递到连接的着色器绑定点。
一、CPP文件
1.准备工序
用于对齐内存分配的包装器函数
目前在C++中没有适用于所有平台和供应商的标准,因此我们将其抽象化。
void* alignedAlloc(size_t size, size_t alignment)
{
void *data = nullptr;
#if defined(_MSC_VER) || defined(__MINGW32__)
data = _aligned_malloc(size, alignment);
#else
int res = posix_memalign(&data, alignment, size);
if (res != 0)
data = nullptr;
#endif
return data;
}
void alignedFree(void* data)
{
#if defined(_MSC_VER) || defined(__MINGW32__)
_aligned_free(data);
#else
free(data);
#endif
}
2.具体类以及变量
class VulkanExample : public VulkanExampleBase
{
public:
struct {
VkPipelineVertexInputStateCreateInfo inputState;
std::vector<VkVertexInputBindingDescription> bindingDescriptions;
std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
} vertices;
vks::Buffer vertexBuffer;
vks::Buffer indexBuffer;
uint32_t indexCount;
struct {
vks::Buffer view;
vks::Buffer dynamic;
} uniformBuffers;
struct {
glm::mat4 projection;
glm::mat4 view;
} uboVS;
// Store random per-object rotations
glm::vec3 rotations[OBJECT_INSTANCES];
glm::vec3 rotationSpeeds[OBJECT_INSTANCES];
// One big uniform buffer that contains all matrices
// Note that we need to manually allocate the data to cope for GPU-specific uniform buffer offset alignments
struct UboDataDynamic {
glm::mat4 *model = nullptr;
} uboDataDynamic;
VkPipeline pipeline;
VkPipelineLayout pipelineLayout;
VkDescriptorSet descriptorSet;
VkDescriptorSetLayout descriptorSetLayout;
float animationTimer = 0.0f;
size_t dynamicAlignment;
3.函数
void buildCommandBuffers()
// Render multiple objects using different model matrices by dynamically offsetting into one uniform buffer
for (uint32_t j = 0; j < OBJECT_INSTANCES; j++)
{
// One dynamic offset per dynamic descriptor to offset into the ubo containing all model matrices
uint32_t dynamicOffset = j * static_cast<uint32_t>(dynamicAlignment);
// Bind the descriptor set for rendering a mesh using the dynamic offset
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 1, &dynamicOffset);
vkCmdDrawIndexed(drawCmdBuffers[i], indexCount, 1, 0, 0, 0);
}
void generateCube()
void generateCube()
{
// Setup vertices indices for a colored cube
std::vector<Vertex> vertices = {
{ { -1.0f, -1.0f, 1.0f },{ 1.0f, 0.0f, 0.0f } },
{ { 1.0f, -1.0f, 1.0f },{ 0.0f, 1.0f, 0.0f } },
{ { 1.0f, 1.0f, 1.0f },{ 0.0f, 0.0f, 1.0f } },
{ { -1.0f, 1.0f, 1.0f },{ 0.0f, 0.0f, 0.0f } },
{ { -1.0f, -1.0f, -1.0f },{ 1.0f, 0.0f, 0.0f } },
{ { 1.0f, -1.0f, -1.0f },{ 0.0f, 1.0f, 0.0f } },
{ { 1.0f, 1.0f, -1.0f },{ 0.0f, 0.0f, 1.0f } },
{ { -1.0f, 1.0f, -1.0f },{ 0.0f, 0.0f, 0.0f } },
};
std::vector<uint32_t> indices = {
0,1,2, 2,3,0, 1,5,6, 6,2,1, 7,6,5, 5,4,7, 4,0,3, 3,7,4, 4,5,1, 1,0,4, 3,2,6, 6,7,3,
};
indexCount = static_cast<uint32_t>(indices.size());
// Create buffers
// For the sake of simplicity we won't stage the vertex data to the gpu memory
// Vertex buffer
VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&vertexBuffer,
vertices.size() * sizeof(Vertex),
vertices.data()));
// Index buffer
VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&indexBuffer,
indices.size() * sizeof(uint32_t),
indices.data()));
}
void setupVertexDescriptions()
void setupVertexDescriptions()
{
// Binding description
vertices.bindingDescriptions = {
vks::initializers::vertexInputBindingDescription(VERTEX_BUFFER_BIND_ID, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX),
};
// Attribute descriptions
vertices.attributeDescriptions = {
vks::initializers::vertexInputAttributeDescription(VERTEX_BUFFER_BIND_ID, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos)), // Location 0 : Position
vks::initializers::vertexInputAttributeDescription(VERTEX_BUFFER_BIND_ID, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, color)), // Location 1 : Color
};
vertices.inputState = vks::initializers::pipelineVertexInputStateCreateInfo();
vertices.inputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertices.bindingDescriptions.size());
vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data();
vertices.inputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertices.attributeDescriptions.size());
vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data();
}
创建描述符池。。。
创建描述符布局。。。
创建描述符。。。
void preparePipelines()
void preparePipelines()
{
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
vks::initializers::pipelineInputAssemblyStateCreateInfo(
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
0,
VK_FALSE);
VkPipelineRasterizationStateCreateInfo rasterizationState =
vks::initializers::pipelineRasterizationStateCreateInfo(
VK_POLYGON_MODE_FILL,
VK_CULL_MODE_NONE,
VK_FRONT_FACE_COUNTER_CLOCKWISE,
0);
VkPipelineColorBlendAttachmentState blendAttachmentState =
vks::initializers::pipelineColorBlendAttachmentState(
0xf,
VK_FALSE);
VkPipelineColorBlendStateCreateInfo colorBlendState =
vks::initializers::pipelineColorBlendStateCreateInfo(
1,
&blendAttachmentState);
VkPipelineDepthStencilStateCreateInfo depthStencilState =
vks::initializers::pipelineDepthStencilStateCreateInfo(
VK_TRUE,
VK_TRUE,
VK_COMPARE_OP_LESS_OR_EQUAL);
VkPipelineViewportStateCreateInfo viewportState =
vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
VkPipelineMultisampleStateCreateInfo multisampleState =
vks::initializers::pipelineMultisampleStateCreateInfo(
VK_SAMPLE_COUNT_1_BIT,
0);
std::vector<VkDynamicState> dynamicStateEnables = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState =
vks::initializers::pipelineDynamicStateCreateInfo(
dynamicStateEnables.data(),
static_cast<uint32_t>(dynamicStateEnables.size()),
0);
// Load shaders
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
shaderStages[0] = loadShader(getShadersPath() + "dynamicuniformbuffer/base.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getShadersPath() + "dynamicuniformbuffer/base.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
VkGraphicsPipelineCreateInfo pipelineCreateInfo =
vks::initializers::pipelineCreateInfo(
pipelineLayout,
renderPass,
0);
pipelineCreateInfo.pVertexInputState = &vertices.inputState;
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
pipelineCreateInfo.pRasterizationState = &rasterizationState;
pipelineCreateInfo.pColorBlendState = &colorBlendState;
pipelineCreateInfo.pMultisampleState = &multisampleState;
pipelineCreateInfo.pViewportState = &viewportState;
pipelineCreateInfo.pDepthStencilState = &depthStencilState;
pipelineCreateInfo.pDynamicState = &dynamicState;
pipelineCreateInfo.stageCount = static_cast<uint32_t>(shaderStages.size());
pipelineCreateInfo.pStages = shaderStages.data();
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline));
}
void prepareUniformBuffers()
// Prepare and initialize uniform buffer containing shader uniforms
void prepareUniformBuffers()
{
// Allocate data for the dynamic uniform buffer object
// We allocate this manually as the alignment of the offset differs between GPUs
// Calculate required alignment based on minimum device offset alignment
size_t minUboAlignment = vulkanDevice->properties.limits.minUniformBufferOffsetAlignment;
dynamicAlignment = sizeof(glm::mat4);
if (minUboAlignment > 0) {
dynamicAlignment = (dynamicAlignment + minUboAlignment - 1) & ~(minUboAlignment - 1);
}
size_t bufferSize = OBJECT_INSTANCES * dynamicAlignment;
uboDataDynamic.model = (glm::mat4*)alignedAlloc(bufferSize, dynamicAlignment);
assert(uboDataDynamic.model);
std::cout << "minUniformBufferOffsetAlignment = " << minUboAlignment << std::endl;
std::cout << "dynamicAlignment = " << dynamicAlignment << std::endl;
// Vertex shader uniform buffer block
// Static shared uniform buffer object with projection and view matrix
VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
&uniformBuffers.view,
sizeof(uboVS)));
// Uniform buffer object with per-object matrices
VK_CHECK_RESULT(vulkanDevice->createBuffer(
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
&uniformBuffers.dynamic,
bufferSize));
// Override descriptor range to [base, base + dynamicAlignment]
uniformBuffers.dynamic.descriptor.range = dynamicAlignment;
// Map persistent
VK_CHECK_RESULT(uniformBuffers.view.map());
VK_CHECK_RESULT(uniformBuffers.dynamic.map());
// Prepare per-object matrices with offsets and random rotations
std::default_random_engine rndEngine(benchmark.active ? 0 : (unsigned)time(nullptr));
std::normal_distribution<float> rndDist(-1.0f, 1.0f);
for (uint32_t i = 0; i < OBJECT_INSTANCES; i++) {
rotations[i] = glm::vec3(rndDist(rndEngine), rndDist(rndEngine), rndDist(rndEngine)) * 2.0f * (float)M_PI;
rotationSpeeds[i] = glm::vec3(rndDist(rndEngine), rndDist(rndEngine), rndDist(rndEngine));
}
updateUniformBuffers();
updateDynamicUniformBuffer(true);
}
二、shader
1.vertexShader
代码如下(示例):
#version 450
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec3 inColor;
layout (binding = 0) uniform UboView
{
mat4 projection;
mat4 view;
} uboView;
layout (binding = 1) uniform UboInstance
{
mat4 model;
} uboInstance;
layout (location = 0) out vec3 outColor;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
outColor = inColor;
mat4 modelView = uboView.view * uboInstance.model;
vec3 worldPos = vec3(modelView * vec4(inPos, 1.0));
gl_Position = uboView.projection * modelView * vec4(inPos.xyz, 1.0);
}
2.fragShader
#version 450
layout (location = 0) in vec3 inColor;
layout (location = 0) out vec4 outFragColor;
void main()
{
outFragColor = vec4(inColor, 1.0);
}