1 /**********************************************************
2 * Copyright 2014 VMware, Inc. All rights reserved.
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 **********************************************************/
26 #include "util/u_inlines.h"
27 #include "util/u_memory.h"
28 #include "util/u_bitmask.h"
29 #include "util/u_simple_shaders.h"
30 #include "tgsi/tgsi_ureg.h"
31 #include "tgsi/tgsi_point_sprite.h"
32 #include "tgsi/tgsi_dump.h"
34 #include "svga_context.h"
35 #include "svga_shader.h"
36 #include "svga_tgsi.h"
40 * Bind a new GS. This updates the derived current gs state, not the
41 * user-specified GS state.
44 bind_gs_state(struct svga_context
*svga
,
45 struct svga_geometry_shader
*gs
)
48 svga
->dirty
|= SVGA_NEW_GS
;
53 * emulate_point_sprite searches the shader variants list to see it there is
54 * a shader variant with a token string that matches the emulation
55 * requirement. It there isn't, then it will use a tgsi utility
56 * tgsi_add_point_sprite to transform the original token string to support
57 * point sprite. A new geometry shader state will be created with the
58 * transformed token string and added to the shader variants list of the
59 * original geometry shader. The new geometry shader state will then be
60 * bound as the current geometry shader.
62 static struct svga_shader
*
63 emulate_point_sprite(struct svga_context
*svga
,
64 struct svga_shader
*shader
,
65 const struct tgsi_token
*tokens
)
67 struct svga_token_key key
;
68 struct tgsi_token
*new_tokens
;
69 const struct tgsi_token
*orig_tokens
;
70 struct svga_geometry_shader
*orig_gs
= (struct svga_geometry_shader
*)shader
;
71 struct svga_geometry_shader
*gs
= NULL
;
72 struct pipe_shader_state templ
;
73 struct svga_stream_output
*streamout
= NULL
;
74 int pos_out_index
= -1;
75 int aa_point_coord_index
= -1;
77 assert(tokens
!= NULL
);
81 /* Create a token key */
82 memset(&key
, 0, sizeof key
);
83 key
.gs
.writes_psize
= 1;
84 key
.gs
.sprite_coord_enable
= svga
->curr
.rast
->templ
.sprite_coord_enable
;
86 key
.gs
.sprite_origin_upper_left
=
87 !(svga
->curr
.rast
->templ
.sprite_coord_mode
== PIPE_SPRITE_COORD_LOWER_LEFT
);
89 key
.gs
.aa_point
= svga
->curr
.rast
->templ
.point_smooth
;
93 /* Check if the original geometry shader has stream output and
94 * if position is one of the outputs.
96 streamout
= orig_gs
->base
.stream_output
;
98 pos_out_index
= streamout
->pos_out_index
;
99 key
.gs
.point_pos_stream_out
= pos_out_index
!= -1;
102 /* Search the shader lists to see if there is a variant that matches
105 gs
= (struct svga_geometry_shader
*)
106 svga_search_shader_token_key(&orig_gs
->base
, &key
);
109 /* If there isn't, then call the tgsi utility tgsi_add_point_sprite
110 * to transform the original tokens to support point sprite.
111 * Flip the sprite origin as SVGA3D device only supports an
115 new_tokens
= tgsi_add_point_sprite(orig_tokens
,
116 key
.gs
.sprite_coord_enable
,
117 key
.gs
.sprite_origin_upper_left
,
118 key
.gs
.point_pos_stream_out
,
120 &aa_point_coord_index
: NULL
);
123 /* if no new tokens are generated for whatever reason, just return */
128 debug_printf("Before tgsi_add_point_sprite ---------------\n");
129 tgsi_dump(orig_tokens
, 0);
130 debug_printf("After tgsi_add_point_sprite --------------\n");
131 tgsi_dump(new_tokens
, 0);
134 templ
.tokens
= new_tokens
;
135 templ
.stream_output
.num_outputs
= 0;
138 templ
.stream_output
= streamout
->info
;
139 /* The tgsi_add_point_sprite utility adds an extra output
140 * for the original point position for stream output purpose.
141 * We need to replace the position output register index in the
142 * stream output declaration with the new register index.
144 if (pos_out_index
!= -1) {
145 assert(orig_gs
!= NULL
);
146 templ
.stream_output
.output
[pos_out_index
].register_index
=
147 orig_gs
->base
.info
.num_outputs
;
151 /* Create a new geometry shader state with the new tokens */
152 gs
= svga
->pipe
.create_gs_state(&svga
->pipe
, &templ
);
154 /* Don't need the token string anymore. There is a local copy
155 * in the shader state.
163 gs
->wide_point
= TRUE
;
164 gs
->aa_point_coord_index
= aa_point_coord_index
;
165 gs
->base
.token_key
= key
;
166 gs
->base
.parent
= &orig_gs
->base
;
167 gs
->base
.next
= NULL
;
169 /* Add the new geometry shader to the head of the shader list
170 * pointed to by the original geometry shader.
173 gs
->base
.next
= orig_gs
->base
.next
;
174 orig_gs
->base
.next
= &gs
->base
;
178 /* Bind the new geometry shader state */
179 bind_gs_state(svga
, gs
);
185 * Generate a geometry shader that emits a wide point by drawing a quad.
186 * This function first creates a passthrough geometry shader and then
187 * calls emulate_point_sprite() to transform the geometry shader to
188 * support point sprite.
190 static struct svga_shader
*
191 add_point_sprite_shader(struct svga_context
*svga
)
193 struct svga_vertex_shader
*vs
= svga
->curr
.vs
;
194 struct svga_geometry_shader
*orig_gs
= vs
->gs
;
195 struct svga_geometry_shader
*new_gs
;
196 const struct tgsi_token
*tokens
;
198 if (orig_gs
== NULL
) {
200 /* If this is the first time adding a geometry shader to this
201 * vertex shader to support point sprite, then create
202 * a passthrough geometry shader first.
204 orig_gs
= (struct svga_geometry_shader
*)
205 util_make_geometry_passthrough_shader(
206 &svga
->pipe
, vs
->base
.info
.num_outputs
,
207 vs
->base
.info
.output_semantic_name
,
208 vs
->base
.info
.output_semantic_index
);
214 if (orig_gs
->base
.parent
)
215 orig_gs
= (struct svga_geometry_shader
*)orig_gs
->base
.parent
;
217 tokens
= orig_gs
->base
.tokens
;
219 /* Call emulate_point_sprite to find or create a transformed
220 * geometry shader for supporting point sprite.
222 new_gs
= (struct svga_geometry_shader
*)
223 emulate_point_sprite(svga
, &orig_gs
->base
, tokens
);
225 /* If this is the first time creating a geometry shader to
226 * support vertex point size, then add the new geometry shader
227 * to the vertex shader.
229 if (vs
->gs
== NULL
) {
233 return &new_gs
->base
;
236 /* update_tgsi_transform provides a hook to transform a shader if needed.
238 static enum pipe_error
239 update_tgsi_transform(struct svga_context
*svga
, unsigned dirty
)
241 struct svga_geometry_shader
*gs
= svga
->curr
.user_gs
; /* current gs */
242 struct svga_vertex_shader
*vs
= svga
->curr
.vs
; /* currently bound vs */
243 struct svga_shader
*orig_gs
; /* original gs */
244 struct svga_shader
*new_gs
; /* new gs */
246 if (!svga_have_vgpu10(svga
))
249 if (svga
->curr
.reduced_prim
== PIPE_PRIM_POINTS
) {
250 /* If the current prim type is POINTS and the current geometry shader
251 * emits wide points, transform the shader to emulate wide points using
252 * quads. NOTE: we don't do emulation of wide points in GS when
253 * transform feedback is enabled.
255 if (gs
!= NULL
&& !gs
->base
.stream_output
&&
256 (gs
->base
.info
.writes_psize
|| gs
->wide_point
)) {
257 orig_gs
= gs
->base
.parent
? gs
->base
.parent
: &gs
->base
;
258 new_gs
= emulate_point_sprite(svga
, orig_gs
, orig_gs
->tokens
);
261 /* If there is not an active geometry shader and the current vertex
262 * shader emits wide point then create a new geometry shader to emulate
265 else if (gs
== NULL
&& !vs
->base
.stream_output
&&
266 (svga
->curr
.rast
->pointsize
> 1.0 ||
267 vs
->base
.info
.writes_psize
)) {
268 new_gs
= add_point_sprite_shader(svga
);
271 /* use the user's GS */
272 bind_gs_state(svga
, svga
->curr
.user_gs
);
275 else if (svga
->curr
.gs
!= svga
->curr
.user_gs
) {
276 /* If current primitive type is not POINTS, then make sure
277 * we don't bind to any of the generated geometry shader
279 bind_gs_state(svga
, svga
->curr
.user_gs
);
281 (void) new_gs
; /* silence the unused var warning */
286 struct svga_tracked_state svga_need_tgsi_transform
=
288 "transform shader for optimization",
292 SVGA_NEW_REDUCED_PRIMITIVE
|
294 update_tgsi_transform