• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Valve Corpoation
3  * Copyright 2020 Raspberry Pi Ltd
4  * SPDX-License-Identifier: MIT
5  */
6 
7 #include "nir.h"
8 #include "nir_builder.h"
9 #include "nir_intrinsics_indices.h"
10 
11 static void
rewrite_offset(nir_builder * b,nir_intrinsic_instr * instr,uint32_t type_sz,uint32_t offset_src,nir_def * size)12 rewrite_offset(nir_builder *b, nir_intrinsic_instr *instr,
13                uint32_t type_sz, uint32_t offset_src, nir_def *size)
14 {
15    /* Compute the maximum offset being accessed and if it is out of bounds
16     * rewrite it to 0 to ensure the access is within bounds.
17     */
18    const uint32_t access_size = instr->num_components * type_sz;
19    nir_def *max_access_offset =
20       nir_iadd_imm(b, instr->src[offset_src].ssa, access_size - 1);
21    nir_def *offset =
22       nir_bcsel(b, nir_uge(b, max_access_offset, size), nir_imm_int(b, 0),
23                 instr->src[offset_src].ssa);
24 
25    /* Rewrite offset */
26    nir_src_rewrite(&instr->src[offset_src], offset);
27 }
28 
29 /*
30  * Wrap a intrinsic in an if, predicated on a "valid" condition. If the
31  * intrinsic produces a destination, it will be zero in the invalid case.
32  */
33 static void
wrap_in_if(nir_builder * b,nir_intrinsic_instr * instr,nir_def * valid)34 wrap_in_if(nir_builder *b, nir_intrinsic_instr *instr, nir_def *valid)
35 {
36    bool has_dest = nir_intrinsic_infos[instr->intrinsic].has_dest;
37    nir_def *res, *zero;
38 
39    if (has_dest) {
40       zero = nir_imm_zero(b, instr->def.num_components,
41                           instr->def.bit_size);
42    }
43 
44    nir_push_if(b, valid);
45    {
46       nir_instr *orig = nir_instr_clone(b->shader, &instr->instr);
47       nir_builder_instr_insert(b, orig);
48 
49       if (has_dest)
50          res = &nir_instr_as_intrinsic(orig)->def;
51    }
52    nir_pop_if(b, NULL);
53 
54    if (has_dest)
55       nir_def_rewrite_uses(&instr->def, nir_if_phi(b, res, zero));
56 
57    /* We've cloned and wrapped, so drop original instruction */
58    nir_instr_remove(&instr->instr);
59 }
60 
61 static void
lower_buffer_load(nir_builder * b,nir_intrinsic_instr * instr)62 lower_buffer_load(nir_builder *b, nir_intrinsic_instr *instr)
63 {
64    uint32_t type_sz = instr->def.bit_size / 8;
65    nir_def *size;
66    nir_def *index = instr->src[0].ssa;
67 
68    if (instr->intrinsic == nir_intrinsic_load_ubo) {
69       size = nir_get_ubo_size(b, 32, index);
70    } else {
71       size = nir_get_ssbo_size(b, index);
72    }
73 
74    rewrite_offset(b, instr, type_sz, 1, size);
75 }
76 
77 static void
lower_buffer_store(nir_builder * b,nir_intrinsic_instr * instr)78 lower_buffer_store(nir_builder *b, nir_intrinsic_instr *instr)
79 {
80    uint32_t type_sz = nir_src_bit_size(instr->src[0]) / 8;
81    rewrite_offset(b, instr, type_sz, 2,
82                   nir_get_ssbo_size(b, instr->src[1].ssa));
83 }
84 
85 static void
lower_buffer_atomic(nir_builder * b,nir_intrinsic_instr * instr)86 lower_buffer_atomic(nir_builder *b, nir_intrinsic_instr *instr)
87 {
88    rewrite_offset(b, instr, 4, 1, nir_get_ssbo_size(b, instr->src[0].ssa));
89 }
90 
91 static void
lower_buffer_shared(nir_builder * b,nir_intrinsic_instr * instr)92 lower_buffer_shared(nir_builder *b, nir_intrinsic_instr *instr)
93 {
94    uint32_t type_sz, offset_src;
95    if (instr->intrinsic == nir_intrinsic_load_shared) {
96       offset_src = 0;
97       type_sz = instr->def.bit_size / 8;
98    } else if (instr->intrinsic == nir_intrinsic_store_shared) {
99       offset_src = 1;
100       type_sz = nir_src_bit_size(instr->src[0]) / 8;
101    } else {
102       /* atomic */
103       offset_src = 0;
104       type_sz = 4;
105    }
106 
107    rewrite_offset(b, instr, type_sz, offset_src,
108                   nir_imm_int(b, b->shader->info.shared_size));
109 }
110 
111 static void
lower_image(nir_builder * b,nir_intrinsic_instr * instr,bool deref)112 lower_image(nir_builder *b, nir_intrinsic_instr *instr, bool deref)
113 {
114    enum glsl_sampler_dim dim = nir_intrinsic_image_dim(instr);
115    uint32_t num_coords = nir_image_intrinsic_coord_components(instr);
116    bool is_array = nir_intrinsic_image_array(instr);
117    nir_def *coord = instr->src[1].ssa;
118 
119    /* Get image size. imageSize for cubes returns the size of a single face. */
120    unsigned size_components = num_coords;
121    if (dim == GLSL_SAMPLER_DIM_CUBE && !is_array)
122       size_components -= 1;
123 
124    nir_def *size = nir_image_size(b, size_components, 32,
125                                   instr->src[0].ssa, nir_imm_int(b, 0),
126                                   .image_array = is_array, .image_dim = dim);
127    if (deref) {
128       nir_instr_as_intrinsic(size->parent_instr)->intrinsic =
129          nir_intrinsic_image_deref_size;
130    }
131 
132    if (dim == GLSL_SAMPLER_DIM_CUBE) {
133       nir_def *z = is_array ? nir_imul_imm(b, nir_channel(b, size, 2), 6)
134                             : nir_imm_int(b, 6);
135 
136       size = nir_vec3(b, nir_channel(b, size, 0), nir_channel(b, size, 1), z);
137    }
138 
139    nir_def *in_bounds = nir_ball(b, nir_ult(b, coord, size));
140 
141    if (dim == GLSL_SAMPLER_DIM_MS) {
142       nir_def *sample = instr->src[2].ssa;
143       nir_def *samples = nir_image_samples(b, 32, instr->src[0].ssa,
144                                            .image_array = is_array, .image_dim = dim);
145       if (deref) {
146          nir_instr_as_intrinsic(samples->parent_instr)->intrinsic =
147             nir_intrinsic_image_deref_samples;
148       }
149 
150       in_bounds = nir_iand(b, in_bounds, nir_ult(b, sample, samples));
151    }
152 
153    /* Only execute if coordinates are in-bounds. Otherwise, return zero. */
154    wrap_in_if(b, instr, in_bounds);
155 }
156 
157 struct pass_opts {
158    nir_intrin_filter_cb filter;
159    const void *data;
160 };
161 
162 static bool
lower(nir_builder * b,nir_intrinsic_instr * intr,void * _opts)163 lower(nir_builder *b, nir_intrinsic_instr *intr, void *_opts)
164 {
165    const struct pass_opts *opts = _opts;
166    if (!opts->filter(intr, opts->data))
167       return false;
168 
169    b->cursor = nir_before_instr(&intr->instr);
170 
171    switch (intr->intrinsic) {
172    case nir_intrinsic_image_load:
173    case nir_intrinsic_image_store:
174    case nir_intrinsic_image_atomic:
175    case nir_intrinsic_image_atomic_swap:
176       lower_image(b, intr, false);
177       return true;
178 
179    case nir_intrinsic_image_deref_load:
180    case nir_intrinsic_image_deref_store:
181    case nir_intrinsic_image_deref_atomic:
182    case nir_intrinsic_image_deref_atomic_swap:
183       lower_image(b, intr, true);
184       return true;
185 
186    case nir_intrinsic_load_ubo:
187    case nir_intrinsic_load_ssbo:
188       lower_buffer_load(b, intr);
189       return true;
190    case nir_intrinsic_store_ssbo:
191       lower_buffer_store(b, intr);
192       return true;
193    case nir_intrinsic_ssbo_atomic:
194    case nir_intrinsic_ssbo_atomic_swap:
195       lower_buffer_atomic(b, intr);
196       return true;
197 
198    case nir_intrinsic_store_shared:
199    case nir_intrinsic_load_shared:
200    case nir_intrinsic_shared_atomic:
201    case nir_intrinsic_shared_atomic_swap:
202       /* Vulkan's robustBufferAccess feature is only concerned with buffers that
203        * are bound through descriptor sets, so shared memory is not included,
204        * but this lowering may be useful for debugging.
205        */
206       lower_buffer_shared(b, intr);
207       return true;
208 
209    default:
210       unreachable("driver requested lowering for unsupported intrinsic");
211    }
212 }
213 
214 /*
215  * Buffer/image robustness lowering with robustBufferAccess/robustImageAccess
216  * semantics. This is sufficient for GL, but not for D3D. However, Vulkan
217  * drivers get buffer robustness lowered via nir_lower_explicit_io.
218  */
219 bool
nir_lower_robust_access(nir_shader * s,nir_intrin_filter_cb filter,const void * data)220 nir_lower_robust_access(nir_shader *s, nir_intrin_filter_cb filter,
221                         const void *data)
222 {
223    struct pass_opts opt = { .filter = filter, .data = data };
224    return nir_shader_intrinsics_pass(s, lower, nir_metadata_control_flow, &opt);
225 }
226