#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" #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_BINDINGS_VALID); 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); BITSET_SET(dynamic, MESA_VK_DYNAMIC_TS_DOMAIN_ORIGIN); } 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); BITSET_SET(dynamic, MESA_VK_DYNAMIC_VP_DEPTH_CLIP_NEGATIVE_ONE_TO_ONE); } if (groups & MESA_VK_GRAPHICS_STATE_DISCARD_RECTANGLES_BIT) { BITSET_SET(dynamic, MESA_VK_DYNAMIC_DR_RECTANGLES); BITSET_SET(dynamic, MESA_VK_DYNAMIC_DR_ENABLE); BITSET_SET(dynamic, MESA_VK_DYNAMIC_DR_MODE); } 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_DEPTH_CLAMP_ENABLE); BITSET_SET(dynamic, MESA_VK_DYNAMIC_RS_DEPTH_CLIP_ENABLE); BITSET_SET(dynamic, MESA_VK_DYNAMIC_RS_POLYGON_MODE); 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_CONSERVATIVE_MODE); BITSET_SET(dynamic, MESA_VK_DYNAMIC_RS_RASTERIZATION_ORDER_AMD); BITSET_SET(dynamic, MESA_VK_DYNAMIC_RS_PROVOKING_VERTEX); BITSET_SET(dynamic, MESA_VK_DYNAMIC_RS_RASTERIZATION_STREAM); 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_MODE); BITSET_SET(dynamic, MESA_VK_DYNAMIC_RS_LINE_STIPPLE_ENABLE); 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_RASTERIZATION_SAMPLES); BITSET_SET(dynamic, MESA_VK_DYNAMIC_MS_SAMPLE_MASK); BITSET_SET(dynamic, MESA_VK_DYNAMIC_MS_ALPHA_TO_COVERAGE_ENABLE); BITSET_SET(dynamic, MESA_VK_DYNAMIC_MS_ALPHA_TO_ONE_ENABLE); BITSET_SET(dynamic, MESA_VK_DYNAMIC_MS_SAMPLE_LOCATIONS_ENABLE); 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_ENABLE); BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_LOGIC_OP); BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_ATTACHMENT_COUNT); BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_COLOR_WRITE_ENABLES); BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_BLEND_ENABLES); BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_BLEND_EQUATIONS); BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_WRITE_MASKS); BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_BLEND_CONSTANTS); } } static enum mesa_vk_graphics_state_groups fully_dynamic_state_groups(const BITSET_WORD *dynamic) { enum mesa_vk_graphics_state_groups groups = 0; if (BITSET_TEST(dynamic, MESA_VK_DYNAMIC_VI) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_VI_BINDING_STRIDES) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_VI_BINDINGS_VALID)) groups |= MESA_VK_GRAPHICS_STATE_VERTEX_INPUT_BIT; if (BITSET_TEST(dynamic, MESA_VK_DYNAMIC_TS_PATCH_CONTROL_POINTS) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_TS_DOMAIN_ORIGIN)) groups |= MESA_VK_GRAPHICS_STATE_TESSELLATION_BIT; if (BITSET_TEST(dynamic, MESA_VK_DYNAMIC_FSR)) groups |= MESA_VK_GRAPHICS_STATE_FRAGMENT_SHADING_RATE_BIT; if (BITSET_TEST(dynamic, MESA_VK_DYNAMIC_DS_DEPTH_TEST_ENABLE) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_DS_DEPTH_WRITE_ENABLE) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_DS_DEPTH_COMPARE_OP) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_DS_DEPTH_BOUNDS_TEST_ENABLE) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_DS_DEPTH_BOUNDS_TEST_BOUNDS) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_DS_STENCIL_TEST_ENABLE) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_DS_STENCIL_OP) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_DS_STENCIL_COMPARE_MASK) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_DS_STENCIL_WRITE_MASK) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_DS_STENCIL_REFERENCE)) groups |= MESA_VK_GRAPHICS_STATE_DEPTH_STENCIL_BIT; if (BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_LOGIC_OP_ENABLE) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_LOGIC_OP) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_ATTACHMENT_COUNT) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_COLOR_WRITE_ENABLES) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_BLEND_ENABLES) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_BLEND_EQUATIONS) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_WRITE_MASKS) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_BLEND_CONSTANTS)) groups |= MESA_VK_GRAPHICS_STATE_COLOR_BLEND_BIT; return groups; } 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 * 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; #define CASE3(VK, MESA1, MESA2, MESA3) \ case VK_DYNAMIC_STATE_##VK: \ BITSET_SET(dynamic, MESA_VK_DYNAMIC_##MESA1); \ BITSET_SET(dynamic, MESA_VK_DYNAMIC_##MESA2); \ BITSET_SET(dynamic, MESA_VK_DYNAMIC_##MESA3); \ break; for (uint32_t i = 0; i < info->dynamicStateCount; i++) { switch (info->pDynamicStates[i]) { CASE3(VERTEX_INPUT_EXT, VI, VI_BINDINGS_VALID, 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( DISCARD_RECTANGLE_ENABLE_EXT, DR_ENABLE) CASE( DISCARD_RECTANGLE_MODE_EXT, DR_MODE) 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) CASE( TESSELLATION_DOMAIN_ORIGIN_EXT, TS_DOMAIN_ORIGIN) CASE( DEPTH_CLAMP_ENABLE_EXT, RS_DEPTH_CLAMP_ENABLE) CASE( POLYGON_MODE_EXT, RS_POLYGON_MODE) CASE( RASTERIZATION_SAMPLES_EXT, MS_RASTERIZATION_SAMPLES) CASE( SAMPLE_MASK_EXT, MS_SAMPLE_MASK) CASE( ALPHA_TO_COVERAGE_ENABLE_EXT, MS_ALPHA_TO_COVERAGE_ENABLE) CASE( ALPHA_TO_ONE_ENABLE_EXT, MS_ALPHA_TO_ONE_ENABLE) CASE( LOGIC_OP_ENABLE_EXT, CB_LOGIC_OP_ENABLE) CASE( COLOR_BLEND_ENABLE_EXT, CB_BLEND_ENABLES) CASE( COLOR_BLEND_EQUATION_EXT, CB_BLEND_EQUATIONS) CASE( COLOR_WRITE_MASK_EXT, CB_WRITE_MASKS) CASE( RASTERIZATION_STREAM_EXT, RS_RASTERIZATION_STREAM) CASE( CONSERVATIVE_RASTERIZATION_MODE_EXT, RS_CONSERVATIVE_MODE) CASE( DEPTH_CLIP_ENABLE_EXT, RS_DEPTH_CLIP_ENABLE) CASE( SAMPLE_LOCATIONS_ENABLE_EXT, MS_SAMPLE_LOCATIONS_ENABLE) CASE( PROVOKING_VERTEX_MODE_EXT, RS_PROVOKING_VERTEX) CASE( LINE_RASTERIZATION_MODE_EXT, RS_LINE_MODE) CASE( LINE_STIPPLE_ENABLE_EXT, RS_LINE_STIPPLE_ENABLE) CASE( DEPTH_CLIP_NEGATIVE_ONE_TO_ONE_EXT, VP_DEPTH_CLIP_NEGATIVE_ONE_TO_ONE) CASE( ATTACHMENT_FEEDBACK_LOOP_ENABLE_EXT, ATTACHMENT_FEEDBACK_LOOP_ENABLE) default: unreachable("Unsupported dynamic graphics state"); } } /* attachmentCount is ignored if all of the states using it are dyanmic. * * TODO: Handle advanced blending here when supported. */ if (BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_BLEND_ENABLES) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_BLEND_EQUATIONS) && BITSET_TEST(dynamic, MESA_VK_DYNAMIC_CB_WRITE_MASKS)) BITSET_SET(dynamic, MESA_VK_DYNAMIC_CB_ATTACHMENT_COUNT); } #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, const VkPipelineVertexInputStateCreateInfo *vi_info) { assert(!IS_DYNAMIC(VI)); memset(vi, 0, sizeof(*vi)); if (!vi_info) return; 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_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_BINDINGS_VALID)) dst->vi_bindings_valid = vi->bindings_valid; 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, const VkPipelineInputAssemblyStateCreateInfo *ia_info) { memset(ia, 0, sizeof(*ia)); if (!ia_info) return; /* From the Vulkan 1.3.224 spec: * * "VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY specifies that the topology * state in VkPipelineInputAssemblyStateCreateInfo only specifies the * topology class, and the specific topology order and adjacency must be * set dynamically with vkCmdSetPrimitiveTopology before any drawing * commands." */ assert(ia_info->topology <= UINT8_MAX); ia->primitive_topology = ia_info->topology; 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, const VkPipelineTessellationStateCreateInfo *ts_info) { *ts = (struct vk_tessellation_state) { .domain_origin = VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT, }; if (!ts_info) return; if (!IS_DYNAMIC(TS_PATCH_CONTROL_POINTS)) { assert(ts_info->patchControlPoints <= UINT8_MAX); ts->patch_control_points = ts_info->patchControlPoints; } if (!IS_DYNAMIC(TS_DOMAIN_ORIGIN)) { 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; } } } 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 = *ts; } 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 (!vp_info) return; 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); } if (!IS_DYNAMIC(VP_DEPTH_CLIP_NEGATIVE_ONE_TO_ONE)) { 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->depth_clip_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); dst->vp.depth_clip_negative_one_to_one = vp->depth_clip_negative_one_to_one; } 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; assert(dr_info->discardRectangleCount <= MESA_VK_MAX_DISCARD_RECTANGLES); dr->mode = dr_info->discardRectangleMode; dr->rectangle_count = dr_info->discardRectangleCount; if (!IS_DYNAMIC(DR_RECTANGLES)) { typed_memcpy(dr->rectangles, dr_info->pDiscardRectangles, dr_info->discardRectangleCount); } } 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.enable = dr->rectangle_count > 0; dst->dr.mode = dr->mode; 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, const VkPipelineRasterizationStateCreateInfo *rs_info) { *rs = (struct vk_rasterization_state) { .rasterizer_discard_enable = false, .conservative_mode = VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT, .extra_primitive_overestimation_size = 0.0f, .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, .depth_clip_enable = IS_DYNAMIC(RS_DEPTH_CLAMP_ENABLE) ? VK_MESA_DEPTH_CLIP_ENABLE_NOT_CLAMP : VK_MESA_DEPTH_CLIP_ENABLE_FALSE, .depth_bias.representation = VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, .depth_bias.exact = false, }; if (!rs_info) return; 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. */ if (!IS_DYNAMIC(RS_DEPTH_CLAMP_ENABLE)) { rs->depth_clamp_enable = rs_info->depthClampEnable; rs->depth_clip_enable = rs_info->depthClampEnable ? VK_MESA_DEPTH_CLIP_ENABLE_FALSE : VK_MESA_DEPTH_CLIP_ENABLE_TRUE; } 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; rs->extra_primitive_overestimation_size = rcs_info->extraPrimitiveOverestimationSize; 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 ? VK_MESA_DEPTH_CLIP_ENABLE_TRUE : VK_MESA_DEPTH_CLIP_ENABLE_FALSE; 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; if (!IS_DYNAMIC(RS_LINE_STIPPLE_ENABLE)) rs->line.stipple.enable = rl_info->stippledLineEnable; if ((IS_DYNAMIC(RS_LINE_STIPPLE_ENABLE) || 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; } case VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT: { const VkDepthBiasRepresentationInfoEXT *dbr_info = (const VkDepthBiasRepresentationInfoEXT *)ext; if (!IS_DYNAMIC(RS_DEPTH_BIAS_FACTORS)) { rs->depth_bias.representation = dbr_info->depthBiasRepresentation; rs->depth_bias.exact = dbr_info->depthBiasExact; } break; } default: break; } } } 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 = *rs; } 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_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) { 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) { memset(ms, 0, sizeof(*ms)); if (!ms_info) return; if (!IS_DYNAMIC(MS_RASTERIZATION_SAMPLES)) { assert(ms_info->rasterizationSamples <= MESA_VK_MAX_SAMPLES); 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) && (IS_DYNAMIC(MS_SAMPLE_LOCATIONS_ENABLE) || (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 = IS_DYNAMIC(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 if (!IS_DYNAMIC(MS_RASTERIZATION_SAMPLES)) { /* 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_dynamic_graphics_state_init_ms(struct vk_dynamic_graphics_state *dst, const BITSET_WORD *needed, const struct vk_multisample_state *ms) { dst->ms.rasterization_samples = ms->rasterization_samples; dst->ms.sample_mask = ms->sample_mask; dst->ms.alpha_to_coverage_enable = ms->alpha_to_coverage_enable; dst->ms.alpha_to_one_enable = ms->alpha_to_one_enable; dst->ms.sample_locations_enable = ms->sample_locations_enable; 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) { 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) { *ds = (struct vk_depth_stencil_state) { .stencil.write_enable = true, }; if (!ds_info) return; 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_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 bool optimize_stencil_face(struct vk_stencil_test_face_state *face, VkCompareOp depthCompareOp, bool consider_write_mask) { /* 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; /* If the write mask is zero, nothing will be written to the stencil buffer * so it's as if all operations are KEEP. */ if (consider_write_mask && face->write_mask == 0) { face->op.pass = VK_STENCIL_OP_KEEP; face->op.fail = VK_STENCIL_OP_KEEP; 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, bool consider_write_mask) { /* stencil.write_enable is a dummy right now that should always be true */ assert(ds->stencil.write_enable); /* From the Vulkan 1.3.221 spec: * * "If there is no depth attachment then the depth test is skipped." */ if (!(ds_aspects & VK_IMAGE_ASPECT_DEPTH_BIT)) ds->depth.test_enable = false; /* From the Vulkan 1.3.221 spec: * * "...or if there is no stencil attachment, the coverage mask is * unmodified by this operation." */ if (!(ds_aspects & VK_IMAGE_ASPECT_STENCIL_BIT)) ds->stencil.test_enable = false; /* 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. */ if (!ds->depth.test_enable) { ds->depth.write_enable = false; ds->depth.compare_op = VK_COMPARE_OP_ALWAYS; } /* If the stencil test is disabled, we won't be writing anything. Make sure * we treat the test as always passing later on as well. */ if (!ds->stencil.test_enable) { 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, consider_write_mask) && !optimize_stencil_face(&ds->stencil.back, ds->depth.compare_op, consider_write_mask)) 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, const VkPipelineColorBlendStateCreateInfo *cb_info) { *cb = (struct vk_color_blend_state) { .color_write_enables = BITFIELD_MASK(MESA_VK_MAX_COLOR_ATTACHMENTS), }; if (!cb_info) return; 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; /* pAttachments is ignored if any of these is not set */ bool full_dynamic = IS_DYNAMIC(CB_BLEND_ENABLES) && IS_DYNAMIC(CB_BLEND_EQUATIONS) && IS_DYNAMIC(CB_WRITE_MASKS); for (uint32_t a = 0; a < cb_info->attachmentCount; a++) { const VkPipelineColorBlendAttachmentState *att = full_dynamic ? NULL : &cb_info->pAttachments[a]; cb->attachments[a] = (struct vk_color_blend_attachment_state) { .blend_enable = IS_DYNAMIC(CB_BLEND_ENABLES) || att->blendEnable, .src_color_blend_factor = IS_DYNAMIC(CB_BLEND_EQUATIONS) ? 0 : att->srcColorBlendFactor, .dst_color_blend_factor = IS_DYNAMIC(CB_BLEND_EQUATIONS) ? 0 : att->dstColorBlendFactor, .src_alpha_blend_factor = IS_DYNAMIC(CB_BLEND_EQUATIONS) ? 0 : att->srcAlphaBlendFactor, .dst_alpha_blend_factor = IS_DYNAMIC(CB_BLEND_EQUATIONS) ? 0 : att->dstAlphaBlendFactor, .write_mask = IS_DYNAMIC(CB_WRITE_MASKS) ? 0xf : att->colorWriteMask, .color_blend_op = IS_DYNAMIC(CB_BLEND_EQUATIONS) ? 0 : att->colorBlendOp, .alpha_blend_op = IS_DYNAMIC(CB_BLEND_EQUATIONS) ? 0 : 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 (!IS_DYNAMIC(CB_COLOR_WRITE_ENABLES) && cw_info != NULL) { uint8_t color_write_enables = 0; assert(cb_info->attachmentCount == cw_info->attachmentCount); for (uint32_t a = 0; a < cw_info->attachmentCount; a++) { if (cw_info->pColorWriteEnables[a]) color_write_enables |= BITFIELD_BIT(a); } cb->color_write_enables = color_write_enables; } else { cb->color_write_enables = BITFIELD_MASK(MESA_VK_MAX_COLOR_ATTACHMENTS); } } 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_enable = cb->logic_op_enable; dst->cb.logic_op = cb->logic_op; dst->cb.color_write_enables = cb->color_write_enables; dst->cb.attachment_count = cb->attachment_count; if (IS_NEEDED(CB_BLEND_ENABLES) || IS_NEEDED(CB_BLEND_EQUATIONS) || IS_NEEDED(CB_WRITE_MASKS)) { typed_memcpy(dst->cb.attachments, cb->attachments, cb->attachment_count); } 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) { 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 struct vk_render_pass_state *driver_rp, const VkGraphicsPipelineCreateInfo *info, VkGraphicsPipelineLibraryFlagsEXT lib) { VkPipelineCreateFlags valid_pipeline_flags = 0; if (lib & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) { valid_pipeline_flags |= VK_PIPELINE_CREATE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR | VK_PIPELINE_CREATE_RENDERING_FRAGMENT_DENSITY_MAP_ATTACHMENT_BIT_EXT; } if (lib & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_OUTPUT_INTERFACE_BIT_EXT) { valid_pipeline_flags |= VK_PIPELINE_CREATE_COLOR_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT | VK_PIPELINE_CREATE_DEPTH_STENCIL_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT; } const VkPipelineCreateFlags pipeline_flags = (driver_rp ? driver_rp->pipeline_flags : vk_get_pipeline_rendering_flags(info)) & valid_pipeline_flags; /* If we already have render pass state and it has attachment info, then * it's complete and we don't need a new one. The one caveat here is that * we may need to add in some rendering flags. */ if (old_rp != NULL && vk_render_pass_state_is_complete(old_rp)) { *rp = *old_rp; rp->pipeline_flags |= pipeline_flags; return; } *rp = (struct vk_render_pass_state) { .render_pass = info->renderPass, .subpass = info->subpass, .pipeline_flags = pipeline_flags, .depth_attachment_format = VK_FORMAT_UNDEFINED, .stencil_attachment_format = VK_FORMAT_UNDEFINED, }; if (info->renderPass != VK_NULL_HANDLE && driver_rp != NULL) { assert(driver_rp->render_pass == info->renderPass); assert(driver_rp->subpass == info->subpass); *rp = *driver_rp; 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 VkAttachmentSampleCountInfoAMD *asc_info = vk_get_pipeline_sample_count_info_amd(info); if (asc_info != NULL) { assert(asc_info->colorAttachmentCount == rp->color_attachment_count); for (uint32_t i = 0; i < asc_info->colorAttachmentCount; i++) { rp->color_attachment_samples[i] = asc_info->pColorAttachmentSamples[i]; } rp->depth_stencil_attachment_samples = asc_info->depthStencilAttachmentSamples; } } 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); \ 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 enum mesa_vk_graphics_state_groups vk_graphics_pipeline_state_groups(const struct vk_graphics_pipeline_state *state) { /* For now, we just validate dynamic state */ enum mesa_vk_graphics_state_groups groups = 0; #define FILL_HAS(STATE, type, s) \ if (state->s != NULL) groups |= STATE FOREACH_STATE_GROUP(FILL_HAS) #undef FILL_HAS return groups | fully_dynamic_state_groups(state->dynamic); } void vk_graphics_pipeline_get_state(const struct vk_graphics_pipeline_state *state, BITSET_WORD *set_state_out) { /* For now, we just validate dynamic state */ enum mesa_vk_graphics_state_groups groups = 0; #define FILL_HAS(STATE, type, s) \ if (state->s != NULL) groups |= STATE FOREACH_STATE_GROUP(FILL_HAS) #undef FILL_HAS BITSET_DECLARE(set_state, MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX); get_dynamic_state_groups(set_state, groups); BITSET_ANDNOT(set_state, set_state, state->dynamic); memcpy(set_state_out, set_state, sizeof(set_state)); } 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 groups = vk_graphics_pipeline_state_groups(state); validate_dynamic_state_groups(state->dynamic, groups); #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_render_pass_state *driver_rp, 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); /* * First, figure out which library-level shader/state groups we need */ VkGraphicsPipelineLibraryFlagsEXT lib; const VkGraphicsPipelineLibraryCreateInfoEXT *gpl_info = vk_find_struct_const(info->pNext, GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT); const VkPipelineLibraryCreateInfoKHR *lib_info = vk_find_struct_const(info->pNext, PIPELINE_LIBRARY_CREATE_INFO_KHR); VkShaderStageFlagBits allowed_stages; if (!(info->flags & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR)) { allowed_stages = VK_SHADER_STAGE_ALL_GRAPHICS | VK_SHADER_STAGE_TASK_BIT_EXT | VK_SHADER_STAGE_MESH_BIT_EXT; } else if (gpl_info) { allowed_stages = 0; /* If we're creating a pipeline library without pre-rasterization, * discard all the associated stages. */ if (gpl_info->flags & VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT) { allowed_stages |= (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT | VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_TASK_BIT_EXT | VK_SHADER_STAGE_MESH_BIT_EXT); } /* If we're creating a pipeline library without fragment shader, * discard that stage. */ if (gpl_info->flags & VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT) allowed_stages |= VK_SHADER_STAGE_FRAGMENT_BIT; } else { /* VkGraphicsPipelineLibraryCreateInfoEXT was omitted, flags should * be assumed to be empty and therefore no shader stage should be * considered. */ allowed_stages = 0; } for (uint32_t i = 0; i < info->stageCount; i++) { state->shader_stages |= info->pStages[i].stage & allowed_stages; } /* In case we return early */ if (alloc_ptr_out != NULL) *alloc_ptr_out = NULL; if (gpl_info) { lib = gpl_info->flags; } else if ((lib_info && lib_info->libraryCount > 0) || (info->flags & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR)) { /* * From the Vulkan 1.3.210 spec: * "If this structure is omitted, and either VkGraphicsPipelineCreateInfo::flags * includes VK_PIPELINE_CREATE_LIBRARY_BIT_KHR or the * VkGraphicsPipelineCreateInfo::pNext chain includes a * VkPipelineLibraryCreateInfoKHR structure with a libraryCount greater than 0, * it is as if flags is 0. Otherwise if this structure is omitted, it is as if * flags includes all possible subsets of the graphics pipeline." */ lib = 0; } 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 (state->shader_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, driver_rp, 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(state->shader_stages & (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_MESH_BIT_NV)); if (state->shader_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; /* From the Vulkan 1.3.218 spec: * * "Fragment shader state is defined by: * ... * - VkPipelineMultisampleStateCreateInfo if sample shading is * enabled or renderpass is not VK_NULL_HANDLE" * * and * * VUID-VkGraphicsPipelineCreateInfo-pMultisampleState-06629 * * "If the pipeline is being created with fragment shader state * pMultisampleState must be NULL or a valid pointer to a valid * VkPipelineMultisampleStateCreateInfo structure" * * so we can reliably detect when to include it based on the * pMultisampleState pointer. */ if (info->pMultisampleState != NULL) 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_COLOR_BIT)) needs |= MESA_VK_GRAPHICS_STATE_COLOR_BLEND_BIT; needs |= MESA_VK_GRAPHICS_STATE_MULTISAMPLE_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 a state is fully dynamic, we don't need to even allocate them. Do * this after we've filtered dynamic state because we still want them to * show up in the dynamic state but don't want the actual state. */ needs &= ~fully_dynamic_state_groups(state->dynamic); /* 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) { if (info->pMultisampleState) 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 #undef IS_NEEDED 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); dst->shader_stages |= src->shader_stages; /* 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 } static bool is_group_all_dynamic(const struct vk_graphics_pipeline_state *state, enum mesa_vk_graphics_state_groups group) { /* Render pass is a bit special, because it contains always-static state * (e.g. the view mask). It's never all dynamic. */ if (group == MESA_VK_GRAPHICS_STATE_RENDER_PASS_BIT) return false; BITSET_DECLARE(group_state, MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX); BITSET_DECLARE(dynamic_state, MESA_VK_DYNAMIC_GRAPHICS_STATE_ENUM_MAX); get_dynamic_state_groups(group_state, group); BITSET_AND(dynamic_state, group_state, state->dynamic); return BITSET_EQUAL(dynamic_state, group_state); } VkResult vk_graphics_pipeline_state_copy(const struct vk_device *device, struct vk_graphics_pipeline_state *state, const struct vk_graphics_pipeline_state *old_state, const VkAllocationCallbacks *alloc, VkSystemAllocationScope scope, void **alloc_ptr_out) { vk_graphics_pipeline_state_validate(old_state); VK_MULTIALLOC(ma); #define ENSURE_STATE_IF_NEEDED(STATE, type, s) \ struct type *new_##s = NULL; \ if (old_state->s && !is_group_all_dynamic(state, STATE)) { \ vk_multialloc_add(&ma, &new_##s, struct type, 1); \ } FOREACH_STATE_GROUP(ENSURE_STATE_IF_NEEDED) #undef ENSURE_STATE_IF_NEEDED /* Sample locations are a bit special. */ struct vk_sample_locations_state *new_sample_locations = NULL; if (old_state->ms && old_state->ms->sample_locations && !BITSET_TEST(old_state->dynamic, MESA_VK_DYNAMIC_MS_SAMPLE_LOCATIONS)) { assert(old_state->ms->sample_locations); vk_multialloc_add(&ma, &new_sample_locations, struct vk_sample_locations_state, 1); } if (ma.size > 0) { *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); } if (new_sample_locations) { *new_sample_locations = *old_state->ms->sample_locations; } #define COPY_STATE_IF_NEEDED(STATE, type, s) \ if (new_##s) { \ *new_##s = *old_state->s; \ } \ state->s = new_##s; FOREACH_STATE_GROUP(COPY_STATE_IF_NEEDED) if (new_ms) { new_ms->sample_locations = new_sample_locations; } state->shader_stages = old_state->shader_stages; BITSET_COPY(state->dynamic, old_state->dynamic); #undef COPY_STATE_IF_NEEDED vk_graphics_pipeline_state_validate(state); return VK_SUCCESS; } 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 = { .write_enable = true, .front = { .compare_mask = -1, .write_mask = -1, }, .back = { .compare_mask = -1, .write_mask = -1, }, }, }, .cb = { .color_write_enables = 0xffu, .attachment_count = MESA_VK_MAX_COLOR_ATTACHMENTS, }, }; 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 get_dynamic_state_groups(dyn->set, groups); /* Vertex input state is always included in a complete pipeline. If p->vi * is NULL, that means that it has been precompiled by the driver, but we * should still track vi_bindings_valid. */ BITSET_SET(dyn->set, MESA_VK_DYNAMIC_VI_BINDINGS_VALID); /* If the pipeline doesn't render any color attachments, we should still * keep track of the fact that it writes 0 attachments, even though none of * the other blend states will be initialized. Normally this would be * initialized with the other blend states. */ if (!p->rp || !(p->rp->attachment_aspects & VK_IMAGE_ASPECT_COLOR_BIT)) { dyn->cb.attachment_count = 0; BITSET_SET(dyn->set, MESA_VK_DYNAMIC_CB_ATTACHMENT_COUNT); } /* Mask off all but the groups we actually found */ 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) if (IS_SET_IN_SRC(VI)) { assert(dst->vi != NULL); 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_BINDINGS_VALID)) COPY_MEMBER(VI_BINDINGS_VALID, vi_bindings_valid); if (IS_SET_IN_SRC(VI_BINDING_STRIDES)) { assert(IS_SET_IN_SRC(VI_BINDINGS_VALID)); u_foreach_bit(a, src->vi_bindings_valid) { COPY_MEMBER(VI_BINDING_STRIDES, vi_binding_strides[a]); } } 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(TS_DOMAIN_ORIGIN, ts.domain_origin); 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_VIEWPORTS, 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_SCISSORS, vp.scissors, src->vp.scissor_count); } COPY_IF_SET(VP_DEPTH_CLIP_NEGATIVE_ONE_TO_ONE, vp.depth_clip_negative_one_to_one); COPY_IF_SET(DR_ENABLE, dr.enable); COPY_IF_SET(DR_MODE, dr.mode); 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_DEPTH_CLAMP_ENABLE, rs.depth_clamp_enable); COPY_IF_SET(RS_DEPTH_CLIP_ENABLE, rs.depth_clip_enable); COPY_IF_SET(RS_POLYGON_MODE, rs.polygon_mode); COPY_IF_SET(RS_CULL_MODE, rs.cull_mode); COPY_IF_SET(RS_FRONT_FACE, rs.front_face); COPY_IF_SET(RS_CONSERVATIVE_MODE, rs.conservative_mode); COPY_IF_SET(RS_EXTRA_PRIMITIVE_OVERESTIMATION_SIZE, rs.extra_primitive_overestimation_size); COPY_IF_SET(RS_RASTERIZATION_ORDER_AMD, rs.rasterization_order_amd); COPY_IF_SET(RS_PROVOKING_VERTEX, rs.provoking_vertex); COPY_IF_SET(RS_RASTERIZATION_STREAM, rs.rasterization_stream); 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_DEPTH_BIAS_FACTORS, rs.depth_bias.representation); COPY_IF_SET(RS_DEPTH_BIAS_FACTORS, rs.depth_bias.exact); COPY_IF_SET(RS_LINE_WIDTH, rs.line.width); COPY_IF_SET(RS_LINE_MODE, rs.line.mode); COPY_IF_SET(RS_LINE_STIPPLE_ENABLE, rs.line.stipple.enable); 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]); COPY_IF_SET(MS_RASTERIZATION_SAMPLES, ms.rasterization_samples); COPY_IF_SET(MS_SAMPLE_MASK, ms.sample_mask); COPY_IF_SET(MS_ALPHA_TO_COVERAGE_ENABLE, ms.alpha_to_coverage_enable); COPY_IF_SET(MS_ALPHA_TO_ONE_ENABLE, ms.alpha_to_one_enable); COPY_IF_SET(MS_SAMPLE_LOCATIONS_ENABLE, ms.sample_locations_enable); if (IS_SET_IN_SRC(MS_SAMPLE_LOCATIONS)) { assert(dst->ms.sample_locations != NULL); 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_ENABLE, cb.logic_op_enable); COPY_IF_SET(CB_LOGIC_OP, cb.logic_op); COPY_IF_SET(CB_ATTACHMENT_COUNT, cb.attachment_count); COPY_IF_SET(CB_COLOR_WRITE_ENABLES, cb.color_write_enables); if (IS_SET_IN_SRC(CB_BLEND_ENABLES)) { for (uint32_t a = 0; a < src->cb.attachment_count; a++) COPY_MEMBER(CB_BLEND_ENABLES, cb.attachments[a].blend_enable); } if (IS_SET_IN_SRC(CB_BLEND_EQUATIONS)) { for (uint32_t a = 0; a < src->cb.attachment_count; a++) { COPY_MEMBER(CB_BLEND_EQUATIONS, cb.attachments[a].src_color_blend_factor); COPY_MEMBER(CB_BLEND_EQUATIONS, cb.attachments[a].dst_color_blend_factor); COPY_MEMBER(CB_BLEND_EQUATIONS, cb.attachments[a].src_alpha_blend_factor); COPY_MEMBER(CB_BLEND_EQUATIONS, cb.attachments[a].dst_alpha_blend_factor); COPY_MEMBER(CB_BLEND_EQUATIONS, cb.attachments[a].color_blend_op); COPY_MEMBER(CB_BLEND_EQUATIONS, cb.attachments[a].alpha_blend_op); } } if (IS_SET_IN_SRC(CB_WRITE_MASKS)) { for (uint32_t a = 0; a < src->cb.attachment_count; a++) COPY_MEMBER(CB_WRITE_MASKS, cb.attachments[a].write_mask); } 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]; } } 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); dyn->vi->bindings[b].stride = desc->stride; dyn->vi->bindings[b].input_rate = desc->inputRate; dyn->vi->bindings[b].divisor = desc->divisor; /* Also set bindings_strides in case a driver is keying off that */ dyn->vi_binding_strides[b] = desc->stride; } dyn->vi->bindings_valid = bindings_valid; SET_DYN_VALUE(dyn, VI_BINDINGS_VALID, 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); dyn->vi->attributes[a].binding = desc->binding; dyn->vi->attributes[a].format = desc->format; dyn->vi->attributes[a].offset = desc->offset; } dyn->vi->attributes_valid = attributes_valid; BITSET_SET(dyn->set, MESA_VK_DYNAMIC_VI); BITSET_SET(dyn->set, MESA_VK_DYNAMIC_VI_BINDING_STRIDES); BITSET_SET(dyn->dirty, MESA_VK_DYNAMIC_VI); BITSET_SET(dyn->dirty, MESA_VK_DYNAMIC_VI_BINDING_STRIDES); } 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_CmdSetTessellationDomainOriginEXT(VkCommandBuffer commandBuffer, VkTessellationDomainOrigin domainOrigin) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_VALUE(dyn, TS_DOMAIN_ORIGIN, ts.domain_origin, domainOrigin); } 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_CmdSetDepthClipNegativeOneToOneEXT(VkCommandBuffer commandBuffer, VkBool32 negativeOneToOne) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_BOOL(dyn, VP_DEPTH_CLIP_NEGATIVE_ONE_TO_ONE, vp.depth_clip_negative_one_to_one, negativeOneToOne); } 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_CmdSetDepthClampEnableEXT(VkCommandBuffer commandBuffer, VkBool32 depthClampEnable) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_BOOL(dyn, RS_DEPTH_CLAMP_ENABLE, rs.depth_clamp_enable, depthClampEnable); } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetDepthClipEnableEXT(VkCommandBuffer commandBuffer, VkBool32 depthClipEnable) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_VALUE(dyn, RS_DEPTH_CLIP_ENABLE, rs.depth_clip_enable, depthClipEnable ? VK_MESA_DEPTH_CLIP_ENABLE_TRUE : VK_MESA_DEPTH_CLIP_ENABLE_FALSE); } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetPolygonModeEXT(VkCommandBuffer commandBuffer, VkPolygonMode polygonMode) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_VALUE(dyn, RS_POLYGON_MODE, rs.polygon_mode, polygonMode); } 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_CmdSetConservativeRasterizationModeEXT( VkCommandBuffer commandBuffer, VkConservativeRasterizationModeEXT conservativeRasterizationMode) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_VALUE(dyn, RS_CONSERVATIVE_MODE, rs.conservative_mode, conservativeRasterizationMode); } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetExtraPrimitiveOverestimationSizeEXT( VkCommandBuffer commandBuffer, float extraPrimitiveOverestimationSize) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_VALUE(dyn, RS_EXTRA_PRIMITIVE_OVERESTIMATION_SIZE, rs.extra_primitive_overestimation_size, extraPrimitiveOverestimationSize); } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetProvokingVertexModeEXT(VkCommandBuffer commandBuffer, VkProvokingVertexModeEXT provokingVertexMode) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_VALUE(dyn, RS_PROVOKING_VERTEX, rs.provoking_vertex, provokingVertexMode); } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetRasterizationStreamEXT(VkCommandBuffer commandBuffer, uint32_t rasterizationStream) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_VALUE(dyn, RS_PROVOKING_VERTEX, rs.rasterization_stream, rasterizationStream); } 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); VkDepthBiasInfoEXT depth_bias_info = { .sType = VK_STRUCTURE_TYPE_DEPTH_BIAS_INFO_EXT, .depthBiasConstantFactor = depthBiasConstantFactor, .depthBiasClamp = depthBiasClamp, .depthBiasSlopeFactor = depthBiasSlopeFactor, }; cmd->base.device->dispatch_table.CmdSetDepthBias2EXT(commandBuffer, &depth_bias_info); } 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_CmdSetLineRasterizationModeEXT(VkCommandBuffer commandBuffer, VkLineRasterizationModeEXT lineRasterizationMode) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_VALUE(dyn, RS_LINE_MODE, rs.line.mode, lineRasterizationMode); } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetLineStippleEnableEXT(VkCommandBuffer commandBuffer, VkBool32 stippledLineEnable) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_BOOL(dyn, RS_LINE_STIPPLE_ENABLE, rs.line.stipple.enable, stippledLineEnable); } 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_CmdSetRasterizationSamplesEXT(VkCommandBuffer commandBuffer, VkSampleCountFlagBits rasterizationSamples) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; assert(rasterizationSamples <= MESA_VK_MAX_SAMPLES); SET_DYN_VALUE(dyn, MS_RASTERIZATION_SAMPLES, ms.rasterization_samples, rasterizationSamples); } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetSampleMaskEXT(VkCommandBuffer commandBuffer, VkSampleCountFlagBits samples, const VkSampleMask *pSampleMask) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; assert(samples <= MESA_VK_MAX_SAMPLES); VkSampleMask sample_mask = *pSampleMask & BITFIELD_MASK(MESA_VK_MAX_SAMPLES); SET_DYN_VALUE(dyn, MS_SAMPLE_MASK, ms.sample_mask, sample_mask); } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetAlphaToCoverageEnableEXT(VkCommandBuffer commandBuffer, VkBool32 alphaToCoverageEnable) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_VALUE(dyn, MS_ALPHA_TO_COVERAGE_ENABLE, ms.alpha_to_coverage_enable, alphaToCoverageEnable); } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetAlphaToOneEnableEXT(VkCommandBuffer commandBuffer, VkBool32 alphaToOneEnable) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_VALUE(dyn, MS_ALPHA_TO_ONE_ENABLE, ms.alpha_to_one_enable, alphaToOneEnable); } 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_CmdSetSampleLocationsEnableEXT(VkCommandBuffer commandBuffer, VkBool32 sampleLocationsEnable) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_BOOL(dyn, MS_SAMPLE_LOCATIONS_ENABLE, ms.sample_locations_enable, sampleLocationsEnable); } 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_CmdSetLogicOpEnableEXT(VkCommandBuffer commandBuffer, VkBool32 logicOpEnable) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_BOOL(dyn, CB_LOGIC_OP_ENABLE, cb.logic_op_enable, logicOpEnable); } 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_CmdSetColorBlendEnableEXT(VkCommandBuffer commandBuffer, uint32_t firstAttachment, uint32_t attachmentCount, const VkBool32 *pColorBlendEnables) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; for (uint32_t i = 0; i < attachmentCount; i++) { uint32_t a = firstAttachment + i; assert(a < ARRAY_SIZE(dyn->cb.attachments)); SET_DYN_BOOL(dyn, CB_BLEND_ENABLES, cb.attachments[a].blend_enable, pColorBlendEnables[i]); } } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetColorBlendEquationEXT(VkCommandBuffer commandBuffer, uint32_t firstAttachment, uint32_t attachmentCount, const VkColorBlendEquationEXT *pColorBlendEquations) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; for (uint32_t i = 0; i < attachmentCount; i++) { uint32_t a = firstAttachment + i; assert(a < ARRAY_SIZE(dyn->cb.attachments)); SET_DYN_VALUE(dyn, CB_BLEND_EQUATIONS, cb.attachments[a].src_color_blend_factor, pColorBlendEquations[i].srcColorBlendFactor); SET_DYN_VALUE(dyn, CB_BLEND_EQUATIONS, cb.attachments[a].dst_color_blend_factor, pColorBlendEquations[i].dstColorBlendFactor); SET_DYN_VALUE(dyn, CB_BLEND_EQUATIONS, cb.attachments[a].color_blend_op, pColorBlendEquations[i].colorBlendOp); SET_DYN_VALUE(dyn, CB_BLEND_EQUATIONS, cb.attachments[a].src_alpha_blend_factor, pColorBlendEquations[i].srcAlphaBlendFactor); SET_DYN_VALUE(dyn, CB_BLEND_EQUATIONS, cb.attachments[a].dst_alpha_blend_factor, pColorBlendEquations[i].dstAlphaBlendFactor); SET_DYN_VALUE(dyn, CB_BLEND_EQUATIONS, cb.attachments[a].alpha_blend_op, pColorBlendEquations[i].alphaBlendOp); } } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetColorWriteMaskEXT(VkCommandBuffer commandBuffer, uint32_t firstAttachment, uint32_t attachmentCount, const VkColorComponentFlags *pColorWriteMasks) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; for (uint32_t i = 0; i < attachmentCount; i++) { uint32_t a = firstAttachment + i; assert(a < ARRAY_SIZE(dyn->cb.attachments)); SET_DYN_VALUE(dyn, CB_WRITE_MASKS, cb.attachments[a].write_mask, pColorWriteMasks[i]); } } 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); } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetColorBlendAdvancedEXT(VkCommandBuffer commandBuffer, uint32_t firstAttachment, uint32_t attachmentCount, const VkColorBlendAdvancedEXT* pColorBlendAdvanced) { unreachable("VK_EXT_blend_operation_advanced unsupported"); } void vk_cmd_set_cb_attachment_count(struct vk_command_buffer *cmd, uint32_t attachment_count) { struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_VALUE(dyn, CB_ATTACHMENT_COUNT, cb.attachment_count, attachment_count); } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetDiscardRectangleEnableEXT(VkCommandBuffer commandBuffer, VkBool32 discardRectangleEnable) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_VALUE(dyn, DR_ENABLE, dr.enable, discardRectangleEnable); } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetDiscardRectangleModeEXT(VkCommandBuffer commandBuffer, VkDiscardRectangleModeEXT discardRectangleMode) { VK_FROM_HANDLE(vk_command_buffer, cmd, commandBuffer); struct vk_dynamic_graphics_state *dyn = &cmd->dynamic_graphics_state; SET_DYN_VALUE(dyn, DR_MODE, dr.mode, discardRectangleMode); } VKAPI_ATTR void VKAPI_CALL vk_common_CmdSetDepthBias2EXT( VkCommandBuffer commandBuffer, const VkDepthBiasInfoEXT* pDepthBiasInfo) { 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, pDepthBiasInfo->depthBiasConstantFactor); SET_DYN_VALUE(dyn, RS_DEPTH_BIAS_FACTORS, rs.depth_bias.clamp, pDepthBiasInfo->depthBiasClamp); SET_DYN_VALUE(dyn, RS_DEPTH_BIAS_FACTORS, rs.depth_bias.slope, pDepthBiasInfo->depthBiasSlopeFactor); /** From the Vulkan 1.3.254 spec: * * "If pNext does not contain a VkDepthBiasRepresentationInfoEXT * structure, then this command is equivalent to including a * VkDepthBiasRepresentationInfoEXT with depthBiasExact set to VK_FALSE * and depthBiasRepresentation set to * VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT." */ const VkDepthBiasRepresentationInfoEXT *dbr_info = vk_find_struct_const(pDepthBiasInfo->pNext, DEPTH_BIAS_REPRESENTATION_INFO_EXT); if (dbr_info) { SET_DYN_VALUE(dyn, RS_DEPTH_BIAS_FACTORS, rs.depth_bias.representation, dbr_info->depthBiasRepresentation); SET_DYN_VALUE(dyn, RS_DEPTH_BIAS_FACTORS, rs.depth_bias.exact, dbr_info->depthBiasExact); } else { SET_DYN_VALUE(dyn, RS_DEPTH_BIAS_FACTORS, rs.depth_bias.representation, VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT); SET_DYN_VALUE(dyn, RS_DEPTH_BIAS_FACTORS, rs.depth_bias.exact, false); } } const char * vk_dynamic_graphic_state_to_str(enum mesa_vk_dynamic_graphics_state state) { #define NAME(name) \ case MESA_VK_DYNAMIC_##name: return #name switch (state) { NAME(VI); NAME(VI_BINDINGS_VALID); NAME(VI_BINDING_STRIDES); NAME(IA_PRIMITIVE_TOPOLOGY); NAME(IA_PRIMITIVE_RESTART_ENABLE); NAME(TS_PATCH_CONTROL_POINTS); NAME(TS_DOMAIN_ORIGIN); NAME(VP_VIEWPORT_COUNT); NAME(VP_VIEWPORTS); NAME(VP_SCISSOR_COUNT); NAME(VP_SCISSORS); NAME(VP_DEPTH_CLIP_NEGATIVE_ONE_TO_ONE); NAME(DR_RECTANGLES); NAME(DR_MODE); NAME(DR_ENABLE); NAME(RS_RASTERIZER_DISCARD_ENABLE); NAME(RS_DEPTH_CLAMP_ENABLE); NAME(RS_DEPTH_CLIP_ENABLE); NAME(RS_POLYGON_MODE); NAME(RS_CULL_MODE); NAME(RS_FRONT_FACE); NAME(RS_CONSERVATIVE_MODE); NAME(RS_EXTRA_PRIMITIVE_OVERESTIMATION_SIZE); NAME(RS_RASTERIZATION_ORDER_AMD); NAME(RS_PROVOKING_VERTEX); NAME(RS_RASTERIZATION_STREAM); NAME(RS_DEPTH_BIAS_ENABLE); NAME(RS_DEPTH_BIAS_FACTORS); NAME(RS_LINE_WIDTH); NAME(RS_LINE_MODE); NAME(RS_LINE_STIPPLE_ENABLE); NAME(RS_LINE_STIPPLE); NAME(FSR); NAME(MS_RASTERIZATION_SAMPLES); NAME(MS_SAMPLE_MASK); NAME(MS_ALPHA_TO_COVERAGE_ENABLE); NAME(MS_ALPHA_TO_ONE_ENABLE); NAME(MS_SAMPLE_LOCATIONS_ENABLE); NAME(MS_SAMPLE_LOCATIONS); NAME(DS_DEPTH_TEST_ENABLE); NAME(DS_DEPTH_WRITE_ENABLE); NAME(DS_DEPTH_COMPARE_OP); NAME(DS_DEPTH_BOUNDS_TEST_ENABLE); NAME(DS_DEPTH_BOUNDS_TEST_BOUNDS); NAME(DS_STENCIL_TEST_ENABLE); NAME(DS_STENCIL_OP); NAME(DS_STENCIL_COMPARE_MASK); NAME(DS_STENCIL_WRITE_MASK); NAME(DS_STENCIL_REFERENCE); NAME(CB_LOGIC_OP_ENABLE); NAME(CB_LOGIC_OP); NAME(CB_ATTACHMENT_COUNT); NAME(CB_COLOR_WRITE_ENABLES); NAME(CB_BLEND_ENABLES); NAME(CB_BLEND_EQUATIONS); NAME(CB_WRITE_MASKS); NAME(CB_BLEND_CONSTANTS); NAME(ATTACHMENT_FEEDBACK_LOOP_ENABLE); default: unreachable("Invalid state"); } #undef NAME }