Graphics state

The Mesa Vulkan runtime provides helpers for managing the numerous pieces of graphics state associated with a VkPipeline or set dynamically on a command buffer. No such helpers are provided for compute or ray-tracing because they have little or no state besides the shaders themselves.

Pipeline state

All (possibly dynamic) Vulkan graphics pipeline state is encapsulated into a single vk_graphics_pipeline_state structure which contains pointers to sub-structures for each of the different state categories. Unlike VkGraphicsPipelineCreateInfo, the pointers in vk_graphics_pipeline_state are guaranteed to be either be NULL or point to valid and properly populated memory.

When creating a pipeline, the vk_graphics_pipeline_state_fill() function can be used to gather all of the state from the core structures as well as various pNext chains into a single state structure. Whenever an extension struct is missing, a reasonable default value is provided whenever possible.

vk_graphics_pipeline_state_fill() automatically handles both the render pass and dynamic rendering. For drivers which use vk_render_pass, the vk_render_pass_state structure will be populated as if for dynamic rendering, regardless of which path is used. Drivers which use their own render pass structure should parse the render pass, if available, and pass a vk_render_pass_state to the driver_rp argument of vk_graphics_pipeline_state_fill() with the relevant information from the specified subpass. If a render pass is available, vk_render_pass_state will be populated with the the information from the driver_rp. If dynamic rendering is used or the driver provides a NULL driver_rp, the vk_render_pass_state structure will be populated for dynamic rendering, including color, depth, and stencil attachment formats.

The usual flow for creating a full graphics pipeline (not library) looks like this:

struct vk_graphics_pipeline_state state = { };
struct vk_graphics_pipeline_all_state all;
vk_graphics_pipeline_state_fill(&device->vk, &state, pCreateInfo,
                                NULL, &all, NULL, 0, NULL);

/* Emit stuff using the state in `state` */

The vk_graphics_pipeline_all_state structure exists to allow the state to sit on the stack instead of requiring a heap allocation. This is useful if you intend to use the state right away and don’t need to store it. For pipeline libraries, it’s likely more useful to use the dynamically allocated version and store the dynamically allocated memory in the library pipeline.

/* Assuming we have a vk_graphics_pipeline_state in pipeline */
memset(&pipeline->state, 0, sizeof(pipeline->state));

for (uint32_t i = 0; i < lib_info->libraryCount; i++) {
   VK_FROM_HANDLE(drv_graphics_pipeline_library, lib, lib_info->pLibraries[i]);
   vk_graphics_pipeline_state_merge(&pipeline->state, &lib->state);
}

/* This assumes you have a void **state_mem in pipeline */
result = vk_graphics_pipeline_state_fill(&device->vk, &pipeline->state,
                                         pCreateInfo, NULL, NULL, pAllocator,
                                         VK_SYSTEM_ALLOCATION_SCOPE_OBJECT,
                                         &pipeline->state_mem);
if (result != VK_SUCCESS)
   return result;

State from dependent libraries can be merged together using vk_graphics_pipeline_state_merge(). vk_graphics_pipeline_state_fill() will then only attempt to populate missing fields. You can also merge dependent pipeline libraries together but store the final state on the stack for immediate consumption:

struct vk_graphics_pipeline_state state = { };

for (uint32_t i = 0; i < lib_info->libraryCount; i++) {
   VK_FROM_HANDLE(drv_graphics_pipeline_library, lib, lib_info->pLibraries[i]);
   vk_graphics_pipeline_state_merge(&state, &lib->state);
}

struct vk_graphics_pipeline_all_state all;
vk_graphics_pipeline_state_fill(&device->vk, &state, pCreateInfo,
                                NULL, &all, NULL, 0, NULL);
Warning

doxygenfunction: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenfunction: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Dynamic state

All dynamic states in Vulkan, regardless of which API version or extension introduced them, are represented by the mesa_vk_dynamic_graphics_state enum. This corresponds to the VkDynamicState enum in the Vulkan API only it’s compact (has no holes due to extension namespacing) and a bit better organized. Each enumerant is named with the name of the state group to which the dynamic state belongs as well as the name of the dynamic state itself. The fact that it’s compact allows us to use to index bitsets.

Warning

doxygenfunction: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

We also provide a vk_dynamic_graphics_state structure which contains all the dynamic graphics states, regardless of which API version or extension introduced them. This structure can be populated from a vk_graphics_pipeline_state via vk_dynamic_graphics_state_init().

Warning

doxygenfunction: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenfunction: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

There is also a vk_dynamic_graphics_state embedded in vk_command_buffer. Should you choose to use them, we provide common implementations for all vkCmdSet*() functions. Two additional functions are provided for the driver to call in CmdBindPipeline() and CmdBindVertexBuffers2():

Warning

doxygenfunction: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenfunction: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

To use the dynamic state framework, you will need the following in your pipeline structure:

struct drv_graphics_pipeline {
   ....
   struct vk_vertex_input_state vi_state;
   struct vk_sample_locations_state sl_state;
   struct vk_dynamic_graphics_state dynamic;
   ...
};

Then, in your pipeline create function,

memset(&pipeline->dynamic, 0, sizeof(pipeline->dynamic));
pipeline->dynamic->vi = &pipeline->vi_state;
pipeline->dynamic->ms.sample_locations = &pipeline->sl_state;
vk_dynamic_graphics_state_init(&pipeline->dynamic, &state);

In your implementation of vkCmdBindPipeline(),

vk_cmd_set_dynamic_graphics_state(&cmd->vk, &pipeline->dynamic_state);

And, finally, at vkCmdDraw*() time, the code to emit dynamic state into your hardware command buffer will look something like this:

static void
emit_dynamic_state(struct drv_cmd_buffer *cmd)
{
   struct vk_dynamic_graphics_state *dyn = &cmd->vk.dynamic_graphics_state;

   if (!vk_dynamic_graphics_state_any_dirty(dyn))
      return;

   if (BITSET_TEST(dyn->dirty, MESA_VK_DYNAMIC_VP_VIEWPORTS) |
       BITSET_TEST(dyn->dirty, MESA_VK_DYNAMIC_VP_VIEWPORT_COUNT)) {
      /* Re-emit viewports */
   }

   if (BITSET_TEST(dyn->dirty, MESA_VK_DYNAMIC_VP_SCISSORS) |
       BITSET_TEST(dyn->dirty, MESA_VK_DYNAMIC_VP_SCISSOR_COUNT)) {
      /* Re-emit scissors */
   }

   /* etc... */

   vk_dynamic_graphics_state_clear_dirty(dyn);
}

Any states used by the currently bound pipeline and attachments are always valid in vk_command_buffer::dynamic_graphics_state so you can always use a state even if it isn’t dirty on this particular draw.

Warning

doxygenfunction: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenfunction: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenfunction: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Depth stencil state optimization

Warning

doxygenfunction: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Reference

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenenum: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml

Warning

doxygenstruct: Cannot find file: /home/user/documentation/docs/mesa/repository/docs/doxygen_xml/index.xml