init
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# Copyright (c) 2023-2024, Sascha Willems
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 the "License";
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
get_filename_component(FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
||||
get_filename_component(PARENT_DIR ${CMAKE_CURRENT_LIST_DIR} PATH)
|
||||
get_filename_component(CATEGORY_NAME ${PARENT_DIR} NAME)
|
||||
|
||||
add_sample_with_tags(
|
||||
ID ${FOLDER_NAME}
|
||||
CATEGORY ${CATEGORY_NAME}
|
||||
AUTHOR "Sascha Willems"
|
||||
NAME "Descriptor buffers"
|
||||
DESCRIPTION "Using VK_KHR_descriptor_buffer"
|
||||
SHADER_FILES_GLSL
|
||||
"descriptor_buffer_basic/glsl/cube.vert"
|
||||
"descriptor_buffer_basic/glsl/cube.frag"
|
||||
SHADER_FILES_HLSL
|
||||
"descriptor_buffer_basic/hlsl/cube.vert.hlsl"
|
||||
"descriptor_buffer_basic/hlsl/cube.frag.hlsl")
|
||||
@@ -0,0 +1,257 @@
|
||||
////
|
||||
- Copyright (c) 2023-2024, Sascha Willems
|
||||
-
|
||||
- SPDX-License-Identifier: Apache-2.0
|
||||
-
|
||||
- Licensed under the Apache License, Version 2.0 the "License";
|
||||
- you may not use this file except in compliance with the License.
|
||||
- You may obtain a copy of the License at
|
||||
-
|
||||
- http://www.apache.org/licenses/LICENSE-2.0
|
||||
-
|
||||
- Unless required by applicable law or agreed to in writing, software
|
||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
- See the License for the specific language governing permissions and
|
||||
- limitations under the License.
|
||||
-
|
||||
////
|
||||
= Descriptor buffers
|
||||
|
||||
ifdef::site-gen-antora[]
|
||||
TIP: The source for this sample can be found in the https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/extensions/descriptor_buffer_basic[Khronos Vulkan samples github repository].
|
||||
endif::[]
|
||||
|
||||
|
||||
== Overview
|
||||
|
||||
Binding and managing descriptors in Vulkan can become pretty complex, both for the application and the driver.
|
||||
With the https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#VK_ext_descriptor_buffer[`VK_EXT_descriptor_buffer`] extension, this interface is simplified and maps more directly to how hardware sees descriptors.
|
||||
It also simplifies the programming model, as you no longer have to create descriptor pool upfront.
|
||||
|
||||
This sample shows how to use that extension by rendering multiple objects with different uniform buffers and images using the new interface of creating and binding descriptors.
|
||||
|
||||
== Deprecated descriptor bindings
|
||||
|
||||
Creating and binding descriptors in Vulkan requires different steps and function calls.
|
||||
|
||||
After all, descriptors are just memory, and something like a `VkDescriptorPool` was an abstract concept that didn't actually map to hardware.
|
||||
On most implementations `vkCreateDescriptorPool` did nothing more than just a memory allocation.
|
||||
Same for `vkAllocateDescriptorSets`, which in the end is also just some sort of memory allocation, while `vkUpdateDescriptorSets` did some memory copies for the descriptors to that buffer.
|
||||
|
||||
With the streamlined descriptor setup from `VK_EXT_descriptor_buffer`, the api now maps more closely to this and removes the need for the following functions:
|
||||
|
||||
* vkCreateDescriptorPool
|
||||
* vkAllocateDescriptorSets
|
||||
* vkUpdateDescriptorSets
|
||||
* vkCmdBindDescriptorSets
|
||||
|
||||
Other concepts of Vulkan's descriptor logic like descriptor set layouts and pipeline layouts are still used and not deprecated.
|
||||
|
||||
== The new way
|
||||
|
||||
The `VK_EXT_descriptor_buffer` replaces all of this with *resource descriptor buffer*.
|
||||
These store descriptors in a way that the GPU can directly read them from such a buffer.
|
||||
The application simply puts them into those buffers.
|
||||
That buffer is then bound at command buffer recording time similar to other buffer types.
|
||||
|
||||
To make the following code easier to understand, let's take a look at the interfaces of our shaders:
|
||||
|
||||
[,glsl]
|
||||
----
|
||||
// Vertex shader
|
||||
layout (set = 0, binding = 0) uniform UBOScene {
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
} uboCamera;
|
||||
|
||||
layout (set = 1, binding = 0) uniform UBOModel {
|
||||
mat4 local;
|
||||
} uboModel;
|
||||
|
||||
// Fragment shader
|
||||
layout (set = 2, binding = 0) uniform sampler2D samplerColorMap;
|
||||
----
|
||||
|
||||
We use three descriptor sets, each with one binding.
|
||||
|
||||
=== Creating the descriptor buffers
|
||||
|
||||
Descriptors are now stored in and accessed from memory, so instead of having to use the old approach of creating dedicated Vulkan objects, we create buffers that will store descriptors instead.
|
||||
|
||||
The extension introduces two different types of descriptors: Resource descriptors for buffers (uniform buffers, shader storage buffers) and sampler/combined image sampler descriptors.
|
||||
In this sample we'll be using both types, so we create two different buffers.
|
||||
|
||||
As is usual in Vulkan, implementations have different size and alignment requirements. Alignment requirements for setting offsets in the descriptor buffer with `vkCmdSetDescriptorBufferOffsetsEXT` are defined by `VkPhysicalDeviceDescriptorBufferPropertiesEXT::descriptorBufferOffsetAlignment`. At the start of the example we fetch that information into `descriptor_buffer_properties`.
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
VkPhysicalDeviceProperties2KHR device_properties{};
|
||||
descriptor_buffer_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT;
|
||||
device_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
|
||||
device_properties.pNext = &descriptor_buffer_properties;
|
||||
vkGetPhysicalDeviceProperties2KHR(get_device().get_gpu().get_handle(), &device_properties);
|
||||
----
|
||||
|
||||
So to calculate the actual buffer sizes required to store the descriptors, we need to get the sizes of set layouts and also align them to `VkPhysicalDeviceDescriptorBufferPropertiesEXT::descriptorBufferOffsetAlignment`. We also need to fetch offsets of the descriptor bindings of a set layout as by the Vulkan specs size of the set layout is at least a sum of sizes of descriptor bindings of this layout but it can be higher than that and there are no guarantees about the layout of descriptor bindings in a descriptor set layout, meaning that first descriptor binding of a set can start exactly at the beginning of a set layout memory or it can start with non 0 offset if driver implementation puts some metadata there.
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
// Get set layout descriptor sizes.
|
||||
vkGetDescriptorSetLayoutSizeEXT(get_device().get_handle(), uniform_binding_descriptor.layout, &uniform_binding_descriptor.size);
|
||||
vkGetDescriptorSetLayoutSizeEXT(get_device().get_handle(), image_binding_descriptor.layout, &image_binding_descriptor.size);
|
||||
|
||||
// Adjust set layout sizes to satisfy alignment requirements.
|
||||
uniform_binding_descriptor.size = aligned_size(uniform_binding_descriptor.size, descriptor_buffer_properties.descriptorBufferOffsetAlignment);
|
||||
image_binding_descriptor.size = aligned_size(image_binding_descriptor.size, descriptor_buffer_properties.descriptorBufferOffsetAlignment);
|
||||
|
||||
// Get descriptor bindings offsets as descriptors are placed inside set layout by those offsets.
|
||||
vkGetDescriptorSetLayoutBindingOffsetEXT(get_device().get_handle(), uniform_binding_descriptor.layout, 0u, &uniform_binding_descriptor.offset);
|
||||
vkGetDescriptorSetLayoutBindingOffsetEXT(get_device().get_handle(), image_binding_descriptor.layout, 0u, &image_binding_descriptor.offset);
|
||||
----
|
||||
|
||||
Creating the resource descriptors for the uniform buffers using the `VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT` usage flag:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
uniform_binding_descriptor.buffer = std::make_unique<vkb::core::Buffer>(get_device(),
|
||||
(static_cast<uint32_t>(cubes.size()) + 1) * uniform_binding_descriptor.size,
|
||||
VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||
----
|
||||
|
||||
Creating the combined image sampler descriptors by additionally adding the `VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT` usage flag:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
image_binding_descriptor.buffer = std::make_unique<vkb::core::Buffer>(get_device(),
|
||||
static_cast<uint32_t>(cubes.size()) * image_binding_descriptor.size,
|
||||
VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||
----
|
||||
|
||||
=== Putting the descriptors into the buffers
|
||||
|
||||
After creating the appropriate buffers we now put the actual descriptors into those buffers, making them accessible to the GPU.
|
||||
This is done with the `vkGetDescriptorEXT` function.
|
||||
|
||||
The sample uses one global uniform buffer that stores the scene matrices, one uniform buffer per object displayed and one combined image sampler per object.
|
||||
|
||||
For resource descriptor buffers, we can simply put buffer device addresses into it.
|
||||
No need for descriptors, as the GPU only needs to know the address of the buffers to access them:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
char *uniform_descriptor_buf_ptr = (char *) resource_descriptor_buffer->get_data();
|
||||
|
||||
// Global matrices uniform buffer
|
||||
VkDescriptorAddressInfoEXT addr_info = {VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT};
|
||||
addr_info.address = uniform_buffers.scene->get_device_address();
|
||||
addr_info.range = uniform_buffers.scene->get_size();
|
||||
addr_info.format = VK_FORMAT_UNDEFINED;
|
||||
|
||||
VkDescriptorGetInfoEXT buffer_descriptor_info{VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT};
|
||||
buffer_descriptor_info.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
buffer_descriptor_info.data.pUniformBuffer = &addr_info;
|
||||
vkGetDescriptorEXT(get_device().get_handle(), &buffer_descriptor_info, descriptor_buffer_properties.uniformBufferDescriptorSize, uniform_descriptor_buf_ptr);
|
||||
|
||||
// Per-cube uniform buffers
|
||||
// We use pointers to offset and align the data we put into the descriptor buffers
|
||||
for (size_t i = 0; i < cubes.size(); i++)
|
||||
{
|
||||
VkDescriptorAddressInfoEXT cube_addr_info = {VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT};
|
||||
cube_addr_info.address = cubes[i].uniform_buffer->get_device_address();
|
||||
cube_addr_info.range = cubes[i].uniform_buffer->get_size();
|
||||
cube_addr_info.format = VK_FORMAT_UNDEFINED;
|
||||
|
||||
buffer_descriptor_info.data.pUniformBuffer = &cube_addr_info;
|
||||
vkGetDescriptorEXT(get_device().get_handle(), &buffer_descriptor_info, descriptor_buffer_properties.uniformBufferDescriptorSize, uniform_descriptor_buf_ptr + (i + 1) * uniform_binding_descriptor.size + uniform_binding_descriptor.offset);
|
||||
}
|
||||
----
|
||||
|
||||
For combined image samplers (or samplers alone) we can't use buffer device addresses as the implementation needs more information, so we have to put actual descriptors into the buffer instead:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
// For combined images we need to put descriptors into the descriptor buffers
|
||||
// We use pointers to offset and align the data we put into the descriptor buffers
|
||||
char *image_descriptor_buf_ptr = (char *) image_binding_descriptor.buffer->get_data();
|
||||
for (size_t i = 0; i < cubes.size(); i++)
|
||||
{
|
||||
VkDescriptorImageInfo image_descriptor = create_descriptor(cubes[i].texture);
|
||||
|
||||
VkDescriptorGetInfoEXT image_descriptor_info{VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT};
|
||||
image_descriptor_info.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
image_descriptor_info.data.pCombinedImageSampler = &image_descriptor;
|
||||
vkGetDescriptorEXT(get_device().get_handle(), &image_descriptor_info, descriptor_buffer_properties.combinedImageSamplerDescriptorSize, image_descriptor_buf_ptr + i * image_binding_descriptor.size + image_binding_descriptor.offset);
|
||||
}
|
||||
----
|
||||
|
||||
=== Binding the buffers
|
||||
|
||||
As noted earlier, we no longer bind descriptor sets using `vkCmdBindDescriptorSets` but instead use `vkCmdBindDescriptorBuffersEXT` to bind the (resource) descriptor buffers and then use `vkCmdSetDescriptorBufferOffsetsEXT` to index into that buffer for the next draw:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
// Descriptor buffer bindings
|
||||
// Binding 0 = uniform buffer
|
||||
VkDescriptorBufferBindingInfoEXT descriptor_buffer_binding_info[2]{};
|
||||
descriptor_buffer_binding_info[0].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT;
|
||||
descriptor_buffer_binding_info[0].address = resource_descriptor_buffer->get_device_address();
|
||||
descriptor_buffer_binding_info[0].usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT;
|
||||
// Binding 1 = Image
|
||||
descriptor_buffer_binding_info[1].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT;
|
||||
descriptor_buffer_binding_info[1].address = image_descriptor_buffer->get_device_address();
|
||||
descriptor_buffer_binding_info[1].usage = VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT;
|
||||
vkCmdBindDescriptorBuffersEXT(draw_cmd_buffers[i], 2, descriptor_buffer_binding_info);
|
||||
|
||||
uint32_t buffer_index_ubo = 0;
|
||||
uint32_t buffer_index_image = 1;
|
||||
|
||||
// Global Matrices (set 0)
|
||||
vkCmdSetDescriptorBufferOffsetsEXT(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &buffer_index_ubo, &buffer_offset);
|
||||
|
||||
// Set and offset into descriptor for each model
|
||||
for (size_t j = 0; j < cubes.size(); j++)
|
||||
{
|
||||
// Uniform buffer (set 1)
|
||||
// Model ubos start at offset * (j + 1) (+1 as slot 0 is global matrices)
|
||||
buffer_offset = (j + 1) * uniform_binding_descriptor.size;
|
||||
vkCmdSetDescriptorBufferOffsetsEXT(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 1, 1, &buffer_index_ubo, &buffer_offset);
|
||||
// Image (set 2)
|
||||
buffer_offset = j * image_binding_descriptor.size;
|
||||
vkCmdSetDescriptorBufferOffsetsEXT(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 2, 1, &buffer_index_image, &buffer_offset);
|
||||
draw_model(models.cube, draw_cmd_buffers[i]);
|
||||
}
|
||||
----
|
||||
|
||||
In detail and in reference to our shader interface:
|
||||
|
||||
Earlier on, we did put the device address for the global matrices uniform buffer at the beginning to the resource descriptor buffer.
|
||||
So we set it to point at `buffer_offset = 0` for set 0:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
vkCmdSetDescriptorBufferOffsetsEXT(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &buffer_index_ubo, &buffer_offset);
|
||||
----
|
||||
|
||||
We then loop through all cubes displayed in the example and let the descriptor buffer point at the next device address using the alignment of the implementation for set 1:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
vkCmdSetDescriptorBufferOffsetsEXT(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 1, 1, &buffer_index_ubo, &buffer_offset);
|
||||
----
|
||||
|
||||
With an alignment of 16 (see `VkPhysicalDeviceDescriptorBufferPropertiesEXT`) the device address for the uniform buffer for the first cube would start at byte 16 in the resource descriptor buffer, the device address for the second cube's uniform buffer would start at byte 32.
|
||||
|
||||
The descriptor buffer containing the descriptors for our combined image samples is bound to set 2:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
vkCmdSetDescriptorBufferOffsetsEXT(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 2, 1, &buffer_index_image, &buffer_offset);
|
||||
----
|
||||
|
||||
== What about the shaders?
|
||||
|
||||
With descriptor set and pipeline layouts, Vulkan decouples the shader interfaces from the application.
|
||||
And since we don't change these but only the way how we provide descriptors to the GPU, *no changes to the shaders are required*.
|
||||
@@ -0,0 +1,486 @@
|
||||
/* Copyright (c) 2024-2025, Sascha Willems
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 the "License";
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Using descriptor buffers replacing descriptor sets with VK_EXT_descriptor_buffer
|
||||
* This render multiple cubes passing uniform buffers and combined image samples to the GPU using descriptor buffers instead of descriptor sets
|
||||
* This allows for a more bindless design
|
||||
*/
|
||||
|
||||
#include "descriptor_buffer_basic.h"
|
||||
|
||||
#include "core/buffer.h"
|
||||
#include "scene_graph/components/sub_mesh.h"
|
||||
|
||||
DescriptorBufferBasic::DescriptorBufferBasic()
|
||||
{
|
||||
title = "Descriptor buffers";
|
||||
|
||||
set_api_version(VK_API_VERSION_1_1);
|
||||
|
||||
// Enable extension required for descriptor buffers
|
||||
add_device_extension(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
|
||||
add_device_extension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
|
||||
add_device_extension(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
|
||||
|
||||
add_device_extension(VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
DescriptorBufferBasic::~DescriptorBufferBasic()
|
||||
{
|
||||
if (has_device())
|
||||
{
|
||||
vkDestroyPipeline(get_device().get_handle(), pipeline, nullptr);
|
||||
vkDestroyPipelineLayout(get_device().get_handle(), pipeline_layout, nullptr);
|
||||
vkDestroyDescriptorSetLayout(get_device().get_handle(), uniform_binding_descriptor.layout, nullptr);
|
||||
vkDestroyDescriptorSetLayout(get_device().get_handle(), image_binding_descriptor.layout, nullptr);
|
||||
for (auto &cube : cubes)
|
||||
{
|
||||
cube.uniform_buffer.reset();
|
||||
cube.texture.image.reset();
|
||||
vkDestroySampler(get_device().get_handle(), cube.texture.sampler, nullptr);
|
||||
}
|
||||
uniform_buffers.scene.reset();
|
||||
uniform_binding_descriptor.buffer.reset();
|
||||
image_binding_descriptor.buffer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorBufferBasic::request_gpu_features(vkb::PhysicalDevice &gpu)
|
||||
{
|
||||
// Enable anisotropic filtering if supported
|
||||
if (gpu.get_features().samplerAnisotropy)
|
||||
{
|
||||
gpu.get_mutable_requested_features().samplerAnisotropy = VK_TRUE;
|
||||
}
|
||||
|
||||
// Enable features required for this example
|
||||
|
||||
// We need device addresses for buffers in certain places
|
||||
REQUEST_REQUIRED_FEATURE(gpu,
|
||||
VkPhysicalDeviceBufferDeviceAddressFeatures,
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES,
|
||||
bufferDeviceAddress);
|
||||
|
||||
// We need to enable the descriptor buffer feature of the VK_EXT_descriptor_buffer extension
|
||||
REQUEST_REQUIRED_FEATURE(gpu,
|
||||
VkPhysicalDeviceDescriptorBufferFeaturesEXT,
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT,
|
||||
descriptorBuffer);
|
||||
}
|
||||
|
||||
void DescriptorBufferBasic::build_command_buffers()
|
||||
{
|
||||
VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info();
|
||||
|
||||
VkClearValue clear_values[2];
|
||||
clear_values[0].color = default_clear_color;
|
||||
clear_values[1].depthStencil = {0.0f, 0};
|
||||
|
||||
VkRenderPassBeginInfo render_pass_begin_info = vkb::initializers::render_pass_begin_info();
|
||||
render_pass_begin_info.renderPass = render_pass;
|
||||
render_pass_begin_info.renderArea.offset.x = 0;
|
||||
render_pass_begin_info.renderArea.offset.y = 0;
|
||||
render_pass_begin_info.renderArea.extent.width = width;
|
||||
render_pass_begin_info.renderArea.extent.height = height;
|
||||
render_pass_begin_info.clearValueCount = 2;
|
||||
render_pass_begin_info.pClearValues = clear_values;
|
||||
|
||||
for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i)
|
||||
{
|
||||
render_pass_begin_info.framebuffer = framebuffers[i];
|
||||
|
||||
VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info));
|
||||
|
||||
vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
|
||||
VkViewport viewport = vkb::initializers::viewport(static_cast<float>(width), static_cast<float>(height), 0.0f, 1.0f);
|
||||
vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport);
|
||||
|
||||
VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0);
|
||||
vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor);
|
||||
|
||||
const auto &vertex_buffer = models.cube->vertex_buffers.at("vertex_buffer");
|
||||
auto &index_buffer = models.cube->index_buffer;
|
||||
|
||||
VkDeviceSize offsets[1] = {0};
|
||||
vkCmdBindVertexBuffers(draw_cmd_buffers[i], 0, 1, vertex_buffer.get(), offsets);
|
||||
vkCmdBindIndexBuffer(draw_cmd_buffers[i], index_buffer->get_handle(), 0, models.cube->index_type);
|
||||
|
||||
// Descriptor buffer bindings
|
||||
// Binding 0 = uniform buffer
|
||||
VkDescriptorBufferBindingInfoEXT descriptor_buffer_binding_info[2]{};
|
||||
descriptor_buffer_binding_info[0].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT;
|
||||
descriptor_buffer_binding_info[0].address = uniform_binding_descriptor.buffer->get_device_address();
|
||||
descriptor_buffer_binding_info[0].usage = VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT;
|
||||
// Binding 1 = Image
|
||||
descriptor_buffer_binding_info[1].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_BUFFER_BINDING_INFO_EXT;
|
||||
descriptor_buffer_binding_info[1].address = image_binding_descriptor.buffer->get_device_address();
|
||||
descriptor_buffer_binding_info[1].usage = VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT;
|
||||
vkCmdBindDescriptorBuffersEXT(draw_cmd_buffers[i], 2, descriptor_buffer_binding_info);
|
||||
|
||||
uint32_t buffer_index_ubo = 0;
|
||||
uint32_t buffer_index_image = 1;
|
||||
VkDeviceSize buffer_offset = 0;
|
||||
|
||||
// Global Matrices (set 0)
|
||||
vkCmdSetDescriptorBufferOffsetsEXT(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &buffer_index_ubo, &buffer_offset);
|
||||
|
||||
// Set an offset into descriptor for each model
|
||||
for (size_t j = 0; j < cubes.size(); j++)
|
||||
{
|
||||
// Uniform buffer (set 1)
|
||||
// Model ubos start at offset * (j + 1) (+1 as slot 0 is global matrices)
|
||||
buffer_offset = (j + 1) * uniform_binding_descriptor.size;
|
||||
vkCmdSetDescriptorBufferOffsetsEXT(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 1, 1, &buffer_index_ubo, &buffer_offset);
|
||||
// Image (set 2)
|
||||
buffer_offset = j * image_binding_descriptor.size;
|
||||
vkCmdSetDescriptorBufferOffsetsEXT(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 2, 1, &buffer_index_image, &buffer_offset);
|
||||
draw_model(models.cube, draw_cmd_buffers[i]);
|
||||
}
|
||||
|
||||
draw_ui(draw_cmd_buffers[i]);
|
||||
|
||||
vkCmdEndRenderPass(draw_cmd_buffers[i]);
|
||||
|
||||
VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorBufferBasic::load_assets()
|
||||
{
|
||||
models.cube = load_model("scenes/textured_unit_cube.gltf");
|
||||
cubes[0].texture = load_texture("textures/crate01_color_height_rgba.ktx", vkb::sg::Image::Color);
|
||||
cubes[1].texture = load_texture("textures/crate02_color_height_rgba.ktx", vkb::sg::Image::Color);
|
||||
}
|
||||
|
||||
inline VkDeviceSize aligned_size(VkDeviceSize value, VkDeviceSize alignment)
|
||||
{
|
||||
return (value + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
void DescriptorBufferBasic::setup_descriptor_set_layout()
|
||||
{
|
||||
// Using descriptor buffers still requires the creation of descriptor set layouts
|
||||
|
||||
VkDescriptorSetLayoutBinding set_layout_binding{};
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo descriptor_layout_create_info{};
|
||||
descriptor_layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
descriptor_layout_create_info.bindingCount = 1;
|
||||
descriptor_layout_create_info.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
|
||||
descriptor_layout_create_info.pBindings = &set_layout_binding;
|
||||
|
||||
// Create a layout for uniform buffers
|
||||
set_layout_binding = vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0);
|
||||
VK_CHECK(vkCreateDescriptorSetLayout(get_device().get_handle(), &descriptor_layout_create_info, nullptr, &uniform_binding_descriptor.layout));
|
||||
|
||||
// Create a layout for combined image samplers
|
||||
set_layout_binding = vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0);
|
||||
VK_CHECK(vkCreateDescriptorSetLayout(get_device().get_handle(), &descriptor_layout_create_info, nullptr, &image_binding_descriptor.layout));
|
||||
|
||||
// Create a pipeline layout using set 0 = Camera UBO, set 1 = Model UBO and set 2 = Model combined image
|
||||
const std::array<VkDescriptorSetLayout, 3> descriptor_set_layouts = {uniform_binding_descriptor.layout, uniform_binding_descriptor.layout, image_binding_descriptor.layout};
|
||||
|
||||
VkPipelineLayoutCreateInfo pipeline_layout_create_info = vkb::initializers::pipeline_layout_create_info(descriptor_set_layouts.data(), static_cast<uint32_t>(descriptor_set_layouts.size()));
|
||||
VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_create_info, nullptr, &pipeline_layout));
|
||||
|
||||
// Get set layout descriptor sizes.
|
||||
vkGetDescriptorSetLayoutSizeEXT(get_device().get_handle(), uniform_binding_descriptor.layout, &uniform_binding_descriptor.size);
|
||||
vkGetDescriptorSetLayoutSizeEXT(get_device().get_handle(), image_binding_descriptor.layout, &image_binding_descriptor.size);
|
||||
|
||||
// Adjust set layout sizes to alignment.
|
||||
uniform_binding_descriptor.size = aligned_size(uniform_binding_descriptor.size, descriptor_buffer_properties.descriptorBufferOffsetAlignment);
|
||||
image_binding_descriptor.size = aligned_size(image_binding_descriptor.size, descriptor_buffer_properties.descriptorBufferOffsetAlignment);
|
||||
|
||||
// Get descriptor bindings offsets as descriptors are placed inside set layout by those offsets.
|
||||
vkGetDescriptorSetLayoutBindingOffsetEXT(get_device().get_handle(), uniform_binding_descriptor.layout, 0u, &uniform_binding_descriptor.offset);
|
||||
vkGetDescriptorSetLayoutBindingOffsetEXT(get_device().get_handle(), image_binding_descriptor.layout, 0u, &image_binding_descriptor.offset);
|
||||
}
|
||||
|
||||
void DescriptorBufferBasic::prepare_pipelines()
|
||||
{
|
||||
VkPipelineInputAssemblyStateCreateInfo input_assembly_state =
|
||||
vkb::initializers::pipeline_input_assembly_state_create_info(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterization_state =
|
||||
vkb::initializers::pipeline_rasterization_state_create_info(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_CLOCKWISE, 0);
|
||||
|
||||
VkPipelineColorBlendAttachmentState blend_attachment_state =
|
||||
vkb::initializers::pipeline_color_blend_attachment_state(0xf, VK_FALSE);
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo color_blend_state =
|
||||
vkb::initializers::pipeline_color_blend_state_create_info(1, &blend_attachment_state);
|
||||
|
||||
// Note: Using reversed depth-buffer for increased precision, so Greater depth values are kept
|
||||
VkPipelineDepthStencilStateCreateInfo depth_stencil_state =
|
||||
vkb::initializers::pipeline_depth_stencil_state_create_info(VK_TRUE, VK_TRUE, VK_COMPARE_OP_GREATER);
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewport_state =
|
||||
vkb::initializers::pipeline_viewport_state_create_info(1, 1, 0);
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisample_state =
|
||||
vkb::initializers::pipeline_multisample_state_create_info(VK_SAMPLE_COUNT_1_BIT, 0);
|
||||
|
||||
std::vector<VkDynamicState> dynamic_state_enables = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
|
||||
|
||||
VkPipelineDynamicStateCreateInfo dynamic_state =
|
||||
vkb::initializers::pipeline_dynamic_state_create_info(dynamic_state_enables);
|
||||
|
||||
// Vertex bindings and attributes
|
||||
const std::vector<VkVertexInputBindingDescription> vertex_input_bindings = {
|
||||
vkb::initializers::vertex_input_binding_description(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX),
|
||||
};
|
||||
const std::vector<VkVertexInputAttributeDescription> vertex_input_attributes = {
|
||||
vkb::initializers::vertex_input_attribute_description(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), // Location 0: Position
|
||||
vkb::initializers::vertex_input_attribute_description(0, 1, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 3), // Location 1: Normal
|
||||
vkb::initializers::vertex_input_attribute_description(0, 2, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 6), // Location 2: UV
|
||||
};
|
||||
VkPipelineVertexInputStateCreateInfo vertex_input_state = vkb::initializers::pipeline_vertex_input_state_create_info();
|
||||
vertex_input_state.vertexBindingDescriptionCount = static_cast<uint32_t>(vertex_input_bindings.size());
|
||||
vertex_input_state.pVertexBindingDescriptions = vertex_input_bindings.data();
|
||||
vertex_input_state.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertex_input_attributes.size());
|
||||
vertex_input_state.pVertexAttributeDescriptions = vertex_input_attributes.data();
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipeline_create_info = vkb::initializers::pipeline_create_info(pipeline_layout, render_pass, 0);
|
||||
pipeline_create_info.pVertexInputState = &vertex_input_state;
|
||||
pipeline_create_info.pInputAssemblyState = &input_assembly_state;
|
||||
pipeline_create_info.pRasterizationState = &rasterization_state;
|
||||
pipeline_create_info.pColorBlendState = &color_blend_state;
|
||||
pipeline_create_info.pMultisampleState = &multisample_state;
|
||||
pipeline_create_info.pViewportState = &viewport_state;
|
||||
pipeline_create_info.pDepthStencilState = &depth_stencil_state;
|
||||
pipeline_create_info.pDynamicState = &dynamic_state;
|
||||
|
||||
// We need to set this flag to let the implementation know that this pipeline uses descriptor buffers
|
||||
pipeline_create_info.flags = VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT;
|
||||
|
||||
const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages = {
|
||||
load_shader("descriptor_buffer_basic", "cube.vert.spv", VK_SHADER_STAGE_VERTEX_BIT),
|
||||
load_shader("descriptor_buffer_basic", "cube.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT)};
|
||||
|
||||
pipeline_create_info.stageCount = static_cast<uint32_t>(shader_stages.size());
|
||||
pipeline_create_info.pStages = shader_stages.data();
|
||||
|
||||
VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info, nullptr, &pipeline));
|
||||
}
|
||||
|
||||
// This function creates the descriptor buffers and puts the descriptors into those buffers, so they can be used during command buffer creation
|
||||
void DescriptorBufferBasic::prepare_descriptor_buffer()
|
||||
{
|
||||
// This buffer will contain resource descriptors for all the uniform buffers (one per cube and one with global matrices)
|
||||
uniform_binding_descriptor.buffer = std::make_unique<vkb::core::BufferC>(get_device(),
|
||||
(static_cast<uint32_t>(cubes.size()) + 1) * uniform_binding_descriptor.size,
|
||||
VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||
|
||||
// Samplers and combined images need to be stored in a separate buffer due to different flags (VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT) (one image per cube)
|
||||
image_binding_descriptor.buffer = std::make_unique<vkb::core::BufferC>(get_device(),
|
||||
static_cast<uint32_t>(cubes.size()) * image_binding_descriptor.size,
|
||||
VK_BUFFER_USAGE_RESOURCE_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SAMPLER_DESCRIPTOR_BUFFER_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||
|
||||
// Put the descriptors into the above buffers
|
||||
|
||||
// For combined images we need to put descriptors into the descriptor buffers
|
||||
// We use pointers to offset and align the data we put into the descriptor buffers
|
||||
char *image_descriptor_buf_ptr = (char *) image_binding_descriptor.buffer->get_data();
|
||||
for (size_t i = 0; i < cubes.size(); i++)
|
||||
{
|
||||
VkDescriptorImageInfo image_descriptor = create_descriptor(cubes[i].texture);
|
||||
|
||||
VkDescriptorGetInfoEXT image_descriptor_info{VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT};
|
||||
image_descriptor_info.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
image_descriptor_info.data.pCombinedImageSampler = &image_descriptor;
|
||||
|
||||
// Note: we can just write combined image sampler descriptors back-to-back in the buffer here
|
||||
// regardless of whether descriptor_buffer_properties.combinedImageSamplerDescriptorSingleArray is true or
|
||||
// false because these aren't an array of descriptors, just individual descriptors in the same buffer.
|
||||
// If these were actually an array (descriptor count > 1) then we would have to check the
|
||||
// combinedImageSamplerDescriptorSingleArray property and separate the image descriptor part from the
|
||||
// sampler descriptor part. We would place all the image descriptors first, followed by all the samplers.
|
||||
|
||||
vkGetDescriptorEXT(get_device().get_handle(), &image_descriptor_info, descriptor_buffer_properties.combinedImageSamplerDescriptorSize, image_descriptor_buf_ptr + i * image_binding_descriptor.size + image_binding_descriptor.offset);
|
||||
}
|
||||
|
||||
// For uniform buffers we only need to put their buffer device addresses into the descriptor buffers
|
||||
char *uniform_descriptor_buf_ptr = (char *) uniform_binding_descriptor.buffer->get_data();
|
||||
|
||||
// Global matrices uniform buffer
|
||||
VkDescriptorAddressInfoEXT addr_info = {VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT};
|
||||
addr_info.address = uniform_buffers.scene->get_device_address();
|
||||
addr_info.range = uniform_buffers.scene->get_size();
|
||||
addr_info.format = VK_FORMAT_UNDEFINED;
|
||||
|
||||
VkDescriptorGetInfoEXT buffer_descriptor_info{VK_STRUCTURE_TYPE_DESCRIPTOR_GET_INFO_EXT};
|
||||
buffer_descriptor_info.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
buffer_descriptor_info.data.pUniformBuffer = &addr_info;
|
||||
vkGetDescriptorEXT(get_device().get_handle(), &buffer_descriptor_info, descriptor_buffer_properties.uniformBufferDescriptorSize, uniform_descriptor_buf_ptr);
|
||||
|
||||
// Per-cube uniform buffers
|
||||
// We use pointers to offset and align the data we put into the descriptor buffers
|
||||
for (size_t i = 0; i < cubes.size(); i++)
|
||||
{
|
||||
VkDescriptorAddressInfoEXT cube_addr_info = {VK_STRUCTURE_TYPE_DESCRIPTOR_ADDRESS_INFO_EXT};
|
||||
cube_addr_info.address = cubes[i].uniform_buffer->get_device_address();
|
||||
cube_addr_info.range = cubes[i].uniform_buffer->get_size();
|
||||
cube_addr_info.format = VK_FORMAT_UNDEFINED;
|
||||
|
||||
buffer_descriptor_info.data.pUniformBuffer = &cube_addr_info;
|
||||
vkGetDescriptorEXT(get_device().get_handle(), &buffer_descriptor_info, descriptor_buffer_properties.uniformBufferDescriptorSize, uniform_descriptor_buf_ptr + (i + 1) * uniform_binding_descriptor.size + uniform_binding_descriptor.offset);
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorBufferBasic::prepare_uniform_buffers()
|
||||
{
|
||||
// Vertex shader scene uniform buffer block
|
||||
uniform_buffers.scene = std::make_unique<vkb::core::BufferC>(get_device(),
|
||||
sizeof(UboScene),
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||
|
||||
// Vertex shader cube model uniform buffer blocks
|
||||
for (auto &cube : cubes)
|
||||
{
|
||||
cube.uniform_buffer = std::make_unique<vkb::core::BufferC>(get_device(),
|
||||
sizeof(glm::mat4),
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||
}
|
||||
|
||||
update_uniform_buffers();
|
||||
update_cube_uniform_buffers(0.0f);
|
||||
}
|
||||
|
||||
void DescriptorBufferBasic::update_uniform_buffers()
|
||||
{
|
||||
ubo_scene.projection = camera.matrices.perspective;
|
||||
ubo_scene.view = camera.matrices.view;
|
||||
uniform_buffers.scene->convert_and_update(ubo_scene);
|
||||
}
|
||||
|
||||
void DescriptorBufferBasic::update_cube_uniform_buffers(float delta_time)
|
||||
{
|
||||
cubes[0].model_mat = glm::translate(glm::mat4(1.0f), glm::vec3(-2.0f, 0.0f, 0.0f));
|
||||
cubes[1].model_mat = glm::translate(glm::mat4(1.0f), glm::vec3(1.5f, 0.5f, 0.0f));
|
||||
|
||||
for (auto &cube : cubes)
|
||||
{
|
||||
cube.model_mat = glm::rotate(cube.model_mat, glm::radians(cube.rotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
cube.model_mat = glm::rotate(cube.model_mat, glm::radians(cube.rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
cube.model_mat = glm::rotate(cube.model_mat, glm::radians(cube.rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
cube.uniform_buffer->convert_and_update(cube.model_mat);
|
||||
}
|
||||
|
||||
if (animate)
|
||||
{
|
||||
cubes[0].rotation.x += 2.5f * delta_time;
|
||||
if (cubes[0].rotation.x > 360.0f)
|
||||
{
|
||||
cubes[0].rotation.x -= 360.0f;
|
||||
}
|
||||
cubes[1].rotation.y += 2.0f * delta_time;
|
||||
if (cubes[1].rotation.y > 360.0f)
|
||||
{
|
||||
cubes[1].rotation.y -= 360.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorBufferBasic::draw()
|
||||
{
|
||||
ApiVulkanSample::prepare_frame();
|
||||
submit_info.commandBufferCount = 1;
|
||||
submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer];
|
||||
VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE));
|
||||
ApiVulkanSample::submit_frame();
|
||||
}
|
||||
|
||||
bool DescriptorBufferBasic::prepare(const vkb::ApplicationOptions &options)
|
||||
{
|
||||
if (!ApiVulkanSample::prepare(options))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
Extension specific functions
|
||||
*/
|
||||
|
||||
VkPhysicalDeviceProperties2KHR device_properties{};
|
||||
descriptor_buffer_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT;
|
||||
device_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
|
||||
device_properties.pNext = &descriptor_buffer_properties;
|
||||
vkGetPhysicalDeviceProperties2KHR(get_device().get_gpu().get_handle(), &device_properties);
|
||||
|
||||
if (descriptor_buffer_properties.maxResourceDescriptorBufferBindings < 2)
|
||||
{
|
||||
LOGE("VkPhysicalDeviceDescriptorBufferPropertiesEXT.maxResourceDescriptorBufferBindings={}. Sample requires at least 2 to run.", descriptor_buffer_properties.maxResourceDescriptorBufferBindings);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
End of extension specific functions
|
||||
*/
|
||||
|
||||
// Note: Using reversed depth-buffer for increased precision, so Znear and Zfar are flipped
|
||||
camera.type = vkb::CameraType::LookAt;
|
||||
camera.set_perspective(60.0f, static_cast<float>(width) / static_cast<float>(height), 512.0f, 0.1f);
|
||||
camera.set_rotation(glm::vec3(0.0f, 0.0f, 0.0f));
|
||||
camera.set_translation(glm::vec3(0.0f, 0.0f, -5.0f));
|
||||
|
||||
load_assets();
|
||||
prepare_uniform_buffers();
|
||||
setup_descriptor_set_layout();
|
||||
prepare_descriptor_buffer();
|
||||
prepare_pipelines();
|
||||
build_command_buffers();
|
||||
prepared = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DescriptorBufferBasic::render(float delta_time)
|
||||
{
|
||||
if (!prepared)
|
||||
{
|
||||
return;
|
||||
}
|
||||
draw();
|
||||
if (animate)
|
||||
{
|
||||
update_cube_uniform_buffers(delta_time);
|
||||
}
|
||||
if (camera.updated)
|
||||
{
|
||||
update_uniform_buffers();
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorBufferBasic::on_update_ui_overlay(vkb::Drawer &drawer)
|
||||
{
|
||||
if (drawer.header("Settings"))
|
||||
{
|
||||
drawer.checkbox("Animate", &animate);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<vkb::VulkanSampleC> create_descriptor_buffer_basic()
|
||||
{
|
||||
return std::make_unique<DescriptorBufferBasic>();
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/* Copyright (c) 2023-2024, Sascha Willems
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 the "License";
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Using descriptor buffers replacing descriptor sets with VK_EXT_descriptor_buffer
|
||||
* This render multiple cubes passing uniform buffers and combined image samples to the GPU using descriptor buffers instead of descriptor sets
|
||||
* This allows for a more bindless design
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api_vulkan_sample.h"
|
||||
|
||||
class DescriptorBufferBasic : public ApiVulkanSample
|
||||
{
|
||||
public:
|
||||
bool animate = true;
|
||||
|
||||
VkPhysicalDeviceDescriptorBufferPropertiesEXT descriptor_buffer_properties{};
|
||||
|
||||
struct DescriptorData
|
||||
{
|
||||
VkDescriptorSetLayout layout{VK_NULL_HANDLE};
|
||||
std::unique_ptr<vkb::core::BufferC> buffer;
|
||||
VkDeviceSize size;
|
||||
VkDeviceSize offset;
|
||||
};
|
||||
DescriptorData uniform_binding_descriptor;
|
||||
DescriptorData image_binding_descriptor;
|
||||
|
||||
struct Cube
|
||||
{
|
||||
Texture texture;
|
||||
std::unique_ptr<vkb::core::BufferC> uniform_buffer;
|
||||
glm::vec3 rotation;
|
||||
glm::mat4 model_mat;
|
||||
};
|
||||
std::array<Cube, 2> cubes;
|
||||
|
||||
struct Models
|
||||
{
|
||||
std::unique_ptr<vkb::sg::SubMesh> cube;
|
||||
} models;
|
||||
|
||||
struct UniformBuffers
|
||||
{
|
||||
std::unique_ptr<vkb::core::BufferC> scene;
|
||||
} uniform_buffers;
|
||||
|
||||
struct UboScene
|
||||
{
|
||||
glm::mat4 projection;
|
||||
glm::mat4 view;
|
||||
} ubo_scene;
|
||||
|
||||
VkPipeline pipeline{VK_NULL_HANDLE};
|
||||
VkPipelineLayout pipeline_layout{VK_NULL_HANDLE};
|
||||
|
||||
DescriptorBufferBasic();
|
||||
~DescriptorBufferBasic() override;
|
||||
virtual void request_gpu_features(vkb::PhysicalDevice &gpu) override;
|
||||
void build_command_buffers() override;
|
||||
void load_assets();
|
||||
void setup_descriptor_set_layout();
|
||||
void prepare_pipelines();
|
||||
void prepare_descriptor_buffer();
|
||||
void prepare_uniform_buffers();
|
||||
void update_uniform_buffers();
|
||||
void update_cube_uniform_buffers(float delta_time);
|
||||
void draw();
|
||||
bool prepare(const vkb::ApplicationOptions &options) override;
|
||||
void render(float delta_time) override;
|
||||
void on_update_ui_overlay(vkb::Drawer &drawer) override;
|
||||
};
|
||||
|
||||
std::unique_ptr<vkb::VulkanSampleC> create_descriptor_buffer_basic();
|
||||
Reference in New Issue
Block a user