Logic operations dynamic state

The source for this sample can be found in the Khronos Vulkan samples github repository.
Sample

Overview

The 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 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).

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 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.

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 VkPipelineDynamicStateCreateInfo).

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 VkGraphicsPipelineCreateInfo needed to create the graphics pipeline.

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.

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 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 the logical operations). The background model is drawn before the cube model (with logical operations enabled) to observe blending results on the cube model.

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 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.

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.

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.

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;