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