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,35 @@
# Copyright (c) 2023-2024, Google
#
# 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 "Google"
NAME "Order-independent transparency (ordered linked lists)"
DESCRIPTION "Order-independent transparency using per-pixel ordered linked lists"
SHADER_FILES_GLSL
"oit_linked_lists/background.frag"
"oit_linked_lists/combine.frag"
"oit_linked_lists/combine.vert"
"oit_linked_lists/fullscreen.vert"
"oit_linked_lists/gather.frag"
"oit_linked_lists/gather.vert")
+89
View File
@@ -0,0 +1,89 @@
////
- Copyright (c) 2023, Google
-
- 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.
-
////
= Order-independent transparency with per-pixel ordered linked lists
ifdef::site-gen-antora[]
TIP: The source for this sample can be found in the https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/api/oit_linked_lists[Khronos Vulkan samples github repository].
endif::[]
:pp: {plus}{plus}
image::./images/sample.png[Sample]
== Overview
This sample implements an order-independent transparency (OIT) algorithm using per-pixel ordered linked lists.
It renders 64 spheres with random color and opacity (from 0.2 to 1.0).
It produces pixel-perfect results.
== Algorithm
The OIT algorithm consists of two passes: the _gather_ pass and the _combine_ pass.
During the gather pass, the transparent geometry is rendered into per-pixel order linked lists.
Each fragment color and depth is pushed into the linked list associated with its destination pixel.
The linked lists head are stored into a storage image that is the size of the screen.
The fragment data (color and depth) is stored into a storage buffer shared by all linked lists.
The _combine_ pass is a screen-space operation.
For each pixel, it sorts the fragments stored in the linked list of that pixel.
It then alpha blends (in the shader code) them to produce the final transparent color and coverage.
Finally, it alpha blends (via the fixed blend function) the transparent color into the backbuffer.
The algorithm can produce pixel-perfect results, even with intersecting geometry.
However, there is a catch.
To keep performance high, the maximum number of sorted fragments per-pixel is limited to 16.
For more than 16 fragments, the algorithm does its best effort to blend the extra fragments, but the results might be inaccurate.
This is well enough for the sample, due to the way the objects are placed in the scene.
In general, there is a trade-off between performance and correctness.
To keep occupancy high, the maximum number of sorted fragments (`SORTED_FRAGMENT_MAX_COUNT` in `combine.frag`) should be kept low.
To get correct results in every situation, that same number should be as high as possible.
The artifacts resulting from a low number of sorted fragments per pixel can be observed by using the `Sorted fragments per pixel` option.
== Options
[cols="2,4,4"]
|===
| Option | Description | Comments
| Sort fragments
| Enable fragment sorting in the combine pass
| This option, when disabled, is meant to demonstrate the visual issues that occur with non-sorted transparent geometry.
| Camera auto-rotation
| Enable the automatic rotation of the camera
|
| Sorted fragments per pixel
| Specify the maximum number of fragments sorted per pixel
| This option, when set to a low number (e.g. 4), highlights the main weakness of the algorithm.
| Background grayscale
| Specify the grayscale value by which the background color is multiplied (0.0 to 1.0)
|
|===
== Tests
This sample was tested on Windows.
The validation layers were enabled and all reported issues were fixed.
The sample was also tested on Linux during development.
Both systems featured an AMD GPU.
Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

@@ -0,0 +1,524 @@
/* Copyright (c) 2023-2025, Google
*
* 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.
*/
#include "oit_linked_lists.h"
#include <algorithm>
OITLinkedLists::OITLinkedLists()
{
}
OITLinkedLists::~OITLinkedLists()
{
if (!has_device())
{
return;
}
vkDestroyPipeline(get_device().get_handle(), combine_pipeline, nullptr);
vkDestroyPipeline(get_device().get_handle(), background_pipeline, nullptr);
vkDestroyPipeline(get_device().get_handle(), gather_pipeline, nullptr);
vkDestroyPipelineLayout(get_device().get_handle(), pipeline_layout, nullptr);
vkDestroyDescriptorPool(get_device().get_handle(), descriptor_pool, VK_NULL_HANDLE);
vkDestroyDescriptorSetLayout(get_device().get_handle(), descriptor_set_layout, nullptr);
destroy_sized_objects();
instance_data.reset();
scene_constants.reset();
vkDestroySampler(get_device().get_handle(), background_texture.sampler, nullptr);
object.reset();
}
bool OITLinkedLists::prepare(const vkb::ApplicationOptions &options)
{
if (!ApiVulkanSample::prepare(options))
{
return false;
}
camera.type = vkb::CameraType::LookAt;
camera.set_position({0.0f, 0.0f, -4.0f});
camera.set_rotation({0.0f, 0.0f, 0.0f});
camera.set_perspective(60.0f, static_cast<float>(width) / static_cast<float>(height), 256.0f, 0.1f);
load_assets();
create_constant_buffers();
create_descriptors();
create_sized_objects(width, height);
create_pipelines();
update_scene_constants();
fill_instance_data();
update_descriptors();
build_command_buffers();
prepared = true;
return true;
}
bool OITLinkedLists::resize(const uint32_t width, const uint32_t height)
{
if ((width != this->width) || (height != this->height))
{
destroy_sized_objects();
create_sized_objects(width, height);
update_descriptors();
}
ApiVulkanSample::resize(width, height);
return true;
}
void OITLinkedLists::render(float delta_time)
{
if (!prepared)
{
return;
}
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();
if (camera_auto_rotation)
{
camera.rotate({delta_time * 5.0f, delta_time * 5.0f, 0.0f});
}
update_scene_constants();
}
void OITLinkedLists::request_gpu_features(vkb::PhysicalDevice &gpu)
{
if (gpu.get_features().fragmentStoresAndAtomics)
{
gpu.get_mutable_requested_features().fragmentStoresAndAtomics = VK_TRUE;
}
else
{
throw std::runtime_error("This sample requires support for buffers and images stores and atomic operations in the fragment shader stage");
}
}
void OITLinkedLists::on_update_ui_overlay(vkb::Drawer &drawer)
{
drawer.checkbox("Sort fragments", &sort_fragments);
drawer.checkbox("Camera auto-rotation", &camera_auto_rotation);
drawer.slider_int("Sorted fragments per pixel", &sorted_fragment_count, kSortedFragmentMinCount, kSortedFragmentMaxCount);
drawer.slider_float("Background grayscale", &background_grayscale, kBackgroundGrayscaleMin, kBackgroundGrayscaleMax);
}
void OITLinkedLists::build_command_buffers()
{
VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info();
VkRenderPassBeginInfo render_pass_begin_info = vkb::initializers::render_pass_begin_info();
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;
for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i)
{
VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info));
{
// Gather pass
{
render_pass_begin_info.framebuffer = gather_framebuffer;
render_pass_begin_info.renderPass = gather_render_pass;
render_pass_begin_info.clearValueCount = 0;
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);
vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, NULL);
vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, gather_pipeline);
draw_model(object, draw_cmd_buffers[i], kInstanceCount);
}
vkCmdEndRenderPass(draw_cmd_buffers[i]);
}
VkImageSubresourceRange subresource_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
vkb::image_layout_transition(
draw_cmd_buffers[i], linked_list_head_image->get_handle(),
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT,
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL,
subresource_range);
// Combine pass
{
VkClearValue clear_values[2];
clear_values[0].color = {{0.0f, 0.0f, 0.0f, 0.0f}};
clear_values[1].depthStencil = {0.0f, 0};
render_pass_begin_info.framebuffer = framebuffers[i];
render_pass_begin_info.renderPass = render_pass;
render_pass_begin_info.clearValueCount = 2;
render_pass_begin_info.pClearValues = clear_values;
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);
vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set, 0, NULL);
vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, background_pipeline);
vkCmdDraw(draw_cmd_buffers[i], 3, 1, 0, 0);
vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, combine_pipeline);
vkCmdDraw(draw_cmd_buffers[i], 3, 1, 0, 0);
draw_ui(draw_cmd_buffers[i]);
}
vkCmdEndRenderPass(draw_cmd_buffers[i]);
}
}
VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i]));
}
}
////////////////////////////////////////////////////////////////////////////////
void OITLinkedLists::create_sized_objects(const uint32_t width, const uint32_t height)
{
create_gather_pass_objects(width, height);
create_fragment_resources(width, height);
clear_sized_resources();
}
void OITLinkedLists::destroy_sized_objects()
{
vkDestroyFramebuffer(get_device().get_handle(), gather_framebuffer, nullptr);
vkDestroyRenderPass(get_device().get_handle(), gather_render_pass, nullptr);
fragment_counter.reset();
fragment_buffer.reset();
fragment_max_count = 0;
linked_list_head_image_view.reset();
linked_list_head_image.reset();
}
void OITLinkedLists::create_gather_pass_objects(const uint32_t width, const uint32_t height)
{
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
VkRenderPassCreateInfo render_pass_create_info = {};
render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_create_info.subpassCount = 1;
render_pass_create_info.pSubpasses = &subpass;
VK_CHECK(vkCreateRenderPass(get_device().get_handle(), &render_pass_create_info, nullptr, &gather_render_pass));
VkFramebufferCreateInfo framebuffer_create_info = vkb::initializers::framebuffer_create_info();
framebuffer_create_info.renderPass = gather_render_pass;
framebuffer_create_info.width = width;
framebuffer_create_info.height = height;
framebuffer_create_info.layers = 1;
VK_CHECK(vkCreateFramebuffer(get_device().get_handle(), &framebuffer_create_info, nullptr, &gather_framebuffer));
}
void OITLinkedLists::create_fragment_resources(const uint32_t width, const uint32_t height)
{
{
const VkExtent3D image_extent = {width, height, 1};
linked_list_head_image = std::make_unique<vkb::core::Image>(get_device(), image_extent, VK_FORMAT_R32_UINT, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_ONLY, VK_SAMPLE_COUNT_1_BIT);
linked_list_head_image_view = std::make_unique<vkb::core::ImageView>(*linked_list_head_image, VK_IMAGE_VIEW_TYPE_2D, VK_FORMAT_R32_UINT);
}
{
fragment_max_count = width * height * kFragmentsPerPixelAverage;
const uint32_t fragment_buffer_size = sizeof(glm::uvec3) * fragment_max_count;
fragment_buffer = std::make_unique<vkb::core::BufferC>(get_device(), fragment_buffer_size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY);
}
{
fragment_counter = std::make_unique<vkb::core::BufferC>(get_device(), sizeof(glm::uint), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_ONLY);
}
}
void OITLinkedLists::clear_sized_resources()
{
VkCommandBuffer command_buffer;
VkCommandBufferAllocateInfo command_buffer_allocate_info = vkb::initializers::command_buffer_allocate_info(cmd_pool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1);
VK_CHECK(vkAllocateCommandBuffers(get_device().get_handle(), &command_buffer_allocate_info, &command_buffer));
VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info();
VK_CHECK(vkBeginCommandBuffer(command_buffer, &command_buffer_begin_info));
{
vkCmdFillBuffer(command_buffer, fragment_counter->get_handle(), 0, sizeof(glm::uint), 0);
VkImageSubresourceRange subresource_range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
vkb::image_layout_transition(
command_buffer, linked_list_head_image->get_handle(),
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_MEMORY_WRITE_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL,
subresource_range);
VkClearColorValue linked_lists_clear_value;
linked_lists_clear_value.uint32[0] = kLinkedListEndSentinel;
linked_lists_clear_value.uint32[1] = kLinkedListEndSentinel;
linked_lists_clear_value.uint32[2] = kLinkedListEndSentinel;
linked_lists_clear_value.uint32[3] = kLinkedListEndSentinel;
vkCmdClearColorImage(command_buffer, linked_list_head_image->get_handle(), VK_IMAGE_LAYOUT_GENERAL, &linked_lists_clear_value, 1, &subresource_range);
}
VK_CHECK(vkEndCommandBuffer(command_buffer));
{
VkSubmitInfo submit_info = {VK_STRUCTURE_TYPE_SUBMIT_INFO};
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer;
VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE));
VK_CHECK(vkQueueWaitIdle(queue));
}
vkFreeCommandBuffers(get_device().get_handle(), cmd_pool, 1, &command_buffer);
}
////////////////////////////////////////////////////////////////////////////////
void OITLinkedLists::load_assets()
{
object = load_model("scenes/geosphere.gltf");
background_texture = load_texture("textures/vulkan_logo_full.ktx", vkb::sg::Image::Color);
}
void OITLinkedLists::create_constant_buffers()
{
scene_constants = std::make_unique<vkb::core::BufferC>(get_device(), sizeof(SceneConstants), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU);
instance_data = std::make_unique<vkb::core::BufferC>(get_device(), sizeof(Instance) * kInstanceCount, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU);
}
void OITLinkedLists::create_descriptors()
{
{
std::vector<VkDescriptorSetLayoutBinding> set_layout_bindings = {
vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0),
vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 1),
vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_FRAGMENT_BIT, 2),
vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 3),
vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 4),
vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 5),
};
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));
}
{
std::vector<VkDescriptorPoolSize> pool_sizes = {
vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2),
vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1),
vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2),
vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1),
};
const uint32_t num_descriptor_sets = 1;
VkDescriptorPoolCreateInfo descriptor_pool_create_info = vkb::initializers::descriptor_pool_create_info(static_cast<uint32_t>(pool_sizes.size()), pool_sizes.data(), num_descriptor_sets);
VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool));
}
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));
}
void OITLinkedLists::update_descriptors()
{
VkDescriptorBufferInfo scene_constants_descriptor = create_descriptor(*scene_constants);
VkDescriptorBufferInfo instance_data_descriptor = create_descriptor(*instance_data);
VkDescriptorImageInfo linked_list_head_image_view_descriptor = vkb::initializers::descriptor_image_info(VK_NULL_HANDLE, linked_list_head_image_view->get_handle(), VK_IMAGE_LAYOUT_GENERAL);
VkDescriptorBufferInfo fragment_buffer_descriptor = create_descriptor(*fragment_buffer);
VkDescriptorBufferInfo fragment_counter_descriptor = create_descriptor(*fragment_counter);
VkDescriptorImageInfo background_texture_descriptor = create_descriptor(background_texture);
std::vector<VkWriteDescriptorSet> write_descriptor_sets = {
vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &scene_constants_descriptor),
vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, &instance_data_descriptor),
vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2, &linked_list_head_image_view_descriptor),
vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3, &fragment_buffer_descriptor),
vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 4, &fragment_counter_descriptor),
vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 5, &background_texture_descriptor),
};
vkUpdateDescriptorSets(get_device().get_handle(), static_cast<uint32_t>(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, NULL);
}
void OITLinkedLists::create_pipelines()
{
{
VkPipelineLayoutCreateInfo pipeline_layout_create_info = vkb::initializers::pipeline_layout_create_info(&descriptor_set_layout, 1);
VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_create_info, nullptr, &pipeline_layout));
}
{
VkPipelineVertexInputStateCreateInfo vertex_input_state = vkb::initializers::pipeline_vertex_input_state_create_info();
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_NONE, 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);
VkPipelineMultisampleStateCreateInfo multisample_state = vkb::initializers::pipeline_multisample_state_create_info(VK_SAMPLE_COUNT_1_BIT, 0);
VkPipelineViewportStateCreateInfo viewport_state = vkb::initializers::pipeline_viewport_state_create_info(1, 1, 0);
VkPipelineDepthStencilStateCreateInfo depth_stencil_state = vkb::initializers::pipeline_depth_stencil_state_create_info(VK_FALSE, VK_FALSE, VK_COMPARE_OP_GREATER);
std::vector<VkDynamicState> dynamic_state_enables = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
VkPipelineDynamicStateCreateInfo dynamicState = vkb::initializers::pipeline_dynamic_state_create_info(dynamic_state_enables.data(), static_cast<uint32_t>(dynamic_state_enables.size()), 0);
std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{};
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 = &dynamicState;
pipeline_create_info.stageCount = static_cast<uint32_t>(shader_stages.size());
pipeline_create_info.pStages = shader_stages.data();
{
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, offsetof(Vertex, pos)),
};
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();
shader_stages[0] = load_shader("oit_linked_lists/gather.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shader_stages[1] = load_shader("oit_linked_lists/gather.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
pipeline_create_info.renderPass = gather_render_pass;
VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info, nullptr, &gather_pipeline));
}
{
vertex_input_state.vertexBindingDescriptionCount = 0;
vertex_input_state.pVertexBindingDescriptions = nullptr;
vertex_input_state.vertexAttributeDescriptionCount = 0;
vertex_input_state.pVertexAttributeDescriptions = nullptr;
shader_stages[0] = load_shader("oit_linked_lists/fullscreen.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shader_stages[1] = load_shader("oit_linked_lists/background.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
pipeline_create_info.renderPass = render_pass;
VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info, nullptr, &background_pipeline));
}
{
vertex_input_state.vertexBindingDescriptionCount = 0;
vertex_input_state.pVertexBindingDescriptions = nullptr;
vertex_input_state.vertexAttributeDescriptionCount = 0;
vertex_input_state.pVertexAttributeDescriptions = nullptr;
blend_attachment_state.blendEnable = VK_TRUE;
blend_attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
blend_attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blend_attachment_state.colorBlendOp = VK_BLEND_OP_ADD;
blend_attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
blend_attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
blend_attachment_state.alphaBlendOp = VK_BLEND_OP_ADD;
shader_stages[0] = load_shader("oit_linked_lists/combine.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shader_stages[1] = load_shader("oit_linked_lists/combine.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
pipeline_create_info.renderPass = render_pass;
VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info, nullptr, &combine_pipeline));
}
}
}
////////////////////////////////////////////////////////////////////////////////
void OITLinkedLists::update_scene_constants()
{
SceneConstants constants = {};
constants.projection = camera.matrices.perspective;
constants.view = camera.matrices.view;
constants.background_grayscale = background_grayscale;
constants.sort_fragments = sort_fragments ? 1U : 0U;
constants.fragment_max_count = fragment_max_count;
constants.sorted_fragment_count = sorted_fragment_count;
scene_constants->convert_and_update(constants);
}
void OITLinkedLists::fill_instance_data()
{
Instance instances[kInstanceCount] = {};
auto get_random_float = []() {
return static_cast<float>(rand()) / (RAND_MAX);
};
for (uint32_t l = 0, instance_index = 0; l < kInstanceLayerCount; ++l)
{
for (uint32_t c = 0; c < kInstanceColumnCount; ++c)
{
for (uint32_t r = 0; r < kInstanceRowCount; ++r, ++instance_index)
{
const float x = static_cast<float>(r) - ((kInstanceRowCount - 1) * 0.5f);
const float y = static_cast<float>(c) - ((kInstanceColumnCount - 1) * 0.5f);
const float z = static_cast<float>(l) - ((kInstanceLayerCount - 1) * 0.5f);
const float scale = 0.02f;
instances[instance_index].model =
glm::scale(
glm::translate(glm::mat4(1.0f), glm::vec3(x, y, z)),
glm::vec3(scale));
instances[instance_index].color.r = get_random_float();
instances[instance_index].color.g = get_random_float();
instances[instance_index].color.b = get_random_float();
instances[instance_index].color.a = get_random_float() * 0.8f + 0.2f;
}
}
}
instance_data->convert_and_update(instances);
}
////////////////////////////////////////////////////////////////////////////////
std::unique_ptr<vkb::VulkanSampleC> create_oit_linked_lists()
{
return std::make_unique<OITLinkedLists>();
}
@@ -0,0 +1,116 @@
/* Copyright (c) 2023-2024, Google
*
* 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.
*/
#pragma once
#include "api_vulkan_sample.h"
#include "rendering/render_pipeline.h"
#include "scene_graph/components/camera.h"
class OITLinkedLists : public ApiVulkanSample
{
public:
OITLinkedLists();
~OITLinkedLists();
bool prepare(const vkb::ApplicationOptions &options) override;
bool resize(const uint32_t width, const uint32_t height) override;
void render(float delta_time) override;
void build_command_buffers() override;
void request_gpu_features(vkb::PhysicalDevice &gpu) override;
void on_update_ui_overlay(vkb::Drawer &drawer) override;
private:
void create_sized_objects(const uint32_t width, const uint32_t height);
void destroy_sized_objects();
void create_gather_pass_objects(const uint32_t width, const uint32_t height);
void create_fragment_resources(const uint32_t width, const uint32_t height);
void clear_sized_resources();
void load_assets();
void create_constant_buffers();
void create_descriptors();
void create_pipelines();
void update_descriptors();
void update_scene_constants();
void fill_instance_data();
private:
static constexpr uint32_t kInstanceRowCount = 4;
static constexpr uint32_t kInstanceColumnCount = 4;
static constexpr uint32_t kInstanceLayerCount = 4;
static constexpr uint32_t kInstanceCount = kInstanceRowCount * kInstanceColumnCount * kInstanceLayerCount;
static constexpr uint32_t kFragmentsPerPixelAverage = 8;
static constexpr int32_t kSortedFragmentMinCount = 1;
static constexpr int32_t kSortedFragmentMaxCount = 16;
static constexpr float kBackgroundGrayscaleMin = 0.0f;
static constexpr float kBackgroundGrayscaleMax = 1.0f;
static constexpr uint32_t kLinkedListEndSentinel = 0xFFFFFFFFU;
struct SceneConstants
{
glm::mat4 projection;
glm::mat4 view;
glm::f32 background_grayscale;
glm::uint sort_fragments;
glm::uint fragment_max_count;
glm::uint sorted_fragment_count;
};
struct Instance
{
glm::mat4 model;
glm::vec4 color;
};
private:
std::unique_ptr<vkb::sg::SubMesh> object;
Texture background_texture;
std::unique_ptr<vkb::core::BufferC> scene_constants;
std::unique_ptr<vkb::core::BufferC> instance_data;
std::unique_ptr<vkb::core::Image> linked_list_head_image;
std::unique_ptr<vkb::core::ImageView> linked_list_head_image_view;
std::unique_ptr<vkb::core::BufferC> fragment_buffer;
std::unique_ptr<vkb::core::BufferC> fragment_counter;
glm::uint fragment_max_count = 0U;
VkRenderPass gather_render_pass = VK_NULL_HANDLE;
VkFramebuffer gather_framebuffer = VK_NULL_HANDLE;
VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE;
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
VkDescriptorSet descriptor_set = VK_NULL_HANDLE;
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
VkPipeline gather_pipeline = VK_NULL_HANDLE;
VkPipeline background_pipeline = VK_NULL_HANDLE;
VkPipeline combine_pipeline = VK_NULL_HANDLE;
int32_t sort_fragments = true;
int32_t camera_auto_rotation = false;
int32_t sorted_fragment_count = kSortedFragmentMaxCount;
float background_grayscale = 0.3f;
};
std::unique_ptr<vkb::VulkanSampleC> create_oit_linked_lists();