xref: /aosp_15_r20/external/mesa3d/src/microsoft/compiler/dxil_container.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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 "dxil_container.h"
25 #include "dxil_module.h"
26 
27 #include "util/u_debug.h"
28 
29 #include <assert.h>
30 
31 const uint32_t DXIL_DXBC = DXIL_FOURCC('D', 'X', 'B', 'C');
32 
33 void
dxil_container_init(struct dxil_container * c)34 dxil_container_init(struct dxil_container *c)
35 {
36    blob_init(&c->parts);
37    c->num_parts = 0;
38 }
39 
40 void
dxil_container_finish(struct dxil_container * c)41 dxil_container_finish(struct dxil_container *c)
42 {
43    blob_finish(&c->parts);
44 }
45 
46 static bool
add_part_header(struct dxil_container * c,enum dxil_part_fourcc fourcc,uint32_t part_size)47 add_part_header(struct dxil_container *c,
48                 enum dxil_part_fourcc fourcc,
49                 uint32_t part_size)
50 {
51    assert(c->parts.size < UINT_MAX);
52    unsigned offset = (unsigned)c->parts.size;
53    if (!blob_write_bytes(&c->parts, &fourcc, sizeof(fourcc)) ||
54        !blob_write_bytes(&c->parts, &part_size, sizeof(part_size)))
55       return false;
56 
57    assert(c->num_parts < DXIL_MAX_PARTS);
58    c->part_offsets[c->num_parts++] = offset;
59    return true;
60 }
61 
62 static bool
add_part(struct dxil_container * c,enum dxil_part_fourcc fourcc,const void * part_data,uint32_t part_size)63 add_part(struct dxil_container *c,
64          enum dxil_part_fourcc fourcc,
65          const void *part_data, uint32_t part_size)
66 {
67    return add_part_header(c, fourcc, part_size) &&
68           blob_write_bytes(&c->parts, part_data, part_size);
69 }
70 
71 bool
dxil_container_add_features(struct dxil_container * c,const struct dxil_features * features)72 dxil_container_add_features(struct dxil_container *c,
73                             const struct dxil_features *features)
74 {
75    /* DXIL feature info is a bitfield packed into a uint64_t. */
76    static_assert(sizeof(struct dxil_features) <= sizeof(uint64_t),
77                  "Expected dxil_features to fit into a uint64_t");
78    uint64_t bits = 0;
79    memcpy(&bits, features, sizeof(struct dxil_features));
80    return add_part(c, DXIL_SFI0, &bits, sizeof(uint64_t));
81 }
82 
83 typedef struct {
84    struct {
85       const char *name;
86       uint32_t offset;
87    } entries[DXIL_SHADER_MAX_IO_ROWS];
88    uint32_t num_entries;
89 } name_offset_cache_t;
90 
91 static uint32_t
get_semantic_name_offset(name_offset_cache_t * cache,const char * name,struct _mesa_string_buffer * buf,uint32_t buf_offset,bool validator_7)92 get_semantic_name_offset(name_offset_cache_t *cache, const char *name,
93                          struct _mesa_string_buffer *buf, uint32_t buf_offset,
94                          bool validator_7)
95 {
96    uint32_t offset = buf->length + buf_offset;
97 
98    /* DXC doesn't de-duplicate arbitrary semantic names until validator 1.7, only SVs. */
99    if (validator_7 || strncmp(name, "SV_", 3) == 0) {
100       /* consider replacing this with a binary search using rb_tree */
101       for (unsigned i = 0; i < cache->num_entries; ++i) {
102          if (!strcmp(name, cache->entries[i].name))
103             return cache->entries[i].offset;
104       }
105 
106       cache->entries[cache->num_entries].name = name;
107       cache->entries[cache->num_entries].offset = offset;
108       ++cache->num_entries;
109    }
110    _mesa_string_buffer_append_len(buf, name, strlen(name) + 1);
111 
112    return offset;
113 }
114 
115 static uint32_t
collect_semantic_names(unsigned num_records,struct dxil_signature_record * io_data,struct _mesa_string_buffer * buf,uint32_t buf_offset,bool validator_7)116 collect_semantic_names(unsigned num_records,
117                        struct dxil_signature_record *io_data,
118                        struct _mesa_string_buffer *buf,
119                        uint32_t buf_offset,
120                        bool validator_7)
121 {
122    name_offset_cache_t cache;
123    cache.num_entries = 0;
124 
125    for (unsigned i = 0; i < num_records; ++i) {
126       struct dxil_signature_record *io = &io_data[i];
127       uint32_t offset = get_semantic_name_offset(&cache, io->name, buf, buf_offset, validator_7);
128       for (unsigned j = 0; j < io->num_elements; ++j)
129          io->elements[j].semantic_name_offset = offset;
130    }
131    if (validator_7 && buf->length % sizeof(uint32_t) != 0) {
132       unsigned padding_to_add = sizeof(uint32_t) - (buf->length % sizeof(uint32_t));
133       char padding[sizeof(uint32_t)] = { 0 };
134       _mesa_string_buffer_append_len(buf, padding, padding_to_add);
135    }
136    return buf_offset + buf->length;
137 }
138 
139 bool
dxil_container_add_io_signature(struct dxil_container * c,enum dxil_part_fourcc part,unsigned num_records,struct dxil_signature_record * io_data,bool validator_7)140 dxil_container_add_io_signature(struct dxil_container *c,
141                                 enum dxil_part_fourcc part,
142                                 unsigned num_records,
143                                 struct dxil_signature_record *io_data,
144                                 bool validator_7)
145 {
146    struct {
147       uint32_t param_count;
148       uint32_t param_offset;
149    } header;
150    header.param_count = 0;
151    uint32_t fixed_size = sizeof(header);
152    header.param_offset = fixed_size;
153 
154    bool retval = true;
155 
156    for (unsigned i = 0; i < num_records; ++i) {
157       /* TODO:
158        * - Here we need to check whether the value is actually part of the
159        * signature */
160       fixed_size += sizeof(struct dxil_signature_element) * io_data[i].num_elements;
161       header.param_count += io_data[i].num_elements;
162    }
163 
164    struct _mesa_string_buffer *names =
165          _mesa_string_buffer_create(NULL, 1024);
166 
167    uint32_t last_offset = collect_semantic_names(num_records, io_data,
168                                                  names, fixed_size,
169                                                  validator_7);
170 
171 
172    if (!add_part_header(c, part, last_offset) ||
173        !blob_write_bytes(&c->parts, &header, sizeof(header))) {
174       retval = false;
175       goto cleanup;
176    }
177 
178    /* write all parts */
179    for (unsigned i = 0; i < num_records; ++i)
180       for (unsigned j = 0; j < io_data[i].num_elements; ++j) {
181          if (!blob_write_bytes(&c->parts, &io_data[i].elements[j],
182                               sizeof(io_data[i].elements[j]))) {
183             retval = false;
184             goto cleanup;
185          }
186       }
187 
188    /* write all names */
189 
190    if (!blob_write_bytes(&c->parts, names->buf, names->length))
191       retval = false;
192 
193 cleanup:
194    _mesa_string_buffer_destroy(names);
195    return retval;
196 }
197 
198 bool
dxil_container_add_state_validation(struct dxil_container * c,const struct dxil_module * m,struct dxil_validation_state * state)199 dxil_container_add_state_validation(struct dxil_container *c,
200                                     const struct dxil_module *m,
201                                     struct dxil_validation_state *state)
202 {
203    uint32_t psv_size = m->minor_validator >= 6 ?
204       sizeof(struct dxil_psv_runtime_info_2) :
205       sizeof(struct dxil_psv_runtime_info_1);
206    uint32_t resource_bind_info_size = m->minor_validator >= 6 ?
207       sizeof(struct dxil_resource_v1) : sizeof(struct dxil_resource_v0);
208    uint32_t dxil_pvs_sig_size = sizeof(struct dxil_psv_signature_element);
209    uint32_t resource_count = state->num_resources;
210 
211    uint32_t size = psv_size + 2 * sizeof(uint32_t);
212    if (resource_count > 0) {
213       size += sizeof (uint32_t) +
214               resource_bind_info_size * resource_count;
215    }
216    uint32_t string_table_size = (m->sem_string_table->length + 3) & ~3u;
217    size  += sizeof(uint32_t) + string_table_size;
218 
219    size  += sizeof(uint32_t) + m->sem_index_table.size * sizeof(uint32_t);
220 
221    if (m->num_sig_inputs || m->num_sig_outputs || m->num_sig_patch_consts) {
222       size  += sizeof(uint32_t);
223    }
224 
225    size += dxil_pvs_sig_size * m->num_sig_inputs;
226    size += dxil_pvs_sig_size * m->num_sig_outputs;
227    size += dxil_pvs_sig_size * m->num_sig_patch_consts;
228 
229    state->state.psv1.sig_input_vectors = (uint8_t)m->num_psv_inputs;
230 
231    for (unsigned i = 0; i < 4; ++i)
232       state->state.psv1.sig_output_vectors[i] = (uint8_t)m->num_psv_outputs[i];
233 
234    if (state->state.psv1.uses_view_id) {
235       for (unsigned i = 0; i < 4; ++i)
236          size += m->dependency_table_dwords_per_input[i] * sizeof(uint32_t);
237    }
238 
239    for (unsigned i = 0; i < 4; ++i)
240       size += m->io_dependency_table_size[i] * sizeof(uint32_t);
241 
242    if (!add_part_header(c, DXIL_PSV0, size))
243       return false;
244 
245    if (!blob_write_bytes(&c->parts, &psv_size, sizeof(psv_size)))
246        return false;
247 
248    if (!blob_write_bytes(&c->parts, &state->state, psv_size))
249       return false;
250 
251    if (!blob_write_bytes(&c->parts, &resource_count, sizeof(resource_count)))
252       return false;
253 
254    if (resource_count > 0) {
255       if (!blob_write_bytes(&c->parts, &resource_bind_info_size, sizeof(resource_bind_info_size)) ||
256           !blob_write_bytes(&c->parts, state->resources.v0, resource_bind_info_size * state->num_resources))
257          return false;
258    }
259 
260 
261    uint32_t fill = 0;
262    if (!blob_write_bytes(&c->parts, &string_table_size, sizeof(string_table_size)) ||
263        !blob_write_bytes(&c->parts, m->sem_string_table->buf, m->sem_string_table->length) ||
264        !blob_write_bytes(&c->parts, &fill, string_table_size - m->sem_string_table->length))
265       return false;
266 
267    if (!blob_write_bytes(&c->parts, &m->sem_index_table.size, sizeof(uint32_t)))
268       return false;
269 
270    if (m->sem_index_table.size > 0) {
271       if (!blob_write_bytes(&c->parts, m->sem_index_table.data,
272                             m->sem_index_table.size * sizeof(uint32_t)))
273          return false;
274    }
275 
276    if (m->num_sig_inputs || m->num_sig_outputs || m->num_sig_patch_consts) {
277       if (!blob_write_bytes(&c->parts, &dxil_pvs_sig_size, sizeof(dxil_pvs_sig_size)))
278          return false;
279 
280       if (!blob_write_bytes(&c->parts, &m->psv_inputs, dxil_pvs_sig_size * m->num_sig_inputs))
281          return false;
282 
283       if (!blob_write_bytes(&c->parts, &m->psv_outputs, dxil_pvs_sig_size * m->num_sig_outputs))
284          return false;
285 
286       if (!blob_write_bytes(&c->parts, &m->psv_patch_consts, dxil_pvs_sig_size * m->num_sig_patch_consts))
287          return false;
288    }
289 
290    /* This was a bug in the DXIL validation logic prior to 1.8. When replicating these I/O dependency
291     * tables from the metadata to the container, the pointer is advanced for each stream,
292     * and then copied for all streams... meaning that the first streams have zero data, since the
293     * pointer is advanced and then never written to. The last stream (that has data) then has the
294     * data from all streams written to it. However, if any stream before the last one has a larger
295     * size, this will cause corruption, since it's writing to the smaller space that was allocated
296     * for the last stream. We assume that never happens, and just zero all earlier streams. */
297    if (m->shader_kind == DXIL_GEOMETRY_SHADER && m->minor_validator < 8) {
298       bool zero_view_id_deps = false, zero_io_deps = false;
299       for (int i = 3; i >= 0; --i) {
300          if (state->state.psv1.uses_view_id && m->dependency_table_dwords_per_input[i]) {
301             if (zero_view_id_deps)
302                memset(m->viewid_dependency_table[i], 0, sizeof(uint32_t) * m->dependency_table_dwords_per_input[i]);
303             zero_view_id_deps = true;
304          }
305          if (m->io_dependency_table_size[i]) {
306             if (zero_io_deps)
307                memset(m->io_dependency_table[i], 0, sizeof(uint32_t) * m->io_dependency_table_size[i]);
308             zero_io_deps = true;
309          }
310       }
311    }
312 
313    if (state->state.psv1.uses_view_id) {
314       for (unsigned i = 0; i < 4; ++i)
315          if (!blob_write_bytes(&c->parts, m->viewid_dependency_table[i],
316                                sizeof(uint32_t) * m->dependency_table_dwords_per_input[i]))
317             return false;
318    }
319 
320    for (unsigned i = 0; i < 4; ++i)
321       if (!blob_write_bytes(&c->parts, m->io_dependency_table[i],
322                             sizeof(uint32_t) * m->io_dependency_table_size[i]))
323          return false;
324 
325    return true;
326 }
327 
328 bool
dxil_container_add_module(struct dxil_container * c,const struct dxil_module * m)329 dxil_container_add_module(struct dxil_container *c,
330                           const struct dxil_module *m)
331 {
332    assert(m->buf.buf_bits == 0); // make sure the module is fully flushed
333    uint32_t version = (m->shader_kind << 16) |
334                       (m->major_version << 4) |
335                       m->minor_version;
336    uint32_t size = 6 * sizeof(uint32_t) + m->buf.blob.size;
337    assert(size % sizeof(uint32_t) == 0);
338    uint32_t uint32_size = size / sizeof(uint32_t);
339    uint32_t magic = 0x4C495844;
340    uint32_t dxil_version = 1 << 8; // I have no idea...
341    uint32_t bitcode_offset = 16;
342    uint32_t bitcode_size = m->buf.blob.size;
343 
344    return add_part_header(c, DXIL_DXIL, size) &&
345           blob_write_bytes(&c->parts, &version, sizeof(version)) &&
346           blob_write_bytes(&c->parts, &uint32_size, sizeof(uint32_size)) &&
347           blob_write_bytes(&c->parts, &magic, sizeof(magic)) &&
348           blob_write_bytes(&c->parts, &dxil_version, sizeof(dxil_version)) &&
349           blob_write_bytes(&c->parts, &bitcode_offset, sizeof(bitcode_offset)) &&
350           blob_write_bytes(&c->parts, &bitcode_size, sizeof(bitcode_size)) &&
351           blob_write_bytes(&c->parts, m->buf.blob.data, m->buf.blob.size);
352 }
353 
354 bool
dxil_container_write(struct dxil_container * c,struct blob * blob)355 dxil_container_write(struct dxil_container *c, struct blob *blob)
356 {
357    assert(blob->size == 0);
358    if (!blob_write_bytes(blob, &DXIL_DXBC, sizeof(DXIL_DXBC)))
359       return false;
360 
361    const uint8_t unsigned_digest[16] = { 0 }; // null-digest means unsigned
362    if (!blob_write_bytes(blob, unsigned_digest, sizeof(unsigned_digest)))
363       return false;
364 
365    uint16_t major_version = 1;
366    uint16_t minor_version = 0;
367    if (!blob_write_bytes(blob, &major_version, sizeof(major_version)) ||
368        !blob_write_bytes(blob, &minor_version, sizeof(minor_version)))
369       return false;
370 
371    size_t header_size = 32 + 4 * c->num_parts;
372    size_t size = header_size + c->parts.size;
373    assert(size <= UINT32_MAX);
374    uint32_t container_size = (uint32_t)size;
375    if (!blob_write_bytes(blob, &container_size, sizeof(container_size)))
376       return false;
377 
378    uint32_t part_offsets[DXIL_MAX_PARTS];
379    for (int i = 0; i < c->num_parts; ++i) {
380       size_t offset = header_size + c->part_offsets[i];
381       assert(offset <= UINT32_MAX);
382       part_offsets[i] = (uint32_t)offset;
383    }
384 
385    if (!blob_write_bytes(blob, &c->num_parts, sizeof(c->num_parts)) ||
386        !blob_write_bytes(blob, part_offsets, sizeof(uint32_t) * c->num_parts) ||
387        !blob_write_bytes(blob, c->parts.data, c->parts.size))
388       return false;
389 
390    return true;
391 }
392