1 // 2 // Copyright (C) 2016 Google, Inc. 3 // 4 // All rights reserved. 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions 8 // are met: 9 // 10 // Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // 13 // Redistributions in binary form must reproduce the above 14 // copyright notice, this list of conditions and the following 15 // disclaimer in the documentation and/or other materials provided 16 // with the distribution. 17 // 18 // Neither the name of Google Inc. nor the names of its 19 // contributors may be used to endorse or promote products derived 20 // from this software without specific prior written permission. 21 // 22 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 // POSSIBILITY OF SUCH DAMAGE. 34 35 #ifndef GLSLANG_GTESTS_TEST_FIXTURE_H 36 #define GLSLANG_GTESTS_TEST_FIXTURE_H 37 38 #include <algorithm> 39 #include <cstdint> 40 #include <fstream> 41 #include <sstream> 42 #include <streambuf> 43 #include <tuple> 44 #include <string> 45 46 #include <gtest/gtest.h> 47 48 #include "SPIRV/GlslangToSpv.h" 49 #include "SPIRV/disassemble.h" 50 #include "SPIRV/doc.h" 51 #include "SPIRV/SPVRemapper.h" 52 #include "glslang/Include/Types.h" 53 #include "glslang/Public/ResourceLimits.h" 54 #include "glslang/Public/ShaderLang.h" 55 56 #include "Initializer.h" 57 #include "Settings.h" 58 59 namespace glslangtest { 60 61 // This function is used to provide custom test name suffixes based on the 62 // shader source file names. Otherwise, the test name suffixes will just be 63 // numbers, which are not quite obvious. 64 std::string FileNameAsCustomTestSuffix( 65 const ::testing::TestParamInfo<std::string>& info); 66 67 enum class Source { 68 GLSL, 69 HLSL, 70 }; 71 72 // Enum for shader compilation semantics. 73 enum class Semantics { 74 OpenGL, 75 Vulkan 76 }; 77 78 // Enum for compilation target. 79 enum class Target { 80 AST, 81 Spv, 82 BothASTAndSpv, 83 }; 84 85 EShLanguage GetShaderStage(const std::string& stage); 86 87 EShMessages DeriveOptions(Source, Semantics, Target); 88 89 // Reads the content of the file at the given |path|. On success, returns true 90 // and the contents; otherwise, returns false and an empty string. 91 std::pair<bool, std::string> ReadFile(const std::string& path); 92 std::pair<bool, std::vector<std::uint32_t> > ReadSpvBinaryFile(const std::string& path); 93 94 // Writes the given |contents| into the file at the given |path|. Returns true 95 // on successful output. 96 bool WriteFile(const std::string& path, const std::string& contents); 97 98 // Returns the suffix of the given |name|. 99 std::string GetSuffix(const std::string& name); 100 101 // Base class for glslang integration tests. It contains many handy utility-like 102 // methods such as reading shader source files, compiling into AST/SPIR-V, and 103 // comparing with expected outputs. 104 // 105 // To write value-Parameterized tests: 106 // using ValueParamTest = GlslangTest<::testing::TestWithParam<std::string>>; 107 // To use as normal fixture: 108 // using FixtureTest = GlslangTest<::testing::Test>; 109 template <typename GT> 110 class GlslangTest : public GT { 111 public: GlslangTest()112 GlslangTest() 113 : defaultVersion(100), 114 defaultProfile(ENoProfile), 115 forceVersionProfile(false), 116 isForwardCompatible(false) { 117 // Perform validation by default. 118 spirvOptions.validate = true; 119 } 120 121 // Tries to load the contents from the file at the given |path|. On success, 122 // writes the contents into |contents|. On failure, errors out. tryLoadFile(const std::string & path,const std::string & tag,std::string * contents)123 void tryLoadFile(const std::string& path, const std::string& tag, 124 std::string* contents) 125 { 126 bool fileReadOk; 127 std::tie(fileReadOk, *contents) = ReadFile(path); 128 ASSERT_TRUE(fileReadOk) << "Cannot open " << tag << " file: " << path; 129 } 130 131 // Tries to load the contents from the file at the given |path|. On success, 132 // writes the contents into |contents|. On failure, errors out. tryLoadSpvFile(const std::string & path,const std::string & tag,std::vector<uint32_t> & contents)133 void tryLoadSpvFile(const std::string& path, const std::string& tag, 134 std::vector<uint32_t>& contents) 135 { 136 bool fileReadOk; 137 std::tie(fileReadOk, contents) = ReadSpvBinaryFile(path); 138 ASSERT_TRUE(fileReadOk) << "Cannot open " << tag << " file: " << path; 139 } 140 141 // Checks the equality of |expected| and |real|. If they are not equal, 142 // write |real| to the given file named as |fname| if update mode is on. 143 void checkEqAndUpdateIfRequested(const std::string& expected, 144 const std::string& real, 145 const std::string& fname, 146 const std::string& errorsAndWarnings = "") 147 { 148 // In order to output the message we want under proper circumstances, 149 // we need the following operator<< stuff. 150 EXPECT_EQ(expected, real) 151 << (GlobalTestSettings.updateMode 152 ? ("Mismatch found and update mode turned on - " 153 "flushing expected result output.\n") 154 : "") 155 << "The following warnings/errors occurred:\n" 156 << errorsAndWarnings; 157 158 // Update the expected output file if requested. 159 // It looks weird to duplicate the comparison between expected_output 160 // and stream.str(). However, if creating a variable for the comparison 161 // result, we cannot have pretty print of the string diff in the above. 162 if (GlobalTestSettings.updateMode && expected != real) { 163 EXPECT_TRUE(WriteFile(fname, real)) << "Flushing failed"; 164 } 165 } 166 167 struct ShaderResult { 168 std::string shaderName; 169 std::string output; 170 std::string error; 171 }; 172 173 // A struct for holding all the information returned by glslang compilation 174 // and linking. 175 struct GlslangResult { 176 std::vector<ShaderResult> shaderResults; 177 std::string linkingOutput; 178 std::string linkingError; 179 bool validationResult; 180 std::string spirvWarningsErrors; 181 std::string spirv; // Optional SPIR-V disassembly text. 182 }; 183 184 // Compiles and the given source |code| of the given shader |stage| into 185 // the target under the semantics conveyed via |controls|. Returns true 186 // and modifies |shader| on success. 187 bool compile(glslang::TShader* shader, const std::string& code, 188 const std::string& entryPointName, EShMessages controls, 189 const TBuiltInResource* resources=nullptr, 190 const std::string* shaderName=nullptr) 191 { 192 const char* shaderStrings = code.data(); 193 const int shaderLengths = static_cast<int>(code.size()); 194 const char* shaderNames = nullptr; 195 196 if ((controls & EShMsgDebugInfo) && shaderName != nullptr) { 197 shaderNames = shaderName->data(); 198 shader->setStringsWithLengthsAndNames( 199 &shaderStrings, &shaderLengths, &shaderNames, 1); 200 } else 201 shader->setStringsWithLengths(&shaderStrings, &shaderLengths, 1); 202 if (!entryPointName.empty()) shader->setEntryPoint(entryPointName.c_str()); 203 204 // A includer that always assumes header name is a relative path to the test folder. 205 class GlslangTestIncluder : public glslang::TShader::Includer { 206 public: includeLocal(const char * headerName,const char *,size_t)207 virtual IncludeResult* includeLocal(const char* headerName, const char* /*includerName*/, 208 size_t /*inclusionDepth*/) override 209 { 210 std::string path = GLSLANG_TEST_DIRECTORY; 211 path += '/'; 212 path += headerName; 213 std::replace(path.begin(), path.end(), '\\', '/'); 214 215 auto [success, fileContent] = ReadFile(path); 216 if (success) { 217 auto buffer = new char[fileContent.size() + 1]; 218 std::copy(fileContent.begin(), fileContent.end(), buffer); 219 buffer[fileContent.size()] = '\0'; 220 221 return new IncludeResult(headerName, buffer, fileContent.size(), buffer); 222 } 223 224 return nullptr; 225 } 226 releaseInclude(IncludeResult * result)227 virtual void releaseInclude(IncludeResult* result) override 228 { 229 if (result != nullptr) { 230 delete[] static_cast<char*>(result->userData); 231 delete result; 232 } 233 } 234 }; 235 236 GlslangTestIncluder includer; 237 return shader->parse((resources ? resources : GetDefaultResources()), defaultVersion, isForwardCompatible, 238 controls, includer); 239 } 240 241 // Compiles and links the given source |code| of the given shader 242 // |stage| into the target under the semantics specified via |controls|. 243 // Returns a GlslangResult instance containing all the information generated 244 // during the process. If the target includes SPIR-V, also disassembles 245 // the result and returns disassembly text. 246 GlslangResult compileAndLink( 247 const std::string& shaderName, const std::string& code, 248 const std::string& entryPointName, EShMessages controls, 249 glslang::EShTargetClientVersion clientTargetVersion, 250 glslang::EShTargetLanguageVersion targetLanguageVersion, 251 bool flattenUniformArrays = false, 252 EShTextureSamplerTransformMode texSampTransMode = EShTexSampTransKeep, 253 bool enableOptimizer = false, 254 bool enableDebug = false, 255 bool enableNonSemanticShaderDebugInfo = false, 256 bool automap = true) 257 { 258 const EShLanguage stage = GetShaderStage(GetSuffix(shaderName)); 259 260 glslang::TShader shader(stage); 261 if (automap) { 262 shader.setAutoMapLocations(true); 263 shader.setAutoMapBindings(true); 264 } 265 266 if (enableDebug) { 267 shader.setDebugInfo(true); 268 } 269 if (enableNonSemanticShaderDebugInfo) { 270 assert(enableDebug && "Debug must be on for non-semantic debug info"); 271 } 272 273 shader.setTextureSamplerTransformMode(texSampTransMode); 274 #ifdef ENABLE_HLSL 275 shader.setFlattenUniformArrays(flattenUniformArrays); 276 #endif 277 278 if (controls & EShMsgSpvRules) { 279 if (controls & EShMsgVulkanRules) { 280 shader.setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl 281 : glslang::EShSourceGlsl, 282 stage, glslang::EShClientVulkan, 100); 283 shader.setEnvClient(glslang::EShClientVulkan, clientTargetVersion); 284 shader.setEnvTarget(glslang::EShTargetSpv, targetLanguageVersion); 285 } else { 286 shader.setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl 287 : glslang::EShSourceGlsl, 288 stage, glslang::EShClientOpenGL, 100); 289 shader.setEnvClient(glslang::EShClientOpenGL, clientTargetVersion); 290 shader.setEnvTarget(glslang::EshTargetSpv, glslang::EShTargetSpv_1_0); 291 } 292 } 293 294 if (options().compileOnly) 295 shader.setCompileOnly(); 296 297 bool success = compile( 298 &shader, code, entryPointName, controls, nullptr, &shaderName); 299 300 glslang::TProgram program; 301 spv::SpvBuildLogger logger; 302 std::vector<uint32_t> spirv_binary; 303 304 if (!options().compileOnly) { 305 program.addShader(&shader); 306 success &= program.link(controls); 307 if (success) 308 program.mapIO(); 309 310 if (success && (controls & EShMsgSpvRules)) { 311 options().disableOptimizer = !enableOptimizer; 312 options().generateDebugInfo = enableDebug; 313 options().emitNonSemanticShaderDebugInfo = enableNonSemanticShaderDebugInfo; 314 options().emitNonSemanticShaderDebugSource = enableNonSemanticShaderDebugInfo; 315 glslang::GlslangToSpv(*program.getIntermediate(stage), spirv_binary, &logger, &options()); 316 } else { 317 return {{ 318 {shaderName, shader.getInfoLog(), shader.getInfoDebugLog()}, 319 }, 320 program.getInfoLog(), 321 program.getInfoDebugLog(), 322 true, 323 "", 324 ""}; 325 } 326 } else { 327 options().disableOptimizer = !enableOptimizer; 328 options().generateDebugInfo = enableDebug; 329 options().emitNonSemanticShaderDebugInfo = enableNonSemanticShaderDebugInfo; 330 options().emitNonSemanticShaderDebugSource = enableNonSemanticShaderDebugInfo; 331 glslang::GlslangToSpv(*shader.getIntermediate(), spirv_binary, &logger, &options()); 332 } 333 334 std::ostringstream disassembly_stream; 335 spv::Disassemble(disassembly_stream, spirv_binary); 336 bool validation_result = !options().validate || logger.getAllMessages().empty(); 337 return {{ 338 {shaderName, shader.getInfoLog(), shader.getInfoDebugLog()}, 339 }, 340 program.getInfoLog(), 341 program.getInfoDebugLog(), 342 validation_result, 343 logger.getAllMessages(), 344 disassembly_stream.str()}; 345 } 346 347 // Compiles and links the given source |code| of the given shader 348 // |stage| into the target under the semantics specified via |controls|. 349 // Returns a GlslangResult instance containing all the information generated 350 // during the process. If the target includes SPIR-V, also disassembles 351 // the result and returns disassembly text. compileLinkIoMap(const std::string shaderName,const std::string & code,const std::string & entryPointName,EShMessages controls,int baseSamplerBinding,int baseTextureBinding,int baseImageBinding,int baseUboBinding,int baseSsboBinding,bool autoMapBindings,bool flattenUniformArrays)352 GlslangResult compileLinkIoMap( 353 const std::string shaderName, const std::string& code, 354 const std::string& entryPointName, EShMessages controls, 355 int baseSamplerBinding, 356 int baseTextureBinding, 357 int baseImageBinding, 358 int baseUboBinding, 359 int baseSsboBinding, 360 bool autoMapBindings, 361 bool flattenUniformArrays) 362 { 363 const EShLanguage stage = GetShaderStage(GetSuffix(shaderName)); 364 365 glslang::TShader shader(stage); 366 shader.setShiftSamplerBinding(baseSamplerBinding); 367 shader.setShiftTextureBinding(baseTextureBinding); 368 shader.setShiftImageBinding(baseImageBinding); 369 shader.setShiftUboBinding(baseUboBinding); 370 shader.setShiftSsboBinding(baseSsboBinding); 371 shader.setAutoMapBindings(autoMapBindings); 372 shader.setAutoMapLocations(true); 373 #ifdef ENABLE_HLSL 374 shader.setFlattenUniformArrays(flattenUniformArrays); 375 #endif 376 377 bool success = compile(&shader, code, entryPointName, controls); 378 379 glslang::TProgram program; 380 program.addShader(&shader); 381 382 success &= program.link(controls); 383 if (success) 384 program.mapIO(); 385 386 spv::SpvBuildLogger logger; 387 388 if (success && (controls & EShMsgSpvRules)) { 389 std::vector<uint32_t> spirv_binary; 390 glslang::GlslangToSpv(*program.getIntermediate(stage), 391 spirv_binary, &logger, &options()); 392 393 std::ostringstream disassembly_stream; 394 spv::Disassemble(disassembly_stream, spirv_binary); 395 bool validation_result = !options().validate || logger.getAllMessages().empty(); 396 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 397 program.getInfoLog(), program.getInfoDebugLog(), 398 validation_result, logger.getAllMessages(), disassembly_stream.str()}; 399 } else { 400 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 401 program.getInfoLog(), program.getInfoDebugLog(), true, "", ""}; 402 } 403 } 404 405 // This is like compileAndLink but with remapping of the SPV binary 406 // through spirvbin_t::remap(). While technically this could be merged 407 // with compileAndLink() above (with the remap step optionally being a no-op) 408 // it is given separately here for ease of future extraction. 409 GlslangResult compileLinkRemap( 410 const std::string shaderName, const std::string& code, 411 const std::string& entryPointName, EShMessages controls, 412 const unsigned int remapOptions = spv::spirvbin_t::NONE) 413 { 414 const EShLanguage stage = GetShaderStage(GetSuffix(shaderName)); 415 416 glslang::TShader shader(stage); 417 shader.setAutoMapBindings(true); 418 shader.setAutoMapLocations(true); 419 420 bool success = compile(&shader, code, entryPointName, controls); 421 422 glslang::TProgram program; 423 program.addShader(&shader); 424 success &= program.link(controls); 425 if (success) 426 program.mapIO(); 427 428 if (success && (controls & EShMsgSpvRules)) { 429 spv::SpvBuildLogger logger; 430 std::vector<std::string> whiteListStrings; 431 std::vector<uint32_t> spirv_binary; 432 glslang::GlslangToSpv(*program.getIntermediate(stage), 433 spirv_binary, &logger, &options()); 434 435 spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, whiteListStrings, remapOptions); 436 437 std::ostringstream disassembly_stream; 438 spv::Disassemble(disassembly_stream, spirv_binary); 439 bool validation_result = !options().validate || logger.getAllMessages().empty(); 440 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 441 program.getInfoLog(), program.getInfoDebugLog(), 442 validation_result, logger.getAllMessages(), disassembly_stream.str()}; 443 } else { 444 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 445 program.getInfoLog(), program.getInfoDebugLog(), true, "", ""}; 446 } 447 } 448 449 // remap the binary in 'code' with the options in remapOptions 450 GlslangResult remap( 451 const std::string shaderName, const std::vector<uint32_t>& code, 452 EShMessages controls, 453 const unsigned int remapOptions = spv::spirvbin_t::NONE) 454 { 455 if ((controls & EShMsgSpvRules)) { 456 std::vector<uint32_t> spirv_binary(code); // scratch copy 457 std::vector<std::string> whiteListStrings; 458 spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, whiteListStrings, remapOptions); 459 460 std::ostringstream disassembly_stream; 461 spv::Disassemble(disassembly_stream, spirv_binary); 462 463 return {{{shaderName, "", ""},}, 464 "", "", 465 true, "", disassembly_stream.str()}; 466 } else { 467 return {{{shaderName, "", ""},}, "", "", true, "", ""}; 468 } 469 } 470 outputResultToStream(std::ostringstream * stream,const GlslangResult & result,EShMessages controls)471 void outputResultToStream(std::ostringstream* stream, 472 const GlslangResult& result, 473 EShMessages controls) 474 { 475 const auto outputIfNotEmpty = [&stream](const std::string& str) { 476 if (!str.empty()) *stream << str << "\n"; 477 }; 478 479 for (const auto& shaderResult : result.shaderResults) { 480 *stream << shaderResult.shaderName << "\n"; 481 outputIfNotEmpty(shaderResult.output); 482 outputIfNotEmpty(shaderResult.error); 483 } 484 outputIfNotEmpty(result.linkingOutput); 485 outputIfNotEmpty(result.linkingError); 486 if (!result.validationResult) { 487 *stream << "Validation failed\n"; 488 } 489 490 if (controls & EShMsgSpvRules) { 491 *stream 492 << (result.spirv.empty() 493 ? "SPIR-V is not generated for failed compile or link\n" 494 : result.spirv); 495 } 496 } 497 498 void loadFileCompileAndCheck(const std::string& testDir, 499 const std::string& testName, 500 Source source, 501 Semantics semantics, 502 glslang::EShTargetClientVersion clientTargetVersion, 503 glslang::EShTargetLanguageVersion targetLanguageVersion, 504 Target target, 505 bool automap = true, 506 const std::string& entryPointName="", 507 const std::string& baseDir="/baseResults/", 508 const bool enableOptimizer = false, 509 const bool enableDebug = false, 510 const bool enableNonSemanticShaderDebugInfo = false) 511 { 512 const std::string inputFname = testDir + "/" + testName; 513 const std::string expectedOutputFname = 514 testDir + baseDir + testName + ".out"; 515 std::string input, expectedOutput; 516 517 tryLoadFile(inputFname, "input", &input); 518 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 519 520 EShMessages controls = DeriveOptions(source, semantics, target); 521 if (enableOptimizer) 522 controls = static_cast<EShMessages>(controls & ~EShMsgHlslLegalization); 523 if (enableDebug) 524 controls = static_cast<EShMessages>(controls | EShMsgDebugInfo); 525 GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion, 526 targetLanguageVersion, false, EShTexSampTransKeep, enableOptimizer, enableDebug, 527 enableNonSemanticShaderDebugInfo, automap); 528 529 // Generate the hybrid output in the way of glslang. 530 std::ostringstream stream; 531 outputResultToStream(&stream, result, controls); 532 533 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 534 expectedOutputFname, result.spirvWarningsErrors); 535 } 536 537 void loadFileCompileAndCheckWithOptions(const std::string &testDir, 538 const std::string &testName, 539 Source source, 540 Semantics semantics, 541 glslang::EShTargetClientVersion clientTargetVersion, 542 glslang::EShTargetLanguageVersion targetLanguageVersion, 543 Target target, bool automap = true, const std::string &entryPointName = "", 544 const std::string &baseDir = "/baseResults/", 545 const EShMessages additionalOptions = EShMessages::EShMsgDefault) 546 { 547 const std::string inputFname = testDir + "/" + testName; 548 const std::string expectedOutputFname = testDir + baseDir + testName + ".out"; 549 std::string input, expectedOutput; 550 551 tryLoadFile(inputFname, "input", &input); 552 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 553 554 EShMessages controls = DeriveOptions(source, semantics, target); 555 controls = static_cast<EShMessages>(controls | additionalOptions); 556 GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion, 557 targetLanguageVersion, false, EShTexSampTransKeep, false, automap); 558 559 // Generate the hybrid output in the way of glslang. 560 std::ostringstream stream; 561 outputResultToStream(&stream, result, controls); 562 563 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname); 564 } 565 566 void loadFileCompileFlattenUniformsAndCheck(const std::string& testDir, 567 const std::string& testName, 568 Source source, 569 Semantics semantics, 570 Target target, 571 const std::string& entryPointName="") 572 { 573 const std::string inputFname = testDir + "/" + testName; 574 const std::string expectedOutputFname = 575 testDir + "/baseResults/" + testName + ".out"; 576 std::string input, expectedOutput; 577 578 tryLoadFile(inputFname, "input", &input); 579 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 580 581 const EShMessages controls = DeriveOptions(source, semantics, target); 582 GlslangResult result = compileAndLink(testName, input, entryPointName, controls, 583 glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, true); 584 585 // Generate the hybrid output in the way of glslang. 586 std::ostringstream stream; 587 outputResultToStream(&stream, result, controls); 588 589 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 590 expectedOutputFname, result.spirvWarningsErrors); 591 } 592 loadFileCompileIoMapAndCheck(const std::string & testDir,const std::string & testName,Source source,Semantics semantics,Target target,const std::string & entryPointName,int baseSamplerBinding,int baseTextureBinding,int baseImageBinding,int baseUboBinding,int baseSsboBinding,bool autoMapBindings,bool flattenUniformArrays)593 void loadFileCompileIoMapAndCheck(const std::string& testDir, 594 const std::string& testName, 595 Source source, 596 Semantics semantics, 597 Target target, 598 const std::string& entryPointName, 599 int baseSamplerBinding, 600 int baseTextureBinding, 601 int baseImageBinding, 602 int baseUboBinding, 603 int baseSsboBinding, 604 bool autoMapBindings, 605 bool flattenUniformArrays) 606 { 607 const std::string inputFname = testDir + "/" + testName; 608 const std::string expectedOutputFname = 609 testDir + "/baseResults/" + testName + ".out"; 610 std::string input, expectedOutput; 611 612 tryLoadFile(inputFname, "input", &input); 613 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 614 615 const EShMessages controls = DeriveOptions(source, semantics, target); 616 GlslangResult result = compileLinkIoMap(testName, input, entryPointName, controls, 617 baseSamplerBinding, baseTextureBinding, baseImageBinding, 618 baseUboBinding, baseSsboBinding, 619 autoMapBindings, 620 flattenUniformArrays); 621 622 // Generate the hybrid output in the way of glslang. 623 std::ostringstream stream; 624 outputResultToStream(&stream, result, controls); 625 626 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 627 expectedOutputFname, result.spirvWarningsErrors); 628 } 629 630 void loadFileCompileRemapAndCheck(const std::string& testDir, 631 const std::string& testName, 632 Source source, 633 Semantics semantics, 634 Target target, 635 const std::string& entryPointName="", 636 const unsigned int remapOptions = spv::spirvbin_t::NONE) 637 { 638 const std::string inputFname = testDir + "/" + testName; 639 const std::string expectedOutputFname = 640 testDir + "/baseResults/" + testName + ".out"; 641 std::string input, expectedOutput; 642 643 tryLoadFile(inputFname, "input", &input); 644 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 645 646 const EShMessages controls = DeriveOptions(source, semantics, target); 647 GlslangResult result = compileLinkRemap(testName, input, entryPointName, controls, remapOptions); 648 649 // Generate the hybrid output in the way of glslang. 650 std::ostringstream stream; 651 outputResultToStream(&stream, result, controls); 652 653 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 654 expectedOutputFname, result.spirvWarningsErrors); 655 } 656 657 void loadFileRemapAndCheck(const std::string& testDir, 658 const std::string& testName, 659 Source source, 660 Semantics semantics, 661 Target target, 662 const unsigned int remapOptions = spv::spirvbin_t::NONE) 663 { 664 const std::string inputFname = testDir + "/" + testName; 665 const std::string expectedOutputFname = 666 testDir + "/baseResults/" + testName + ".out"; 667 std::vector<std::uint32_t> input; 668 std::string expectedOutput; 669 670 tryLoadSpvFile(inputFname, "input", input); 671 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 672 673 const EShMessages controls = DeriveOptions(source, semantics, target); 674 GlslangResult result = remap(testName, input, controls, remapOptions); 675 676 // Generate the hybrid output in the way of glslang. 677 std::ostringstream stream; 678 outputResultToStream(&stream, result, controls); 679 680 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 681 expectedOutputFname, result.spirvWarningsErrors); 682 } 683 684 // Preprocesses the given |source| code. On success, returns true, the 685 // preprocessed shader, and warning messages. Otherwise, returns false, an 686 // empty string, and error messages. preprocess(const std::string & source)687 std::tuple<bool, std::string, std::string> preprocess( 688 const std::string& source) 689 { 690 const char* shaderStrings = source.data(); 691 const int shaderLengths = static_cast<int>(source.size()); 692 693 glslang::TShader shader(EShLangVertex); 694 shader.setStringsWithLengths(&shaderStrings, &shaderLengths, 1); 695 std::string ppShader; 696 glslang::TShader::ForbidIncluder includer; 697 const bool success = shader.preprocess( 698 GetDefaultResources(), defaultVersion, defaultProfile, forceVersionProfile, isForwardCompatible, 699 (EShMessages)(EShMsgOnlyPreprocessor | EShMsgCascadingErrors), 700 &ppShader, includer); 701 702 std::string log = shader.getInfoLog(); 703 log += shader.getInfoDebugLog(); 704 if (success) { 705 return std::make_tuple(true, ppShader, log); 706 } else { 707 return std::make_tuple(false, "", log); 708 } 709 } 710 loadFilePreprocessAndCheck(const std::string & testDir,const std::string & testName)711 void loadFilePreprocessAndCheck(const std::string& testDir, 712 const std::string& testName) 713 { 714 const std::string inputFname = testDir + "/" + testName; 715 const std::string expectedOutputFname = 716 testDir + "/baseResults/" + testName + ".out"; 717 const std::string expectedErrorFname = 718 testDir + "/baseResults/" + testName + ".err"; 719 std::string input, expectedOutput, expectedError; 720 721 tryLoadFile(inputFname, "input", &input); 722 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 723 tryLoadFile(expectedErrorFname, "expected error", &expectedError); 724 725 bool ppOk; 726 std::string output, error; 727 std::tie(ppOk, output, error) = preprocess(input); 728 if (!output.empty()) output += '\n'; 729 if (!error.empty()) error += '\n'; 730 731 checkEqAndUpdateIfRequested(expectedOutput, output, 732 expectedOutputFname); 733 checkEqAndUpdateIfRequested(expectedError, error, 734 expectedErrorFname); 735 } 736 737 void loadCompileUpgradeTextureToSampledTextureAndDropSamplersAndCheck(const std::string& testDir, 738 const std::string& testName, 739 Source source, 740 Semantics semantics, 741 Target target, 742 const std::string& entryPointName = "") 743 { 744 const std::string inputFname = testDir + "/" + testName; 745 const std::string expectedOutputFname = testDir + "/baseResults/" + testName + ".out"; 746 std::string input, expectedOutput; 747 748 tryLoadFile(inputFname, "input", &input); 749 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 750 751 const EShMessages controls = DeriveOptions(source, semantics, target); 752 GlslangResult result = compileAndLink(testName, input, entryPointName, controls, 753 glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, false, 754 EShTexSampTransUpgradeTextureRemoveSampler); 755 756 // Generate the hybrid output in the way of glslang. 757 std::ostringstream stream; 758 outputResultToStream(&stream, result, controls); 759 760 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 761 expectedOutputFname, result.spirvWarningsErrors); 762 } 763 options()764 glslang::SpvOptions& options() { return spirvOptions; } 765 766 private: 767 const int defaultVersion; 768 const EProfile defaultProfile; 769 const bool forceVersionProfile; 770 const bool isForwardCompatible; 771 glslang::SpvOptions spirvOptions; 772 }; 773 774 } // namespace glslangtest 775 776 #endif // GLSLANG_GTESTS_TEST_FIXTURE_H 777