1 /*
2 * Copyright (c) 2014-2024 Broadcom. All Rights Reserved.
3 * The term “Broadcom” refers to Broadcom Inc.
4 * and/or its subsidiaries.
5 * SPDX-License-Identifier: MIT
6 */
7
8 #include "util/u_inlines.h"
9 #include "util/u_memory.h"
10 #include "util/u_bitmask.h"
11 #include "util/u_simple_shaders.h"
12 #include "tgsi/tgsi_point_sprite.h"
13 #include "tgsi/tgsi_dynamic_indexing.h"
14 #include "tgsi/tgsi_vpos.h"
15 #include "tgsi/tgsi_dump.h"
16
17 #include "svga_context.h"
18 #include "svga_shader.h"
19 #include "svga_tgsi.h"
20
21
22 /**
23 * Bind a new GS. This updates the derived current gs state, not the
24 * user-specified GS state.
25 */
26 static void
bind_gs_state(struct svga_context * svga,struct svga_geometry_shader * gs)27 bind_gs_state(struct svga_context *svga,
28 struct svga_geometry_shader *gs)
29 {
30 svga->curr.gs = gs;
31 svga->dirty |= SVGA_NEW_GS;
32 }
33
34
35 static void
insert_at_head(struct svga_shader * head,struct svga_shader * shader)36 insert_at_head(struct svga_shader *head, struct svga_shader *shader)
37 {
38 shader->parent = head;
39 shader->next = head->next;
40 head->next = shader;
41 }
42
43
44 /**
45 * Bind shader
46 */
47 static void
bind_shader(struct svga_context * svga,const enum pipe_shader_type shader_type,struct svga_shader * shader)48 bind_shader(struct svga_context *svga,
49 const enum pipe_shader_type shader_type,
50 struct svga_shader *shader)
51 {
52 switch (shader_type) {
53 case PIPE_SHADER_VERTEX:
54 svga->pipe.bind_vs_state(&svga->pipe, shader);
55 break;
56 case PIPE_SHADER_FRAGMENT:
57 /**
58 * Avoid pipe->bind_fs_state call because it goes through aapoint
59 * layer. We loose linked list of all transformed shaders if aapoint
60 * is used.
61 */
62 svga_bind_fs_state(&svga->pipe, shader);
63 break;
64 case PIPE_SHADER_GEOMETRY:
65 svga->pipe.bind_gs_state(&svga->pipe, shader);
66 break;
67 case PIPE_SHADER_TESS_CTRL:
68 svga->pipe.bind_tcs_state(&svga->pipe, shader);
69 break;
70 case PIPE_SHADER_TESS_EVAL:
71 svga->pipe.bind_tes_state(&svga->pipe, shader);
72 break;
73 default:
74 return;
75 }
76 }
77
78
79
80 /**
81 * Create shader
82 */
83 static void *
create_shader(struct svga_context * svga,const enum pipe_shader_type shader_type,struct pipe_shader_state * state)84 create_shader(struct svga_context *svga,
85 const enum pipe_shader_type shader_type,
86 struct pipe_shader_state *state)
87 {
88 switch (shader_type) {
89 case PIPE_SHADER_VERTEX:
90 return svga->pipe.create_vs_state(&svga->pipe, state);
91 case PIPE_SHADER_FRAGMENT:
92 /**
93 * Avoid pipe->create_fs_state call because it goes through aapoint
94 * layer. We loose linked list of all transformed shaders if aapoint
95 * is used.
96 */
97 return svga_create_fs_state(&svga->pipe, state);
98 case PIPE_SHADER_GEOMETRY:
99 return svga->pipe.create_gs_state(&svga->pipe, state);
100 case PIPE_SHADER_TESS_CTRL:
101 return svga->pipe.create_tcs_state(&svga->pipe, state);
102 case PIPE_SHADER_TESS_EVAL:
103 return svga->pipe.create_tes_state(&svga->pipe, state);
104 default:
105 return NULL;
106 }
107 }
108
109
110 static void
write_vpos(struct svga_context * svga,struct svga_shader * shader)111 write_vpos(struct svga_context *svga,
112 struct svga_shader *shader)
113 {
114 struct svga_token_key key;
115 bool use_existing = false;
116 struct svga_shader *transform_shader;
117 const struct tgsi_shader_info *info = &shader->tgsi_info;
118
119 /* Create a token key */
120 memset(&key, 0, sizeof key);
121 key.vs.write_position = 1;
122
123 if (shader->next) {
124 transform_shader = svga_search_shader_token_key(shader->next, &key);
125 if (transform_shader) {
126 use_existing = true;
127 }
128 }
129
130 if (!use_existing) {
131 struct pipe_shader_state state = {0};
132 struct tgsi_token *new_tokens = NULL;
133
134 new_tokens = tgsi_write_vpos(shader->tokens,
135 info->immediate_count);
136 if (!new_tokens)
137 return;
138
139 pipe_shader_state_from_tgsi(&state, new_tokens);
140
141 transform_shader = create_shader(svga, info->processor, &state);
142 insert_at_head(shader, transform_shader);
143 FREE(new_tokens);
144 }
145 transform_shader->token_key = key;
146 bind_shader(svga, info->processor, transform_shader);
147 }
148
149
150 /**
151 * transform_dynamic_indexing searches shader variant list to see if
152 * we have transformed shader for dynamic indexing and reuse/bind it. If we
153 * don't have transformed shader, then it will create new shader from which
154 * dynamic indexing will be removed. It will also be added to the shader
155 * variant list and this new shader will be bind to current svga state.
156 */
157 static void
transform_dynamic_indexing(struct svga_context * svga,struct svga_shader * shader)158 transform_dynamic_indexing(struct svga_context *svga,
159 struct svga_shader *shader)
160 {
161 struct svga_token_key key;
162 bool use_existing = false;
163 struct svga_shader *transform_shader;
164 const struct tgsi_shader_info *info = &shader->tgsi_info;
165
166 /* Create a token key */
167 memset(&key, 0, sizeof key);
168 key.dynamic_indexing = 1;
169
170 if (shader->next) {
171 transform_shader = svga_search_shader_token_key(shader->next, &key);
172 if (transform_shader) {
173 use_existing = true;
174 }
175 }
176
177 struct tgsi_token *new_tokens = NULL;
178
179 if (!use_existing) {
180 struct pipe_shader_state state = {0};
181 new_tokens = tgsi_remove_dynamic_indexing(shader->tokens,
182 info->const_buffers_declared,
183 info->samplers_declared,
184 info->immediate_count);
185 if (!new_tokens)
186 return;
187
188 pipe_shader_state_from_tgsi(&state, new_tokens);
189
190 transform_shader = create_shader(svga, info->processor, &state);
191 insert_at_head(shader, transform_shader);
192 }
193 transform_shader->token_key = key;
194 bind_shader(svga, info->processor, transform_shader);
195 if (new_tokens)
196 FREE(new_tokens);
197 }
198
199
200 /**
201 * emulate_point_sprite searches the shader variants list to see it there is
202 * a shader variant with a token string that matches the emulation
203 * requirement. It there isn't, then it will use a tgsi utility
204 * tgsi_add_point_sprite to transform the original token string to support
205 * point sprite. A new geometry shader state will be created with the
206 * transformed token string and added to the shader variants list of the
207 * original geometry shader. The new geometry shader state will then be
208 * bound as the current geometry shader.
209 */
210 static struct svga_shader *
emulate_point_sprite(struct svga_context * svga,struct svga_shader * shader,const struct tgsi_token * tokens)211 emulate_point_sprite(struct svga_context *svga,
212 struct svga_shader *shader,
213 const struct tgsi_token *tokens)
214 {
215 struct svga_token_key key;
216 struct tgsi_token *new_tokens;
217 const struct tgsi_token *orig_tokens;
218 struct svga_geometry_shader *orig_gs = (struct svga_geometry_shader *)shader;
219 struct svga_geometry_shader *gs = NULL;
220 struct pipe_shader_state templ = {0};
221 struct svga_stream_output *streamout = NULL;
222 int pos_out_index = -1;
223 int aa_point_coord_index = -1;
224 struct pipe_screen *screen = svga->pipe.screen;
225 bool has_texcoord_semantic =
226 screen->get_param(screen, PIPE_CAP_TGSI_TEXCOORD);
227
228 assert(tokens != NULL);
229
230 orig_tokens = tokens;
231
232 /* Create a token key */
233 memset(&key, 0, sizeof key);
234 key.gs.writes_psize = 1;
235 key.gs.sprite_coord_enable = svga->curr.rast->templ.sprite_coord_enable;
236 if (has_texcoord_semantic)
237 key.gs.sprite_coord_enable |= 0x1; /* For TGSI_SEMANTIC_PCOORD */
238
239 key.gs.sprite_origin_upper_left =
240 !(svga->curr.rast->templ.sprite_coord_mode == PIPE_SPRITE_COORD_LOWER_LEFT);
241
242 key.gs.aa_point = svga->curr.rast->templ.point_smooth;
243
244 if (orig_gs) {
245
246 /* Check if the original geometry shader has stream output and
247 * if position is one of the outputs.
248 */
249 streamout = orig_gs->base.stream_output;
250 if (streamout) {
251 pos_out_index = streamout->pos_out_index;
252 key.gs.point_pos_stream_out = pos_out_index != -1;
253 }
254
255 /* Search the shader lists to see if there is a variant that matches
256 * this token key.
257 */
258 gs = (struct svga_geometry_shader *)
259 svga_search_shader_token_key(&orig_gs->base, &key);
260 }
261
262 /* If there isn't, then call the tgsi utility tgsi_add_point_sprite
263 * to transform the original tokens to support point sprite.
264 * Flip the sprite origin as SVGA3D device only supports an
265 * upper-left origin.
266 */
267 if (!gs) {
268 new_tokens = tgsi_add_point_sprite(orig_tokens,
269 key.gs.sprite_coord_enable,
270 key.gs.sprite_origin_upper_left,
271 key.gs.point_pos_stream_out,
272 has_texcoord_semantic,
273 key.gs.aa_point ?
274 &aa_point_coord_index : NULL);
275
276 if (!new_tokens) {
277 /* if no new tokens are generated for whatever reason, just return */
278 return NULL;
279 }
280
281 if (0) {
282 debug_printf("Before tgsi_add_point_sprite ---------------\n");
283 tgsi_dump(orig_tokens, 0);
284 debug_printf("After tgsi_add_point_sprite --------------\n");
285 tgsi_dump(new_tokens, 0);
286 }
287
288 pipe_shader_state_from_tgsi(&templ, new_tokens);
289 templ.stream_output.num_outputs = 0;
290
291 if (streamout) {
292 templ.stream_output = streamout->info;
293 /* The tgsi_add_point_sprite utility adds an extra output
294 * for the original point position for stream output purpose.
295 * We need to replace the position output register index in the
296 * stream output declaration with the new register index.
297 */
298 if (pos_out_index != -1) {
299 assert(orig_gs != NULL);
300 templ.stream_output.output[pos_out_index].register_index =
301 orig_gs->base.tgsi_info.num_outputs;
302 }
303 }
304
305 /* Create a new geometry shader state with the new tokens */
306 gs = svga->pipe.create_gs_state(&svga->pipe, &templ);
307
308 /* Don't need the token string anymore. There is a local copy
309 * in the shader state.
310 */
311 FREE(new_tokens);
312
313 if (!gs) {
314 return NULL;
315 }
316
317 gs->wide_point = true;
318 gs->aa_point_coord_index = aa_point_coord_index;
319 gs->base.token_key = key;
320 gs->base.parent = &orig_gs->base;
321 gs->base.next = NULL;
322
323 /* Add the new geometry shader to the head of the shader list
324 * pointed to by the original geometry shader.
325 */
326 if (orig_gs) {
327 gs->base.next = orig_gs->base.next;
328 orig_gs->base.next = &gs->base;
329 }
330 }
331
332 /* Bind the new geometry shader state */
333 bind_gs_state(svga, gs);
334
335 return &gs->base;
336 }
337
338 /**
339 * Generate a geometry shader that emits a wide point by drawing a quad.
340 * This function first creates a passthrough geometry shader and then
341 * calls emulate_point_sprite() to transform the geometry shader to
342 * support point sprite.
343 */
344 static struct svga_shader *
add_point_sprite_shader(struct svga_context * svga)345 add_point_sprite_shader(struct svga_context *svga)
346 {
347 struct svga_vertex_shader *vs = svga->curr.vs;
348 struct svga_geometry_shader *orig_gs = vs->gs;
349 struct svga_geometry_shader *new_gs;
350 const struct tgsi_token *tokens;
351
352 if (orig_gs == NULL) {
353
354 /* If this is the first time adding a geometry shader to this
355 * vertex shader to support point sprite, then create
356 * a passthrough geometry shader first.
357 */
358 orig_gs = (struct svga_geometry_shader *)
359 util_make_geometry_passthrough_shader(
360 &svga->pipe, vs->base.tgsi_info.num_outputs,
361 vs->base.tgsi_info.output_semantic_name,
362 vs->base.tgsi_info.output_semantic_index);
363
364 if (!orig_gs)
365 return NULL;
366 }
367 else {
368 if (orig_gs->base.parent)
369 orig_gs = (struct svga_geometry_shader *)orig_gs->base.parent;
370 }
371 tokens = orig_gs->base.tokens;
372
373 /* Call emulate_point_sprite to find or create a transformed
374 * geometry shader for supporting point sprite.
375 */
376 new_gs = (struct svga_geometry_shader *)
377 emulate_point_sprite(svga, &orig_gs->base, tokens);
378
379 /* If this is the first time creating a geometry shader to
380 * support vertex point size, then add the new geometry shader
381 * to the vertex shader.
382 */
383 if (vs->gs == NULL) {
384 vs->gs = new_gs;
385 }
386
387 return &new_gs->base;
388 }
389
390
391 static bool
has_dynamic_indexing(const struct tgsi_shader_info * info)392 has_dynamic_indexing(const struct tgsi_shader_info *info)
393 {
394 return (info->dim_indirect_files & (1u << TGSI_FILE_CONSTANT)) ||
395 (info->indirect_files & (1u << TGSI_FILE_SAMPLER));
396 }
397
398
399 /* update_tgsi_transform provides a hook to transform a shader if needed.
400 */
401 static enum pipe_error
update_tgsi_transform(struct svga_context * svga,uint64_t dirty)402 update_tgsi_transform(struct svga_context *svga, uint64_t dirty)
403 {
404 struct svga_geometry_shader *gs = svga->curr.user_gs; /* current gs */
405 struct svga_vertex_shader *vs = svga->curr.vs; /* currently bound vs */
406 struct svga_fragment_shader *fs = svga->curr.fs; /* currently bound fs */
407 struct svga_tcs_shader *tcs = svga->curr.tcs; /* currently bound tcs */
408 struct svga_tes_shader *tes = svga->curr.tes; /* currently bound tes */
409 struct svga_shader *orig_gs; /* original gs */
410 struct svga_shader *new_gs; /* new gs */
411
412 assert(svga_have_vgpu10(svga));
413
414 if (vs->base.tgsi_info.num_outputs == 0) {
415 write_vpos(svga, &vs->base);
416 }
417
418 if (vs && has_dynamic_indexing(&vs->base.tgsi_info)) {
419 transform_dynamic_indexing(svga, &vs->base);
420 }
421 if (fs && has_dynamic_indexing(&fs->base.tgsi_info)) {
422 transform_dynamic_indexing(svga, &fs->base);
423 }
424 if (gs && has_dynamic_indexing(&gs->base.tgsi_info)) {
425 transform_dynamic_indexing(svga, &gs->base);
426 }
427 if (tcs && has_dynamic_indexing(&tcs->base.tgsi_info)) {
428 transform_dynamic_indexing(svga, &tcs->base);
429 }
430 if (tes && has_dynamic_indexing(&tes->base.tgsi_info)) {
431 transform_dynamic_indexing(svga, &tes->base);
432 }
433
434 if (svga->curr.reduced_prim == MESA_PRIM_POINTS) {
435 /* If the current prim type is POINTS and the current geometry shader
436 * emits wide points, transform the shader to emulate wide points using
437 * quads. NOTE: we don't do emulation of wide points in GS when
438 * transform feedback is enabled.
439 */
440 if (gs != NULL && !gs->base.stream_output &&
441 (gs->base.tgsi_info.writes_psize || gs->wide_point)) {
442 orig_gs = gs->base.parent ? gs->base.parent : &gs->base;
443 new_gs = emulate_point_sprite(svga, orig_gs, orig_gs->tokens);
444 }
445
446 /* If there is not an active geometry shader and the current vertex
447 * shader emits wide point then create a new geometry shader to emulate
448 * wide point.
449 */
450 else if (gs == NULL && !vs->base.stream_output &&
451 (svga->curr.rast->pointsize > 1.0 ||
452 vs->base.tgsi_info.writes_psize)) {
453 new_gs = add_point_sprite_shader(svga);
454 }
455 else {
456 /* use the user's GS */
457 bind_gs_state(svga, svga->curr.user_gs);
458 }
459 }
460 else if (svga->curr.gs != svga->curr.user_gs) {
461 /* If current primitive type is not POINTS, then make sure
462 * we don't bind to any of the generated geometry shader
463 */
464 bind_gs_state(svga, svga->curr.user_gs);
465 }
466 (void) new_gs; /* silence the unused var warning */
467
468 return PIPE_OK;
469 }
470
471 struct svga_tracked_state svga_need_tgsi_transform =
472 {
473 "transform shader for optimization",
474 (SVGA_NEW_VS |
475 SVGA_NEW_FS |
476 SVGA_NEW_GS |
477 SVGA_NEW_REDUCED_PRIMITIVE |
478 SVGA_NEW_RAST),
479 update_tgsi_transform
480 };
481