1 /*
2 * Copyright © 2024 Intel 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 "compiler/nir/nir_builder.h"
25 #include "intel_nir.h"
26
27 /**
28 * Pack either the explicit LOD or LOD bias and the array index together.
29 */
30 static bool
pack_lod_and_array_index(nir_builder * b,nir_tex_instr * tex)31 pack_lod_and_array_index(nir_builder *b, nir_tex_instr *tex)
32 {
33 /* If 32-bit texture coordinates are used, pack either the explicit LOD or
34 * LOD bias and the array index into a single (32-bit) value.
35 */
36 int lod_index = nir_tex_instr_src_index(tex, nir_tex_src_lod);
37 if (lod_index < 0) {
38 lod_index = nir_tex_instr_src_index(tex, nir_tex_src_bias);
39
40 /* The explicit LOD or LOD bias may not be found if this lowering has
41 * already occured. The explicit LOD may also not be found in some
42 * cases where it is zero.
43 */
44 if (lod_index < 0)
45 return false;
46 }
47
48 assert(nir_tex_instr_src_type(tex, lod_index) == nir_type_float);
49
50 /* Also do not perform this packing if the explicit LOD is zero. */
51 if (tex->op == nir_texop_txl &&
52 nir_src_is_const(tex->src[lod_index].src) &&
53 nir_src_as_float(tex->src[lod_index].src) == 0.0) {
54 return false;
55 }
56
57 const int coord_index = nir_tex_instr_src_index(tex, nir_tex_src_coord);
58 assert(coord_index >= 0);
59
60 nir_def *lod = tex->src[lod_index].src.ssa;
61 nir_def *coord = tex->src[coord_index].src.ssa;
62
63 assert(nir_tex_instr_src_type(tex, coord_index) == nir_type_float);
64
65 if (coord->bit_size < 32)
66 return false;
67
68 b->cursor = nir_before_instr(&tex->instr);
69
70 /* First, combine the two values. The packing format is a little weird.
71 * The explicit LOD / LOD bias is stored as float, as normal. However, the
72 * array index is converted to an integer and smashed into the low 9 bits.
73 */
74 const unsigned array_index = tex->coord_components - 1;
75
76 nir_def *clamped_ai =
77 nir_umin(b,
78 nir_f2u32(b, nir_fround_even(b, nir_channel(b, coord,
79 array_index))),
80 nir_imm_int(b, 511));
81
82 nir_def *lod_ai = nir_ior(b, nir_iand_imm(b, lod, 0xfffffe00), clamped_ai);
83
84 /* Second, replace the coordinate with a new value that has one fewer
85 * component (i.e., drop the array index).
86 */
87 nir_def *reduced_coord = nir_trim_vector(b, coord,
88 tex->coord_components - 1);
89 tex->coord_components--;
90
91 /* Finally, remove the old sources and add the new. */
92 nir_src_rewrite(&tex->src[coord_index].src, reduced_coord);
93
94 nir_tex_instr_remove_src(tex, lod_index);
95 nir_tex_instr_add_src(tex, nir_tex_src_backend1, lod_ai);
96
97 return true;
98 }
99
100 /**
101 * Pack either the explicit LOD/Bias and the offset together.
102 */
103 static bool
pack_lod_or_bias_and_offset(nir_builder * b,nir_tex_instr * tex)104 pack_lod_or_bias_and_offset(nir_builder *b, nir_tex_instr *tex)
105 {
106 int offset_index = nir_tex_instr_src_index(tex, nir_tex_src_offset);
107 if (offset_index < 0)
108 return false;
109
110 /* If 32-bit texture coordinates are used, pack either the explicit LOD or
111 * LOD bias and the array index into a single (32-bit) value.
112 */
113 int lod_index = nir_tex_instr_src_index(tex, nir_tex_src_lod);
114 if (lod_index < 0) {
115 lod_index = nir_tex_instr_src_index(tex, nir_tex_src_bias);
116
117 /* The explicit LOD or LOD bias may not be found if this lowering has
118 * already occured. The explicit LOD may also not be found in some
119 * cases where it is zero.
120 */
121 if (lod_index < 0)
122 return false;
123 }
124
125 assert(nir_tex_instr_src_type(tex, lod_index) == nir_type_float);
126
127 /* Also do not perform this packing if the explicit LOD is zero. */
128 if (nir_src_is_const(tex->src[lod_index].src) &&
129 nir_src_as_float(tex->src[lod_index].src) == 0.0) {
130 return false;
131 }
132
133 nir_def *lod = tex->src[lod_index].src.ssa;
134 nir_def *offset = tex->src[offset_index].src.ssa;
135
136 b->cursor = nir_before_instr(&tex->instr);
137
138 /* When using the programmable offsets instruction gather4_po_l_c with
139 * SIMD16 or SIMD32 the U, V offsets are combined with LOD/bias parameters
140 * on the 12 LSBs. For the offset parameters on gather instructions the 6
141 * least significant bits are honored as signed value with a range
142 * [-32..31].
143 *
144 * Pack Offset U, and V for texture gather with offsets.
145 *
146 * ------------------------------------------
147 * |Bits | [31:12] | [11:6] | [5:0] |
148 * ------------------------------------------
149 * |OffsetUV | LOD/Bias | OffsetV | OffsetU |
150 * ------------------------------------------
151 */
152 nir_def *offu = nir_iand_imm(b, nir_channel(b, offset, 0), 0x3F);
153 nir_def *offv = nir_iand_imm(b, nir_channel(b, offset, 1), 0x3F);
154
155 nir_def *offsetUV = nir_ior(b, offu, nir_ishl_imm(b, offv, 6));
156
157 nir_def *lod_offsetUV = nir_ior(b, offsetUV,
158 nir_iand_imm(b, lod, 0xFFFFF000));
159 nir_tex_instr_remove_src(tex, offset_index);
160 nir_tex_instr_add_src(tex, nir_tex_src_backend2, lod_offsetUV);
161
162 return true;
163 }
164
165 static bool
intel_nir_lower_texture_instr(nir_builder * b,nir_instr * instr,void * cb_data)166 intel_nir_lower_texture_instr(nir_builder *b, nir_instr *instr, void *cb_data)
167 {
168 if (instr->type != nir_instr_type_tex)
169 return false;
170
171 const struct intel_nir_lower_texture_opts *opts = cb_data;
172 nir_tex_instr *tex = nir_instr_as_tex(instr);
173
174 switch (tex->op) {
175 case nir_texop_txl:
176 case nir_texop_txb:
177 case nir_texop_tg4:
178 if (tex->is_array &&
179 tex->sampler_dim == GLSL_SAMPLER_DIM_CUBE &&
180 opts->combined_lod_and_array_index) {
181 return pack_lod_and_array_index(b, tex);
182 }
183
184 if (tex->op == nir_texop_tg4 && opts->combined_lod_or_bias_and_offset) {
185 return pack_lod_or_bias_and_offset(b, tex);
186 }
187
188 return false;
189 default:
190 /* Nothing to do */
191 return false;
192 }
193
194 return false;
195 }
196
197 bool
intel_nir_lower_texture(nir_shader * shader,const struct intel_nir_lower_texture_opts * opts)198 intel_nir_lower_texture(nir_shader *shader,
199 const struct intel_nir_lower_texture_opts *opts)
200 {
201 return nir_shader_instructions_pass(shader,
202 intel_nir_lower_texture_instr,
203 nir_metadata_none,
204 (void *)opts);
205 }
206