17ec681f3Smrg/*
27ec681f3Smrg * Copyright © Microsoft Corporation
37ec681f3Smrg *
47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg * to deal in the Software without restriction, including without limitation
77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
97ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg *
117ec681f3Smrg * The above copyright notice and this permission notice (including the next
127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
137ec681f3Smrg * Software.
147ec681f3Smrg *
157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
207ec681f3Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
217ec681f3Smrg * IN THE SOFTWARE.
227ec681f3Smrg */
237ec681f3Smrg
247ec681f3Smrg#include "dxil_container.h"
257ec681f3Smrg#include "dxil_module.h"
267ec681f3Smrg
277ec681f3Smrg#include "util/u_debug.h"
287ec681f3Smrg
297ec681f3Smrg#include <assert.h>
307ec681f3Smrg
317ec681f3Smrgconst uint32_t DXIL_DXBC = DXIL_FOURCC('D', 'X', 'B', 'C');
327ec681f3Smrg
337ec681f3Smrgvoid
347ec681f3Smrgdxil_container_init(struct dxil_container *c)
357ec681f3Smrg{
367ec681f3Smrg   blob_init(&c->parts);
377ec681f3Smrg   c->num_parts = 0;
387ec681f3Smrg}
397ec681f3Smrg
407ec681f3Smrgvoid
417ec681f3Smrgdxil_container_finish(struct dxil_container *c)
427ec681f3Smrg{
437ec681f3Smrg   blob_finish(&c->parts);
447ec681f3Smrg}
457ec681f3Smrg
467ec681f3Smrgstatic bool
477ec681f3Smrgadd_part_header(struct dxil_container *c,
487ec681f3Smrg                enum dxil_part_fourcc fourcc,
497ec681f3Smrg                uint32_t part_size)
507ec681f3Smrg{
517ec681f3Smrg   assert(c->parts.size < UINT_MAX);
527ec681f3Smrg   unsigned offset = (unsigned)c->parts.size;
537ec681f3Smrg   if (!blob_write_bytes(&c->parts, &fourcc, sizeof(fourcc)) ||
547ec681f3Smrg       !blob_write_bytes(&c->parts, &part_size, sizeof(part_size)))
557ec681f3Smrg      return false;
567ec681f3Smrg
577ec681f3Smrg   assert(c->num_parts < DXIL_MAX_PARTS);
587ec681f3Smrg   c->part_offsets[c->num_parts++] = offset;
597ec681f3Smrg   return true;
607ec681f3Smrg}
617ec681f3Smrg
627ec681f3Smrgstatic bool
637ec681f3Smrgadd_part(struct dxil_container *c,
647ec681f3Smrg         enum dxil_part_fourcc fourcc,
657ec681f3Smrg         const void *part_data, uint32_t part_size)
667ec681f3Smrg{
677ec681f3Smrg   return add_part_header(c, fourcc, part_size) &&
687ec681f3Smrg          blob_write_bytes(&c->parts, part_data, part_size);
697ec681f3Smrg}
707ec681f3Smrg
717ec681f3Smrgbool
727ec681f3Smrgdxil_container_add_features(struct dxil_container *c,
737ec681f3Smrg                            const struct dxil_features *features)
747ec681f3Smrg{
757ec681f3Smrg   union {
767ec681f3Smrg      struct dxil_features flags;
777ec681f3Smrg      uint64_t bits;
787ec681f3Smrg   } u = { .flags = *features };
797ec681f3Smrg   return add_part(c, DXIL_SFI0, &u.bits, sizeof(u.bits));
807ec681f3Smrg}
817ec681f3Smrg
827ec681f3Smrgtypedef struct {
837ec681f3Smrg   struct {
847ec681f3Smrg      const char *name;
857ec681f3Smrg      uint32_t offset;
867ec681f3Smrg   } entries[DXIL_SHADER_MAX_IO_ROWS];
877ec681f3Smrg   uint32_t num_entries;
887ec681f3Smrg} name_offset_cache_t;
897ec681f3Smrg
907ec681f3Smrgstatic uint32_t
917ec681f3Smrgget_semantic_name_offset(name_offset_cache_t *cache, const char *name,
927ec681f3Smrg                         struct _mesa_string_buffer *buf, uint32_t buf_offset)
937ec681f3Smrg{
947ec681f3Smrg   uint32_t offset = buf->length + buf_offset;
957ec681f3Smrg
967ec681f3Smrg   // DXC doesn't de-duplicate arbitrary semantic names, only SVs.
977ec681f3Smrg   if (strncmp(name, "SV_", 3) == 0) {
987ec681f3Smrg      /* consider replacing this with a binary search using rb_tree */
997ec681f3Smrg      for (unsigned i = 0; i < cache->num_entries; ++i) {
1007ec681f3Smrg         if (!strcmp(name, cache->entries[i].name))
1017ec681f3Smrg            return cache->entries[i].offset;
1027ec681f3Smrg      }
1037ec681f3Smrg
1047ec681f3Smrg      cache->entries[cache->num_entries].name = name;
1057ec681f3Smrg      cache->entries[cache->num_entries].offset = offset;
1067ec681f3Smrg      ++cache->num_entries;
1077ec681f3Smrg   }
1087ec681f3Smrg   _mesa_string_buffer_append_len(buf, name, strlen(name) + 1);
1097ec681f3Smrg
1107ec681f3Smrg   return offset;
1117ec681f3Smrg}
1127ec681f3Smrg
1137ec681f3Smrgstatic uint32_t
1147ec681f3Smrgcollect_semantic_names(unsigned num_records,
1157ec681f3Smrg                       struct dxil_signature_record *io_data,
1167ec681f3Smrg                       struct _mesa_string_buffer *buf,
1177ec681f3Smrg                       uint32_t buf_offset)
1187ec681f3Smrg{
1197ec681f3Smrg   name_offset_cache_t cache;
1207ec681f3Smrg   cache.num_entries = 0;
1217ec681f3Smrg
1227ec681f3Smrg   for (unsigned i = 0; i < num_records; ++i) {
1237ec681f3Smrg      struct dxil_signature_record *io = &io_data[i];
1247ec681f3Smrg      uint32_t offset = get_semantic_name_offset(&cache, io->name, buf, buf_offset);
1257ec681f3Smrg      for (unsigned j = 0; j < io->num_elements; ++j)
1267ec681f3Smrg         io->elements[j].semantic_name_offset = offset;
1277ec681f3Smrg   }
1287ec681f3Smrg   return buf_offset + buf->length;
1297ec681f3Smrg}
1307ec681f3Smrg
1317ec681f3Smrgbool
1327ec681f3Smrgdxil_container_add_io_signature(struct dxil_container *c,
1337ec681f3Smrg                                enum dxil_part_fourcc part,
1347ec681f3Smrg                                unsigned num_records,
1357ec681f3Smrg                                struct dxil_signature_record *io_data)
1367ec681f3Smrg{
1377ec681f3Smrg   struct {
1387ec681f3Smrg      uint32_t param_count;
1397ec681f3Smrg      uint32_t param_offset;
1407ec681f3Smrg   } header;
1417ec681f3Smrg   header.param_count = 0;
1427ec681f3Smrg   uint32_t fixed_size = sizeof(header);
1437ec681f3Smrg   header.param_offset = fixed_size;
1447ec681f3Smrg
1457ec681f3Smrg   bool retval = true;
1467ec681f3Smrg
1477ec681f3Smrg   for (unsigned i = 0; i < num_records; ++i) {
1487ec681f3Smrg      /* TODO:
1497ec681f3Smrg       * - Here we need to check whether the value is actually part of the
1507ec681f3Smrg       * signature */
1517ec681f3Smrg      fixed_size += sizeof(struct dxil_signature_element) * io_data[i].num_elements;
1527ec681f3Smrg      header.param_count += io_data[i].num_elements;
1537ec681f3Smrg   }
1547ec681f3Smrg
1557ec681f3Smrg   struct _mesa_string_buffer *names =
1567ec681f3Smrg         _mesa_string_buffer_create(NULL, 1024);
1577ec681f3Smrg
1587ec681f3Smrg   uint32_t last_offset = collect_semantic_names(num_records, io_data,
1597ec681f3Smrg                                                 names, fixed_size);
1607ec681f3Smrg
1617ec681f3Smrg
1627ec681f3Smrg   if (!add_part_header(c, part, last_offset) ||
1637ec681f3Smrg       !blob_write_bytes(&c->parts, &header, sizeof(header))) {
1647ec681f3Smrg      retval = false;
1657ec681f3Smrg      goto cleanup;
1667ec681f3Smrg   }
1677ec681f3Smrg
1687ec681f3Smrg   /* write all parts */
1697ec681f3Smrg   for (unsigned i = 0; i < num_records; ++i)
1707ec681f3Smrg      for (unsigned j = 0; j < io_data[i].num_elements; ++j) {
1717ec681f3Smrg         if (!blob_write_bytes(&c->parts, &io_data[i].elements[j],
1727ec681f3Smrg                              sizeof(io_data[i].elements[j]))) {
1737ec681f3Smrg            retval = false;
1747ec681f3Smrg            goto cleanup;
1757ec681f3Smrg         }
1767ec681f3Smrg      }
1777ec681f3Smrg
1787ec681f3Smrg   /* write all names */
1797ec681f3Smrg
1807ec681f3Smrg   if (!blob_write_bytes(&c->parts, names->buf, names->length))
1817ec681f3Smrg      retval = false;
1827ec681f3Smrg
1837ec681f3Smrgcleanup:
1847ec681f3Smrg   _mesa_string_buffer_destroy(names);
1857ec681f3Smrg   return retval;
1867ec681f3Smrg}
1877ec681f3Smrg
1887ec681f3Smrgbool
1897ec681f3Smrgdxil_container_add_state_validation(struct dxil_container *c,
1907ec681f3Smrg                                    const struct dxil_module *m,
1917ec681f3Smrg                                    struct dxil_validation_state *state)
1927ec681f3Smrg{
1937ec681f3Smrg   uint32_t psv1_size = sizeof(struct dxil_psv_runtime_info_1);
1947ec681f3Smrg   uint32_t resource_bind_info_size = 4 * sizeof(uint32_t);
1957ec681f3Smrg   uint32_t dxil_pvs_sig_size = sizeof(struct dxil_psv_signature_element);
1967ec681f3Smrg   uint32_t resource_count = state->num_resources;
1977ec681f3Smrg
1987ec681f3Smrg   uint32_t size = psv1_size + 2 * sizeof(uint32_t);
1997ec681f3Smrg   if (resource_count > 0) {
2007ec681f3Smrg      size += sizeof (uint32_t) +
2017ec681f3Smrg              resource_bind_info_size * resource_count;
2027ec681f3Smrg   }
2037ec681f3Smrg   uint32_t string_table_size = (m->sem_string_table->length + 3) & ~3u;
2047ec681f3Smrg   size  += sizeof(uint32_t) + string_table_size;
2057ec681f3Smrg
2067ec681f3Smrg   // Semantic index table size, currently always 0
2077ec681f3Smrg   size  += sizeof(uint32_t) + m->sem_index_table.size * sizeof(uint32_t);
2087ec681f3Smrg
2097ec681f3Smrg   if (m->num_sig_inputs || m->num_sig_outputs) {
2107ec681f3Smrg      size  += sizeof(uint32_t);
2117ec681f3Smrg   }
2127ec681f3Smrg
2137ec681f3Smrg   size += dxil_pvs_sig_size * m->num_sig_inputs;
2147ec681f3Smrg   size += dxil_pvs_sig_size * m->num_sig_outputs;
2157ec681f3Smrg   // size += dxil_pvs_sig_size * m->num_sig_patch_const...;
2167ec681f3Smrg
2177ec681f3Smrg   state->state.sig_input_vectors = (uint8_t)m->num_psv_inputs;
2187ec681f3Smrg
2197ec681f3Smrg   // TODO: check proper stream
2207ec681f3Smrg   state->state.sig_output_vectors[0] = (uint8_t)m->num_psv_outputs;
2217ec681f3Smrg
2227ec681f3Smrg   // TODO: Add viewID records size
2237ec681f3Smrg
2247ec681f3Smrg   // TODO: Add sig input output dependency table size
2257ec681f3Smrg   uint32_t dependency_table_size = 0;
2267ec681f3Smrg   if (state->state.sig_input_vectors > 0) {
2277ec681f3Smrg      for (unsigned i = 0; i < 4; ++i) {
2287ec681f3Smrg         if (state->state.sig_output_vectors[i] > 0)
2297ec681f3Smrg            dependency_table_size += sizeof(uint32_t) * ((state->state.sig_output_vectors[i] + 7) >> 3) *
2307ec681f3Smrg                    state->state.sig_input_vectors * 4;
2317ec681f3Smrg      }
2327ec681f3Smrg   }
2337ec681f3Smrg   size += dependency_table_size;
2347ec681f3Smrg   // TODO: Domain shader table goes here
2357ec681f3Smrg
2367ec681f3Smrg   if (!add_part_header(c, DXIL_PSV0, size))
2377ec681f3Smrg      return false;
2387ec681f3Smrg
2397ec681f3Smrg   if (!blob_write_bytes(&c->parts, &psv1_size, sizeof(psv1_size)))
2407ec681f3Smrg       return false;
2417ec681f3Smrg
2427ec681f3Smrg   if (!blob_write_bytes(&c->parts, &state->state, psv1_size))
2437ec681f3Smrg      return false;
2447ec681f3Smrg
2457ec681f3Smrg   if (!blob_write_bytes(&c->parts, &resource_count, sizeof(resource_count)))
2467ec681f3Smrg      return false;
2477ec681f3Smrg
2487ec681f3Smrg   if (resource_count > 0) {
2497ec681f3Smrg      if (!blob_write_bytes(&c->parts, &resource_bind_info_size, sizeof(resource_bind_info_size)) ||
2507ec681f3Smrg          !blob_write_bytes(&c->parts, state->resources, resource_bind_info_size * state->num_resources))
2517ec681f3Smrg         return false;
2527ec681f3Smrg   }
2537ec681f3Smrg
2547ec681f3Smrg
2557ec681f3Smrg   uint32_t fill = 0;
2567ec681f3Smrg   if (!blob_write_bytes(&c->parts, &string_table_size, sizeof(string_table_size)) ||
2577ec681f3Smrg       !blob_write_bytes(&c->parts, m->sem_string_table->buf, m->sem_string_table->length) ||
2587ec681f3Smrg       !blob_write_bytes(&c->parts, &fill, string_table_size - m->sem_string_table->length))
2597ec681f3Smrg      return false;
2607ec681f3Smrg
2617ec681f3Smrg   // TODO: write the correct semantic index table. Currently it is empty
2627ec681f3Smrg   if (!blob_write_bytes(&c->parts, &m->sem_index_table.size, sizeof(uint32_t)))
2637ec681f3Smrg      return false;
2647ec681f3Smrg
2657ec681f3Smrg   if (m->sem_index_table.size > 0) {
2667ec681f3Smrg      if (!blob_write_bytes(&c->parts, m->sem_index_table.data,
2677ec681f3Smrg                            m->sem_index_table.size * sizeof(uint32_t)))
2687ec681f3Smrg         return false;
2697ec681f3Smrg   }
2707ec681f3Smrg
2717ec681f3Smrg   if (m->num_sig_inputs || m->num_sig_outputs) {
2727ec681f3Smrg      if (!blob_write_bytes(&c->parts, &dxil_pvs_sig_size, sizeof(dxil_pvs_sig_size)))
2737ec681f3Smrg         return false;
2747ec681f3Smrg
2757ec681f3Smrg      if (!blob_write_bytes(&c->parts, &m->psv_inputs, dxil_pvs_sig_size * m->num_sig_inputs))
2767ec681f3Smrg         return false;
2777ec681f3Smrg
2787ec681f3Smrg      if (!blob_write_bytes(&c->parts, &m->psv_outputs, dxil_pvs_sig_size * m->num_sig_outputs))
2797ec681f3Smrg         return false;
2807ec681f3Smrg   }
2817ec681f3Smrg
2827ec681f3Smrg   // TODO: Write PatchConst...
2837ec681f3Smrg
2847ec681f3Smrg   // TODO: Handle case when ViewID is used
2857ec681f3Smrg
2867ec681f3Smrg   // TODO: Handle sig input output dependency table
2877ec681f3Smrg
2887ec681f3Smrg   for (uint32_t i = 0; i < dependency_table_size; ++i)
2897ec681f3Smrg      blob_write_uint8(&c->parts, 0);
2907ec681f3Smrg
2917ec681f3Smrg   return true;
2927ec681f3Smrg}
2937ec681f3Smrg
2947ec681f3Smrgbool
2957ec681f3Smrgdxil_container_add_module(struct dxil_container *c,
2967ec681f3Smrg                          const struct dxil_module *m)
2977ec681f3Smrg{
2987ec681f3Smrg   assert(m->buf.buf_bits == 0); // make sure the module is fully flushed
2997ec681f3Smrg   uint32_t version = (m->shader_kind << 16) |
3007ec681f3Smrg                      (m->major_version << 4) |
3017ec681f3Smrg                      m->minor_version;
3027ec681f3Smrg   uint32_t size = 6 * sizeof(uint32_t) + m->buf.blob.size;
3037ec681f3Smrg   assert(size % sizeof(uint32_t) == 0);
3047ec681f3Smrg   uint32_t uint32_size = size / sizeof(uint32_t);
3057ec681f3Smrg   uint32_t magic = 0x4C495844;
3067ec681f3Smrg   uint32_t dxil_version = 1 << 8; // I have no idea...
3077ec681f3Smrg   uint32_t bitcode_offset = 16;
3087ec681f3Smrg   uint32_t bitcode_size = m->buf.blob.size;
3097ec681f3Smrg
3107ec681f3Smrg   return add_part_header(c, DXIL_DXIL, size) &&
3117ec681f3Smrg          blob_write_bytes(&c->parts, &version, sizeof(version)) &&
3127ec681f3Smrg          blob_write_bytes(&c->parts, &uint32_size, sizeof(uint32_size)) &&
3137ec681f3Smrg          blob_write_bytes(&c->parts, &magic, sizeof(magic)) &&
3147ec681f3Smrg          blob_write_bytes(&c->parts, &dxil_version, sizeof(dxil_version)) &&
3157ec681f3Smrg          blob_write_bytes(&c->parts, &bitcode_offset, sizeof(bitcode_offset)) &&
3167ec681f3Smrg          blob_write_bytes(&c->parts, &bitcode_size, sizeof(bitcode_size)) &&
3177ec681f3Smrg          blob_write_bytes(&c->parts, m->buf.blob.data, m->buf.blob.size);
3187ec681f3Smrg}
3197ec681f3Smrg
3207ec681f3Smrgbool
3217ec681f3Smrgdxil_container_write(struct dxil_container *c, struct blob *blob)
3227ec681f3Smrg{
3237ec681f3Smrg   assert(blob->size == 0);
3247ec681f3Smrg   if (!blob_write_bytes(blob, &DXIL_DXBC, sizeof(DXIL_DXBC)))
3257ec681f3Smrg      return false;
3267ec681f3Smrg
3277ec681f3Smrg   const uint8_t unsigned_digest[16] = { 0 }; // null-digest means unsigned
3287ec681f3Smrg   if (!blob_write_bytes(blob, unsigned_digest, sizeof(unsigned_digest)))
3297ec681f3Smrg      return false;
3307ec681f3Smrg
3317ec681f3Smrg   uint16_t major_version = 1;
3327ec681f3Smrg   uint16_t minor_version = 0;
3337ec681f3Smrg   if (!blob_write_bytes(blob, &major_version, sizeof(major_version)) ||
3347ec681f3Smrg       !blob_write_bytes(blob, &minor_version, sizeof(minor_version)))
3357ec681f3Smrg      return false;
3367ec681f3Smrg
3377ec681f3Smrg   size_t header_size = 32 + 4 * c->num_parts;
3387ec681f3Smrg   size_t size = header_size + c->parts.size;
3397ec681f3Smrg   assert(size <= UINT32_MAX);
3407ec681f3Smrg   uint32_t container_size = (uint32_t)size;
3417ec681f3Smrg   if (!blob_write_bytes(blob, &container_size, sizeof(container_size)))
3427ec681f3Smrg      return false;
3437ec681f3Smrg
3447ec681f3Smrg   uint32_t part_offsets[DXIL_MAX_PARTS];
3457ec681f3Smrg   for (int i = 0; i < c->num_parts; ++i) {
3467ec681f3Smrg      size_t offset = header_size + c->part_offsets[i];
3477ec681f3Smrg      assert(offset <= UINT32_MAX);
3487ec681f3Smrg      part_offsets[i] = (uint32_t)offset;
3497ec681f3Smrg   }
3507ec681f3Smrg
3517ec681f3Smrg   if (!blob_write_bytes(blob, &c->num_parts, sizeof(c->num_parts)) ||
3527ec681f3Smrg       !blob_write_bytes(blob, part_offsets, sizeof(uint32_t) * c->num_parts) ||
3537ec681f3Smrg       !blob_write_bytes(blob, c->parts.data, c->parts.size))
3547ec681f3Smrg      return false;
3557ec681f3Smrg
3567ec681f3Smrg   return true;
3577ec681f3Smrg}
358