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
26 /* This pass optimizes GL access qualifiers. So far it does two things:
28 * - Infer readonly when it's missing.
29 * - Infer ACCESS_CAN_REORDER when the following are true:
30 * - Either there are no writes, or ACCESS_NON_WRITEABLE and ACCESS_RESTRICT
31 * are both set. In either case there are no writes to the underlying
33 * - If ACCESS_COHERENT is set, then there must be no memory barriers
34 * involving the access. Coherent accesses may return different results
35 * before and after barriers.
36 * - ACCESS_VOLATILE is not set.
38 * If these conditions are true, then image and buffer reads may be treated as
39 * if they were uniform buffer reads, i.e. they may be arbitrarily moved,
40 * combined, rematerialized etc.
44 struct set
*vars_written
;
52 gather_intrinsic(struct access_state
*state
, nir_intrinsic_instr
*instr
)
55 switch (instr
->intrinsic
) {
56 case nir_intrinsic_image_deref_store
:
57 case nir_intrinsic_image_deref_atomic_add
:
58 case nir_intrinsic_image_deref_atomic_imin
:
59 case nir_intrinsic_image_deref_atomic_umin
:
60 case nir_intrinsic_image_deref_atomic_imax
:
61 case nir_intrinsic_image_deref_atomic_umax
:
62 case nir_intrinsic_image_deref_atomic_and
:
63 case nir_intrinsic_image_deref_atomic_or
:
64 case nir_intrinsic_image_deref_atomic_xor
:
65 case nir_intrinsic_image_deref_atomic_exchange
:
66 case nir_intrinsic_image_deref_atomic_comp_swap
:
67 case nir_intrinsic_image_deref_atomic_fadd
:
68 var
= nir_intrinsic_get_var(instr
, 0);
70 /* In OpenGL, buffer images use normal buffer objects, whereas other
71 * image types use textures which cannot alias with buffer objects.
72 * Therefore we have to group buffer samplers together with SSBO's.
74 if (glsl_get_sampler_dim(glsl_without_array(var
->type
)) ==
76 state
->buffers_written
= true;
78 state
->images_written
= true;
80 if (var
->data
.mode
== nir_var_uniform
)
81 _mesa_set_add(state
->vars_written
, var
);
84 case nir_intrinsic_bindless_image_store
:
85 case nir_intrinsic_bindless_image_atomic_add
:
86 case nir_intrinsic_bindless_image_atomic_imin
:
87 case nir_intrinsic_bindless_image_atomic_umin
:
88 case nir_intrinsic_bindless_image_atomic_imax
:
89 case nir_intrinsic_bindless_image_atomic_umax
:
90 case nir_intrinsic_bindless_image_atomic_and
:
91 case nir_intrinsic_bindless_image_atomic_or
:
92 case nir_intrinsic_bindless_image_atomic_xor
:
93 case nir_intrinsic_bindless_image_atomic_exchange
:
94 case nir_intrinsic_bindless_image_atomic_comp_swap
:
95 case nir_intrinsic_bindless_image_atomic_fadd
:
96 if (nir_intrinsic_image_dim(instr
) == GLSL_SAMPLER_DIM_BUF
)
97 state
->buffers_written
= true;
99 state
->images_written
= true;
102 case nir_intrinsic_store_deref
:
103 case nir_intrinsic_deref_atomic_add
:
104 case nir_intrinsic_deref_atomic_imin
:
105 case nir_intrinsic_deref_atomic_umin
:
106 case nir_intrinsic_deref_atomic_imax
:
107 case nir_intrinsic_deref_atomic_umax
:
108 case nir_intrinsic_deref_atomic_and
:
109 case nir_intrinsic_deref_atomic_or
:
110 case nir_intrinsic_deref_atomic_xor
:
111 case nir_intrinsic_deref_atomic_exchange
:
112 case nir_intrinsic_deref_atomic_comp_swap
:
113 case nir_intrinsic_deref_atomic_fadd
:
114 case nir_intrinsic_deref_atomic_fmin
:
115 case nir_intrinsic_deref_atomic_fmax
:
116 case nir_intrinsic_deref_atomic_fcomp_swap
:
117 var
= nir_intrinsic_get_var(instr
, 0);
118 if (var
->data
.mode
!= nir_var_mem_ssbo
)
121 _mesa_set_add(state
->vars_written
, var
);
122 state
->buffers_written
= true;
124 case nir_intrinsic_memory_barrier
:
125 state
->buffer_barriers
= true;
126 state
->image_barriers
= true;
129 case nir_intrinsic_memory_barrier_buffer
:
130 state
->buffer_barriers
= true;
133 case nir_intrinsic_memory_barrier_image
:
134 state
->image_barriers
= true;
143 process_variable(struct access_state
*state
, nir_variable
*var
)
145 if (var
->data
.mode
!= nir_var_mem_ssbo
&&
146 !(var
->data
.mode
== nir_var_uniform
&&
147 glsl_type_is_image(var
->type
)))
150 /* Ignore variables we've already marked */
151 if (var
->data
.image
.access
& ACCESS_CAN_REORDER
)
154 if (!(var
->data
.image
.access
& ACCESS_NON_WRITEABLE
) &&
155 !_mesa_set_search(state
->vars_written
, var
)) {
156 var
->data
.image
.access
|= ACCESS_NON_WRITEABLE
;
164 can_reorder(struct access_state
*state
, enum gl_access_qualifier access
,
165 bool is_buffer
, bool is_ssbo
)
167 bool is_any_written
= is_buffer
? state
->buffers_written
:
168 state
->images_written
;
170 /* Can we guarantee that the underlying memory is never written? */
171 if (!is_any_written
||
172 ((access
& ACCESS_NON_WRITEABLE
) &&
173 (access
& ACCESS_RESTRICT
))) {
174 /* Note: memoryBarrierBuffer() is only guaranteed to flush buffer
175 * variables and not imageBuffer's, so we only consider the GL-level
178 bool is_any_barrier
= is_ssbo
?
179 state
->buffer_barriers
: state
->image_barriers
;
181 return (!is_any_barrier
|| !(access
& ACCESS_COHERENT
)) &&
182 !(access
& ACCESS_VOLATILE
);
189 process_intrinsic(struct access_state
*state
, nir_intrinsic_instr
*instr
)
191 switch (instr
->intrinsic
) {
192 case nir_intrinsic_bindless_image_load
:
193 if (nir_intrinsic_access(instr
) & ACCESS_CAN_REORDER
)
196 /* We have less information about bindless intrinsics, since we can't
197 * always trace uses back to the variable. Don't try and infer if it's
198 * read-only, unless there are no image writes at all.
200 bool progress
= false;
202 nir_intrinsic_image_dim(instr
) == GLSL_SAMPLER_DIM_BUF
;
204 bool is_any_written
=
205 is_buffer
? state
->buffers_written
: state
->images_written
;
207 if (!(nir_intrinsic_access(instr
) & ACCESS_NON_WRITEABLE
) &&
210 nir_intrinsic_set_access(instr
,
211 nir_intrinsic_access(instr
) |
212 ACCESS_NON_WRITEABLE
);
215 if (can_reorder(state
, nir_intrinsic_access(instr
), is_buffer
, false)) {
217 nir_intrinsic_set_access(instr
,
218 nir_intrinsic_access(instr
) |
224 case nir_intrinsic_load_deref
:
225 case nir_intrinsic_image_deref_load
: {
226 nir_variable
*var
= nir_intrinsic_get_var(instr
, 0);
228 if (instr
->intrinsic
== nir_intrinsic_load_deref
&&
229 var
->data
.mode
!= nir_var_mem_ssbo
)
232 if (nir_intrinsic_access(instr
) & ACCESS_CAN_REORDER
)
235 bool progress
= false;
237 /* Check if we were able to mark the whole variable non-writeable */
238 if (!(nir_intrinsic_access(instr
) & ACCESS_NON_WRITEABLE
) &&
239 var
->data
.image
.access
& ACCESS_NON_WRITEABLE
) {
241 nir_intrinsic_set_access(instr
,
242 nir_intrinsic_access(instr
) |
243 ACCESS_NON_WRITEABLE
);
246 bool is_ssbo
= var
->data
.mode
== nir_var_mem_ssbo
;
248 bool is_buffer
= is_ssbo
||
249 glsl_get_sampler_dim(glsl_without_array(var
->type
)) == GLSL_SAMPLER_DIM_BUF
;
251 if (can_reorder(state
, nir_intrinsic_access(instr
), is_buffer
, is_ssbo
)) {
253 nir_intrinsic_set_access(instr
,
254 nir_intrinsic_access(instr
) |
267 opt_access_impl(struct access_state
*state
,
268 nir_function_impl
*impl
)
270 bool progress
= false;
272 nir_foreach_block(block
, impl
) {
273 nir_foreach_instr(instr
, block
) {
274 if (instr
->type
== nir_instr_type_intrinsic
)
275 progress
|= process_intrinsic(state
,
276 nir_instr_as_intrinsic(instr
));
281 nir_metadata_preserve(impl
,
282 nir_metadata_block_index
|
283 nir_metadata_dominance
|
284 nir_metadata_live_ssa_defs
|
285 nir_metadata_loop_analysis
);
293 nir_opt_access(nir_shader
*shader
)
295 struct access_state state
= {
296 .vars_written
= _mesa_pointer_set_create(NULL
),
299 bool var_progress
= false;
300 bool progress
= false;
302 nir_foreach_function(func
, shader
) {
304 nir_foreach_block(block
, func
->impl
) {
305 nir_foreach_instr(instr
, block
) {
306 if (instr
->type
== nir_instr_type_intrinsic
)
307 gather_intrinsic(&state
, nir_instr_as_intrinsic(instr
));
313 nir_foreach_variable(var
, &shader
->uniforms
)
314 var_progress
|= process_variable(&state
, var
);
316 nir_foreach_function(func
, shader
) {
318 progress
|= opt_access_impl(&state
, func
->impl
);
320 /* If we make a change to the uniforms, update all the impls. */
322 nir_metadata_preserve(func
->impl
,
323 nir_metadata_block_index
|
324 nir_metadata_dominance
|
325 nir_metadata_live_ssa_defs
|
326 nir_metadata_loop_analysis
);
331 progress
|= var_progress
;
333 _mesa_set_destroy(state
.vars_written
, NULL
);