effect_codegen_spirv.cpp (81201B)
1 /* 2 * Copyright (C) 2014 Patrick Mours 3 * SPDX-License-Identifier: BSD-3-Clause 4 */ 5 6 #include "effect_parser.hpp" 7 #include "effect_codegen.hpp" 8 #include <cassert> 9 #include <cstring> // memcmp 10 #include <algorithm> // std::find_if, std::max 11 #include <unordered_set> 12 13 // Use the C++ variant of the SPIR-V headers 14 #include <spirv.hpp> 15 namespace spv { 16 #include <GLSL.std.450.h> 17 } 18 19 using namespace reshadefx; 20 21 /// <summary> 22 /// A single instruction in a SPIR-V module 23 /// </summary> 24 struct spirv_instruction 25 { 26 spv::Op op; 27 spv::Id type; 28 spv::Id result; 29 std::vector<spv::Id> operands; 30 31 explicit spirv_instruction(spv::Op op = spv::OpNop) : op(op), type(0), result(0) {} 32 spirv_instruction(spv::Op op, spv::Id result) : op(op), type(result), result(0) {} 33 spirv_instruction(spv::Op op, spv::Id type, spv::Id result) : op(op), type(type), result(result) {} 34 35 /// <summary> 36 /// Add a single operand to the instruction. 37 /// </summary> 38 spirv_instruction &add(spv::Id operand) 39 { 40 operands.push_back(operand); 41 return *this; 42 } 43 44 /// <summary> 45 /// Add a range of operands to the instruction. 46 /// </summary> 47 template <typename It> 48 spirv_instruction &add(It begin, It end) 49 { 50 operands.insert(operands.end(), begin, end); 51 return *this; 52 } 53 54 /// <summary> 55 /// Add a null-terminated literal UTF-8 string to the instruction. 56 /// </summary> 57 spirv_instruction &add_string(const char *string) 58 { 59 uint32_t word; 60 do { 61 word = 0; 62 for (uint32_t i = 0; i < 4 && *string; ++i) 63 reinterpret_cast<uint8_t *>(&word)[i] = *string++; 64 add(word); 65 } while (*string || (word & 0xFF000000)); 66 return *this; 67 } 68 69 /// <summary> 70 /// Write this instruction to a SPIR-V module. 71 /// </summary> 72 /// <param name="output">The output stream to append this instruction to.</param> 73 void write(std::vector<uint32_t> &output) const 74 { 75 // See https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html 76 // 0 | Opcode: The 16 high-order bits are the WordCount of the instruction. The 16 low-order bits are the opcode enumerant. 77 // 1 | Optional instruction type <id> 78 // . | Optional instruction Result <id> 79 // . | Operand 1 (if needed) 80 // . | Operand 2 (if needed) 81 // ... | ... 82 // WordCount - 1 | Operand N (N is determined by WordCount minus the 1 to 3 words used for the opcode, instruction type <id>, and instruction Result <id>). 83 84 const uint32_t num_words = 1 + (type != 0) + (result != 0) + static_cast<uint32_t>(operands.size()); 85 output.push_back((num_words << spv::WordCountShift) | op); 86 87 // Optional instruction type ID 88 if (type != 0) 89 output.push_back(type); 90 91 // Optional instruction result ID 92 if (result != 0) 93 output.push_back(result); 94 95 // Write out the operands 96 output.insert(output.end(), operands.begin(), operands.end()); 97 } 98 }; 99 100 /// <summary> 101 /// A list of instructions forming a basic block in the SPIR-V module 102 /// </summary> 103 struct spirv_basic_block 104 { 105 std::vector<spirv_instruction> instructions; 106 107 /// <summary> 108 /// Append another basic block the end of this one. 109 /// </summary> 110 void append(const spirv_basic_block &block) 111 { 112 instructions.insert(instructions.end(), block.instructions.begin(), block.instructions.end()); 113 } 114 }; 115 116 class codegen_spirv final : public codegen 117 { 118 public: 119 codegen_spirv(bool vulkan_semantics, bool debug_info, bool uniforms_to_spec_constants, bool enable_16bit_types, bool flip_vert_y) 120 : _debug_info(debug_info), _vulkan_semantics(vulkan_semantics), _uniforms_to_spec_constants(uniforms_to_spec_constants), _enable_16bit_types(enable_16bit_types), _flip_vert_y(flip_vert_y) 121 { 122 _glsl_ext = make_id(); 123 } 124 125 private: 126 struct type_lookup 127 { 128 reshadefx::type type; 129 bool is_ptr; 130 uint32_t array_stride; 131 std::pair<spv::StorageClass, spv::ImageFormat> storage; 132 133 friend bool operator==(const type_lookup &lhs, const type_lookup &rhs) 134 { 135 return lhs.type == rhs.type && lhs.is_ptr == rhs.is_ptr && lhs.array_stride == rhs.array_stride && lhs.storage == rhs.storage; 136 } 137 }; 138 struct function_blocks 139 { 140 spirv_basic_block declaration; 141 spirv_basic_block variables; 142 spirv_basic_block definition; 143 type return_type; 144 std::vector<type> param_types; 145 bool is_entry_point = false; 146 147 friend bool operator==(const function_blocks &lhs, const function_blocks &rhs) 148 { 149 if (lhs.param_types.size() != rhs.param_types.size()) 150 return false; 151 for (size_t i = 0; i < lhs.param_types.size(); ++i) 152 if (!(lhs.param_types[i] == rhs.param_types[i])) 153 return false; 154 return lhs.return_type == rhs.return_type; 155 } 156 }; 157 158 spirv_basic_block _entries; 159 spirv_basic_block _execution_modes; 160 spirv_basic_block _debug_a; 161 spirv_basic_block _debug_b; 162 spirv_basic_block _annotations; 163 spirv_basic_block _types_and_constants; 164 spirv_basic_block _variables; 165 166 std::unordered_set<spv::Id> _spec_constants; 167 std::unordered_set<spv::Capability> _capabilities; 168 std::vector<std::pair<type_lookup, spv::Id>> _type_lookup; 169 std::vector<std::tuple<type, constant, spv::Id>> _constant_lookup; 170 std::vector<std::pair<function_blocks, spv::Id>> _function_type_lookup; 171 std::unordered_map<std::string, spv::Id> _string_lookup; 172 std::unordered_map<spv::Id, std::pair<spv::StorageClass, spv::ImageFormat>> _storage_lookup; 173 std::unordered_map<std::string, uint32_t> _semantic_to_location; 174 175 std::vector<function_blocks> _functions_blocks; 176 std::unordered_map<id, spirv_basic_block> _block_data; 177 spirv_basic_block *_current_block_data = nullptr; 178 179 bool _debug_info = false; 180 bool _vulkan_semantics = false; 181 bool _uniforms_to_spec_constants = false; 182 bool _enable_16bit_types = false; 183 bool _flip_vert_y = false; 184 id _glsl_ext = 0; 185 id _global_ubo_type = 0; 186 id _global_ubo_variable = 0; 187 std::vector<spv::Id> _global_ubo_types; 188 function_blocks *_current_function = nullptr; 189 190 inline void add_location(const location &loc, spirv_basic_block &block) 191 { 192 if (loc.source.empty() || !_debug_info) 193 return; 194 195 spv::Id file; 196 197 if (const auto it = _string_lookup.find(loc.source); 198 it != _string_lookup.end()) 199 file = it->second; 200 else { 201 add_instruction(spv::OpString, 0, _debug_a, file) 202 .add_string(loc.source.c_str()); 203 _string_lookup.emplace(loc.source, file); 204 } 205 206 // https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpLine 207 add_instruction_without_result(spv::OpLine, block) 208 .add(file) 209 .add(loc.line) 210 .add(loc.column); 211 } 212 inline spirv_instruction &add_instruction(spv::Op op, spv::Id type = 0) 213 { 214 assert(is_in_function() && is_in_block()); 215 return add_instruction(op, type, *_current_block_data); 216 } 217 inline spirv_instruction &add_instruction(spv::Op op, spv::Id type, spirv_basic_block &block) 218 { 219 spirv_instruction &instruction = add_instruction_without_result(op, block); 220 instruction.type = type; 221 instruction.result = make_id(); 222 return instruction; 223 } 224 inline spirv_instruction &add_instruction(spv::Op op, spv::Id type, spirv_basic_block &block, spv::Id &result) 225 { 226 spirv_instruction &instruction = add_instruction_without_result(op, block); 227 instruction.type = type; 228 instruction.result = result = make_id(); 229 return instruction; 230 } 231 inline spirv_instruction &add_instruction_without_result(spv::Op op) 232 { 233 assert(is_in_function() && is_in_block()); 234 return add_instruction_without_result(op, *_current_block_data); 235 } 236 inline spirv_instruction &add_instruction_without_result(spv::Op op, spirv_basic_block &block) 237 { 238 return block.instructions.emplace_back(op); 239 } 240 241 void write_result(module &module) override 242 { 243 // First initialize the UBO type now that all member types are known 244 if (_global_ubo_type != 0) 245 { 246 spirv_instruction &type_inst = add_instruction_without_result(spv::OpTypeStruct, _types_and_constants); 247 type_inst.add(_global_ubo_types.begin(), _global_ubo_types.end()); 248 type_inst.result = _global_ubo_type; 249 250 spirv_instruction &variable_inst = add_instruction_without_result(spv::OpVariable, _variables); 251 variable_inst.add(spv::StorageClassUniform); 252 variable_inst.type = convert_type({ type::t_struct, 0, 0, type::q_uniform, 0, _global_ubo_type }, true, spv::StorageClassUniform); 253 variable_inst.result = _global_ubo_variable; 254 255 add_name(variable_inst.result, "$Globals"); 256 } 257 258 module = std::move(_module); 259 260 std::vector<spv::Id> spirv; 261 262 // Write SPIRV header info 263 spirv.push_back(spv::MagicNumber); 264 spirv.push_back(0x10300); // Force SPIR-V 1.3 265 spirv.push_back(0u); // Generator magic number, see https://www.khronos.org/registry/spir-v/api/spir-v.xml 266 spirv.push_back(_next_id); // Maximum ID 267 spirv.push_back(0u); // Reserved for instruction schema 268 269 // All capabilities 270 spirv_instruction(spv::OpCapability) 271 .add(spv::CapabilityShader) // Implicitly declares the Matrix capability too 272 .write(spirv); 273 274 for (spv::Capability capability : _capabilities) 275 spirv_instruction(spv::OpCapability) 276 .add(capability) 277 .write(spirv); 278 279 // Optional extension instructions 280 spirv_instruction(spv::OpExtInstImport, _glsl_ext) 281 .add_string("GLSL.std.450") // Import GLSL extension 282 .write(spirv); 283 284 // Single required memory model instruction 285 spirv_instruction(spv::OpMemoryModel) 286 .add(spv::AddressingModelLogical) 287 .add(spv::MemoryModelGLSL450) 288 .write(spirv); 289 290 // All entry point declarations 291 for (const auto &node : _entries.instructions) 292 node.write(spirv); 293 294 // All execution mode declarations 295 for (const auto &node : _execution_modes.instructions) 296 node.write(spirv); 297 298 spirv_instruction(spv::OpSource) 299 .add(spv::SourceLanguageUnknown) // ReShade FX is not a reserved token at the moment 300 .add(0) // Language version, TODO: Maybe fill in ReShade version here? 301 .write(spirv); 302 303 if (_debug_info) 304 { 305 // All debug instructions 306 for (const auto &node : _debug_a.instructions) 307 node.write(spirv); 308 for (const auto &node : _debug_b.instructions) 309 node.write(spirv); 310 } 311 312 // All annotation instructions 313 for (const auto &node : _annotations.instructions) 314 node.write(spirv); 315 316 // All type declarations 317 for (const auto &node : _types_and_constants.instructions) 318 node.write(spirv); 319 for (const auto &node : _variables.instructions) 320 node.write(spirv); 321 322 // All function definitions 323 for (const auto &function : _functions_blocks) 324 { 325 if (function.definition.instructions.empty()) 326 continue; 327 328 for (const auto &node : function.declaration.instructions) 329 node.write(spirv); 330 331 // Grab first label and move it in front of variable declarations 332 function.definition.instructions.front().write(spirv); 333 assert(function.definition.instructions.front().op == spv::OpLabel); 334 335 for (const auto &node : function.variables.instructions) 336 node.write(spirv); 337 for (auto it = function.definition.instructions.begin() + 1; it != function.definition.instructions.end(); ++it) 338 it->write(spirv); 339 } 340 341 module.code.assign(reinterpret_cast<const char *>(spirv.data()), reinterpret_cast<const char *>(spirv.data() + spirv.size())); 342 } 343 344 spv::Id convert_type(type info, bool is_ptr = false, spv::StorageClass storage = spv::StorageClassFunction, spv::ImageFormat format = spv::ImageFormatUnknown, uint32_t array_stride = 0) 345 { 346 assert(array_stride == 0 || info.is_array()); 347 348 // The storage class is only relevant for pointers, so ignore it for other types during lookup 349 if (is_ptr == false) 350 storage = spv::StorageClassFunction; 351 // There cannot be sampler variables that are local to a function, so always assume uniform storage for them 352 if (info.is_object()) 353 storage = spv::StorageClassUniformConstant; 354 else 355 assert(format == spv::ImageFormatUnknown); 356 357 if (info.is_sampler() || info.is_storage()) 358 info.rows = info.cols = 1; 359 360 // Fall back to 32-bit types and use relaxed precision decoration instead if 16-bit types are not enabled 361 if (!_enable_16bit_types && info.is_numeric() && info.precision() < 32) 362 info.base = static_cast<type::datatype>(info.base + 1); // min16int -> int, min16uint -> uint, min16float -> float 363 364 const type_lookup lookup { info, is_ptr, array_stride, { storage, format } }; 365 366 if (const auto it = std::find_if(_type_lookup.begin(), _type_lookup.end(), 367 [&lookup](const auto &lookup_it) { return lookup_it.first == lookup; }); 368 it != _type_lookup.end()) 369 return it->second; 370 371 spv::Id type, elem_type; 372 if (is_ptr) 373 { 374 elem_type = convert_type(info, false, storage, format, array_stride); 375 376 add_instruction(spv::OpTypePointer, 0, _types_and_constants, type) 377 .add(storage) 378 .add(elem_type); 379 } 380 else if (info.is_array()) 381 { 382 auto elem_info = info; 383 elem_info.array_length = 0; 384 385 // Make sure we don't get any dynamic arrays here 386 assert(info.array_length > 0); 387 388 elem_type = convert_type(elem_info, false, storage, format); 389 const spv::Id array_length = emit_constant(info.array_length); 390 391 add_instruction(spv::OpTypeArray, 0, _types_and_constants, type) 392 .add(elem_type) 393 .add(array_length); 394 395 if (array_stride != 0) 396 add_decoration(type, spv::DecorationArrayStride, { array_stride }); 397 } 398 else if (info.is_matrix()) 399 { 400 // Convert MxN matrix to a SPIR-V matrix with M vectors with N elements 401 auto elem_info = info; 402 elem_info.rows = info.cols; 403 elem_info.cols = 1; 404 405 elem_type = convert_type(elem_info, false, storage, format); 406 407 // Matrix types with just one row are interpreted as if they were a vector type 408 if (info.rows == 1) 409 return elem_type; 410 411 add_instruction(spv::OpTypeMatrix, 0, _types_and_constants, type) 412 .add(elem_type) 413 .add(info.rows); 414 } 415 else if (info.is_vector()) 416 { 417 auto elem_info = info; 418 elem_info.rows = 1; 419 elem_info.cols = 1; 420 421 elem_type = convert_type(elem_info, false, storage, format); 422 423 add_instruction(spv::OpTypeVector, 0, _types_and_constants, type) 424 .add(elem_type) 425 .add(info.rows); 426 } 427 else 428 { 429 switch (info.base) 430 { 431 case type::t_void: 432 assert(info.rows == 0 && info.cols == 0); 433 add_instruction(spv::OpTypeVoid, 0, _types_and_constants, type); 434 break; 435 case type::t_bool: 436 assert(info.rows == 1 && info.cols == 1); 437 add_instruction(spv::OpTypeBool, 0, _types_and_constants, type); 438 break; 439 case type::t_min16int: 440 assert(_enable_16bit_types && info.rows == 1 && info.cols == 1); 441 add_capability(spv::CapabilityInt16); 442 if (storage == spv::StorageClassInput || storage == spv::StorageClassOutput) 443 add_capability(spv::CapabilityStorageInputOutput16); 444 add_instruction(spv::OpTypeInt, 0, _types_and_constants, type) 445 .add(16) // Width 446 .add(1); // Signedness 447 break; 448 case type::t_int: 449 assert(info.rows == 1 && info.cols == 1); 450 add_instruction(spv::OpTypeInt, 0, _types_and_constants, type) 451 .add(32) // Width 452 .add(1); // Signedness 453 break; 454 case type::t_min16uint: 455 assert(_enable_16bit_types && info.rows == 1 && info.cols == 1); 456 add_capability(spv::CapabilityInt16); 457 if (storage == spv::StorageClassInput || storage == spv::StorageClassOutput) 458 add_capability(spv::CapabilityStorageInputOutput16); 459 add_instruction(spv::OpTypeInt, 0, _types_and_constants, type) 460 .add(16) // Width 461 .add(0); // Signedness 462 break; 463 case type::t_uint: 464 assert(info.rows == 1 && info.cols == 1); 465 add_instruction(spv::OpTypeInt, 0, _types_and_constants, type) 466 .add(32) // Width 467 .add(0); // Signedness 468 break; 469 case type::t_min16float: 470 assert(_enable_16bit_types && info.rows == 1 && info.cols == 1); 471 add_capability(spv::CapabilityFloat16); 472 if (storage == spv::StorageClassInput || storage == spv::StorageClassOutput) 473 add_capability(spv::CapabilityStorageInputOutput16); 474 add_instruction(spv::OpTypeFloat, 0, _types_and_constants, type) 475 .add(16); // Width 476 break; 477 case type::t_float: 478 assert(info.rows == 1 && info.cols == 1); 479 add_instruction(spv::OpTypeFloat, 0, _types_and_constants, type) 480 .add(32); // Width 481 break; 482 case type::t_struct: 483 assert(info.rows == 0 && info.cols == 0 && info.definition != 0); 484 type = info.definition; 485 break; 486 case type::t_sampler1d_int: 487 case type::t_sampler1d_uint: 488 case type::t_sampler1d_float: 489 add_capability(spv::CapabilitySampled1D); 490 [[fallthrough]]; 491 case type::t_sampler2d_int: 492 case type::t_sampler2d_uint: 493 case type::t_sampler2d_float: 494 case type::t_sampler3d_int: 495 case type::t_sampler3d_uint: 496 case type::t_sampler3d_float: 497 elem_type = convert_image_type(info, format); 498 add_instruction(spv::OpTypeSampledImage, 0, _types_and_constants, type) 499 .add(elem_type); 500 break; 501 case type::t_storage1d_int: 502 case type::t_storage1d_uint: 503 case type::t_storage1d_float: 504 add_capability(spv::CapabilityImage1D); 505 [[fallthrough]]; 506 case type::t_storage2d_int: 507 case type::t_storage2d_uint: 508 case type::t_storage2d_float: 509 case type::t_storage3d_int: 510 case type::t_storage3d_uint: 511 case type::t_storage3d_float: 512 // No format specified for the storage image 513 if (format == spv::ImageFormatUnknown) 514 add_capability(spv::CapabilityStorageImageWriteWithoutFormat); 515 return convert_image_type(info, format); 516 default: 517 return assert(false), 0; 518 } 519 } 520 521 _type_lookup.push_back({ lookup, type }); 522 523 return type; 524 } 525 spv::Id convert_type(const function_blocks &info) 526 { 527 if (const auto it = std::find_if(_function_type_lookup.begin(), _function_type_lookup.end(), 528 [&lookup = info](const auto &lookup_it) { return lookup_it.first == lookup; }); 529 it != _function_type_lookup.end()) 530 return it->second; 531 532 auto return_type = convert_type(info.return_type); 533 assert(return_type != 0); 534 535 std::vector<spv::Id> param_type_ids; 536 param_type_ids.reserve(info.param_types.size()); 537 for (const type ¶m_type : info.param_types) 538 param_type_ids.push_back(convert_type(param_type, true)); 539 540 spirv_instruction &inst = add_instruction(spv::OpTypeFunction, 0, _types_and_constants); 541 inst.add(return_type); 542 inst.add(param_type_ids.begin(), param_type_ids.end()); 543 544 _function_type_lookup.push_back({ info, inst.result });; 545 546 return inst.result; 547 } 548 spv::Id convert_image_type(type info, spv::ImageFormat format = spv::ImageFormatUnknown) 549 { 550 type_lookup lookup { info, false, 0u, { spv::StorageClassUniformConstant, format } }; 551 552 auto elem_info = info; 553 elem_info.rows = 1; 554 elem_info.cols = 1; 555 556 if (!info.is_numeric()) 557 { 558 if ((info.is_integral() && info.is_signed()) || (format >= spv::ImageFormatRgba32i && format <= spv::ImageFormatR8i)) 559 elem_info.base = type::t_int; 560 else if ((info.is_integral() && info.is_unsigned()) || (format >= spv::ImageFormatRgba32ui && format <= spv::ImageFormatR8ui)) 561 elem_info.base = type::t_uint; 562 else 563 elem_info.base = type::t_float; 564 } 565 566 if (!info.is_storage()) 567 { 568 lookup.type = elem_info; 569 lookup.type.base = static_cast<type::datatype>(type::t_texture1d + info.texture_dimension() - 1); 570 lookup.type.definition = static_cast<uint32_t>(elem_info.base); 571 } 572 573 if (const auto it = std::find_if(_type_lookup.begin(), _type_lookup.end(), 574 [&lookup](const auto &lookup_it) { return lookup_it.first == lookup; }); 575 it != _type_lookup.end()) 576 return it->second; 577 578 spv::Id type, elem_type = convert_type(elem_info, false, spv::StorageClassUniformConstant); 579 580 add_instruction(spv::OpTypeImage, 0, _types_and_constants, type) 581 .add(elem_type) // Sampled Type (always a scalar type) 582 .add(spv::Dim1D + info.texture_dimension() - 1) 583 .add(0) // Not a depth image 584 .add(0) // Not an array 585 .add(0) // Not multi-sampled 586 .add(info.is_storage() ? 2 : 1) // Used with a sampler or as storage 587 .add(format); 588 589 _type_lookup.push_back({ lookup, type }); 590 591 return type; 592 } 593 594 uint32_t semantic_to_location(const std::string &semantic, uint32_t max_array_length = 1) 595 { 596 if (semantic.compare(0, 5, "COLOR") == 0) 597 return std::strtoul(semantic.c_str() + 5, nullptr, 10); 598 if (semantic.compare(0, 9, "SV_TARGET") == 0) 599 return std::strtoul(semantic.c_str() + 9, nullptr, 10); 600 601 if (const auto it = _semantic_to_location.find(semantic); 602 it != _semantic_to_location.end()) 603 return it->second; 604 605 // Extract the semantic index from the semantic name (e.g. 2 for "TEXCOORD2") 606 size_t digit_index = semantic.size() - 1; 607 while (digit_index != 0 && semantic[digit_index] >= '0' && semantic[digit_index] <= '9') 608 digit_index--; 609 digit_index++; 610 611 const uint32_t semantic_digit = std::strtoul(semantic.c_str() + digit_index, nullptr, 10); 612 const std::string semantic_base = semantic.substr(0, digit_index); 613 614 uint32_t location = static_cast<uint32_t>(_semantic_to_location.size()); 615 616 // Now create adjoining location indices for all possible semantic indices belonging to this semantic name 617 for (uint32_t a = 0; a < semantic_digit + max_array_length; ++a) 618 { 619 const auto insert = _semantic_to_location.emplace(semantic_base + std::to_string(a), location + a); 620 if (!insert.second) 621 { 622 assert(a == 0 || (insert.first->second - a) == location); 623 624 // Semantic was already created with a different location index, so need to remap to that 625 location = insert.first->second - a; 626 } 627 } 628 629 return location + semantic_digit; 630 } 631 632 const spv::BuiltIn semantic_to_builtin(const std::string &semantic, shader_type stype) const 633 { 634 if (semantic == "SV_POSITION") 635 return stype == shader_type::ps ? spv::BuiltInFragCoord : spv::BuiltInPosition; 636 if (semantic == "SV_POINTSIZE") 637 return spv::BuiltInPointSize; 638 if (semantic == "SV_DEPTH") 639 return spv::BuiltInFragDepth; 640 if (semantic == "SV_VERTEXID") 641 return _vulkan_semantics ? spv::BuiltInVertexIndex : spv::BuiltInVertexId; 642 if (semantic == "SV_ISFRONTFACE") 643 return spv::BuiltInFrontFacing; 644 if (semantic == "SV_GROUPID") 645 return spv::BuiltInWorkgroupId; 646 if (semantic == "SV_GROUPINDEX") 647 return spv::BuiltInLocalInvocationIndex; 648 if (semantic == "SV_GROUPTHREADID") 649 return spv::BuiltInLocalInvocationId; 650 if (semantic == "SV_DISPATCHTHREADID") 651 return spv::BuiltInGlobalInvocationId; 652 return spv::BuiltInMax; 653 } 654 const spv::ImageFormat format_to_image_format(texture_format format) 655 { 656 switch (format) 657 { 658 default: 659 assert(false); 660 [[fallthrough]]; 661 case texture_format::unknown: 662 return spv::ImageFormatUnknown; 663 case texture_format::r8: 664 add_capability(spv::CapabilityStorageImageExtendedFormats); 665 return spv::ImageFormatR8; 666 case texture_format::r16: 667 add_capability(spv::CapabilityStorageImageExtendedFormats); 668 return spv::ImageFormatR16; 669 case texture_format::r16f: 670 add_capability(spv::CapabilityStorageImageExtendedFormats); 671 return spv::ImageFormatR16f; 672 case texture_format::r32i: 673 return spv::ImageFormatR32i; 674 case texture_format::r32u: 675 return spv::ImageFormatR32ui; 676 case texture_format::r32f: 677 return spv::ImageFormatR32f; 678 case texture_format::rg8: 679 add_capability(spv::CapabilityStorageImageExtendedFormats); 680 return spv::ImageFormatRg8; 681 case texture_format::rg16: 682 add_capability(spv::CapabilityStorageImageExtendedFormats); 683 return spv::ImageFormatRg16; 684 case texture_format::rg16f: 685 add_capability(spv::CapabilityStorageImageExtendedFormats); 686 return spv::ImageFormatRg16f; 687 case texture_format::rg32f: 688 add_capability(spv::CapabilityStorageImageExtendedFormats); 689 return spv::ImageFormatRg32f; 690 case texture_format::rgba8: 691 return spv::ImageFormatRgba8; 692 case texture_format::rgba16: 693 add_capability(spv::CapabilityStorageImageExtendedFormats); 694 return spv::ImageFormatRgba16; 695 case texture_format::rgba16f: 696 return spv::ImageFormatRgba16f; 697 case texture_format::rgba32f: 698 return spv::ImageFormatRgba32f; 699 case texture_format::rgb10a2: 700 add_capability(spv::CapabilityStorageImageExtendedFormats); 701 return spv::ImageFormatRgb10A2; 702 } 703 } 704 705 inline void add_name(id id, const char *name) 706 { 707 if (!_debug_info) 708 return; 709 710 assert(name != nullptr); 711 // https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpName 712 add_instruction_without_result(spv::OpName, _debug_b) 713 .add(id) 714 .add_string(name); 715 } 716 inline void add_builtin(id id, spv::BuiltIn builtin) 717 { 718 add_instruction_without_result(spv::OpDecorate, _annotations) 719 .add(id) 720 .add(spv::DecorationBuiltIn) 721 .add(builtin); 722 } 723 inline void add_decoration(id id, spv::Decoration decoration, std::initializer_list<uint32_t> values = {}) 724 { 725 // https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpDecorate 726 add_instruction_without_result(spv::OpDecorate, _annotations) 727 .add(id) 728 .add(decoration) 729 .add(values.begin(), values.end()); 730 } 731 inline void add_member_name(id id, uint32_t member_index, const char *name) 732 { 733 if (!_debug_info) 734 return; 735 736 assert(name != nullptr); 737 // https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpMemberName 738 add_instruction_without_result(spv::OpMemberName, _debug_b) 739 .add(id) 740 .add(member_index) 741 .add_string(name); 742 } 743 inline void add_member_builtin(id id, uint32_t member_index, spv::BuiltIn builtin) 744 { 745 add_instruction_without_result(spv::OpMemberDecorate, _annotations) 746 .add(id) 747 .add(member_index) 748 .add(spv::DecorationBuiltIn) 749 .add(builtin); 750 } 751 inline void add_member_decoration(id id, uint32_t member_index, spv::Decoration decoration, std::initializer_list<uint32_t> values = {}) 752 { 753 // https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpMemberDecorate 754 add_instruction_without_result(spv::OpMemberDecorate, _annotations) 755 .add(id) 756 .add(member_index) 757 .add(decoration) 758 .add(values.begin(), values.end()); 759 } 760 inline void add_capability(spv::Capability capability) 761 { 762 _capabilities.insert(capability); 763 } 764 765 id define_struct(const location &loc, struct_info &info) override 766 { 767 // First define all member types to make sure they are declared before the struct type references them 768 std::vector<spv::Id> member_types; 769 member_types.reserve(info.member_list.size()); 770 for (const struct_member_info &member : info.member_list) 771 member_types.push_back(convert_type(member.type)); 772 773 // Afterwards define the actual struct type 774 add_location(loc, _types_and_constants); 775 776 add_instruction(spv::OpTypeStruct, 0, _types_and_constants, info.definition) 777 .add(member_types.begin(), member_types.end()); 778 779 if (!info.unique_name.empty()) 780 add_name(info.definition, info.unique_name.c_str()); 781 782 for (uint32_t index = 0; index < info.member_list.size(); ++index) 783 { 784 const struct_member_info &member = info.member_list[index]; 785 786 add_member_name(info.definition, index, member.name.c_str()); 787 788 if (!_enable_16bit_types && member.type.is_numeric() && member.type.precision() < 32) 789 add_member_decoration(info.definition, index, spv::DecorationRelaxedPrecision); 790 } 791 792 _structs.push_back(info); 793 794 return info.definition; 795 } 796 id define_texture(const location &, texture_info &info) override 797 { 798 info.id = make_id(); // Need to create an unique ID here too, so that the symbol lookup for textures works 799 info.binding = ~0u; 800 801 _module.textures.push_back(info); 802 803 return info.id; 804 } 805 id define_sampler(const location &loc, const texture_info &, sampler_info &info) override 806 { 807 info.id = define_variable(loc, info.type, info.unique_name.c_str(), spv::StorageClassUniformConstant); 808 info.binding = _module.num_sampler_bindings++; 809 info.texture_binding = ~0u; 810 811 add_decoration(info.id, spv::DecorationBinding, { info.binding }); 812 add_decoration(info.id, spv::DecorationDescriptorSet, { 1 }); 813 814 _module.samplers.push_back(info); 815 816 return info.id; 817 } 818 id define_storage(const location &loc, const texture_info &tex_info, storage_info &info) override 819 { 820 info.id = define_variable(loc, info.type, info.unique_name.c_str(), spv::StorageClassUniformConstant, format_to_image_format(tex_info.format)); 821 info.binding = _module.num_storage_bindings++; 822 823 add_decoration(info.id, spv::DecorationBinding, { info.binding }); 824 add_decoration(info.id, spv::DecorationDescriptorSet, { 2 }); 825 826 _module.storages.push_back(info); 827 828 return info.id; 829 } 830 id define_uniform(const location &, uniform_info &info) override 831 { 832 if (_uniforms_to_spec_constants && info.has_initializer_value) 833 { 834 const id res = emit_constant(info.type, info.initializer_value, true); 835 836 add_name(res, info.name.c_str()); 837 838 const auto add_spec_constant = [this](const spirv_instruction &inst, const uniform_info &info, const constant &initializer_value, size_t initializer_offset) { 839 assert(inst.op == spv::OpSpecConstant || inst.op == spv::OpSpecConstantTrue || inst.op == spv::OpSpecConstantFalse); 840 841 const uint32_t spec_id = static_cast<uint32_t>(_module.spec_constants.size()); 842 add_decoration(inst.result, spv::DecorationSpecId, { spec_id }); 843 844 uniform_info scalar_info = info; 845 scalar_info.type.rows = 1; 846 scalar_info.type.cols = 1; 847 scalar_info.size = 4; 848 scalar_info.offset = static_cast<uint32_t>(initializer_offset); 849 scalar_info.initializer_value = {}; 850 scalar_info.initializer_value.as_uint[0] = initializer_value.as_uint[initializer_offset]; 851 852 _module.spec_constants.push_back(scalar_info); 853 }; 854 855 const spirv_instruction &base_inst = _types_and_constants.instructions.back(); 856 assert(base_inst.result == res); 857 858 // External specialization constants need to be scalars 859 if (info.type.is_scalar()) 860 { 861 add_spec_constant(base_inst, info, info.initializer_value, 0); 862 } 863 else 864 { 865 assert(base_inst.op == spv::OpSpecConstantComposite); 866 867 // Add each individual scalar component of the constant as a separate external specialization constant 868 for (size_t i = 0; i < (info.type.is_array() ? base_inst.operands.size() : 1); ++i) 869 { 870 constant initializer_value = info.initializer_value; 871 spirv_instruction elem_inst = base_inst; 872 873 if (info.type.is_array()) 874 { 875 elem_inst = *std::find_if(_types_and_constants.instructions.rbegin(), _types_and_constants.instructions.rend(), 876 [elem = base_inst.operands[i]](const auto &it) { return it.result == elem; }); 877 878 assert(initializer_value.array_data.size() == base_inst.operands.size()); 879 initializer_value = initializer_value.array_data[i]; 880 } 881 882 for (size_t row = 0; row < elem_inst.operands.size(); ++row) 883 { 884 const spirv_instruction &row_inst = *std::find_if(_types_and_constants.instructions.rbegin(), _types_and_constants.instructions.rend(), 885 [elem = elem_inst.operands[row]](const auto &it) { return it.result == elem; }); 886 887 if (row_inst.op != spv::OpSpecConstantComposite) 888 { 889 add_spec_constant(row_inst, info, initializer_value, row); 890 continue; 891 } 892 893 for (size_t col = 0; col < row_inst.operands.size(); ++col) 894 { 895 const spirv_instruction &col_inst = *std::find_if(_types_and_constants.instructions.rbegin(), _types_and_constants.instructions.rend(), 896 [elem = row_inst.operands[col]](const auto &it) { return it.result == elem; }); 897 898 add_spec_constant(col_inst, info, initializer_value, row * info.type.cols + col); 899 } 900 } 901 } 902 } 903 904 return res; 905 } 906 else 907 { 908 // Create global uniform buffer variable on demand 909 if (_global_ubo_type == 0) 910 { 911 _global_ubo_type = make_id(); 912 913 add_decoration(_global_ubo_type, spv::DecorationBlock); 914 } 915 if (_global_ubo_variable == 0) 916 { 917 _global_ubo_variable = make_id(); 918 919 add_decoration(_global_ubo_variable, spv::DecorationDescriptorSet, { 0 }); 920 add_decoration(_global_ubo_variable, spv::DecorationBinding, { 0 }); 921 } 922 923 uint32_t alignment = (info.type.rows == 3 ? 4 : info.type.rows) * 4; 924 info.size = info.type.rows * 4; 925 926 uint32_t array_stride = 16; 927 const uint32_t matrix_stride = 16; 928 929 if (info.type.is_matrix()) 930 { 931 alignment = matrix_stride; 932 info.size = info.type.rows * matrix_stride; 933 } 934 if (info.type.is_array()) 935 { 936 alignment = array_stride; 937 array_stride = align_up(info.size, array_stride); 938 // Uniform block rules do not permit anything in the padding of an array 939 info.size = array_stride * info.type.array_length; 940 } 941 942 info.offset = _module.total_uniform_size; 943 info.offset = align_up(info.offset, alignment); 944 _module.total_uniform_size = info.offset + info.size; 945 946 type ubo_type = info.type; 947 // Convert boolean uniform variables to integer type so that they have a defined size 948 if (info.type.is_boolean()) 949 ubo_type.base = type::t_uint; 950 951 const uint32_t member_index = static_cast<uint32_t>(_global_ubo_types.size()); 952 953 // Composite objects in the uniform storage class must be explicitly laid out, which includes array types requiring a stride decoration 954 _global_ubo_types.push_back( 955 convert_type(ubo_type, false, spv::StorageClassUniform, spv::ImageFormatUnknown, info.type.is_array() ? array_stride : 0u)); 956 957 add_member_name(_global_ubo_type, member_index, info.name.c_str()); 958 959 add_member_decoration(_global_ubo_type, member_index, spv::DecorationOffset, { info.offset }); 960 961 if (info.type.is_matrix()) 962 { 963 // Read matrices in column major layout, even though they are actually row major, to avoid transposing them on every access (since SPIR-V uses column matrices) 964 // TODO: This technically only works with square matrices 965 add_member_decoration(_global_ubo_type, member_index, spv::DecorationColMajor); 966 add_member_decoration(_global_ubo_type, member_index, spv::DecorationMatrixStride, { matrix_stride }); 967 } 968 969 _module.uniforms.push_back(info); 970 971 return 0xF0000000 | member_index; 972 } 973 } 974 id define_variable(const location &loc, const type &type, std::string name, bool global, id initializer_value) override 975 { 976 spv::StorageClass storage = spv::StorageClassFunction; 977 if (type.has(type::q_groupshared)) 978 storage = spv::StorageClassWorkgroup; 979 else if (global) 980 storage = spv::StorageClassPrivate; 981 982 return define_variable(loc, type, name.c_str(), storage, spv::ImageFormatUnknown, initializer_value); 983 } 984 id define_variable(const location &loc, const type &type, const char *name, spv::StorageClass storage, spv::ImageFormat format = spv::ImageFormatUnknown, spv::Id initializer_value = 0) 985 { 986 assert(storage != spv::StorageClassFunction || _current_function != nullptr); 987 988 spirv_basic_block &block = (storage != spv::StorageClassFunction) ? 989 _variables : _current_function->variables; 990 991 add_location(loc, block); 992 993 spv::Id res; 994 // https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpVariable 995 spirv_instruction &inst = add_instruction(spv::OpVariable, convert_type(type, true, storage, format), block, res) 996 .add(storage); 997 998 if (initializer_value != 0) 999 { 1000 if (storage != spv::StorageClassFunction || _current_function->is_entry_point) 1001 { 1002 // The initializer for variables must be a constant 1003 inst.add(initializer_value); 1004 } 1005 else 1006 { 1007 // Only use the variable initializer on global variables, since local variables for e.g. "for" statements need to be assigned in their respective scope and not their declaration 1008 expression variable; 1009 variable.reset_to_lvalue(loc, res, type); 1010 emit_store(variable, initializer_value); 1011 } 1012 } 1013 1014 if (name != nullptr && *name != '\0') 1015 add_name(res, name); 1016 1017 if (!_enable_16bit_types && type.is_numeric() && type.precision() < 32) 1018 add_decoration(res, spv::DecorationRelaxedPrecision); 1019 1020 _storage_lookup[res] = { storage, format }; 1021 1022 return res; 1023 } 1024 id define_function(const location &loc, function_info &info) override 1025 { 1026 assert(!is_in_function()); 1027 1028 auto &function = _functions_blocks.emplace_back(); 1029 function.return_type = info.return_type; 1030 1031 _current_function = &function; 1032 1033 for (auto ¶m : info.parameter_list) 1034 function.param_types.push_back(param.type); 1035 1036 add_location(loc, function.declaration); 1037 1038 // https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpFunction 1039 add_instruction(spv::OpFunction, convert_type(info.return_type), function.declaration, info.definition) 1040 .add(spv::FunctionControlMaskNone) 1041 .add(convert_type(function)); 1042 1043 if (!info.name.empty()) 1044 add_name(info.definition, info.name.c_str()); 1045 1046 for (auto ¶m : info.parameter_list) 1047 { 1048 add_location(param.location, function.declaration); 1049 1050 param.definition = add_instruction(spv::OpFunctionParameter, convert_type(param.type, true), function.declaration).result; 1051 1052 add_name(param.definition, param.name.c_str()); 1053 } 1054 1055 _functions.push_back(std::make_unique<function_info>(info)); 1056 1057 return info.definition; 1058 } 1059 1060 void define_entry_point(function_info &func, shader_type stype, int num_threads[3]) override 1061 { 1062 // Modify entry point name so each thread configuration is made separate 1063 if (stype == shader_type::cs) 1064 func.unique_name = 'E' + func.unique_name + 1065 '_' + std::to_string(num_threads[0]) + 1066 '_' + std::to_string(num_threads[1]) + 1067 '_' + std::to_string(num_threads[2]); 1068 1069 if (const auto it = std::find_if(_module.entry_points.begin(), _module.entry_points.end(), 1070 [&func](const auto &ep) { return ep.name == func.unique_name; }); 1071 it != _module.entry_points.end()) 1072 return; 1073 1074 _module.entry_points.push_back({ func.unique_name, stype }); 1075 1076 spv::Id position_variable = 0, point_size_variable = 0; 1077 std::vector<spv::Id> inputs_and_outputs; 1078 std::vector<expression> call_params; 1079 1080 // Generate the glue entry point function 1081 function_info entry_point; 1082 entry_point.return_type = { type::t_void }; 1083 1084 define_function({}, entry_point); 1085 enter_block(create_block()); 1086 1087 _current_function->is_entry_point = true; 1088 1089 const auto create_varying_param = [this, &call_params](const struct_member_info ¶m) { 1090 // Initialize all output variables with zero 1091 const spv::Id variable = define_variable({}, param.type, nullptr, spv::StorageClassFunction, spv::ImageFormatUnknown, emit_constant(param.type, 0u)); 1092 1093 expression &call_param = call_params.emplace_back(); 1094 call_param.reset_to_lvalue({}, variable, param.type); 1095 1096 return variable; 1097 }; 1098 1099 const auto create_varying_variable = [this, &inputs_and_outputs, &position_variable, &point_size_variable, stype](const type ¶m_type, const std::string &semantic, spv::StorageClass storage, int a = 0) { 1100 const spv::Id variable = define_variable({}, param_type, nullptr, storage); 1101 1102 if (const spv::BuiltIn builtin = semantic_to_builtin(semantic, stype); 1103 builtin != spv::BuiltInMax) 1104 { 1105 assert(a == 0); // Built-in variables cannot be arrays 1106 1107 add_builtin(variable, builtin); 1108 1109 if (builtin == spv::BuiltInPosition && storage == spv::StorageClassOutput) 1110 position_variable = variable; 1111 if (builtin == spv::BuiltInPointSize && storage == spv::StorageClassOutput) 1112 point_size_variable = variable; 1113 } 1114 else 1115 { 1116 assert(stype != shader_type::cs); // Compute shaders cannot have custom inputs or outputs 1117 1118 const uint32_t location = semantic_to_location(semantic, std::max(1, param_type.array_length)); 1119 add_decoration(variable, spv::DecorationLocation, { location + a }); 1120 } 1121 1122 if (param_type.has(type::q_noperspective)) 1123 add_decoration(variable, spv::DecorationNoPerspective); 1124 if (param_type.has(type::q_centroid)) 1125 add_decoration(variable, spv::DecorationCentroid); 1126 if (param_type.has(type::q_nointerpolation)) 1127 add_decoration(variable, spv::DecorationFlat); 1128 1129 inputs_and_outputs.push_back(variable); 1130 return variable; 1131 }; 1132 1133 // Translate function parameters to input/output variables 1134 for (const struct_member_info ¶m : func.parameter_list) 1135 { 1136 spv::Id param_var = create_varying_param(param); 1137 1138 // Create separate input/output variables for "inout" parameters 1139 if (param.type.has(type::q_in)) 1140 { 1141 spv::Id param_value = 0; 1142 1143 // Flatten structure parameters 1144 if (param.type.is_struct()) 1145 { 1146 const struct_info &definition = get_struct(param.type.definition); 1147 1148 type struct_type = param.type; 1149 const int array_length = std::max(1, param.type.array_length); 1150 struct_type.array_length = 0; 1151 1152 // Struct arrays need to be flattened into individual elements as well 1153 std::vector<spv::Id> array_elements; 1154 array_elements.reserve(array_length); 1155 for (int a = 0; a < array_length; a++) 1156 { 1157 std::vector<spv::Id> struct_elements; 1158 struct_elements.reserve(definition.member_list.size()); 1159 for (const struct_member_info &member : definition.member_list) 1160 { 1161 spv::Id input_var = create_varying_variable(member.type, member.semantic, spv::StorageClassInput, a); 1162 1163 param_value = add_instruction(spv::OpLoad, convert_type(member.type)) 1164 .add(input_var).result; 1165 struct_elements.push_back(param_value); 1166 } 1167 1168 param_value = add_instruction(spv::OpCompositeConstruct, convert_type(struct_type)) 1169 .add(struct_elements.begin(), struct_elements.end()).result; 1170 array_elements.push_back(param_value); 1171 } 1172 1173 if (param.type.is_array()) 1174 { 1175 // Build the array from all constructed struct elements 1176 param_value = add_instruction(spv::OpCompositeConstruct, convert_type(param.type)) 1177 .add(array_elements.begin(), array_elements.end()).result; 1178 } 1179 } 1180 else 1181 { 1182 spv::Id input_var = create_varying_variable(param.type, param.semantic, spv::StorageClassInput); 1183 1184 param_value = add_instruction(spv::OpLoad, convert_type(param.type)) 1185 .add(input_var).result; 1186 } 1187 1188 add_instruction_without_result(spv::OpStore) 1189 .add(param_var) 1190 .add(param_value); 1191 } 1192 1193 if (param.type.has(type::q_out)) 1194 { 1195 if (param.type.is_struct()) 1196 { 1197 const struct_info &definition = get_struct(param.type.definition); 1198 1199 for (int a = 0, array_length = std::max(1, param.type.array_length); a < array_length; a++) 1200 { 1201 for (const struct_member_info &member : definition.member_list) 1202 { 1203 create_varying_variable(member.type, member.semantic, spv::StorageClassOutput, a); 1204 } 1205 } 1206 } 1207 else 1208 { 1209 create_varying_variable(param.type, param.semantic, spv::StorageClassOutput); 1210 } 1211 } 1212 } 1213 1214 const auto call_result = emit_call({}, func.definition, func.return_type, call_params); 1215 1216 for (size_t i = 0, inputs_and_outputs_index = 0; i < func.parameter_list.size(); ++i) 1217 { 1218 const struct_member_info ¶m = func.parameter_list[i]; 1219 1220 if (param.type.has(type::q_out)) 1221 { 1222 const spv::Id value = add_instruction(spv::OpLoad, convert_type(param.type)) 1223 .add(call_params[i].base).result; 1224 1225 if (param.type.is_struct()) 1226 { 1227 const struct_info &definition = get_struct(param.type.definition); 1228 1229 type struct_type = param.type; 1230 const int array_length = std::max(1, param.type.array_length); 1231 struct_type.array_length = 0; 1232 1233 // Skip input variables if this is an "inout" parameter 1234 if (param.type.has(type::q_in)) 1235 inputs_and_outputs_index += definition.member_list.size() * array_length; 1236 1237 // Split up struct array into individual struct elements again 1238 for (int a = 0; a < array_length; a++) 1239 { 1240 spv::Id element_value = value; 1241 if (param.type.is_array()) 1242 { 1243 element_value = add_instruction(spv::OpCompositeExtract, convert_type(struct_type)) 1244 .add(value) 1245 .add(a).result; 1246 } 1247 1248 // Split out struct fields into separate output variables again 1249 for (uint32_t member_index = 0; member_index < definition.member_list.size(); ++member_index) 1250 { 1251 const struct_member_info &member = definition.member_list[member_index]; 1252 1253 const spv::Id member_value = add_instruction(spv::OpCompositeExtract, convert_type(member.type)) 1254 .add(element_value) 1255 .add(member_index).result; 1256 1257 add_instruction_without_result(spv::OpStore) 1258 .add(inputs_and_outputs[inputs_and_outputs_index++]) 1259 .add(member_value); 1260 } 1261 } 1262 } 1263 else 1264 { 1265 // Skip input variable if this is an "inout" parameter (see loop above) 1266 if (param.type.has(type::q_in)) 1267 inputs_and_outputs_index += 1; 1268 1269 add_instruction_without_result(spv::OpStore) 1270 .add(inputs_and_outputs[inputs_and_outputs_index++]) 1271 .add(value); 1272 } 1273 } 1274 else 1275 { 1276 // Input parameters do not need to store anything, but increase the input/output variable index 1277 if (param.type.is_struct()) 1278 { 1279 const struct_info &definition = get_struct(param.type.definition); 1280 inputs_and_outputs_index += definition.member_list.size() * std::max(1, param.type.array_length); 1281 } 1282 else 1283 { 1284 inputs_and_outputs_index += 1; 1285 } 1286 } 1287 } 1288 1289 if (func.return_type.is_struct()) 1290 { 1291 const struct_info &definition = get_struct(func.return_type.definition); 1292 1293 for (uint32_t member_index = 0; member_index < definition.member_list.size(); ++member_index) 1294 { 1295 const struct_member_info &member = definition.member_list[member_index]; 1296 1297 const spv::Id result = create_varying_variable(member.type, member.semantic, spv::StorageClassOutput); 1298 const spv::Id member_result = add_instruction(spv::OpCompositeExtract, convert_type(member.type)) 1299 .add(call_result) 1300 .add(member_index).result; 1301 1302 add_instruction_without_result(spv::OpStore) 1303 .add(result) 1304 .add(member_result); 1305 } 1306 } 1307 else if (!func.return_type.is_void()) 1308 { 1309 const spv::Id result = create_varying_variable(func.return_type, func.return_semantic, spv::StorageClassOutput); 1310 1311 add_instruction_without_result(spv::OpStore) 1312 .add(result) 1313 .add(call_result); 1314 } 1315 1316 // Add code to flip the output vertically 1317 if (_flip_vert_y && position_variable != 0 && stype == shader_type::vs) 1318 { 1319 expression position; 1320 position.reset_to_lvalue({}, position_variable, { type::t_float, 4, 1 }); 1321 position.add_constant_index_access(1); // Y component 1322 1323 // gl_Position.y = -gl_Position.y 1324 emit_store(position, 1325 emit_unary_op({}, tokenid::minus, { type::t_float, 1, 1 }, 1326 emit_load(position, false))); 1327 } 1328 1329 // Add code that sets the point size to a default value (in case this vertex shader is used with point primitives) 1330 if (point_size_variable == 0 && stype == shader_type::vs) 1331 { 1332 create_varying_variable({ type::t_float, 1, 1 }, "SV_POINTSIZE", spv::StorageClassOutput); 1333 1334 expression point_size; 1335 point_size.reset_to_lvalue({}, point_size_variable, { type::t_float, 1, 1 }); 1336 1337 // gl_PointSize = 1.0 1338 emit_store(point_size, emit_constant({ type::t_float, 1, 1 }, 1)); 1339 } 1340 1341 leave_block_and_return(0); 1342 leave_function(); 1343 1344 spv::ExecutionModel model; 1345 switch (stype) 1346 { 1347 case shader_type::vs: 1348 model = spv::ExecutionModelVertex; 1349 break; 1350 case shader_type::ps: 1351 model = spv::ExecutionModelFragment; 1352 add_instruction_without_result(spv::OpExecutionMode, _execution_modes) 1353 .add(entry_point.definition) 1354 .add(_vulkan_semantics ? spv::ExecutionModeOriginUpperLeft : spv::ExecutionModeOriginLowerLeft); 1355 break; 1356 case shader_type::cs: 1357 model = spv::ExecutionModelGLCompute; 1358 add_instruction_without_result(spv::OpExecutionMode, _execution_modes) 1359 .add(entry_point.definition) 1360 .add(spv::ExecutionModeLocalSize) 1361 .add(num_threads[0]) 1362 .add(num_threads[1]) 1363 .add(num_threads[2]); 1364 break; 1365 default: 1366 assert(false); 1367 return; 1368 } 1369 1370 assert(!func.unique_name.empty()); 1371 add_instruction_without_result(spv::OpEntryPoint, _entries) 1372 .add(model) 1373 .add(entry_point.definition) 1374 .add_string(func.unique_name.c_str()) 1375 .add(inputs_and_outputs.begin(), inputs_and_outputs.end()); 1376 } 1377 1378 id emit_load(const expression &exp, bool) override 1379 { 1380 if (exp.is_constant) // Constant expressions do not have a complex access chain 1381 return emit_constant(exp.type, exp.constant); 1382 1383 size_t i = 0; 1384 spv::Id result = exp.base; 1385 auto base_type = exp.type; 1386 bool is_uniform_bool = false; 1387 1388 if (exp.is_lvalue || !exp.chain.empty()) 1389 add_location(exp.location, *_current_block_data); 1390 1391 // If a variable is referenced, load the value first 1392 if (exp.is_lvalue && _spec_constants.find(exp.base) == _spec_constants.end()) 1393 { 1394 if (!exp.chain.empty()) 1395 base_type = exp.chain[0].from; 1396 1397 std::pair<spv::StorageClass, spv::ImageFormat> storage = { spv::StorageClassFunction, spv::ImageFormatUnknown }; 1398 if (const auto it = _storage_lookup.find(exp.base); 1399 it != _storage_lookup.end()) 1400 storage = it->second; 1401 1402 spirv_instruction *access_chain = nullptr; 1403 1404 // Check if this is a uniform variable (see 'define_uniform' function above) and dereference it 1405 if (result & 0xF0000000) 1406 { 1407 const uint32_t member_index = result ^ 0xF0000000; 1408 1409 storage.first = spv::StorageClassUniform; 1410 is_uniform_bool = base_type.is_boolean(); 1411 1412 if (is_uniform_bool) 1413 base_type.base = type::t_uint; 1414 1415 access_chain = &add_instruction(spv::OpAccessChain) 1416 .add(_global_ubo_variable) 1417 .add(emit_constant(member_index)); 1418 } 1419 1420 // Any indexing expressions can be resolved during load with an 'OpAccessChain' already 1421 if (!exp.chain.empty() && ( 1422 exp.chain[0].op == expression::operation::op_member || 1423 exp.chain[0].op == expression::operation::op_dynamic_index || 1424 exp.chain[0].op == expression::operation::op_constant_index)) 1425 { 1426 // Ensure that 'access_chain' cannot get invalidated by calls to 'emit_constant' or 'convert_type' 1427 assert(_current_block_data != &_types_and_constants); 1428 1429 // Use access chain from uniform if possible, otherwise create new one 1430 if (access_chain == nullptr) access_chain = 1431 &add_instruction(spv::OpAccessChain).add(result); // Base 1432 1433 // Ignore first index into 1xN matrices, since they were translated to a vector type in SPIR-V 1434 if (exp.chain[0].from.rows == 1 && exp.chain[0].from.cols > 1) 1435 i = 1; 1436 1437 for (; i < exp.chain.size() && ( 1438 exp.chain[i].op == expression::operation::op_member || 1439 exp.chain[i].op == expression::operation::op_dynamic_index || 1440 exp.chain[i].op == expression::operation::op_constant_index); ++i) 1441 access_chain->add(exp.chain[i].op == expression::operation::op_dynamic_index ? 1442 exp.chain[i].index : 1443 emit_constant(exp.chain[i].index)); // Indexes 1444 1445 base_type = exp.chain[i - 1].to; 1446 access_chain->type = convert_type(base_type, true, storage.first, storage.second); // Last type is the result 1447 result = access_chain->result; 1448 } 1449 else if (access_chain != nullptr) 1450 { 1451 access_chain->type = convert_type(base_type, true, storage.first, storage.second, base_type.is_array() ? 16u : 0u); 1452 result = access_chain->result; 1453 } 1454 1455 result = add_instruction(spv::OpLoad, convert_type(base_type, false, spv::StorageClassFunction, storage.second)) 1456 .add(result) // Pointer 1457 .result; 1458 } 1459 1460 // Need to convert boolean uniforms which are actually integers in SPIR-V 1461 if (is_uniform_bool) 1462 { 1463 base_type.base = type::t_bool; 1464 1465 result = add_instruction(spv::OpINotEqual, convert_type(base_type)) 1466 .add(result) 1467 .add(emit_constant(0)) 1468 .result; 1469 } 1470 1471 // Work through all remaining operations in the access chain and apply them to the value 1472 for (; i < exp.chain.size(); ++i) 1473 { 1474 assert(result != 0); 1475 const auto &op = exp.chain[i]; 1476 1477 switch (op.op) 1478 { 1479 case expression::operation::op_cast: 1480 if (op.from.is_scalar() && !op.to.is_scalar()) 1481 { 1482 type cast_type = op.to; 1483 cast_type.base = op.from.base; 1484 1485 std::vector<expression> args; 1486 for (unsigned int c = 0; c < op.to.components(); ++c) 1487 args.emplace_back().reset_to_rvalue(exp.location, result, op.from); 1488 1489 result = emit_construct(exp.location, cast_type, args); 1490 } 1491 1492 if (op.from.is_boolean()) 1493 { 1494 const spv::Id true_constant = emit_constant(op.to, 1); 1495 const spv::Id false_constant = emit_constant(op.to, 0); 1496 1497 result = add_instruction(spv::OpSelect, convert_type(op.to)) 1498 .add(result) // Condition 1499 .add(true_constant) 1500 .add(false_constant) 1501 .result; 1502 } 1503 else 1504 { 1505 spv::Op spv_op = spv::OpNop; 1506 switch (op.to.base) 1507 { 1508 case type::t_bool: 1509 if (op.from.is_floating_point()) 1510 spv_op = spv::OpFOrdNotEqual; 1511 else 1512 spv_op = spv::OpINotEqual; 1513 // Add instruction to compare value against zero instead of casting 1514 result = add_instruction(spv_op, convert_type(op.to)) 1515 .add(result) 1516 .add(emit_constant(op.from, 0)) 1517 .result; 1518 continue; 1519 case type::t_min16int: 1520 case type::t_int: 1521 if (op.from.is_floating_point()) 1522 spv_op = spv::OpConvertFToS; 1523 else if (op.from.precision() == op.to.precision()) 1524 spv_op = spv::OpBitcast; 1525 else if (_enable_16bit_types) 1526 spv_op = spv::OpSConvert; 1527 else 1528 continue; // Do not have to add conversion instruction between min16int/int if 16-bit types are not enabled 1529 break; 1530 case type::t_min16uint: 1531 case type::t_uint: 1532 if (op.from.is_floating_point()) 1533 spv_op = spv::OpConvertFToU; 1534 else if (op.from.precision() == op.to.precision()) 1535 spv_op = spv::OpBitcast; 1536 else if (_enable_16bit_types) 1537 spv_op = spv::OpUConvert; 1538 else 1539 continue; 1540 break; 1541 case type::t_min16float: 1542 case type::t_float: 1543 if (op.from.is_floating_point() && !_enable_16bit_types) 1544 continue; // Do not have to add conversion instruction between min16float/float if 16-bit types are not enabled 1545 else if (op.from.is_floating_point()) 1546 spv_op = spv::OpFConvert; 1547 else if (op.from.is_signed()) 1548 spv_op = spv::OpConvertSToF; 1549 else 1550 spv_op = spv::OpConvertUToF; 1551 break; 1552 default: 1553 assert(false); 1554 } 1555 1556 result = add_instruction(spv_op, convert_type(op.to)) 1557 .add(result) 1558 .result; 1559 } 1560 break; 1561 case expression::operation::op_dynamic_index: 1562 assert(op.from.is_vector() && op.to.is_scalar()); 1563 result = add_instruction(spv::OpVectorExtractDynamic, convert_type(op.to)) 1564 .add(result) // Vector 1565 .add(op.index) // Index 1566 .result; 1567 break; 1568 case expression::operation::op_member: // In case of struct return values, which are r-values 1569 case expression::operation::op_constant_index: 1570 assert(op.from.is_vector() || op.from.is_matrix() || op.from.is_struct()); 1571 result = add_instruction(spv::OpCompositeExtract, convert_type(op.to)) 1572 .add(result) 1573 .add(op.index) // Literal Index 1574 .result; 1575 break; 1576 case expression::operation::op_swizzle: 1577 if (op.to.is_vector()) 1578 { 1579 if (op.from.is_matrix()) 1580 { 1581 spv::Id components[4]; 1582 for (unsigned int c = 0; c < 4 && op.swizzle[c] >= 0; ++c) 1583 { 1584 const unsigned int row = op.swizzle[c] / 4; 1585 const unsigned int column = op.swizzle[c] - row * 4; 1586 1587 type scalar_type = op.to; 1588 scalar_type.rows = 1; 1589 scalar_type.cols = 1; 1590 1591 spirv_instruction &node = add_instruction(spv::OpCompositeExtract, convert_type(scalar_type)) 1592 .add(result); 1593 1594 if (op.from.rows > 1) // Matrix types with a single row are actually vectors, so they don't need the extra index 1595 node.add(row); 1596 1597 node.add(column); 1598 1599 components[c] = node.result; 1600 } 1601 1602 spirv_instruction &node = add_instruction(spv::OpCompositeConstruct, convert_type(op.to)); 1603 for (unsigned int c = 0; c < 4 && op.swizzle[c] >= 0; ++c) 1604 node.add(components[c]); 1605 result = node.result; 1606 break; 1607 } 1608 else if (op.from.is_vector()) 1609 { 1610 spirv_instruction &node = add_instruction(spv::OpVectorShuffle, convert_type(op.to)) 1611 .add(result) // Vector 1 1612 .add(result); // Vector 2 1613 for (unsigned int c = 0; c < 4 && op.swizzle[c] >= 0; ++c) 1614 node.add(op.swizzle[c]); 1615 result = node.result; 1616 break; 1617 } 1618 else 1619 { 1620 spirv_instruction &node = add_instruction(spv::OpCompositeConstruct, convert_type(op.to)); 1621 for (unsigned int c = 0; c < op.to.rows; ++c) 1622 node.add(result); 1623 result = node.result; 1624 break; 1625 } 1626 } 1627 else if (op.from.is_matrix() && op.to.is_scalar()) 1628 { 1629 assert(op.swizzle[1] < 0); 1630 1631 spirv_instruction &node = add_instruction(spv::OpCompositeExtract, convert_type(op.to)) 1632 .add(result); // Composite 1633 if (op.from.rows > 1) 1634 { 1635 const unsigned int row = op.swizzle[0] / 4; 1636 const unsigned int column = op.swizzle[0] - row * 4; 1637 node.add(row); 1638 node.add(column); 1639 } 1640 else 1641 { 1642 node.add(op.swizzle[0]); 1643 } 1644 result = node.result; // Result ID 1645 break; 1646 } 1647 assert(false); 1648 break; 1649 } 1650 } 1651 1652 return result; 1653 } 1654 void emit_store(const expression &exp, id value) override 1655 { 1656 assert(value != 0 && exp.is_lvalue && !exp.is_constant && !exp.type.is_sampler()); 1657 1658 add_location(exp.location, *_current_block_data); 1659 1660 size_t i = 0; 1661 // Any indexing expressions can be resolved with an 'OpAccessChain' already 1662 spv::Id target = emit_access_chain(exp, i); 1663 auto base_type = exp.chain.empty() ? exp.type : i == 0 ? exp.chain[0].from : exp.chain[i - 1].to; 1664 1665 // TODO: Complex access chains like float4x4[0].m00m10[0] = 0; 1666 // Work through all remaining operations in the access chain and apply them to the value 1667 for (; i < exp.chain.size(); ++i) 1668 { 1669 const auto &op = exp.chain[i]; 1670 switch (op.op) 1671 { 1672 case expression::operation::op_cast: 1673 case expression::operation::op_member: 1674 // These should have been handled above already (and casting does not make sense for a store operation) 1675 break; 1676 case expression::operation::op_dynamic_index: 1677 case expression::operation::op_constant_index: 1678 assert(false); 1679 break; 1680 case expression::operation::op_swizzle: 1681 { 1682 spv::Id result = add_instruction(spv::OpLoad, convert_type(base_type)) 1683 .add(target) // Pointer 1684 .result; // Result ID 1685 1686 if (base_type.is_vector()) 1687 { 1688 spirv_instruction &node = add_instruction(spv::OpVectorShuffle, convert_type(base_type)) 1689 .add(result) // Vector 1 1690 .add(value); // Vector 2 1691 1692 unsigned int shuffle[4] = { 0, 1, 2, 3 }; 1693 for (unsigned int c = 0; c < base_type.rows; ++c) 1694 if (op.swizzle[c] >= 0) 1695 shuffle[op.swizzle[c]] = base_type.rows + c; 1696 for (unsigned int c = 0; c < base_type.rows; ++c) 1697 node.add(shuffle[c]); 1698 1699 value = node.result; 1700 } 1701 else if (op.to.is_scalar()) 1702 { 1703 assert(op.swizzle[1] < 0); 1704 1705 spirv_instruction &node = add_instruction(spv::OpCompositeInsert, convert_type(base_type)) 1706 .add(value) // Object 1707 .add(result); // Composite 1708 1709 if (op.from.is_matrix() && op.from.rows > 1) 1710 { 1711 const unsigned int row = op.swizzle[0] / 4; 1712 const unsigned int column = op.swizzle[0] - row * 4; 1713 node.add(row); 1714 node.add(column); 1715 } 1716 else 1717 { 1718 node.add(op.swizzle[0]); 1719 } 1720 1721 value = node.result; // Result ID 1722 } 1723 else 1724 { 1725 // TODO: Implement matrix to vector swizzles 1726 assert(false); 1727 } 1728 break; 1729 } 1730 } 1731 } 1732 1733 add_instruction_without_result(spv::OpStore) 1734 .add(target) 1735 .add(value); 1736 } 1737 id emit_access_chain(const expression &exp, size_t &i) override 1738 { 1739 // This function cannot create access chains for uniform variables 1740 assert((exp.base & 0xF0000000) == 0); 1741 1742 i = 0; 1743 if (exp.chain.empty() || ( 1744 exp.chain[0].op != expression::operation::op_member && 1745 exp.chain[0].op != expression::operation::op_dynamic_index && 1746 exp.chain[0].op != expression::operation::op_constant_index)) 1747 return exp.base; 1748 1749 std::pair<spv::StorageClass, spv::ImageFormat> storage = { spv::StorageClassFunction, spv::ImageFormatUnknown }; 1750 if (const auto it = _storage_lookup.find(exp.base); 1751 it != _storage_lookup.end()) 1752 storage = it->second; 1753 1754 // Ensure that 'access_chain' cannot get invalidated by calls to 'emit_constant' or 'convert_type' 1755 assert(_current_block_data != &_types_and_constants); 1756 1757 spirv_instruction *access_chain = 1758 &add_instruction(spv::OpAccessChain).add(exp.base); // Base 1759 1760 // Ignore first index into 1xN matrices, since they were translated to a vector type in SPIR-V 1761 if (exp.chain[0].from.rows == 1 && exp.chain[0].from.cols > 1) 1762 i = 1; 1763 1764 for (; i < exp.chain.size() && ( 1765 exp.chain[i].op == expression::operation::op_member || 1766 exp.chain[i].op == expression::operation::op_dynamic_index || 1767 exp.chain[i].op == expression::operation::op_constant_index); ++i) 1768 access_chain->add(exp.chain[i].op == expression::operation::op_dynamic_index ? 1769 exp.chain[i].index : 1770 emit_constant(exp.chain[i].index)); // Indexes 1771 1772 access_chain->type = convert_type(exp.chain[i - 1].to, true, storage.first, storage.second); // Last type is the result 1773 return access_chain->result; 1774 } 1775 1776 id emit_constant(uint32_t value) 1777 { 1778 return emit_constant({ type::t_uint, 1, 1 }, value); 1779 } 1780 id emit_constant(const type &type, uint32_t value) 1781 { 1782 // Create a constant value of the specified type 1783 constant data = {}; // Initialize to zero, so that components not set below still have a defined value for the lookup via std::memcmp 1784 for (unsigned int i = 0; i < type.components(); ++i) 1785 if (type.is_integral()) 1786 data.as_uint[i] = value; 1787 else 1788 data.as_float[i] = static_cast<float>(value); 1789 1790 return emit_constant(type, data, false); 1791 } 1792 id emit_constant(const type &type, const constant &data) override 1793 { 1794 return emit_constant(type, data, false); 1795 } 1796 id emit_constant(const type &type, const constant &data, bool spec_constant) 1797 { 1798 if (!spec_constant) // Specialization constants cannot reuse other constants 1799 { 1800 if (const auto it = std::find_if(_constant_lookup.begin(), _constant_lookup.end(), 1801 [&type, &data](auto &x) { 1802 if (!(std::get<0>(x) == type && std::memcmp(&std::get<1>(x).as_uint[0], &data.as_uint[0], sizeof(uint32_t) * 16) == 0 && std::get<1>(x).array_data.size() == data.array_data.size())) 1803 return false; 1804 for (size_t i = 0; i < data.array_data.size(); ++i) 1805 if (std::memcmp(&std::get<1>(x).array_data[i].as_uint[0], &data.array_data[i].as_uint[0], sizeof(uint32_t) * 16) != 0) 1806 return false; 1807 return true; 1808 }); 1809 it != _constant_lookup.end()) 1810 return std::get<2>(*it); // Re-use existing constant instead of duplicating the definition 1811 } 1812 1813 spv::Id result; 1814 if (type.is_array()) 1815 { 1816 assert(type.array_length > 0); // Unsized arrays cannot be constants 1817 1818 auto elem_type = type; 1819 elem_type.array_length = 0; 1820 1821 std::vector<spv::Id> elements; 1822 elements.reserve(type.array_length); 1823 1824 // Fill up elements with constant array data 1825 for (const constant &elem : data.array_data) 1826 elements.push_back(emit_constant(elem_type, elem, spec_constant)); 1827 // Fill up any remaining elements with a default value (when the array data did not specify them) 1828 for (size_t i = elements.size(); i < static_cast<size_t>(type.array_length); ++i) 1829 elements.push_back(emit_constant(elem_type, {}, spec_constant)); 1830 1831 result = add_instruction(spec_constant ? spv::OpSpecConstantComposite : spv::OpConstantComposite, convert_type(type), _types_and_constants) 1832 .add(elements.begin(), elements.end()) 1833 .result; 1834 } 1835 else if (type.is_struct()) 1836 { 1837 assert(!spec_constant); // Structures cannot be specialization constants 1838 1839 result = add_instruction(spv::OpConstantNull, convert_type(type), _types_and_constants) 1840 .result; 1841 } 1842 else if (type.is_vector() || type.is_matrix()) 1843 { 1844 auto elem_type = type; 1845 elem_type.rows = type.cols; 1846 elem_type.cols = 1; 1847 1848 spv::Id rows[4] = {}; 1849 1850 // Construct matrix constant out of row vector constants 1851 // Construct vector constant out of scalar constants for each element 1852 for (unsigned int i = 0; i < type.rows; ++i) 1853 { 1854 constant row_data = {}; 1855 for (unsigned int k = 0; k < type.cols; ++k) 1856 row_data.as_uint[k] = data.as_uint[i * type.cols + k]; 1857 1858 rows[i] = emit_constant(elem_type, row_data, spec_constant); 1859 } 1860 1861 if (type.rows == 1) 1862 { 1863 result = rows[0]; 1864 } 1865 else 1866 { 1867 spirv_instruction &node = add_instruction(spec_constant ? spv::OpSpecConstantComposite : spv::OpConstantComposite, convert_type(type), _types_and_constants); 1868 for (unsigned int i = 0; i < type.rows; ++i) 1869 node.add(rows[i]); 1870 1871 result = node.result; 1872 } 1873 } 1874 else if (type.is_boolean()) 1875 { 1876 result = add_instruction(data.as_uint[0] ? 1877 (spec_constant ? spv::OpSpecConstantTrue : spv::OpConstantTrue) : 1878 (spec_constant ? spv::OpSpecConstantFalse : spv::OpConstantFalse), convert_type(type), _types_and_constants) 1879 .result; 1880 } 1881 else 1882 { 1883 assert(type.is_scalar()); 1884 1885 result = add_instruction(spec_constant ? spv::OpSpecConstant : spv::OpConstant, convert_type(type), _types_and_constants) 1886 .add(data.as_uint[0]) 1887 .result; 1888 } 1889 1890 if (spec_constant) // Keep track of all specialization constants 1891 _spec_constants.insert(result); 1892 else 1893 _constant_lookup.push_back({ type, data, result }); 1894 1895 return result; 1896 } 1897 1898 id emit_unary_op(const location &loc, tokenid op, const type &type, id val) override 1899 { 1900 spv::Op spv_op = spv::OpNop; 1901 1902 switch (op) 1903 { 1904 case tokenid::minus: 1905 spv_op = type.is_floating_point() ? spv::OpFNegate : spv::OpSNegate; 1906 break; 1907 case tokenid::tilde: 1908 spv_op = spv::OpNot; 1909 break; 1910 case tokenid::exclaim: 1911 spv_op = spv::OpLogicalNot; 1912 break; 1913 default: 1914 return assert(false), 0; 1915 } 1916 1917 add_location(loc, *_current_block_data); 1918 1919 spirv_instruction &inst = add_instruction(spv_op, convert_type(type)); 1920 inst.add(val); // Operand 1921 1922 return inst.result; 1923 } 1924 id emit_binary_op(const location &loc, tokenid op, const type &res_type, const type &type, id lhs, id rhs) override 1925 { 1926 spv::Op spv_op = spv::OpNop; 1927 1928 switch (op) 1929 { 1930 case tokenid::plus: 1931 case tokenid::plus_plus: 1932 case tokenid::plus_equal: 1933 spv_op = type.is_floating_point() ? spv::OpFAdd : spv::OpIAdd; 1934 break; 1935 case tokenid::minus: 1936 case tokenid::minus_minus: 1937 case tokenid::minus_equal: 1938 spv_op = type.is_floating_point() ? spv::OpFSub : spv::OpISub; 1939 break; 1940 case tokenid::star: 1941 case tokenid::star_equal: 1942 spv_op = type.is_floating_point() ? spv::OpFMul : spv::OpIMul; 1943 break; 1944 case tokenid::slash: 1945 case tokenid::slash_equal: 1946 spv_op = type.is_floating_point() ? spv::OpFDiv : type.is_signed() ? spv::OpSDiv : spv::OpUDiv; 1947 break; 1948 case tokenid::percent: 1949 case tokenid::percent_equal: 1950 spv_op = type.is_floating_point() ? spv::OpFRem : type.is_signed() ? spv::OpSRem : spv::OpUMod; 1951 break; 1952 case tokenid::caret: 1953 case tokenid::caret_equal: 1954 spv_op = spv::OpBitwiseXor; 1955 break; 1956 case tokenid::pipe: 1957 case tokenid::pipe_equal: 1958 spv_op = spv::OpBitwiseOr; 1959 break; 1960 case tokenid::ampersand: 1961 case tokenid::ampersand_equal: 1962 spv_op = spv::OpBitwiseAnd; 1963 break; 1964 case tokenid::less_less: 1965 case tokenid::less_less_equal: 1966 spv_op = spv::OpShiftLeftLogical; 1967 break; 1968 case tokenid::greater_greater: 1969 case tokenid::greater_greater_equal: 1970 spv_op = type.is_signed() ? spv::OpShiftRightArithmetic : spv::OpShiftRightLogical; 1971 break; 1972 case tokenid::pipe_pipe: 1973 spv_op = spv::OpLogicalOr; 1974 break; 1975 case tokenid::ampersand_ampersand: 1976 spv_op = spv::OpLogicalAnd; 1977 break; 1978 case tokenid::less: 1979 spv_op = type.is_floating_point() ? spv::OpFOrdLessThan : 1980 type.is_signed() ? spv::OpSLessThan : spv::OpULessThan; 1981 break; 1982 case tokenid::less_equal: 1983 spv_op = type.is_floating_point() ? spv::OpFOrdLessThanEqual : 1984 type.is_signed() ? spv::OpSLessThanEqual : spv::OpULessThanEqual; 1985 break; 1986 case tokenid::greater: 1987 spv_op = type.is_floating_point() ? spv::OpFOrdGreaterThan : 1988 type.is_signed() ? spv::OpSGreaterThan : spv::OpUGreaterThan; 1989 break; 1990 case tokenid::greater_equal: 1991 spv_op = type.is_floating_point() ? spv::OpFOrdGreaterThanEqual : 1992 type.is_signed() ? spv::OpSGreaterThanEqual : spv::OpUGreaterThanEqual; 1993 break; 1994 case tokenid::equal_equal: 1995 spv_op = type.is_floating_point() ? spv::OpFOrdEqual : 1996 type.is_boolean() ? spv::OpLogicalEqual : spv::OpIEqual; 1997 break; 1998 case tokenid::exclaim_equal: 1999 spv_op = type.is_floating_point() ? spv::OpFOrdNotEqual : 2000 type.is_boolean() ? spv::OpLogicalNotEqual : spv::OpINotEqual; 2001 break; 2002 default: 2003 return assert(false), 0; 2004 } 2005 2006 add_location(loc, *_current_block_data); 2007 2008 // Binary operators generally only work on scalars and vectors in SPIR-V, so need to apply them to matrices component-wise 2009 if (type.is_matrix() && type.rows != 1) 2010 { 2011 std::vector<spv::Id> ids; 2012 ids.reserve(type.cols); 2013 2014 auto vector_type = type; 2015 vector_type.rows = type.cols; 2016 vector_type.cols = 1; 2017 2018 for (unsigned int row = 0; row < type.rows; ++row) 2019 { 2020 const spv::Id lhs_elem = add_instruction(spv::OpCompositeExtract, convert_type(vector_type)) 2021 .add(lhs) 2022 .add(row) 2023 .result; 2024 const spv::Id rhs_elem = add_instruction(spv::OpCompositeExtract, convert_type(vector_type)) 2025 .add(rhs) 2026 .add(row) 2027 .result; 2028 2029 spirv_instruction &inst = add_instruction(spv_op, convert_type(vector_type)); 2030 inst.add(lhs_elem); // Operand 1 2031 inst.add(rhs_elem); // Operand 2 2032 2033 if (res_type.has(type::q_precise)) 2034 add_decoration(inst.result, spv::DecorationNoContraction); 2035 if (!_enable_16bit_types && res_type.precision() < 32) 2036 add_decoration(inst.result, spv::DecorationRelaxedPrecision); 2037 2038 ids.push_back(inst.result); 2039 } 2040 2041 spirv_instruction &inst = add_instruction(spv::OpCompositeConstruct, convert_type(res_type)); 2042 inst.add(ids.begin(), ids.end()); 2043 2044 return inst.result; 2045 } 2046 else 2047 { 2048 spirv_instruction &inst = add_instruction(spv_op, convert_type(res_type)); 2049 inst.add(lhs); // Operand 1 2050 inst.add(rhs); // Operand 2 2051 2052 if (res_type.has(type::q_precise)) 2053 add_decoration(inst.result, spv::DecorationNoContraction); 2054 if (!_enable_16bit_types && res_type.precision() < 32) 2055 add_decoration(inst.result, spv::DecorationRelaxedPrecision); 2056 2057 return inst.result; 2058 } 2059 } 2060 id emit_ternary_op(const location &loc, tokenid op, const type &type, id condition, id true_value, id false_value) override 2061 { 2062 if (op != tokenid::question) 2063 return assert(false), 0; 2064 2065 add_location(loc, *_current_block_data); 2066 2067 spirv_instruction &inst = add_instruction(spv::OpSelect, convert_type(type)); 2068 inst.add(condition); // Condition 2069 inst.add(true_value); // Object 1 2070 inst.add(false_value); // Object 2 2071 2072 return inst.result; 2073 } 2074 id emit_call(const location &loc, id function, const type &res_type, const std::vector<expression> &args) override 2075 { 2076 #ifndef NDEBUG 2077 for (const expression &arg : args) 2078 assert(arg.chain.empty() && arg.base != 0); 2079 #endif 2080 add_location(loc, *_current_block_data); 2081 2082 // https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpFunctionCall 2083 spirv_instruction &inst = add_instruction(spv::OpFunctionCall, convert_type(res_type)); 2084 inst.add(function); // Function 2085 for (const expression &arg : args) 2086 inst.add(arg.base); // Arguments 2087 2088 return inst.result; 2089 } 2090 id emit_call_intrinsic(const location &loc, id intrinsic, const type &res_type, const std::vector<expression> &args) override 2091 { 2092 #ifndef NDEBUG 2093 for (const expression &arg : args) 2094 assert(arg.chain.empty() && arg.base != 0); 2095 #endif 2096 add_location(loc, *_current_block_data); 2097 2098 enum 2099 { 2100 #define IMPLEMENT_INTRINSIC_SPIRV(name, i, code) name##i, 2101 #include "effect_symbol_table_intrinsics.inl" 2102 }; 2103 2104 switch (intrinsic) 2105 { 2106 #define IMPLEMENT_INTRINSIC_SPIRV(name, i, code) case name##i: code 2107 #include "effect_symbol_table_intrinsics.inl" 2108 default: 2109 return assert(false), 0; 2110 } 2111 } 2112 id emit_construct(const location &loc, const type &type, const std::vector<expression> &args) override 2113 { 2114 #ifndef NDEBUG 2115 for (const expression &arg : args) 2116 assert((arg.type.is_scalar() || type.is_array()) && arg.chain.empty() && arg.base != 0); 2117 #endif 2118 add_location(loc, *_current_block_data); 2119 2120 std::vector<spv::Id> ids; 2121 ids.reserve(args.size()); 2122 2123 // There must be exactly one constituent for each top-level component of the result 2124 if (type.is_matrix()) 2125 { 2126 auto vector_type = type; 2127 vector_type.rows = type.cols; 2128 vector_type.cols = 1; 2129 2130 // Turn the list of scalar arguments into a list of column vectors 2131 for (size_t arg = 0; arg < args.size(); arg += vector_type.rows) 2132 { 2133 spirv_instruction &inst = add_instruction(spv::OpCompositeConstruct, convert_type(vector_type)); 2134 for (unsigned row = 0; row < vector_type.rows; ++row) 2135 inst.add(args[arg + row].base); 2136 2137 ids.push_back(inst.result); 2138 } 2139 } 2140 else 2141 { 2142 assert(type.is_vector() || type.is_array()); 2143 2144 // The exception is that for constructing a vector, a contiguous subset of the scalars consumed can be represented by a vector operand instead 2145 for (const expression &arg : args) 2146 ids.push_back(arg.base); 2147 } 2148 2149 spirv_instruction &inst = add_instruction(spv::OpCompositeConstruct, convert_type(type)); 2150 inst.add(ids.begin(), ids.end()); 2151 2152 return inst.result; 2153 } 2154 2155 void emit_if(const location &loc, id, id condition_block, id true_statement_block, id false_statement_block, unsigned int selection_control) override 2156 { 2157 spirv_instruction merge_label = _current_block_data->instructions.back(); 2158 assert(merge_label.op == spv::OpLabel); 2159 _current_block_data->instructions.pop_back(); 2160 2161 // Add previous block containing the condition value first 2162 _current_block_data->append(_block_data[condition_block]); 2163 2164 spirv_instruction branch_inst = _current_block_data->instructions.back(); 2165 assert(branch_inst.op == spv::OpBranchConditional); 2166 _current_block_data->instructions.pop_back(); 2167 2168 // Add structured control flow instruction 2169 add_location(loc, *_current_block_data); 2170 add_instruction_without_result(spv::OpSelectionMerge) 2171 .add(merge_label.result) 2172 .add(selection_control & 0x3); // 'SelectionControl' happens to match the flags produced by the parser 2173 2174 // Append all blocks belonging to the branch 2175 _current_block_data->instructions.push_back(branch_inst); 2176 _current_block_data->append(_block_data[true_statement_block]); 2177 _current_block_data->append(_block_data[false_statement_block]); 2178 2179 _current_block_data->instructions.push_back(merge_label); 2180 } 2181 id emit_phi(const location &loc, id, id condition_block, id true_value, id true_statement_block, id false_value, id false_statement_block, const type &type) override 2182 { 2183 spirv_instruction merge_label = _current_block_data->instructions.back(); 2184 assert(merge_label.op == spv::OpLabel); 2185 _current_block_data->instructions.pop_back(); 2186 2187 // Add previous block containing the condition value first 2188 _current_block_data->append(_block_data[condition_block]); 2189 2190 if (true_statement_block != condition_block) 2191 _current_block_data->append(_block_data[true_statement_block]); 2192 if (false_statement_block != condition_block) 2193 _current_block_data->append(_block_data[false_statement_block]); 2194 2195 _current_block_data->instructions.push_back(merge_label); 2196 2197 add_location(loc, *_current_block_data); 2198 2199 // https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpPhi 2200 spirv_instruction &inst = add_instruction(spv::OpPhi, convert_type(type)) 2201 .add(true_value) // Variable 0 2202 .add(true_statement_block) // Parent 0 2203 .add(false_value) // Variable 1 2204 .add(false_statement_block); // Parent 1 2205 2206 return inst.result; 2207 } 2208 void emit_loop(const location &loc, id, id prev_block, id header_block, id condition_block, id loop_block, id continue_block, unsigned int loop_control) override 2209 { 2210 spirv_instruction merge_label = _current_block_data->instructions.back(); 2211 assert(merge_label.op == spv::OpLabel); 2212 _current_block_data->instructions.pop_back(); 2213 2214 // Add previous block first 2215 _current_block_data->append(_block_data[prev_block]); 2216 2217 // Fill header block 2218 assert(_block_data[header_block].instructions.size() == 2); 2219 _current_block_data->instructions.push_back(_block_data[header_block].instructions[0]); 2220 assert(_current_block_data->instructions.back().op == spv::OpLabel); 2221 2222 // Add structured control flow instruction 2223 add_location(loc, *_current_block_data); 2224 add_instruction_without_result(spv::OpLoopMerge) 2225 .add(merge_label.result) 2226 .add(continue_block) 2227 .add(loop_control & 0x3); // 'LoopControl' happens to match the flags produced by the parser 2228 2229 _current_block_data->instructions.push_back(_block_data[header_block].instructions[1]); 2230 assert(_current_block_data->instructions.back().op == spv::OpBranch); 2231 2232 // Add condition block if it exists 2233 if (condition_block != 0) 2234 _current_block_data->append(_block_data[condition_block]); 2235 2236 // Append loop body block before continue block 2237 _current_block_data->append(_block_data[loop_block]); 2238 _current_block_data->append(_block_data[continue_block]); 2239 2240 _current_block_data->instructions.push_back(merge_label); 2241 } 2242 void emit_switch(const location &loc, id, id selector_block, id default_label, id default_block, const std::vector<id> &case_literal_and_labels, const std::vector<id> &case_blocks, unsigned int selection_control) override 2243 { 2244 assert(case_blocks.size() == case_literal_and_labels.size() / 2); 2245 2246 spirv_instruction merge_label = _current_block_data->instructions.back(); 2247 assert(merge_label.op == spv::OpLabel); 2248 _current_block_data->instructions.pop_back(); 2249 2250 // Add previous block containing the selector value first 2251 _current_block_data->append(_block_data[selector_block]); 2252 2253 spirv_instruction switch_inst = _current_block_data->instructions.back(); 2254 assert(switch_inst.op == spv::OpSwitch); 2255 _current_block_data->instructions.pop_back(); 2256 2257 // Add structured control flow instruction 2258 add_location(loc, *_current_block_data); 2259 add_instruction_without_result(spv::OpSelectionMerge) 2260 .add(merge_label.result) 2261 .add(selection_control & 0x3); // 'SelectionControl' happens to match the flags produced by the parser 2262 2263 // Update switch instruction to contain all case labels 2264 switch_inst.operands[1] = default_label; 2265 switch_inst.add(case_literal_and_labels.begin(), case_literal_and_labels.end()); 2266 2267 // Append all blocks belonging to the switch 2268 _current_block_data->instructions.push_back(switch_inst); 2269 2270 std::vector<id> blocks = case_blocks; 2271 if (default_label != merge_label.result) 2272 blocks.push_back(default_block); 2273 // Eliminate duplicates (because of multiple case labels pointing to the same block) 2274 std::sort(blocks.begin(), blocks.end()); 2275 blocks.erase(std::unique(blocks.begin(), blocks.end()), blocks.end()); 2276 for (const id case_block : blocks) 2277 _current_block_data->append(_block_data[case_block]); 2278 2279 _current_block_data->instructions.push_back(merge_label); 2280 } 2281 2282 bool is_in_function() const override { return _current_function != nullptr; } 2283 2284 id set_block(id id) override 2285 { 2286 _last_block = _current_block; 2287 _current_block = id; 2288 _current_block_data = &_block_data[id]; 2289 2290 return _last_block; 2291 } 2292 void enter_block(id id) override 2293 { 2294 assert(id != 0); 2295 // Can only use labels inside functions and should never be in another basic block if creating a new one 2296 assert(is_in_function() && !is_in_block()); 2297 2298 set_block(id); 2299 2300 add_instruction_without_result(spv::OpLabel) 2301 .result = id; 2302 } 2303 id leave_block_and_kill() override 2304 { 2305 assert(is_in_function()); // Can only discard inside functions 2306 2307 if (!is_in_block()) 2308 return 0; 2309 2310 add_instruction_without_result(spv::OpKill); 2311 2312 return set_block(0); 2313 } 2314 id leave_block_and_return(id value) override 2315 { 2316 assert(is_in_function()); // Can only return from inside functions 2317 2318 if (!is_in_block()) // Might already have left the last block in which case this has to be ignored 2319 return 0; 2320 2321 if (_current_function->return_type.is_void()) 2322 { 2323 add_instruction_without_result(spv::OpReturn); 2324 } 2325 else 2326 { 2327 if (0 == value) // The implicit return statement needs this 2328 value = add_instruction(spv::OpUndef, convert_type(_current_function->return_type), _types_and_constants).result; 2329 2330 add_instruction_without_result(spv::OpReturnValue) 2331 .add(value); 2332 } 2333 2334 return set_block(0); 2335 } 2336 id leave_block_and_switch(id value, id default_target) override 2337 { 2338 assert(value != 0 && default_target != 0); 2339 assert(is_in_function()); // Can only switch inside functions 2340 2341 if (!is_in_block()) 2342 return _last_block; 2343 2344 add_instruction_without_result(spv::OpSwitch) 2345 .add(value) 2346 .add(default_target); 2347 2348 return set_block(0); 2349 } 2350 id leave_block_and_branch(id target, unsigned int) override 2351 { 2352 assert(target != 0); 2353 assert(is_in_function()); // Can only branch inside functions 2354 2355 if (!is_in_block()) 2356 return _last_block; 2357 2358 add_instruction_without_result(spv::OpBranch) 2359 .add(target); 2360 2361 return set_block(0); 2362 } 2363 id leave_block_and_branch_conditional(id condition, id true_target, id false_target) override 2364 { 2365 assert(condition != 0 && true_target != 0 && false_target != 0); 2366 assert(is_in_function()); // Can only branch inside functions 2367 2368 if (!is_in_block()) 2369 return _last_block; 2370 2371 add_instruction_without_result(spv::OpBranchConditional) 2372 .add(condition) 2373 .add(true_target) 2374 .add(false_target); 2375 2376 return set_block(0); 2377 } 2378 void leave_function() override 2379 { 2380 assert(is_in_function()); // Can only leave if there was a function to begin with 2381 2382 _current_function->definition = _block_data[_last_block]; 2383 2384 // Append function end instruction 2385 add_instruction_without_result(spv::OpFunctionEnd, _current_function->definition); 2386 2387 _current_function = nullptr; 2388 } 2389 }; 2390 2391 codegen *reshadefx::create_codegen_spirv(bool vulkan_semantics, bool debug_info, bool uniforms_to_spec_constants, bool enable_16bit_types, bool flip_vert_y) 2392 { 2393 return new codegen_spirv(vulkan_semantics, debug_info, uniforms_to_spec_constants, enable_16bit_types, flip_vert_y); 2394 }