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 31const uint32_t DXIL_DXBC = DXIL_FOURCC('D', 'X', 'B', 'C'); 32 33void 34dxil_container_init(struct dxil_container *c) 35{ 36 blob_init(&c->parts); 37 c->num_parts = 0; 38} 39 40void 41dxil_container_finish(struct dxil_container *c) 42{ 43 blob_finish(&c->parts); 44} 45 46static bool 47add_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 62static bool 63add_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 71bool 72dxil_container_add_features(struct dxil_container *c, 73 const struct dxil_features *features) 74{ 75 union { 76 struct dxil_features flags; 77 uint64_t bits; 78 } u = { .flags = *features }; 79 return add_part(c, DXIL_SFI0, &u.bits, sizeof(u.bits)); 80} 81 82typedef struct { 83 struct { 84 const char *name; 85 uint32_t offset; 86 } entries[DXIL_SHADER_MAX_IO_ROWS]; 87 uint32_t num_entries; 88} name_offset_cache_t; 89 90static uint32_t 91get_semantic_name_offset(name_offset_cache_t *cache, const char *name, 92 struct _mesa_string_buffer *buf, uint32_t buf_offset) 93{ 94 uint32_t offset = buf->length + buf_offset; 95 96 // DXC doesn't de-duplicate arbitrary semantic names, only SVs. 97 if (strncmp(name, "SV_", 3) == 0) { 98 /* consider replacing this with a binary search using rb_tree */ 99 for (unsigned i = 0; i < cache->num_entries; ++i) { 100 if (!strcmp(name, cache->entries[i].name)) 101 return cache->entries[i].offset; 102 } 103 104 cache->entries[cache->num_entries].name = name; 105 cache->entries[cache->num_entries].offset = offset; 106 ++cache->num_entries; 107 } 108 _mesa_string_buffer_append_len(buf, name, strlen(name) + 1); 109 110 return offset; 111} 112 113static uint32_t 114collect_semantic_names(unsigned num_records, 115 struct dxil_signature_record *io_data, 116 struct _mesa_string_buffer *buf, 117 uint32_t buf_offset) 118{ 119 name_offset_cache_t cache; 120 cache.num_entries = 0; 121 122 for (unsigned i = 0; i < num_records; ++i) { 123 struct dxil_signature_record *io = &io_data[i]; 124 uint32_t offset = get_semantic_name_offset(&cache, io->name, buf, buf_offset); 125 for (unsigned j = 0; j < io->num_elements; ++j) 126 io->elements[j].semantic_name_offset = offset; 127 } 128 return buf_offset + buf->length; 129} 130 131bool 132dxil_container_add_io_signature(struct dxil_container *c, 133 enum dxil_part_fourcc part, 134 unsigned num_records, 135 struct dxil_signature_record *io_data) 136{ 137 struct { 138 uint32_t param_count; 139 uint32_t param_offset; 140 } header; 141 header.param_count = 0; 142 uint32_t fixed_size = sizeof(header); 143 header.param_offset = fixed_size; 144 145 bool retval = true; 146 147 for (unsigned i = 0; i < num_records; ++i) { 148 /* TODO: 149 * - Here we need to check whether the value is actually part of the 150 * signature */ 151 fixed_size += sizeof(struct dxil_signature_element) * io_data[i].num_elements; 152 header.param_count += io_data[i].num_elements; 153 } 154 155 struct _mesa_string_buffer *names = 156 _mesa_string_buffer_create(NULL, 1024); 157 158 uint32_t last_offset = collect_semantic_names(num_records, io_data, 159 names, fixed_size); 160 161 162 if (!add_part_header(c, part, last_offset) || 163 !blob_write_bytes(&c->parts, &header, sizeof(header))) { 164 retval = false; 165 goto cleanup; 166 } 167 168 /* write all parts */ 169 for (unsigned i = 0; i < num_records; ++i) 170 for (unsigned j = 0; j < io_data[i].num_elements; ++j) { 171 if (!blob_write_bytes(&c->parts, &io_data[i].elements[j], 172 sizeof(io_data[i].elements[j]))) { 173 retval = false; 174 goto cleanup; 175 } 176 } 177 178 /* write all names */ 179 180 if (!blob_write_bytes(&c->parts, names->buf, names->length)) 181 retval = false; 182 183cleanup: 184 _mesa_string_buffer_destroy(names); 185 return retval; 186} 187 188bool 189dxil_container_add_state_validation(struct dxil_container *c, 190 const struct dxil_module *m, 191 struct dxil_validation_state *state) 192{ 193 uint32_t psv1_size = sizeof(struct dxil_psv_runtime_info_1); 194 uint32_t resource_bind_info_size = 4 * sizeof(uint32_t); 195 uint32_t dxil_pvs_sig_size = sizeof(struct dxil_psv_signature_element); 196 uint32_t resource_count = state->num_resources; 197 198 uint32_t size = psv1_size + 2 * sizeof(uint32_t); 199 if (resource_count > 0) { 200 size += sizeof (uint32_t) + 201 resource_bind_info_size * resource_count; 202 } 203 uint32_t string_table_size = (m->sem_string_table->length + 3) & ~3u; 204 size += sizeof(uint32_t) + string_table_size; 205 206 // Semantic index table size, currently always 0 207 size += sizeof(uint32_t) + m->sem_index_table.size * sizeof(uint32_t); 208 209 if (m->num_sig_inputs || m->num_sig_outputs) { 210 size += sizeof(uint32_t); 211 } 212 213 size += dxil_pvs_sig_size * m->num_sig_inputs; 214 size += dxil_pvs_sig_size * m->num_sig_outputs; 215 // size += dxil_pvs_sig_size * m->num_sig_patch_const...; 216 217 state->state.sig_input_vectors = (uint8_t)m->num_psv_inputs; 218 219 // TODO: check proper stream 220 state->state.sig_output_vectors[0] = (uint8_t)m->num_psv_outputs; 221 222 // TODO: Add viewID records size 223 224 // TODO: Add sig input output dependency table size 225 uint32_t dependency_table_size = 0; 226 if (state->state.sig_input_vectors > 0) { 227 for (unsigned i = 0; i < 4; ++i) { 228 if (state->state.sig_output_vectors[i] > 0) 229 dependency_table_size += sizeof(uint32_t) * ((state->state.sig_output_vectors[i] + 7) >> 3) * 230 state->state.sig_input_vectors * 4; 231 } 232 } 233 size += dependency_table_size; 234 // TODO: Domain shader table goes here 235 236 if (!add_part_header(c, DXIL_PSV0, size)) 237 return false; 238 239 if (!blob_write_bytes(&c->parts, &psv1_size, sizeof(psv1_size))) 240 return false; 241 242 if (!blob_write_bytes(&c->parts, &state->state, psv1_size)) 243 return false; 244 245 if (!blob_write_bytes(&c->parts, &resource_count, sizeof(resource_count))) 246 return false; 247 248 if (resource_count > 0) { 249 if (!blob_write_bytes(&c->parts, &resource_bind_info_size, sizeof(resource_bind_info_size)) || 250 !blob_write_bytes(&c->parts, state->resources, resource_bind_info_size * state->num_resources)) 251 return false; 252 } 253 254 255 uint32_t fill = 0; 256 if (!blob_write_bytes(&c->parts, &string_table_size, sizeof(string_table_size)) || 257 !blob_write_bytes(&c->parts, m->sem_string_table->buf, m->sem_string_table->length) || 258 !blob_write_bytes(&c->parts, &fill, string_table_size - m->sem_string_table->length)) 259 return false; 260 261 // TODO: write the correct semantic index table. Currently it is empty 262 if (!blob_write_bytes(&c->parts, &m->sem_index_table.size, sizeof(uint32_t))) 263 return false; 264 265 if (m->sem_index_table.size > 0) { 266 if (!blob_write_bytes(&c->parts, m->sem_index_table.data, 267 m->sem_index_table.size * sizeof(uint32_t))) 268 return false; 269 } 270 271 if (m->num_sig_inputs || m->num_sig_outputs) { 272 if (!blob_write_bytes(&c->parts, &dxil_pvs_sig_size, sizeof(dxil_pvs_sig_size))) 273 return false; 274 275 if (!blob_write_bytes(&c->parts, &m->psv_inputs, dxil_pvs_sig_size * m->num_sig_inputs)) 276 return false; 277 278 if (!blob_write_bytes(&c->parts, &m->psv_outputs, dxil_pvs_sig_size * m->num_sig_outputs)) 279 return false; 280 } 281 282 // TODO: Write PatchConst... 283 284 // TODO: Handle case when ViewID is used 285 286 // TODO: Handle sig input output dependency table 287 288 for (uint32_t i = 0; i < dependency_table_size; ++i) 289 blob_write_uint8(&c->parts, 0); 290 291 return true; 292} 293 294bool 295dxil_container_add_module(struct dxil_container *c, 296 const struct dxil_module *m) 297{ 298 assert(m->buf.buf_bits == 0); // make sure the module is fully flushed 299 uint32_t version = (m->shader_kind << 16) | 300 (m->major_version << 4) | 301 m->minor_version; 302 uint32_t size = 6 * sizeof(uint32_t) + m->buf.blob.size; 303 assert(size % sizeof(uint32_t) == 0); 304 uint32_t uint32_size = size / sizeof(uint32_t); 305 uint32_t magic = 0x4C495844; 306 uint32_t dxil_version = 1 << 8; // I have no idea... 307 uint32_t bitcode_offset = 16; 308 uint32_t bitcode_size = m->buf.blob.size; 309 310 return add_part_header(c, DXIL_DXIL, size) && 311 blob_write_bytes(&c->parts, &version, sizeof(version)) && 312 blob_write_bytes(&c->parts, &uint32_size, sizeof(uint32_size)) && 313 blob_write_bytes(&c->parts, &magic, sizeof(magic)) && 314 blob_write_bytes(&c->parts, &dxil_version, sizeof(dxil_version)) && 315 blob_write_bytes(&c->parts, &bitcode_offset, sizeof(bitcode_offset)) && 316 blob_write_bytes(&c->parts, &bitcode_size, sizeof(bitcode_size)) && 317 blob_write_bytes(&c->parts, m->buf.blob.data, m->buf.blob.size); 318} 319 320bool 321dxil_container_write(struct dxil_container *c, struct blob *blob) 322{ 323 assert(blob->size == 0); 324 if (!blob_write_bytes(blob, &DXIL_DXBC, sizeof(DXIL_DXBC))) 325 return false; 326 327 const uint8_t unsigned_digest[16] = { 0 }; // null-digest means unsigned 328 if (!blob_write_bytes(blob, unsigned_digest, sizeof(unsigned_digest))) 329 return false; 330 331 uint16_t major_version = 1; 332 uint16_t minor_version = 0; 333 if (!blob_write_bytes(blob, &major_version, sizeof(major_version)) || 334 !blob_write_bytes(blob, &minor_version, sizeof(minor_version))) 335 return false; 336 337 size_t header_size = 32 + 4 * c->num_parts; 338 size_t size = header_size + c->parts.size; 339 assert(size <= UINT32_MAX); 340 uint32_t container_size = (uint32_t)size; 341 if (!blob_write_bytes(blob, &container_size, sizeof(container_size))) 342 return false; 343 344 uint32_t part_offsets[DXIL_MAX_PARTS]; 345 for (int i = 0; i < c->num_parts; ++i) { 346 size_t offset = header_size + c->part_offsets[i]; 347 assert(offset <= UINT32_MAX); 348 part_offsets[i] = (uint32_t)offset; 349 } 350 351 if (!blob_write_bytes(blob, &c->num_parts, sizeof(c->num_parts)) || 352 !blob_write_bytes(blob, part_offsets, sizeof(uint32_t) * c->num_parts) || 353 !blob_write_bytes(blob, c->parts.data, c->parts.size)) 354 return false; 355 356 return true; 357} 358