2 * Copyright © 2019 Valve Corporation
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:
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
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
27 /* This pass optimizes GL access qualifiers. So far it does two things:
29 * - Infer readonly when it's missing.
30 * - Infer ACCESS_CAN_REORDER when the following are true:
31 * - Either there are no writes, or ACCESS_NON_WRITEABLE and ACCESS_RESTRICT
32 * are both set. In either case there are no writes to the underlying
34 * - If ACCESS_COHERENT is set, then there must be no memory barriers
35 * involving the access. Coherent accesses may return different results
36 * before and after barriers.
37 * - ACCESS_VOLATILE is not set.
39 * If these conditions are true, then image and buffer reads may be treated as
40 * if they were uniform buffer reads, i.e. they may be arbitrarily moved,
41 * combined, rematerialized etc.
45 struct set
*vars_written
;
53 gather_intrinsic(struct access_state
*state
, nir_intrinsic_instr
*instr
)
56 switch (instr
->intrinsic
) {
57 case nir_intrinsic_image_deref_store
:
58 case nir_intrinsic_image_deref_atomic_add
:
59 case nir_intrinsic_image_deref_atomic_min
:
60 case nir_intrinsic_image_deref_atomic_max
:
61 case nir_intrinsic_image_deref_atomic_and
:
62 case nir_intrinsic_image_deref_atomic_or
:
63 case nir_intrinsic_image_deref_atomic_xor
:
64 case nir_intrinsic_image_deref_atomic_exchange
:
65 case nir_intrinsic_image_deref_atomic_comp_swap
:
66 case nir_intrinsic_image_deref_atomic_fadd
:
67 var
= nir_intrinsic_get_var(instr
, 0);
69 /* In OpenGL, buffer images use normal buffer objects, whereas other
70 * image types use textures which cannot alias with buffer objects.
71 * Therefore we have to group buffer samplers together with SSBO's.
73 if (glsl_get_sampler_dim(glsl_without_array(var
->type
)) ==
75 state
->buffers_written
= true;
77 state
->images_written
= true;
79 if (var
->data
.mode
== nir_var_uniform
)
80 _mesa_set_add(state
->vars_written
, var
);
83 case nir_intrinsic_bindless_image_store
:
84 case nir_intrinsic_bindless_image_atomic_add
:
85 case nir_intrinsic_bindless_image_atomic_min
:
86 case nir_intrinsic_bindless_image_atomic_max
:
87 case nir_intrinsic_bindless_image_atomic_and
:
88 case nir_intrinsic_bindless_image_atomic_or
:
89 case nir_intrinsic_bindless_image_atomic_xor
:
90 case nir_intrinsic_bindless_image_atomic_exchange
:
91 case nir_intrinsic_bindless_image_atomic_comp_swap
:
92 case nir_intrinsic_bindless_image_atomic_fadd
:
93 if (nir_intrinsic_image_dim(instr
) == GLSL_SAMPLER_DIM_BUF
)
94 state
->buffers_written
= true;
96 state
->images_written
= true;
99 case nir_intrinsic_store_deref
:
100 case nir_intrinsic_deref_atomic_add
:
101 case nir_intrinsic_deref_atomic_imin
:
102 case nir_intrinsic_deref_atomic_umin
:
103 case nir_intrinsic_deref_atomic_imax
:
104 case nir_intrinsic_deref_atomic_umax
:
105 case nir_intrinsic_deref_atomic_and
:
106 case nir_intrinsic_deref_atomic_or
:
107 case nir_intrinsic_deref_atomic_xor
:
108 case nir_intrinsic_deref_atomic_exchange
:
109 case nir_intrinsic_deref_atomic_comp_swap
:
110 case nir_intrinsic_deref_atomic_fadd
:
111 case nir_intrinsic_deref_atomic_fmin
:
112 case nir_intrinsic_deref_atomic_fmax
:
113 case nir_intrinsic_deref_atomic_fcomp_swap
:
114 var
= nir_intrinsic_get_var(instr
, 0);
115 if (var
->data
.mode
!= nir_var_mem_ssbo
)
118 _mesa_set_add(state
->vars_written
, var
);
119 state
->buffers_written
= true;
121 case nir_intrinsic_memory_barrier
:
122 state
->buffer_barriers
= true;
123 state
->image_barriers
= true;
126 case nir_intrinsic_memory_barrier_buffer
:
127 state
->buffer_barriers
= true;
130 case nir_intrinsic_memory_barrier_image
:
131 state
->image_barriers
= true;
140 process_variable(struct access_state
*state
, nir_variable
*var
)
142 if (var
->data
.mode
!= nir_var_mem_ssbo
&&
143 !(var
->data
.mode
== nir_var_uniform
&&
144 glsl_type_is_image(var
->type
)))
147 /* Ignore variables we've already marked */
148 if (var
->data
.image
.access
& ACCESS_CAN_REORDER
)
151 if (!(var
->data
.image
.access
& ACCESS_NON_WRITEABLE
) &&
152 !_mesa_set_search(state
->vars_written
, var
)) {
153 var
->data
.image
.access
|= ACCESS_NON_WRITEABLE
;
161 can_reorder(struct access_state
*state
, enum gl_access_qualifier access
,
162 bool is_buffer
, bool is_ssbo
)
164 bool is_any_written
= is_buffer
? state
->buffers_written
:
165 state
->images_written
;
167 /* Can we guarantee that the underlying memory is never written? */
168 if (!is_any_written
||
169 ((access
& ACCESS_NON_WRITEABLE
) &&
170 (access
& ACCESS_RESTRICT
))) {
171 /* Note: memoryBarrierBuffer() is only guaranteed to flush buffer
172 * variables and not imageBuffer's, so we only consider the GL-level
175 bool is_any_barrier
= is_ssbo
?
176 state
->buffer_barriers
: state
->image_barriers
;
178 return (!is_any_barrier
|| !(access
& ACCESS_COHERENT
)) &&
179 !(access
& ACCESS_VOLATILE
);
186 process_intrinsic(struct access_state
*state
, nir_intrinsic_instr
*instr
)
188 switch (instr
->intrinsic
) {
189 case nir_intrinsic_bindless_image_load
:
190 if (nir_intrinsic_access(instr
) & ACCESS_CAN_REORDER
)
193 /* We have less information about bindless intrinsics, since we can't
194 * always trace uses back to the variable. Don't try and infer if it's
195 * read-only, unless there are no image writes at all.
197 bool progress
= false;
199 nir_intrinsic_image_dim(instr
) == GLSL_SAMPLER_DIM_BUF
;
201 bool is_any_written
=
202 is_buffer
? state
->buffers_written
: state
->images_written
;
204 if (!(nir_intrinsic_access(instr
) & ACCESS_NON_WRITEABLE
) &&
207 nir_intrinsic_set_access(instr
,
208 nir_intrinsic_access(instr
) |
209 ACCESS_NON_WRITEABLE
);
212 if (can_reorder(state
, nir_intrinsic_access(instr
), is_buffer
, false)) {
214 nir_intrinsic_set_access(instr
,
215 nir_intrinsic_access(instr
) |
221 case nir_intrinsic_load_deref
:
222 case nir_intrinsic_image_deref_load
: {
223 nir_variable
*var
= nir_intrinsic_get_var(instr
, 0);
225 if (instr
->intrinsic
== nir_intrinsic_load_deref
&&
226 var
->data
.mode
!= nir_var_mem_ssbo
)
229 if (nir_intrinsic_access(instr
) & ACCESS_CAN_REORDER
)
232 bool progress
= false;
234 /* Check if we were able to mark the whole variable non-writeable */
235 if (!(nir_intrinsic_access(instr
) & ACCESS_NON_WRITEABLE
) &&
236 var
->data
.image
.access
& ACCESS_NON_WRITEABLE
) {
238 nir_intrinsic_set_access(instr
,
239 nir_intrinsic_access(instr
) |
240 ACCESS_NON_WRITEABLE
);
243 bool is_ssbo
= var
->data
.mode
== nir_var_mem_ssbo
;
245 bool is_buffer
= is_ssbo
||
246 glsl_get_sampler_dim(glsl_without_array(var
->type
)) == GLSL_SAMPLER_DIM_BUF
;
248 if (can_reorder(state
, nir_intrinsic_access(instr
), is_buffer
, is_ssbo
)) {
250 nir_intrinsic_set_access(instr
,
251 nir_intrinsic_access(instr
) |
264 opt_access_impl(struct access_state
*state
,
265 nir_function_impl
*impl
)
267 bool progress
= false;
269 nir_foreach_block(block
, impl
) {
270 nir_foreach_instr(instr
, block
) {
271 if (instr
->type
== nir_instr_type_intrinsic
)
272 progress
|= process_intrinsic(state
,
273 nir_instr_as_intrinsic(instr
));
278 nir_metadata_preserve(impl
,
279 nir_metadata_block_index
|
280 nir_metadata_dominance
|
281 nir_metadata_live_ssa_defs
|
282 nir_metadata_loop_analysis
);
290 gl_nir_opt_access(nir_shader
*shader
)
292 struct access_state state
= {
293 .vars_written
= _mesa_pointer_set_create(NULL
),
296 bool var_progress
= false;
297 bool progress
= false;
299 nir_foreach_function(func
, shader
) {
301 nir_foreach_block(block
, func
->impl
) {
302 nir_foreach_instr(instr
, block
) {
303 if (instr
->type
== nir_instr_type_intrinsic
)
304 gather_intrinsic(&state
, nir_instr_as_intrinsic(instr
));
310 nir_foreach_variable(var
, &shader
->uniforms
)
311 var_progress
|= process_variable(&state
, var
);
313 nir_foreach_function(func
, shader
) {
315 progress
|= opt_access_impl(&state
, func
->impl
);
317 /* If we make a change to the uniforms, update all the impls. */
319 nir_metadata_preserve(func
->impl
,
320 nir_metadata_block_index
|
321 nir_metadata_dominance
|
322 nir_metadata_live_ssa_defs
|
323 nir_metadata_loop_analysis
);
328 progress
|= var_progress
;
330 _mesa_set_destroy(state
.vars_written
, NULL
);