ec316a8947ecb06d1f6e6e7d28bf62f2dd880d2d
[mesa.git] / src / compiler / nir / nir_opt_access.c
1 /*
2 * Copyright © 2019 Valve Corporation
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
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "nir.h"
25
26 /* This pass optimizes GL access qualifiers. So far it does two things:
27 *
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
32 * memory.
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.
37 *
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.
41 */
42
43 struct access_state {
44 struct set *vars_written;
45 bool images_written;
46 bool buffers_written;
47 bool image_barriers;
48 bool buffer_barriers;
49 };
50
51 static void
52 gather_intrinsic(struct access_state *state, nir_intrinsic_instr *instr)
53 {
54 nir_variable *var;
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);
69
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.
73 */
74 if (glsl_get_sampler_dim(glsl_without_array(var->type)) ==
75 GLSL_SAMPLER_DIM_BUF)
76 state->buffers_written = true;
77 else
78 state->images_written = true;
79
80 if (var->data.mode == nir_var_uniform)
81 _mesa_set_add(state->vars_written, var);
82 break;
83
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;
98 else
99 state->images_written = true;
100 break;
101
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)
119 break;
120
121 _mesa_set_add(state->vars_written, var);
122 state->buffers_written = true;
123
124 case nir_intrinsic_memory_barrier:
125 state->buffer_barriers = true;
126 state->image_barriers = true;
127 break;
128
129 case nir_intrinsic_memory_barrier_buffer:
130 state->buffer_barriers = true;
131 break;
132
133 case nir_intrinsic_memory_barrier_image:
134 state->image_barriers = true;
135 break;
136
137 default:
138 break;
139 }
140 }
141
142 static bool
143 process_variable(struct access_state *state, nir_variable *var)
144 {
145 if (var->data.mode != nir_var_mem_ssbo &&
146 !(var->data.mode == nir_var_uniform &&
147 glsl_type_is_image(var->type)))
148 return false;
149
150 /* Ignore variables we've already marked */
151 if (var->data.image.access & ACCESS_CAN_REORDER)
152 return false;
153
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;
157 return true;
158 }
159
160 return false;
161 }
162
163 static bool
164 can_reorder(struct access_state *state, enum gl_access_qualifier access,
165 bool is_buffer, bool is_ssbo)
166 {
167 bool is_any_written = is_buffer ? state->buffers_written :
168 state->images_written;
169
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
176 * type here.
177 */
178 bool is_any_barrier = is_ssbo ?
179 state->buffer_barriers : state->image_barriers;
180
181 return (!is_any_barrier || !(access & ACCESS_COHERENT)) &&
182 !(access & ACCESS_VOLATILE);
183 }
184
185 return false;
186 }
187
188 static bool
189 process_intrinsic(struct access_state *state, nir_intrinsic_instr *instr)
190 {
191 switch (instr->intrinsic) {
192 case nir_intrinsic_bindless_image_load:
193 if (nir_intrinsic_access(instr) & ACCESS_CAN_REORDER)
194 return false;
195
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.
199 */
200 bool progress = false;
201 bool is_buffer =
202 nir_intrinsic_image_dim(instr) == GLSL_SAMPLER_DIM_BUF;
203
204 bool is_any_written =
205 is_buffer ? state->buffers_written : state->images_written;
206
207 if (!(nir_intrinsic_access(instr) & ACCESS_NON_WRITEABLE) &&
208 !is_any_written) {
209 progress = true;
210 nir_intrinsic_set_access(instr,
211 nir_intrinsic_access(instr) |
212 ACCESS_NON_WRITEABLE);
213 }
214
215 if (can_reorder(state, nir_intrinsic_access(instr), is_buffer, false)) {
216 progress = true;
217 nir_intrinsic_set_access(instr,
218 nir_intrinsic_access(instr) |
219 ACCESS_CAN_REORDER);
220 }
221
222 return progress;
223
224 case nir_intrinsic_load_deref:
225 case nir_intrinsic_image_deref_load: {
226 nir_variable *var = nir_intrinsic_get_var(instr, 0);
227
228 if (instr->intrinsic == nir_intrinsic_load_deref &&
229 var->data.mode != nir_var_mem_ssbo)
230 return false;
231
232 if (nir_intrinsic_access(instr) & ACCESS_CAN_REORDER)
233 return false;
234
235 bool progress = false;
236
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) {
240 progress = true;
241 nir_intrinsic_set_access(instr,
242 nir_intrinsic_access(instr) |
243 ACCESS_NON_WRITEABLE);
244 }
245
246 bool is_ssbo = var->data.mode == nir_var_mem_ssbo;
247
248 bool is_buffer = is_ssbo ||
249 glsl_get_sampler_dim(glsl_without_array(var->type)) == GLSL_SAMPLER_DIM_BUF;
250
251 if (can_reorder(state, nir_intrinsic_access(instr), is_buffer, is_ssbo)) {
252 progress = true;
253 nir_intrinsic_set_access(instr,
254 nir_intrinsic_access(instr) |
255 ACCESS_CAN_REORDER);
256 }
257
258 return progress;
259 }
260
261 default:
262 return false;
263 }
264 }
265
266 static bool
267 opt_access_impl(struct access_state *state,
268 nir_function_impl *impl)
269 {
270 bool progress = false;
271
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));
277 }
278 }
279
280 if (progress) {
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);
286 }
287
288
289 return progress;
290 }
291
292 bool
293 nir_opt_access(nir_shader *shader)
294 {
295 struct access_state state = {
296 .vars_written = _mesa_pointer_set_create(NULL),
297 };
298
299 bool var_progress = false;
300 bool progress = false;
301
302 nir_foreach_function(func, shader) {
303 if (func->impl) {
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));
308 }
309 }
310 }
311 }
312
313 nir_foreach_variable(var, &shader->uniforms)
314 var_progress |= process_variable(&state, var);
315
316 nir_foreach_function(func, shader) {
317 if (func->impl) {
318 progress |= opt_access_impl(&state, func->impl);
319
320 /* If we make a change to the uniforms, update all the impls. */
321 if (var_progress) {
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);
327 }
328 }
329 }
330
331 progress |= var_progress;
332
333 _mesa_set_destroy(state.vars_written, NULL);
334 return progress;
335 }