From 6b6c1a7ddd64ad2952b9ae86b25c537f1dcfa51b Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Wed, 29 Jun 2022 13:42:26 -0500 Subject: [PATCH 1/8] vulkan: Add a vk_limits.h file for runtime limits Individual driver limits may be smaller than these. Reviewed-by: Alyssa Rosenzweig Reviewed-by: Lionel Landwerlin Part-of: --- src/vulkan/runtime/vk_command_buffer.h | 6 +---- src/vulkan/runtime/vk_limits.h | 32 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 src/vulkan/runtime/vk_limits.h diff --git a/src/vulkan/runtime/vk_command_buffer.h b/src/vulkan/runtime/vk_command_buffer.h index 4a9b3385a307..e910bef67f25 100644 --- a/src/vulkan/runtime/vk_command_buffer.h +++ b/src/vulkan/runtime/vk_command_buffer.h @@ -25,6 +25,7 @@ #define VK_COMMAND_BUFFER_H #include "vk_cmd_queue.h" +#include "vk_limits.h" #include "vk_object.h" #include "util/list.h" #include "util/u_dynarray.h" @@ -38,11 +39,6 @@ struct vk_framebuffer; struct vk_image_view; struct vk_render_pass; -/* Since VkSubpassDescription2::viewMask is a 32-bit integer, there are a - * maximum of 32 possible views. - */ -#define MESA_VK_MAX_MULTIVIEW_VIEW_COUNT 32 - struct vk_attachment_view_state { VkImageLayout layout; VkImageLayout stencil_layout; diff --git a/src/vulkan/runtime/vk_limits.h b/src/vulkan/runtime/vk_limits.h new file mode 100644 index 000000000000..d26c9d1fc6f8 --- /dev/null +++ b/src/vulkan/runtime/vk_limits.h @@ -0,0 +1,32 @@ +/* + * Copyright © 2022 Collabora, LTD + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef VK_LIMITS_H +#define VK_LIMITS_H + +/* Since VkSubpassDescription2::viewMask is a 32-bit integer, there are a + * maximum of 32 possible views. + */ +#define MESA_VK_MAX_MULTIVIEW_VIEW_COUNT 32 + +#endif /* VK_LIMITS_H */ -- GitLab From fb3f9c0aac601959fd8636120a6191b2ccd3dd40 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Mon, 27 Jun 2022 09:03:02 -0500 Subject: [PATCH 2/8] vulkan: Add an enum for all dynamic graphics states Also, add a helper function to turn VkPipelineDynamicStateCreateInfo into a bitset. Reviewed-by: Lionel Landwerlin Part-of: --- src/vulkan/runtime/meson.build | 2 + src/vulkan/runtime/vk_graphics_state.c | 67 ++++++++++++++++++ src/vulkan/runtime/vk_graphics_state.h | 96 ++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 src/vulkan/runtime/vk_graphics_state.c create mode 100644 src/vulkan/runtime/vk_graphics_state.h diff --git a/src/vulkan/runtime/meson.build b/src/vulkan/runtime/meson.build index 1f5a96331900..cd4d9ed4fd19 100644 --- a/src/vulkan/runtime/meson.build +++ b/src/vulkan/runtime/meson.build @@ -48,6 +48,8 @@ vulkan_runtime_files = files( 'vk_fence.h', 'vk_framebuffer.c', 'vk_framebuffer.h', + 'vk_graphics_state.c', + 'vk_graphics_state.h', 'vk_image.c', 'vk_image.h', 'vk_instance.c', diff --git a/src/vulkan/runtime/vk_graphics_state.c b/src/vulkan/runtime/vk_graphics_state.c new file mode 100644 index 000000000000..84dc41062fa3 --- /dev/null +++ b/src/vulkan/runtime/vk_graphics_state.c @@ -0,0 +1,67 @@ +#include "vk_graphics_state.h" + +void +vk_get_dynamic_graphics_states(BITSET_WORD *dynamic, + const VkPipelineDynamicStateCreateInfo *info) +{ + /* From the Vulkan 1.3.218 spec: + * + * "pDynamicState is a pointer to a VkPipelineDynamicStateCreateInfo + * structure defining which properties of the pipeline state object are + * dynamic and can be changed independently of the pipeline state. This + * can be NULL, which means no state in the pipeline is considered + * dynamic." + */ + if (info == NULL) + return; + +#define CASE(VK, MESA) \ + case VK_DYNAMIC_STATE_##VK: \ + BITSET_SET(dynamic, MESA_VK_DYNAMIC_##MESA); \ + break; + +#define CASE2(VK, MESA1, MESA2) \ + case VK_DYNAMIC_STATE_##VK: \ + BITSET_SET(dynamic, MESA_VK_DYNAMIC_##MESA1); \ + BITSET_SET(dynamic, MESA_VK_DYNAMIC_##MESA2); \ + break; + + for (uint32_t i = 0; i < info->dynamicStateCount; i++) { + switch (info->pDynamicStates[i]) { + CASE2(VERTEX_INPUT_EXT, VI, VI_BINDING_STRIDES) + CASE( VERTEX_INPUT_BINDING_STRIDE, VI_BINDING_STRIDES) + CASE( VIEWPORT, VP_VIEWPORTS) + CASE( SCISSOR, VP_SCISSORS) + CASE( LINE_WIDTH, RS_LINE_WIDTH) + CASE( DEPTH_BIAS, RS_DEPTH_BIAS_FACTORS) + CASE( BLEND_CONSTANTS, CB_BLEND_CONSTANTS) + CASE( DEPTH_BOUNDS, DS_DEPTH_BOUNDS_TEST_BOUNDS) + CASE( STENCIL_COMPARE_MASK, DS_STENCIL_COMPARE_MASK) + CASE( STENCIL_WRITE_MASK, DS_STENCIL_WRITE_MASK) + CASE( STENCIL_REFERENCE, DS_STENCIL_REFERENCE) + CASE( CULL_MODE, RS_CULL_MODE) + CASE( FRONT_FACE, RS_FRONT_FACE) + CASE( PRIMITIVE_TOPOLOGY, IA_PRIMITIVE_TOPOLOGY) + CASE2(VIEWPORT_WITH_COUNT, VP_VIEWPORT_COUNT, VP_VIEWPORTS) + CASE2(SCISSOR_WITH_COUNT, VP_SCISSOR_COUNT, VP_SCISSORS) + CASE( DEPTH_TEST_ENABLE, DS_DEPTH_TEST_ENABLE) + CASE( DEPTH_WRITE_ENABLE, DS_DEPTH_WRITE_ENABLE) + CASE( DEPTH_COMPARE_OP, DS_DEPTH_COMPARE_OP) + CASE( DEPTH_BOUNDS_TEST_ENABLE, DS_DEPTH_BOUNDS_TEST_ENABLE) + CASE( STENCIL_TEST_ENABLE, DS_STENCIL_TEST_ENABLE) + CASE( STENCIL_OP, DS_STENCIL_OP) + CASE( RASTERIZER_DISCARD_ENABLE, RS_RASTERIZER_DISCARD_ENABLE) + CASE( DEPTH_BIAS_ENABLE, RS_DEPTH_BIAS_ENABLE) + CASE( PRIMITIVE_RESTART_ENABLE, IA_PRIMITIVE_RESTART_ENABLE) + CASE( DISCARD_RECTANGLE_EXT, DR_RECTANGLES) + CASE( SAMPLE_LOCATIONS_EXT, MS_SAMPLE_LOCATIONS) + CASE( FRAGMENT_SHADING_RATE_KHR, FSR) + CASE( LINE_STIPPLE_EXT, RS_LINE_STIPPLE) + CASE( PATCH_CONTROL_POINTS_EXT, TS_PATCH_CONTROL_POINTS) + CASE( LOGIC_OP_EXT, CB_LOGIC_OP) + CASE( COLOR_WRITE_ENABLE_EXT, CB_COLOR_WRITE_ENABLES) + default: + unreachable("Unsupported dynamic graphics state"); + } + } +} diff --git a/src/vulkan/runtime/vk_graphics_state.h b/src/vulkan/runtime/vk_graphics_state.h new file mode 100644 index 000000000000..a5bcc9f2d535 --- /dev/null +++ b/src/vulkan/runtime/vk_graphics_state.h @@ -0,0 +1,96 @@ +/* + * Copyright © 2022 Collabora, Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef VK_GRAPHICS_STATE_H +#define VK_GRAPHICS_STATE_H + +#include "vulkan/vulkan_core.h" + +#include "util/bitset.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Enumeration of all Vulkan dynamic graphics states + * + * Enumerants are named with both the abreviation of the state group to which + * the state belongs as well as the name of the state itself. These are + * intended to pretty closely match the VkDynamicState enum but may not match + * perfectly all the time. + */ +enum mesa_vk_dynamic_graphics_state { + MESA_VK_DYNAMIC_VI, + MESA_VK_DYNAMIC_VI_BINDING_STRIDES, + MESA_VK_DYNAMIC_IA_PRIMITIVE_TOPOLOGY, + MESA_VK_DYNAMIC_IA_PRIMITIVE_RESTART_ENABLE, + MESA_VK_DYNAMIC_TS_PATCH_CONTROL_POINTS, + MESA_VK_DYNAMIC_VP_VIEWPORT_COUNT, + MESA_VK_DYNAMIC_VP_VIEWPORTS, + MESA_VK_DYNAMIC_VP_SCISSOR_COUNT, + MESA_VK_DYNAMIC_VP_SCISSORS, + MESA_VK_DYNAMIC_DR_RECTANGLES, + MESA_VK_DYNAMIC_RS_RASTERIZER_DISCARD_ENABLE, + MESA_VK_DYNAMIC_RS_CULL_MODE, + MESA_VK_DYNAMIC_RS_FRONT_FACE, + MESA_VK_DYNAMIC_RS_DEPTH_BIAS_ENABLE, + MESA_VK_DYNAMIC_RS_DEPTH_BIAS_FACTORS, + MESA_VK_DYNAMIC_RS_LINE_WIDTH, + MESA_VK_DYNAMIC_RS_LINE_STIPPLE, + MESA_VK_DYNAMIC_FSR, + MESA_VK_DYNAMIC_MS_SAMPLE_LOCATIONS, + MESA_VK_DYNAMIC_DS_DEPTH_TEST_ENABLE, + MESA_VK_DYNAMIC_DS_DEPTH_WRITE_ENABLE, + MESA_VK_DYNAMIC_DS_DEPTH_COMPARE_OP, + MESA_VK_DYNAMIC_DS_DEPTH_BOUNDS_TEST_ENABLE, + MESA_VK_DYNAMIC_DS_DEPTH_BOUNDS_TEST_BOUNDS, + MESA_VK_DYNAMIC_DS_STENCIL_TEST_ENABLE, + MESA_VK_DYNAMIC_DS_STENCIL_OP, + MESA_VK_DYNAMIC_DS_STENCIL_COMPARE_MASK, + MESA_VK_DYNAMIC_DS_STENCIL_WRITE_MASK, + MESA_VK_DYNAMIC_DS_STENCIL_REFERENCE, + MESA_VK_DYNAMIC_CB_LOGIC_OP, + MESA_VK_DYNAMIC_CB_COLOR_WRITE_ENABLES, + MESA_VK_DYNAMIC_CB_BLEND_CONSTANTS, + + /* Must be left at the end */ + MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX, +}; + +/** Populate a bitset with dynamic states + * + * This function maps a VkPipelineDynamicStateCreateInfo to a bitset indexed + * by mesa_vk_dynamic_graphics_state enumerants. + * + * @param[out] dynamic Bitset to populate + * @param[in] info VkPipelineDynamicStateCreateInfo or NULL + */ +void +vk_get_dynamic_graphics_states(BITSET_WORD *dynamic, + const VkPipelineDynamicStateCreateInfo *info); + +#ifdef __cplusplus +} +#endif + +#endif /* VK_GRAPHICS_STATE_H */ -- GitLab From fed81dc3066c9713987d9278f089756a79436599 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Mon, 4 Jul 2022 09:49:02 -0500 Subject: [PATCH 3/8] vulkan: Add standard sample locations This looks a bit funny because we define the struct in the .c file but that will be fixed in the next commit. Reviewed-by: Lionel Landwerlin Part-of: --- src/vulkan/runtime/meson.build | 2 + .../runtime/vk_standard_sample_locations.c | 162 ++++++++++++++++++ .../runtime/vk_standard_sample_locations.h | 47 +++++ 3 files changed, 211 insertions(+) create mode 100644 src/vulkan/runtime/vk_standard_sample_locations.c create mode 100644 src/vulkan/runtime/vk_standard_sample_locations.h diff --git a/src/vulkan/runtime/meson.build b/src/vulkan/runtime/meson.build index cd4d9ed4fd19..54b389d8c1d1 100644 --- a/src/vulkan/runtime/meson.build +++ b/src/vulkan/runtime/meson.build @@ -77,6 +77,8 @@ vulkan_runtime_files = files( 'vk_semaphore.h', 'vk_shader_module.c', 'vk_shader_module.h', + 'vk_standard_sample_locations.c', + 'vk_standard_sample_locations.h', 'vk_sync.c', 'vk_sync.h', 'vk_sync_binary.c', diff --git a/src/vulkan/runtime/vk_standard_sample_locations.c b/src/vulkan/runtime/vk_standard_sample_locations.c new file mode 100644 index 000000000000..6d0884a5b116 --- /dev/null +++ b/src/vulkan/runtime/vk_standard_sample_locations.c @@ -0,0 +1,162 @@ +/* + * Copyright © 2020 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "vk_standard_sample_locations.h" + +#include "util/macros.h" + +struct vk_sample_locations_state { + VkSampleCountFlagBits per_pixel; + VkExtent2D grid_size; + VkSampleLocationEXT locations[16]; +}; + +/** + * 1x MSAA has a single sample at the center: (0.5, 0.5) -> (0x8, 0x8). + */ +static const struct vk_sample_locations_state sample_locations_state_1x = { + .per_pixel = VK_SAMPLE_COUNT_1_BIT, + .grid_size = { 1, 1 }, + .locations = { + { 0.5, 0.5 }, + }, +}; + + +/** + * 2x MSAA sample positions are (0.25, 0.25) and (0.75, 0.75): + * 4 c + * 4 0 + * c 1 + */ +static const struct vk_sample_locations_state sample_locations_state_2x = { + .per_pixel = VK_SAMPLE_COUNT_2_BIT, + .grid_size = { 1, 1 }, + .locations = { + { 0.75, 0.75 }, + { 0.25, 0.25 }, + }, +}; + +/** + * Sample positions: + * 2 6 a e + * 2 0 + * 6 1 + * a 2 + * e 3 + */ +static const struct vk_sample_locations_state sample_locations_state_4x = { + .per_pixel = VK_SAMPLE_COUNT_4_BIT, + .grid_size = { 1, 1 }, + .locations = { + { 0.375, 0.125 }, + { 0.875, 0.375 }, + { 0.125, 0.625 }, + { 0.625, 0.875 }, + }, +}; + +/** + * Sample positions: + * 1 3 5 7 9 b d f + * 1 7 + * 3 3 + * 5 0 + * 7 5 + * 9 2 + * b 1 + * d 4 + * f 6 + */ +static const struct vk_sample_locations_state sample_locations_state_8x = { + .per_pixel = VK_SAMPLE_COUNT_8_BIT, + .grid_size = { 1, 1 }, + .locations = { + { 0.5625, 0.3125 }, + { 0.4375, 0.6875 }, + { 0.8125, 0.5625 }, + { 0.3125, 0.1875 }, + { 0.1875, 0.8125 }, + { 0.0625, 0.4375 }, + { 0.6875, 0.9375 }, + { 0.9375, 0.0625 }, + }, +}; + +/** + * Sample positions: + * + * 0 1 2 3 4 5 6 7 8 9 a b c d e f + * 0 15 + * 1 9 + * 2 10 + * 3 7 + * 4 13 + * 5 1 + * 6 4 + * 7 3 + * 8 12 + * 9 0 + * a 2 + * b 6 + * c 11 + * d 5 + * e 8 + * f 14 + */ +static const struct vk_sample_locations_state sample_locations_state_16x = { + .per_pixel = VK_SAMPLE_COUNT_16_BIT, + .grid_size = { 1, 1 }, + .locations = { + { 0.5625, 0.5625 }, + { 0.4375, 0.3125 }, + { 0.3125, 0.6250 }, + { 0.7500, 0.4375 }, + { 0.1875, 0.3750 }, + { 0.6250, 0.8125 }, + { 0.8125, 0.6875 }, + { 0.6875, 0.1875 }, + { 0.3750, 0.8750 }, + { 0.5000, 0.0625 }, + { 0.2500, 0.1250 }, + { 0.1250, 0.7500 }, + { 0.0000, 0.5000 }, + { 0.9375, 0.2500 }, + { 0.8750, 0.9375 }, + { 0.0625, 0.0000 }, + }, +}; + +const struct vk_sample_locations_state * +vk_standard_sample_locations_state(VkSampleCountFlagBits sample_count) +{ + switch (sample_count) { + case 1: return &sample_locations_state_1x; + case 2: return &sample_locations_state_2x; + case 4: return &sample_locations_state_4x; + case 8: return &sample_locations_state_8x; + case 16: return &sample_locations_state_16x; + default: unreachable("Sample count has no standard locations"); + } +} diff --git a/src/vulkan/runtime/vk_standard_sample_locations.h b/src/vulkan/runtime/vk_standard_sample_locations.h new file mode 100644 index 000000000000..8dc57a4acd21 --- /dev/null +++ b/src/vulkan/runtime/vk_standard_sample_locations.h @@ -0,0 +1,47 @@ +/* + * Copyright © 2020 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef VK_STANDARD_SAMPLE_LOCATIONS_H +#define VK_STANDARD_SAMPLE_LOCATIONS_H + +#include "vulkan/vulkan_core.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct vk_sample_locations_state; + +/** Returns standard sample locations for a given sample count + * + * These are the sample locations defined in the Vulkan spec for when + * standardSampleLocations is supported. + */ +const struct vk_sample_locations_state* +vk_standard_sample_locations_state(VkSampleCountFlagBits sample_count); + + +#ifdef __cplusplus +} +#endif + +#endif /* VK_STANDARD_SAMPLE_LOCATIONS_H */ -- GitLab From 7ca8dcb05fcfc5cdd8e92f298fc585fac512457d Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Wed, 13 Jul 2022 10:02:30 -0500 Subject: [PATCH 4/8] vulkan: Add data structures to store all graphics state Reviewed-by: Lionel Landwerlin Part-of: --- docs/vulkan/graphics-state.rst | 135 +++ docs/vulkan/index.rst | 1 + src/vulkan/runtime/vk_graphics_state.c | 1021 +++++++++++++++++ src/vulkan/runtime/vk_graphics_state.h | 517 +++++++++ src/vulkan/runtime/vk_limits.h | 26 + .../runtime/vk_standard_sample_locations.c | 8 +- 6 files changed, 1701 insertions(+), 7 deletions(-) create mode 100644 docs/vulkan/graphics-state.rst diff --git a/docs/vulkan/graphics-state.rst b/docs/vulkan/graphics-state.rst new file mode 100644 index 000000000000..b74857c7a1ae --- /dev/null +++ b/docs/vulkan/graphics-state.rst @@ -0,0 +1,135 @@ +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 :cpp:struct:`vk_graphics_pipeline_state` structure which contains +pointers to sub-structures for each of the different state categories. +Unlike :cpp:type:`VkGraphicsPipelineCreateInfo`, the pointers in +:cpp:struct:`vk_graphics_pipeline_state` are guaranteed to be either be +NULL or point to valid and properly populated memory. + +When creating a pipeline, the +:cpp:func:`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. The +usual flow for creating a full graphics pipeline (not library) looks like +this: + +.. code-block:: c + + 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 :cpp:struct:`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. + +.. code-block:: c + + /* 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 +:cpp:func:`vk_graphics_pipeline_state_merge`. +:cpp:func:`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: + +.. code-block:: c + + 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); + +.. doxygenfunction:: vk_graphics_pipeline_state_fill + +.. doxygenfunction:: vk_graphics_pipeline_state_merge + +Reference +--------- + +.. doxygenstruct:: vk_graphics_pipeline_state + :members: + +.. doxygenstruct:: vk_vertex_binding_state + :members: + +.. doxygenstruct:: vk_vertex_attribute_state + :members: + +.. doxygenstruct:: vk_vertex_input_state + :members: + +.. doxygenstruct:: vk_input_assembly_state + :members: + +.. doxygenstruct:: vk_tessellation_state + :members: + +.. doxygenstruct:: vk_viewport_state + :members: + +.. doxygenstruct:: vk_discard_rectangles_state + :members: + +.. doxygenstruct:: vk_rasterization_state + :members: + +.. doxygenstruct:: vk_fragment_shading_rate_state + :members: + +.. doxygenstruct:: vk_sample_locations_state + :members: + +.. doxygenstruct:: vk_multisample_state + :members: + +.. doxygenstruct:: vk_stencil_test_face_state + :members: + +.. doxygenstruct:: vk_depth_stencil_state + :members: + +.. doxygenstruct:: vk_color_blend_state + :members: + +.. doxygenstruct:: vk_render_pass_state + :members: + diff --git a/docs/vulkan/index.rst b/docs/vulkan/index.rst index c4acd9e89bbb..8562261135f1 100644 --- a/docs/vulkan/index.rst +++ b/docs/vulkan/index.rst @@ -11,4 +11,5 @@ hardware-agnostic bits in common code. base-objs dispatch + graphics-state renderpass diff --git a/src/vulkan/runtime/vk_graphics_state.c b/src/vulkan/runtime/vk_graphics_state.c index 84dc41062fa3..3a8341da920a 100644 --- a/src/vulkan/runtime/vk_graphics_state.c +++ b/src/vulkan/runtime/vk_graphics_state.c @@ -1,9 +1,120 @@ #include "vk_graphics_state.h" +#include "vk_alloc.h" +#include "vk_device.h" +#include "vk_log.h" +#include "vk_render_pass.h" +#include "vk_standard_sample_locations.h" +#include "vk_util.h" + +#include + +enum mesa_vk_graphics_state_groups { + MESA_VK_GRAPHICS_STATE_VERTEX_INPUT_BIT = (1 << 0), + MESA_VK_GRAPHICS_STATE_INPUT_ASSEMBLY_BIT = (1 << 1), + MESA_VK_GRAPHICS_STATE_TESSELLATION_BIT = (1 << 2), + MESA_VK_GRAPHICS_STATE_VIEWPORT_BIT = (1 << 3), + MESA_VK_GRAPHICS_STATE_DISCARD_RECTANGLES_BIT = (1 << 4), + MESA_VK_GRAPHICS_STATE_RASTERIZATION_BIT = (1 << 5), + MESA_VK_GRAPHICS_STATE_FRAGMENT_SHADING_RATE_BIT = (1 << 6), + MESA_VK_GRAPHICS_STATE_MULTISAMPLE_BIT = (1 << 7), + MESA_VK_GRAPHICS_STATE_DEPTH_STENCIL_BIT = (1 << 8), + MESA_VK_GRAPHICS_STATE_COLOR_BLEND_BIT = (1 << 9), + MESA_VK_GRAPHICS_STATE_RENDER_PASS_BIT = (1 << 10), +}; + +static void +clear_all_dynamic_state(BITSET_WORD *dynamic) +{ + /* Clear the whole array so there are no undefined bits at the top */ + memset(dynamic, 0, sizeof(*dynamic) * + BITSET_WORDS(MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX)); +} + +static void +get_dynamic_state_groups(BITSET_WORD *dynamic, + enum mesa_vk_graphics_state_groups groups) +{ + clear_all_dynamic_state(dynamic); + + if (groups & MESA_VK_GRAPHICS_STATE_VERTEX_INPUT_BIT) { + BITSET_SET(dynamic, MESA_VK_DYNAMIC_VI); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_VI_BINDING_STRIDES); + } + + if (groups & MESA_VK_GRAPHICS_STATE_INPUT_ASSEMBLY_BIT) { + BITSET_SET(dynamic, MESA_VK_DYNAMIC_IA_PRIMITIVE_TOPOLOGY); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_IA_PRIMITIVE_RESTART_ENABLE); + } + + if (groups & MESA_VK_GRAPHICS_STATE_TESSELLATION_BIT) + BITSET_SET(dynamic, MESA_VK_DYNAMIC_TS_PATCH_CONTROL_POINTS); + + if (groups & MESA_VK_GRAPHICS_STATE_VIEWPORT_BIT) { + BITSET_SET(dynamic, MESA_VK_DYNAMIC_VP_VIEWPORT_COUNT); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_VP_VIEWPORTS); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_VP_SCISSOR_COUNT); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_VP_SCISSORS); + } + + if (groups & MESA_VK_GRAPHICS_STATE_DISCARD_RECTANGLES_BIT) + BITSET_SET(dynamic, MESA_VK_DYNAMIC_DR_RECTANGLES); + + if (groups & MESA_VK_GRAPHICS_STATE_RASTERIZATION_BIT) { + BITSET_SET(dynamic, MESA_VK_DYNAMIC_RS_RASTERIZER_DISCARD_ENABLE); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_RS_CULL_MODE); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_RS_FRONT_FACE); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_RS_DEPTH_BIAS_ENABLE); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_RS_DEPTH_BIAS_FACTORS); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_RS_LINE_WIDTH); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_RS_LINE_STIPPLE); + } + + if (groups & MESA_VK_GRAPHICS_STATE_FRAGMENT_SHADING_RATE_BIT) + BITSET_SET(dynamic, MESA_VK_DYNAMIC_FSR); + + if (groups & MESA_VK_GRAPHICS_STATE_MULTISAMPLE_BIT) + BITSET_SET(dynamic, MESA_VK_DYNAMIC_MS_SAMPLE_LOCATIONS); + + if (groups & MESA_VK_GRAPHICS_STATE_DEPTH_STENCIL_BIT) { + BITSET_SET(dynamic, MESA_VK_DYNAMIC_DS_DEPTH_TEST_ENABLE); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_DS_DEPTH_WRITE_ENABLE); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_DS_DEPTH_COMPARE_OP); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_DS_DEPTH_BOUNDS_TEST_ENABLE); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_DS_DEPTH_BOUNDS_TEST_BOUNDS); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_DS_STENCIL_TEST_ENABLE); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_DS_STENCIL_OP); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_DS_STENCIL_COMPARE_MASK); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_DS_STENCIL_WRITE_MASK); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_DS_STENCIL_REFERENCE); + } + + if (groups & MESA_VK_GRAPHICS_STATE_COLOR_BLEND_BIT) { + BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_LOGIC_OP); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_COLOR_WRITE_ENABLES); + BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_BLEND_CONSTANTS); + } +} + +static void +validate_dynamic_state_groups(const BITSET_WORD *dynamic, + enum mesa_vk_graphics_state_groups groups) +{ +#ifndef NDEBUG + BITSET_DECLARE(all_dynamic, MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX); + get_dynamic_state_groups(all_dynamic, groups); + + for (uint32_t w = 0; w < ARRAY_SIZE(all_dynamic); w++) + assert(!(dynamic[w] & ~all_dynamic[w])); +#endif +} + void vk_get_dynamic_graphics_states(BITSET_WORD *dynamic, const VkPipelineDynamicStateCreateInfo *info) { + clear_all_dynamic_state(dynamic); + /* From the Vulkan 1.3.218 spec: * * "pDynamicState is a pointer to a VkPipelineDynamicStateCreateInfo @@ -65,3 +176,913 @@ vk_get_dynamic_graphics_states(BITSET_WORD *dynamic, } } } + +#define IS_DYNAMIC(STATE) \ + BITSET_TEST(dynamic, MESA_VK_DYNAMIC_##STATE) + +static void +vk_vertex_input_state_init(struct vk_vertex_input_state *vi, + const BITSET_WORD *dynamic, + const VkPipelineVertexInputStateCreateInfo *vi_info) +{ + assert(!IS_DYNAMIC(VI)); + + memset(vi, 0, sizeof(*vi)); + + for (uint32_t i = 0; i < vi_info->vertexBindingDescriptionCount; i++) { + const VkVertexInputBindingDescription *desc = + &vi_info->pVertexBindingDescriptions[i]; + + assert(desc->binding < MESA_VK_MAX_VERTEX_BINDINGS); + assert(desc->stride <= MESA_VK_MAX_VERTEX_BINDING_STRIDE); + assert(desc->inputRate <= 1); + + const uint32_t b = desc->binding; + vi->bindings_valid |= BITFIELD_BIT(b); + vi->bindings[b].stride = desc->stride; + vi->bindings[b].input_rate = desc->inputRate; + vi->bindings[b].divisor = 1; + } + + for (uint32_t i = 0; i < vi_info->vertexAttributeDescriptionCount; i++) { + const VkVertexInputAttributeDescription *desc = + &vi_info->pVertexAttributeDescriptions[i]; + + assert(desc->location < MESA_VK_MAX_VERTEX_ATTRIBUTES); + assert(desc->binding < MESA_VK_MAX_VERTEX_BINDINGS); + assert(vi->bindings_valid & BITFIELD_BIT(desc->binding)); + + const uint32_t a = desc->location; + vi->attributes_valid |= BITFIELD_BIT(a); + vi->attributes[a].binding = desc->binding; + vi->attributes[a].format = desc->format; + vi->attributes[a].offset = desc->offset; + } + + const VkPipelineVertexInputDivisorStateCreateInfoEXT *vi_div_state = + vk_find_struct_const(vi_info->pNext, + PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT); + if (vi_div_state) { + for (uint32_t i = 0; i < vi_div_state->vertexBindingDivisorCount; i++) { + const VkVertexInputBindingDivisorDescriptionEXT *desc = + &vi_div_state->pVertexBindingDivisors[i]; + + assert(desc->binding < MESA_VK_MAX_VERTEX_BINDINGS); + assert(vi->bindings_valid & BITFIELD_BIT(desc->binding)); + + const uint32_t b = desc->binding; + vi->bindings[b].divisor = desc->divisor; + } + } +} + +static void +vk_input_assembly_state_init(struct vk_input_assembly_state *ia, + const BITSET_WORD *dynamic, + const VkPipelineInputAssemblyStateCreateInfo *ia_info) +{ + if (IS_DYNAMIC(IA_PRIMITIVE_TOPOLOGY)) { + ia->primitive_topology = -1; + } else { + assert(ia_info->topology <= UINT8_MAX); + ia->primitive_topology = ia_info->topology; + } + + ia->primitive_restart_enable = ia_info->primitiveRestartEnable; +} + +static void +vk_tessellation_state_init(struct vk_tessellation_state *ts, + const BITSET_WORD *dynamic, + const VkPipelineTessellationStateCreateInfo *ts_info) +{ + if (IS_DYNAMIC(TS_PATCH_CONTROL_POINTS)) { + ts->patch_control_points = 0; + } else { + assert(ts_info->patchControlPoints <= UINT8_MAX); + ts->patch_control_points = ts_info->patchControlPoints; + } + + const VkPipelineTessellationDomainOriginStateCreateInfo *ts_do_info = + vk_find_struct_const(ts_info->pNext, + PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO); + if (ts_do_info != NULL) { + assert(ts_do_info->domainOrigin <= UINT8_MAX); + ts->domain_origin = ts_do_info->domainOrigin; + } else { + ts->domain_origin = VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT; + } +} + +static void +vk_viewport_state_init(struct vk_viewport_state *vp, + const BITSET_WORD *dynamic, + const VkPipelineViewportStateCreateInfo *vp_info) +{ + memset(vp, 0, sizeof(*vp)); + + if (!IS_DYNAMIC(VP_VIEWPORT_COUNT)) { + assert(vp_info->viewportCount <= MESA_VK_MAX_VIEWPORTS); + vp->viewport_count = vp_info->viewportCount; + } + + if (!IS_DYNAMIC(VP_VIEWPORTS)) { + assert(!IS_DYNAMIC(VP_VIEWPORT_COUNT)); + typed_memcpy(vp->viewports, vp_info->pViewports, + vp_info->viewportCount); + } + + if (!IS_DYNAMIC(VP_SCISSOR_COUNT)) { + assert(vp_info->scissorCount <= MESA_VK_MAX_SCISSORS); + vp->scissor_count = vp_info->scissorCount; + } + + if (!IS_DYNAMIC(VP_SCISSORS)) { + assert(!IS_DYNAMIC(VP_SCISSOR_COUNT)); + typed_memcpy(vp->scissors, vp_info->pScissors, + vp_info->scissorCount); + } + + const VkPipelineViewportDepthClipControlCreateInfoEXT *vp_dcc_info = + vk_find_struct_const(vp_info->pNext, + PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT); + if (vp_dcc_info != NULL) + vp->negative_one_to_one = vp_dcc_info->negativeOneToOne; +} + +static void +vk_discard_rectangles_state_init(struct vk_discard_rectangles_state *dr, + const BITSET_WORD *dynamic, + const VkPipelineDiscardRectangleStateCreateInfoEXT *dr_info) +{ + memset(dr, 0, sizeof(*dr)); + + if (dr_info == NULL) + return; + + dr->mode = dr_info->discardRectangleMode; + + if (!IS_DYNAMIC(DR_RECTANGLES)) { + assert(dr_info->discardRectangleCount <= MESA_VK_MAX_DISCARD_RECTANGLES); + dr->rectangle_count = dr_info->discardRectangleCount; + typed_memcpy(dr->rectangles, dr_info->pDiscardRectangles, + dr_info->discardRectangleCount); + } +} + +static void +vk_rasterization_state_init(struct vk_rasterization_state *rs, + const BITSET_WORD *dynamic, + const VkPipelineRasterizationStateCreateInfo *rs_info) +{ + *rs = (struct vk_rasterization_state) { + .rasterizer_discard_enable = false, + .conservative_mode = VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT, + .rasterization_order_amd = VK_RASTERIZATION_ORDER_STRICT_AMD, + .provoking_vertex = VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT, + .line.mode = VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT, + }; + + if (!IS_DYNAMIC(RS_RASTERIZER_DISCARD_ENABLE)) + rs->rasterizer_discard_enable = rs_info->rasterizerDiscardEnable; + + /* From the Vulkan 1.3.218 spec: + * + * "If VkPipelineRasterizationDepthClipStateCreateInfoEXT is present in + * the graphics pipeline state then depth clipping is disabled if + * VkPipelineRasterizationDepthClipStateCreateInfoEXT::depthClipEnable + * is VK_FALSE. Otherwise, if + * VkPipelineRasterizationDepthClipStateCreateInfoEXT is not present, + * depth clipping is disabled when + * VkPipelineRasterizationStateCreateInfo::depthClampEnable is VK_TRUE. + */ + rs->depth_clamp_enable = rs_info->depthClampEnable; + rs->depth_clip_enable = !rs_info->depthClampEnable; + + rs->polygon_mode = rs_info->polygonMode; + + rs->cull_mode = rs_info->cullMode; + rs->front_face = rs_info->frontFace; + rs->depth_bias.enable = rs_info->depthBiasEnable; + if ((rs_info->depthBiasEnable || IS_DYNAMIC(RS_DEPTH_BIAS_ENABLE)) && + !IS_DYNAMIC(RS_DEPTH_BIAS_FACTORS)) { + rs->depth_bias.constant = rs_info->depthBiasConstantFactor; + rs->depth_bias.clamp = rs_info->depthBiasClamp; + rs->depth_bias.slope = rs_info->depthBiasSlopeFactor; + } + rs->line.width = rs_info->lineWidth; + + vk_foreach_struct_const(ext, rs_info->pNext) { + switch (ext->sType) { + case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT: { + const VkPipelineRasterizationConservativeStateCreateInfoEXT *rcs_info = + (const VkPipelineRasterizationConservativeStateCreateInfoEXT *)ext; + rs->conservative_mode = rcs_info->conservativeRasterizationMode; + break; + } + + case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT: { + const VkPipelineRasterizationDepthClipStateCreateInfoEXT *rdc_info = + (const VkPipelineRasterizationDepthClipStateCreateInfoEXT *)ext; + rs->depth_clip_enable = rdc_info->depthClipEnable; + break; + } + + case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT: { + const VkPipelineRasterizationLineStateCreateInfoEXT *rl_info = + (const VkPipelineRasterizationLineStateCreateInfoEXT *)ext; + rs->line.mode = rl_info->lineRasterizationMode; + rs->line.stipple.enable = rl_info->stippledLineEnable; + if (rs->line.stipple.enable && !IS_DYNAMIC(RS_LINE_STIPPLE)) { + rs->line.stipple.factor = rl_info->lineStippleFactor; + rs->line.stipple.pattern = rl_info->lineStipplePattern; + } + break; + } + + case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT: { + const VkPipelineRasterizationProvokingVertexStateCreateInfoEXT *rpv_info = + (const VkPipelineRasterizationProvokingVertexStateCreateInfoEXT *)ext; + rs->provoking_vertex = rpv_info->provokingVertexMode; + break; + } + + case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD: { + const VkPipelineRasterizationStateRasterizationOrderAMD *rro_info = + (const VkPipelineRasterizationStateRasterizationOrderAMD *)ext; + rs->rasterization_order_amd = rro_info->rasterizationOrder; + break; + } + + case VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT: { + const VkPipelineRasterizationStateStreamCreateInfoEXT *rss_info = + (const VkPipelineRasterizationStateStreamCreateInfoEXT *)ext; + rs->rasterization_stream = rss_info->rasterizationStream; + break; + } + + default: + break; + } + } +} + +static void +vk_fragment_shading_rate_state_init( + struct vk_fragment_shading_rate_state *fsr, + const BITSET_WORD *dynamic, + const VkPipelineFragmentShadingRateStateCreateInfoKHR *fsr_info) +{ + if (fsr_info != NULL) { + fsr->fragment_size = fsr_info->fragmentSize; + fsr->combiner_ops[0] = fsr_info->combinerOps[0]; + fsr->combiner_ops[1] = fsr_info->combinerOps[1]; + } else { + fsr->fragment_size = (VkExtent2D) { 1, 1 }; + fsr->combiner_ops[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR; + fsr->combiner_ops[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR; + } +} + +static void +vk_sample_locations_state_init(struct vk_sample_locations_state *sl, + const VkSampleLocationsInfoEXT *sl_info) +{ + sl->per_pixel = sl_info->sampleLocationsPerPixel; + sl->grid_size = sl_info->sampleLocationGridSize; + + /* From the Vulkan 1.3.218 spec: + * + * VUID-VkSampleLocationsInfoEXT-sampleLocationsCount-01527 + * + * "sampleLocationsCount must equal sampleLocationsPerPixel * + * sampleLocationGridSize.width * sampleLocationGridSize.height" + */ + assert(sl_info->sampleLocationsCount == + sl_info->sampleLocationsPerPixel * + sl_info->sampleLocationGridSize.width * + sl_info->sampleLocationGridSize.height); + + assert(sl_info->sampleLocationsCount <= MESA_VK_MAX_SAMPLE_LOCATIONS); + typed_memcpy(sl->locations, sl_info->pSampleLocations, + sl_info->sampleLocationsCount); +} + +static void +vk_multisample_state_init(struct vk_multisample_state *ms, + const BITSET_WORD *dynamic, + const VkPipelineMultisampleStateCreateInfo *ms_info) +{ + ms->rasterization_samples = ms_info->rasterizationSamples; + ms->sample_shading_enable = ms_info->sampleShadingEnable; + ms->min_sample_shading = ms_info->minSampleShading; + + /* From the Vulkan 1.3.218 spec: + * + * "If pSampleMask is NULL, it is treated as if the mask has all bits + * set to 1." + */ + ms->sample_mask = ms_info->pSampleMask ? *ms_info->pSampleMask : ~0; + + ms->alpha_to_coverage_enable = ms_info->alphaToCoverageEnable; + ms->alpha_to_one_enable = ms_info->alphaToOneEnable; + + /* These get filled in by vk_multisample_sample_locations_state_init() */ + ms->sample_locations_enable = false; + ms->sample_locations = NULL; +} + +static bool +needs_sample_locations_state( + const BITSET_WORD *dynamic, + const VkPipelineSampleLocationsStateCreateInfoEXT *sl_info) +{ + return !IS_DYNAMIC(MS_SAMPLE_LOCATIONS) && + sl_info != NULL && sl_info->sampleLocationsEnable; +} + +static void +vk_multisample_sample_locations_state_init( + struct vk_multisample_state *ms, + struct vk_sample_locations_state *sl, + const BITSET_WORD *dynamic, + const VkPipelineMultisampleStateCreateInfo *ms_info, + const VkPipelineSampleLocationsStateCreateInfoEXT *sl_info) +{ + ms->sample_locations_enable = + sl_info != NULL && sl_info->sampleLocationsEnable; + + assert(ms->sample_locations == NULL); + if (!IS_DYNAMIC(MS_SAMPLE_LOCATIONS)) { + if (ms->sample_locations_enable) { + vk_sample_locations_state_init(sl, &sl_info->sampleLocationsInfo); + ms->sample_locations = sl; + } else { + /* Otherwise, pre-populate with the standard sample locations. If + * the driver doesn't support standard sample locations, it probably + * doesn't support custom locations either and can completely ignore + * this state. + */ + ms->sample_locations = + vk_standard_sample_locations_state(ms_info->rasterizationSamples); + } + } +} + +static void +vk_stencil_test_face_state_init(struct vk_stencil_test_face_state *face, + const VkStencilOpState *info) +{ + face->op.fail = info->failOp; + face->op.pass = info->passOp; + face->op.depth_fail = info->depthFailOp; + face->op.compare = info->compareOp; + face->compare_mask = info->compareMask; + face->write_mask = info->writeMask; + face->reference = info->reference; +} + +static void +vk_depth_stencil_state_init(struct vk_depth_stencil_state *ds, + const BITSET_WORD *dynamic, + const VkPipelineDepthStencilStateCreateInfo *ds_info) +{ + memset(ds, 0, sizeof(*ds)); + + ds->depth.test_enable = ds_info->depthTestEnable; + ds->depth.write_enable = ds_info->depthWriteEnable; + ds->depth.compare_op = ds_info->depthCompareOp; + ds->depth.bounds_test.enable = ds_info->depthBoundsTestEnable; + ds->depth.bounds_test.min = ds_info->minDepthBounds; + ds->depth.bounds_test.max = ds_info->maxDepthBounds; + + ds->stencil.test_enable = ds_info->stencilTestEnable; + vk_stencil_test_face_state_init(&ds->stencil.front, &ds_info->front); + vk_stencil_test_face_state_init(&ds->stencil.back, &ds_info->back); +} + +static void +vk_color_blend_state_init(struct vk_color_blend_state *cb, + const BITSET_WORD *dynamic, + const VkPipelineColorBlendStateCreateInfo *cb_info) +{ + memset(cb, 0, sizeof(*cb)); + + cb->logic_op_enable = cb_info->logicOpEnable; + cb->logic_op = cb_info->logicOp; + + assert(cb_info->attachmentCount <= MESA_VK_MAX_COLOR_ATTACHMENTS); + cb->attachment_count = cb_info->attachmentCount; + for (uint32_t a = 0; a < cb_info->attachmentCount; a++) { + const VkPipelineColorBlendAttachmentState *att = + &cb_info->pAttachments[a]; + + cb->attachments[a] = (struct vk_color_blend_attachment_state) { + .blend_enable = att->blendEnable, + .src_color_blend_factor = att->srcColorBlendFactor, + .dst_color_blend_factor = att->dstColorBlendFactor, + .src_alpha_blend_factor = att->srcAlphaBlendFactor, + .dst_alpha_blend_factor = att->dstAlphaBlendFactor, + .write_mask = att->colorWriteMask, + .color_blend_op = att->colorBlendOp, + .alpha_blend_op = att->alphaBlendOp, + }; + } + + for (uint32_t i = 0; i < 4; i++) + cb->blend_constants[i] = cb_info->blendConstants[i]; + + const VkPipelineColorWriteCreateInfoEXT *cw_info = + vk_find_struct_const(cb_info->pNext, PIPELINE_COLOR_WRITE_CREATE_INFO_EXT); + if (cw_info != NULL) { + assert(cb_info->attachmentCount == cw_info->attachmentCount); + for (uint32_t a = 0; a < cw_info->attachmentCount; a++) { + if (cw_info->pColorWriteEnables[a]) + cb->color_write_enables |= BITFIELD_BIT(a); + } + } else { + cb->color_write_enables = BITFIELD_MASK(cb_info->attachmentCount); + } +} + +static bool +vk_render_pass_state_is_complete(const struct vk_render_pass_state *rp) +{ + return rp->attachment_aspects != VK_IMAGE_ASPECT_METADATA_BIT; +} + +static void +vk_render_pass_state_init(struct vk_render_pass_state *rp, + const struct vk_render_pass_state *old_rp, + const VkGraphicsPipelineCreateInfo *info, + const struct vk_subpass_info *sp_info, + VkGraphicsPipelineLibraryFlagsEXT lib) +{ + /* If we already have render pass state and it has attachment info, then + * it's complete and we don't need a new one. + */ + if (old_rp != NULL && vk_render_pass_state_is_complete(old_rp)) { + *rp = *old_rp; + return; + } + + *rp = (struct vk_render_pass_state) { + .depth_attachment_format = VK_FORMAT_UNDEFINED, + .stencil_attachment_format = VK_FORMAT_UNDEFINED, + }; + + if (info->renderPass != VK_NULL_HANDLE && sp_info != NULL) { + rp->render_pass = info->renderPass; + rp->subpass = info->subpass; + rp->attachment_aspects = sp_info->attachment_aspects; + rp->view_mask = sp_info->view_mask; + return; + } + + const VkPipelineRenderingCreateInfo *r_info = + vk_get_pipeline_rendering_create_info(info); + + if (r_info == NULL) + return; + + rp->view_mask = r_info->viewMask; + + /* From the Vulkan 1.3.218 spec description of pre-rasterization state: + * + * "Fragment shader state is defined by: + * ... + * * VkRenderPass and subpass parameter + * * The viewMask parameter of VkPipelineRenderingCreateInfo (formats + * are ignored)" + * + * The description of fragment shader state contains identical text. + * + * If we have a render pass then we have full information. Even if we're + * dynamic-rendering-only, the presence of a render pass means the + * rendering info came from a vk_render_pass and is therefore complete. + * Otherwise, all we can grab is the view mask and we have to leave the + * rest for later. + */ + if (info->renderPass == VK_NULL_HANDLE && + !(lib & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT)) { + rp->attachment_aspects = VK_IMAGE_ASPECT_METADATA_BIT; + return; + } + + assert(r_info->colorAttachmentCount <= MESA_VK_MAX_COLOR_ATTACHMENTS); + rp->color_attachment_count = r_info->colorAttachmentCount; + for (uint32_t i = 0; i < r_info->colorAttachmentCount; i++) { + rp->color_attachment_formats[i] = r_info->pColorAttachmentFormats[i]; + if (r_info->pColorAttachmentFormats[i] != VK_FORMAT_UNDEFINED) + rp->attachment_aspects |= VK_IMAGE_ASPECT_COLOR_BIT; + } + + rp->depth_attachment_format = r_info->depthAttachmentFormat; + if (r_info->depthAttachmentFormat != VK_FORMAT_UNDEFINED) + rp->attachment_aspects |= VK_IMAGE_ASPECT_DEPTH_BIT; + + rp->stencil_attachment_format = r_info->stencilAttachmentFormat; + if (r_info->stencilAttachmentFormat != VK_FORMAT_UNDEFINED) + rp->attachment_aspects |= VK_IMAGE_ASPECT_STENCIL_BIT; + + const VkRenderingSelfDependencyInfoMESA *rsd_info = + vk_find_struct_const(r_info->pNext, RENDERING_SELF_DEPENDENCY_INFO_MESA); + if (rsd_info != NULL) { + STATIC_ASSERT(sizeof(rp->color_self_dependencies) * 8 >= + MESA_VK_MAX_COLOR_ATTACHMENTS); + rp->color_self_dependencies = rsd_info->colorSelfDependencies; + rp->depth_self_dependency = rsd_info->depthSelfDependency; + rp->stencil_self_dependency = rsd_info->stencilSelfDependency; + } +} + +#define FOREACH_STATE_GROUP(f) \ + f(MESA_VK_GRAPHICS_STATE_VERTEX_INPUT_BIT, \ + vk_vertex_input_state, vi); \ + f(MESA_VK_GRAPHICS_STATE_INPUT_ASSEMBLY_BIT, \ + vk_input_assembly_state, ia); \ + f(MESA_VK_GRAPHICS_STATE_TESSELLATION_BIT, \ + vk_tessellation_state, ts); \ + f(MESA_VK_GRAPHICS_STATE_VIEWPORT_BIT, \ + vk_viewport_state, vp); \ + f(MESA_VK_GRAPHICS_STATE_DISCARD_RECTANGLES_BIT, \ + vk_discard_rectangles_state, dr); \ + f(MESA_VK_GRAPHICS_STATE_RASTERIZATION_BIT, \ + vk_rasterization_state, rs); \ + f(MESA_VK_GRAPHICS_STATE_FRAGMENT_SHADING_RATE_BIT, \ + vk_fragment_shading_rate_state, fsr); \ + f(MESA_VK_GRAPHICS_STATE_MULTISAMPLE_BIT, \ + vk_multisample_state, ms); \ + f(MESA_VK_GRAPHICS_STATE_DEPTH_STENCIL_BIT, \ + vk_depth_stencil_state, ds); \ + f(MESA_VK_GRAPHICS_STATE_COLOR_BLEND_BIT, \ + vk_color_blend_state, cb); \ + f(MESA_VK_GRAPHICS_STATE_RENDER_PASS_BIT, \ + vk_render_pass_state, rp); + +static void +vk_graphics_pipeline_state_validate(const struct vk_graphics_pipeline_state *state) +{ +#ifndef NDEBUG + /* For now, we just validate dynamic state */ + enum mesa_vk_graphics_state_groups has = 0; + +#define FILL_HAS(STATE, type, s) \ + if (state->s != NULL) has |= STATE + + FOREACH_STATE_GROUP(FILL_HAS) + +#undef FILL_HAS + + validate_dynamic_state_groups(state->dynamic, has); +#endif +} + +static bool +may_have_rasterization(const struct vk_graphics_pipeline_state *state, + const BITSET_WORD *dynamic, + const VkGraphicsPipelineCreateInfo *info) +{ + if (state->rs) { + /* We default rasterizer_discard_enable to false when dynamic */ + return !state->rs->rasterizer_discard_enable; + } else { + return IS_DYNAMIC(RS_RASTERIZER_DISCARD_ENABLE) || + !info->pRasterizationState->rasterizerDiscardEnable; + } +} + +VkResult +vk_graphics_pipeline_state_fill(const struct vk_device *device, + struct vk_graphics_pipeline_state *state, + const VkGraphicsPipelineCreateInfo *info, + const struct vk_subpass_info *sp_info, + struct vk_graphics_pipeline_all_state *all, + const VkAllocationCallbacks *alloc, + VkSystemAllocationScope scope, + void **alloc_ptr_out) +{ + vk_graphics_pipeline_state_validate(state); + + BITSET_DECLARE(dynamic, MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX); + vk_get_dynamic_graphics_states(dynamic, info->pDynamicState); + + VkShaderStageFlags stages = 0; + for (uint32_t i = 0; i < info->stageCount; i++) + stages |= info->pStages[i].stage; + + /* In case we return early */ + if (alloc_ptr_out != NULL) + *alloc_ptr_out = NULL; + + /* + * First, figure out which library-level shader/state groups we need + */ + + VkGraphicsPipelineLibraryFlagsEXT lib; + if (info->flags & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR) { + const VkGraphicsPipelineLibraryCreateInfoEXT *gfx_lib_info = + vk_find_struct_const(info->pNext, GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT); + + /* If we're building a pipeline library, trust the client. + * + * From the Vulkan 1.3.218 spec: + * + * VUID-VkGraphicsPipelineLibraryCreateInfoEXT-flags-requiredbitmask + * + * "flags must not be 0" + */ + assert(gfx_lib_info->flags != 0); + lib = gfx_lib_info->flags; + } else { + /* We're building a complete pipeline. From the Vulkan 1.3.218 spec: + * + * "A complete graphics pipeline always includes pre-rasterization + * shader state, with other subsets included depending on that state. + * If the pre-rasterization shader state includes a vertex shader, + * then vertex input state is included in a complete graphics + * pipeline. If the value of + * VkPipelineRasterizationStateCreateInfo::rasterizerDiscardEnable in + * the pre-rasterization shader state is VK_FALSE or the + * VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE dynamic state is + * enabled fragment shader state and fragment output interface state + * is included in a complete graphics pipeline." + */ + lib = VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT; + + if (stages & VK_SHADER_STAGE_VERTEX_BIT) + lib |= VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT; + + if (may_have_rasterization(state, dynamic, info)) { + lib |= VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT; + lib |= VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT; + } + } + + /* + * Next, turn those into individual states. Among other things, this + * de-duplicates things like FSR and multisample state which appear in + * multiple library groups. + */ + + enum mesa_vk_graphics_state_groups needs = 0; + if (lib & VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT) { + needs |= MESA_VK_GRAPHICS_STATE_VERTEX_INPUT_BIT; + needs |= MESA_VK_GRAPHICS_STATE_INPUT_ASSEMBLY_BIT; + } + + /* Other stuff potentially depends on this so gather it early */ + struct vk_render_pass_state rp; + if (lib & (VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT | + VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT | + VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT)) { + vk_render_pass_state_init(&rp, state->rp, info, sp_info, lib); + + needs |= MESA_VK_GRAPHICS_STATE_RENDER_PASS_BIT; + + /* If the old state was incomplete but the new one isn't, set state->rp + * to NULL so it gets replaced with the new version. + */ + if (state->rp != NULL && + !vk_render_pass_state_is_complete(state->rp) && + vk_render_pass_state_is_complete(&rp)) + state->rp = NULL; + } + + if (lib & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) { + /* From the Vulkan 1.3.218 spec: + * + * VUID-VkGraphicsPipelineCreateInfo-stage-02096 + * + * "If the pipeline is being created with pre-rasterization shader + * state the stage member of one element of pStages must be either + * VK_SHADER_STAGE_VERTEX_BIT or VK_SHADER_STAGE_MESH_BIT_NV" + */ + assert(stages & (VK_SHADER_STAGE_VERTEX_BIT | + VK_SHADER_STAGE_MESH_BIT_NV)); + + if (stages & (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | + VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)) + needs |= MESA_VK_GRAPHICS_STATE_TESSELLATION_BIT; + + if (may_have_rasterization(state, dynamic, info)) + needs |= MESA_VK_GRAPHICS_STATE_VIEWPORT_BIT; + + needs |= MESA_VK_GRAPHICS_STATE_DISCARD_RECTANGLES_BIT; + needs |= MESA_VK_GRAPHICS_STATE_RASTERIZATION_BIT; + needs |= MESA_VK_GRAPHICS_STATE_FRAGMENT_SHADING_RATE_BIT; + } + + if (lib & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) { + needs |= MESA_VK_GRAPHICS_STATE_FRAGMENT_SHADING_RATE_BIT; + needs |= MESA_VK_GRAPHICS_STATE_MULTISAMPLE_BIT; + + /* From the Vulkan 1.3.218 spec: + * + * VUID-VkGraphicsPipelineCreateInfo-renderPass-06043 + * + * "If renderPass is not VK_NULL_HANDLE, the pipeline is being + * created with fragment shader state, and subpass uses a + * depth/stencil attachment, pDepthStencilState must be a valid + * pointer to a valid VkPipelineDepthStencilStateCreateInfo + * structure" + * + * VUID-VkGraphicsPipelineCreateInfo-renderPass-06053 + * + * "If renderPass is VK_NULL_HANDLE, the pipeline is being created + * with fragment shader state and fragment output interface state, + * and either of VkPipelineRenderingCreateInfo::depthAttachmentFormat + * or VkPipelineRenderingCreateInfo::stencilAttachmentFormat are not + * VK_FORMAT_UNDEFINED, pDepthStencilState must be a valid pointer to + * a valid VkPipelineDepthStencilStateCreateInfo structure" + * + * VUID-VkGraphicsPipelineCreateInfo-renderPass-06590 + * + * "If renderPass is VK_NULL_HANDLE and the pipeline is being created + * with fragment shader state but not fragment output interface + * state, pDepthStencilState must be a valid pointer to a valid + * VkPipelineDepthStencilStateCreateInfo structure" + * + * In the first case, we'll have a real set of aspects in rp. In the + * second case, where we have both fragment shader and fragment output + * state, we will also have a valid set of aspects. In the third case + * where we only have fragment shader state and no render pass, the + * vk_render_pass_state will be incomplete. + */ + if ((rp.attachment_aspects & (VK_IMAGE_ASPECT_DEPTH_BIT | + VK_IMAGE_ASPECT_STENCIL_BIT)) || + !vk_render_pass_state_is_complete(&rp)) + needs |= MESA_VK_GRAPHICS_STATE_DEPTH_STENCIL_BIT; + } + + if (lib & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT) { + if (rp.attachment_aspects & (VK_IMAGE_ASPECT_DEPTH_BIT | + VK_IMAGE_ASPECT_STENCIL_BIT)) + needs |= MESA_VK_GRAPHICS_STATE_DEPTH_STENCIL_BIT; + + if (rp.attachment_aspects & (VK_IMAGE_ASPECT_COLOR_BIT)) + needs |= MESA_VK_GRAPHICS_STATE_COLOR_BLEND_BIT; + } + + /* + * Next, Filter off any states we already have. + */ + +#define FILTER_NEEDS(STATE, type, s) \ + if (state->s != NULL) needs &= ~STATE + + FOREACH_STATE_GROUP(FILTER_NEEDS) + +#undef FILTER_NEEDS + + /* Filter dynamic state down to just what we're adding */ + BITSET_DECLARE(dynamic_filter, MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX); + get_dynamic_state_groups(dynamic_filter, needs); + BITSET_AND(dynamic, dynamic, dynamic_filter); + + /* And add it in */ + BITSET_OR(state->dynamic, state->dynamic, dynamic); + + /* + * If vertex state or fragment shading rate state are fully dynamic, we + * don't need to even allocate them. Do this after we've filtered + * dynamic state because we want to keep the MESA_VK_DYNAMIC_VI and + * MESA_VK_DYNAMIC_FSR bits in the dynamic state but don't want the + * actual state. + */ + if (BITSET_TEST(dynamic, MESA_VK_DYNAMIC_VI)) + needs &= ~MESA_VK_GRAPHICS_STATE_VERTEX_INPUT_BIT; + if (BITSET_TEST(dynamic, MESA_VK_DYNAMIC_FSR)) + needs &= ~MESA_VK_GRAPHICS_STATE_FRAGMENT_SHADING_RATE_BIT; + + /* If we don't need to set up any new states, bail early */ + if (needs == 0) + return VK_SUCCESS; + + /* + * Now, ensure that we have space for each of the states we're going to + * fill. If all != NULL, we'll pull from that. Otherwise, we need to + * allocate memory. + */ + + VK_MULTIALLOC(ma); + +#define ENSURE_STATE_IF_NEEDED(STATE, type, s) \ + struct type *new_##s = NULL; \ + if (needs & STATE) { \ + if (all == NULL) { \ + vk_multialloc_add(&ma, &new_##s, struct type, 1); \ + } else { \ + new_##s = &all->s; \ + } \ + } + + FOREACH_STATE_GROUP(ENSURE_STATE_IF_NEEDED) + +#undef ENSURE_STATE_IF_NEEDED + + /* Sample locations are a bit special. We don't want to waste the memory + * for 64 floats if we don't need to. Also, we set up standard sample + * locations if no user-provided sample locations are available. + */ + const VkPipelineSampleLocationsStateCreateInfoEXT *sl_info = NULL; + struct vk_sample_locations_state *new_sl = NULL; + if (needs & MESA_VK_GRAPHICS_STATE_MULTISAMPLE_BIT) { + sl_info = vk_find_struct_const(info->pMultisampleState->pNext, + PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT); + if (needs_sample_locations_state(dynamic, sl_info)) { + if (all == NULL) { + vk_multialloc_add(&ma, &new_sl, struct vk_sample_locations_state, 1); + } else { + new_sl = &all->ms_sample_locations; + } + } + } + + /* + * Allocate memory, if needed + */ + + if (ma.size > 0) { + assert(all == NULL); + *alloc_ptr_out = vk_multialloc_alloc2(&ma, &device->alloc, alloc, scope); + if (*alloc_ptr_out == NULL) + return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); + } + + /* + * Create aliases for various input infos so we can use or FOREACH macro + */ + +#define INFO_ALIAS(_State, s) \ + const VkPipeline##_State##StateCreateInfo *s##_info = info->p##_State##State + + INFO_ALIAS(VertexInput, vi); + INFO_ALIAS(InputAssembly, ia); + INFO_ALIAS(Tessellation, ts); + INFO_ALIAS(Viewport, vp); + INFO_ALIAS(Rasterization, rs); + INFO_ALIAS(Multisample, ms); + INFO_ALIAS(DepthStencil, ds); + INFO_ALIAS(ColorBlend, cb); + +#undef INFO_ALIAS + + const VkPipelineDiscardRectangleStateCreateInfoEXT *dr_info = + vk_find_struct_const(info->pNext, PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT); + + const VkPipelineFragmentShadingRateStateCreateInfoKHR *fsr_info = + vk_find_struct_const(info->pNext, PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR); + + /* + * Finally, fill out all the states + */ + +#define INIT_STATE_IF_NEEDED(STATE, type, s) \ + if (needs & STATE) { \ + type##_init(new_##s, dynamic, s##_info); \ + state->s = new_##s; \ + } + + /* render pass state is special and we just copy it */ +#define vk_render_pass_state_init(s, d, i) *s = rp + + FOREACH_STATE_GROUP(INIT_STATE_IF_NEEDED) + +#undef vk_render_pass_state_init +#undef INIT_STATE_IF_NEEDED + + if (needs & MESA_VK_GRAPHICS_STATE_MULTISAMPLE_BIT) { + vk_multisample_sample_locations_state_init(new_ms, new_sl, dynamic, + ms_info, sl_info); + } + + return VK_SUCCESS; +} + +#undef IS_DYNAMIC + +void +vk_graphics_pipeline_state_merge(struct vk_graphics_pipeline_state *dst, + const struct vk_graphics_pipeline_state *src) +{ + vk_graphics_pipeline_state_validate(dst); + vk_graphics_pipeline_state_validate(src); + + BITSET_OR(dst->dynamic, dst->dynamic, src->dynamic); + + /* Render pass state needs special care because a render pass state may be + * incomplete (view mask only). See vk_render_pass_state_init(). + */ + if (dst->rp != NULL && src->rp != NULL && + !vk_render_pass_state_is_complete(dst->rp) && + vk_render_pass_state_is_complete(src->rp)) + dst->rp = src->rp; + +#define MERGE(STATE, type, state) \ + if (dst->state == NULL && src->state != NULL) dst->state = src->state; + + FOREACH_STATE_GROUP(MERGE) + +#undef MERGE +} diff --git a/src/vulkan/runtime/vk_graphics_state.h b/src/vulkan/runtime/vk_graphics_state.h index a5bcc9f2d535..0d7ef70ddba3 100644 --- a/src/vulkan/runtime/vk_graphics_state.h +++ b/src/vulkan/runtime/vk_graphics_state.h @@ -26,12 +26,16 @@ #include "vulkan/vulkan_core.h" +#include "vk_limits.h" + #include "util/bitset.h" #ifdef __cplusplus extern "C" { #endif +struct vk_device; + /** Enumeration of all Vulkan dynamic graphics states * * Enumerants are named with both the abreviation of the state group to which @@ -89,6 +93,519 @@ void vk_get_dynamic_graphics_states(BITSET_WORD *dynamic, const VkPipelineDynamicStateCreateInfo *info); +struct vk_vertex_binding_state { + /** VkVertexInputBindingDescription::stride */ + uint16_t stride; + + /** VkVertexInputBindingDescription::inputRate */ + uint16_t input_rate; + + /** VkVertexInputBindingDivisorDescriptionEXT::divisor or 1 */ + uint32_t divisor; +}; + +struct vk_vertex_attribute_state { + /** VkVertexInputAttributeDescription::binding */ + uint32_t binding; + + /** VkVertexInputAttributeDescription::format */ + VkFormat format; + + /** VkVertexInputAttributeDescription::offset */ + uint32_t offset; +}; + +struct vk_vertex_input_state { + /** Bitset of which bindings are valid, indexed by binding */ + uint32_t bindings_valid; + struct vk_vertex_binding_state bindings[MESA_VK_MAX_VERTEX_BINDINGS]; + + /** Bitset of which attributes are valid, indexed by location */ + uint32_t attributes_valid; + struct vk_vertex_attribute_state attributes[MESA_VK_MAX_VERTEX_ATTRIBUTES]; +}; + +struct vk_input_assembly_state { + /** VkPipelineInputAssemblyStateCreateInfo::topology + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_IA_PRIMITIVE_TOPOLOGY + */ + uint8_t primitive_topology; + + /** VkPipelineInputAssemblyStateCreateInfo::primitiveRestartEnable + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_IA_PRIMITIVE_RESTART_ENABLE + */ + bool primitive_restart_enable; +}; + +struct vk_tessellation_state { + /** VkPipelineTessellationStateCreateInfo::patchControlPoints */ + uint8_t patch_control_points; + + /** VkPipelineTessellationDomainOriginStateCreateInfo::domainOrigin */ + uint8_t domain_origin; +}; + +struct vk_viewport_state { + /** VkPipelineViewportDepthClipControlCreateInfoEXT::negativeOneToOne */ + bool negative_one_to_one; + + /** VkPipelineViewportStateCreateInfo::viewportCount */ + uint8_t viewport_count; + + /** VkPipelineViewportStateCreateInfo::scissorCount */ + uint8_t scissor_count; + + /** VkPipelineViewportStateCreateInfo::pViewports */ + VkRect2D scissors[MESA_VK_MAX_SCISSORS]; + + /** VkPipelineViewportStateCreateInfo::pScissors */ + VkViewport viewports[MESA_VK_MAX_VIEWPORTS]; +}; + +struct vk_discard_rectangles_state { + /** VkPipelineDiscardRectangleStateCreateInfoEXT::discardRectangleMode */ + VkDiscardRectangleModeEXT mode; + + /** VkPipelineDiscardRectangleStateCreateInfoEXT::discardRectangleCount */ + uint32_t rectangle_count; + + /** VkPipelineDiscardRectangleStateCreateInfoEXT::pDiscardRectangles */ + VkRect2D rectangles[MESA_VK_MAX_DISCARD_RECTANGLES]; +}; + +struct vk_rasterization_state { + /** VkPipelineRasterizationStateCreateInfo::rasterizerDiscardEnable + * + * This will be false if rasterizer discard is dynamic + */ + bool rasterizer_discard_enable; + + /** VkPipelineRasterizationStateCreateInfo::depthClampEnable */ + bool depth_clamp_enable; + + /** VkPipelineRasterizationDepthClipStateCreateInfoEXT::depthClipEnable */ + bool depth_clip_enable; + + /** VkPipelineRasterizationStateCreateInfo::polygonMode */ + VkPolygonMode polygon_mode; + + /** VkPipelineRasterizationStateCreateInfo::cullMode */ + VkCullModeFlags cull_mode; + + /** VkPipelineRasterizationStateCreateInfo::frontFace */ + VkFrontFace front_face; + + /** VkPipelineRasterizationConservativeStateCreateInfoEXT::conservativeRasterizationMode */ + VkConservativeRasterizationModeEXT conservative_mode; + + /** VkPipelineRasterizationStateRasterizationOrderAMD::rasterizationOrder */ + VkRasterizationOrderAMD rasterization_order_amd; + + /** VkPipelineRasterizationProvokingVertexStateCreateInfoEXT::provokingVertexMode */ + VkProvokingVertexModeEXT provoking_vertex; + + /** VkPipelineRasterizationStateStreamCreateInfoEXT::rasterizationStream */ + uint32_t rasterization_stream; + + struct { + /** VkPipelineRasterizationStateCreateInfo::depthBiasEnable */ + bool enable; + + /** VkPipelineRasterizationStateCreateInfo::depthBiasConstantFactor */ + float constant; + + /** VkPipelineRasterizationStateCreateInfo::depthBiasClamp */ + float clamp; + + /** VkPipelineRasterizationStateCreateInfo::depthBiasSlopeFactor */ + float slope; + } depth_bias; + + struct { + /** VkPipelineRasterizationStateCreateInfo::lineWidth */ + float width; + + /** VkPipelineRasterizationLineStateCreateInfoEXT::lineRasterizationMode + * + * Will be set to VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT if + * VkPipelineRasterizationLineStateCreateInfoEXT is not provided. + */ + VkLineRasterizationModeEXT mode; + + struct { + /** VkPipelineRasterizationLineStateCreateInfoEXT::stippledLineEnable */ + bool enable; + + /** VkPipelineRasterizationLineStateCreateInfoEXT::lineStippleFactor */ + uint32_t factor; + + /** VkPipelineRasterizationLineStateCreateInfoEXT::lineStipplePattern */ + uint16_t pattern; + } stipple; + } line; +}; + +struct vk_fragment_shading_rate_state { + /** VkPipelineFragmentShadingRateStateCreateInfoKHR::fragmentSize + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_FSR + */ + VkExtent2D fragment_size; + + /** VkPipelineFragmentShadingRateStateCreateInfoKHR::combinerOps + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_FSR + */ + VkFragmentShadingRateCombinerOpKHR combiner_ops[2]; +}; + +struct vk_sample_locations_state { + /** VkSampleLocationsInfoEXT::sampleLocationsPerPixel */ + VkSampleCountFlagBits per_pixel; + + /** VkSampleLocationsInfoEXT::sampleLocationGridSize */ + VkExtent2D grid_size; + + /** VkSampleLocationsInfoEXT::sampleLocations */ + VkSampleLocationEXT locations[MESA_VK_MAX_SAMPLE_LOCATIONS]; +}; + +struct vk_multisample_state { + /** VkPipelineMultisampleStateCreateInfo::rasterizationSamples */ + VkSampleCountFlagBits rasterization_samples; + + /** VkPipelineMultisampleStateCreateInfo::sampleShadingEnable */ + bool sample_shading_enable; + + /** VkPipelineMultisampleStateCreateInfo::minSampleShading */ + float min_sample_shading; + + /** VkPipelineMultisampleStateCreateInfo::pSampleMask */ + uint16_t sample_mask; + + /** VkPipelineMultisampleStateCreateInfo::alphaToCoverageEnable */ + bool alpha_to_coverage_enable; + + /** VkPipelineMultisampleStateCreateInfo::alphaToOneEnable */ + bool alpha_to_one_enable; + + /** VkPipelineSampleLocationsStateCreateInfoEXT::sampleLocationsEnable */ + bool sample_locations_enable; + + /** VkPipelineSampleLocationsStateCreateInfoEXT::sampleLocationsInfo + * + * May be NULL for dynamic sample locations. + */ + const struct vk_sample_locations_state *sample_locations; +}; + +/** Represents the stencil test state for a face */ +struct vk_stencil_test_face_state { + /* + * MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_STENCIL_OP + */ + struct { + /** VkStencilOpState::failOp */ + uint8_t fail; + + /** VkStencilOpState::passOp */ + uint8_t pass; + + /** VkStencilOpState::depthFailOp */ + uint8_t depth_fail; + + /** VkStencilOpState::compareOp */ + uint8_t compare; + } op; + + /** VkStencilOpState::compareMask + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_STENCIL_COMPARE_MASK + */ + uint8_t compare_mask; + + /** VkStencilOpState::writeMask + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_STENCIL_WRITE_MASK + */ + uint8_t write_mask; + + /** VkStencilOpState::reference + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_STENCIL_REFERENCE + */ + uint8_t reference; +}; + +struct vk_depth_stencil_state { + struct { + /** VkPipelineDepthStencilStateCreateInfo::depthTestEnable + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_DEPTH_TEST_ENABLE + */ + bool test_enable; + + /** VkPipelineDepthStencilStateCreateInfo::depthWriteEnable + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_DEPTH_WRITE_ENABLE + */ + bool write_enable; + + /** VkPipelineDepthStencilStateCreateInfo::depthCompareOp + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_DEPTH_COMPARE_OP + */ + VkCompareOp compare_op; + + struct { + /** VkPipelineDepthStencilStateCreateInfo::depthBoundsTestEnable + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_DEPTH_BOUNDS_TEST_ENABLE + */ + bool enable; + + /** VkPipelineDepthStencilStateCreateInfo::min/maxDepthBounds + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_DEPTH_BOUNDS_TEST_BOUNDS + */ + float min, max; + } bounds_test; + } depth; + + struct { + /** VkPipelineDepthStencilStateCreateInfo::stencilTestEnable + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_DS_STENCIL_TEST_ENABLE + */ + bool test_enable; + + /** VkPipelineDepthStencilStateCreateInfo::front */ + struct vk_stencil_test_face_state front; + + /** VkPipelineDepthStencilStateCreateInfo::back */ + struct vk_stencil_test_face_state back; + } stencil; +}; + +struct vk_color_blend_attachment_state { + /** VkPipelineColorBlendAttachmentState::blendEnable */ + bool blend_enable; + + /** VkPipelineColorBlendAttachmentState::srcColorBlendFactor */ + uint8_t src_color_blend_factor; + + /** VkPipelineColorBlendAttachmentState::dstColorBlendFactor */ + uint8_t dst_color_blend_factor; + + /** VkPipelineColorBlendAttachmentState::srcAlphaBlendFactor */ + uint8_t src_alpha_blend_factor; + + /** VkPipelineColorBlendAttachmentState::dstAlphaBlendFactor */ + uint8_t dst_alpha_blend_factor; + + /** VkPipelineColorBlendAttachmentState::colorWriteMask */ + uint8_t write_mask; + + /** VkPipelineColorBlendAttachmentState::colorBlendOp */ + VkBlendOp color_blend_op; + + /** VkPipelineColorBlendAttachmentState::alphaBlendOp */ + VkBlendOp alpha_blend_op; +}; + +struct vk_color_blend_state { + /** VkPipelineColorBlendStateCreateInfo::logicOpEnable */ + bool logic_op_enable; + + /** VkPipelineColorBlendStateCreateInfo::logicOp */ + uint8_t logic_op; + + /** VkPipelineColorWriteCreateInfoEXT::pColorWriteEnables */ + uint8_t color_write_enables; + + /** VkPipelineColorBlendStateCreateInfo::attachmentCount */ + uint8_t attachment_count; + + /** VkPipelineColorBlendStateCreateInfo::pAttachments */ + struct vk_color_blend_attachment_state attachments[MESA_VK_MAX_COLOR_ATTACHMENTS]; + + /** VkPipelineColorBlendStateCreateInfo::blendConstants */ + float blend_constants[4]; +}; + +struct vk_render_pass_state { + /** Set of image aspects bound as color/depth/stencil attachments + * + * Set to VK_IMAGE_ASPECT_METADATA_BIT to indicate that attachment info + * is invalid. + */ + VkImageAspectFlags attachment_aspects; + + /** VkGraphicsPipelineCreateInfo::renderPass */ + VkRenderPass render_pass; + + /** VkGraphicsPipelineCreateInfo::subpass */ + uint32_t subpass; + + /** VkPipelineRenderingCreateInfo::viewMask */ + uint32_t view_mask; + + /** VkRenderingSelfDependencyInfoMESA::colorSelfDependencies */ + uint8_t color_self_dependencies; + + /** VkRenderingSelfDependencyInfoMESA::depthSelfDependency */ + bool depth_self_dependency; + + /** VkRenderingSelfDependencyInfoMESA::stencilSelfDependency */ + bool stencil_self_dependency; + + /** VkPipelineRenderingCreateInfo::colorAttachmentCount */ + uint8_t color_attachment_count; + + /** VkPipelineRenderingCreateInfo::pColorAttachmentFormats */ + VkFormat color_attachment_formats[MESA_VK_MAX_COLOR_ATTACHMENTS]; + + /** VkPipelineRenderingCreateInfo::depthAttachmentFormat */ + VkFormat depth_attachment_format; + + /** VkPipelineRenderingCreateInfo::stencilAttachmentFormat */ + VkFormat stencil_attachment_format; +}; + +struct vk_graphics_pipeline_all_state { + struct vk_vertex_input_state vi; + struct vk_input_assembly_state ia; + struct vk_tessellation_state ts; + struct vk_viewport_state vp; + struct vk_discard_rectangles_state dr; + struct vk_rasterization_state rs; + struct vk_fragment_shading_rate_state fsr; + struct vk_multisample_state ms; + struct vk_sample_locations_state ms_sample_locations; + struct vk_depth_stencil_state ds; + struct vk_color_blend_state cb; + struct vk_render_pass_state rp; +}; + +struct vk_graphics_pipeline_state { + /** Bitset of which states are dynamic */ + BITSET_DECLARE(dynamic, MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX); + + /** Vertex input state */ + const struct vk_vertex_input_state *vi; + + /** Input assembly state */ + const struct vk_input_assembly_state *ia; + + /** Tessellation state */ + const struct vk_tessellation_state *ts; + + /** Viewport state */ + const struct vk_viewport_state *vp; + + /** Discard Rectangles state */ + const struct vk_discard_rectangles_state *dr; + + /** Rasterization state */ + const struct vk_rasterization_state *rs; + + /** Fragment shading rate state */ + const struct vk_fragment_shading_rate_state *fsr; + + /** Multiesample state */ + const struct vk_multisample_state *ms; + + /** Depth stencil state */ + const struct vk_depth_stencil_state *ds; + + /** Color blend state */ + const struct vk_color_blend_state *cb; + + /** Render pass state */ + const struct vk_render_pass_state *rp; +}; + +/** Struct for extra information that we need from the subpass. + * + * This struct need only be provided if the driver has its own render pass + * implementation. If the driver uses the common render pass implementation, + * we can get this information ourselves. + */ +struct vk_subpass_info { + uint32_t view_mask; + VkImageAspectFlags attachment_aspects; +}; + +/** Populate a vk_graphics_pipeline_state from VkGraphicsPipelineCreateInfo + * + * This function crawls the provided VkGraphicsPipelineCreateInfo and uses it + * to populate the vk_graphics_pipeline_state. Upon returning from this + * function, all pointers in `state` will either be `NULL` or point to a valid + * sub-state structure. Whenever an extension struct is missing, a reasonable + * default value is provided whenever possible. Some states may be left NULL + * if the state does not exist (such as when rasterizer discard is enabled) or + * if all of the corresponding states are dynamic. + * + * This function assumes that the vk_graphics_pipeline_state is already valid + * (i.e., all pointers are NULL or point to valid states). Any states already + * present are assumed to be identical to how we would populate them from + * VkGraphicsPipelineCreateInfo. + * + * This function can operate in one of two modes with respect to how the + * memory for states is allocated. If a `vk_graphics_pipeline_all_state` + * struct is provided, any newly populated states will point to the relevant + * field in `all`. If `all == NULL`, it attempts to dynamically allocate any + * newly required states using the provided allocator and scope. The pointer + * to this new blob of memory is returned via `alloc_ptr_out` and must + * eventually be freed by the driver. + * + * @param[in] device The Vulkan device + * @param[out] state The graphics pipeline state to populate + * @param[in] info The pCreateInfo from vkCreateGraphicsPipelines + * @param[in] sp_info Subpass info if the driver implements render + * passes itself. This should be NULL for drivers + * that use the common render pass infrastructure + * built on top of dynamic rendering. + * @param[in] all The vk_graphics_pipeline_all_state to use to + * back any newly needed states. If NULL, newly + * needed states will be dynamically allocated + * instead. + * @param[in] alloc Allocation callbacks for dynamically allocating + * new state memory. + * @param[in] scope Allocation scope for dynamically allocating new + * state memory. + * @param[out] alloc_ptr_out Will be populated with a pointer to any newly + * allocated state. The driver is responsible for + * freeing this pointer. + */ +VkResult +vk_graphics_pipeline_state_fill(const struct vk_device *device, + struct vk_graphics_pipeline_state *state, + const VkGraphicsPipelineCreateInfo *info, + const struct vk_subpass_info *sp_info, + struct vk_graphics_pipeline_all_state *all, + const VkAllocationCallbacks *alloc, + VkSystemAllocationScope scope, + void **alloc_ptr_out); + +/** Merge one vk_graphics_pipeline_state into another + * + * Both the destination and source states are assumed to be valid (i.e., all + * pointers are NULL or point to valid states). Any states which exist in + * both are expected to be identical and the state already in dst is used. + * The only exception here is render pass state which may be only partially + * defined in which case the fully defined one (if any) is used. + * + * @param[out] dst The destination state. When the function returns, this + * will be the union of the original dst and src. + * @param[in] src The source state + */ +void +vk_graphics_pipeline_state_merge(struct vk_graphics_pipeline_state *dst, + const struct vk_graphics_pipeline_state *src); + #ifdef __cplusplus } #endif diff --git a/src/vulkan/runtime/vk_limits.h b/src/vulkan/runtime/vk_limits.h index d26c9d1fc6f8..8294b764975a 100644 --- a/src/vulkan/runtime/vk_limits.h +++ b/src/vulkan/runtime/vk_limits.h @@ -24,6 +24,32 @@ #ifndef VK_LIMITS_H #define VK_LIMITS_H +#define MESA_VK_MAX_VERTEX_BINDINGS 32 +#define MESA_VK_MAX_VERTEX_ATTRIBUTES 32 + +/* As of June 29, 2022, according to vulkan.gpuinfo.org, 99% of all reports + * listed a max vertex stride that fits in 16 bits. + */ +#define MESA_VK_MAX_VERTEX_BINDING_STRIDE UINT16_MAX + +#define MESA_VK_MAX_VIEWPORTS 16 +#define MESA_VK_MAX_SCISSORS 16 +#define MESA_VK_MAX_DISCARD_RECTANGLES 4 + +/* As of June 29, 2022, according to vulkan.gpuinfo.org, no reports list more + * than 16 samples for framebufferColorSampleCounts except one layer running + * on top of WARP on Windows. + */ +#define MESA_VK_MAX_SAMPLES 16 + +/* As of June 29, 2022, according to vulkan.gpuinfo.org, the only GPUs + * claiming support for maxSampleLocationGridSize greater than 1x1 is AMD + * which supports 2x2 but only up to 8 samples. + */ +#define MESA_VK_MAX_SAMPLE_LOCATIONS 32 + +#define MESA_VK_MAX_COLOR_ATTACHMENTS 8 + /* Since VkSubpassDescription2::viewMask is a 32-bit integer, there are a * maximum of 32 possible views. */ diff --git a/src/vulkan/runtime/vk_standard_sample_locations.c b/src/vulkan/runtime/vk_standard_sample_locations.c index 6d0884a5b116..de3fa4993012 100644 --- a/src/vulkan/runtime/vk_standard_sample_locations.c +++ b/src/vulkan/runtime/vk_standard_sample_locations.c @@ -23,13 +23,7 @@ #include "vk_standard_sample_locations.h" -#include "util/macros.h" - -struct vk_sample_locations_state { - VkSampleCountFlagBits per_pixel; - VkExtent2D grid_size; - VkSampleLocationEXT locations[16]; -}; +#include "vk_graphics_state.h" /** * 1x MSAA has a single sample at the center: (0.5, 0.5) -> (0x8, 0x8). -- GitLab From 09188c96595e857dbf932bc653c2da30b259abe0 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Wed, 13 Jul 2022 15:20:38 -0500 Subject: [PATCH 5/8] util: Constify __bitset_test_range Reviewed-by: Alyssa Rosenzweig Reviewed-by: Lionel Landwerlin Part-of: --- src/util/bitset.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/bitset.h b/src/util/bitset.h index 55cb5d10977c..e1e5962e59f3 100644 --- a/src/util/bitset.h +++ b/src/util/bitset.h @@ -207,7 +207,7 @@ __bitset_shl(BITSET_WORD *x, unsigned amount, unsigned n) (assert (!"BITSET_CLEAR_RANGE: bit range crosses word boundary"), 0)) static inline bool -__bitset_test_range(BITSET_WORD *r, unsigned start, unsigned end) +__bitset_test_range(const BITSET_WORD *r, unsigned start, unsigned end) { const unsigned size = end - start + 1; const unsigned start_mod = start % BITSET_WORDBITS; -- GitLab From fcedb1250bb8f05ef047b8b477169d70af9ef27d Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Wed, 29 Jun 2022 18:26:05 -0500 Subject: [PATCH 6/8] vulkan: Add a common data structure for dynamic states Reviewed-by: Lionel Landwerlin Part-of: --- docs/vulkan/graphics-state.rst | 30 ++ src/vulkan/runtime/vk_graphics_state.c | 390 +++++++++++++++++++++++++ src/vulkan/runtime/vk_graphics_state.h | 247 ++++++++++++++++ 3 files changed, 667 insertions(+) diff --git a/docs/vulkan/graphics-state.rst b/docs/vulkan/graphics-state.rst index b74857c7a1ae..278a59850314 100644 --- a/docs/vulkan/graphics-state.rst +++ b/docs/vulkan/graphics-state.rst @@ -82,6 +82,32 @@ together but store the final state on the stack for immediate consumption: .. doxygenfunction:: vk_graphics_pipeline_state_merge + +Dynamic state +------------- + +All dynamic states in Vulkan, regardless of which API version or extension +introduced them, are represented by the +:cpp:enum:`mesa_vk_dynamic_graphics_state` enum. This corresponds to the +:cpp:type:`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. + +.. doxygenfunction:: vk_get_dynamic_graphics_states + +We also provide a :cpp:struct:`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 +:cpp:struct:`vk_graphics_pipeline_state` via +:cpp:func:`vk_dynamic_graphics_state_init`. + +.. doxygenfunction:: vk_dynamic_graphics_state_init + +.. doxygenfunction:: vk_dynamic_graphics_state_copy + + Reference --------- @@ -133,3 +159,7 @@ Reference .. doxygenstruct:: vk_render_pass_state :members: +.. doxygenenum:: mesa_vk_dynamic_graphics_state + +.. doxygenstruct:: vk_dynamic_graphics_state + :members: diff --git a/src/vulkan/runtime/vk_graphics_state.c b/src/vulkan/runtime/vk_graphics_state.c index 3a8341da920a..2a6344b4d1f5 100644 --- a/src/vulkan/runtime/vk_graphics_state.c +++ b/src/vulkan/runtime/vk_graphics_state.c @@ -180,6 +180,9 @@ vk_get_dynamic_graphics_states(BITSET_WORD *dynamic, #define IS_DYNAMIC(STATE) \ BITSET_TEST(dynamic, MESA_VK_DYNAMIC_##STATE) +#define IS_NEEDED(STATE) \ + BITSET_TEST(needed, MESA_VK_DYNAMIC_##STATE) + static void vk_vertex_input_state_init(struct vk_vertex_input_state *vi, const BITSET_WORD *dynamic, @@ -236,6 +239,24 @@ vk_vertex_input_state_init(struct vk_vertex_input_state *vi, } } +static void +vk_dynamic_graphics_state_init_vi(struct vk_dynamic_graphics_state *dst, + const BITSET_WORD *needed, + const struct vk_vertex_input_state *vi) +{ + if (IS_NEEDED(VI)) + *dst->vi = *vi; + + if (IS_NEEDED(VI_BINDING_STRIDES)) { + for (uint32_t b = 0; b < MESA_VK_MAX_VERTEX_BINDINGS; b++) { + if (vi->bindings_valid & BITFIELD_BIT(b)) + dst->vi_binding_strides[b] = vi->bindings[b].stride; + else + dst->vi_binding_strides[b] = 0; + } + } +} + static void vk_input_assembly_state_init(struct vk_input_assembly_state *ia, const BITSET_WORD *dynamic, @@ -251,6 +272,14 @@ vk_input_assembly_state_init(struct vk_input_assembly_state *ia, ia->primitive_restart_enable = ia_info->primitiveRestartEnable; } +static void +vk_dynamic_graphics_state_init_ia(struct vk_dynamic_graphics_state *dst, + const BITSET_WORD *needed, + const struct vk_input_assembly_state *ia) +{ + dst->ia = *ia; +} + static void vk_tessellation_state_init(struct vk_tessellation_state *ts, const BITSET_WORD *dynamic, @@ -274,6 +303,14 @@ vk_tessellation_state_init(struct vk_tessellation_state *ts, } } +static void +vk_dynamic_graphics_state_init_ts(struct vk_dynamic_graphics_state *dst, + const BITSET_WORD *needed, + const struct vk_tessellation_state *ts) +{ + dst->ts.patch_control_points = ts->patch_control_points; +} + static void vk_viewport_state_init(struct vk_viewport_state *vp, const BITSET_WORD *dynamic, @@ -310,6 +347,20 @@ vk_viewport_state_init(struct vk_viewport_state *vp, vp->negative_one_to_one = vp_dcc_info->negativeOneToOne; } +static void +vk_dynamic_graphics_state_init_vp(struct vk_dynamic_graphics_state *dst, + const BITSET_WORD *needed, + const struct vk_viewport_state *vp) +{ + dst->vp.viewport_count = vp->viewport_count; + if (IS_NEEDED(VP_VIEWPORTS)) + typed_memcpy(dst->vp.viewports, vp->viewports, vp->viewport_count); + + dst->vp.scissor_count = vp->scissor_count; + if (IS_NEEDED(VP_SCISSORS)) + typed_memcpy(dst->vp.scissors, vp->scissors, vp->scissor_count); +} + static void vk_discard_rectangles_state_init(struct vk_discard_rectangles_state *dr, const BITSET_WORD *dynamic, @@ -330,6 +381,15 @@ vk_discard_rectangles_state_init(struct vk_discard_rectangles_state *dr, } } +static void +vk_dynamic_graphics_state_init_dr(struct vk_dynamic_graphics_state *dst, + const BITSET_WORD *needed, + const struct vk_discard_rectangles_state *dr) +{ + dst->dr.rectangle_count = dr->rectangle_count; + typed_memcpy(dst->dr.rectangles, dr->rectangles, dr->rectangle_count); +} + static void vk_rasterization_state_init(struct vk_rasterization_state *rs, const BITSET_WORD *dynamic, @@ -427,6 +487,23 @@ vk_rasterization_state_init(struct vk_rasterization_state *rs, } } +static void +vk_dynamic_graphics_state_init_rs(struct vk_dynamic_graphics_state *dst, + const BITSET_WORD *needed, + const struct vk_rasterization_state *rs) +{ + dst->rs.rasterizer_discard_enable = rs->rasterizer_discard_enable; + dst->rs.cull_mode = rs->cull_mode; + dst->rs.front_face = rs->front_face; + dst->rs.depth_bias.enable = rs->depth_bias.enable; + dst->rs.depth_bias.constant = rs->depth_bias.constant; + dst->rs.depth_bias.clamp = rs->depth_bias.clamp; + dst->rs.depth_bias.slope = rs->depth_bias.slope; + dst->rs.line.width = rs->line.width; + dst->rs.line.stipple.factor = rs->line.stipple.factor; + dst->rs.line.stipple.pattern = rs->line.stipple.pattern; +} + static void vk_fragment_shading_rate_state_init( struct vk_fragment_shading_rate_state *fsr, @@ -444,6 +521,15 @@ vk_fragment_shading_rate_state_init( } } +static void +vk_dynamic_graphics_state_init_fsr( + struct vk_dynamic_graphics_state *dst, + const BITSET_WORD *needed, + const struct vk_fragment_shading_rate_state *fsr) +{ + dst->fsr = *fsr; +} + static void vk_sample_locations_state_init(struct vk_sample_locations_state *sl, const VkSampleLocationsInfoEXT *sl_info) @@ -529,6 +615,15 @@ vk_multisample_sample_locations_state_init( } } +static void +vk_dynamic_graphics_state_init_ms(struct vk_dynamic_graphics_state *dst, + const BITSET_WORD *needed, + const struct vk_multisample_state *ms) +{ + if (IS_NEEDED(MS_SAMPLE_LOCATIONS)) + *dst->ms.sample_locations = *ms->sample_locations; +} + static void vk_stencil_test_face_state_init(struct vk_stencil_test_face_state *face, const VkStencilOpState *info) @@ -561,6 +656,14 @@ vk_depth_stencil_state_init(struct vk_depth_stencil_state *ds, vk_stencil_test_face_state_init(&ds->stencil.back, &ds_info->back); } +static void +vk_dynamic_graphics_state_init_ds(struct vk_dynamic_graphics_state *dst, + const BITSET_WORD *needed, + const struct vk_depth_stencil_state *ds) +{ + dst->ds = *ds; +} + static void vk_color_blend_state_init(struct vk_color_blend_state *cb, const BITSET_WORD *dynamic, @@ -605,6 +708,18 @@ vk_color_blend_state_init(struct vk_color_blend_state *cb, } } +static void +vk_dynamic_graphics_state_init_cb(struct vk_dynamic_graphics_state *dst, + const BITSET_WORD *needed, + const struct vk_color_blend_state *cb) +{ + dst->cb.logic_op = cb->logic_op; + dst->cb.color_write_enables = cb->color_write_enables; + + if (IS_NEEDED(CB_BLEND_CONSTANTS)) + typed_memcpy(dst->cb.blend_constants, cb->blend_constants, 4); +} + static bool vk_render_pass_state_is_complete(const struct vk_render_pass_state *rp) { @@ -696,6 +811,12 @@ vk_render_pass_state_init(struct vk_render_pass_state *rp, } } +static void +vk_dynamic_graphics_state_init_rp(struct vk_dynamic_graphics_state *dst, + const BITSET_WORD *needed, + const struct vk_render_pass_state *rp) +{ } + #define FOREACH_STATE_GROUP(f) \ f(MESA_VK_GRAPHICS_STATE_VERTEX_INPUT_BIT, \ vk_vertex_input_state, vi); \ @@ -1061,6 +1182,7 @@ vk_graphics_pipeline_state_fill(const struct vk_device *device, } #undef IS_DYNAMIC +#undef IS_NEEDED void vk_graphics_pipeline_state_merge(struct vk_graphics_pipeline_state *dst, @@ -1086,3 +1208,271 @@ vk_graphics_pipeline_state_merge(struct vk_graphics_pipeline_state *dst, #undef MERGE } + +const struct vk_dynamic_graphics_state vk_default_dynamic_graphics_state = { + .rs = { + .line = { + .width = 1.0f, + }, + }, + .fsr = { + .fragment_size = {1u, 1u}, + .combiner_ops = { + VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR, + VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR, + }, + }, + .ds = { + .depth = { + .bounds_test = { + .min = 0.0f, + .max = 1.0f, + }, + }, + .stencil = { + .front = { + .compare_mask = -1, + .write_mask = -1, + }, + .back = { + .compare_mask = -1, + .write_mask = -1, + }, + }, + }, + .cb = { + .color_write_enables = 0xffffffffu, + }, +}; + +void +vk_dynamic_graphics_state_init(struct vk_dynamic_graphics_state *dyn) +{ + *dyn = vk_default_dynamic_graphics_state; +} + +void +vk_dynamic_graphics_state_clear(struct vk_dynamic_graphics_state *dyn) +{ + struct vk_vertex_input_state *vi = dyn->vi; + struct vk_sample_locations_state *sl = dyn->ms.sample_locations; + + *dyn = vk_default_dynamic_graphics_state; + + if (vi != NULL) { + memset(vi, 0, sizeof(*vi)); + dyn->vi = vi; + } + + if (sl != NULL) { + memset(sl, 0, sizeof(*sl)); + dyn->ms.sample_locations = sl; + } +} + +void +vk_dynamic_graphics_state_fill(struct vk_dynamic_graphics_state *dyn, + const struct vk_graphics_pipeline_state *p) +{ + /* This funciton (and the individual vk_dynamic_graphics_state_init_* + * functions it calls) are a bit sloppy. Instead of checking every single + * bit, we just copy everything and set the bits the right way at the end + * based on what groups we actually had. + */ + enum mesa_vk_graphics_state_groups groups = 0; + + BITSET_DECLARE(needed, MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX); + BITSET_COPY(needed, p->dynamic); + BITSET_NOT(needed); + + /* We only want to copy these if the driver has filled out the relevant + * pointer in the dynamic state struct. If not, they don't support them + * as dynamic state and we should leave them alone. + */ + if (dyn->vi == NULL) + BITSET_CLEAR(needed, MESA_VK_DYNAMIC_VI); + if (dyn->ms.sample_locations == NULL) + BITSET_CLEAR(needed, MESA_VK_DYNAMIC_MS_SAMPLE_LOCATIONS); + +#define INIT_DYNAMIC_STATE(STATE, type, s) \ + if (p->s != NULL) { \ + vk_dynamic_graphics_state_init_##s(dyn, needed, p->s); \ + groups |= STATE; \ + } + + FOREACH_STATE_GROUP(INIT_DYNAMIC_STATE); + +#undef INIT_DYNAMIC_STATE + + /* Mask off all but the groups we actually found */ + get_dynamic_state_groups(dyn->set, groups); + BITSET_AND(dyn->set, dyn->set, needed); +} + +#define SET_DYN_VALUE(dst, STATE, state, value) do { \ + if (!BITSET_TEST((dst)->set, MESA_VK_DYNAMIC_##STATE) || \ + (dst)->state != (value)) { \ + (dst)->state = (value); \ + assert((dst)->state == (value)); \ + BITSET_SET(dst->set, MESA_VK_DYNAMIC_##STATE); \ + BITSET_SET(dst->dirty, MESA_VK_DYNAMIC_##STATE); \ + } \ +} while(0) + +#define SET_DYN_BOOL(dst, STATE, state, value) \ + SET_DYN_VALUE(dst, STATE, state, (bool)value); + +#define SET_DYN_ARRAY(dst, STATE, state, start, count, src) do { \ + assert(start + count <= ARRAY_SIZE((dst)->state)); \ + STATIC_ASSERT(sizeof(*(dst)->state) == sizeof(*(src))); \ + const size_t __state_size = sizeof(*(dst)->state) * (count); \ + if (!BITSET_TEST((dst)->set, MESA_VK_DYNAMIC_##STATE) || \ + memcmp((dst)->state + start, src, __state_size)) { \ + memcpy((dst)->state + start, src, __state_size); \ + BITSET_SET(dst->set, MESA_VK_DYNAMIC_##STATE); \ + BITSET_SET(dst->dirty, MESA_VK_DYNAMIC_##STATE); \ + } \ +} while(0) + +void +vk_dynamic_graphics_state_copy(struct vk_dynamic_graphics_state *dst, + const struct vk_dynamic_graphics_state *src) +{ +#define IS_SET_IN_SRC(STATE) \ + BITSET_TEST(src->set, MESA_VK_DYNAMIC_##STATE) + +#define COPY_MEMBER(STATE, state) \ + SET_DYN_VALUE(dst, STATE, state, src->state) + +#define COPY_ARRAY(STATE, state, count) \ + SET_DYN_ARRAY(dst, STATE, state, 0, count, src->state) + +#define COPY_IF_SET(STATE, state) \ + if (IS_SET_IN_SRC(STATE)) SET_DYN_VALUE(dst, STATE, state, src->state) + + assert((dst->vi != NULL) == (src->vi != NULL)); + if (dst->vi != NULL && IS_SET_IN_SRC(VI)) { + COPY_MEMBER(VI, vi->bindings_valid); + u_foreach_bit(b, src->vi->bindings_valid) { + COPY_MEMBER(VI, vi->bindings[b].stride); + COPY_MEMBER(VI, vi->bindings[b].input_rate); + COPY_MEMBER(VI, vi->bindings[b].divisor); + } + COPY_MEMBER(VI, vi->attributes_valid); + u_foreach_bit(a, src->vi->attributes_valid) { + COPY_MEMBER(VI, vi->attributes[a].binding); + COPY_MEMBER(VI, vi->attributes[a].format); + COPY_MEMBER(VI, vi->attributes[a].offset); + } + } + + if (IS_SET_IN_SRC(VI_BINDING_STRIDES)) { + COPY_ARRAY(VI_BINDING_STRIDES, vi_binding_strides, + MESA_VK_MAX_VERTEX_BINDINGS); + } + + COPY_IF_SET(IA_PRIMITIVE_TOPOLOGY, ia.primitive_topology); + COPY_IF_SET(IA_PRIMITIVE_RESTART_ENABLE, ia.primitive_restart_enable); + COPY_IF_SET(TS_PATCH_CONTROL_POINTS, ts.patch_control_points); + + COPY_IF_SET(VP_VIEWPORT_COUNT, vp.viewport_count); + if (IS_SET_IN_SRC(VP_VIEWPORTS)) { + assert(IS_SET_IN_SRC(VP_VIEWPORT_COUNT)); + COPY_ARRAY(VP_VIEWPORT_COUNT, vp.viewports, src->vp.viewport_count); + } + + COPY_IF_SET(VP_SCISSOR_COUNT, vp.scissor_count); + if (IS_SET_IN_SRC(VP_SCISSORS)) { + assert(IS_SET_IN_SRC(VP_SCISSOR_COUNT)); + COPY_ARRAY(VP_SCISSOR_COUNT, vp.scissors, src->vp.scissor_count); + } + + if (IS_SET_IN_SRC(DR_RECTANGLES)) { + COPY_MEMBER(DR_RECTANGLES, dr.rectangle_count); + COPY_ARRAY(DR_RECTANGLES, dr.rectangles, src->dr.rectangle_count); + } + + COPY_IF_SET(RS_RASTERIZER_DISCARD_ENABLE, rs.rasterizer_discard_enable); + COPY_IF_SET(RS_CULL_MODE, rs.cull_mode); + COPY_IF_SET(RS_FRONT_FACE, rs.front_face); + COPY_IF_SET(RS_DEPTH_BIAS_ENABLE, rs.depth_bias.enable); + COPY_IF_SET(RS_DEPTH_BIAS_FACTORS, rs.depth_bias.constant); + COPY_IF_SET(RS_DEPTH_BIAS_FACTORS, rs.depth_bias.clamp); + COPY_IF_SET(RS_DEPTH_BIAS_FACTORS, rs.depth_bias.slope); + COPY_IF_SET(RS_LINE_WIDTH, rs.line.width); + COPY_IF_SET(RS_LINE_STIPPLE, rs.line.stipple.factor); + COPY_IF_SET(RS_LINE_STIPPLE, rs.line.stipple.pattern); + + COPY_IF_SET(FSR, fsr.fragment_size.width); + COPY_IF_SET(FSR, fsr.fragment_size.height); + COPY_IF_SET(FSR, fsr.combiner_ops[0]); + COPY_IF_SET(FSR, fsr.combiner_ops[1]); + + assert((dst->ms.sample_locations == NULL) == + (src->ms.sample_locations == NULL)); + if (dst->ms.sample_locations != NULL && + IS_SET_IN_SRC(MS_SAMPLE_LOCATIONS)) { + COPY_MEMBER(MS_SAMPLE_LOCATIONS, ms.sample_locations->per_pixel); + COPY_MEMBER(MS_SAMPLE_LOCATIONS, ms.sample_locations->grid_size.width); + COPY_MEMBER(MS_SAMPLE_LOCATIONS, ms.sample_locations->grid_size.height); + const uint32_t sl_count = src->ms.sample_locations->per_pixel * + src->ms.sample_locations->grid_size.width * + src->ms.sample_locations->grid_size.height; + COPY_ARRAY(MS_SAMPLE_LOCATIONS, ms.sample_locations->locations, sl_count); + } + + COPY_IF_SET(DS_DEPTH_TEST_ENABLE, ds.depth.test_enable); + COPY_IF_SET(DS_DEPTH_WRITE_ENABLE, ds.depth.write_enable); + COPY_IF_SET(DS_DEPTH_COMPARE_OP, ds.depth.compare_op); + COPY_IF_SET(DS_DEPTH_BOUNDS_TEST_ENABLE, ds.depth.bounds_test.enable); + if (IS_SET_IN_SRC(DS_DEPTH_BOUNDS_TEST_BOUNDS)) { + COPY_MEMBER(DS_DEPTH_BOUNDS_TEST_BOUNDS, ds.depth.bounds_test.min); + COPY_MEMBER(DS_DEPTH_BOUNDS_TEST_BOUNDS, ds.depth.bounds_test.max); + } + + COPY_IF_SET(DS_STENCIL_TEST_ENABLE, ds.stencil.test_enable); + if (IS_SET_IN_SRC(DS_STENCIL_OP)) { + COPY_MEMBER(DS_STENCIL_OP, ds.stencil.front.op.fail); + COPY_MEMBER(DS_STENCIL_OP, ds.stencil.front.op.pass); + COPY_MEMBER(DS_STENCIL_OP, ds.stencil.front.op.depth_fail); + COPY_MEMBER(DS_STENCIL_OP, ds.stencil.front.op.compare); + COPY_MEMBER(DS_STENCIL_OP, ds.stencil.back.op.fail); + COPY_MEMBER(DS_STENCIL_OP, ds.stencil.back.op.pass); + COPY_MEMBER(DS_STENCIL_OP, ds.stencil.back.op.depth_fail); + COPY_MEMBER(DS_STENCIL_OP, ds.stencil.back.op.compare); + } + if (IS_SET_IN_SRC(DS_STENCIL_COMPARE_MASK)) { + COPY_MEMBER(DS_STENCIL_COMPARE_MASK, ds.stencil.front.compare_mask); + COPY_MEMBER(DS_STENCIL_COMPARE_MASK, ds.stencil.back.compare_mask); + } + if (IS_SET_IN_SRC(DS_STENCIL_WRITE_MASK)) { + COPY_MEMBER(DS_STENCIL_WRITE_MASK, ds.stencil.front.write_mask); + COPY_MEMBER(DS_STENCIL_WRITE_MASK, ds.stencil.back.write_mask); + } + if (IS_SET_IN_SRC(DS_STENCIL_REFERENCE)) { + COPY_MEMBER(DS_STENCIL_REFERENCE, ds.stencil.front.reference); + COPY_MEMBER(DS_STENCIL_REFERENCE, ds.stencil.back.reference); + } + + COPY_IF_SET(CB_LOGIC_OP, cb.logic_op); + COPY_IF_SET(CB_COLOR_WRITE_ENABLES, cb.color_write_enables); + if (IS_SET_IN_SRC(CB_BLEND_CONSTANTS)) + COPY_ARRAY(CB_BLEND_CONSTANTS, cb.blend_constants, 4); + +#undef IS_SET_IN_SRC +#undef MARK_DIRTY +#undef COPY_MEMBER +#undef COPY_ARRAY +#undef COPY_IF_SET + + for (uint32_t w = 0; w < ARRAY_SIZE(dst->dirty); w++) { + /* If it's in the source but isn't set in the destination at all, mark + * it dirty. It's possible that the default values just happen to equal + * the value from src. + */ + dst->dirty[w] |= src->set[w] & ~dst->set[w]; + + /* Everything that was in the source is now in the destination */ + dst->set[w] |= src->set[w]; + } +} diff --git a/src/vulkan/runtime/vk_graphics_state.h b/src/vulkan/runtime/vk_graphics_state.h index 0d7ef70ddba3..f593052fdaa1 100644 --- a/src/vulkan/runtime/vk_graphics_state.h +++ b/src/vulkan/runtime/vk_graphics_state.h @@ -474,6 +474,184 @@ struct vk_render_pass_state { VkFormat stencil_attachment_format; }; +/** Struct representing all dynamic graphics state + * + * Before invoking any core functions, the driver must properly populate + * initialize this struct: + * + * - Initialize using vk_default_dynamic_graphics_state, if desired + * - Set vi to a driver-allocated vk_vertex_input_state struct + * - Set ms.sample_locations to a driver-allocated + * vk_sample_locations_state struct + */ +struct vk_dynamic_graphics_state { + /** Vertex input state + * + * Must be provided by the driver if VK_EXT_vertex_input_dynamic_state is + * supported. + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_VI + */ + struct vk_vertex_input_state *vi; + + /** Vertex binding strides + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_VI_BINDING_STRIDES + */ + uint16_t vi_binding_strides[MESA_VK_MAX_VERTEX_BINDINGS]; + + struct vk_input_assembly_state ia; + + struct { + uint32_t patch_control_points; + } ts; + + /** Viewport state */ + struct { + /** Viewport count + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_VP_VIEWPORT_COUNT + */ + uint32_t viewport_count; + + /** Viewports + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_VP_VIEWPORTS + */ + VkViewport viewports[MESA_VK_MAX_VIEWPORTS]; + + /** Scissor count + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_VP_SCISSOR_COUNT + */ + uint32_t scissor_count; + + /** Scissor rects + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_VP_SCISSORS + */ + VkRect2D scissors[MESA_VK_MAX_SCISSORS]; + } vp; + + /** Discard rectangles + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_DR_RECTANGLES + */ + struct { + uint32_t rectangle_count; + VkRect2D rectangles[MESA_VK_MAX_DISCARD_RECTANGLES]; + } dr; + + /** Rasterization state */ + struct { + /** Rasterizer discard + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_RS_RASTERIZER_DISCARD_ENABLE + */ + bool rasterizer_discard_enable; + + /** Cull mode + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_RS_CULL_MODE + */ + VkCullModeFlags cull_mode; + + /** Front face + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_RS_FRONT_FACE + */ + VkFrontFace front_face; + + struct { + /** Depth bias enable + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_RS_DEPTH_BIAS_ENABLE + */ + bool enable; + + /** Depth bias constant factor + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_RS_DEPTH_BIAS_FACTORS + */ + float constant; + + /** Depth bias clamp + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_RS_DEPTH_BIAS_FACTORS + */ + float clamp; + + /** Depth bias slope + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_RS_DEPTH_BIAS_FACTORS + */ + float slope; + } depth_bias; + + struct { + /** Line width + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_RS_LINE_WIDTH + */ + float width; + + /** Line stipple + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_RS_LINE_STIPPLE + */ + struct { + uint32_t factor; + uint16_t pattern; + } stipple; + } line; + } rs; + + struct vk_fragment_shading_rate_state fsr; + + /** Multisample state */ + struct { + /** Sample locations + * + * Must be provided by the driver if VK_EXT_sample_locations is + * supported. + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_MS_SAMPLE_LOCATIONS + */ + struct vk_sample_locations_state *sample_locations; + } ms; + + struct vk_depth_stencil_state ds; + + /** Color blend state */ + struct { + /** Integer color logic op + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_CB_LOGIC_OP, + */ + VkLogicOp logic_op; + + /** Color write enables + * + * Bitmask of color write enables, indexed by color attachment index. + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_CB_COLOR_WRITE_ENABLES, + */ + uint32_t color_write_enables; + + /** Blend constants + * + * MESA_VK_DYNAMIC_GRAPHICS_STATE_CB_BLEND_CONSTANTS, + */ + float blend_constants[4]; + } cb; + + /** For pipelines, which bits of dynamic state are set */ + BITSET_DECLARE(set, MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX); + + /** For command buffers, which bits of dynamic state have changed */ + BITSET_DECLARE(dirty, MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX); +}; + struct vk_graphics_pipeline_all_state { struct vk_vertex_input_state vi; struct vk_input_assembly_state ia; @@ -606,6 +784,75 @@ void vk_graphics_pipeline_state_merge(struct vk_graphics_pipeline_state *dst, const struct vk_graphics_pipeline_state *src); +extern const struct vk_dynamic_graphics_state vk_default_dynamic_graphics_state; + +/** Initialize a vk_dynamic_graphics_state with defaults + * + * @param[out] dyn Dynamic graphics state to initizlie + */ +void +vk_dynamic_graphics_state_init(struct vk_dynamic_graphics_state *dyn); + +/** Clear a vk_dynamic_graphics_state to defaults + * + * @param[out] dyn Dynamic graphics state to initizlie + */ +void +vk_dynamic_graphics_state_clear(struct vk_dynamic_graphics_state *dyn); + +/** Initialize a vk_dynamic_graphics_state for a pipeline + * + * @param[out] dyn Dynamic graphics state to initizlie + * @param[in] supported Bitset of all dynamic state supported by the driver. + * @param[in] p The pipeline state from which to initialize the + * dynamic state. + */ +void +vk_dynamic_graphics_state_fill(struct vk_dynamic_graphics_state *dyn, + const struct vk_graphics_pipeline_state *p); + +/** Mark all states in the given vk_dynamic_graphics_state dirty + * + * @param[out] d Dynamic graphics state struct + */ +static inline void +vk_dynamic_graphics_state_dirty_all(struct vk_dynamic_graphics_state *d) +{ + BITSET_SET_RANGE(d->dirty, 0, MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX - 1); +} + +/** Mark all states in the given vk_dynamic_graphics_state not dirty + * + * @param[out] d Dynamic graphics state struct + */ +static inline void +vk_dynamic_graphics_state_clear_dirty(struct vk_dynamic_graphics_state *d) +{ + memset(d->dirty, 0, sizeof(d->dirty)); +} + +/** Test if any states in the given vk_dynamic_graphics_state are dirty + * + * @param[in] d Dynamic graphics state struct to test + * @returns true if any state is dirty + */ +static inline bool +vk_dynamic_graphics_state_any_dirty(const struct vk_dynamic_graphics_state *d) +{ + return BITSET_TEST_RANGE(d->dirty, + 0, MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX - 1); +} + +/** Copies all set state from src to dst + * + * Both src and dst are assumed to be properly initialized dynamic state + * structs. Anything not set in src, as indicated by src->set, is ignored and + * those bits of dst are left untouched. + */ +void +vk_dynamic_graphics_state_copy(struct vk_dynamic_graphics_state *dst, + const struct vk_dynamic_graphics_state *src); + #ifdef __cplusplus } #endif -- GitLab From 4ad149a8fc7974d9a6633557337ec2a866303072 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Thu, 30 Jun 2022 22:35:44 -0500 Subject: [PATCH 7/8] vulkan: Add a common implementation of vkCmdSet* Reviewed-by: Lionel Landwerlin Part-of: --- docs/vulkan/graphics-state.rst | 74 +++- src/vulkan/runtime/vk_command_buffer.c | 2 + src/vulkan/runtime/vk_command_buffer.h | 4 +- src/vulkan/runtime/vk_graphics_state.c | 496 +++++++++++++++++++++++++ src/vulkan/runtime/vk_graphics_state.h | 33 +- 5 files changed, 606 insertions(+), 3 deletions(-) diff --git a/docs/vulkan/graphics-state.rst b/docs/vulkan/graphics-state.rst index 278a59850314..5f88fa24790c 100644 --- a/docs/vulkan/graphics-state.rst +++ b/docs/vulkan/graphics-state.rst @@ -104,9 +104,81 @@ or extension introduced them. This structure can be populated from a :cpp:func:`vk_dynamic_graphics_state_init`. .. doxygenfunction:: vk_dynamic_graphics_state_init - .. doxygenfunction:: vk_dynamic_graphics_state_copy +There is also a :cpp:struct:`vk_dynamic_graphics_state` embedded in +:cpp:struct:`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()``: + +.. doxygenfunction:: vk_cmd_set_dynamic_graphics_state +.. doxygenfunction:: vk_cmd_set_vertex_binding_strides + +To use the dynamic state framework, you will need the following in your +pipeline structure: + +.. code-block:: c + + 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, + +.. code-block:: c + + 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()``, + +.. code-block:: c + + 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: + +.. code-block:: c + + 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. + +.. doxygenfunction:: vk_dynamic_graphics_state_dirty_all +.. doxygenfunction:: vk_dynamic_graphics_state_clear_dirty +.. doxygenfunction:: vk_dynamic_graphics_state_any_dirty + Reference --------- diff --git a/src/vulkan/runtime/vk_command_buffer.c b/src/vulkan/runtime/vk_command_buffer.c index 477562ee8fb8..d50a74808066 100644 --- a/src/vulkan/runtime/vk_command_buffer.c +++ b/src/vulkan/runtime/vk_command_buffer.c @@ -38,6 +38,7 @@ vk_command_buffer_init(struct vk_command_buffer *command_buffer, command_buffer->pool = pool; command_buffer->level = level; + vk_dynamic_graphics_state_init(&command_buffer->dynamic_graphics_state); vk_cmd_queue_init(&command_buffer->cmd_queue, &pool->alloc); util_dynarray_init(&command_buffer->labels, NULL); command_buffer->region_begin = true; @@ -50,6 +51,7 @@ vk_command_buffer_init(struct vk_command_buffer *command_buffer, void vk_command_buffer_reset(struct vk_command_buffer *command_buffer) { + vk_dynamic_graphics_state_clear(&command_buffer->dynamic_graphics_state); vk_command_buffer_reset_render_pass(command_buffer); vk_cmd_queue_reset(&command_buffer->cmd_queue); util_dynarray_clear(&command_buffer->labels); diff --git a/src/vulkan/runtime/vk_command_buffer.h b/src/vulkan/runtime/vk_command_buffer.h index e910bef67f25..2d1184a8c04d 100644 --- a/src/vulkan/runtime/vk_command_buffer.h +++ b/src/vulkan/runtime/vk_command_buffer.h @@ -25,7 +25,7 @@ #define VK_COMMAND_BUFFER_H #include "vk_cmd_queue.h" -#include "vk_limits.h" +#include "vk_graphics_state.h" #include "vk_object.h" #include "util/list.h" #include "util/u_dynarray.h" @@ -66,6 +66,8 @@ struct vk_command_buffer { /** VkCommandBufferAllocateInfo::level */ VkCommandBufferLevel level; + struct vk_dynamic_graphics_state dynamic_graphics_state; + /** Link in vk_command_pool::command_buffers if pool != NULL */ struct list_head pool_link; diff --git a/src/vulkan/runtime/vk_graphics_state.c b/src/vulkan/runtime/vk_graphics_state.c index 2a6344b4d1f5..a9587568cdd5 100644 --- a/src/vulkan/runtime/vk_graphics_state.c +++ b/src/vulkan/runtime/vk_graphics_state.c @@ -1,6 +1,8 @@ #include "vk_graphics_state.h" #include "vk_alloc.h" +#include "vk_command_buffer.h" +#include "vk_common_entrypoints.h" #include "vk_device.h" #include "vk_log.h" #include "vk_render_pass.h" @@ -1476,3 +1478,497 @@ vk_dynamic_graphics_state_copy(struct vk_dynamic_graphics_state *dst, dst->set[w] |= src->set[w]; } } + +void +vk_cmd_set_dynamic_graphics_state(struct vk_command_buffer *cmd, + const struct vk_dynamic_graphics_state *state) +{ + vk_dynamic_graphics_state_copy(&cmd->dynamic_graphics_state, state); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetVertexInputEXT(VkCommandBuffer commandBuffer, + uint32_t vertexBindingDescriptionCount, + const VkVertexInputBindingDescription2EXT* pVertexBindingDescriptions, + uint32_t vertexAttributeDescriptionCount, + const VkVertexInputAttributeDescription2EXT* pVertexAttributeDescriptions) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + uint32_t bindings_valid = 0; + for (uint32_t i = 0; i < vertexBindingDescriptionCount; i++) { + const VkVertexInputBindingDescription2EXT *desc = + &pVertexBindingDescriptions[i]; + + assert(desc->binding < MESA_VK_MAX_VERTEX_BINDINGS); + assert(desc->stride <= MESA_VK_MAX_VERTEX_BINDING_STRIDE); + assert(desc->inputRate <= UINT8_MAX); + + const uint32_t b = desc->binding; + bindings_valid |= BITFIELD_BIT(b); + SET_DYN_VALUE(dyn, VI, vi->bindings[b].stride, desc->stride); + SET_DYN_VALUE(dyn, VI, vi->bindings[b].input_rate, desc->inputRate); + SET_DYN_VALUE(dyn, VI, vi->bindings[b].divisor, desc->divisor); + + /* Also set bindings_strides in case a driver is keying off that */ + SET_DYN_VALUE(dyn, VI_BINDING_STRIDES, + vi_binding_strides[b], desc->stride); + } + SET_DYN_VALUE(dyn, VI, vi->bindings_valid, bindings_valid); + + uint32_t attributes_valid = 0; + for (uint32_t i = 0; i < vertexAttributeDescriptionCount; i++) { + const VkVertexInputAttributeDescription2EXT *desc = + &pVertexAttributeDescriptions[i]; + + assert(desc->location < MESA_VK_MAX_VERTEX_ATTRIBUTES); + assert(desc->binding < MESA_VK_MAX_VERTEX_BINDINGS); + assert(bindings_valid & BITFIELD_BIT(desc->binding)); + + const uint32_t a = desc->location; + attributes_valid |= BITFIELD_BIT(a); + SET_DYN_VALUE(dyn, VI, vi->attributes[a].binding, desc->binding); + SET_DYN_VALUE(dyn, VI, vi->attributes[a].format, desc->format); + SET_DYN_VALUE(dyn, VI, vi->attributes[a].offset, desc->offset); + } + SET_DYN_VALUE(dyn, VI, vi->attributes_valid, attributes_valid); +} + +void +vk_cmd_set_vertex_binding_strides(struct vk_command_buffer *cmd, + uint32_t first_binding, + uint32_t binding_count, + const VkDeviceSize *strides) +{ + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + for (uint32_t i = 0; i < binding_count; i++) { + SET_DYN_VALUE(dyn, VI_BINDING_STRIDES, + vi_binding_strides[first_binding + i], strides[i]); + } +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetPrimitiveTopology(VkCommandBuffer commandBuffer, + VkPrimitiveTopology primitiveTopology) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, IA_PRIMITIVE_TOPOLOGY, + ia.primitive_topology, primitiveTopology); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetPrimitiveRestartEnable(VkCommandBuffer commandBuffer, + VkBool32 primitiveRestartEnable) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_BOOL(dyn, IA_PRIMITIVE_RESTART_ENABLE, + ia.primitive_restart_enable, primitiveRestartEnable); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetPatchControlPointsEXT(VkCommandBuffer commandBuffer, + uint32_t patchControlPoints) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, TS_PATCH_CONTROL_POINTS, + ts.patch_control_points, patchControlPoints); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetViewport(VkCommandBuffer commandBuffer, + uint32_t firstViewport, + uint32_t viewportCount, + const VkViewport *pViewports) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_ARRAY(dyn, VP_VIEWPORTS, vp.viewports, + firstViewport, viewportCount, pViewports); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetViewportWithCount(VkCommandBuffer commandBuffer, + uint32_t viewportCount, + const VkViewport *pViewports) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, VP_VIEWPORT_COUNT, vp.viewport_count, viewportCount); + SET_DYN_ARRAY(dyn, VP_VIEWPORTS, vp.viewports, 0, viewportCount, pViewports); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetScissor(VkCommandBuffer commandBuffer, + uint32_t firstScissor, + uint32_t scissorCount, + const VkRect2D *pScissors) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_ARRAY(dyn, VP_SCISSORS, vp.scissors, + firstScissor, scissorCount, pScissors); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetScissorWithCount(VkCommandBuffer commandBuffer, + uint32_t scissorCount, + const VkRect2D *pScissors) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, VP_SCISSOR_COUNT, vp.scissor_count, scissorCount); + SET_DYN_ARRAY(dyn, VP_SCISSORS, vp.scissors, 0, scissorCount, pScissors); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetDiscardRectangleEXT(VkCommandBuffer commandBuffer, + uint32_t firstDiscardRectangle, + uint32_t discardRectangleCount, + const VkRect2D *pDiscardRectangles) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, DR_RECTANGLES, dr.rectangle_count, discardRectangleCount); + SET_DYN_ARRAY(dyn, DR_RECTANGLES, dr.rectangles, firstDiscardRectangle, + discardRectangleCount, pDiscardRectangles); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetRasterizerDiscardEnableEXT(VkCommandBuffer commandBuffer, + VkBool32 rasterizerDiscardEnable) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_BOOL(dyn, RS_RASTERIZER_DISCARD_ENABLE, + rs.rasterizer_discard_enable, rasterizerDiscardEnable); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetCullMode(VkCommandBuffer commandBuffer, + VkCullModeFlags cullMode) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, RS_CULL_MODE, rs.cull_mode, cullMode); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetFrontFace(VkCommandBuffer commandBuffer, + VkFrontFace frontFace) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, RS_FRONT_FACE, rs.front_face, frontFace); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetDepthBiasEnable(VkCommandBuffer commandBuffer, + VkBool32 depthBiasEnable) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_BOOL(dyn, RS_DEPTH_BIAS_ENABLE, + rs.depth_bias.enable, depthBiasEnable); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetDepthBias(VkCommandBuffer commandBuffer, + float depthBiasConstantFactor, + float depthBiasClamp, + float depthBiasSlopeFactor) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, RS_DEPTH_BIAS_FACTORS, + rs.depth_bias.constant, depthBiasConstantFactor); + SET_DYN_VALUE(dyn, RS_DEPTH_BIAS_FACTORS, + rs.depth_bias.clamp, depthBiasClamp); + SET_DYN_VALUE(dyn, RS_DEPTH_BIAS_FACTORS, + rs.depth_bias.slope, depthBiasSlopeFactor); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetLineWidth(VkCommandBuffer commandBuffer, + float lineWidth) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, RS_LINE_WIDTH, rs.line.width, lineWidth); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetLineStippleEXT(VkCommandBuffer commandBuffer, + uint32_t lineStippleFactor, + uint16_t lineStipplePattern) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, RS_LINE_STIPPLE, + rs.line.stipple.factor, lineStippleFactor); + SET_DYN_VALUE(dyn, RS_LINE_STIPPLE, + rs.line.stipple.pattern, lineStipplePattern); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetFragmentShadingRateKHR(VkCommandBuffer commandBuffer, + const VkExtent2D *pFragmentSize, + const VkFragmentShadingRateCombinerOpKHR combinerOps[2]) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, FSR, fsr.fragment_size.width, pFragmentSize->width); + SET_DYN_VALUE(dyn, FSR, fsr.fragment_size.height, pFragmentSize->height); + SET_DYN_VALUE(dyn, FSR, fsr.combiner_ops[0], combinerOps[0]); + SET_DYN_VALUE(dyn, FSR, fsr.combiner_ops[1], combinerOps[1]); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetSampleLocationsEXT(VkCommandBuffer commandBuffer, + const VkSampleLocationsInfoEXT *pSampleLocationsInfo) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, MS_SAMPLE_LOCATIONS, + ms.sample_locations->per_pixel, + pSampleLocationsInfo->sampleLocationsPerPixel); + SET_DYN_VALUE(dyn, MS_SAMPLE_LOCATIONS, + ms.sample_locations->grid_size.width, + pSampleLocationsInfo->sampleLocationGridSize.width); + SET_DYN_VALUE(dyn, MS_SAMPLE_LOCATIONS, + ms.sample_locations->grid_size.height, + pSampleLocationsInfo->sampleLocationGridSize.height); + + assert(pSampleLocationsInfo->sampleLocationsCount == + pSampleLocationsInfo->sampleLocationsPerPixel * + pSampleLocationsInfo->sampleLocationGridSize.width * + pSampleLocationsInfo->sampleLocationGridSize.height); + + assert(pSampleLocationsInfo->sampleLocationsCount <= + MESA_VK_MAX_SAMPLE_LOCATIONS); + + SET_DYN_ARRAY(dyn, MS_SAMPLE_LOCATIONS, + ms.sample_locations->locations, + 0, pSampleLocationsInfo->sampleLocationsCount, + pSampleLocationsInfo->pSampleLocations); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetDepthTestEnable(VkCommandBuffer commandBuffer, + VkBool32 depthTestEnable) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_BOOL(dyn, DS_DEPTH_TEST_ENABLE, + ds.depth.test_enable, depthTestEnable); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetDepthWriteEnable(VkCommandBuffer commandBuffer, + VkBool32 depthWriteEnable) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_BOOL(dyn, DS_DEPTH_WRITE_ENABLE, + ds.depth.write_enable, depthWriteEnable); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetDepthCompareOp(VkCommandBuffer commandBuffer, + VkCompareOp depthCompareOp) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, DS_DEPTH_COMPARE_OP, ds.depth.compare_op, + depthCompareOp); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetDepthBoundsTestEnable(VkCommandBuffer commandBuffer, + VkBool32 depthBoundsTestEnable) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_BOOL(dyn, DS_DEPTH_BOUNDS_TEST_ENABLE, + ds.depth.bounds_test.enable, depthBoundsTestEnable); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetDepthBounds(VkCommandBuffer commandBuffer, + float minDepthBounds, + float maxDepthBounds) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, DS_DEPTH_BOUNDS_TEST_BOUNDS, + ds.depth.bounds_test.min, minDepthBounds); + SET_DYN_VALUE(dyn, DS_DEPTH_BOUNDS_TEST_BOUNDS, + ds.depth.bounds_test.max, maxDepthBounds); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetStencilTestEnable(VkCommandBuffer commandBuffer, + VkBool32 stencilTestEnable) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_BOOL(dyn, DS_STENCIL_TEST_ENABLE, + ds.stencil.test_enable, stencilTestEnable); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetStencilOp(VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + VkStencilOp failOp, + VkStencilOp passOp, + VkStencilOp depthFailOp, + VkCompareOp compareOp) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + if (faceMask & VK_STENCIL_FACE_FRONT_BIT) { + SET_DYN_VALUE(dyn, DS_STENCIL_OP, ds.stencil.front.op.fail, failOp); + SET_DYN_VALUE(dyn, DS_STENCIL_OP, ds.stencil.front.op.pass, passOp); + SET_DYN_VALUE(dyn, DS_STENCIL_OP, ds.stencil.front.op.depth_fail, depthFailOp); + SET_DYN_VALUE(dyn, DS_STENCIL_OP, ds.stencil.front.op.compare, compareOp); + } + + if (faceMask & VK_STENCIL_FACE_BACK_BIT) { + SET_DYN_VALUE(dyn, DS_STENCIL_OP, ds.stencil.back.op.fail, failOp); + SET_DYN_VALUE(dyn, DS_STENCIL_OP, ds.stencil.back.op.pass, passOp); + SET_DYN_VALUE(dyn, DS_STENCIL_OP, ds.stencil.back.op.depth_fail, depthFailOp); + SET_DYN_VALUE(dyn, DS_STENCIL_OP, ds.stencil.back.op.compare, compareOp); + } +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetStencilCompareMask(VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t compareMask) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + /* We assume 8-bit stencil always */ + STATIC_ASSERT(sizeof(dyn->ds.stencil.front.write_mask) == 1); + + if (faceMask & VK_STENCIL_FACE_FRONT_BIT) { + SET_DYN_VALUE(dyn, DS_STENCIL_COMPARE_MASK, + ds.stencil.front.compare_mask, (uint8_t)compareMask); + } + if (faceMask & VK_STENCIL_FACE_BACK_BIT) { + SET_DYN_VALUE(dyn, DS_STENCIL_COMPARE_MASK, + ds.stencil.back.compare_mask, (uint8_t)compareMask); + } +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetStencilWriteMask(VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t writeMask) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + /* We assume 8-bit stencil always */ + STATIC_ASSERT(sizeof(dyn->ds.stencil.front.write_mask) == 1); + + if (faceMask & VK_STENCIL_FACE_FRONT_BIT) { + SET_DYN_VALUE(dyn, DS_STENCIL_WRITE_MASK, + ds.stencil.front.write_mask, (uint8_t)writeMask); + } + if (faceMask & VK_STENCIL_FACE_BACK_BIT) { + SET_DYN_VALUE(dyn, DS_STENCIL_WRITE_MASK, + ds.stencil.back.write_mask, (uint8_t)writeMask); + } +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetStencilReference(VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t reference) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + /* We assume 8-bit stencil always */ + STATIC_ASSERT(sizeof(dyn->ds.stencil.front.write_mask) == 1); + + if (faceMask & VK_STENCIL_FACE_FRONT_BIT) { + SET_DYN_VALUE(dyn, DS_STENCIL_REFERENCE, + ds.stencil.front.reference, (uint8_t)reference); + } + if (faceMask & VK_STENCIL_FACE_BACK_BIT) { + SET_DYN_VALUE(dyn, DS_STENCIL_REFERENCE, + ds.stencil.back.reference, (uint8_t)reference); + } +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetLogicOpEXT(VkCommandBuffer commandBuffer, + VkLogicOp logicOp) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_VALUE(dyn, CB_LOGIC_OP, cb.logic_op, logicOp); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetColorWriteEnableEXT(VkCommandBuffer commandBuffer, + uint32_t attachmentCount, + const VkBool32 *pColorWriteEnables) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + assert(attachmentCount <= MESA_VK_MAX_COLOR_ATTACHMENTS); + + uint8_t color_write_enables = 0; + for (uint32_t a = 0; a < attachmentCount; a++) { + if (pColorWriteEnables[a]) + color_write_enables |= BITFIELD_BIT(a); + } + + SET_DYN_VALUE(dyn, CB_COLOR_WRITE_ENABLES, + cb.color_write_enables, color_write_enables); +} + +VKAPI_ATTR void VKAPI_CALL +vk_common_CmdSetBlendConstants(VkCommandBuffer commandBuffer, + const float blendConstants[4]) +{ + VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); + struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; + + SET_DYN_ARRAY(dyn, CB_BLEND_CONSTANTS, cb.blend_constants, + 0, 4, blendConstants); +} diff --git a/src/vulkan/runtime/vk_graphics_state.h b/src/vulkan/runtime/vk_graphics_state.h index f593052fdaa1..9a56c4f22821 100644 --- a/src/vulkan/runtime/vk_graphics_state.h +++ b/src/vulkan/runtime/vk_graphics_state.h @@ -34,6 +34,7 @@ extern "C" { #endif +struct vk_command_buffer; struct vk_device; /** Enumeration of all Vulkan dynamic graphics states @@ -828,7 +829,7 @@ vk_dynamic_graphics_state_dirty_all(struct vk_dynamic_graphics_state *d) static inline void vk_dynamic_graphics_state_clear_dirty(struct vk_dynamic_graphics_state *d) { - memset(d->dirty, 0, sizeof(d->dirty)); + BITSET_ZERO(d->dirty); } /** Test if any states in the given vk_dynamic_graphics_state are dirty @@ -848,11 +849,41 @@ vk_dynamic_graphics_state_any_dirty(const struct vk_dynamic_graphics_state *d) * Both src and dst are assumed to be properly initialized dynamic state * structs. Anything not set in src, as indicated by src->set, is ignored and * those bits of dst are left untouched. + * + * @param[out] dst Copy destination + * @param[in] src Copy source */ void vk_dynamic_graphics_state_copy(struct vk_dynamic_graphics_state *dst, const struct vk_dynamic_graphics_state *src); +/** Set all of the state in src on a command buffer + * + * Anything not set, as indicated by src->set, is ignored and those states in + * the command buffer are left untouched. + * + * @param[inout] cmd Command buffer to update + * @param[in] src State to set + */ +void +vk_cmd_set_dynamic_graphics_state(struct vk_command_buffer *cmd, + const struct vk_dynamic_graphics_state *src); + +/** Set vertex binding strides on a command buffer + * + * This is the dynamic state part of vkCmdBindVertexBuffers2(). + * + * @param[inout] cmd Command buffer to update + * @param[in] first_binding First binding to update + * @param[in] binding_count Number of bindings to update + * @param[in] strides binding_count many stride values to set + */ +void +vk_cmd_set_vertex_binding_strides(struct vk_command_buffer *cmd, + uint32_t first_binding, + uint32_t binding_count, + const VkDeviceSize *strides); + #ifdef __cplusplus } #endif -- GitLab From 9066cc86bb1ed5dd187f5be169f3135e9056ab5e Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Thu, 7 Jul 2022 12:09:16 -0500 Subject: [PATCH 8/8] vulkan: Copy the depth/stencil state optimization code from ANV Instead of having stencil writes as an out parameter of the optimization function, we add a new write_enable field for stencil that's equivalent to the similarly named field for depth. This doesn't mean drivers must actually support disabling stencil writes independently but the information may be helpful on some hardware. Reviewed-by: Lionel Landwerlin Part-of: --- docs/vulkan/graphics-state.rst | 6 ++ src/vulkan/runtime/vk_graphics_state.c | 96 ++++++++++++++++++++++++++ src/vulkan/runtime/vk_graphics_state.h | 27 ++++++++ 3 files changed, 129 insertions(+) diff --git a/docs/vulkan/graphics-state.rst b/docs/vulkan/graphics-state.rst index 5f88fa24790c..a188f56a2d72 100644 --- a/docs/vulkan/graphics-state.rst +++ b/docs/vulkan/graphics-state.rst @@ -180,6 +180,12 @@ use a state even if it isn't dirty on this particular draw. .. doxygenfunction:: vk_dynamic_graphics_state_any_dirty +Depth stencil state optimization +-------------------------------- + +.. doxygenfunction:: vk_optimize_depth_stencil_state + + Reference --------- diff --git a/src/vulkan/runtime/vk_graphics_state.c b/src/vulkan/runtime/vk_graphics_state.c index a9587568cdd5..19a158306fa6 100644 --- a/src/vulkan/runtime/vk_graphics_state.c +++ b/src/vulkan/runtime/vk_graphics_state.c @@ -654,6 +654,7 @@ vk_depth_stencil_state_init(struct vk_depth_stencil_state *ds, ds->depth.bounds_test.max = ds_info->maxDepthBounds; ds->stencil.test_enable = ds_info->stencilTestEnable; + ds->stencil.write_enable = true; vk_stencil_test_face_state_init(&ds->stencil.front, &ds_info->front); vk_stencil_test_face_state_init(&ds->stencil.back, &ds_info->back); } @@ -666,6 +667,100 @@ vk_dynamic_graphics_state_init_ds(struct vk_dynamic_graphics_state *dst, dst->ds = *ds; } +static bool +optimize_stencil_face(struct vk_stencil_test_face_state *face, + VkCompareOp depthCompareOp) +{ + /* If compareOp is ALWAYS then the stencil test will never fail and failOp + * will never happen. Set failOp to KEEP in this case. + */ + if (face->op.compare == VK_COMPARE_OP_ALWAYS) + face->op.fail = VK_STENCIL_OP_KEEP; + + /* If compareOp is NEVER or depthCompareOp is NEVER then one of the depth + * or stencil tests will fail and passOp will never happen. + */ + if (face->op.compare == VK_COMPARE_OP_NEVER || + depthCompareOp == VK_COMPARE_OP_NEVER) + face->op.pass = VK_STENCIL_OP_KEEP; + + /* If compareOp is NEVER or depthCompareOp is ALWAYS then either the + * stencil test will fail or the depth test will pass. In either case, + * depthFailOp will never happen. + */ + if (face->op.compare == VK_COMPARE_OP_NEVER || + depthCompareOp == VK_COMPARE_OP_ALWAYS) + face->op.depth_fail = VK_STENCIL_OP_KEEP; + + return face->op.fail != VK_STENCIL_OP_KEEP || + face->op.depth_fail != VK_STENCIL_OP_KEEP || + face->op.pass != VK_STENCIL_OP_KEEP; +} + +void +vk_optimize_depth_stencil_state(struct vk_depth_stencil_state *ds, + VkImageAspectFlags ds_aspects) +{ + /* stencil.write_enable is a dummy right now that should always be true */ + assert(ds->stencil.write_enable); + + /* If the depth test is disabled, we won't be writing anything. Make sure we + * treat the test as always passing later on as well. + * + * Also, the Vulkan spec requires that if either depth or stencil is not + * present, the pipeline is to act as if the test silently passes. In that + * case we won't write either. + */ + if (!ds->depth.test_enable || !(ds_aspects & VK_IMAGE_ASPECT_DEPTH_BIT)) { + ds->depth.write_enable = false; + ds->depth.compare_op = VK_COMPARE_OP_ALWAYS; + } + + if (!(ds_aspects & VK_IMAGE_ASPECT_STENCIL_BIT)) { + ds->stencil.write_enable = false; + ds->stencil.front.op.compare = VK_COMPARE_OP_ALWAYS; + ds->stencil.back.op.compare = VK_COMPARE_OP_ALWAYS; + } + + /* If the stencil test is enabled and always fails, then we will never get + * to the depth test so we can just disable the depth test entirely. + */ + if (ds->stencil.test_enable && + ds->stencil.front.op.compare == VK_COMPARE_OP_NEVER && + ds->stencil.back.op.compare == VK_COMPARE_OP_NEVER) { + ds->depth.test_enable = false; + ds->depth.write_enable = false; + } + + /* If depthCompareOp is EQUAL then the value we would be writing to the + * depth buffer is the same as the value that's already there so there's no + * point in writing it. + */ + if (ds->depth.compare_op == VK_COMPARE_OP_EQUAL) + ds->depth.write_enable = false; + + /* If the stencil ops are such that we don't actually ever modify the + * stencil buffer, we should disable writes. + */ + if (!optimize_stencil_face(&ds->stencil.front, ds->depth.compare_op) && + !optimize_stencil_face(&ds->stencil.back, ds->depth.compare_op)) + ds->stencil.write_enable = false; + + /* If the depth test always passes and we never write out depth, that's the + * same as if the depth test is disabled entirely. + */ + if (ds->depth.compare_op == VK_COMPARE_OP_ALWAYS && !ds->depth.write_enable) + ds->depth.test_enable = false; + + /* If the stencil test always passes and we never write out stencil, that's + * the same as if the stencil test is disabled entirely. + */ + if (ds->stencil.front.op.compare == VK_COMPARE_OP_ALWAYS && + ds->stencil.back.op.compare == VK_COMPARE_OP_ALWAYS && + !ds->stencil.write_enable) + ds->stencil.test_enable = false; +} + static void vk_color_blend_state_init(struct vk_color_blend_state *cb, const BITSET_WORD *dynamic, @@ -1232,6 +1327,7 @@ const struct vk_dynamic_graphics_state vk_default_dynamic_graphics_state = { }, }, .stencil = { + .write_enable = true, .front = { .compare_mask = -1, .write_mask = -1, diff --git a/src/vulkan/runtime/vk_graphics_state.h b/src/vulkan/runtime/vk_graphics_state.h index 9a56c4f22821..3f204ae63329 100644 --- a/src/vulkan/runtime/vk_graphics_state.h +++ b/src/vulkan/runtime/vk_graphics_state.h @@ -382,6 +382,16 @@ struct vk_depth_stencil_state { */ bool test_enable; + /** Whether or not stencil is should be written + * + * This does not map directly to any particular Vulkan API state and is + * initialized to true. If independent stencil disable ever becomes a + * thing, it will use this state. vk_optimize_depth_stencil_state() may + * set this to false if it can prove that the stencil test will never + * alter the stencil value. + */ + bool write_enable; + /** VkPipelineDepthStencilStateCreateInfo::front */ struct vk_stencil_test_face_state front; @@ -390,6 +400,23 @@ struct vk_depth_stencil_state { } stencil; }; +/** Optimize a depth/stencil state + * + * The way depth and stencil testing is specified, there are many case where, + * regardless of depth/stencil writes being enabled, nothing actually gets + * written due to some other bit of state being set. In the presence of + * discards, it's fairly easy to get into cases where early depth/stencil + * testing is disabled on some hardware, leading to a fairly big performance + * hit. This function attempts to optimize the depth stencil state and + * disable writes and sometimes even testing whenever possible. + * + * @param[inout] ds The depth stencil state to optimize + * @param[in] ds_aspects Which image aspects are present in the render + * pass. + */ +void vk_optimize_depth_stencil_state(struct vk_depth_stencil_state *ds, + VkImageAspectFlags ds_aspects); + struct vk_color_blend_attachment_state { /** VkPipelineColorBlendAttachmentState::blendEnable */ bool blend_enable; -- GitLab