tu: Add multiview lowering pass
[mesa.git] / src / freedreno / vulkan / tu_nir_lower_multiview.c
1 /*
2 * Copyright © 2020 Valve Corporation
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "tu_private.h"
7 #include "nir_builder.h"
8
9 /* Some a6xx variants cannot support a non-contiguous multiview mask. Instead,
10 * inside the shader something like this needs to be inserted:
11 *
12 * gl_Position = ((1ull << gl_ViewIndex) & view_mask) ? gl_Position : vec4(0.);
13 *
14 * Scan backwards until we find the gl_Position write (there should only be
15 * one).
16 */
17 static bool
18 lower_multiview_mask(nir_function_impl *impl, uint32_t mask)
19 {
20 nir_builder b;
21 nir_builder_init(&b, impl);
22
23 nir_foreach_block_reverse(block, impl) {
24 nir_foreach_instr_reverse(instr, block) {
25 if (instr->type != nir_instr_type_intrinsic)
26 continue;
27
28 nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
29 if (intrin->intrinsic != nir_intrinsic_store_deref)
30 continue;
31
32 nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]);
33 if (deref->mode != nir_var_shader_out)
34 continue;
35
36 nir_variable *var = nir_deref_instr_get_variable(deref);
37 if (var->data.location != VARYING_SLOT_POS)
38 continue;
39
40 assert(intrin->src[1].is_ssa);
41 nir_ssa_def *orig_src = intrin->src[1].ssa;
42 b.cursor = nir_before_instr(instr);
43
44 /* ((1ull << gl_ViewIndex) & mask) != 0 */
45 nir_ssa_def *cmp =
46 nir_i2b(&b, nir_iand(&b, nir_imm_int(&b, mask),
47 nir_ishl(&b, nir_imm_int(&b, 1),
48 nir_load_view_index(&b))));
49
50 nir_ssa_def *src = nir_bcsel(&b, cmp, orig_src, nir_imm_float(&b, 0.));
51 nir_instr_rewrite_src(instr, &intrin->src[1], nir_src_for_ssa(src));
52
53 nir_metadata_preserve(impl, nir_metadata_block_index |
54 nir_metadata_dominance);
55 return true;
56 }
57 }
58
59 nir_metadata_preserve(impl, nir_metadata_all);
60 return false;
61 }
62
63 bool
64 tu_nir_lower_multiview(nir_shader *nir, uint32_t mask, struct tu_device *dev)
65 {
66 nir_function_impl *entrypoint = nir_shader_get_entrypoint(nir);
67
68 if (!dev->physical_device->supports_multiview_mask &&
69 !util_is_power_of_two_or_zero(mask + 1)) {
70 return lower_multiview_mask(entrypoint, mask);
71 }
72
73 nir_metadata_preserve(entrypoint, nir_metadata_all);
74 return false;
75 }
76