1 /*
2 * Copyright © 2022 Valve Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * Authors:
24 * Mike Blumenkrantz <michael.blumenkrantz@gmail.com>
25 */
26
27
28 /**
29 * this file is used to optimize pipeline state management
30 * pipeline state comparisons are the most significant cause of CPU overhead aside from descriptors,
31 * so more effort must be taken to reduce it by any means
32 */
33 #include "zink_types.h"
34 #include "zink_pipeline.h"
35 #include "zink_program.h"
36 #include "zink_screen.h"
37
38 /* runtime-optimized pipeline state hashing */
39 template <zink_dynamic_state DYNAMIC_STATE>
40 static uint32_t
hash_gfx_pipeline_state(const void * key,struct zink_screen * screen)41 hash_gfx_pipeline_state(const void *key, struct zink_screen *screen)
42 {
43 const struct zink_gfx_pipeline_state *state = (const struct zink_gfx_pipeline_state *)key;
44 uint32_t hash = _mesa_hash_data(key, screen->have_full_ds3 ?
45 offsetof(struct zink_gfx_pipeline_state, sample_mask) :
46 offsetof(struct zink_gfx_pipeline_state, hash));
47 if (DYNAMIC_STATE < ZINK_DYNAMIC_STATE2)
48 hash = XXH32(&state->dyn_state3, sizeof(state->dyn_state3), hash);
49 if (DYNAMIC_STATE < ZINK_DYNAMIC_STATE3)
50 hash = XXH32(&state->dyn_state2, sizeof(state->dyn_state2), hash);
51 if (DYNAMIC_STATE != ZINK_NO_DYNAMIC_STATE)
52 return hash;
53 return XXH32(&state->dyn_state1, sizeof(state->dyn_state1), hash);
54 }
55
56 template <bool HAS_DYNAMIC>
57 static unsigned
get_pipeline_idx(enum mesa_prim mode,VkPrimitiveTopology vkmode)58 get_pipeline_idx(enum mesa_prim mode, VkPrimitiveTopology vkmode)
59 {
60 /* VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY specifies that the topology state in
61 * VkPipelineInputAssemblyStateCreateInfo only specifies the topology class,
62 * and the specific topology order and adjacency must be set dynamically
63 * with vkCmdSetPrimitiveTopology before any drawing commands.
64 */
65 if (HAS_DYNAMIC) {
66 return get_primtype_idx(mode);
67 }
68 return vkmode;
69 }
70
71 /*
72 VUID-vkCmdBindVertexBuffers2-pStrides-06209
73 If pStrides is not NULL each element of pStrides must be either 0 or greater than or equal
74 to the maximum extent of all vertex input attributes fetched from the corresponding
75 binding, where the extent is calculated as the VkVertexInputAttributeDescription::offset
76 plus VkVertexInputAttributeDescription::format size
77
78 * thus, if the stride doesn't meet the minimum requirement for a binding,
79 * disable the dynamic state here and use a fully-baked pipeline
80 */
81 static bool
check_vertex_strides(struct zink_context * ctx)82 check_vertex_strides(struct zink_context *ctx)
83 {
84 const struct zink_vertex_elements_state *ves = ctx->element_state;
85 for (unsigned i = 0; i < ves->hw_state.num_bindings; i++) {
86 const struct pipe_vertex_buffer *vb = ctx->vertex_buffers + ves->hw_state.binding_map[i];
87 unsigned stride = vb->buffer.resource ? ves->hw_state.b.strides[i] : 0;
88 if (stride && stride < ves->min_stride[i])
89 return false;
90 }
91 return true;
92 }
93
94 /* runtime-optimized function to recalc pipeline state and find a usable pipeline:
95 * in theory, zink supports many feature levels,
96 * but it's important to provide a more optimized codepath for drivers that support all the best features
97 */
98 template <zink_dynamic_state DYNAMIC_STATE, bool HAVE_LIB>
99 VkPipeline
zink_get_gfx_pipeline(struct zink_context * ctx,struct zink_gfx_program * prog,struct zink_gfx_pipeline_state * state,enum mesa_prim mode)100 zink_get_gfx_pipeline(struct zink_context *ctx,
101 struct zink_gfx_program *prog,
102 struct zink_gfx_pipeline_state *state,
103 enum mesa_prim mode)
104 {
105 struct zink_screen *screen = zink_screen(ctx->base.screen);
106 bool uses_dynamic_stride = state->uses_dynamic_stride;
107
108 VkPrimitiveTopology vkmode = zink_primitive_topology(mode);
109 const unsigned idx = screen->info.dynamic_state3_props.dynamicPrimitiveTopologyUnrestricted ?
110 0 :
111 get_pipeline_idx<DYNAMIC_STATE >= ZINK_DYNAMIC_STATE>(mode, vkmode);
112 assert(idx <= ARRAY_SIZE(prog->pipelines[0]));
113 if (!state->dirty && !state->modules_changed &&
114 ((DYNAMIC_STATE == ZINK_DYNAMIC_VERTEX_INPUT || DYNAMIC_STATE == ZINK_DYNAMIC_VERTEX_INPUT2) && !ctx->vertex_state_changed) &&
115 idx == state->idx)
116 return state->pipeline;
117
118 struct hash_entry *entry = NULL;
119
120 /* recalc the base pipeline state hash */
121 if (state->dirty) {
122 if (state->pipeline) //avoid on first hash
123 state->final_hash ^= state->hash;
124 state->hash = hash_gfx_pipeline_state<DYNAMIC_STATE>(state, screen);
125 state->final_hash ^= state->hash;
126 state->dirty = false;
127 }
128 /* extra safety asserts for optimal path to catch refactoring bugs */
129 if (prog->optimal_keys) {
130 ASSERTED const union zink_shader_key_optimal *opt = (union zink_shader_key_optimal*)&prog->last_variant_hash;
131 ASSERTED uint32_t sanitized = zink_sanitize_optimal_key(ctx->gfx_stages, ctx->gfx_pipeline_state.shader_keys_optimal.key.val);
132 assert(opt->val == sanitized);
133 assert(state->optimal_key == sanitized);
134 }
135 /* recalc vertex state if missing optimal extensions */
136 if (DYNAMIC_STATE != ZINK_DYNAMIC_VERTEX_INPUT2 && DYNAMIC_STATE != ZINK_DYNAMIC_VERTEX_INPUT && ctx->vertex_state_changed) {
137 if (state->pipeline)
138 state->final_hash ^= state->vertex_hash;
139 /* even if dynamic stride is available, it may not be usable with the current pipeline */
140 if (DYNAMIC_STATE != ZINK_NO_DYNAMIC_STATE)
141 uses_dynamic_stride = check_vertex_strides(ctx);
142 if (!uses_dynamic_stride) {
143 uint32_t hash = 0;
144 /* if we don't have dynamic states, we have to hash the enabled vertex buffer bindings */
145 uint32_t vertex_buffers_enabled_mask = state->vertex_buffers_enabled_mask;
146 hash = XXH32(&vertex_buffers_enabled_mask, sizeof(uint32_t), hash);
147
148 for (unsigned i = 0; i < state->element_state->num_bindings; i++) {
149 const unsigned buffer_id = ctx->element_state->hw_state.binding_map[i];
150 struct pipe_vertex_buffer *vb = ctx->vertex_buffers + buffer_id;
151 state->vertex_strides[buffer_id] = vb->buffer.resource ? state->element_state->b.strides[i] : 0;
152 hash = XXH32(&state->vertex_strides[buffer_id], sizeof(uint32_t), hash);
153 }
154 state->vertex_hash = hash ^ state->element_state->hash;
155 } else
156 state->vertex_hash = state->element_state->hash;
157 state->final_hash ^= state->vertex_hash;
158 }
159 state->modules_changed = false;
160 state->uses_dynamic_stride = uses_dynamic_stride;
161 state->idx = idx;
162 ctx->vertex_state_changed = false;
163
164 const int rp_idx = state->render_pass ? 1 : 0;
165 /* shortcut for reusing previous pipeline across program changes */
166 if (DYNAMIC_STATE == ZINK_DYNAMIC_VERTEX_INPUT || DYNAMIC_STATE == ZINK_DYNAMIC_VERTEX_INPUT2) {
167 if (prog->last_finalized_hash[rp_idx][idx] == state->final_hash &&
168 !prog->inline_variants && likely(prog->last_pipeline[rp_idx][idx]) &&
169 /* this data is too big to compare in the fast-path */
170 likely(!prog->shaders[MESA_SHADER_FRAGMENT]->fs.legacy_shadow_mask)) {
171 state->pipeline = prog->last_pipeline[rp_idx][idx];
172 return state->pipeline;
173 }
174 }
175 entry = _mesa_hash_table_search_pre_hashed(&prog->pipelines[rp_idx][idx], state->final_hash, state);
176
177 if (!entry) {
178 /* always wait on async precompile/cache fence */
179 util_queue_fence_wait(&prog->base.cache_fence);
180 struct zink_gfx_pipeline_cache_entry *pc_entry = CALLOC_STRUCT(zink_gfx_pipeline_cache_entry);
181 if (!pc_entry)
182 return VK_NULL_HANDLE;
183 /* cache entries must have all state needed to construct pipelines
184 * TODO: maybe optimize this since all these values aren't actually needed
185 */
186 memcpy(&pc_entry->state, state, sizeof(*state));
187 pc_entry->state.rendering_info.pColorAttachmentFormats = pc_entry->state.rendering_formats;
188 pc_entry->prog = prog;
189 /* init the optimized background compile fence */
190 util_queue_fence_init(&pc_entry->fence);
191 entry = _mesa_hash_table_insert_pre_hashed(&prog->pipelines[rp_idx][idx], state->final_hash, pc_entry, pc_entry);
192 if (prog->base.uses_shobj && !prog->is_separable) {
193 memcpy(pc_entry->shobjs, prog->objs, sizeof(prog->objs));
194 zink_gfx_program_compile_queue(ctx, pc_entry);
195 } else if (HAVE_LIB && zink_can_use_pipeline_libs(ctx)) {
196 /* this is the graphics pipeline library path: find/construct all partial pipelines */
197 simple_mtx_lock(&prog->libs->lock);
198 struct set_entry *he = _mesa_set_search(&prog->libs->libs, &ctx->gfx_pipeline_state.optimal_key);
199 struct zink_gfx_library_key *gkey;
200 if (he) {
201 gkey = (struct zink_gfx_library_key *)he->key;
202 } else {
203 assert(!prog->is_separable);
204 gkey = zink_create_pipeline_lib(screen, prog, &ctx->gfx_pipeline_state);
205 }
206 simple_mtx_unlock(&prog->libs->lock);
207 struct zink_gfx_input_key *ikey = DYNAMIC_STATE == ZINK_DYNAMIC_VERTEX_INPUT ?
208 zink_find_or_create_input_dynamic(ctx, vkmode) :
209 zink_find_or_create_input(ctx, vkmode);
210 struct zink_gfx_output_key *okey = DYNAMIC_STATE >= ZINK_DYNAMIC_STATE3 && screen->have_full_ds3 ?
211 zink_find_or_create_output_ds3(ctx) :
212 zink_find_or_create_output(ctx);
213 /* partial pipelines are stored to the cache entry for async optimized pipeline compiles */
214 pc_entry->gpl.ikey = ikey;
215 pc_entry->gpl.gkey = gkey;
216 pc_entry->gpl.okey = okey;
217 /* try to hit optimized compile cache first if possible */
218 if (!prog->is_separable)
219 pc_entry->pipeline = zink_create_gfx_pipeline_combined(screen, prog, ikey->pipeline, &gkey->pipeline, 1, okey->pipeline, true, true);
220 if (!pc_entry->pipeline) {
221 /* create the non-optimized pipeline first using fast-linking to avoid stuttering */
222 pc_entry->pipeline = zink_create_gfx_pipeline_combined(screen, prog, ikey->pipeline, &gkey->pipeline, 1, okey->pipeline, false, false);
223 if (!prog->is_separable)
224 /* trigger async optimized pipeline compile if this was the fast-linked unoptimized pipeline */
225 zink_gfx_program_compile_queue(ctx, pc_entry);
226 }
227 } else {
228 /* optimize by default only when expecting precompiles in order to reduce stuttering */
229 if (DYNAMIC_STATE != ZINK_DYNAMIC_VERTEX_INPUT2 && DYNAMIC_STATE != ZINK_DYNAMIC_VERTEX_INPUT)
230 pc_entry->pipeline = zink_create_gfx_pipeline(screen, prog, prog->objs, state, state->element_state->binding_map, vkmode, !HAVE_LIB, NULL);
231 else
232 pc_entry->pipeline = zink_create_gfx_pipeline(screen, prog, prog->objs, state, NULL, vkmode, !HAVE_LIB, NULL);
233 if (HAVE_LIB && !prog->is_separable)
234 /* trigger async optimized pipeline compile if this was an unoptimized pipeline */
235 zink_gfx_program_compile_queue(ctx, pc_entry);
236 }
237 if (pc_entry->pipeline == VK_NULL_HANDLE)
238 return VK_NULL_HANDLE;
239
240 zink_screen_update_pipeline_cache(screen, &prog->base, false);
241 }
242
243 struct zink_gfx_pipeline_cache_entry *cache_entry = (struct zink_gfx_pipeline_cache_entry *)entry->data;
244 state->pipeline = cache_entry->pipeline;
245 /* update states for fastpath */
246 if (DYNAMIC_STATE >= ZINK_DYNAMIC_VERTEX_INPUT) {
247 prog->last_finalized_hash[rp_idx][idx] = state->final_hash;
248 prog->last_pipeline[rp_idx][idx] = cache_entry->pipeline;
249 }
250 return state->pipeline;
251 }
252
253 /* runtime-optimized pipeline state comparisons */
254 template <zink_pipeline_dynamic_state DYNAMIC_STATE, unsigned STAGE_MASK>
255 static bool
equals_gfx_pipeline_state(const void * a,const void * b)256 equals_gfx_pipeline_state(const void *a, const void *b)
257 {
258 const struct zink_gfx_pipeline_state *sa = (const struct zink_gfx_pipeline_state *)a;
259 const struct zink_gfx_pipeline_state *sb = (const struct zink_gfx_pipeline_state *)b;
260 if (DYNAMIC_STATE < ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT) {
261 if (sa->uses_dynamic_stride != sb->uses_dynamic_stride)
262 return false;
263 }
264 if (DYNAMIC_STATE == ZINK_PIPELINE_NO_DYNAMIC_STATE ||
265 (DYNAMIC_STATE < ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT && !sa->uses_dynamic_stride)) {
266 if (sa->vertex_buffers_enabled_mask != sb->vertex_buffers_enabled_mask)
267 return false;
268 /* if we don't have dynamic states, we have to hash the enabled vertex buffer bindings */
269 uint32_t mask_a = sa->vertex_buffers_enabled_mask;
270 uint32_t mask_b = sb->vertex_buffers_enabled_mask;
271 while (mask_a || mask_b) {
272 unsigned idx_a = u_bit_scan(&mask_a);
273 unsigned idx_b = u_bit_scan(&mask_b);
274 if (sa->vertex_strides[idx_a] != sb->vertex_strides[idx_b])
275 return false;
276 }
277 }
278
279 /* each dynamic state extension has its own struct on the pipeline state to compare
280 * if all extensions are supported, none of them are accessed
281 */
282 if (DYNAMIC_STATE == ZINK_PIPELINE_NO_DYNAMIC_STATE) {
283 if (memcmp(&sa->dyn_state1, &sb->dyn_state1, offsetof(struct zink_pipeline_dynamic_state1, depth_stencil_alpha_state)))
284 return false;
285 if (!!sa->dyn_state1.depth_stencil_alpha_state != !!sb->dyn_state1.depth_stencil_alpha_state ||
286 (sa->dyn_state1.depth_stencil_alpha_state &&
287 memcmp(sa->dyn_state1.depth_stencil_alpha_state, sb->dyn_state1.depth_stencil_alpha_state,
288 sizeof(struct zink_depth_stencil_alpha_hw_state))))
289 return false;
290 }
291 if (DYNAMIC_STATE < ZINK_PIPELINE_DYNAMIC_STATE3) {
292 if (DYNAMIC_STATE < ZINK_PIPELINE_DYNAMIC_STATE2) {
293 if (memcmp(&sa->dyn_state2, &sb->dyn_state2, sizeof(sa->dyn_state2)))
294 return false;
295 }
296 if (memcmp(&sa->dyn_state3, &sb->dyn_state3, sizeof(sa->dyn_state3)))
297 return false;
298 } else if (DYNAMIC_STATE != ZINK_PIPELINE_DYNAMIC_STATE2_PCP &&
299 DYNAMIC_STATE != ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT2_PCP &&
300 DYNAMIC_STATE != ZINK_PIPELINE_DYNAMIC_STATE3_PCP &&
301 DYNAMIC_STATE != ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT_PCP &&
302 (STAGE_MASK & BITFIELD_BIT(MESA_SHADER_TESS_EVAL)) &&
303 !(STAGE_MASK & BITFIELD_BIT(MESA_SHADER_TESS_CTRL))) {
304 if (sa->dyn_state2.vertices_per_patch != sb->dyn_state2.vertices_per_patch)
305 return false;
306 }
307 /* optimal keys are the fastest path: only a single uint32_t comparison for all shader module variants */
308 if (STAGE_MASK & STAGE_MASK_OPTIMAL) {
309 if (sa->optimal_key != sb->optimal_key)
310 return false;
311 if (STAGE_MASK & STAGE_MASK_OPTIMAL_SHADOW) {
312 if (sa->shadow != sb->shadow)
313 return false;
314 }
315 } else {
316 if (STAGE_MASK & BITFIELD_BIT(MESA_SHADER_TESS_CTRL)) {
317 if (sa->modules[MESA_SHADER_TESS_CTRL] != sb->modules[MESA_SHADER_TESS_CTRL])
318 return false;
319 }
320 if (STAGE_MASK & BITFIELD_BIT(MESA_SHADER_TESS_EVAL)) {
321 if (sa->modules[MESA_SHADER_TESS_EVAL] != sb->modules[MESA_SHADER_TESS_EVAL])
322 return false;
323 }
324 if (STAGE_MASK & BITFIELD_BIT(MESA_SHADER_GEOMETRY)) {
325 if (sa->modules[MESA_SHADER_GEOMETRY] != sb->modules[MESA_SHADER_GEOMETRY])
326 return false;
327 }
328 if (sa->modules[MESA_SHADER_VERTEX] != sb->modules[MESA_SHADER_VERTEX])
329 return false;
330 if (sa->modules[MESA_SHADER_FRAGMENT] != sb->modules[MESA_SHADER_FRAGMENT])
331 return false;
332 }
333 /* the base pipeline state is a 12 byte comparison */
334 return !memcmp(a, b, offsetof(struct zink_gfx_pipeline_state, hash));
335 }
336
337 /* below is a bunch of code to pick the right equals_gfx_pipeline_state template for runtime */
338 template <zink_pipeline_dynamic_state DYNAMIC_STATE, unsigned STAGE_MASK>
339 static equals_gfx_pipeline_state_func
get_optimal_gfx_pipeline_stage_eq_func(bool optimal_keys,bool shadow_needs_shader_swizzle)340 get_optimal_gfx_pipeline_stage_eq_func(bool optimal_keys, bool shadow_needs_shader_swizzle)
341 {
342 if (optimal_keys) {
343 if (shadow_needs_shader_swizzle)
344 return equals_gfx_pipeline_state<DYNAMIC_STATE, STAGE_MASK | STAGE_MASK_OPTIMAL | STAGE_MASK_OPTIMAL_SHADOW>;
345 return equals_gfx_pipeline_state<DYNAMIC_STATE, STAGE_MASK | STAGE_MASK_OPTIMAL>;
346 }
347 return equals_gfx_pipeline_state<DYNAMIC_STATE, STAGE_MASK>;
348 }
349
350 template <zink_pipeline_dynamic_state DYNAMIC_STATE>
351 static equals_gfx_pipeline_state_func
get_gfx_pipeline_stage_eq_func(struct zink_gfx_program * prog,bool optimal_keys)352 get_gfx_pipeline_stage_eq_func(struct zink_gfx_program *prog, bool optimal_keys)
353 {
354 bool shadow_needs_shader_swizzle = prog->shaders[MESA_SHADER_FRAGMENT]->fs.legacy_shadow_mask > 0;
355 unsigned vertex_stages = prog->stages_present & BITFIELD_MASK(MESA_SHADER_FRAGMENT);
356 if (vertex_stages & BITFIELD_BIT(MESA_SHADER_TESS_CTRL)) {
357 if (prog->shaders[MESA_SHADER_TESS_CTRL]->non_fs.is_generated)
358 vertex_stages &= ~BITFIELD_BIT(MESA_SHADER_TESS_CTRL);
359 }
360 if (vertex_stages & BITFIELD_BIT(MESA_SHADER_TESS_CTRL)) {
361 if (vertex_stages == BITFIELD_MASK(MESA_SHADER_FRAGMENT))
362 /* all stages */
363 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
364 BITFIELD_MASK(MESA_SHADER_COMPUTE)>(optimal_keys, shadow_needs_shader_swizzle);
365 if (vertex_stages == BITFIELD_MASK(MESA_SHADER_GEOMETRY))
366 /* tess only: includes generated tcs too */
367 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
368 BITFIELD_MASK(MESA_SHADER_COMPUTE) & ~BITFIELD_BIT(MESA_SHADER_GEOMETRY)>(optimal_keys, shadow_needs_shader_swizzle);
369 if (vertex_stages == (BITFIELD_BIT(MESA_SHADER_VERTEX) | BITFIELD_BIT(MESA_SHADER_GEOMETRY)))
370 /* geom only */
371 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
372 BITFIELD_BIT(MESA_SHADER_VERTEX) | BITFIELD_BIT(MESA_SHADER_FRAGMENT) | BITFIELD_BIT(MESA_SHADER_GEOMETRY)>(optimal_keys, shadow_needs_shader_swizzle);
373 }
374 if (vertex_stages == (BITFIELD_MASK(MESA_SHADER_FRAGMENT) & ~BITFIELD_BIT(MESA_SHADER_TESS_CTRL)))
375 /* all stages but tcs */
376 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
377 BITFIELD_MASK(MESA_SHADER_COMPUTE) & ~BITFIELD_BIT(MESA_SHADER_TESS_CTRL)>(optimal_keys, shadow_needs_shader_swizzle);
378 if (vertex_stages == (BITFIELD_MASK(MESA_SHADER_GEOMETRY) & ~BITFIELD_BIT(MESA_SHADER_TESS_CTRL)))
379 /* tess only: generated tcs */
380 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
381 BITFIELD_MASK(MESA_SHADER_COMPUTE) & ~(BITFIELD_BIT(MESA_SHADER_GEOMETRY) | BITFIELD_BIT(MESA_SHADER_TESS_CTRL))>(optimal_keys, shadow_needs_shader_swizzle);
382 if (vertex_stages == (BITFIELD_BIT(MESA_SHADER_VERTEX) | BITFIELD_BIT(MESA_SHADER_GEOMETRY)))
383 /* geom only */
384 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
385 BITFIELD_BIT(MESA_SHADER_VERTEX) | BITFIELD_BIT(MESA_SHADER_FRAGMENT) | BITFIELD_BIT(MESA_SHADER_GEOMETRY)>(optimal_keys, shadow_needs_shader_swizzle);
386 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
387 BITFIELD_BIT(MESA_SHADER_VERTEX) | BITFIELD_BIT(MESA_SHADER_FRAGMENT)>(optimal_keys, shadow_needs_shader_swizzle);
388 }
389
390 equals_gfx_pipeline_state_func
zink_get_gfx_pipeline_eq_func(struct zink_screen * screen,struct zink_gfx_program * prog)391 zink_get_gfx_pipeline_eq_func(struct zink_screen *screen, struct zink_gfx_program *prog)
392 {
393 if (screen->info.have_EXT_extended_dynamic_state) {
394 if (screen->info.have_EXT_extended_dynamic_state2) {
395 if (screen->info.have_EXT_extended_dynamic_state3) {
396 if (screen->info.have_EXT_vertex_input_dynamic_state) {
397 if (screen->info.dynamic_state2_feats.extendedDynamicState2PatchControlPoints)
398 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT_PCP>(prog, screen->optimal_keys);
399 else
400 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT>(prog, screen->optimal_keys);
401 } else {
402 if (screen->info.dynamic_state2_feats.extendedDynamicState2PatchControlPoints)
403 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_STATE3_PCP>(prog, screen->optimal_keys);
404 else
405 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_STATE3>(prog, screen->optimal_keys);
406 }
407 }
408 if (screen->info.have_EXT_vertex_input_dynamic_state) {
409 if (screen->info.dynamic_state2_feats.extendedDynamicState2PatchControlPoints)
410 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT2_PCP>(prog, screen->optimal_keys);
411 else
412 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT2>(prog, screen->optimal_keys);
413 } else {
414 if (screen->info.dynamic_state2_feats.extendedDynamicState2PatchControlPoints)
415 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_STATE2_PCP>(prog, screen->optimal_keys);
416 else
417 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_STATE2>(prog, screen->optimal_keys);
418 }
419 }
420 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_STATE>(prog, screen->optimal_keys);
421 }
422 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_NO_DYNAMIC_STATE>(prog, screen->optimal_keys);
423 }
424