Home | History | Annotate | Download | only in svga
      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.
    253        */
    254       if (gs != NULL && (gs->base.info.writes_psize || gs->wide_point)) {
    255          orig_gs = gs->base.parent ? gs->base.parent : &gs->base;
    256          new_gs = emulate_point_sprite(svga, orig_gs, orig_gs->tokens);
    257       }
    258 
    259       /* If there is not an active geometry shader and the current vertex
    260        * shader emits wide point then create a new geometry shader to emulate
    261        * wide point.
    262        */
    263       else if (gs == NULL &&
    264                (svga->curr.rast->pointsize > 1.0 ||
    265                 vs->base.info.writes_psize)) {
    266          new_gs = add_point_sprite_shader(svga);
    267       }
    268       else {
    269          /* use the user's GS */
    270          bind_gs_state(svga, svga->curr.user_gs);
    271       }
    272    }
    273    else if (svga->curr.gs != svga->curr.user_gs) {
    274       /* If current primitive type is not POINTS, then make sure
    275        * we don't bind to any of the generated geometry shader
    276        */
    277       bind_gs_state(svga, svga->curr.user_gs);
    278    }
    279    (void) new_gs;    /* silence the unused var warning */
    280 
    281    return PIPE_OK;
    282 }
    283 
    284 struct svga_tracked_state svga_need_tgsi_transform =
    285 {
    286    "transform shader for optimization",
    287    (SVGA_NEW_VS |
    288     SVGA_NEW_FS |
    289     SVGA_NEW_GS |
    290     SVGA_NEW_REDUCED_PRIMITIVE |
    291     SVGA_NEW_RAST),
    292    update_tgsi_transform
    293 };
    294