diff --git a/framework/gui.cpp b/framework/gui.cpp index 9019ef914..41373e68d 100644 --- a/framework/gui.cpp +++ b/framework/gui.cpp @@ -1136,6 +1136,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); diff --git a/framework/gui.h b/framework/gui.h index 628288713..dd902638a 100644 --- a/framework/gui.h +++ b/framework/gui.h @@ -127,6 +127,15 @@ class Drawer */ bool checkbox(const char *caption, int32_t *value); + /** + * @brief Adds a radio button to the gui + * @param caption The text to display + * @param selectedOption The pointer to the variable set using the radio button. + * @param elementOption Value set to the selectedOption when the button is selected. + * @returns True if adding item was successful + */ + 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 @@ -179,33 +188,6 @@ class Drawer */ void text(const char *formatstr, ...); - /** - * @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 * @tparam OP Mode of the color element. diff --git a/framework/hpp_gui.cpp b/framework/hpp_gui.cpp index 063b82476..40d9db9ae 100644 --- a/framework/hpp_gui.cpp +++ b/framework/hpp_gui.cpp @@ -1064,6 +1064,15 @@ bool HPPDrawer::checkbox(const std::string &caption, int32_t *value) return res; } +bool HPPDrawer::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 HPPDrawer::input_float(const std::string &caption, float *value, float step, uint32_t precision) { bool res = ImGui::InputFloat(caption.c_str(), value, step, step * 10.0f, precision); diff --git a/framework/hpp_gui.h b/framework/hpp_gui.h index 585cd7bc6..37055ddb6 100644 --- a/framework/hpp_gui.h +++ b/framework/hpp_gui.h @@ -115,6 +115,15 @@ class HPPDrawer */ bool checkbox(const std::string &caption, int32_t *value); + /** + * @brief Adds a radio button to the gui + * @param caption The text to display + * @param selectedOption The pointer to the variable set using the radio button. + * @param elementOption Value set to the selectedOption when the button is selected. + * @returns True if adding item was successful + */ + 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 diff --git a/samples/README.adoc b/samples/README.adoc index 7d0942739..4e024e089 100644 --- a/samples/README.adoc +++ b/samples/README.adoc @@ -477,6 +477,12 @@ Demonstrates how a mesh shader can be used to achieve the same results as with g Demonstrate how to use shader objects. +=== xref:./extensions/dynamic_blending[Dynamic blending] + +*Extension:* https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_extended_dynamic_state.html[`VK_EXT_extended_dynamic_state3`] + +Demonstrate how to use the blending related functions available in the VK_EXT_extended_dynamic_state3 extension. + === xref:./extensions/dynamic_line_rasterization/README.adoc[Dynamic line rasterization] *Extensions:* https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_line_rasterization.html[`VK_EXT_line_rasterization`], https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_extended_dynamic_state3.html[`VK_EXT_extended_dynamic_state3`] diff --git a/samples/extensions/dynamic_blending/CMakeLists.txt b/samples/extensions/dynamic_blending/CMakeLists.txt new file mode 100644 index 000000000..555d1a114 --- /dev/null +++ b/samples/extensions/dynamic_blending/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (c) 2023, Mobica Limited +# +# 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 "Khronos" + NAME "dynamic_blending" + DESCRIPTION "Dynamic blending options available in the VK_EXT_extended_dynamic_state3 extension." + SHADER_FILES_GLSL + "dynamic_blending/blending.vert" + "dynamic_blending/blending.frag" +) diff --git a/samples/extensions/dynamic_blending/README.adoc b/samples/extensions/dynamic_blending/README.adoc new file mode 100644 index 000000000..9c9815b10 --- /dev/null +++ b/samples/extensions/dynamic_blending/README.adoc @@ -0,0 +1,67 @@ +//// +- Copyright (c) 2023, Mobica Limited +- +- 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. +- +//// + +== Dynamic blending + +=== 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 extension `VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME` need to be enabled. +The extension `VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME` is required for the advanced blend equations. +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 an array of +`VkColorBlendEquationEXT` objects which determine operators and factors for +color and alpha blending. +* The `VkCmdSetColorBlendAdvancedEXT` function expects an array of `VkColorBlendAdvancedEXT` objects, which determine +blending operators and premultiplication for color blending. +* The `vkCmdSetColorWriteMaskEXT` function expects an array of +`VkColorComponentFlags` objects. These objects can be created by combining +the desired color bit flags using bitwise oring. + +=== 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/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/vkCmdSetColorWriteMaskEXT.html diff --git a/samples/extensions/dynamic_blending/dynamic_blending.cpp b/samples/extensions/dynamic_blending/dynamic_blending.cpp new file mode 100644 index 000000000..e431ab810 --- /dev/null +++ b/samples/extensions/dynamic_blending/dynamic_blending.cpp @@ -0,0 +1,629 @@ +/* Copyright (c) 2023, Mobica + * + * 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 "dynamic_blending.h" + +#include "common/vk_common.h" +#include "gui.h" + +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); + + title = "Dynamic blending"; +} + +DynamicBlending::~DynamicBlending() +{ + 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); + + initialize_operator_names(); + prepare_uniform_buffers(); + prepare_scene(); + setup_descriptor_pool(); + create_descriptor_set_layout(); + create_descriptor_set(); + create_pipelines(); + 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::initialize_operator_names() +{ + 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)); + } + current_blend_color_operator_index = VK_BLEND_OP_ADD; + current_blend_alpha_operator_index = VK_BLEND_OP_ADD; + + 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)); + } + current_advanced_blend_operator_index = VK_BLEND_OP_SRC_OVER_EXT - VK_BLEND_OP_ZERO_EXT; + + 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 = { + {{-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, 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}}}; +} + +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; + } + { + 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::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::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(), + static_cast(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)}; + + 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)); +} + +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); + + 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); +} + +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); + + 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, + }; + + 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_pipeline() +{ + vkDestroyPipeline(get_device().get_handle(), pipeline, nullptr); + create_pipelines(); +} + +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::randomize_color(std::array &color, bool alpha) +{ + for (size_t i = 0; i < 3; ++i) + { + color[i] = rnd_dist(rnd_engine); + } + if (alpha) + color[3] = rnd_dist(rnd_engine); +} + +void DynamicBlending::update_color_uniform() +{ + update_color(); + build_command_buffers(); +} + +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_CONJOINT_EXT; + color_blend_advanced.clampResults = VK_TRUE; + vkCmdSetColorBlendAdvancedEXT(cmd_buff, 0, 1, &color_blend_advanced); + } + + 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)); + } +} + +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::on_update_ui_overlay(vkb::Drawer &drawer) +{ + 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_op(caption, color, color_edit_width, flags)) + // if (drawer.color_edit(caption, color, color_edit_width, flags)) + { + update_color_uniform(); + } + 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)) + { + update_color_uniform(); + } + ImGui::PopID(); + if (same_line) + ImGui::SameLine(); + }; + + auto add_next_button = [&](int32_t &index, int32_t first, int32_t last) { + + }; + + 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_uniform_buffers(); + } + ImGui::PopID(); + ImGui::SameLine(); + if (drawer.combo_box(caption, &index, names)) + { + update_uniform_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_index == 0 ? "First face" : "Second face")) + { + 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]); + + ImGui::PushID(++item_id); + if (drawer.button("Random")) + { + for (int i = 0; i < 4; ++i) + randomize_color(current_face.color[i]); + update_color(); + } + ImGui::PopID(); + + 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")) + { + if (drawer.checkbox("Enabled", &blend_enable)) + { + update_color_uniform(); + } + if (drawer.radio_button("BlendEquationEXT", ¤t_blend_option, 0)) + { + update_pipeline(); + update_color_uniform(); + } + if (drawer.radio_button("BlendAdvancedEXT", ¤t_blend_option, 1)) + { + update_pipeline(); + update_color_uniform(); + } + 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); + if (drawer.checkbox("Src premultiplied", &src_premultiplied)) + { + update_color_uniform(); + } + if (drawer.checkbox("Dst premultiplied", &dst_premultiplied)) + { + update_color_uniform(); + } + } + } + } +} + +void DynamicBlending::render(float delta_time) +{ + if (!prepared) + { + return; + } + draw(); + if (camera.updated) + { + update_uniform_buffers(); + } +} + +void DynamicBlending::draw() +{ + 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(); + + 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_uniform_buffers(); + return true; +} + +std::unique_ptr create_dynamic_blending() +{ + return std::make_unique(); +} diff --git a/samples/extensions/dynamic_blending/dynamic_blending.h b/samples/extensions/dynamic_blending/dynamic_blending.h new file mode 100644 index 000000000..41dd5de23 --- /dev/null +++ b/samples/extensions/dynamic_blending/dynamic_blending.h @@ -0,0 +1,144 @@ +/* Copyright (c) 2023, Mobica + * + * 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" + +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; + bool resize(const uint32_t width, const uint32_t height) 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(); + void initialize_operator_names(); + void update_pipeline(); + + 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, 4> 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; + + struct + { + std::vector values; + std::vector names; + } blend_operator, advanced_blend_operator; + + std::vector blend_factor_names; + + 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; + + std::array clear_color = {0.5f, 0.5f, 0.5f, 1.0f}; + int32_t current_blend_color_operator_index; + int32_t current_blend_alpha_operator_index; + int32_t current_advanced_blend_operator_index; + 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; + bool clamp_results = 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 new file mode 100644 index 000000000..73eea247f --- /dev/null +++ b/shaders/dynamic_blending/blending.frag @@ -0,0 +1,56 @@ +#version 450 +/* Copyright (c) 2023, Mobica Limited + * + * 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. + */ + +layout (binding = 1) uniform Col +{ + vec4 data[8]; +} color; + + +layout (location = 0) in vec2 inUV; +layout (location = 1) flat in uint colorOffset; + +layout (location = 0) out vec4 outColor; + +vec4 sampleTexture(vec2 uv) +{ + vec4 c00 = color.data[0 + colorOffset]; + vec4 c01 = color.data[1 + colorOffset]; + vec4 c02 = color.data[2 + colorOffset]; + vec4 c03 = color.data[3 + colorOffset]; + + vec4 b0 = mix(c00, c01, uv.x); + vec4 b1 = mix(c02, c03, uv.x); + + vec4 p0 = mix(b0, b1, uv.y); + + return p0; +} + +void main() +{ + outColor = sampleTexture(inUV); +} + + + + + + + + diff --git a/shaders/dynamic_blending/blending.vert b/shaders/dynamic_blending/blending.vert new file mode 100644 index 000000000..2707a0860 --- /dev/null +++ b/shaders/dynamic_blending/blending.vert @@ -0,0 +1,37 @@ +#version 450 +/* Copyright (c) 2023, Mobica Limited + * + * 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. + */ + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec2 inUv; + +layout (binding = 0) uniform Ubo +{ + mat4 projection; + mat4 view; + mat4 model; +} ubo; + +layout (location = 0) out vec2 outUV; +layout (location = 1) out uint colorOffset; + +void main() +{ + outUV = inUv; + colorOffset = inPos.z == 1.0f ? 4 : 0; + gl_Position = ubo.projection * ubo.view * ubo.model * vec4(inPos.xyz, 1.0f); +}