1 /*
2  * Copyright © 2015 Red Hat
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 FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 
24 #include "nir.h"
25 #include "nir_builder.h"
26 
27 /* Lower glDrawPixels().
28  *
29  * This is based on the logic in st_get_drawpix_shader() in TGSI compiler.
30  *
31  * Run before nir_lower_io.
32  */
33 
34 typedef struct {
35    const nir_lower_drawpixels_options *options;
36    nir_shader   *shader;
37    nir_builder   b;
38    nir_variable *texcoord, *texcoord_const, *scale, *bias, *tex, *pixelmap;
39 } lower_drawpixels_state;
40 
41 static nir_ssa_def *
get_texcoord(lower_drawpixels_state * state)42 get_texcoord(lower_drawpixels_state *state)
43 {
44    if (state->texcoord == NULL) {
45       nir_variable *texcoord = NULL;
46 
47       /* find gl_TexCoord, if it exists: */
48       nir_foreach_shader_in_variable(var, state->shader) {
49          if (var->data.location == VARYING_SLOT_TEX0) {
50             texcoord = var;
51             break;
52          }
53       }
54 
55       /* otherwise create it: */
56       if (texcoord == NULL) {
57          texcoord = nir_variable_create(state->shader,
58                                         nir_var_shader_in,
59                                         glsl_vec4_type(),
60                                         "gl_TexCoord");
61          texcoord->data.location = VARYING_SLOT_TEX0;
62       }
63 
64       state->texcoord = texcoord;
65    }
66    return nir_load_var(&state->b, state->texcoord);
67 }
68 
69 static nir_variable *
create_uniform(nir_shader * shader,const char * name,const gl_state_index16 state_tokens[STATE_LENGTH])70 create_uniform(nir_shader *shader, const char *name,
71                const gl_state_index16 state_tokens[STATE_LENGTH])
72 {
73    nir_variable *var = nir_variable_create(shader,
74                                            nir_var_uniform,
75                                            glsl_vec4_type(),
76                                            name);
77    var->num_state_slots = 1;
78    var->state_slots = ralloc_array(var, nir_state_slot, 1);
79    memcpy(var->state_slots[0].tokens, state_tokens,
80           sizeof(var->state_slots[0].tokens));
81    return var;
82 }
83 
84 static nir_ssa_def *
get_scale(lower_drawpixels_state * state)85 get_scale(lower_drawpixels_state *state)
86 {
87    if (state->scale == NULL) {
88       state->scale = create_uniform(state->shader, "gl_PTscale",
89                                     state->options->scale_state_tokens);
90    }
91    return nir_load_var(&state->b, state->scale);
92 }
93 
94 static nir_ssa_def *
get_bias(lower_drawpixels_state * state)95 get_bias(lower_drawpixels_state *state)
96 {
97    if (state->bias == NULL) {
98       state->bias = create_uniform(state->shader, "gl_PTbias",
99                                    state->options->bias_state_tokens);
100    }
101    return nir_load_var(&state->b, state->bias);
102 }
103 
104 static nir_ssa_def *
get_texcoord_const(lower_drawpixels_state * state)105 get_texcoord_const(lower_drawpixels_state *state)
106 {
107    if (state->texcoord_const == NULL) {
108       state->texcoord_const = create_uniform(state->shader,
109                                    "gl_MultiTexCoord0",
110                                    state->options->texcoord_state_tokens);
111    }
112    return nir_load_var(&state->b, state->texcoord_const);
113 }
114 
115 static void
lower_color(lower_drawpixels_state * state,nir_intrinsic_instr * intr)116 lower_color(lower_drawpixels_state *state, nir_intrinsic_instr *intr)
117 {
118    nir_builder *b = &state->b;
119    nir_ssa_def *texcoord;
120    nir_tex_instr *tex;
121    nir_ssa_def *def;
122 
123    assert(intr->dest.is_ssa);
124 
125    b->cursor = nir_before_instr(&intr->instr);
126 
127    texcoord = get_texcoord(state);
128 
129    const struct glsl_type *sampler2D =
130       glsl_sampler_type(GLSL_SAMPLER_DIM_2D, false, false, GLSL_TYPE_FLOAT);
131 
132    if (!state->tex) {
133       state->tex =
134          nir_variable_create(b->shader, nir_var_uniform, sampler2D, "drawpix");
135       state->tex->data.binding = state->options->drawpix_sampler;
136       state->tex->data.explicit_binding = true;
137       state->tex->data.how_declared = nir_var_hidden;
138    }
139 
140    nir_deref_instr *tex_deref = nir_build_deref_var(b, state->tex);
141 
142    /* replace load_var(gl_Color) w/ texture sample:
143     *   TEX def, texcoord, drawpix_sampler, 2D
144     */
145    tex = nir_tex_instr_create(state->shader, 3);
146    tex->op = nir_texop_tex;
147    tex->sampler_dim = GLSL_SAMPLER_DIM_2D;
148    tex->coord_components = 2;
149    tex->dest_type = nir_type_float32;
150    tex->src[0].src_type = nir_tex_src_texture_deref;
151    tex->src[0].src = nir_src_for_ssa(&tex_deref->dest.ssa);
152    tex->src[1].src_type = nir_tex_src_sampler_deref;
153    tex->src[1].src = nir_src_for_ssa(&tex_deref->dest.ssa);
154    tex->src[2].src_type = nir_tex_src_coord;
155    tex->src[2].src =
156       nir_src_for_ssa(nir_channels(b, texcoord,
157                                    (1 << tex->coord_components) - 1));
158 
159    nir_ssa_dest_init(&tex->instr, &tex->dest, 4, 32, NULL);
160    nir_builder_instr_insert(b, &tex->instr);
161    def = &tex->dest.ssa;
162 
163    /* Apply the scale and bias. */
164    if (state->options->scale_and_bias) {
165       /* MAD def, def, scale, bias; */
166       def = nir_ffma(b, def, get_scale(state), get_bias(state));
167    }
168 
169    if (state->options->pixel_maps) {
170       if (!state->pixelmap) {
171          state->pixelmap = nir_variable_create(b->shader, nir_var_uniform,
172                                                sampler2D, "pixelmap");
173          state->pixelmap->data.binding = state->options->pixelmap_sampler;
174          state->pixelmap->data.explicit_binding = true;
175          state->pixelmap->data.how_declared = nir_var_hidden;
176       }
177 
178       nir_deref_instr *pixelmap_deref =
179          nir_build_deref_var(b, state->pixelmap);
180 
181       /* do four pixel map look-ups with two TEX instructions: */
182       nir_ssa_def *def_xy, *def_zw;
183 
184       /* TEX def.xy, def.xyyy, pixelmap_sampler, 2D; */
185       tex = nir_tex_instr_create(state->shader, 3);
186       tex->op = nir_texop_tex;
187       tex->sampler_dim = GLSL_SAMPLER_DIM_2D;
188       tex->coord_components = 2;
189       tex->sampler_index = state->options->pixelmap_sampler;
190       tex->texture_index = state->options->pixelmap_sampler;
191       tex->dest_type = nir_type_float32;
192       tex->src[0].src_type = nir_tex_src_texture_deref;
193       tex->src[0].src = nir_src_for_ssa(&pixelmap_deref->dest.ssa);
194       tex->src[1].src_type = nir_tex_src_sampler_deref;
195       tex->src[1].src = nir_src_for_ssa(&pixelmap_deref->dest.ssa);
196       tex->src[2].src_type = nir_tex_src_coord;
197       tex->src[2].src = nir_src_for_ssa(nir_channels(b, def, 0x3));
198 
199       nir_ssa_dest_init(&tex->instr, &tex->dest, 4, 32, NULL);
200       nir_builder_instr_insert(b, &tex->instr);
201       def_xy = &tex->dest.ssa;
202 
203       /* TEX def.zw, def.zwww, pixelmap_sampler, 2D; */
204       tex = nir_tex_instr_create(state->shader, 1);
205       tex->op = nir_texop_tex;
206       tex->sampler_dim = GLSL_SAMPLER_DIM_2D;
207       tex->coord_components = 2;
208       tex->sampler_index = state->options->pixelmap_sampler;
209       tex->dest_type = nir_type_float32;
210       tex->src[0].src_type = nir_tex_src_coord;
211       tex->src[0].src = nir_src_for_ssa(nir_channels(b, def, 0xc));
212 
213       nir_ssa_dest_init(&tex->instr, &tex->dest, 4, 32, NULL);
214       nir_builder_instr_insert(b, &tex->instr);
215       def_zw = &tex->dest.ssa;
216 
217       /* def = vec4(def.xy, def.zw); */
218       def = nir_vec4(b,
219                      nir_channel(b, def_xy, 0),
220                      nir_channel(b, def_xy, 1),
221                      nir_channel(b, def_zw, 0),
222                      nir_channel(b, def_zw, 1));
223    }
224 
225    nir_ssa_def_rewrite_uses(&intr->dest.ssa, def);
226 }
227 
228 static void
lower_texcoord(lower_drawpixels_state * state,nir_intrinsic_instr * intr)229 lower_texcoord(lower_drawpixels_state *state, nir_intrinsic_instr *intr)
230 {
231    state->b.cursor = nir_before_instr(&intr->instr);
232 
233    nir_ssa_def *texcoord_const = get_texcoord_const(state);
234    nir_ssa_def_rewrite_uses(&intr->dest.ssa, texcoord_const);
235 }
236 
237 static bool
lower_drawpixels_block(lower_drawpixels_state * state,nir_block * block)238 lower_drawpixels_block(lower_drawpixels_state *state, nir_block *block)
239 {
240    nir_foreach_instr_safe(instr, block) {
241       if (instr->type == nir_instr_type_intrinsic) {
242          nir_intrinsic_instr *intr = nir_instr_as_intrinsic(instr);
243 
244          switch (intr->intrinsic) {
245          case nir_intrinsic_load_deref: {
246             nir_deref_instr *deref = nir_src_as_deref(intr->src[0]);
247             nir_variable *var = nir_deref_instr_get_variable(deref);
248 
249             if (var->data.location == VARYING_SLOT_COL0) {
250                /* gl_Color should not have array/struct derefs: */
251                assert(deref->deref_type == nir_deref_type_var);
252                lower_color(state, intr);
253             } else if (var->data.location == VARYING_SLOT_TEX0) {
254                /* gl_TexCoord should not have array/struct derefs: */
255                assert(deref->deref_type == nir_deref_type_var);
256                lower_texcoord(state, intr);
257             }
258             break;
259          }
260 
261          case nir_intrinsic_load_color0:
262             lower_color(state, intr);
263             break;
264 
265          case nir_intrinsic_load_interpolated_input:
266          case nir_intrinsic_load_input: {
267             if (nir_intrinsic_io_semantics(intr).location == VARYING_SLOT_TEX0)
268                lower_texcoord(state, intr);
269             break;
270          }
271          default:
272             break;
273          }
274       }
275    }
276 
277    return true;
278 }
279 
280 static void
lower_drawpixels_impl(lower_drawpixels_state * state,nir_function_impl * impl)281 lower_drawpixels_impl(lower_drawpixels_state *state, nir_function_impl *impl)
282 {
283    nir_builder_init(&state->b, impl);
284 
285    nir_foreach_block(block, impl) {
286       lower_drawpixels_block(state, block);
287    }
288    nir_metadata_preserve(impl, nir_metadata_block_index |
289                                nir_metadata_dominance);
290 }
291 
292 void
nir_lower_drawpixels(nir_shader * shader,const nir_lower_drawpixels_options * options)293 nir_lower_drawpixels(nir_shader *shader,
294                      const nir_lower_drawpixels_options *options)
295 {
296    lower_drawpixels_state state = {
297       .options = options,
298       .shader = shader,
299    };
300 
301    assert(shader->info.stage == MESA_SHADER_FRAGMENT);
302 
303    nir_foreach_function(function, shader) {
304       if (function->impl)
305          lower_drawpixels_impl(&state, function->impl);
306    }
307 }
308