This commit is contained in:
xsl
2025-09-04 10:54:47 +08:00
commit 6bc8f61b18
1808 changed files with 208268 additions and 0 deletions
@@ -0,0 +1,33 @@
# Copyright (c) 2022-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(
ID ${FOLDER_NAME}
CATEGORY ${CATEGORY_NAME}
AUTHOR "Sascha Willems"
NAME "Conditional rendering"
DESCRIPTION "Demonstrates usage of VK_EXT_conditional_rendering for conditionally toggling the visibility of sub-meshes of a complex glTF model."
SHADER_FILES_GLSL
"conditional_rendering/glsl/model.vert"
"conditional_rendering/glsl/model.frag"
SHADER_FILES_HLSL
"conditional_rendering/hlsl/model.vert.hlsl"
"conditional_rendering/hlsl/model.frag.hlsl")
@@ -0,0 +1,171 @@
////
- Copyright (c) 2022-2023, 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.
-
////
= Conditional rendering
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/conditional_rendering[Khronos Vulkan samples github repository].
endif::[]
:pp: {plus}{plus}
image::./images/sample.png[Sample]
== Overview
The https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_EXT_conditional_rendering.html[VK_EXT_conditional_rendering] extension allows the execution of rendering commands to be conditional based on a value taken from a dedicated conditional buffer.
This may help an application reduce the latency by conditionally discarding rendering commands without application intervention.
This sample demonstrates usage of this extension for conditionally toggling the visibility of sub-meshes of a complex glTF model.
Instead of having to update command buffers, this is done by updating the aforementioned buffer.
== Conditional buffer
As mentioned in the introduction a buffer is used to conditionally execute rendering and dispatch commands (for compute, which is not done in this sample).
The first step is setting up this buffer.
Important notes on setting up a conditional buffer:
* A *dedicated buffer type* named `VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT`
* The buffer format is fixed to consecutive *32-bit values*
* Offset is also aligned at 32-bits
The fixed alignment makes it easy to map this to C/C{pp} host structures:
[,cpp]
----
std::vector<int32_t> conditional_visibility_list;
----
Setting up the buffer is no different from other buffers:
[,cpp]
----
conditional_visibility_buffer =
std::make_unique<vkb::core::Buffer>(get_device(),
sizeof(int32_t) * conditional_visibility_list.size(),
VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT,
VMA_MEMORY_USAGE_CPU_TO_GPU);
----
With this we get a buffer that matches the size and layout of the host application.
For simplicity we create a host visible buffer in this sample.
Depending on the use-case a device local buffer would yield better performance but would also require a different update strategy
== Conditional execution
The extension introduces two new functions that allow you to mark regions of a command buffer for conditional execution:
[,cpp]
----
// Begins a new conditional rendering block
void vkCmdBeginConditionalRenderingEXT(VkCommandBuffer commandBuffer, const VkConditionalRenderingBeginInfoEXT* pConditionalRenderingBegin)
// Ends the current conditional rendering block
void vkCmdEndConditionalRenderingEXT(VkCommandBuffer commandBuffer)
----
Wrapping drawing and/or dispatch commands in such regions will result in them only being executed if our conditional buffer contains a non-zero value at the given offset.
A basic example of this could look like this:
[,cpp]
----
VkConditionalRenderingBeginInfoEXT conditional_rendering_info{};
conditional_rendering_info.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
conditional_rendering_info.buffer = conditional_buffer.buffer;
conditional_rendering_info.offset = current_mesh_index * sizeof(int32_t);
vkCmdBeginConditionalRenderingEXT(command_buffer, &conditional_rendering_info);
vkCmdDrawIndexed(...);
vkCmdEndConditionalRenderingEXT(command_buffer);
----
The conditional_rendering_info structure contains the parameters used by the `vkCmdBeginConditionalRenderingEXT` function to determine if the commands in that region are to be executed.
So for this basic example if the 32-bit conditional buffer value at the selected offset is zero, the `vkCmdDrawIndexed` will not be executed.
Changing the buffer value at the select offset 0 to 1 and synchronizing the buffer will have the draw command executed for the next draw.
Moving to the actual example we create a conditional buffer with one 32-bit value per node in the glTF scene:
[,cpp]
----
// Setup the host visilibty list
conditional_visibility_list.resize(linear_scene_nodes.size());
std::fill(conditional_visibility_list.begin(), conditional_visibility_list.end(), 1);
// Create a buffer to hold the visibility list
conditional_visibility_buffer =
std::make_unique<vkb::core::Buffer>(get_device(),
sizeof(int32_t) * conditional_visibility_list.size(),
VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT,
VMA_MEMORY_USAGE_CPU_TO_GPU);
// Copy the current visibility list to the dedicated buffer
conditional_visibility_buffer->update(conditional_visibility_list.data(), sizeof(int32_t) * conditional_visibility_list.size());
----
Using this setup, each visible glTF node maps to an entry in the conditional visibility buffer by it's unique node index, calculated as `node_index * sizeof(int32_t)`:
image::./images/conditional-buffer-mapping.png[Buffer mapping]
So we can now control draws using values stored in the conditional buffer.
To do so, the command buffer iterates over all nodes of the gltF scene (put into a linear vector for convenience) and wraps the draw command for each node in a conditional rendering block, so a node is only drawn when the visibility buffer value at it's offset equals 1:
[,cpp]
----
uint32_t node_index = 0;
for (auto &node : linear_scene_nodes)
{
glm::mat4 node_transform = node.node->get_transform().get_world_matrix();
VkDeviceSize offsets[1] = {0};
const auto &vertex_buffer_pos = node.sub_mesh->vertex_buffers.at("position");
const auto &vertex_buffer_normal = node.sub_mesh->vertex_buffers.at("normal");
auto & index_buffer = node.sub_mesh->index_buffer;
auto mat = dynamic_cast<const vkb::sg::PBRMaterial *>(node.sub_mesh->get_material());
// Start a conditional rendering block, commands in this block are only executed if the buffer at the current position is 1 at command buffer submission time
VkConditionalRenderingBeginInfoEXT conditional_rendering_info{};
conditional_rendering_info.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
conditional_rendering_info.buffer = conditional_visibility_buffer->get_handle();
// We offset into the visibility buffer based on the index of the node to be drawn
conditional_rendering_info.offset = sizeof(int32_t) * node_index;
vkCmdBeginConditionalRenderingEXT(draw_cmd_buffers[i], &conditional_rendering_info);
// Pass data for the current node via push commands
push_const_block.model_matrix = node_transform;
push_const_block.color = glm::vec4(mat->base_color_factor.rgb, 1.0f);
vkCmdPushConstants(draw_cmd_buffers[i], pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(push_const_block), &push_const_block);
vkCmdBindVertexBuffers(draw_cmd_buffers[i], 0, 1, vertex_buffer_pos.get(), offsets);
vkCmdBindVertexBuffers(draw_cmd_buffers[i], 1, 1, vertex_buffer_normal.get(), offsets);
vkCmdBindIndexBuffer(draw_cmd_buffers[i], index_buffer->get_handle(), 0, node.sub_mesh->index_type);
vkCmdDrawIndexed(draw_cmd_buffers[i], node.sub_mesh->vertex_indices, 1, 0, 0, 0);
// End the conditional rendering block
vkCmdEndConditionalRenderingEXT(draw_cmd_buffers[i]);
node_index++;
}
----
With the above command buffer setup, we can toggle visibility of each node in the glTF scene by just changing the conditional buffer value at the node's offsets.
@@ -0,0 +1,423 @@
/* Copyright (c) 2022-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 VK_EXT_conditional_rendering, which executes or discards draw commands based on values sourced from a buffer
*/
#include "conditional_rendering.h"
#include "gltf_loader.h"
#include "scene_graph/components/mesh.h"
#include "scene_graph/components/pbr_material.h"
#include "scene_graph/components/sub_mesh.h"
ConditionalRendering::ConditionalRendering()
{
title = "Conditional rendering";
add_device_extension(VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME);
}
ConditionalRendering::~ConditionalRendering()
{
if (has_device())
{
vkDestroyPipeline(get_device().get_handle(), pipeline, nullptr);
vkDestroyPipelineLayout(get_device().get_handle(), pipeline_layout, nullptr);
vkDestroyDescriptorSetLayout(get_device().get_handle(), descriptor_set_layout, nullptr);
}
}
void ConditionalRendering::request_gpu_features(vkb::PhysicalDevice &gpu)
{
// We need to enable conditional rendering using a new feature struct
REQUEST_REQUIRED_FEATURE(gpu,
VkPhysicalDeviceConditionalRenderingFeaturesEXT,
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT,
conditionalRendering);
}
void ConditionalRendering::build_command_buffers()
{
VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info();
VkClearValue clear_values[2];
clear_values[0].color = {{0.0f, 0.0f, 0.0f, 0.0f}};
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.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)
{
VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info));
render_pass_begin_info.framebuffer = framebuffers[i];
vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
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);
vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, nullptr);
uint32_t node_index = 0;
for (auto &node : linear_scene_nodes)
{
const auto &vertex_buffer_pos = node.sub_mesh->vertex_buffers.at("position");
const auto &vertex_buffer_normal = node.sub_mesh->vertex_buffers.at("normal");
auto &index_buffer = node.sub_mesh->index_buffer;
// Start a conditional rendering block, commands in this block are only executed if the buffer at the current position is 1 at command buffer submission time
VkConditionalRenderingBeginInfoEXT conditional_rendering_info{};
conditional_rendering_info.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
conditional_rendering_info.buffer = conditional_visibility_buffer->get_handle();
// We offset into the visibility buffer based on the index of the node to be drawn
conditional_rendering_info.offset = sizeof(int32_t) * node_index;
vkCmdBeginConditionalRenderingEXT(draw_cmd_buffers[i], &conditional_rendering_info);
// Pass data for the current node via push commands
auto node_material = dynamic_cast<const vkb::sg::PBRMaterial *>(node.sub_mesh->get_material());
push_const_block.model_matrix = node.node->get_transform().get_world_matrix();
push_const_block.color = glm::vec4(node_material->base_color_factor.rgb, 1.0f);
vkCmdPushConstants(draw_cmd_buffers[i], pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(push_const_block), &push_const_block);
VkDeviceSize offsets[1] = {0};
vkCmdBindVertexBuffers(draw_cmd_buffers[i], 0, 1, vertex_buffer_pos.get(), offsets);
vkCmdBindVertexBuffers(draw_cmd_buffers[i], 1, 1, vertex_buffer_normal.get(), offsets);
vkCmdBindIndexBuffer(draw_cmd_buffers[i], index_buffer->get_handle(), 0, node.sub_mesh->index_type);
vkCmdDrawIndexed(draw_cmd_buffers[i], node.sub_mesh->vertex_indices, 1, 0, 0, 0);
// End the conditional rendering block
vkCmdEndConditionalRenderingEXT(draw_cmd_buffers[i]);
node_index++;
}
draw_ui(draw_cmd_buffers[i]);
vkCmdEndRenderPass(draw_cmd_buffers[i]);
VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i]));
}
}
void ConditionalRendering::load_assets()
{
vkb::GLTFLoader loader{get_device()};
scene = loader.read_scene_from_file("scenes/Buggy/glTF-Embedded/Buggy.gltf");
assert(scene);
// Store all scene nodes in a linear vector for easier access
for (auto &mesh : scene->get_components<vkb::sg::Mesh>())
{
for (auto &node : mesh->get_nodes())
{
for (auto &sub_mesh : mesh->get_submeshes())
{
linear_scene_nodes.push_back({mesh->get_name(), node, sub_mesh});
}
}
}
// By default, all nodes should be visible, so we initialize the list with ones for each element
conditional_visibility_list.resize(linear_scene_nodes.size());
std::fill(conditional_visibility_list.begin(), conditional_visibility_list.end(), 1);
}
void ConditionalRendering::setup_descriptor_pool()
{
std::vector<VkDescriptorPoolSize> pool_sizes = {
vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 4)};
VkDescriptorPoolCreateInfo descriptor_pool_create_info =
vkb::initializers::descriptor_pool_create_info(static_cast<uint32_t>(pool_sizes.size()), pool_sizes.data(), 1);
VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool));
}
void ConditionalRendering::setup_descriptor_set_layout()
{
std::vector<VkDescriptorSetLayoutBinding> set_layout_bindings = {
vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0)};
VkDescriptorSetLayoutCreateInfo descriptor_layout_create_info =
vkb::initializers::descriptor_set_layout_create_info(set_layout_bindings.data(), static_cast<uint32_t>(set_layout_bindings.size()));
VK_CHECK(vkCreateDescriptorSetLayout(get_device().get_handle(), &descriptor_layout_create_info, nullptr, &descriptor_set_layout));
VkPipelineLayoutCreateInfo pipeline_layout_create_info =
vkb::initializers::pipeline_layout_create_info(
&descriptor_set_layout,
1);
// Pass scene node information via push constants
VkPushConstantRange push_constant_range = vkb::initializers::push_constant_range(VK_SHADER_STAGE_VERTEX_BIT, sizeof(push_const_block), 0);
pipeline_layout_create_info.pushConstantRangeCount = 1;
pipeline_layout_create_info.pPushConstantRanges = &push_constant_range;
VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_create_info, nullptr, &pipeline_layout));
}
void ConditionalRendering::setup_descriptor_sets()
{
VkDescriptorSetAllocateInfo alloc_info =
vkb::initializers::descriptor_set_allocate_info(
descriptor_pool,
&descriptor_set_layout,
1);
VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_set));
VkDescriptorBufferInfo matrix_buffer_descriptor = create_descriptor(*uniform_buffer);
std::vector<VkWriteDescriptorSet> write_descriptor_sets = {
vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &matrix_buffer_descriptor)};
vkUpdateDescriptorSets(get_device().get_handle(), static_cast<uint32_t>(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr);
}
void ConditionalRendering::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_COUNTER_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.data(),
static_cast<uint32_t>(dynamic_state_enables.size()),
0);
VkGraphicsPipelineCreateInfo pipeline_create_info =
vkb::initializers::pipeline_create_info(
pipeline_layout,
render_pass,
0);
std::vector<VkPipelineColorBlendAttachmentState> blend_attachment_states = {
vkb::initializers::pipeline_color_blend_attachment_state(0xf, VK_FALSE)};
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;
pipeline_create_info.layout = pipeline_layout;
std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages;
pipeline_create_info.stageCount = static_cast<uint32_t>(shader_stages.size());
pipeline_create_info.pStages = shader_stages.data();
// Vertex bindings an attributes for model rendering
// Binding description, we use separate buffers for the vertex attributes
std::vector<VkVertexInputBindingDescription> vertex_input_bindings = {
vkb::initializers::vertex_input_binding_description(0, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX),
vkb::initializers::vertex_input_binding_description(1, sizeof(glm::vec3), VK_VERTEX_INPUT_RATE_VERTEX),
};
// Attribute descriptions
std::vector<VkVertexInputAttributeDescription> vertex_input_attributes = {
vkb::initializers::vertex_input_attribute_description(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), // Position
vkb::initializers::vertex_input_attribute_description(1, 1, VK_FORMAT_R32G32B32_SFLOAT, 0), // Normal
};
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();
pipeline_create_info.pVertexInputState = &vertex_input_state;
shader_stages[0] = load_shader("conditional_rendering", "model.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shader_stages[1] = load_shader("conditional_rendering", "model.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info, nullptr, &pipeline));
}
// Prepare and initialize uniform buffer containing shader uniforms
void ConditionalRendering::prepare_uniform_buffers()
{
// Matrices vertex shader uniform buffer
uniform_buffer = std::make_unique<vkb::core::BufferC>(get_device(),
sizeof(uniform_data),
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VMA_MEMORY_USAGE_CPU_TO_GPU);
update_uniform_buffers();
}
void ConditionalRendering::update_uniform_buffers()
{
uniform_data.projection = camera.matrices.perspective;
// Scale the view matrix as the model is pretty large, and also flip it upside down
uniform_data.view = glm::scale(camera.matrices.view, glm::vec3(0.1f, -0.1f, 0.1f));
uniform_buffer->convert_and_update(uniform_data);
}
// Creates a dedicated buffer to store the visibility information sourced at draw time
void ConditionalRendering::prepare_visibility_buffer()
{
// Conditional values are 32 bits wide and if it's zero the rendering commands are discarded
// We therefore create a buffer that can hold int32 conditional values for all nodes in the glTF scene
// The extension also introduces the new buffer usage flag VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT that we need to set
conditional_visibility_buffer = std::make_unique<vkb::core::BufferC>(get_device(),
sizeof(int32_t) * conditional_visibility_list.size(),
VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT,
VMA_MEMORY_USAGE_CPU_TO_GPU);
update_visibility_buffer();
}
// Updates the visibility buffer with the currently selected node visibility
void ConditionalRendering::update_visibility_buffer()
{
conditional_visibility_buffer->update(conditional_visibility_list.data(), sizeof(int32_t) * conditional_visibility_list.size());
}
void ConditionalRendering::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 ConditionalRendering::prepare(const vkb::ApplicationOptions &options)
{
if (!ApiVulkanSample::prepare(options))
{
return false;
}
camera.type = vkb::CameraType::LookAt;
camera.set_position(glm::vec3(1.9f, 2.05f, -18.0f));
camera.set_rotation(glm::vec3(-11.25f, -38.0f, 0.0f));
// Note: Using reversed depth-buffer for increased precision, so Znear and Zfar are flipped
camera.set_perspective(60.0f, static_cast<float>(width) / static_cast<float>(height), 256.0f, 0.1f);
load_assets();
prepare_uniform_buffers();
prepare_visibility_buffer();
setup_descriptor_set_layout();
prepare_pipelines();
setup_descriptor_pool();
setup_descriptor_sets();
build_command_buffers();
prepared = true;
return true;
}
void ConditionalRendering::render(float delta_time)
{
if (!prepared)
{
return;
}
draw();
if (camera.updated)
{
update_uniform_buffers();
}
}
void ConditionalRendering::on_update_ui_overlay(vkb::Drawer &drawer)
{
if (drawer.header("Visibility"))
{
if (drawer.button("All"))
{
std::fill(conditional_visibility_list.begin(), conditional_visibility_list.end(), 1);
update_visibility_buffer();
}
ImGui::SameLine();
if (drawer.button("None"))
{
std::fill(conditional_visibility_list.begin(), conditional_visibility_list.end(), 0);
update_visibility_buffer();
}
ImGui::NewLine();
ImGui::BeginChild("InnerRegion", ImVec2(200.0f, 400.0f), false);
uint32_t idx = 0;
for (auto &node : linear_scene_nodes)
{
if (drawer.checkbox(("[" + std::to_string(idx) + "] " + node.name).c_str(), &conditional_visibility_list[idx]))
{
update_visibility_buffer();
}
idx++;
}
ImGui::EndChild();
}
}
bool ConditionalRendering::resize(const uint32_t width, const uint32_t height)
{
ApiVulkanSample::resize(width, height);
update_uniform_buffers();
return true;
}
std::unique_ptr<vkb::Application> create_conditional_rendering()
{
return std::make_unique<ConditionalRendering>();
}
@@ -0,0 +1,82 @@
/* 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 VK_EXT_conditional_rendering, which executes or discards draw commands based on values sourced from a buffer
*/
#pragma once
#include "api_vulkan_sample.h"
class ConditionalRendering : public ApiVulkanSample
{
public:
std::unique_ptr<vkb::core::BufferC> vertex_buffer = nullptr;
std::unique_ptr<vkb::core::BufferC> index_buffer = nullptr;
std::unique_ptr<vkb::sg::Scene> scene;
struct SceneNode
{
std::string name;
vkb::sg::Node *node;
vkb::sg::SubMesh *sub_mesh;
};
std::vector<SceneNode> linear_scene_nodes;
struct UniformData
{
glm::mat4 projection;
glm::mat4 view;
} uniform_data;
std::unique_ptr<vkb::core::BufferC> uniform_buffer;
VkPipeline pipeline;
VkPipelineLayout pipeline_layout;
VkDescriptorSet descriptor_set;
VkDescriptorSetLayout descriptor_set_layout;
struct
{
glm::mat4 model_matrix;
glm::vec4 color;
} push_const_block;
std::vector<int32_t> conditional_visibility_list;
std::unique_ptr<vkb::core::BufferC> conditional_visibility_buffer;
ConditionalRendering();
~ConditionalRendering();
virtual void request_gpu_features(vkb::PhysicalDevice &gpu) override;
void build_command_buffers() override;
void load_assets();
void setup_descriptor_pool();
void setup_descriptor_set_layout();
void setup_descriptor_sets();
void prepare_pipelines();
void prepare_uniform_buffers();
void update_uniform_buffers();
void prepare_visibility_buffer();
void update_visibility_buffer();
void draw();
bool prepare(const vkb::ApplicationOptions &options) override;
virtual void render(float delta_time) override;
virtual void on_update_ui_overlay(vkb::Drawer &drawer) override;
virtual bool resize(const uint32_t width, const uint32_t height) override;
};
std::unique_ptr<vkb::Application> create_conditional_rendering();
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB