1 /*
2 * Copyright (c) 2021 Lima Project
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 #include "nir_builder.h"
26 #include "lima_ir.h"
27
28 static nir_ssa_def *
get_proj_index(nir_instr * coord_instr,nir_instr * proj_instr,int coord_components,int * proj_idx)29 get_proj_index(nir_instr *coord_instr, nir_instr *proj_instr,
30 int coord_components, int *proj_idx)
31 {
32 *proj_idx = -1;
33 if (coord_instr->type != nir_instr_type_alu ||
34 proj_instr->type != nir_instr_type_alu)
35 return NULL;
36
37 nir_alu_instr *coord_alu = nir_instr_as_alu(coord_instr);
38 nir_alu_instr *proj_alu = nir_instr_as_alu(proj_instr);
39
40 if (coord_alu->op != nir_op_mov ||
41 proj_alu->op != nir_op_mov)
42 return NULL;
43
44 if (!coord_alu->dest.dest.is_ssa ||
45 !proj_alu->dest.dest.is_ssa)
46 return NULL;
47
48 if (!coord_alu->src[0].src.is_ssa ||
49 !proj_alu->src[0].src.is_ssa)
50 return NULL;
51
52 nir_ssa_def *coord_src_ssa = coord_alu->src[0].src.ssa;
53 nir_ssa_def *proj_src_ssa = proj_alu->src[0].src.ssa;
54
55 if (coord_src_ssa != proj_src_ssa)
56 return NULL;
57
58 if (coord_src_ssa->parent_instr->type != nir_instr_type_intrinsic)
59 return NULL;
60
61 nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(coord_src_ssa->parent_instr);
62 if (intrin->intrinsic != nir_intrinsic_load_input)
63 return NULL;
64
65 if (nir_dest_num_components(intrin->dest) != 4)
66 return NULL;
67
68 /* Coords must be in .xyz */
69 for (int i = 0; i < coord_components; i++) {
70 if (coord_alu->src[0].swizzle[i] != i)
71 return NULL;
72 }
73
74 *proj_idx = proj_alu->src[0].swizzle[0];
75
76 return coord_src_ssa;
77 }
78
79 static bool
lima_nir_lower_txp_instr(nir_builder * b,nir_instr * instr,UNUSED void * cb_data)80 lima_nir_lower_txp_instr(nir_builder *b, nir_instr *instr,
81 UNUSED void *cb_data)
82 {
83 if (instr->type != nir_instr_type_tex)
84 return false;
85
86 nir_tex_instr *tex = nir_instr_as_tex(instr);
87
88 int proj_idx = nir_tex_instr_src_index(tex, nir_tex_src_projector);
89 int coords_idx = nir_tex_instr_src_index(tex, nir_tex_src_coord);
90
91 if (proj_idx < 0)
92 return false;
93
94 switch (tex->sampler_dim) {
95 case GLSL_SAMPLER_DIM_RECT:
96 case GLSL_SAMPLER_DIM_1D:
97 case GLSL_SAMPLER_DIM_2D:
98 case GLSL_SAMPLER_DIM_3D:
99 break;
100 default:
101 return false;
102 }
103
104 b->cursor = nir_before_instr(&tex->instr);
105
106 /* Merge coords and projector into single backend-specific source.
107 * It's easy if texture2DProj argument is vec3, it's more tricky with
108 * vec4 since NIR just drops Z component that we need, so we have to
109 * step back and use load_input SSA instead of mov as a source for
110 * newly constructed vec4
111 */
112 nir_ssa_def *proj_ssa = nir_ssa_for_src(b, tex->src[proj_idx].src, 1);
113 nir_ssa_def *coords_ssa = nir_ssa_for_src(b, tex->src[coords_idx].src,
114 nir_tex_instr_src_size(tex, coords_idx));
115
116 int proj_idx_in_vec = -1;
117 nir_ssa_def *load_input = get_proj_index(coords_ssa->parent_instr,
118 proj_ssa->parent_instr,
119 tex->coord_components,
120 &proj_idx_in_vec);
121 nir_ssa_def *combined;
122 if (load_input && proj_idx_in_vec == 3) {
123 unsigned xyzw[] = { 0, 1, 2, 3 };
124 combined = nir_swizzle(b, load_input, xyzw, 4);
125 tex->coord_components = 4;
126 } else if (load_input && proj_idx_in_vec == 2) {
127 unsigned xyz[] = { 0, 1, 2 };
128 combined = nir_swizzle(b, load_input, xyz, 3);
129 tex->coord_components = 3;
130 } else {
131 switch (tex->coord_components) {
132 default:
133 case 1:
134 /* We still need vec3 for 1D textures, so duplicate coordinate */
135 combined = nir_vec3(b,
136 nir_channel(b, coords_ssa, 0),
137 nir_channel(b, coords_ssa, 0),
138 nir_channel(b, proj_ssa, 0));
139 tex->coord_components = 3;
140 break;
141 case 2:
142 combined = nir_vec3(b,
143 nir_channel(b, coords_ssa, 0),
144 nir_channel(b, coords_ssa, 1),
145 nir_channel(b, proj_ssa, 0));
146 tex->coord_components = 3;
147 break;
148 case 3:
149 combined = nir_vec4(b,
150 nir_channel(b, coords_ssa, 0),
151 nir_channel(b, coords_ssa, 1),
152 nir_channel(b, coords_ssa, 2),
153 nir_channel(b, proj_ssa, 0));
154 tex->coord_components = 4;
155 }
156 }
157
158 nir_tex_instr_remove_src(tex, nir_tex_instr_src_index(tex, nir_tex_src_coord));
159 nir_tex_instr_remove_src(tex, nir_tex_instr_src_index(tex, nir_tex_src_projector));
160 nir_tex_instr_add_src(tex, nir_tex_src_backend1, nir_src_for_ssa(combined));
161
162 return true;
163 }
164
165 bool
lima_nir_lower_txp(nir_shader * shader)166 lima_nir_lower_txp(nir_shader *shader)
167 {
168 return nir_shader_instructions_pass(shader, lima_nir_lower_txp_instr,
169 nir_metadata_block_index |
170 nir_metadata_dominance,
171 NULL);
172 }
173