diff --git a/framework/gui.cpp b/framework/gui.cpp index 48e8dc3851..d137806708 100644 --- a/framework/gui.cpp +++ b/framework/gui.cpp @@ -1176,6 +1176,15 @@ bool Drawer::checkbox(const char *caption, int32_t *value) return res; } +bool Drawer::radio_button(const char *caption, int32_t *selectedOption, const int32_t elementOption) +{ + bool res = ImGui::RadioButton(caption, selectedOption, elementOption); + if (res) + dirty = true; + + return res; +} + bool Drawer::input_float(const char *caption, float *value, float step, uint32_t precision) { bool res = ImGui::InputFloat(caption, value, step, step * 10.0f, precision); @@ -1245,16 +1254,48 @@ void Drawer::text(const char *formatstr, ...) va_end(args); } -bool Drawer::color_picker(const char *caption, float *color, ImGuiColorEditFlags flags, uint16_t width) +bool Drawer::color_picker(const char *caption, std::array &color, float width, ImGuiColorEditFlags flags) { - bool res = ImGui::ColorEdit4(caption, color, flags); - if (res) - { - dirty = true; - }; - return res; + bool res; + ImGui::PushItemWidth(width); + res = ImGui::ColorPicker3(caption, color.data(), flags); + ImGui::PopItemWidth(); + if (res) + dirty = true; + return res; +} + +bool Drawer::color_picker(const char *caption, std::array &color, float width, ImGuiColorEditFlags flags) +{ + bool res; + ImGui::PushItemWidth(width); + res = ImGui::ColorPicker4(caption, color.data(), flags); + ImGui::PopItemWidth(); + if (res) + dirty = true; + return res; } +bool Drawer::color_edit(const char *caption, std::array &color, float width, ImGuiColorEditFlags flags) +{ + bool res; + ImGui::PushItemWidth(width); + res = ImGui::ColorEdit3(caption, color.data(), flags); + ImGui::PopItemWidth(); + if (res) + dirty = true; + return res; +} +bool Drawer::color_edit(const char *caption, std::array &color, float width, ImGuiColorEditFlags flags) +{ + bool res; + ImGui::PushItemWidth(width); + res = ImGui::ColorEdit4(caption, color.data(), flags); + ImGui::PopItemWidth(); + if (res) + dirty = true; + return res; +} } // namespace vkb diff --git a/framework/gui.h b/framework/gui.h index 1519797863..cdac4ec8ea 100644 --- a/framework/gui.h +++ b/framework/gui.h @@ -121,6 +121,8 @@ class Drawer */ bool checkbox(const char *caption, int32_t *value); + bool radio_button(const char *caption, int32_t *selectedOption, const int32_t elementOption); + /** * @brief Adds a number input field to the gui * @param caption The text to display @@ -173,7 +175,41 @@ class Drawer */ void text(const char *formatstr, ...); - bool color_picker(const char *caption, float *color, ImGuiColorEditFlags flags, uint16_t width = 0); + /** + * @brief Adds a color picker to the gui + * @param caption The text to display + * @param color Color channel array on which the picker works. It contains values ranging from 0 to 1. + * @param width Element width. Zero is a special value for the default element width. + * @param flags Flags to modify the appearance and behavior of the element. + */ + bool color_picker(const char *caption, std::array &color, float width = 0.0f, ImGuiColorEditFlags flags = 0); + + /** + * @brief Adds a color picker to the gui + * @param caption The text to display + * @param color Color channel array on which the picker works. It contains values ranging from 0 to 1. + * @param width Element width. Zero is a special value for the default element width. + * @param flags Flags to modify the appearance and behavior of the element. + */ + bool color_picker(const char *caption, std::array &color, float width = 0.0f, ImGuiColorEditFlags flags = 0); + + /** + * @brief Adds a color edit to the gui + * @param caption The text to display + * @param color Color channel array on which the picker works. It contains values ranging from 0 to 1. + * @param width Element width. Zero is a special value for the default element width. + * @param flags Flags to modify the appearance and behavior of the element. + */ + bool color_edit(const char *caption, std::array &color, float width = 0.0f, ImGuiColorEditFlags flags = 0); + + /** + * @brief Adds a color edit to the gui + * @param caption The text to display + * @param color Color channel array on which the picker works. It contains values ranging from 0 to 1. + * @param width Element width. Zero is a special value for the default element width. + * @param flags Flags to modify the appearance and behavior of the element. + */ + bool color_edit(const char *caption, std::array &color, float width = 0.0f, ImGuiColorEditFlags flags = 0); private: bool dirty{false}; diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 3ebb6b178b..d739f95ee1 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -85,6 +85,7 @@ set(ORDER_LIST "fragment_shader_barycentric" "gshader_to_mshader" "color_write_enable" + "dynamic_blending" #Performance Samples "swapchain_images" diff --git a/samples/README.md b/samples/README.md index 630e4c5bb6..93c2cab624 100644 --- a/samples/README.md +++ b/samples/README.md @@ -343,6 +343,10 @@ Demonstrate how to create multiple color blend attachments and then toggle them **Extension:** [```VK_EXT_mesh_shader```](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_mesh_shader.html)
Demonstrates how a mesh shader can be used to achieve the same results as with geometry shader, it loads model from a file and visualizes its normals. +### [Dynamic blending](./extensions/dynamic_blending)
+**Extension**: [```VK_EXT_extended_dynamic_state3```](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_extended_dynamic_state.html)
+ +Demonstrate how to use the blending related functions available in the VK_EXT_extended_dynamic_state3 extension. ## Tooling Samples diff --git a/samples/extensions/dynamic_blending/README.md b/samples/extensions/dynamic_blending/README.md new file mode 100644 index 0000000000..67af56cfe2 --- /dev/null +++ b/samples/extensions/dynamic_blending/README.md @@ -0,0 +1,51 @@ + + +# Color write enable +## Overview +This sample demonstrates the functionality of VK_EXT_extended_dynamic_state3 related to blending. It includes the following features: +- `vkCmdSetColorBlendEnableEXT`: toggles blending on and off. +- `vkCmdSetColorBlendEquationEXT`: modifies blending operators and factors. +- `vkCmdSetColorBlendAdvancedEXT`: utilizes more complex blending operators. +- `vkCmdSetColorWriteMaskEXT`: toggles individual channels on and off. + +## How to use in Vulkan +To utilize this feature, the device extensions `VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME` and `VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME` need to be enabled. +All presented functions take an array of objects defining their action for subsequent color attachments. +The `vkCmdSetColorBlendEnableEXT` function expects an array of booleans to toggle blending. +The `vkCmdSetColorBlendEquationEXT` function expects and array of VkColorBlendEquationEXT objectswhich determine operators and factors for color and alpha blending. +The `vkCmdSetColorBlendAdvancedEXT` function expects and array of VkColorBlendAdvancedEXT objects, which determine blending operators and premultiplication for color blending. +The `vkCmdSetColorWriteMaskEXT` function expects and array of VkColorComponentFlags objects. These objects can be created by combining the desired color bit flags using bitwise addition. + +## The sample +The sample demonstrates how to set up an application to work with this extension: +- Enabling the extension. +- Setting parameters for the presented methods. + +The sample demonstrates how the use of each operator affects color blending. + +## Documentation links +[https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdSetColorBlendEnableEXT.html](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdSetColorBlendEnableEXT.html) + +[https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdSetColorBlendEquationEXT.html](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdSetColorBlendEquationEXT.html) + +[https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdSetColorBlendAdvancedEXT.html](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdSetColorBlendAdvancedEXT.html) + +[https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdSetColorWriteMaskEXT.html](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdSetColorWriteMaskEXT.html) + diff --git a/samples/extensions/dynamic_blending/dynamic_blending.cpp b/samples/extensions/dynamic_blending/dynamic_blending.cpp index 7f23e26a58..29723e99ad 100644 --- a/samples/extensions/dynamic_blending/dynamic_blending.cpp +++ b/samples/extensions/dynamic_blending/dynamic_blending.cpp @@ -22,460 +22,605 @@ DynamicBlending::DynamicBlending() { - add_instance_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); - add_device_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); - add_device_extension(VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME); - add_device_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); - add_device_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); + add_instance_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + add_device_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); + add_device_extension(VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME); - title = "Dynamic blending"; + title = "Dynamic blending"; } DynamicBlending::~DynamicBlending() { - if (device) - { - // NYI - } + if (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); + vkDestroyDescriptorPool(get_device().get_handle(), descriptor_pool, nullptr); + } } bool DynamicBlending::prepare(const vkb::ApplicationOptions &options) { - if (!ApiVulkanSample::prepare(options)) - { - return false; - } - - camera.type = vkb::CameraType::LookAt; - camera.set_position({0.0f, 0.0f, -5.0f}); - camera.set_rotation({-15.0f, 15.0f, 0.0f}); - camera.set_perspective(45.0f, static_cast(width) / static_cast(height), 256.0f, 0.1f); - - prepare_uniform_buffers(); - prepare_scene(); - setup_descriptor_pool(); - create_descriptor_set_layout(); - create_descriptor_set(); - create_pipelines(); - build_command_buffers(); - - prepared = true; - - return true; + if (!ApiVulkanSample::prepare(options)) + { + return false; + } + + camera.type = vkb::CameraType::LookAt; + camera.set_position({0.0f, 0.0f, -5.0f}); + camera.set_rotation({-15.0f, 15.0f, 0.0f}); + camera.set_perspective(45.0f, static_cast(width) / static_cast(height), 256.0f, 0.1f); + + initialize_operator_lists(); + prepare_uniform_buffers(); + prepare_scene(); + setup_descriptor_pool(); + create_descriptor_set_layout(); + create_descriptor_set(); + create_pipeline(); + build_command_buffers(); + + rnd_engine = std::default_random_engine(time(nullptr)); + rnd_dist = std::uniform_real_distribution(0.0f, 1.0f); + + prepared = true; + + return true; } -void DynamicBlending::prepare_scene() { - vertices = { - {{-1.0f, -1.0f, 1.0f}, {0.0f, 0.0f}}, - {{1.0f, -1.0f, 1.0f}, {1.0f, 0.0f}}, - {{1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}, - {{-1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, - - {{-1.0f, -1.0f, -1.0f},{0.0f, 0.0f}}, - {{1.0f, -1.0f, -1.0f}, {1.0f, 0.0f}}, - {{1.0f, 1.0f, -1.0f}, {1.0f, 1.0f}}, - {{-1.0f, 1.0f, -1.0f}, {0.0f, 1.0f}}, - }; - - std::vector indices = { - 6, 5, 4, 4, 7, 6, - 0, 1, 2, 2, 3, 0 - }; - - index_count = static_cast(indices.size()); - - vertex_buffer_size = vertices.size() * sizeof(Vertex); - auto index_buffer_size = indices.size() * sizeof(uint32_t); - - vertex_buffer = std::make_unique(get_device(), - vertex_buffer_size, - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - VMA_MEMORY_USAGE_GPU_TO_CPU); - vertex_buffer->update(vertices.data(), vertex_buffer_size); - - index_buffer = std::make_unique(get_device(), - index_buffer_size, - VK_BUFFER_USAGE_INDEX_BUFFER_BIT, - VMA_MEMORY_USAGE_GPU_TO_CPU); - index_buffer->update(indices.data(), index_buffer_size); - - face_preferences[0].index_offset = 0; - face_preferences[0].index_count = index_count / 2; - face_preferences[0].color_bit_enabled = {true, true, true, true}; - face_preferences[0].color = {{ - {1.0f, 0.0f, 0.0f, 1.0f}, - {0.0f, 1.0f, 0.0f, 1.0f}, - {0.0f, 0.0f, 1.0f, 1.0f}, - {0.0f, 0.0f, 0.0f, 1.0f} - }}; - - face_preferences[1].index_offset = index_count / 2; - face_preferences[1].index_count = index_count / 2; - face_preferences[1].color_bit_enabled = {true, true, true, true}; - face_preferences[1].color = {{ - {0.0f, 1.0f, 1.0f, 1.0f}, - {1.0f, 0.0f, 1.0f, 1.0f}, - {1.0f, 1.0f, 0.0f, 1.0f}, - {1.0f, 1.0f, 1.0f, 1.0f} - }}; +void DynamicBlending::initialize_operator_lists() +{ + for (uint32_t i = VK_BLEND_OP_ADD; i <= VK_BLEND_OP_MAX; ++i) + { + VkBlendOp op = static_cast(i); + blend_operator.values.push_back(op); + blend_operator.names.push_back(vkb::to_string(op)); + } + + for (uint32_t i = VK_BLEND_OP_ZERO_EXT; i <= VK_BLEND_OP_BLUE_EXT; ++i) + { + VkBlendOp op = static_cast(i); + advanced_blend_operator.values.push_back(op); + advanced_blend_operator.names.push_back(vkb::to_string(op)); + } + + for (uint32_t i = VK_BLEND_FACTOR_ZERO; i <= VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; ++i) + { + // The substr(16) call is used to drop the "VK_BLEND_FACTOR_" prefix + blend_factor.names.push_back(vkb::to_string(static_cast(i)).substr(16)); + } +} +void DynamicBlending::prepare_scene() +{ + // Vertices of a cube. UVs are used for the interpolation. + vertices = { + {{-1.0f, -1.0f, 1.0f}, {0.0f, 0.0f}}, + {{1.0f, -1.0f, 1.0f}, {1.0f, 0.0f}}, + {{1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}, + {{-1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, + + {{-1.0f, -1.0f, -1.0f}, {0.0f, 0.0f}}, + {{1.0f, -1.0f, -1.0f}, {1.0f, 0.0f}}, + {{1.0f, 1.0f, -1.0f}, {1.0f, 1.0f}}, + {{-1.0f, 1.0f, -1.0f}, {0.0f, 1.0f}}, + }; + + // Indices of the front and back face. + std::vector indices = { + 6, 5, 4, 4, 7, 6, + 0, 1, 2, 2, 3, 0}; + + index_count = static_cast(indices.size()); + + vertex_buffer_size = vertices.size() * sizeof(Vertex); + auto index_buffer_size = indices.size() * sizeof(uint32_t); + + vertex_buffer = std::make_unique(get_device(), + vertex_buffer_size, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VMA_MEMORY_USAGE_GPU_TO_CPU); + vertex_buffer->update(vertices.data(), vertex_buffer_size); + + index_buffer = std::make_unique(get_device(), + index_buffer_size, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VMA_MEMORY_USAGE_GPU_TO_CPU); + index_buffer->update(indices.data(), index_buffer_size); + + face_preferences[0].name = "First face"; + face_preferences[0].index_offset = 0; + face_preferences[0].index_count = index_count / 2; + face_preferences[0].color_bit_enabled = {true, true, true, true}; + face_preferences[0].color = {{{1.0f, 0.0f, 0.0f, 1.0f}, + {0.0f, 1.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 1.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 1.0f}}}; + + face_preferences[1].name = "Second face"; + face_preferences[1].index_offset = index_count / 2; + face_preferences[1].index_count = index_count / 2; + face_preferences[1].color_bit_enabled = {true, true, true, true}; + face_preferences[1].color = {{{0.0f, 1.0f, 1.0f, 0.5f}, + {1.0f, 0.0f, 1.0f, 0.5f}, + {1.0f, 1.0f, 0.0f, 0.5f}, + {1.0f, 1.0f, 1.0f, 0.5f}}}; + + update_color_buffer(); } void DynamicBlending::request_gpu_features(vkb::PhysicalDevice &gpu) { - { - auto &features = gpu.request_extension_features(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT); - features.extendedDynamicState3ColorWriteMask = VK_TRUE; - features.extendedDynamicState3ColorBlendEnable = VK_TRUE; - features.extendedDynamicState3ColorBlendAdvanced = VK_TRUE; - features.extendedDynamicState3ColorBlendEquation = VK_TRUE; - } - { - blend_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT; - blend_properties.pNext = VK_NULL_HANDLE; - - VkPhysicalDeviceProperties2 device_properties2 = {}; - device_properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - device_properties2.pNext = &blend_properties; - vkGetPhysicalDeviceProperties2(gpu.get_handle(), &device_properties2); - - } + { + auto &features = gpu.request_extension_features(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_3_FEATURES_EXT); + features.extendedDynamicState3ColorWriteMask = VK_TRUE; + features.extendedDynamicState3ColorBlendEnable = VK_TRUE; + features.extendedDynamicState3ColorBlendAdvanced = VK_TRUE; + features.extendedDynamicState3ColorBlendEquation = VK_TRUE; + } + { + auto &features = gpu.request_extension_features(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT); + features.advancedBlendCoherentOperations = VK_TRUE; + } } -void DynamicBlending::prepare_uniform_buffers() { - camera_ubo = std::make_unique(get_device(), sizeof(CameraUbo), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); - color_ubo = std::make_unique(get_device(), sizeof(ColorUbo), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); - +void DynamicBlending::prepare_uniform_buffers() +{ + camera_ubo = std::make_unique(get_device(), sizeof(CameraUbo), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); + color_ubo = std::make_unique(get_device(), sizeof(ColorUbo), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); } -void DynamicBlending::update_uniform_buffers() { - CameraUbo cam; - cam.model = glm::mat4(1.0f); - cam.model = glm::translate(cam.model, glm::vec3(0.0f)); - cam.view = camera.matrices.view; - cam.projection = camera.matrices.perspective; - - camera_ubo->convert_and_update(cam); - - update_color(); - - glm::mat4 invView = glm::inverse(camera.matrices.view); - glm::vec4 plane0(vertices[0].pos[0], vertices[0].pos[1], vertices[0].pos[2], 1.0f); - glm::vec4 plane1(vertices[4].pos[0], vertices[4].pos[1], vertices[4].pos[2], 1.0f); - - plane0 = invView * plane0; - plane1 = invView * plane1; - - reverse = plane0.z < plane1.z; - - build_command_buffers(); +void DynamicBlending::update_camera_buffer() +{ + CameraUbo cam; + cam.model = glm::mat4(1.0f); + cam.model = glm::translate(cam.model, glm::vec3(0.0f)); + cam.view = camera.matrices.view; + cam.projection = camera.matrices.perspective; + + camera_ubo->convert_and_update(cam); + + // Calculate reverse view matrix to determine which face is currently closer. + glm::mat4 inv_view = glm::inverse(camera.matrices.view); + glm::vec4 face0(vertices[0].pos[0], vertices[0].pos[1], vertices[0].pos[2], 1.0f); + glm::vec4 face1(vertices[4].pos[0], vertices[4].pos[1], vertices[4].pos[2], 1.0f); + + face0 = inv_view * face0; + face1 = inv_view * face1; + + // Set a flag to determine which face should be rendered first. + bool updated_reverse_face_draw = face0.z < face1.z; + if (updated_reverse_face_draw != reverse_face_draw) + { + reverse_face_draw = updated_reverse_face_draw; + build_command_buffers(); + } } +void DynamicBlending::update_color_buffer() +{ + for (uint32_t face = 0; face < 2; ++face) + for (uint32_t vertex = 0; vertex < 4; ++vertex) + { + auto &input_color = face_preferences[face].color[vertex]; + color.data[face * 4 + vertex] = glm::vec4(input_color[0], input_color[1], input_color[2], input_color[3]); + } + + color_ubo->convert_and_update(color); +} -void DynamicBlending::setup_descriptor_pool() { - std::vector pool_sizes = { - vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2u), - }; - - VkDescriptorPoolCreateInfo descriptor_pool_create_info = - vkb::initializers::descriptor_pool_create_info( - static_cast(pool_sizes.size()), - pool_sizes.data(), - pool_sizes.size()); - VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool)); +void DynamicBlending::setup_descriptor_pool() +{ + std::vector pool_sizes = { + vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2u), + }; + + VkDescriptorPoolCreateInfo descriptor_pool_create_info = + vkb::initializers::descriptor_pool_create_info( + static_cast(pool_sizes.size()), + pool_sizes.data(), + pool_sizes.size()); + VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool)); } -void DynamicBlending::create_descriptor_set_layout() { - std::vector set_layout_bindings = { - vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0u), - vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 1u) - }; +void DynamicBlending::create_descriptor_set_layout() +{ + std::vector set_layout_bindings = { + vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0u), + vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT, 1u)}; - VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info = vkb::initializers::descriptor_set_layout_create_info(set_layout_bindings); - VK_CHECK(vkCreateDescriptorSetLayout(get_device().get_handle(), &descriptor_set_layout_create_info, nullptr, &descriptor_set_layout)); + VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info = vkb::initializers::descriptor_set_layout_create_info(set_layout_bindings); + VK_CHECK(vkCreateDescriptorSetLayout(get_device().get_handle(), &descriptor_set_layout_create_info, nullptr, &descriptor_set_layout)); - VkPipelineLayoutCreateInfo pipeline_layout_create_info = vkb::initializers::pipeline_layout_create_info(&descriptor_set_layout); - VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_create_info, nullptr, &pipeline_layout)); + VkPipelineLayoutCreateInfo pipeline_layout_create_info = vkb::initializers::pipeline_layout_create_info(&descriptor_set_layout); + VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_create_info, nullptr, &pipeline_layout)); } -void DynamicBlending::create_descriptor_set() { - VkDescriptorSetAllocateInfo alloc_info = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &descriptor_set_layout, 1u); - VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_set)); +void DynamicBlending::create_descriptor_set() +{ + VkDescriptorSetAllocateInfo alloc_info = vkb::initializers::descriptor_set_allocate_info(descriptor_pool, &descriptor_set_layout, 1u); + VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_set)); - VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*camera_ubo); - VkDescriptorBufferInfo color_descriptor = create_descriptor(*color_ubo); + VkDescriptorBufferInfo buffer_descriptor = create_descriptor(*camera_ubo); + VkDescriptorBufferInfo color_descriptor = create_descriptor(*color_ubo); - std::vector write_descriptor_sets = { - vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0u, &buffer_descriptor), - vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u, &color_descriptor), - }; + std::vector write_descriptor_sets = { + vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0u, &buffer_descriptor), + vkb::initializers::write_descriptor_set(descriptor_set, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u, &color_descriptor), + }; - vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0u, nullptr); + vkUpdateDescriptorSets(get_device().get_handle(), static_cast(write_descriptor_sets.size()), write_descriptor_sets.data(), 0u, nullptr); } -void DynamicBlending::create_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_NONE, - VK_FRONT_FACE_COUNTER_CLOCKWISE, - 0); - - VkPipelineColorBlendAttachmentState blend_attachment_state = - vkb::initializers::pipeline_color_blend_attachment_state( - VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, - VK_TRUE); - - blend_attachment_state.blendEnable = VK_TRUE; - blend_attachment_state.colorBlendOp = VK_BLEND_OP_ADD; - blend_attachment_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - blend_attachment_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - blend_attachment_state.alphaBlendOp = VK_BLEND_OP_ADD; - blend_attachment_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; - blend_attachment_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE; - - VkPipelineColorBlendAdvancedStateCreateInfoEXT blendAdvancedEXT{}; - blendAdvancedEXT.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT; - blendAdvancedEXT.blendOverlap = VK_BLEND_OVERLAP_UNCORRELATED_EXT; - - VkPipelineColorBlendStateCreateInfo color_blend_state = - vkb::initializers::pipeline_color_blend_state_create_info( - 1, - &blend_attachment_state); - - color_blend_state.logicOpEnable = VK_FALSE; - color_blend_state.pNext = &blendAdvancedEXT; - - 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 dynamic_state_enables = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR, - VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT, - // NYI - // VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT, - // VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT, - // VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT - }; - - VkPipelineDynamicStateCreateInfo dynamic_state = - vkb::initializers::pipeline_dynamic_state_create_info( - dynamic_state_enables.data(), - static_cast(dynamic_state_enables.size()), - 0); - - - const std::vector vertex_input_bindings = { - vkb::initializers::vertex_input_binding_description(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX), - }; - const std::vector vertex_input_attributes = { - vkb::initializers::vertex_input_attribute_description(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos)), - vkb::initializers::vertex_input_attribute_description(0, 1, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)), - }; - - VkPipelineVertexInputStateCreateInfo vertex_input_state = vkb::initializers::pipeline_vertex_input_state_create_info(); - vertex_input_state.vertexBindingDescriptionCount = static_cast(vertex_input_bindings.size()); - vertex_input_state.pVertexBindingDescriptions = vertex_input_bindings.data(); - vertex_input_state.vertexAttributeDescriptionCount = static_cast(vertex_input_attributes.size()); - vertex_input_state.pVertexAttributeDescriptions = vertex_input_attributes.data(); - - std::array shader_stages{}; - shader_stages[0] = load_shader("dynamic_blending/blending.vert", VK_SHADER_STAGE_VERTEX_BIT); - shader_stages[1] = load_shader("dynamic_blending/blending.frag", VK_SHADER_STAGE_FRAGMENT_BIT); - - VkGraphicsPipelineCreateInfo graphics_create{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO}; - graphics_create.pNext = VK_NULL_HANDLE; - graphics_create.renderPass = render_pass; - graphics_create.pInputAssemblyState = &input_assembly_state; - graphics_create.pRasterizationState = &rasterization_state; - graphics_create.pColorBlendState = &color_blend_state; - graphics_create.pMultisampleState = &multisample_state; - graphics_create.pViewportState = &viewport_state; - graphics_create.pDepthStencilState = &depth_stencil_state; - graphics_create.pDynamicState = &dynamic_state; - graphics_create.pVertexInputState = &vertex_input_state; - graphics_create.pTessellationState = VK_NULL_HANDLE; - graphics_create.stageCount = 2; - graphics_create.pStages = shader_stages.data(); - graphics_create.layout = pipeline_layout; - - VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), - pipeline_cache, - 1, - &graphics_create, - VK_NULL_HANDLE, - &pipeline)); -} - -void DynamicBlending::update_color() { - for(uint32_t face = 0; face < 2; ++face) - for(uint32_t vertex = 0; vertex < 4; ++vertex) { - auto &input_color = face_preferences[face].color[vertex]; - color.data[face * 4 + vertex] = glm::vec4(input_color[0], input_color[1], input_color[2], input_color[3]); - } - color_ubo->convert_and_update(color); +void DynamicBlending::create_pipeline() +{ + 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( + VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + VK_TRUE); + + VkPipelineColorBlendAdvancedStateCreateInfoEXT blendAdvancedEXT{}; + blendAdvancedEXT.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT; + blendAdvancedEXT.blendOverlap = VK_BLEND_OVERLAP_UNCORRELATED_EXT; + + VkPipelineColorBlendStateCreateInfo color_blend_state = + vkb::initializers::pipeline_color_blend_state_create_info( + 1, + &blend_attachment_state); + + 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 dynamic_state_enables = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT, + VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT, + }; + + // Extensions are used alternatively. Switching to a different extension triggers pipeline rebuild. + switch (current_blend_option) + { + case 0: + dynamic_state_enables.push_back(VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT); + break; + case 1: + dynamic_state_enables.push_back(VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT); + } + + VkPipelineDynamicStateCreateInfo dynamic_state = + vkb::initializers::pipeline_dynamic_state_create_info( + dynamic_state_enables.data(), + static_cast(dynamic_state_enables.size()), + 0); + + const std::vector vertex_input_bindings = { + vkb::initializers::vertex_input_binding_description(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX), + }; + const std::vector vertex_input_attributes = { + vkb::initializers::vertex_input_attribute_description(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos)), + vkb::initializers::vertex_input_attribute_description(0, 1, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)), + }; + + VkPipelineVertexInputStateCreateInfo vertex_input_state = vkb::initializers::pipeline_vertex_input_state_create_info(); + vertex_input_state.vertexBindingDescriptionCount = static_cast(vertex_input_bindings.size()); + vertex_input_state.pVertexBindingDescriptions = vertex_input_bindings.data(); + vertex_input_state.vertexAttributeDescriptionCount = static_cast(vertex_input_attributes.size()); + vertex_input_state.pVertexAttributeDescriptions = vertex_input_attributes.data(); + + std::array shader_stages{}; + shader_stages[0] = load_shader("dynamic_blending/blending.vert", VK_SHADER_STAGE_VERTEX_BIT); + shader_stages[1] = load_shader("dynamic_blending/blending.frag", VK_SHADER_STAGE_FRAGMENT_BIT); + + VkGraphicsPipelineCreateInfo graphics_create{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO}; + graphics_create.pNext = VK_NULL_HANDLE; + graphics_create.renderPass = render_pass; + graphics_create.pInputAssemblyState = &input_assembly_state; + graphics_create.pRasterizationState = &rasterization_state; + graphics_create.pColorBlendState = &color_blend_state; + graphics_create.pMultisampleState = &multisample_state; + graphics_create.pViewportState = &viewport_state; + graphics_create.pDepthStencilState = &depth_stencil_state; + graphics_create.pDynamicState = &dynamic_state; + graphics_create.pVertexInputState = &vertex_input_state; + graphics_create.pTessellationState = VK_NULL_HANDLE; + graphics_create.stageCount = 2; + graphics_create.pStages = shader_stages.data(); + graphics_create.layout = pipeline_layout; + + VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), + pipeline_cache, + 1, + &graphics_create, + VK_NULL_HANDLE, + &pipeline)); } -void DynamicBlending::update_color_uniform() { - update_color(); - build_command_buffers(); +void DynamicBlending::update_pipeline() +{ + vkDestroyPipeline(get_device().get_handle(), pipeline, nullptr); + create_pipeline(); } -void DynamicBlending::build_command_buffers() +void DynamicBlending::randomize_color(std::array &color, bool alpha) { - VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info(); - - std::array clear_values; - clear_values[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}}; - clear_values[1].depthStencil = {0.0f, 0u}; - - 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 = static_cast(clear_values.size()); - render_pass_begin_info.pClearValues = clear_values.data(); - - for (uint32_t i = 0u; i < draw_cmd_buffers.size(); ++i) - { - render_pass_begin_info.framebuffer = framebuffers[i]; - auto &cmd_buff = draw_cmd_buffers[i]; - - VK_CHECK(vkBeginCommandBuffer(cmd_buff, &command_buffer_begin_info)); - - vkCmdBeginRenderPass(cmd_buff, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); - - VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); - vkCmdSetViewport(cmd_buff, 0u, 1u, &viewport); - - VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); - vkCmdSetScissor(cmd_buff, 0u, 1u, &scissor); - - { - vkCmdBindDescriptorSets(cmd_buff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0u, 1u, &descriptor_set, 0u, nullptr); - vkCmdBindPipeline(cmd_buff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + for (size_t i = 0; i < 3; ++i) + color[i] = rnd_dist(rnd_engine); - 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, VK_INDEX_TYPE_UINT32); - - if(reverse) { - build_command_buffer_for_plane(cmd_buff, face_preferences[1]); - build_command_buffer_for_plane(cmd_buff, face_preferences[0]); - } - else { - build_command_buffer_for_plane(cmd_buff, face_preferences[0]); - build_command_buffer_for_plane(cmd_buff, face_preferences[1]); - } - } - - draw_ui(cmd_buff); - - - vkCmdEndRenderPass(cmd_buff); - - VK_CHECK(vkEndCommandBuffer(cmd_buff)); - } + if (alpha) + color[3] = rnd_dist(rnd_engine); } -void DynamicBlending::build_command_buffer_for_plane(VkCommandBuffer &command_buffer, FacePreferences preferences) { - std::array color_bit = { - (preferences.color_bit_enabled[0] ? VK_COLOR_COMPONENT_R_BIT : 0u) | - (preferences.color_bit_enabled[1] ? VK_COLOR_COMPONENT_G_BIT : 0u) | - (preferences.color_bit_enabled[2] ? VK_COLOR_COMPONENT_B_BIT : 0u) | - (preferences.color_bit_enabled[3] ? VK_COLOR_COMPONENT_A_BIT : 0u) - }; - vkCmdSetColorWriteMaskEXT(command_buffer, 0, 1, color_bit.data()); - vkCmdDrawIndexed(command_buffer, preferences.index_count, 1, preferences.index_offset, 0, 0); +void DynamicBlending::build_command_buffers() +{ + VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info(); + + std::array clear_values; + clear_values[0].color = {clear_color[0], clear_color[1], clear_color[2], clear_color[3]}; + clear_values[1].depthStencil = {0.0f, 0u}; + + 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 = static_cast(clear_values.size()); + render_pass_begin_info.pClearValues = clear_values.data(); + + for (uint32_t i = 0u; i < draw_cmd_buffers.size(); ++i) + { + render_pass_begin_info.framebuffer = framebuffers[i]; + auto &cmd_buff = draw_cmd_buffers[i]; + + VK_CHECK(vkBeginCommandBuffer(cmd_buff, &command_buffer_begin_info)); + + vkCmdBeginRenderPass(cmd_buff, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); + + VkViewport viewport = vkb::initializers::viewport(static_cast(width), static_cast(height), 0.0f, 1.0f); + vkCmdSetViewport(cmd_buff, 0u, 1u, &viewport); + + VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(cmd_buff, 0u, 1u, &scissor); + + vkCmdBindDescriptorSets(cmd_buff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0u, 1u, &descriptor_set, 0u, nullptr); + vkCmdBindPipeline(cmd_buff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + + 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, VK_INDEX_TYPE_UINT32); + + const VkBool32 blend_enable = this->blend_enable; + vkCmdSetColorBlendEnableEXT(cmd_buff, 0, 1, &blend_enable); + + if (current_blend_option == 0) + { + VkColorBlendEquationEXT color_blend_equation; + color_blend_equation.colorBlendOp = blend_operator.values[current_blend_color_operator_index]; + color_blend_equation.srcColorBlendFactor = static_cast(current_src_color_blend_factor); + color_blend_equation.dstColorBlendFactor = static_cast(current_dst_color_blend_factor); + color_blend_equation.alphaBlendOp = blend_operator.values[current_blend_alpha_operator_index]; + color_blend_equation.srcAlphaBlendFactor = static_cast(current_src_alpha_blend_factor); + color_blend_equation.dstAlphaBlendFactor = static_cast(current_dst_alpha_blend_factor); + vkCmdSetColorBlendEquationEXT(cmd_buff, 0, 1, &color_blend_equation); + } + else + { + VkColorBlendAdvancedEXT color_blend_advanced; + color_blend_advanced.advancedBlendOp = advanced_blend_operator.values[current_advanced_blend_operator_index]; + color_blend_advanced.srcPremultiplied = src_premultiplied; + color_blend_advanced.dstPremultiplied = dst_premultiplied; + color_blend_advanced.blendOverlap = VK_BLEND_OVERLAP_UNCORRELATED_EXT; + color_blend_advanced.clampResults = VK_TRUE; + vkCmdSetColorBlendAdvancedEXT(cmd_buff, 0, 1, &color_blend_advanced); + } + + auto build_command_buffer_for_plane = [&](VkCommandBuffer &command_buffer, FacePreferences preferences) { + VkColorComponentFlags color_bit_enabled = + (preferences.color_bit_enabled[0] ? VK_COLOR_COMPONENT_R_BIT : 0u) | + (preferences.color_bit_enabled[1] ? VK_COLOR_COMPONENT_G_BIT : 0u) | + (preferences.color_bit_enabled[2] ? VK_COLOR_COMPONENT_B_BIT : 0u) | + (preferences.color_bit_enabled[3] ? VK_COLOR_COMPONENT_A_BIT : 0u); + vkCmdSetColorWriteMaskEXT(command_buffer, 0, 1, &color_bit_enabled); + vkCmdDrawIndexed(command_buffer, preferences.index_count, 1, preferences.index_offset, 0, 0); + }; + + // Trivial order-independent transparency + if (reverse_face_draw) + { + build_command_buffer_for_plane(cmd_buff, face_preferences[1]); + build_command_buffer_for_plane(cmd_buff, face_preferences[0]); + } + else + { + build_command_buffer_for_plane(cmd_buff, face_preferences[0]); + build_command_buffer_for_plane(cmd_buff, face_preferences[1]); + } + + draw_ui(cmd_buff); + + vkCmdEndRenderPass(cmd_buff); + + VK_CHECK(vkEndCommandBuffer(cmd_buff)); + } } - void DynamicBlending::on_update_ui_overlay(vkb::Drawer &drawer) { - FacePreferences ¤t_plane = face_preferences[current_face_index]; - if (drawer.button("Switch face")) { - - current_face_index = current_face_index == 0 ? 1 : 0; - on_update_ui_overlay(drawer); - } - if (drawer.header(current_face_index == 0 ? "Far face" : "Close face")) - { - if (drawer.color_picker("Top left", current_plane.color[0], ImGuiColorEditFlags_None, 200)) - { - update_color_uniform(); - } - if (drawer.color_picker("Top right", current_plane.color[1], ImGuiColorEditFlags_None, 200)) - { - update_color_uniform(); - } - if (drawer.color_picker("Bottom left", current_plane.color[2], ImGuiColorEditFlags_None, 200)) - { - update_color_uniform(); - } - if (drawer.color_picker("Bottom right", current_plane.color[3], ImGuiColorEditFlags_None, 200)) - { - update_color_uniform(); - } - if (drawer.checkbox("Red", ¤t_plane.color_bit_enabled[0])) - { - update_color_uniform(); - } - if (drawer.checkbox("Green",¤t_plane.color_bit_enabled[1])) - { - update_color_uniform(); - } - if (drawer.checkbox("Blue", ¤t_plane.color_bit_enabled[2])) - { - update_color_uniform(); - } - } + uint32_t item_id = 0; + + auto add_color_edit = [&](const char *caption, std::array &color) { + ImGuiColorEditFlags flags = ImGuiColorEditFlags_None | ImGuiColorEditFlags_Float; + float color_edit_width = 200; + ImGui::PushID(++item_id); + if (drawer.color_edit(caption, color, color_edit_width, flags)) + { + update_color_buffer(); + } + ImGui::PopID(); + }; + + auto add_random_button = [&](std::array, 4> &color_arr) { + ImGui::PushID(++item_id); + if (drawer.button("Random")) + { + for (int i = 0; i < 4; ++i) + randomize_color(color_arr[i]); + update_color_buffer(); + } + ImGui::PopID(); + }; + + auto add_color_mask_checkbox = [&](const char *caption, bool &enabled, bool same_line = true) { + ImGui::PushID(++item_id); + if (drawer.checkbox(caption, &enabled)) + { + build_command_buffers(); + } + ImGui::PopID(); + if (same_line) + ImGui::SameLine(); + }; + + auto add_checkbox = [&](const char *caption, bool &value) { + if (drawer.checkbox(caption, &value)) + { + build_command_buffers(); + } + }; + + auto add_blend_option_radio_button = [&](const char *caption, int32_t ¤t_option, int32_t option) { + if (drawer.radio_button(caption, ¤t_option, option)) + { + update_pipeline(); + build_command_buffers(); + } + }; + + auto add_combo_with_button = [&](const char *caption, int32_t &index, int32_t first, int32_t last, std::vector names) { + ImGui::PushID(++item_id); + if (drawer.button("Next")) + { + index = (index + 1) % (last - first + 1); + update_color_buffer(); + } + ImGui::PopID(); + ImGui::SameLine(); + if (drawer.combo_box(caption, &index, names)) + { + build_command_buffers(); + } + }; + + add_color_edit("Background", clear_color); + + for (int i = 0; i < 2; ++i) + { + FacePreferences ¤t_face = face_preferences[i]; + if (drawer.header(current_face.name.c_str())) + { + add_color_edit("Top left", current_face.color[0]); + add_color_edit("Top right", current_face.color[1]); + add_color_edit("Bottom left", current_face.color[2]); + add_color_edit("Bottom right", current_face.color[3]); + + add_random_button(current_face.color); + + drawer.text("Color write mask"); + add_color_mask_checkbox("Red", current_face.color_bit_enabled[0]); + add_color_mask_checkbox("Green", current_face.color_bit_enabled[1]); + add_color_mask_checkbox("Blue", current_face.color_bit_enabled[2]); + add_color_mask_checkbox("Alpha", current_face.color_bit_enabled[3], false); + } + } + + if (drawer.header("Blending")) + { + add_checkbox("Enabled", blend_enable); + add_blend_option_radio_button("BlendEquationEXT", current_blend_option, 0); + add_blend_option_radio_button("BlendAdvancedEXT", current_blend_option, 1); + + switch (current_blend_option) + { + case 0: + if (drawer.header("BlendEquationEXT")) + { + add_combo_with_button("Color operator", current_blend_color_operator_index, VK_BLEND_OP_ADD, VK_BLEND_OP_MAX, blend_operator.names); + add_combo_with_button("SrcColorBlendFactor", current_src_color_blend_factor, VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, blend_factor.names); + add_combo_with_button("DstColorBlendFactor", current_dst_color_blend_factor, VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, blend_factor.names); + + add_combo_with_button("Alpha operator", current_blend_alpha_operator_index, VK_BLEND_OP_ADD, VK_BLEND_OP_MAX, blend_operator.names); + add_combo_with_button("SrcAlphaBlendFactor", current_src_alpha_blend_factor, VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, blend_factor.names); + add_combo_with_button("DstAlphaBlendFactor", current_dst_alpha_blend_factor, VK_BLEND_FACTOR_ZERO, VK_BLEND_FACTOR_SRC_ALPHA_SATURATE, blend_factor.names); + } + break; + case 1: + if (drawer.header("BlendAdvancedEXT")) + { + add_combo_with_button("Operator", current_advanced_blend_operator_index, VK_BLEND_OP_ZERO_EXT, VK_BLEND_OP_BLUE_EXT, advanced_blend_operator.names); + add_checkbox("SrcPremultiplied", src_premultiplied); + add_checkbox("DstPremultiplied", dst_premultiplied); + } + } + } } void DynamicBlending::render(float delta_time) { - if (!prepared) - { - return; - } - draw(); - if (camera.updated) - { - update_uniform_buffers(); - } + if (!prepared) + { + return; + } + draw(); + if (camera.updated) + { + update_camera_buffer(); + } } void DynamicBlending::draw() { - ApiVulkanSample::prepare_frame(); - submit_info.commandBufferCount = 1; - submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; + ApiVulkanSample::prepare_frame(); + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer]; - VK_CHECK(vkQueueSubmit(queue, 1u, &submit_info, VK_NULL_HANDLE)); - ApiVulkanSample::submit_frame(); + VK_CHECK(vkQueueSubmit(queue, 1u, &submit_info, VK_NULL_HANDLE)); + ApiVulkanSample::submit_frame(); - VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; } +bool DynamicBlending::resize(const uint32_t width, const uint32_t height) +{ + ApiVulkanSample::resize(width, height); + update_camera_buffer(); + return true; +} std::unique_ptr create_dynamic_blending() { - return std::make_unique(); + return std::make_unique(); } diff --git a/samples/extensions/dynamic_blending/dynamic_blending.h b/samples/extensions/dynamic_blending/dynamic_blending.h index 4d23e81e0e..b48facdaa6 100644 --- a/samples/extensions/dynamic_blending/dynamic_blending.h +++ b/samples/extensions/dynamic_blending/dynamic_blending.h @@ -22,91 +22,108 @@ class DynamicBlending : public ApiVulkanSample { public: - DynamicBlending(); - ~DynamicBlending(); - - void render(float delta_time) override; - void build_command_buffers() override; - bool prepare(const vkb::ApplicationOptions &options) override; - void request_gpu_features(vkb::PhysicalDevice &gpu) override; - void on_update_ui_overlay(vkb::Drawer &drawer) override; - - void prepare_scene(); - void setup_descriptor_pool(); - void create_descriptor_set_layout(); - void create_descriptor_set(); - void create_pipelines(); - void prepare_uniform_buffers(); - void update_uniform_buffers(); - void update_color_uniform(); - void draw(); - -private: - VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT blend_properties; - - bool reverse = false; - - VkCommandBuffer copy_cmd; - - struct Texture { - VkImage image; - VkDeviceMemory memory; - VkImageView view; - VkSampler sampler; - } texture; - - struct FacePreferences { - uint16_t index_offset; - uint16_t index_count; - std::array color_bit_enabled; - std::array color; - } face_preferences[2]; - - struct Vertex - { - std::array pos; - std::array uv; - }; - - struct CameraUbo - { - alignas(16) glm::mat4 projection; - alignas(16) glm::mat4 view; - alignas(16) glm::mat4 model; - }; - - struct ColorUbo - { - alignas(32) glm::vec4 data[8]; - }; - - struct - { - std::unique_ptr common; - } uniform_buffers; - - std::unique_ptr vertex_buffer; - std::unique_ptr index_buffer; - uint32_t index_count = 0; - - std::vector vertices; - uint32_t vertex_buffer_size; - uint8_t current_face_index = 1; - - std::unique_ptr camera_ubo; - std::array color_bit; - - ColorUbo color; - std::unique_ptr color_ubo; - - VkDescriptorPool descriptor_pool; - VkDescriptorSetLayout descriptor_set_layout; - VkPipelineLayout pipeline_layout; - VkDescriptorSet descriptor_set; - VkPipeline pipeline; - - void build_command_buffer_for_plane(VkCommandBuffer &command_buffer, FacePreferences preferences); - void update_color(); + DynamicBlending(); + ~DynamicBlending(); + + void render(float delta_time) override; + void build_command_buffers() override; + bool prepare(const vkb::ApplicationOptions &options) override; + void request_gpu_features(vkb::PhysicalDevice &gpu) override; + void on_update_ui_overlay(vkb::Drawer &drawer) override; + bool resize(const uint32_t width, const uint32_t height) override; + + private: + void prepare_scene(); + void setup_descriptor_pool(); + void create_descriptor_set_layout(); + void create_descriptor_set(); + void create_pipeline(); + void prepare_uniform_buffers(); + void update_camera_buffer(); + void update_color_buffer(); + void draw(); + void initialize_operator_lists(); + void update_pipeline(); + + VkDescriptorPool descriptor_pool; + VkDescriptorSetLayout descriptor_set_layout; + VkPipelineLayout pipeline_layout; + VkDescriptorSet descriptor_set; + VkPipeline pipeline; + + bool reverse_face_draw = false; + + VkCommandBuffer copy_cmd; + + struct FacePreferences + { + std::string name; + uint16_t index_offset; + uint16_t index_count; + std::array color_bit_enabled; + std::array, 4> color; + } face_preferences[2]; + + struct Vertex + { + std::array pos; + std::array uv; + }; + std::vector vertices; + + std::unique_ptr vertex_buffer; + uint32_t vertex_buffer_size; + std::unique_ptr index_buffer; + uint32_t index_count = 0; + + struct CameraUbo + { + alignas(16) glm::mat4 projection; + alignas(16) glm::mat4 view; + alignas(16) glm::mat4 model; + }; + std::unique_ptr camera_ubo; + + struct ColorUbo + { + alignas(32) glm::vec4 data[8]; + }; + ColorUbo color; + std::unique_ptr color_ubo; + + struct + { + std::vector values; + std::vector names; + } blend_operator, advanced_blend_operator; + + struct + { + std::vector names; + } blend_factor; + + std::array clear_color = {0.5f, 0.5f, 0.5f, 1.0f}; + + int32_t current_blend_color_operator_index = VK_BLEND_OP_ADD; + int32_t current_blend_alpha_operator_index = VK_BLEND_OP_ADD; + int32_t current_advanced_blend_operator_index = VK_BLEND_OP_SRC_OVER_EXT - VK_BLEND_OP_ZERO_EXT; + ; + int32_t current_blend_option = 0; + int32_t current_src_color_blend_factor = VK_BLEND_FACTOR_SRC_ALPHA; + int32_t current_dst_color_blend_factor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + int32_t current_src_alpha_blend_factor = VK_BLEND_FACTOR_ZERO; + int32_t current_dst_alpha_blend_factor = VK_BLEND_FACTOR_ONE; + + bool blend_enable = true; + bool src_premultiplied = true; + bool dst_premultiplied = true; + + std::default_random_engine rnd_engine; + std::uniform_real_distribution rnd_dist; + + // void build_command_buffer_for_plane(VkCommandBuffer &command_buffer, FacePreferences preferences); + // void update_color(); + void randomize_color(std::array &color, bool alpha = false); }; std::unique_ptr create_dynamic_blending(); diff --git a/shaders/dynamic_blending/blending.frag b/shaders/dynamic_blending/blending.frag index 73eea247fe..b71a426d91 100644 --- a/shaders/dynamic_blending/blending.frag +++ b/shaders/dynamic_blending/blending.frag @@ -27,7 +27,8 @@ layout (location = 1) flat in uint colorOffset; layout (location = 0) out vec4 outColor; -vec4 sampleTexture(vec2 uv) +// Bilinear interpolation +vec4 interpolation(vec2 uv) { vec4 c00 = color.data[0 + colorOffset]; vec4 c01 = color.data[1 + colorOffset]; @@ -44,7 +45,7 @@ vec4 sampleTexture(vec2 uv) void main() { - outColor = sampleTexture(inUV); + outColor = interpolation(inUV); } diff --git a/shaders/dynamic_blending/blending.vert b/shaders/dynamic_blending/blending.vert index 2707a08607..73c2f9b204 100644 --- a/shaders/dynamic_blending/blending.vert +++ b/shaders/dynamic_blending/blending.vert @@ -32,6 +32,8 @@ layout (location = 1) out uint colorOffset; void main() { outUV = inUv; + // The colorOffset depends on which face (front/back) the point belongs to. + // It is used to offset the reference to the color passed as a uniform to the fragment shader. colorOffset = inPos.z == 1.0f ? 4 : 0; gl_Position = ubo.projection * ubo.view * ubo.model * vec4(inPos.xyz, 1.0f); }