1 /********************************************************** 2 * Copyright 2014 VMware, Inc. All rights reserved. 3 * 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: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 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 22 * SOFTWARE. 23 * 24 **********************************************************/ 25 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" 33 34 #include "svga_context.h" 35 #include "svga_shader.h" 36 #include "svga_tgsi.h" 37 38 39 /** 40 * Bind a new GS. This updates the derived current gs state, not the 41 * user-specified GS state. 42 */ 43 static void 44 bind_gs_state(struct svga_context *svga, 45 struct svga_geometry_shader *gs) 46 { 47 svga->curr.gs = gs; 48 svga->dirty |= SVGA_NEW_GS; 49 } 50 51 52 /** 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. 61 */ 62 static struct svga_shader * 63 emulate_point_sprite(struct svga_context *svga, 64 struct svga_shader *shader, 65 const struct tgsi_token *tokens) 66 { 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; 76 77 assert(tokens != NULL); 78 79 orig_tokens = tokens; 80 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; 85 86 key.gs.sprite_origin_upper_left = 87 !(svga->curr.rast->templ.sprite_coord_mode == PIPE_SPRITE_COORD_LOWER_LEFT); 88 89 key.gs.aa_point = svga->curr.rast->templ.point_smooth; 90 91 if (orig_gs) { 92 93 /* Check if the original geometry shader has stream output and 94 * if position is one of the outputs. 95 */ 96 streamout = orig_gs->base.stream_output; 97 if (streamout) { 98 pos_out_index = streamout->pos_out_index; 99 key.gs.point_pos_stream_out = pos_out_index != -1; 100 } 101 102 /* Search the shader lists to see if there is a variant that matches 103 * this token key. 104 */ 105 gs = (struct svga_geometry_shader *) 106 svga_search_shader_token_key(&orig_gs->base, &key); 107 } 108 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 112 * upper-left origin. 113 */ 114 if (!gs) { 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, 119 key.gs.aa_point ? 120 &aa_point_coord_index : NULL); 121 122 if (!new_tokens) { 123 /* if no new tokens are generated for whatever reason, just return */ 124 return NULL; 125 } 126 127 if (0) { 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); 132 } 133 134 templ.tokens = new_tokens; 135 templ.stream_output.num_outputs = 0; 136 137 if (streamout) { 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. 143 */ 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; 148 } 149 } 150 151 /* Create a new geometry shader state with the new tokens */ 152 gs = svga->pipe.create_gs_state(&svga->pipe, &templ); 153 154 /* Don't need the token string anymore. There is a local copy 155 * in the shader state. 156 */ 157 FREE(new_tokens); 158 159 if (!gs) { 160 return NULL; 161 } 162 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; 168 169 /* Add the new geometry shader to the head of the shader list 170 * pointed to by the original geometry shader. 171 */ 172 if (orig_gs) { 173 gs->base.next = orig_gs->base.next; 174 orig_gs->base.next = &gs->base; 175 } 176 } 177 178 /* Bind the new geometry shader state */ 179 bind_gs_state(svga, gs); 180 181 return &gs->base; 182 } 183 184 /** 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. 189 */ 190 static struct svga_shader * 191 add_point_sprite_shader(struct svga_context *svga) 192 { 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; 197 198 if (orig_gs == NULL) { 199 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. 203 */ 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); 209 210 if (!orig_gs) 211 return NULL; 212 } 213 else { 214 if (orig_gs->base.parent) 215 orig_gs = (struct svga_geometry_shader *)orig_gs->base.parent; 216 } 217 tokens = orig_gs->base.tokens; 218 219 /* Call emulate_point_sprite to find or create a transformed 220 * geometry shader for supporting point sprite. 221 */ 222 new_gs = (struct svga_geometry_shader *) 223 emulate_point_sprite(svga, &orig_gs->base, tokens); 224 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. 228 */ 229 if (vs->gs == NULL) { 230 vs->gs = new_gs; 231 } 232 233 return &new_gs->base; 234 } 235 236 /* update_tgsi_transform provides a hook to transform a shader if needed. 237 */ 238 static enum pipe_error 239 update_tgsi_transform(struct svga_context *svga, unsigned dirty) 240 { 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 */ 245 246 if (!svga_have_vgpu10(svga)) 247 return PIPE_OK; 248 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. 254 */ 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); 259 } 260 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 263 * wide point. 264 */ 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); 269 } 270 else { 271 /* use the user's GS */ 272 bind_gs_state(svga, svga->curr.user_gs); 273 } 274 } 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 278 */ 279 bind_gs_state(svga, svga->curr.user_gs); 280 } 281 (void) new_gs; /* silence the unused var warning */ 282 283 return PIPE_OK; 284 } 285 286 struct svga_tracked_state svga_need_tgsi_transform = 287 { 288 "transform shader for optimization", 289 (SVGA_NEW_VS | 290 SVGA_NEW_FS | 291 SVGA_NEW_GS | 292 SVGA_NEW_REDUCED_PRIMITIVE | 293 SVGA_NEW_RAST), 294 update_tgsi_transform 295 }; 296