xref: /aosp_15_r20/external/mesa3d/src/gallium/drivers/svga/svga_state_tgsi_transform.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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