1 /*
2 * Copyright © Microsoft 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 "nir.h"
25 #include "nir_builder.h"
26 #include "d3d12_compiler.h"
27 #include "d3d12_nir_passes.h"
28 #include "dxil_nir.h"
29 #include "program/prog_statevars.h"
30
31 struct lower_state {
32 nir_variable *uniform; /* (1/w, 1/h, pt_sz, max_sz) */
33 nir_variable *pos_out;
34 nir_variable *psiz_out;
35 nir_variable *point_coord_out[9];
36 unsigned num_point_coords;
37 nir_variable *varying_out[VARYING_SLOT_MAX];
38
39 nir_ssa_def *point_dir_imm[4];
40 nir_ssa_def *point_coord_imm[4];
41
42 /* Current point primitive */
43 nir_ssa_def *point_pos;
44 nir_ssa_def *point_size;
45 nir_ssa_def *varying[VARYING_SLOT_MAX];
46 unsigned varying_write_mask[VARYING_SLOT_MAX];
47
48 bool sprite_origin_lower_left;
49 bool point_size_per_vertex;
50 bool aa_point;
51 };
52
53 static void
find_outputs(nir_shader * shader,struct lower_state * state)54 find_outputs(nir_shader *shader, struct lower_state *state)
55 {
56 nir_foreach_variable_with_modes(var, shader, nir_var_shader_out) {
57 switch (var->data.location) {
58 case VARYING_SLOT_POS:
59 state->pos_out = var;
60 break;
61 case VARYING_SLOT_PSIZ:
62 state->psiz_out = var;
63 break;
64 default:
65 state->varying_out[var->data.location] = var;
66 break;
67 }
68 }
69 }
70
71 static nir_ssa_def *
get_point_dir(nir_builder * b,struct lower_state * state,unsigned i)72 get_point_dir(nir_builder *b, struct lower_state *state, unsigned i)
73 {
74 if (state->point_dir_imm[0] == NULL) {
75 state->point_dir_imm[0] = nir_imm_vec2(b, -1, -1);
76 state->point_dir_imm[1] = nir_imm_vec2(b, -1, 1);
77 state->point_dir_imm[2] = nir_imm_vec2(b, 1, -1);
78 state->point_dir_imm[3] = nir_imm_vec2(b, 1, 1);
79 }
80
81 return state->point_dir_imm[i];
82 }
83
84 static nir_ssa_def *
get_point_coord(nir_builder * b,struct lower_state * state,unsigned i)85 get_point_coord(nir_builder *b, struct lower_state *state, unsigned i)
86 {
87 if (state->point_coord_imm[0] == NULL) {
88 if (state->sprite_origin_lower_left) {
89 state->point_coord_imm[0] = nir_imm_vec4(b, 0, 0, 0, 1);
90 state->point_coord_imm[1] = nir_imm_vec4(b, 0, 1, 0, 1);
91 state->point_coord_imm[2] = nir_imm_vec4(b, 1, 0, 0, 1);
92 state->point_coord_imm[3] = nir_imm_vec4(b, 1, 1, 0, 1);
93 } else {
94 state->point_coord_imm[0] = nir_imm_vec4(b, 0, 1, 0, 1);
95 state->point_coord_imm[1] = nir_imm_vec4(b, 0, 0, 0, 1);
96 state->point_coord_imm[2] = nir_imm_vec4(b, 1, 1, 0, 1);
97 state->point_coord_imm[3] = nir_imm_vec4(b, 1, 0, 0, 1);
98 }
99 }
100
101 return state->point_coord_imm[i];
102 }
103
104 /**
105 * scaled_point_size = pointSize * pos.w * ViewportSizeRcp
106 */
107 static void
get_scaled_point_size(nir_builder * b,struct lower_state * state,nir_ssa_def ** x,nir_ssa_def ** y)108 get_scaled_point_size(nir_builder *b, struct lower_state *state,
109 nir_ssa_def **x, nir_ssa_def **y)
110 {
111 /* State uniform contains: (1/ViewportWidth, 1/ViewportHeight, PointSize, MaxPointSize) */
112 nir_ssa_def *uniform = nir_load_var(b, state->uniform);
113 nir_ssa_def *point_size = state->point_size;
114
115 /* clamp point-size to valid range */
116 if (point_size && state->point_size_per_vertex) {
117 point_size = nir_fmax(b, point_size, nir_imm_float(b, 1.0f));
118 point_size = nir_fmin(b, point_size, nir_imm_float(b, D3D12_MAX_POINT_SIZE));
119 } else {
120 /* Use static point size (from uniform) if the shader output was not set */
121 point_size = nir_channel(b, uniform, 2);
122 }
123
124 point_size = nir_fmul(b, point_size, nir_channel(b, state->point_pos, 3));
125 *x = nir_fmul(b, point_size, nir_channel(b, uniform, 0));
126 *y = nir_fmul(b, point_size, nir_channel(b, uniform, 1));
127 }
128
129 static bool
lower_store(nir_intrinsic_instr * instr,nir_builder * b,struct lower_state * state)130 lower_store(nir_intrinsic_instr *instr, nir_builder *b, struct lower_state *state)
131 {
132 nir_deref_instr *deref = nir_src_as_deref(instr->src[0]);
133 if (nir_deref_mode_is(deref, nir_var_shader_out)) {
134 nir_variable *var = nir_deref_instr_get_variable(deref);
135
136 switch (var->data.location) {
137 case VARYING_SLOT_POS:
138 state->point_pos = instr->src[1].ssa;
139 break;
140 case VARYING_SLOT_PSIZ:
141 state->point_size = instr->src[1].ssa;
142 break;
143 default:
144 state->varying[var->data.location] = instr->src[1].ssa;
145 state->varying_write_mask[var->data.location] = nir_intrinsic_write_mask(instr);
146 break;
147 }
148
149 nir_instr_remove(&instr->instr);
150 return true;
151 }
152
153 return false;
154 }
155
156 static bool
lower_emit_vertex(nir_intrinsic_instr * instr,nir_builder * b,struct lower_state * state)157 lower_emit_vertex(nir_intrinsic_instr *instr, nir_builder *b, struct lower_state *state)
158 {
159 unsigned stream_id = nir_intrinsic_stream_id(instr);
160
161 nir_ssa_def *point_width, *point_height;
162 get_scaled_point_size(b, state, &point_width, &point_height);
163
164 nir_instr_remove(&instr->instr);
165
166 for (unsigned i = 0; i < 4; i++) {
167 /* All outputs need to be emitted for each vertex */
168 for (unsigned slot = 0; slot < VARYING_SLOT_MAX; ++slot) {
169 if (state->varying[slot] != NULL) {
170 nir_store_var(b, state->varying_out[slot], state->varying[slot],
171 state->varying_write_mask[slot]);
172 }
173 }
174
175 /* pos = scaled_point_size * point_dir + point_pos */
176 nir_ssa_def *point_dir = get_point_dir(b, state, i);
177 nir_ssa_def *pos = nir_vec4(b,
178 nir_ffma(b,
179 point_width,
180 nir_channel(b, point_dir, 0),
181 nir_channel(b, state->point_pos, 0)),
182 nir_ffma(b,
183 point_height,
184 nir_channel(b, point_dir, 1),
185 nir_channel(b, state->point_pos, 1)),
186 nir_channel(b, state->point_pos, 2),
187 nir_channel(b, state->point_pos, 3));
188 nir_store_var(b, state->pos_out, pos, 0xf);
189
190 /* point coord */
191 nir_ssa_def *point_coord = get_point_coord(b, state, i);
192 for (unsigned j = 0; j < state->num_point_coords; ++j)
193 nir_store_var(b, state->point_coord_out[j], point_coord, 0xf);
194
195 /* EmitVertex */
196 nir_emit_vertex(b, .stream_id = stream_id);
197 }
198
199 /* EndPrimitive */
200 nir_end_primitive(b, .stream_id = stream_id);
201
202 /* Reset everything */
203 state->point_pos = NULL;
204 state->point_size = NULL;
205 for (unsigned i = 0; i < VARYING_SLOT_MAX; ++i)
206 state->varying[i] = NULL;
207
208 return true;
209 }
210
211 static bool
lower_instr(nir_intrinsic_instr * instr,nir_builder * b,struct lower_state * state)212 lower_instr(nir_intrinsic_instr *instr, nir_builder *b, struct lower_state *state)
213 {
214 b->cursor = nir_before_instr(&instr->instr);
215
216 if (instr->intrinsic == nir_intrinsic_store_deref) {
217 return lower_store(instr, b, state);
218 } else if (instr->intrinsic == nir_intrinsic_emit_vertex) {
219 return lower_emit_vertex(instr, b, state);
220 } else if (instr->intrinsic == nir_intrinsic_end_primitive) {
221 nir_instr_remove(&instr->instr);
222 return true;
223 }
224
225 return false;
226 }
227
228 bool
d3d12_lower_point_sprite(nir_shader * shader,bool sprite_origin_lower_left,bool point_size_per_vertex,unsigned point_coord_enable,uint64_t next_inputs_read)229 d3d12_lower_point_sprite(nir_shader *shader,
230 bool sprite_origin_lower_left,
231 bool point_size_per_vertex,
232 unsigned point_coord_enable,
233 uint64_t next_inputs_read)
234 {
235 const gl_state_index16 tokens[4] = { STATE_INTERNAL_DRIVER,
236 D3D12_STATE_VAR_PT_SPRITE };
237 struct lower_state state;
238 bool progress = false;
239
240 assert(shader->info.gs.output_primitive == GL_POINTS);
241
242 memset(&state, 0, sizeof(state));
243 find_outputs(shader, &state);
244 state.sprite_origin_lower_left = sprite_origin_lower_left;
245 state.point_size_per_vertex = point_size_per_vertex;
246
247 /* Create uniform to retrieve inverse of viewport size and point size:
248 * (1/ViewportWidth, 1/ViewportHeight, PointSize, MaxPointSize) */
249 state.uniform = nir_variable_create(shader,
250 nir_var_uniform,
251 glsl_vec4_type(),
252 "d3d12_ViewportSizeRcp");
253 state.uniform->num_state_slots = 1;
254 state.uniform->state_slots = ralloc_array(state.uniform, nir_state_slot, 1);
255 memcpy(state.uniform->state_slots[0].tokens, tokens,
256 sizeof(state.uniform->state_slots[0].tokens));
257 shader->num_uniforms++;
258
259 /* Create new outputs for point tex coordinates */
260 unsigned count = 0;
261 for (unsigned int sem = 0; sem < 9; sem++) {
262 if (point_coord_enable & BITFIELD64_BIT(sem)) {
263 char tmp[100];
264 unsigned location = VARYING_SLOT_VAR0 + sem;
265
266 snprintf(tmp, ARRAY_SIZE(tmp), "gl_TexCoord%dMESA", count);
267
268 nir_variable *var = nir_variable_create(shader,
269 nir_var_shader_out,
270 glsl_vec4_type(),
271 tmp);
272 var->data.location = location;
273 state.point_coord_out[count++] = var;
274 }
275 }
276 state.num_point_coords = count;
277 if (point_coord_enable) {
278 dxil_reassign_driver_locations(shader, nir_var_shader_out,
279 next_inputs_read);
280 }
281
282 nir_foreach_function(function, shader) {
283 if (function->impl) {
284 nir_builder builder;
285 nir_builder_init(&builder, function->impl);
286 nir_foreach_block(block, function->impl) {
287 nir_foreach_instr_safe(instr, block) {
288 if (instr->type == nir_instr_type_intrinsic)
289 progress |= lower_instr(nir_instr_as_intrinsic(instr),
290 &builder,
291 &state);
292 }
293 }
294
295 nir_metadata_preserve(function->impl, nir_metadata_block_index |
296 nir_metadata_dominance);
297 }
298 }
299
300 shader->info.gs.output_primitive = GL_TRIANGLE_STRIP;
301 shader->info.gs.vertices_out *= 4;
302
303 return progress;
304 }
305