• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2024 Imagination Technologies Ltd.
3  *
4  * SPDX-License-Identifier: MIT
5  */
6 
7 /**
8  * \file pco_nir_pvfio.c
9  *
10  * \brief PCO NIR per-vertex/fragment input/output passes.
11  */
12 
13 #include "compiler/glsl_types.h"
14 #include "compiler/shader_enums.h"
15 #include "nir.h"
16 #include "nir_builder.h"
17 #include "pco.h"
18 #include "pco_builder.h"
19 #include "pco_internal.h"
20 #include "util/macros.h"
21 #include "util/u_dynarray.h"
22 
23 #include <assert.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 
27 /** Per-fragment output pass state. */
28 struct pfo_state {
29    struct util_dynarray stores; /** List of fragment stores. */
30    pco_fs_data *fs; /** Fragment-specific data. */
31 };
32 
33 /**
34  * \brief Returns a NIR intrinsic instruction if a NIR instruction matches the
35  *        provided intrinsic op.
36  *
37  * \param[in] instr NIR instruction.
38  * \param[in] op Desired intrinsic op.
39  * \return The intrinsic instruction, else NULL.
40  */
is_intr(nir_instr * instr,nir_intrinsic_op op)41 static inline nir_intrinsic_instr *is_intr(nir_instr *instr,
42                                            nir_intrinsic_op op)
43 {
44    nir_intrinsic_instr *intr = NULL;
45 
46    if (instr->type != nir_instr_type_intrinsic)
47       return NULL;
48 
49    intr = nir_instr_as_intrinsic(instr);
50 
51    if (intr->intrinsic != op)
52       return NULL;
53 
54    return intr;
55 }
56 
57 /**
58  * \brief Returns the GLSL base type equivalent of a pipe format.
59  *
60  * \param[in] format Pipe format.
61  * \return The GLSL base type, or GLSL_TYPE_ERROR if unsupported/invalid.
62  */
base_type_from_fmt(enum pipe_format format)63 static inline enum glsl_base_type base_type_from_fmt(enum pipe_format format)
64 {
65    const struct util_format_description *desc = util_format_description(format);
66    int chan = util_format_get_first_non_void_channel(format);
67    if (chan < 0)
68       return GLSL_TYPE_ERROR;
69 
70    switch (desc->channel[chan].type) {
71    case UTIL_FORMAT_TYPE_UNSIGNED:
72       return GLSL_TYPE_UINT;
73 
74    case UTIL_FORMAT_TYPE_SIGNED:
75       return GLSL_TYPE_INT;
76 
77    case UTIL_FORMAT_TYPE_FLOAT:
78       return GLSL_TYPE_FLOAT;
79 
80    default:
81       break;
82    }
83 
84    return GLSL_TYPE_ERROR;
85 }
86 
87 /**
88  * \brief Lowers a PFO-related instruction.
89  *
90  * \param[in] b NIR builder.
91  * \param[in] instr NIR instruction.
92  * \param[in] cb_data User callback data.
93  * \return True if the instruction was lowered.
94  */
lower_pfo(nir_builder * b,nir_instr * instr,void * cb_data)95 static bool lower_pfo(nir_builder *b, nir_instr *instr, void *cb_data)
96 {
97    struct pfo_state *state = cb_data;
98 
99    /* TODO NEXT: move into separate function (pack_to_pbe),
100     * and use data from driver to actually figure out format stuff!
101     */
102    nir_intrinsic_instr *intr;
103    if ((intr = is_intr(instr, nir_intrinsic_store_output))) {
104       /* Skip stores we've already processed. */
105       util_dynarray_foreach (&state->stores, nir_intrinsic_instr *, store) {
106          if (intr == *store)
107             return false;
108       }
109 
110       nir_src *value = &intr->src[0];
111       nir_src *offset = &intr->src[1];
112 
113       /* TODO: more accurate way of detecting this */
114       /* Already in expected format. */
115       if (b->shader->info.internal && nir_src_num_components(*value) == 1) {
116          util_dynarray_append(&state->stores, nir_intrinsic_instr *, intr);
117          return false;
118       }
119 
120       assert(nir_src_as_uint(*offset) == 0);
121 
122       assert(nir_src_num_components(*value) == 4);
123       assert(nir_src_bit_size(*value) == 32);
124 
125       struct nir_io_semantics io_semantics = nir_intrinsic_io_semantics(intr);
126       gl_frag_result location = io_semantics.location;
127 
128       enum pipe_format format = state->fs->output_formats[location];
129 
130       unsigned format_bits = util_format_get_blocksizebits(format);
131       assert(!(format_bits % 32));
132 
133       /* Update the type of the stored variable. */
134       nir_variable *var = nir_find_variable_with_location(b->shader,
135                                                           nir_var_shader_out,
136                                                           location);
137       assert(var);
138 
139       var->type = glsl_simple_explicit_type(base_type_from_fmt(format),
140                                             format_bits / 32,
141                                             1,
142                                             0,
143                                             false,
144                                             0);
145 
146       b->cursor = nir_after_block(
147          nir_impl_last_block(nir_shader_get_entrypoint(b->shader)));
148 
149       /* Emit and track the new store. */
150       /* TODO: support other formats. */
151       if (format == PIPE_FORMAT_R8G8B8A8_UNORM) {
152          nir_intrinsic_instr *store =
153             nir_store_output(b,
154                              nir_pack_unorm_4x8(b, value->ssa),
155                              offset->ssa,
156                              .base = nir_intrinsic_base(intr),
157                              .write_mask = 1,
158                              .component = 0,
159                              .src_type = nir_type_uint32,
160                              .io_semantics = io_semantics,
161                              .io_xfb = nir_intrinsic_io_xfb(intr),
162                              .io_xfb2 = nir_intrinsic_io_xfb2(intr));
163          util_dynarray_append(&state->stores, nir_intrinsic_instr *, store);
164       } else {
165          unreachable();
166       }
167 
168       /* Remove the old store. */
169       b->cursor = nir_instr_remove(instr);
170 
171       return true;
172    }
173 
174    return false;
175 }
176 
177 /**
178  * \brief Per-fragment output pass.
179  *
180  * \param[in,out] nir NIR shader.
181  * \param[in,out] fs Fragment shader-specific data.
182  * \return True if the pass made progress.
183  */
pco_nir_pfo(nir_shader * nir,pco_fs_data * fs)184 bool pco_nir_pfo(nir_shader *nir, pco_fs_data *fs)
185 {
186    assert(nir->info.stage == MESA_SHADER_FRAGMENT);
187 
188    struct pfo_state state = { .fs = fs };
189    util_dynarray_init(&state.stores, NULL);
190 
191    bool progress =
192       nir_shader_instructions_pass(nir, lower_pfo, nir_metadata_none, &state);
193 
194    util_dynarray_fini(&state.stores);
195 
196    return progress;
197 }
198 
199 /**
200  * \brief Per-vertex input pass.
201  *
202  * \param[in,out] nir NIR shader.
203  * \param[in,out] vs Vertex shader-specific data.
204  * \return True if the pass made progress.
205  */
pco_nir_pvi(nir_shader * nir,pco_vs_data * vs)206 bool pco_nir_pvi(nir_shader *nir, pco_vs_data *vs)
207 {
208    assert(nir->info.stage == MESA_SHADER_VERTEX);
209 
210    puts("finishme: pco_nir_pvi");
211 
212    /* TODO: format conversion and inserting unspecified/missing components. */
213 
214    return false;
215 }
216