1 /*
2  * Copyright © 2015 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23 
24 #include "nir.h"
25 #include "nir_builder.h"
26 
27 /**
28  * @file
29  *
30  * This pass combines clip and cull distance arrays in separate locations and
31  * colocates them both in VARYING_SLOT_CLIP_DIST0.  It does so by maintaining
32  * two arrays but making them compact and using location_frac to stack them on
33  * top of each other.
34  */
35 
36 /**
37  * Get the length of the clip/cull distance array, looking past
38  * any interface block arrays.
39  */
40 static unsigned
get_unwrapped_array_length(nir_shader * nir,nir_variable * var)41 get_unwrapped_array_length(nir_shader *nir, nir_variable *var)
42 {
43    if (!var)
44       return 0;
45 
46    /* Unwrap GS input and TCS input/output interfaces.  We want the
47     * underlying clip/cull distance array length, not the per-vertex
48     * array length.
49     */
50    const struct glsl_type *type = var->type;
51    if (nir_is_arrayed_io(var, nir->info.stage))
52       type = glsl_get_array_element(type);
53 
54    assert(glsl_type_is_array(type));
55 
56    return glsl_get_length(type);
57 }
58 
59 static bool
combine_clip_cull(nir_shader * nir,nir_variable_mode mode,bool store_info)60 combine_clip_cull(nir_shader *nir,
61                   nir_variable_mode mode,
62                   bool store_info)
63 {
64    nir_variable *cull = NULL;
65    nir_variable *clip = NULL;
66 
67    nir_foreach_variable_with_modes(var, nir, mode) {
68       if (var->data.location == VARYING_SLOT_CLIP_DIST0)
69          clip = var;
70 
71       if (var->data.location == VARYING_SLOT_CULL_DIST0)
72          cull = var;
73    }
74 
75    if (!cull && !clip) {
76       /* If this is run after optimizations and the variables have been
77        * eliminated, we should update the shader info, because no other
78        * place does that.
79        */
80       if (store_info) {
81          nir->info.clip_distance_array_size = 0;
82          nir->info.cull_distance_array_size = 0;
83       }
84       return false;
85    }
86 
87    if (!cull && clip) {
88       /* The GLSL IR lowering pass must have converted these to vectors */
89       if (!clip->data.compact)
90          return false;
91 
92       /* If this pass has already run, don't repeat.  We would think that
93        * the combined clip/cull distance array was clip-only and mess up.
94        */
95       if (clip->data.how_declared == nir_var_hidden)
96          return false;
97    }
98 
99    const unsigned clip_array_size = get_unwrapped_array_length(nir, clip);
100    const unsigned cull_array_size = get_unwrapped_array_length(nir, cull);
101 
102    if (store_info) {
103       nir->info.clip_distance_array_size = clip_array_size;
104       nir->info.cull_distance_array_size = cull_array_size;
105    }
106 
107    if (clip) {
108       assert(clip->data.compact);
109       clip->data.how_declared = nir_var_hidden;
110    }
111 
112    if (cull) {
113       assert(cull->data.compact);
114       cull->data.how_declared = nir_var_hidden;
115       cull->data.location = VARYING_SLOT_CLIP_DIST0 + clip_array_size / 4;
116       cull->data.location_frac = clip_array_size % 4;
117    }
118 
119    return true;
120 }
121 
122 bool
nir_lower_clip_cull_distance_arrays(nir_shader * nir)123 nir_lower_clip_cull_distance_arrays(nir_shader *nir)
124 {
125    bool progress = false;
126 
127    if (nir->info.stage <= MESA_SHADER_GEOMETRY)
128       progress |= combine_clip_cull(nir, nir_var_shader_out, true);
129 
130    if (nir->info.stage > MESA_SHADER_VERTEX) {
131       progress |= combine_clip_cull(nir, nir_var_shader_in,
132                                     nir->info.stage == MESA_SHADER_FRAGMENT);
133    }
134 
135    nir_foreach_function(function, nir) {
136       if (!function->impl)
137          continue;
138 
139       if (progress) {
140          nir_metadata_preserve(function->impl,
141                                nir_metadata_block_index |
142                                nir_metadata_dominance |
143                                nir_metadata_live_ssa_defs |
144                                nir_metadata_loop_analysis);
145       } else {
146          nir_metadata_preserve(function->impl, nir_metadata_all);
147       }
148    }
149 
150    return progress;
151 }
152