Strictly speaking - the format given in GLSL_EXT_debug_printf is a client side implementation of SPIR-V NonSemantic.DebugPrintf There is nothing stopping someone from creating a debug printf implementation that goes `printf("Use this &q to print int", myInt)` but this requires both having a different HLL and Tool consuming it. Currently RenderDoc and the Validation Layers both follow the same synt
| 621 | // Currently RenderDoc and the Validation Layers both follow the same syntax, but that also could possibly change. |
| 622 | // Therefore, we validate these here based on the VVL implementation only |
| 623 | bool DebugPrintfPass::Validate(const Function& current_function, const InstructionMeta& meta) { |
| 624 | static const char* tag = "DEBUG-PRINTF-FORMATTING"; |
| 625 | |
| 626 | struct ParamInfo { |
| 627 | bool is_float = false; // else int (don't attempt to validate unsigned vs signed here) |
| 628 | bool is_64_bit = false; |
| 629 | bool is_pointer = false; |
| 630 | uint32_t vector_size = 0; // zero == scalar |
| 631 | char modifier[32]; |
| 632 | }; |
| 633 | |
| 634 | // where we find the first arugment in OpExtInst instruction |
| 635 | const uint32_t first_argument_offset = 6; |
| 636 | |
| 637 | if (meta.target_instruction->Length() < first_argument_offset) { |
| 638 | module_.InternalError(tag, "OpExtInst in a invalid SPIR-V format and should have been caught in spirv-val"); |
| 639 | return false; |
| 640 | } |
| 641 | |
| 642 | uint32_t string_id = meta.target_instruction->Word(5); |
| 643 | const char* op_string = nullptr; |
| 644 | for (const auto& inst : module_.debug_source_) { |
| 645 | if (inst->Opcode() == spv::OpString && inst->ResultId() == string_id) { |
| 646 | op_string = inst->GetAsString(2); |
| 647 | break; |
| 648 | } |
| 649 | } |
| 650 | if (!op_string) { |
| 651 | module_.InternalError(tag, "OpExtInst points to an empty/invalid OpString, this should have been caught in spirv-val"); |
| 652 | return false; |
| 653 | } |
| 654 | |
| 655 | const size_t op_string_len = strlen(op_string); |
| 656 | if (op_string_len == 0) { |
| 657 | module_.InternalError(tag, "OpString is empty (string was found, but is empty)"); |
| 658 | return false; |
| 659 | } |
| 660 | |
| 661 | // If we are going to print the OpString to the user for an error/warning, we need to process it first |
| 662 | auto print_op_string = [&op_string]() { |
| 663 | // If there is a '\n' we want to print it like we see in the shader, so need to escape the backslash |
| 664 | std::string result; |
| 665 | for (const char* p = op_string; *p != '\0'; ++p) { |
| 666 | if (*p == '\n') { |
| 667 | result += "\\n"; |
| 668 | } else if (*p == '\t') { |
| 669 | result += "\\t"; |
| 670 | } else { |
| 671 | result += *p; |
| 672 | } |
| 673 | } |
| 674 | return result; |
| 675 | }; |
| 676 | |
| 677 | bool valid = true; |
| 678 | std::vector<ParamInfo> param_infos; |
| 679 | |
| 680 | // No reason to start checking at the last character, since always need % and something following it |
nothing calls this directly
no test coverage detected