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 <[email protected]>
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 union zink_shader_key_optimal sanitized = {};
132 sanitized.val = zink_sanitize_optimal_key(ctx->gfx_stages, ctx->gfx_pipeline_state.shader_keys_optimal.key.val);
133 assert(opt->val == sanitized.val);
134 assert(state->optimal_key == sanitized.val);
135 }
136 /* recalc vertex state if missing optimal extensions */
137 if (DYNAMIC_STATE != ZINK_DYNAMIC_VERTEX_INPUT2 && DYNAMIC_STATE != ZINK_DYNAMIC_VERTEX_INPUT && ctx->vertex_state_changed) {
138 if (state->pipeline)
139 state->final_hash ^= state->vertex_hash;
140 /* even if dynamic stride is available, it may not be usable with the current pipeline */
141 if (DYNAMIC_STATE != ZINK_NO_DYNAMIC_STATE)
142 uses_dynamic_stride = check_vertex_strides(ctx);
143 if (!uses_dynamic_stride) {
144 uint32_t hash = 0;
145 /* if we don't have dynamic states, we have to hash the enabled vertex buffer bindings */
146 uint32_t vertex_buffers_enabled_mask = state->vertex_buffers_enabled_mask;
147 hash = XXH32(&vertex_buffers_enabled_mask, sizeof(uint32_t), hash);
148
149 for (unsigned i = 0; i < state->element_state->num_bindings; i++) {
150 const unsigned buffer_id = ctx->element_state->hw_state.binding_map[i];
151 struct pipe_vertex_buffer *vb = ctx->vertex_buffers + buffer_id;
152 state->vertex_strides[buffer_id] = vb->buffer.resource ? state->element_state->b.strides[i] : 0;
153 hash = XXH32(&state->vertex_strides[buffer_id], sizeof(uint32_t), hash);
154 }
155 state->vertex_hash = hash ^ state->element_state->hash;
156 } else
157 state->vertex_hash = state->element_state->hash;
158 state->final_hash ^= state->vertex_hash;
159 }
160 state->modules_changed = false;
161 state->uses_dynamic_stride = uses_dynamic_stride;
162 state->idx = idx;
163 ctx->vertex_state_changed = false;
164
165 const int rp_idx = state->render_pass ? 1 : 0;
166 /* shortcut for reusing previous pipeline across program changes */
167 if (DYNAMIC_STATE == ZINK_DYNAMIC_VERTEX_INPUT || DYNAMIC_STATE == ZINK_DYNAMIC_VERTEX_INPUT2) {
168 if (prog->last_finalized_hash[rp_idx][idx] == state->final_hash &&
169 !prog->inline_variants && likely(prog->last_pipeline[rp_idx][idx]) &&
170 /* this data is too big to compare in the fast-path */
171 likely(!prog->shaders[MESA_SHADER_FRAGMENT]->fs.legacy_shadow_mask)) {
172 state->pipeline = prog->last_pipeline[rp_idx][idx]->pipeline;
173 return state->pipeline;
174 }
175 }
176 entry = _mesa_hash_table_search_pre_hashed(&prog->pipelines[rp_idx][idx], state->final_hash, state);
177
178 if (!entry) {
179 /* always wait on async precompile/cache fence */
180 util_queue_fence_wait(&prog->base.cache_fence);
181 struct zink_gfx_pipeline_cache_entry *pc_entry = CALLOC_STRUCT(zink_gfx_pipeline_cache_entry);
182 if (!pc_entry)
183 return VK_NULL_HANDLE;
184 /* cache entries must have all state needed to construct pipelines
185 * TODO: maybe optimize this since all these values aren't actually needed
186 */
187 memcpy(&pc_entry->state, state, sizeof(*state));
188 pc_entry->state.rendering_info.pColorAttachmentFormats = pc_entry->state.rendering_formats;
189 pc_entry->prog = prog;
190 /* init the optimized background compile fence */
191 util_queue_fence_init(&pc_entry->fence);
192 entry = _mesa_hash_table_insert_pre_hashed(&prog->pipelines[rp_idx][idx], state->final_hash, pc_entry, pc_entry);
193 if (prog->base.uses_shobj && !prog->is_separable) {
194 memcpy(pc_entry->shobjs, prog->objs, sizeof(prog->objs));
195 zink_gfx_program_compile_queue(ctx, pc_entry);
196 } else if (HAVE_LIB && zink_can_use_pipeline_libs(ctx)) {
197 /* this is the graphics pipeline library path: find/construct all partial pipelines */
198 simple_mtx_lock(&prog->libs->lock);
199 struct set_entry *he = _mesa_set_search(&prog->libs->libs, &ctx->gfx_pipeline_state.optimal_key);
200 struct zink_gfx_library_key *gkey;
201 if (he) {
202 gkey = (struct zink_gfx_library_key *)he->key;
203 } else {
204 assert(!prog->is_separable);
205 gkey = zink_create_pipeline_lib(screen, prog, &ctx->gfx_pipeline_state);
206 }
207 simple_mtx_unlock(&prog->libs->lock);
208 struct zink_gfx_input_key *ikey = DYNAMIC_STATE == ZINK_DYNAMIC_VERTEX_INPUT ?
209 zink_find_or_create_input_dynamic(ctx, vkmode) :
210 zink_find_or_create_input(ctx, vkmode);
211 struct zink_gfx_output_key *okey = DYNAMIC_STATE >= ZINK_DYNAMIC_STATE3 && screen->have_full_ds3 ?
212 zink_find_or_create_output_ds3(ctx) :
213 zink_find_or_create_output(ctx);
214 /* partial pipelines are stored to the cache entry for async optimized pipeline compiles */
215 pc_entry->gpl.ikey = ikey;
216 pc_entry->gpl.gkey = gkey;
217 pc_entry->gpl.okey = okey;
218 /* try to hit optimized compile cache first if possible */
219 if (!prog->is_separable)
220 pc_entry->pipeline = zink_create_gfx_pipeline_combined(screen, prog, ikey->pipeline, &gkey->pipeline, 1, okey->pipeline, true, true);
221 if (!pc_entry->pipeline) {
222 /* create the non-optimized pipeline first using fast-linking to avoid stuttering */
223 pc_entry->pipeline = zink_create_gfx_pipeline_combined(screen, prog, ikey->pipeline, &gkey->pipeline, 1, okey->pipeline, false, false);
224 if (!prog->is_separable)
225 /* trigger async optimized pipeline compile if this was the fast-linked unoptimized pipeline */
226 zink_gfx_program_compile_queue(ctx, pc_entry);
227 }
228 } else {
229 /* optimize by default only when expecting precompiles in order to reduce stuttering */
230 if (DYNAMIC_STATE != ZINK_DYNAMIC_VERTEX_INPUT2 && DYNAMIC_STATE != ZINK_DYNAMIC_VERTEX_INPUT)
231 pc_entry->pipeline = zink_create_gfx_pipeline(screen, prog, prog->objs, state, state->element_state->binding_map, vkmode, !HAVE_LIB);
232 else
233 pc_entry->pipeline = zink_create_gfx_pipeline(screen, prog, prog->objs, state, NULL, vkmode, !HAVE_LIB);
234 if (HAVE_LIB && !prog->is_separable)
235 /* trigger async optimized pipeline compile if this was an unoptimized pipeline */
236 zink_gfx_program_compile_queue(ctx, pc_entry);
237 }
238 if (pc_entry->pipeline == VK_NULL_HANDLE)
239 return VK_NULL_HANDLE;
240
241 zink_screen_update_pipeline_cache(screen, &prog->base, false);
242 }
243
244 struct zink_gfx_pipeline_cache_entry *cache_entry = (struct zink_gfx_pipeline_cache_entry *)entry->data;
245 state->pipeline = cache_entry->pipeline;
246 /* update states for fastpath */
247 if (DYNAMIC_STATE >= ZINK_DYNAMIC_VERTEX_INPUT) {
248 prog->last_finalized_hash[rp_idx][idx] = state->final_hash;
249 prog->last_pipeline[rp_idx][idx] = cache_entry;
250 }
251 return state->pipeline;
252 }
253
254 /* runtime-optimized pipeline state comparisons */
255 template <zink_pipeline_dynamic_state DYNAMIC_STATE, unsigned STAGE_MASK>
256 static bool
equals_gfx_pipeline_state(const void * a,const void * b)257 equals_gfx_pipeline_state(const void *a, const void *b)
258 {
259 const struct zink_gfx_pipeline_state *sa = (const struct zink_gfx_pipeline_state *)a;
260 const struct zink_gfx_pipeline_state *sb = (const struct zink_gfx_pipeline_state *)b;
261 if (DYNAMIC_STATE < ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT) {
262 if (sa->uses_dynamic_stride != sb->uses_dynamic_stride)
263 return false;
264 }
265 if (DYNAMIC_STATE == ZINK_PIPELINE_NO_DYNAMIC_STATE ||
266 (DYNAMIC_STATE < ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT && !sa->uses_dynamic_stride)) {
267 if (sa->vertex_buffers_enabled_mask != sb->vertex_buffers_enabled_mask)
268 return false;
269 /* if we don't have dynamic states, we have to hash the enabled vertex buffer bindings */
270 uint32_t mask_a = sa->vertex_buffers_enabled_mask;
271 uint32_t mask_b = sb->vertex_buffers_enabled_mask;
272 while (mask_a || mask_b) {
273 unsigned idx_a = u_bit_scan(&mask_a);
274 unsigned idx_b = u_bit_scan(&mask_b);
275 if (sa->vertex_strides[idx_a] != sb->vertex_strides[idx_b])
276 return false;
277 }
278 }
279
280 /* each dynamic state extension has its own struct on the pipeline state to compare
281 * if all extensions are supported, none of them are accessed
282 */
283 if (DYNAMIC_STATE == ZINK_PIPELINE_NO_DYNAMIC_STATE) {
284 if (memcmp(&sa->dyn_state1, &sb->dyn_state1, offsetof(struct zink_pipeline_dynamic_state1, depth_stencil_alpha_state)))
285 return false;
286 if (!!sa->dyn_state1.depth_stencil_alpha_state != !!sb->dyn_state1.depth_stencil_alpha_state ||
287 (sa->dyn_state1.depth_stencil_alpha_state &&
288 memcmp(sa->dyn_state1.depth_stencil_alpha_state, sb->dyn_state1.depth_stencil_alpha_state,
289 sizeof(struct zink_depth_stencil_alpha_hw_state))))
290 return false;
291 }
292 if (DYNAMIC_STATE < ZINK_PIPELINE_DYNAMIC_STATE3) {
293 if (DYNAMIC_STATE < ZINK_PIPELINE_DYNAMIC_STATE2) {
294 if (memcmp(&sa->dyn_state2, &sb->dyn_state2, sizeof(sa->dyn_state2)))
295 return false;
296 }
297 if (memcmp(&sa->dyn_state3, &sb->dyn_state3, sizeof(sa->dyn_state3)))
298 return false;
299 } else if (DYNAMIC_STATE != ZINK_PIPELINE_DYNAMIC_STATE2_PCP &&
300 DYNAMIC_STATE != ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT2_PCP &&
301 DYNAMIC_STATE != ZINK_PIPELINE_DYNAMIC_STATE3_PCP &&
302 DYNAMIC_STATE != ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT_PCP &&
303 (STAGE_MASK & BITFIELD_BIT(MESA_SHADER_TESS_EVAL)) &&
304 !(STAGE_MASK & BITFIELD_BIT(MESA_SHADER_TESS_CTRL))) {
305 if (sa->dyn_state2.vertices_per_patch != sb->dyn_state2.vertices_per_patch)
306 return false;
307 }
308 /* optimal keys are the fastest path: only a single uint32_t comparison for all shader module variants */
309 if (STAGE_MASK & STAGE_MASK_OPTIMAL) {
310 if (sa->optimal_key != sb->optimal_key)
311 return false;
312 if (STAGE_MASK & STAGE_MASK_OPTIMAL_SHADOW) {
313 if (sa->shadow != sb->shadow)
314 return false;
315 }
316 } else {
317 if (STAGE_MASK & BITFIELD_BIT(MESA_SHADER_TESS_CTRL)) {
318 if (sa->modules[MESA_SHADER_TESS_CTRL] != sb->modules[MESA_SHADER_TESS_CTRL])
319 return false;
320 }
321 if (STAGE_MASK & BITFIELD_BIT(MESA_SHADER_TESS_EVAL)) {
322 if (sa->modules[MESA_SHADER_TESS_EVAL] != sb->modules[MESA_SHADER_TESS_EVAL])
323 return false;
324 }
325 if (STAGE_MASK & BITFIELD_BIT(MESA_SHADER_GEOMETRY)) {
326 if (sa->modules[MESA_SHADER_GEOMETRY] != sb->modules[MESA_SHADER_GEOMETRY])
327 return false;
328 }
329 if (sa->modules[MESA_SHADER_VERTEX] != sb->modules[MESA_SHADER_VERTEX])
330 return false;
331 if (sa->modules[MESA_SHADER_FRAGMENT] != sb->modules[MESA_SHADER_FRAGMENT])
332 return false;
333 }
334 /* the base pipeline state is a 12 byte comparison */
335 return !memcmp(a, b, offsetof(struct zink_gfx_pipeline_state, hash));
336 }
337
338 /* below is a bunch of code to pick the right equals_gfx_pipeline_state template for runtime */
339 template <zink_pipeline_dynamic_state DYNAMIC_STATE, unsigned STAGE_MASK>
340 static equals_gfx_pipeline_state_func
get_optimal_gfx_pipeline_stage_eq_func(bool optimal_keys,bool shadow_needs_shader_swizzle)341 get_optimal_gfx_pipeline_stage_eq_func(bool optimal_keys, bool shadow_needs_shader_swizzle)
342 {
343 if (optimal_keys) {
344 if (shadow_needs_shader_swizzle)
345 return equals_gfx_pipeline_state<DYNAMIC_STATE, STAGE_MASK | STAGE_MASK_OPTIMAL | STAGE_MASK_OPTIMAL_SHADOW>;
346 return equals_gfx_pipeline_state<DYNAMIC_STATE, STAGE_MASK | STAGE_MASK_OPTIMAL>;
347 }
348 return equals_gfx_pipeline_state<DYNAMIC_STATE, STAGE_MASK>;
349 }
350
351 template <zink_pipeline_dynamic_state DYNAMIC_STATE>
352 static equals_gfx_pipeline_state_func
get_gfx_pipeline_stage_eq_func(struct zink_gfx_program * prog,bool optimal_keys)353 get_gfx_pipeline_stage_eq_func(struct zink_gfx_program *prog, bool optimal_keys)
354 {
355 bool shadow_needs_shader_swizzle = prog->shaders[MESA_SHADER_FRAGMENT]->fs.legacy_shadow_mask > 0;
356 unsigned vertex_stages = prog->stages_present & BITFIELD_MASK(MESA_SHADER_FRAGMENT);
357 if (vertex_stages & BITFIELD_BIT(MESA_SHADER_TESS_CTRL)) {
358 if (prog->shaders[MESA_SHADER_TESS_CTRL]->non_fs.is_generated)
359 vertex_stages &= ~BITFIELD_BIT(MESA_SHADER_TESS_CTRL);
360 }
361 if (vertex_stages & BITFIELD_BIT(MESA_SHADER_TESS_CTRL)) {
362 if (vertex_stages == BITFIELD_MASK(MESA_SHADER_FRAGMENT))
363 /* all stages */
364 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
365 BITFIELD_MASK(MESA_SHADER_COMPUTE)>(optimal_keys, shadow_needs_shader_swizzle);
366 if (vertex_stages == BITFIELD_MASK(MESA_SHADER_GEOMETRY))
367 /* tess only: includes generated tcs too */
368 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
369 BITFIELD_MASK(MESA_SHADER_COMPUTE) & ~BITFIELD_BIT(MESA_SHADER_GEOMETRY)>(optimal_keys, shadow_needs_shader_swizzle);
370 if (vertex_stages == (BITFIELD_BIT(MESA_SHADER_VERTEX) | BITFIELD_BIT(MESA_SHADER_GEOMETRY)))
371 /* geom only */
372 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
373 BITFIELD_BIT(MESA_SHADER_VERTEX) | BITFIELD_BIT(MESA_SHADER_FRAGMENT) | BITFIELD_BIT(MESA_SHADER_GEOMETRY)>(optimal_keys, shadow_needs_shader_swizzle);
374 }
375 if (vertex_stages == (BITFIELD_MASK(MESA_SHADER_FRAGMENT) & ~BITFIELD_BIT(MESA_SHADER_TESS_CTRL)))
376 /* all stages but tcs */
377 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
378 BITFIELD_MASK(MESA_SHADER_COMPUTE) & ~BITFIELD_BIT(MESA_SHADER_TESS_CTRL)>(optimal_keys, shadow_needs_shader_swizzle);
379 if (vertex_stages == (BITFIELD_MASK(MESA_SHADER_GEOMETRY) & ~BITFIELD_BIT(MESA_SHADER_TESS_CTRL)))
380 /* tess only: generated tcs */
381 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
382 BITFIELD_MASK(MESA_SHADER_COMPUTE) & ~(BITFIELD_BIT(MESA_SHADER_GEOMETRY) | BITFIELD_BIT(MESA_SHADER_TESS_CTRL))>(optimal_keys, shadow_needs_shader_swizzle);
383 if (vertex_stages == (BITFIELD_BIT(MESA_SHADER_VERTEX) | BITFIELD_BIT(MESA_SHADER_GEOMETRY)))
384 /* geom only */
385 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
386 BITFIELD_BIT(MESA_SHADER_VERTEX) | BITFIELD_BIT(MESA_SHADER_FRAGMENT) | BITFIELD_BIT(MESA_SHADER_GEOMETRY)>(optimal_keys, shadow_needs_shader_swizzle);
387 return get_optimal_gfx_pipeline_stage_eq_func<DYNAMIC_STATE,
388 BITFIELD_BIT(MESA_SHADER_VERTEX) | BITFIELD_BIT(MESA_SHADER_FRAGMENT)>(optimal_keys, shadow_needs_shader_swizzle);
389 }
390
391 equals_gfx_pipeline_state_func
zink_get_gfx_pipeline_eq_func(struct zink_screen * screen,struct zink_gfx_program * prog)392 zink_get_gfx_pipeline_eq_func(struct zink_screen *screen, struct zink_gfx_program *prog)
393 {
394 if (screen->info.have_EXT_extended_dynamic_state) {
395 if (screen->info.have_EXT_extended_dynamic_state2) {
396 if (screen->info.have_EXT_extended_dynamic_state3) {
397 if (screen->info.have_EXT_vertex_input_dynamic_state) {
398 if (screen->info.dynamic_state2_feats.extendedDynamicState2PatchControlPoints)
399 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT_PCP>(prog, screen->optimal_keys);
400 else
401 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT>(prog, screen->optimal_keys);
402 } else {
403 if (screen->info.dynamic_state2_feats.extendedDynamicState2PatchControlPoints)
404 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_STATE3_PCP>(prog, screen->optimal_keys);
405 else
406 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_STATE3>(prog, screen->optimal_keys);
407 }
408 }
409 if (screen->info.have_EXT_vertex_input_dynamic_state) {
410 if (screen->info.dynamic_state2_feats.extendedDynamicState2PatchControlPoints)
411 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT2_PCP>(prog, screen->optimal_keys);
412 else
413 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_VERTEX_INPUT2>(prog, screen->optimal_keys);
414 } else {
415 if (screen->info.dynamic_state2_feats.extendedDynamicState2PatchControlPoints)
416 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_STATE2_PCP>(prog, screen->optimal_keys);
417 else
418 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_STATE2>(prog, screen->optimal_keys);
419 }
420 }
421 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_DYNAMIC_STATE>(prog, screen->optimal_keys);
422 }
423 return get_gfx_pipeline_stage_eq_func<ZINK_PIPELINE_NO_DYNAMIC_STATE>(prog, screen->optimal_keys);
424 }
425