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.
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:
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
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.
30 * Lower sampler and image references of (non-bindless) uniforms by removing
31 * struct dereferences, and synthesizing new uniform variables without structs
34 * This will allow backends to have a simple, uniform treatment of bindless and
35 * non-bindless samplers and images.
45 * tmp = texture(s[n].tex[m], coord);
49 * decl_var uniform INTERP_MODE_NONE sampler2D[2][2] lower@s.tex (...)
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)
54 * and lower@s.tex has var->data.binding set to the base index as defined by
55 * the opaque uniform mapping.
58 #include "compiler/nir/nir.h"
59 #include "compiler/nir/nir_builder.h"
60 #include "compiler/nir/nir_deref.h"
62 #include "ir_uniform.h"
64 #include "main/compiler.h"
65 #include "main/mtypes.h"
67 struct lower_samplers_as_deref_state
{
69 const struct gl_shader_program
*shader_program
;
70 struct hash_table
*remap_table
;
73 /* Prepare for removing struct derefs. This pre-pass generates the name
74 * of the lowered deref, and calculates the lowered type and location.
75 * After that, once looking up (or creating if needed) the lowered var,
76 * constructing the new chain of deref instructions is a simple loop
77 * that skips the struct deref's
79 * path: appended to as we descend down the chain of deref instrs
80 * and remove struct derefs
81 * location: increased as we descend down and remove struct derefs
82 * type: updated as we recurse back up the chain of deref instrs
83 * with the resulting type after removing struct derefs
86 remove_struct_derefs_prep(nir_deref_instr
**p
, char **name
,
87 unsigned *location
, const struct glsl_type
**type
)
89 nir_deref_instr
*cur
= p
[0], *next
= p
[1];
96 switch (next
->deref_type
) {
97 case nir_deref_type_array
: {
98 unsigned length
= glsl_get_length(cur
->type
);
100 remove_struct_derefs_prep(&p
[1], name
, location
, type
);
102 *type
= glsl_array_type(*type
, length
, glsl_get_explicit_stride(cur
->type
));
106 case nir_deref_type_struct
: {
107 *location
+= glsl_get_struct_location_offset(cur
->type
, next
->strct
.index
);
108 ralloc_asprintf_append(name
, ".%s",
109 glsl_get_struct_elem_name(cur
->type
, next
->strct
.index
));
111 remove_struct_derefs_prep(&p
[1], name
, location
, type
);
116 unreachable("Invalid deref type");
121 static nir_deref_instr
*
122 lower_deref(nir_builder
*b
, struct lower_samplers_as_deref_state
*state
,
123 nir_deref_instr
*deref
)
125 nir_variable
*var
= nir_deref_instr_get_variable(deref
);
126 gl_shader_stage stage
= state
->shader
->info
.stage
;
128 if (var
->data
.bindless
|| var
->data
.mode
!= nir_var_uniform
)
132 nir_deref_path_init(&path
, deref
, state
->remap_table
);
133 assert(path
.path
[0]->deref_type
== nir_deref_type_var
);
135 char *name
= ralloc_asprintf(state
->remap_table
, "lower@%s", var
->name
);
136 unsigned location
= var
->data
.location
;
137 const struct glsl_type
*type
= NULL
;
141 * We end up needing to do this in two passes, in order to generate
142 * the name of the lowered var (and detecting whether there even are
143 * any struct deref's), and then the second pass to construct the
144 * actual deref instructions after looking up / generating a new
145 * nir_variable (since we need to construct the deref_var first)
148 remove_struct_derefs_prep(path
.path
, &name
, &location
, &type
);
150 if (state
->shader_program
&& var
->data
.how_declared
!= nir_var_hidden
) {
151 /* For GLSL programs, look up the bindings in the uniform storage. */
152 assert(location
< state
->shader_program
->data
->NumUniformStorage
&&
153 state
->shader_program
->data
->UniformStorage
[location
].opaque
[stage
].active
);
155 binding
= state
->shader_program
->data
->UniformStorage
[location
].opaque
[stage
].index
;
157 /* For ARB programs, built-in shaders, or internally generated sampler
158 * variables in GLSL programs, assume that whoever created the shader
159 * set the bindings correctly already.
161 assert(var
->data
.explicit_binding
);
162 binding
= var
->data
.binding
;
165 if (var
->type
== type
) {
166 /* Fast path: We did not encounter any struct derefs. */
167 var
->data
.binding
= binding
;
171 uint32_t hash
= _mesa_key_hash_string(name
);
172 struct hash_entry
*h
=
173 _mesa_hash_table_search_pre_hashed(state
->remap_table
, hash
, name
);
176 var
= (nir_variable
*)h
->data
;
178 var
= nir_variable_create(state
->shader
, nir_var_uniform
, type
, name
);
179 var
->data
.binding
= binding
;
181 /* Don't set var->data.location. The old structure location could be
182 * used to index into gl_uniform_storage, assuming the full structure
183 * was walked in order. With the new split variables, this invariant
184 * no longer holds and there's no meaningful way to start from a base
185 * location and access a particular array element. Just leave it 0.
188 _mesa_hash_table_insert_pre_hashed(state
->remap_table
, hash
, name
, var
);
191 /* construct a new deref based on lowered var (skipping the struct deref's
192 * from the original deref:
194 nir_deref_instr
*new_deref
= nir_build_deref_var(b
, var
);
195 for (nir_deref_instr
**p
= &path
.path
[1]; *p
; p
++) {
196 if ((*p
)->deref_type
== nir_deref_type_struct
)
199 assert((*p
)->deref_type
== nir_deref_type_array
);
201 new_deref
= nir_build_deref_array(b
, new_deref
,
202 nir_ssa_for_src(b
, (*p
)->arr
.index
, 1));
209 record_textures_used(struct shader_info
*info
,
210 nir_deref_instr
*deref
,
213 nir_variable
*var
= nir_deref_instr_get_variable(deref
);
215 /* Structs have been lowered already, so get_aoa_size is sufficient. */
216 const unsigned size
=
217 glsl_type_is_array(var
->type
) ? glsl_get_aoa_size(var
->type
) : 1;
218 unsigned mask
= ((1ull << MAX2(size
, 1)) - 1) << var
->data
.binding
;
220 info
->textures_used
|= mask
;
222 if (op
== nir_texop_txf
||
223 op
== nir_texop_txf_ms
||
224 op
== nir_texop_txf_ms_mcs
)
225 info
->textures_used_by_txf
|= mask
;
229 lower_sampler(nir_tex_instr
*instr
, struct lower_samplers_as_deref_state
*state
,
233 nir_tex_instr_src_index(instr
, nir_tex_src_texture_deref
);
235 nir_tex_instr_src_index(instr
, nir_tex_src_sampler_deref
);
237 b
->cursor
= nir_before_instr(&instr
->instr
);
239 if (texture_idx
>= 0) {
240 assert(instr
->src
[texture_idx
].src
.is_ssa
);
242 nir_deref_instr
*texture_deref
=
243 lower_deref(b
, state
, nir_src_as_deref(instr
->src
[texture_idx
].src
));
244 /* only lower non-bindless: */
246 nir_instr_rewrite_src(&instr
->instr
, &instr
->src
[texture_idx
].src
,
247 nir_src_for_ssa(&texture_deref
->dest
.ssa
));
248 record_textures_used(&b
->shader
->info
, texture_deref
, instr
->op
);
252 if (sampler_idx
>= 0) {
253 assert(instr
->src
[sampler_idx
].src
.is_ssa
);
254 nir_deref_instr
*sampler_deref
=
255 lower_deref(b
, state
, nir_src_as_deref(instr
->src
[sampler_idx
].src
));
256 /* only lower non-bindless: */
258 nir_instr_rewrite_src(&instr
->instr
, &instr
->src
[sampler_idx
].src
,
259 nir_src_for_ssa(&sampler_deref
->dest
.ssa
));
267 lower_intrinsic(nir_intrinsic_instr
*instr
,
268 struct lower_samplers_as_deref_state
*state
,
271 if (instr
->intrinsic
== nir_intrinsic_image_deref_load
||
272 instr
->intrinsic
== nir_intrinsic_image_deref_store
||
273 instr
->intrinsic
== nir_intrinsic_image_deref_atomic_add
||
274 instr
->intrinsic
== nir_intrinsic_image_deref_atomic_min
||
275 instr
->intrinsic
== nir_intrinsic_image_deref_atomic_max
||
276 instr
->intrinsic
== nir_intrinsic_image_deref_atomic_and
||
277 instr
->intrinsic
== nir_intrinsic_image_deref_atomic_or
||
278 instr
->intrinsic
== nir_intrinsic_image_deref_atomic_xor
||
279 instr
->intrinsic
== nir_intrinsic_image_deref_atomic_exchange
||
280 instr
->intrinsic
== nir_intrinsic_image_deref_atomic_comp_swap
||
281 instr
->intrinsic
== nir_intrinsic_image_deref_atomic_fadd
||
282 instr
->intrinsic
== nir_intrinsic_image_deref_size
) {
284 b
->cursor
= nir_before_instr(&instr
->instr
);
285 nir_deref_instr
*deref
=
286 lower_deref(b
, state
, nir_src_as_deref(instr
->src
[0]));
287 /* don't lower bindless: */
290 nir_instr_rewrite_src(&instr
->instr
, &instr
->src
[0],
291 nir_src_for_ssa(&deref
->dest
.ssa
));
299 lower_impl(nir_function_impl
*impl
, struct lower_samplers_as_deref_state
*state
)
302 nir_builder_init(&b
, impl
);
303 bool progress
= false;
305 nir_foreach_block(block
, impl
) {
306 nir_foreach_instr(instr
, block
) {
307 if (instr
->type
== nir_instr_type_tex
)
308 progress
|= lower_sampler(nir_instr_as_tex(instr
), state
, &b
);
309 else if (instr
->type
== nir_instr_type_intrinsic
)
310 progress
|= lower_intrinsic(nir_instr_as_intrinsic(instr
), state
, &b
);
318 gl_nir_lower_samplers_as_deref(nir_shader
*shader
,
319 const struct gl_shader_program
*shader_program
)
321 bool progress
= false;
322 struct lower_samplers_as_deref_state state
;
324 state
.shader
= shader
;
325 state
.shader_program
= shader_program
;
326 state
.remap_table
= _mesa_hash_table_create(NULL
, _mesa_key_hash_string
,
327 _mesa_key_string_equal
);
329 shader
->info
.textures_used
= 0;
330 shader
->info
.textures_used_by_txf
= 0;
332 nir_foreach_function(function
, shader
) {
334 progress
|= lower_impl(function
->impl
, &state
);
337 /* keys are freed automatically by ralloc */
338 _mesa_hash_table_destroy(state
.remap_table
, NULL
);
341 nir_remove_dead_derefs(shader
);