1 /* -*- mesa-c++ -*-
2 *
3 * Copyright (c) 2021 Collabora LTD
4 *
5 * Author: Gert Wollny <gert.wollny@collabora.com>
6 *
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 * on the rights to use, copy, modify, merge, publish, distribute, sub
11 * license, and/or sell copies of the Software, and to permit persons to whom
12 * the Software is furnished to do so, subject to the following conditions:
13 *
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
16 * Software.
17 *
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 NON-INFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 * USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27
28 #include "sfn_nir.h"
29
30 #include "nir.h"
31 #include "nir_builder.h"
32
33
34 static nir_ssa_def *
r600_legalize_image_load_store_impl(nir_builder * b,nir_instr * instr,UNUSED void * _options)35 r600_legalize_image_load_store_impl(nir_builder *b, nir_instr *instr,
36 UNUSED void *_options)
37 {
38 b->cursor = nir_before_instr(instr);
39 auto ir = nir_instr_as_intrinsic(instr);
40
41 nir_ssa_def *default_value = nir_imm_vec4(b, 0.0, 0.0, 0.0, 0.0);
42
43 nir_ssa_def *result = NIR_LOWER_INSTR_PROGRESS_REPLACE;
44
45 bool load_value = ir->intrinsic != nir_intrinsic_image_store;
46
47 if (load_value)
48 default_value = nir_imm_zero(b, nir_dest_num_components(ir->dest),
49 nir_dest_bit_size(ir->dest));
50
51 auto image_exists = nir_ult(b, ir->src[0].ssa, nir_imm_int(b, b->shader->info.num_images));
52
53 nir_if *if_exists = nir_push_if(b, image_exists);
54
55 nir_if *load_if = nullptr;
56
57 if (ir->intrinsic != nir_intrinsic_image_size) {
58
59 /* Image exists start */
60 auto new_index = nir_umin(b, ir->src[0].ssa,
61 nir_imm_int(b, b->shader->info.num_images - 1));
62 nir_instr_rewrite_src_ssa(instr, &ir->src[0], new_index);
63
64 enum glsl_sampler_dim dim = nir_intrinsic_image_dim(ir);
65
66 unsigned num_components = 2;
67 switch (dim) {
68 case GLSL_SAMPLER_DIM_BUF:
69 case GLSL_SAMPLER_DIM_1D:
70 num_components = 1; break;
71 case GLSL_SAMPLER_DIM_2D:
72 case GLSL_SAMPLER_DIM_RECT:
73 case GLSL_SAMPLER_DIM_CUBE:
74 num_components = 2; break;
75 case GLSL_SAMPLER_DIM_3D:
76 num_components = 3; break;
77 default:
78 unreachable("Unexpected image size");
79 }
80
81 if (num_components < 3 && nir_intrinsic_image_array(ir))
82 num_components++;
83
84 auto img_size = nir_image_size(b, num_components, 32, ir->src[0].ssa, nir_imm_int(b, 0),
85 dim, nir_intrinsic_image_array(ir),
86 nir_intrinsic_format(ir),
87 nir_intrinsic_access(ir));
88
89 unsigned mask = (1 << num_components) - 1;
90 unsigned num_src1_comp = MIN2(ir->src[1].ssa->num_components, num_components);
91 unsigned src1_mask = (1 << num_src1_comp) - 1;
92
93 auto in_range = nir_ult(b,
94 nir_channels(b, ir->src[1].ssa, src1_mask),
95 nir_channels(b, img_size, mask));
96
97 switch (num_components) {
98 case 2: in_range = nir_iand(b, nir_channel(b, in_range, 0), nir_channel(b, in_range, 1)); break;
99 case 3: {
100 auto tmp = nir_iand(b, nir_channel(b, in_range, 0), nir_channel(b, in_range, 1));
101 in_range = nir_iand(b, tmp, nir_channel(b, in_range, 2));
102 break;
103 }
104 }
105
106 /* Access is in range start */
107 load_if = nir_push_if(b, in_range);
108 }
109
110 auto new_load = nir_instr_clone(b->shader, instr);
111 auto new_load_ir = nir_instr_as_intrinsic(new_load);
112
113 nir_builder_instr_insert(b, new_load);
114
115 if (load_value)
116 result = &new_load_ir->dest.ssa;
117
118 if (ir->intrinsic != nir_intrinsic_image_size) {
119 /* Access is out of range start */
120 nir_if *load_else = nir_push_else(b, load_if);
121
122 nir_pop_if(b, load_else);
123 /* End range check */
124
125 if (load_value)
126 result = nir_if_phi(b, result, default_value);
127 }
128
129 /* Start image doesn't exists */
130 nir_if *else_exists = nir_push_else(b, if_exists);
131
132 /* Nothing to do, default is already set */
133 nir_pop_if(b, else_exists);
134
135 if (load_value)
136 result = nir_if_phi(b, result, default_value);
137
138 if (load_value)
139 b->cursor = nir_after_instr(result->parent_instr);
140 else
141 b->cursor = nir_after_cf_node(&else_exists->cf_node);
142
143 return result;
144 }
145
146 static bool
r600_legalize_image_load_store_filter(const nir_instr * instr,UNUSED const void * _options)147 r600_legalize_image_load_store_filter(const nir_instr *instr,
148 UNUSED const void *_options)
149 {
150 if (instr->type != nir_instr_type_intrinsic)
151 return false;
152
153 auto ir = nir_instr_as_intrinsic(instr);
154 switch (ir->intrinsic) {
155 case nir_intrinsic_image_store:
156 case nir_intrinsic_image_load:
157 case nir_intrinsic_image_atomic_add:
158 case nir_intrinsic_image_atomic_and:
159 case nir_intrinsic_image_atomic_or:
160 case nir_intrinsic_image_atomic_xor:
161 case nir_intrinsic_image_atomic_exchange:
162 case nir_intrinsic_image_atomic_comp_swap:
163 case nir_intrinsic_image_atomic_umin:
164 case nir_intrinsic_image_atomic_umax:
165 case nir_intrinsic_image_atomic_imin:
166 case nir_intrinsic_image_atomic_imax:
167 case nir_intrinsic_image_size:
168 return true;
169 default:
170 return false;
171 }
172 }
173
174 /* This pass makes sure only existing images are accessd and
175 * the access is withing range, if not zero is returned by all
176 * image ops that return a value.
177 */
178 bool
r600_legalize_image_load_store(nir_shader * shader)179 r600_legalize_image_load_store(nir_shader *shader)
180 {
181 return nir_shader_lower_instructions(shader,
182 r600_legalize_image_load_store_filter,
183 r600_legalize_image_load_store_impl,
184 nullptr);
185 };
186