////
- 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.
-
////
= Logic operations dynamic state
ifdef::site-gen-antora[]
TIP: The source for this sample can be found in the https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/extensions/logic_op_dynamic_state[Khronos Vulkan samples github repository].
endif::[]
image::./images/logic_op_dynamic_state_screenshot.png[Sample]
== Overview
The https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_extended_dynamic_state2.html[VK_EXT_extended_dynamic_state2] extension allows to use dynamic states e.g.
the VK_DYNAMIC_STATE_LOGIC_OP_EXT.
This may help an application to change a logical operation used without creating a new pipeline.
The sample demonstrates usage of this extension with dynamically changed logical operations applied to blending.
== Surface format setting
Logical operations are applied only for https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#framebuffer-logicop[signed and unsigned integer and normalized integer framebuffers].
In the sample the surface format is changed to `VK_FORMAT_B8G8R8A8_UNORM` by overriding the virtual method `create_render_context` (derived from `ApiVulkanSample` class).
[,C++]
----
auto surface_priority_list = std::vector<VkSurfaceFormatKHR>{
{VK_FORMAT_B8G8R8A8_UNORM, VK_COLORSPACE_SRGB_NONLINEAR_KHR},
};
render_context = platform.create_render_context(*device.get(), surface, surface_priority_list);
----
== Enabling logical operations in pipeline creation
In the sample two pipelines are created:
* a pipeline for a background model (the background pipeline);
* a pipeline for a cube model (the baseline pipeline).
The cube model is used to present blending effect achieved using the dynamic logical operations.
The `logicOpEnable` member of the `VkPipelineColorBlendStateCreateInfo` structure https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineColorBlendStateCreateInfo.html[controls if logical operations should be applied].
The `logicOpEnable` member is set to `VK_TRUE` during creation of the baseline pipeline in the `create_pipeline` method.
[,C++]
----
VkPipelineColorBlendStateCreateInfo color_blend_state =
vkb::initializers::pipeline_color_blend_state_create_info(1, &blend_attachment_state);
/* Enable logic operations */
color_blend_state.logicOpEnable = VK_TRUE;
----
In the same method `VK_DYNAMIC_STATE_LOGIC_OP_EXT` is added to the vector of `VkDynamicState` (used to define pipeline dynamic state in https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineDynamicStateCreateInfo.html[`VkPipelineDynamicStateCreateInfo`]).
[,C++]
----
std::vector<VkDynamicState> dynamic_state_enables = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_LOGIC_OP_EXT,
};
VkPipelineDynamicStateCreateInfo dynamic_state =
vkb::initializers::pipeline_dynamic_state_create_info(
dynamic_state_enables.data(),
static_cast<uint32_t>(dynamic_state_enables.size()),
0);
----
Both structures: `color_blend_state` and `dynamic_state` are used to define https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkGraphicsPipelineCreateInfo.html[`VkGraphicsPipelineCreateInfo`] needed to create the graphics pipeline.
[,C++]
----
VkGraphicsPipelineCreateInfo graphics_create{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
graphics_create.pColorBlendState = &color_blend_state;
graphics_create.pDynamicState = &dynamic_state;
...
VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &graphics_create, VK_NULL_HANDLE, &pipeline.baseline));
----
Logical operations are not enabled in the background pipeline.
[,C++]
----
std::vector<VkDynamicState> dynamic_state_enables_background = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
/* Disable logic operations in background pipeline */
color_blend_state.logicOpEnable = VK_FALSE;
----
== Logical operation dynamic setting in command buffer creation
Calling https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCmdSetLogicOpEXT.html[`vkCmdSetLogicOpEXT`] allows to set the logic operation dynamically.
In the sample it is called before drawing the model in the `build_command_buffers` method using a value provided by the GUI.
Available logical operations are defined by the `VkLogicOp` enumeration (check the Vulkan specification for details of https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VkLogicOp[the logical operations]).
The background model is drawn before the cube model (with logical operations enabled) to observe blending results on the cube model.
[,C++]
----
VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffer, &command_begin));
...
/* Drawing background */
draw_model(background_model, draw_cmd_buffer);
...
/* Set logic operation chosen in GUI for the cube model */
vkCmdSetLogicOpEXT(draw_cmd_buffer, static_cast<VkLogicOp>(gui_settings.selected_operation));
/* Draw model */
draw_created_model(draw_cmd_buffer);
...
VK_CHECK(vkEndCommandBuffer(draw_cmd_buffer));
----
== Enabling the required extensions
The extended dynamic state 2 API requires Vulkan 1.0 and the appropriate headers / SDK is required.
This extension has been https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_extended_dynamic_state2.html#_promotion_to_vulkan_1_3[partially] promoted to Vulkan 1.3.
The device extension `VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME` requires `VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME` instance extension to be enabled.
[,C++]
----
add_instance_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
add_device_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
----
Enabling extension features is done using `VkPhysicalDeviceExtendedDynamicState2FeaturesEXT` and `VkPhysicalDeviceExtendedDynamicStateFeaturesEXT` structures.
[,C++]
----
typedef struct VkPhysicalDeviceExtendedDynamicState2FeaturesEXT {
VkStructureType sType;
void* pNext;
VkBool32 extendedDynamicState2;
VkBool32 extendedDynamicState2LogicOp;
VkBool32 extendedDynamicState2PatchControlPoints;
} VkPhysicalD
typedef struct VkPhysicalDeviceExtendedDynamicStateFeaturesEXT {
VkStructureType sType;
void* pNext;
VkBool32 extendedDynamicState;
} VkPhysicalDeviceExtendedDynamicStateFeaturesEXT;
----
In the sample it is implemented in the `request_gpu_features` method.
[,C++]
----
auto &requested_extended_dynamic_state2_features =
gpu.request_extension_features<VkPhysicalDeviceExtendedDynamicState2FeaturesEXT>(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_2_FEATURES_EXT);
requested_extended_dynamic_state2_features.extendedDynamicState2 = VK_TRUE;
requested_extended_dynamic_state2_features.extendedDynamicState2LogicOp = VK_TRUE;
auto &requested_extended_dynamic_state_feature =
gpu.request_extension_features<VkPhysicalDeviceExtendedDynamicStateFeaturesEXT>(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT);
requested_extended_dynamic_state_feature.extendedDynamicState = VK_TRUE;
----