• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2005-2007  Brian Paul   All Rights Reserved.
3  * Copyright (C) 2008  VMware, Inc.   All Rights Reserved.
4  * Copyright © 2014 Intel Corporation
5  * Copyright © 2017 Advanced Micro Devices, Inc.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the next
15  * paragraph) shall be included in all copies or substantial portions of the
16  * Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  */
26 
27 /**
28  * \file
29  *
30  * Lower sampler and image references of (non-bindless) uniforms by removing
31  * struct dereferences, and synthesizing new uniform variables without structs
32  * if required.
33  *
34  * This will allow backends to have a simple, uniform treatment of bindless and
35  * non-bindless samplers and images.
36  *
37  * Example:
38  *
39  *   struct S {
40  *      sampler2D tex[2];
41  *      sampler2D other;
42  *   };
43  *   uniform S s[2];
44  *
45  *   tmp = texture(s[n].tex[m], coord);
46  *
47  * Becomes:
48  *
49  *   decl_var uniform INTERP_MODE_NONE sampler2D[2][2] lower@s.tex (...)
50  *
51  *   vec1 32 ssa_idx = $(2 * n + m)
52  *   vec4 32 ssa_out = tex ssa_coord (coord), lower@s.tex[n][m] (texture), lower@s.tex[n][m] (sampler)
53  *
54  * and lower@s.tex has var->data.binding set to the base index as defined by
55  * the opaque uniform mapping.
56  */
57 
58 #include "nir.h"
59 #include "nir_builder.h"
60 #include "compiler/glsl/ir_uniform.h"
61 
62 #include "main/compiler.h"
63 #include "main/mtypes.h"
64 #include "program/prog_parameter.h"
65 #include "program/program.h"
66 
67 struct lower_samplers_as_deref_state {
68    nir_shader *shader;
69    const struct gl_shader_program *shader_program;
70    struct hash_table *remap_table;
71 };
72 
73 static void
remove_struct_derefs(nir_deref * tail,struct lower_samplers_as_deref_state * state,nir_builder * b,char ** path,unsigned * location)74 remove_struct_derefs(nir_deref *tail,
75                      struct lower_samplers_as_deref_state *state,
76                      nir_builder *b, char **path, unsigned *location)
77 {
78    if (!tail->child)
79       return;
80 
81    switch (tail->child->deref_type) {
82    case nir_deref_type_array: {
83       unsigned length = glsl_get_length(tail->type);
84 
85       remove_struct_derefs(tail->child, state, b, path, location);
86 
87       tail->type = glsl_get_array_instance(tail->child->type, length);
88       break;
89    }
90 
91    case nir_deref_type_struct: {
92       nir_deref_struct *deref_struct = nir_deref_as_struct(tail->child);
93 
94       *location += glsl_get_record_location_offset(tail->type, deref_struct->index);
95       ralloc_asprintf_append(path, ".%s",
96                              glsl_get_struct_elem_name(tail->type, deref_struct->index));
97 
98       remove_struct_derefs(tail->child, state, b, path, location);
99 
100       /* Drop the struct deref and re-parent. */
101       ralloc_steal(tail, tail->child->child);
102       tail->type = tail->child->type;
103       tail->child = tail->child->child;
104       break;
105    }
106 
107    default:
108       unreachable("Invalid deref type");
109       break;
110    }
111 }
112 
113 static void
lower_deref(nir_deref_var * deref,struct lower_samplers_as_deref_state * state,nir_builder * b)114 lower_deref(nir_deref_var *deref,
115             struct lower_samplers_as_deref_state *state,
116             nir_builder *b)
117 {
118    nir_variable *var = deref->var;
119    gl_shader_stage stage = state->shader->info.stage;
120    unsigned location = var->data.location;
121    unsigned binding;
122    const struct glsl_type *orig_type = deref->deref.type;
123    char *path;
124 
125    assert(var->data.mode == nir_var_uniform);
126 
127    path = ralloc_asprintf(state->remap_table, "lower@%s", var->name);
128    remove_struct_derefs(&deref->deref, state, b, &path, &location);
129 
130    assert(location < state->shader_program->data->NumUniformStorage &&
131           state->shader_program->data->UniformStorage[location].opaque[stage].active);
132 
133    binding = state->shader_program->data->UniformStorage[location].opaque[stage].index;
134 
135    if (orig_type == deref->deref.type) {
136       /* Fast path: We did not encounter any struct derefs. */
137       var->data.binding = binding;
138       return;
139    }
140 
141    uint32_t hash = _mesa_key_hash_string(path);
142    struct hash_entry *h =
143       _mesa_hash_table_search_pre_hashed(state->remap_table, hash, path);
144 
145    if (h) {
146       var = (nir_variable *)h->data;
147    } else {
148       var = nir_variable_create(state->shader, nir_var_uniform, deref->deref.type, path);
149       var->data.binding = binding;
150       _mesa_hash_table_insert_pre_hashed(state->remap_table, hash, path, var);
151    }
152 
153    deref->var = var;
154 }
155 
156 static bool
lower_sampler(nir_tex_instr * instr,struct lower_samplers_as_deref_state * state,nir_builder * b)157 lower_sampler(nir_tex_instr *instr, struct lower_samplers_as_deref_state *state,
158               nir_builder *b)
159 {
160    if (!instr->texture)
161       return false;
162 
163    /* In GLSL, we only fill out the texture field.  The sampler is inferred */
164    assert(instr->sampler == NULL);
165 
166    b->cursor = nir_before_instr(&instr->instr);
167    lower_deref(instr->texture, state, b);
168 
169    if (instr->op != nir_texop_txf_ms &&
170        instr->op != nir_texop_txf_ms_mcs &&
171        instr->op != nir_texop_samples_identical) {
172       nir_instr_rewrite_deref(&instr->instr, &instr->sampler,
173                               nir_deref_var_clone(instr->texture, instr));
174    } else {
175       assert(!instr->sampler);
176    }
177 
178    return true;
179 }
180 
181 static bool
lower_intrinsic(nir_intrinsic_instr * instr,struct lower_samplers_as_deref_state * state,nir_builder * b)182 lower_intrinsic(nir_intrinsic_instr *instr,
183                 struct lower_samplers_as_deref_state *state,
184                 nir_builder *b)
185 {
186    if (instr->intrinsic == nir_intrinsic_image_load ||
187        instr->intrinsic == nir_intrinsic_image_store ||
188        instr->intrinsic == nir_intrinsic_image_atomic_add ||
189        instr->intrinsic == nir_intrinsic_image_atomic_min ||
190        instr->intrinsic == nir_intrinsic_image_atomic_max ||
191        instr->intrinsic == nir_intrinsic_image_atomic_and ||
192        instr->intrinsic == nir_intrinsic_image_atomic_or ||
193        instr->intrinsic == nir_intrinsic_image_atomic_xor ||
194        instr->intrinsic == nir_intrinsic_image_atomic_exchange ||
195        instr->intrinsic == nir_intrinsic_image_atomic_comp_swap ||
196        instr->intrinsic == nir_intrinsic_image_size) {
197       b->cursor = nir_before_instr(&instr->instr);
198       lower_deref(instr->variables[0], state, b);
199       return true;
200    }
201 
202    return false;
203 }
204 
205 static bool
lower_impl(nir_function_impl * impl,struct lower_samplers_as_deref_state * state)206 lower_impl(nir_function_impl *impl, struct lower_samplers_as_deref_state *state)
207 {
208    nir_builder b;
209    nir_builder_init(&b, impl);
210    bool progress = false;
211 
212    nir_foreach_block(block, impl) {
213       nir_foreach_instr(instr, block) {
214          if (instr->type == nir_instr_type_tex)
215             progress |= lower_sampler(nir_instr_as_tex(instr), state, &b);
216          else if (instr->type == nir_instr_type_intrinsic)
217             progress |= lower_intrinsic(nir_instr_as_intrinsic(instr), state, &b);
218       }
219    }
220 
221    return progress;
222 }
223 
224 bool
nir_lower_samplers_as_deref(nir_shader * shader,const struct gl_shader_program * shader_program)225 nir_lower_samplers_as_deref(nir_shader *shader,
226                             const struct gl_shader_program *shader_program)
227 {
228    bool progress = false;
229    struct lower_samplers_as_deref_state state;
230 
231    state.shader = shader;
232    state.shader_program = shader_program;
233    state.remap_table = _mesa_hash_table_create(NULL, _mesa_key_hash_string,
234                                                _mesa_key_string_equal);
235 
236    nir_foreach_function(function, shader) {
237       if (function->impl)
238          progress |= lower_impl(function->impl, &state);
239    }
240 
241    /* keys are freed automatically by ralloc */
242    _mesa_hash_table_destroy(state.remap_table, NULL);
243 
244    return progress;
245 }
246