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 <cstdint> 39 #include <fstream> 40 #include <sstream> 41 #include <streambuf> 42 #include <tuple> 43 #include <string> 44 45 #include <gtest/gtest.h> 46 47 #include "SPIRV/GlslangToSpv.h" 48 #include "SPIRV/disassemble.h" 49 #include "SPIRV/doc.h" 50 #include "SPIRV/SPVRemapper.h" 51 #include "StandAlone/ResourceLimits.h" 52 #include "glslang/Public/ShaderLang.h" 53 54 #include "Initializer.h" 55 #include "Settings.h" 56 57 namespace glslangtest { 58 59 // This function is used to provide custom test name suffixes based on the 60 // shader source file names. Otherwise, the test name suffixes will just be 61 // numbers, which are not quite obvious. 62 std::string FileNameAsCustomTestSuffix( 63 const ::testing::TestParamInfo<std::string>& info); 64 65 enum class Source { 66 GLSL, 67 HLSL, 68 }; 69 70 // Enum for shader compilation semantics. 71 enum class Semantics { 72 OpenGL, 73 Vulkan 74 }; 75 76 // Enum for compilation target. 77 enum class Target { 78 AST, 79 Spv, 80 BothASTAndSpv, 81 }; 82 83 EShLanguage GetShaderStage(const std::string& stage); 84 85 EShMessages DeriveOptions(Source, Semantics, Target); 86 87 // Reads the content of the file at the given |path|. On success, returns true 88 // and the contents; otherwise, returns false and an empty string. 89 std::pair<bool, std::string> ReadFile(const std::string& path); 90 std::pair<bool, std::vector<std::uint32_t> > ReadSpvBinaryFile(const std::string& path); 91 92 // Writes the given |contents| into the file at the given |path|. Returns true 93 // on successful output. 94 bool WriteFile(const std::string& path, const std::string& contents); 95 96 // Returns the suffix of the given |name|. 97 std::string GetSuffix(const std::string& name); 98 99 // Base class for glslang integration tests. It contains many handy utility-like 100 // methods such as reading shader source files, compiling into AST/SPIR-V, and 101 // comparing with expected outputs. 102 // 103 // To write value-Parameterized tests: 104 // using ValueParamTest = GlslangTest<::testing::TestWithParam<std::string>>; 105 // To use as normal fixture: 106 // using FixtureTest = GlslangTest<::testing::Test>; 107 template <typename GT> 108 class GlslangTest : public GT { 109 public: GlslangTest()110 GlslangTest() 111 : defaultVersion(100), 112 defaultProfile(ENoProfile), 113 forceVersionProfile(false), 114 isForwardCompatible(false) { 115 // Perform validation by default. 116 validatorOptions.validate = true; 117 } 118 119 // Tries to load the contents from the file at the given |path|. On success, 120 // writes the contents into |contents|. On failure, errors out. tryLoadFile(const std::string & path,const std::string & tag,std::string * contents)121 void tryLoadFile(const std::string& path, const std::string& tag, 122 std::string* contents) 123 { 124 bool fileReadOk; 125 std::tie(fileReadOk, *contents) = ReadFile(path); 126 ASSERT_TRUE(fileReadOk) << "Cannot open " << tag << " file: " << path; 127 } 128 129 // Tries to load the contents from the file at the given |path|. On success, 130 // writes the contents into |contents|. On failure, errors out. tryLoadSpvFile(const std::string & path,const std::string & tag,std::vector<uint32_t> & contents)131 void tryLoadSpvFile(const std::string& path, const std::string& tag, 132 std::vector<uint32_t>& contents) 133 { 134 bool fileReadOk; 135 std::tie(fileReadOk, contents) = ReadSpvBinaryFile(path); 136 ASSERT_TRUE(fileReadOk) << "Cannot open " << tag << " file: " << path; 137 } 138 139 // Checks the equality of |expected| and |real|. If they are not equal, 140 // write |real| to the given file named as |fname| if update mode is on. 141 void checkEqAndUpdateIfRequested(const std::string& expected, 142 const std::string& real, 143 const std::string& fname, 144 const std::string& errorsAndWarnings = "") 145 { 146 // In order to output the message we want under proper circumstances, 147 // we need the following operator<< stuff. 148 EXPECT_EQ(expected, real) 149 << (GlobalTestSettings.updateMode 150 ? ("Mismatch found and update mode turned on - " 151 "flushing expected result output.\n") 152 : "") 153 << "The following warnings/errors occurred:\n" 154 << errorsAndWarnings; 155 156 // Update the expected output file if requested. 157 // It looks weird to duplicate the comparison between expected_output 158 // and stream.str(). However, if creating a variable for the comparison 159 // result, we cannot have pretty print of the string diff in the above. 160 if (GlobalTestSettings.updateMode && expected != real) { 161 EXPECT_TRUE(WriteFile(fname, real)) << "Flushing failed"; 162 } 163 } 164 165 struct ShaderResult { 166 std::string shaderName; 167 std::string output; 168 std::string error; 169 }; 170 171 // A struct for holding all the information returned by glslang compilation 172 // and linking. 173 struct GlslangResult { 174 std::vector<ShaderResult> shaderResults; 175 std::string linkingOutput; 176 std::string linkingError; 177 bool validationResult; 178 std::string spirvWarningsErrors; 179 std::string spirv; // Optional SPIR-V disassembly text. 180 }; 181 182 // Compiles and the given source |code| of the given shader |stage| into 183 // the target under the semantics conveyed via |controls|. Returns true 184 // and modifies |shader| on success. 185 bool compile(glslang::TShader* shader, const std::string& code, 186 const std::string& entryPointName, EShMessages controls, 187 const TBuiltInResource* resources=nullptr, 188 const std::string* shaderName=nullptr) 189 { 190 const char* shaderStrings = code.data(); 191 const int shaderLengths = static_cast<int>(code.size()); 192 const char* shaderNames = nullptr; 193 194 if ((controls & EShMsgDebugInfo) && shaderName != nullptr) { 195 shaderNames = shaderName->data(); 196 shader->setStringsWithLengthsAndNames( 197 &shaderStrings, &shaderLengths, &shaderNames, 1); 198 } else 199 shader->setStringsWithLengths(&shaderStrings, &shaderLengths, 1); 200 if (!entryPointName.empty()) shader->setEntryPoint(entryPointName.c_str()); 201 return shader->parse( 202 (resources ? resources : &glslang::DefaultTBuiltInResource), 203 defaultVersion, isForwardCompatible, controls); 204 } 205 206 // Compiles and links the given source |code| of the given shader 207 // |stage| into the target under the semantics specified via |controls|. 208 // Returns a GlslangResult instance containing all the information generated 209 // during the process. If the target includes SPIR-V, also disassembles 210 // the result and returns disassembly text. 211 GlslangResult compileAndLink( 212 const std::string& shaderName, const std::string& code, 213 const std::string& entryPointName, EShMessages controls, 214 glslang::EShTargetClientVersion clientTargetVersion, 215 bool flattenUniformArrays = false, 216 EShTextureSamplerTransformMode texSampTransMode = EShTexSampTransKeep, 217 bool enableOptimizer = false, 218 bool enableDebug = false, 219 bool automap = true) 220 { 221 const EShLanguage stage = GetShaderStage(GetSuffix(shaderName)); 222 223 glslang::TShader shader(stage); 224 if (automap) { 225 shader.setAutoMapLocations(true); 226 shader.setAutoMapBindings(true); 227 } 228 shader.setTextureSamplerTransformMode(texSampTransMode); 229 shader.setFlattenUniformArrays(flattenUniformArrays); 230 231 if (controls & EShMsgSpvRules) { 232 if (controls & EShMsgVulkanRules) { 233 shader.setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl 234 : glslang::EShSourceGlsl, 235 stage, glslang::EShClientVulkan, 100); 236 shader.setEnvClient(glslang::EShClientVulkan, clientTargetVersion); 237 shader.setEnvTarget(glslang::EShTargetSpv, 238 clientTargetVersion == glslang::EShTargetVulkan_1_1 ? glslang::EShTargetSpv_1_3 239 : glslang::EShTargetSpv_1_0); 240 } else { 241 shader.setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl 242 : glslang::EShSourceGlsl, 243 stage, glslang::EShClientOpenGL, 100); 244 shader.setEnvClient(glslang::EShClientOpenGL, clientTargetVersion); 245 shader.setEnvTarget(glslang::EshTargetSpv, glslang::EShTargetSpv_1_0); 246 } 247 } 248 249 bool success = compile( 250 &shader, code, entryPointName, controls, nullptr, &shaderName); 251 252 glslang::TProgram program; 253 program.addShader(&shader); 254 success &= program.link(controls); 255 256 spv::SpvBuildLogger logger; 257 258 if (success && (controls & EShMsgSpvRules)) { 259 std::vector<uint32_t> spirv_binary; 260 options().disableOptimizer = !enableOptimizer; 261 options().generateDebugInfo = enableDebug; 262 glslang::GlslangToSpv(*program.getIntermediate(stage), 263 spirv_binary, &logger, &options()); 264 265 std::ostringstream disassembly_stream; 266 spv::Parameterize(); 267 spv::Disassemble(disassembly_stream, spirv_binary); 268 bool validation_result = !options().validate || logger.getAllMessages().empty(); 269 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 270 program.getInfoLog(), program.getInfoDebugLog(), 271 validation_result, logger.getAllMessages(), disassembly_stream.str()}; 272 } else { 273 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 274 program.getInfoLog(), program.getInfoDebugLog(), true, "", ""}; 275 } 276 } 277 278 // Compiles and links the given source |code| of the given shader 279 // |stage| into the target under the semantics specified via |controls|. 280 // Returns a GlslangResult instance containing all the information generated 281 // during the process. If the target includes SPIR-V, also disassembles 282 // 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)283 GlslangResult compileLinkIoMap( 284 const std::string shaderName, const std::string& code, 285 const std::string& entryPointName, EShMessages controls, 286 int baseSamplerBinding, 287 int baseTextureBinding, 288 int baseImageBinding, 289 int baseUboBinding, 290 int baseSsboBinding, 291 bool autoMapBindings, 292 bool flattenUniformArrays) 293 { 294 const EShLanguage stage = GetShaderStage(GetSuffix(shaderName)); 295 296 glslang::TShader shader(stage); 297 shader.setShiftSamplerBinding(baseSamplerBinding); 298 shader.setShiftTextureBinding(baseTextureBinding); 299 shader.setShiftImageBinding(baseImageBinding); 300 shader.setShiftUboBinding(baseUboBinding); 301 shader.setShiftSsboBinding(baseSsboBinding); 302 shader.setAutoMapBindings(autoMapBindings); 303 shader.setAutoMapLocations(true); 304 shader.setFlattenUniformArrays(flattenUniformArrays); 305 306 bool success = compile(&shader, code, entryPointName, controls); 307 308 glslang::TProgram program; 309 program.addShader(&shader); 310 311 success &= program.link(controls); 312 success &= program.mapIO(); 313 314 spv::SpvBuildLogger logger; 315 316 if (success && (controls & EShMsgSpvRules)) { 317 std::vector<uint32_t> spirv_binary; 318 glslang::GlslangToSpv(*program.getIntermediate(stage), 319 spirv_binary, &logger, &options()); 320 321 std::ostringstream disassembly_stream; 322 spv::Parameterize(); 323 spv::Disassemble(disassembly_stream, spirv_binary); 324 bool validation_result = !options().validate || logger.getAllMessages().empty(); 325 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 326 program.getInfoLog(), program.getInfoDebugLog(), 327 validation_result, logger.getAllMessages(), disassembly_stream.str()}; 328 } else { 329 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 330 program.getInfoLog(), program.getInfoDebugLog(), true, "", ""}; 331 } 332 } 333 334 // This is like compileAndLink but with remapping of the SPV binary 335 // through spirvbin_t::remap(). While technically this could be merged 336 // with compileAndLink() above (with the remap step optionally being a no-op) 337 // it is given separately here for ease of future extraction. 338 GlslangResult compileLinkRemap( 339 const std::string shaderName, const std::string& code, 340 const std::string& entryPointName, EShMessages controls, 341 const unsigned int remapOptions = spv::spirvbin_t::NONE) 342 { 343 const EShLanguage stage = GetShaderStage(GetSuffix(shaderName)); 344 345 glslang::TShader shader(stage); 346 shader.setAutoMapBindings(true); 347 shader.setAutoMapLocations(true); 348 349 bool success = compile(&shader, code, entryPointName, controls); 350 351 glslang::TProgram program; 352 program.addShader(&shader); 353 success &= program.link(controls); 354 355 spv::SpvBuildLogger logger; 356 357 if (success && (controls & EShMsgSpvRules)) { 358 std::vector<uint32_t> spirv_binary; 359 glslang::GlslangToSpv(*program.getIntermediate(stage), 360 spirv_binary, &logger, &options()); 361 362 spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, remapOptions); 363 364 std::ostringstream disassembly_stream; 365 spv::Parameterize(); 366 spv::Disassemble(disassembly_stream, spirv_binary); 367 bool validation_result = !options().validate || logger.getAllMessages().empty(); 368 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 369 program.getInfoLog(), program.getInfoDebugLog(), 370 validation_result, logger.getAllMessages(), disassembly_stream.str()}; 371 } else { 372 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 373 program.getInfoLog(), program.getInfoDebugLog(), true, "", ""}; 374 } 375 } 376 377 // remap the binary in 'code' with the options in remapOptions 378 GlslangResult remap( 379 const std::string shaderName, const std::vector<uint32_t>& code, 380 EShMessages controls, 381 const unsigned int remapOptions = spv::spirvbin_t::NONE) 382 { 383 if ((controls & EShMsgSpvRules)) { 384 std::vector<uint32_t> spirv_binary(code); // scratch copy 385 386 spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, remapOptions); 387 388 std::ostringstream disassembly_stream; 389 spv::Parameterize(); 390 spv::Disassemble(disassembly_stream, spirv_binary); 391 392 return {{{shaderName, "", ""},}, 393 "", "", 394 true, "", disassembly_stream.str()}; 395 } else { 396 return {{{shaderName, "", ""},}, "", "", true, "", ""}; 397 } 398 } 399 outputResultToStream(std::ostringstream * stream,const GlslangResult & result,EShMessages controls)400 void outputResultToStream(std::ostringstream* stream, 401 const GlslangResult& result, 402 EShMessages controls) 403 { 404 const auto outputIfNotEmpty = [&stream](const std::string& str) { 405 if (!str.empty()) *stream << str << "\n"; 406 }; 407 408 for (const auto& shaderResult : result.shaderResults) { 409 *stream << shaderResult.shaderName << "\n"; 410 outputIfNotEmpty(shaderResult.output); 411 outputIfNotEmpty(shaderResult.error); 412 } 413 outputIfNotEmpty(result.linkingOutput); 414 outputIfNotEmpty(result.linkingError); 415 if (!result.validationResult) { 416 *stream << "Validation failed\n"; 417 } 418 419 if (controls & EShMsgSpvRules) { 420 *stream 421 << (result.spirv.empty() 422 ? "SPIR-V is not generated for failed compile or link\n" 423 : result.spirv); 424 } 425 } 426 427 void loadFileCompileAndCheck(const std::string& testDir, 428 const std::string& testName, 429 Source source, 430 Semantics semantics, 431 glslang::EShTargetClientVersion clientTargetVersion, 432 Target target, 433 bool automap = true, 434 const std::string& entryPointName="", 435 const std::string& baseDir="/baseResults/", 436 const bool enableOptimizer = false, 437 const bool enableDebug = false) 438 { 439 const std::string inputFname = testDir + "/" + testName; 440 const std::string expectedOutputFname = 441 testDir + baseDir + testName + ".out"; 442 std::string input, expectedOutput; 443 444 tryLoadFile(inputFname, "input", &input); 445 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 446 447 EShMessages controls = DeriveOptions(source, semantics, target); 448 if (enableOptimizer) 449 controls = static_cast<EShMessages>(controls & ~EShMsgHlslLegalization); 450 if (enableDebug) 451 controls = static_cast<EShMessages>(controls | EShMsgDebugInfo); 452 GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion, false, 453 EShTexSampTransKeep, enableOptimizer, enableDebug, automap); 454 455 // Generate the hybrid output in the way of glslangValidator. 456 std::ostringstream stream; 457 outputResultToStream(&stream, result, controls); 458 459 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 460 expectedOutputFname, result.spirvWarningsErrors); 461 } 462 463 void loadFileCompileAndCheckWithOptions(const std::string &testDir, 464 const std::string &testName, 465 Source source, 466 Semantics semantics, 467 glslang::EShTargetClientVersion clientTargetVersion, 468 Target target, bool automap = true, const std::string &entryPointName = "", 469 const std::string &baseDir = "/baseResults/", 470 const EShMessages additionalOptions = EShMessages::EShMsgDefault) 471 { 472 const std::string inputFname = testDir + "/" + testName; 473 const std::string expectedOutputFname = testDir + baseDir + testName + ".out"; 474 std::string input, expectedOutput; 475 476 tryLoadFile(inputFname, "input", &input); 477 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 478 479 EShMessages controls = DeriveOptions(source, semantics, target); 480 controls = static_cast<EShMessages>(controls | additionalOptions); 481 GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion, false, 482 EShTexSampTransKeep, false, automap); 483 484 // Generate the hybrid output in the way of glslangValidator. 485 std::ostringstream stream; 486 outputResultToStream(&stream, result, controls); 487 488 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname); 489 } 490 491 void loadFileCompileFlattenUniformsAndCheck(const std::string& testDir, 492 const std::string& testName, 493 Source source, 494 Semantics semantics, 495 Target target, 496 const std::string& entryPointName="") 497 { 498 const std::string inputFname = testDir + "/" + testName; 499 const std::string expectedOutputFname = 500 testDir + "/baseResults/" + testName + ".out"; 501 std::string input, expectedOutput; 502 503 tryLoadFile(inputFname, "input", &input); 504 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 505 506 const EShMessages controls = DeriveOptions(source, semantics, target); 507 GlslangResult result = compileAndLink(testName, input, entryPointName, controls, 508 glslang::EShTargetVulkan_1_0, true); 509 510 // Generate the hybrid output in the way of glslangValidator. 511 std::ostringstream stream; 512 outputResultToStream(&stream, result, controls); 513 514 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 515 expectedOutputFname, result.spirvWarningsErrors); 516 } 517 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)518 void loadFileCompileIoMapAndCheck(const std::string& testDir, 519 const std::string& testName, 520 Source source, 521 Semantics semantics, 522 Target target, 523 const std::string& entryPointName, 524 int baseSamplerBinding, 525 int baseTextureBinding, 526 int baseImageBinding, 527 int baseUboBinding, 528 int baseSsboBinding, 529 bool autoMapBindings, 530 bool flattenUniformArrays) 531 { 532 const std::string inputFname = testDir + "/" + testName; 533 const std::string expectedOutputFname = 534 testDir + "/baseResults/" + testName + ".out"; 535 std::string input, expectedOutput; 536 537 tryLoadFile(inputFname, "input", &input); 538 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 539 540 const EShMessages controls = DeriveOptions(source, semantics, target); 541 GlslangResult result = compileLinkIoMap(testName, input, entryPointName, controls, 542 baseSamplerBinding, baseTextureBinding, baseImageBinding, 543 baseUboBinding, baseSsboBinding, 544 autoMapBindings, 545 flattenUniformArrays); 546 547 // Generate the hybrid output in the way of glslangValidator. 548 std::ostringstream stream; 549 outputResultToStream(&stream, result, controls); 550 551 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 552 expectedOutputFname, result.spirvWarningsErrors); 553 } 554 555 void loadFileCompileRemapAndCheck(const std::string& testDir, 556 const std::string& testName, 557 Source source, 558 Semantics semantics, 559 Target target, 560 const std::string& entryPointName="", 561 const unsigned int remapOptions = spv::spirvbin_t::NONE) 562 { 563 const std::string inputFname = testDir + "/" + testName; 564 const std::string expectedOutputFname = 565 testDir + "/baseResults/" + testName + ".out"; 566 std::string input, expectedOutput; 567 568 tryLoadFile(inputFname, "input", &input); 569 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 570 571 const EShMessages controls = DeriveOptions(source, semantics, target); 572 GlslangResult result = compileLinkRemap(testName, input, entryPointName, controls, remapOptions); 573 574 // Generate the hybrid output in the way of glslangValidator. 575 std::ostringstream stream; 576 outputResultToStream(&stream, result, controls); 577 578 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 579 expectedOutputFname, result.spirvWarningsErrors); 580 } 581 582 void loadFileRemapAndCheck(const std::string& testDir, 583 const std::string& testName, 584 Source source, 585 Semantics semantics, 586 Target target, 587 const unsigned int remapOptions = spv::spirvbin_t::NONE) 588 { 589 const std::string inputFname = testDir + "/" + testName; 590 const std::string expectedOutputFname = 591 testDir + "/baseResults/" + testName + ".out"; 592 std::vector<std::uint32_t> input; 593 std::string expectedOutput; 594 595 tryLoadSpvFile(inputFname, "input", input); 596 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 597 598 const EShMessages controls = DeriveOptions(source, semantics, target); 599 GlslangResult result = remap(testName, input, controls, remapOptions); 600 601 // Generate the hybrid output in the way of glslangValidator. 602 std::ostringstream stream; 603 outputResultToStream(&stream, result, controls); 604 605 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 606 expectedOutputFname, result.spirvWarningsErrors); 607 } 608 609 // Preprocesses the given |source| code. On success, returns true, the 610 // preprocessed shader, and warning messages. Otherwise, returns false, an 611 // empty string, and error messages. preprocess(const std::string & source)612 std::tuple<bool, std::string, std::string> preprocess( 613 const std::string& source) 614 { 615 const char* shaderStrings = source.data(); 616 const int shaderLengths = static_cast<int>(source.size()); 617 618 glslang::TShader shader(EShLangVertex); 619 shader.setStringsWithLengths(&shaderStrings, &shaderLengths, 1); 620 std::string ppShader; 621 glslang::TShader::ForbidIncluder includer; 622 const bool success = shader.preprocess( 623 &glslang::DefaultTBuiltInResource, defaultVersion, defaultProfile, 624 forceVersionProfile, isForwardCompatible, (EShMessages)(EShMsgOnlyPreprocessor | EShMsgCascadingErrors), 625 &ppShader, includer); 626 627 std::string log = shader.getInfoLog(); 628 log += shader.getInfoDebugLog(); 629 if (success) { 630 return std::make_tuple(true, ppShader, log); 631 } else { 632 return std::make_tuple(false, "", log); 633 } 634 } 635 loadFilePreprocessAndCheck(const std::string & testDir,const std::string & testName)636 void loadFilePreprocessAndCheck(const std::string& testDir, 637 const std::string& testName) 638 { 639 const std::string inputFname = testDir + "/" + testName; 640 const std::string expectedOutputFname = 641 testDir + "/baseResults/" + testName + ".out"; 642 const std::string expectedErrorFname = 643 testDir + "/baseResults/" + testName + ".err"; 644 std::string input, expectedOutput, expectedError; 645 646 tryLoadFile(inputFname, "input", &input); 647 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 648 tryLoadFile(expectedErrorFname, "expected error", &expectedError); 649 650 bool ppOk; 651 std::string output, error; 652 std::tie(ppOk, output, error) = preprocess(input); 653 if (!output.empty()) output += '\n'; 654 if (!error.empty()) error += '\n'; 655 656 checkEqAndUpdateIfRequested(expectedOutput, output, 657 expectedOutputFname); 658 checkEqAndUpdateIfRequested(expectedError, error, 659 expectedErrorFname); 660 } 661 662 void loadCompileUpgradeTextureToSampledTextureAndDropSamplersAndCheck(const std::string& testDir, 663 const std::string& testName, 664 Source source, 665 Semantics semantics, 666 Target target, 667 const std::string& entryPointName = "") 668 { 669 const std::string inputFname = testDir + "/" + testName; 670 const std::string expectedOutputFname = testDir + "/baseResults/" + testName + ".out"; 671 std::string input, expectedOutput; 672 673 tryLoadFile(inputFname, "input", &input); 674 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 675 676 const EShMessages controls = DeriveOptions(source, semantics, target); 677 GlslangResult result = compileAndLink(testName, input, entryPointName, controls, 678 glslang::EShTargetVulkan_1_0, false, 679 EShTexSampTransUpgradeTextureRemoveSampler); 680 681 // Generate the hybrid output in the way of glslangValidator. 682 std::ostringstream stream; 683 outputResultToStream(&stream, result, controls); 684 685 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 686 expectedOutputFname, result.spirvWarningsErrors); 687 } 688 options()689 glslang::SpvOptions& options() { return validatorOptions; } 690 691 private: 692 const int defaultVersion; 693 const EProfile defaultProfile; 694 const bool forceVersionProfile; 695 const bool isForwardCompatible; 696 glslang::SpvOptions validatorOptions; 697 }; 698 699 } // namespace glslangtest 700 701 #endif // GLSLANG_GTESTS_TEST_FIXTURE_H 702