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 spirvOptions.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 glslang::EShTargetLanguageVersion targetLanguageVersion, 216 bool flattenUniformArrays = false, 217 EShTextureSamplerTransformMode texSampTransMode = EShTexSampTransKeep, 218 bool enableOptimizer = false, 219 bool enableDebug = false, 220 bool automap = true) 221 { 222 const EShLanguage stage = GetShaderStage(GetSuffix(shaderName)); 223 224 glslang::TShader shader(stage); 225 if (automap) { 226 shader.setAutoMapLocations(true); 227 shader.setAutoMapBindings(true); 228 } 229 shader.setTextureSamplerTransformMode(texSampTransMode); 230 #ifdef ENABLE_HLSL 231 shader.setFlattenUniformArrays(flattenUniformArrays); 232 #endif 233 234 if (controls & EShMsgSpvRules) { 235 if (controls & EShMsgVulkanRules) { 236 shader.setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl 237 : glslang::EShSourceGlsl, 238 stage, glslang::EShClientVulkan, 100); 239 shader.setEnvClient(glslang::EShClientVulkan, clientTargetVersion); 240 shader.setEnvTarget(glslang::EShTargetSpv, targetLanguageVersion); 241 } else { 242 shader.setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl 243 : glslang::EShSourceGlsl, 244 stage, glslang::EShClientOpenGL, 100); 245 shader.setEnvClient(glslang::EShClientOpenGL, clientTargetVersion); 246 shader.setEnvTarget(glslang::EshTargetSpv, glslang::EShTargetSpv_1_0); 247 } 248 } 249 250 bool success = compile( 251 &shader, code, entryPointName, controls, nullptr, &shaderName); 252 253 glslang::TProgram program; 254 program.addShader(&shader); 255 success &= program.link(controls); 256 257 spv::SpvBuildLogger logger; 258 259 if (success && (controls & EShMsgSpvRules)) { 260 std::vector<uint32_t> spirv_binary; 261 options().disableOptimizer = !enableOptimizer; 262 options().generateDebugInfo = enableDebug; 263 glslang::GlslangToSpv(*program.getIntermediate(stage), 264 spirv_binary, &logger, &options()); 265 266 std::ostringstream disassembly_stream; 267 spv::Parameterize(); 268 spv::Disassemble(disassembly_stream, spirv_binary); 269 bool validation_result = !options().validate || logger.getAllMessages().empty(); 270 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 271 program.getInfoLog(), program.getInfoDebugLog(), 272 validation_result, logger.getAllMessages(), disassembly_stream.str()}; 273 } else { 274 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 275 program.getInfoLog(), program.getInfoDebugLog(), true, "", ""}; 276 } 277 } 278 279 // Compiles and links the given source |code| of the given shader 280 // |stage| into the target under the semantics specified via |controls|. 281 // Returns a GlslangResult instance containing all the information generated 282 // during the process. If the target includes SPIR-V, also disassembles 283 // 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)284 GlslangResult compileLinkIoMap( 285 const std::string shaderName, const std::string& code, 286 const std::string& entryPointName, EShMessages controls, 287 int baseSamplerBinding, 288 int baseTextureBinding, 289 int baseImageBinding, 290 int baseUboBinding, 291 int baseSsboBinding, 292 bool autoMapBindings, 293 bool flattenUniformArrays) 294 { 295 const EShLanguage stage = GetShaderStage(GetSuffix(shaderName)); 296 297 glslang::TShader shader(stage); 298 shader.setShiftSamplerBinding(baseSamplerBinding); 299 shader.setShiftTextureBinding(baseTextureBinding); 300 shader.setShiftImageBinding(baseImageBinding); 301 shader.setShiftUboBinding(baseUboBinding); 302 shader.setShiftSsboBinding(baseSsboBinding); 303 shader.setAutoMapBindings(autoMapBindings); 304 shader.setAutoMapLocations(true); 305 #ifdef ENABLE_HLSL 306 shader.setFlattenUniformArrays(flattenUniformArrays); 307 #endif 308 309 bool success = compile(&shader, code, entryPointName, controls); 310 311 glslang::TProgram program; 312 program.addShader(&shader); 313 314 success &= program.link(controls); 315 #ifndef GLSLANG_WEB 316 success &= program.mapIO(); 317 #endif 318 319 spv::SpvBuildLogger logger; 320 321 if (success && (controls & EShMsgSpvRules)) { 322 std::vector<uint32_t> spirv_binary; 323 glslang::GlslangToSpv(*program.getIntermediate(stage), 324 spirv_binary, &logger, &options()); 325 326 std::ostringstream disassembly_stream; 327 spv::Parameterize(); 328 spv::Disassemble(disassembly_stream, spirv_binary); 329 bool validation_result = !options().validate || logger.getAllMessages().empty(); 330 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 331 program.getInfoLog(), program.getInfoDebugLog(), 332 validation_result, logger.getAllMessages(), disassembly_stream.str()}; 333 } else { 334 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 335 program.getInfoLog(), program.getInfoDebugLog(), true, "", ""}; 336 } 337 } 338 339 // This is like compileAndLink but with remapping of the SPV binary 340 // through spirvbin_t::remap(). While technically this could be merged 341 // with compileAndLink() above (with the remap step optionally being a no-op) 342 // it is given separately here for ease of future extraction. 343 GlslangResult compileLinkRemap( 344 const std::string shaderName, const std::string& code, 345 const std::string& entryPointName, EShMessages controls, 346 const unsigned int remapOptions = spv::spirvbin_t::NONE) 347 { 348 const EShLanguage stage = GetShaderStage(GetSuffix(shaderName)); 349 350 glslang::TShader shader(stage); 351 shader.setAutoMapBindings(true); 352 shader.setAutoMapLocations(true); 353 354 bool success = compile(&shader, code, entryPointName, controls); 355 356 glslang::TProgram program; 357 program.addShader(&shader); 358 success &= program.link(controls); 359 360 spv::SpvBuildLogger logger; 361 362 if (success && (controls & EShMsgSpvRules)) { 363 std::vector<uint32_t> spirv_binary; 364 glslang::GlslangToSpv(*program.getIntermediate(stage), 365 spirv_binary, &logger, &options()); 366 367 spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, remapOptions); 368 369 std::ostringstream disassembly_stream; 370 spv::Parameterize(); 371 spv::Disassemble(disassembly_stream, spirv_binary); 372 bool validation_result = !options().validate || logger.getAllMessages().empty(); 373 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 374 program.getInfoLog(), program.getInfoDebugLog(), 375 validation_result, logger.getAllMessages(), disassembly_stream.str()}; 376 } else { 377 return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, 378 program.getInfoLog(), program.getInfoDebugLog(), true, "", ""}; 379 } 380 } 381 382 // remap the binary in 'code' with the options in remapOptions 383 GlslangResult remap( 384 const std::string shaderName, const std::vector<uint32_t>& code, 385 EShMessages controls, 386 const unsigned int remapOptions = spv::spirvbin_t::NONE) 387 { 388 if ((controls & EShMsgSpvRules)) { 389 std::vector<uint32_t> spirv_binary(code); // scratch copy 390 391 spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, remapOptions); 392 393 std::ostringstream disassembly_stream; 394 spv::Parameterize(); 395 spv::Disassemble(disassembly_stream, spirv_binary); 396 397 return {{{shaderName, "", ""},}, 398 "", "", 399 true, "", disassembly_stream.str()}; 400 } else { 401 return {{{shaderName, "", ""},}, "", "", true, "", ""}; 402 } 403 } 404 outputResultToStream(std::ostringstream * stream,const GlslangResult & result,EShMessages controls)405 void outputResultToStream(std::ostringstream* stream, 406 const GlslangResult& result, 407 EShMessages controls) 408 { 409 const auto outputIfNotEmpty = [&stream](const std::string& str) { 410 if (!str.empty()) *stream << str << "\n"; 411 }; 412 413 for (const auto& shaderResult : result.shaderResults) { 414 *stream << shaderResult.shaderName << "\n"; 415 outputIfNotEmpty(shaderResult.output); 416 outputIfNotEmpty(shaderResult.error); 417 } 418 outputIfNotEmpty(result.linkingOutput); 419 outputIfNotEmpty(result.linkingError); 420 if (!result.validationResult) { 421 *stream << "Validation failed\n"; 422 } 423 424 if (controls & EShMsgSpvRules) { 425 *stream 426 << (result.spirv.empty() 427 ? "SPIR-V is not generated for failed compile or link\n" 428 : result.spirv); 429 } 430 } 431 432 void loadFileCompileAndCheck(const std::string& testDir, 433 const std::string& testName, 434 Source source, 435 Semantics semantics, 436 glslang::EShTargetClientVersion clientTargetVersion, 437 glslang::EShTargetLanguageVersion targetLanguageVersion, 438 Target target, 439 bool automap = true, 440 const std::string& entryPointName="", 441 const std::string& baseDir="/baseResults/", 442 const bool enableOptimizer = false, 443 const bool enableDebug = false) 444 { 445 const std::string inputFname = testDir + "/" + testName; 446 const std::string expectedOutputFname = 447 testDir + baseDir + testName + ".out"; 448 std::string input, expectedOutput; 449 450 tryLoadFile(inputFname, "input", &input); 451 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 452 453 EShMessages controls = DeriveOptions(source, semantics, target); 454 if (enableOptimizer) 455 controls = static_cast<EShMessages>(controls & ~EShMsgHlslLegalization); 456 if (enableDebug) 457 controls = static_cast<EShMessages>(controls | EShMsgDebugInfo); 458 GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion, 459 targetLanguageVersion, false, EShTexSampTransKeep, enableOptimizer, enableDebug, automap); 460 461 // Generate the hybrid output in the way of glslangValidator. 462 std::ostringstream stream; 463 outputResultToStream(&stream, result, controls); 464 465 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 466 expectedOutputFname, result.spirvWarningsErrors); 467 } 468 469 void loadFileCompileAndCheckWithOptions(const std::string &testDir, 470 const std::string &testName, 471 Source source, 472 Semantics semantics, 473 glslang::EShTargetClientVersion clientTargetVersion, 474 glslang::EShTargetLanguageVersion targetLanguageVersion, 475 Target target, bool automap = true, const std::string &entryPointName = "", 476 const std::string &baseDir = "/baseResults/", 477 const EShMessages additionalOptions = EShMessages::EShMsgDefault) 478 { 479 const std::string inputFname = testDir + "/" + testName; 480 const std::string expectedOutputFname = testDir + baseDir + testName + ".out"; 481 std::string input, expectedOutput; 482 483 tryLoadFile(inputFname, "input", &input); 484 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 485 486 EShMessages controls = DeriveOptions(source, semantics, target); 487 controls = static_cast<EShMessages>(controls | additionalOptions); 488 GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion, 489 targetLanguageVersion, false, EShTexSampTransKeep, false, automap); 490 491 // Generate the hybrid output in the way of glslangValidator. 492 std::ostringstream stream; 493 outputResultToStream(&stream, result, controls); 494 495 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname); 496 } 497 498 void loadFileCompileFlattenUniformsAndCheck(const std::string& testDir, 499 const std::string& testName, 500 Source source, 501 Semantics semantics, 502 Target target, 503 const std::string& entryPointName="") 504 { 505 const std::string inputFname = testDir + "/" + testName; 506 const std::string expectedOutputFname = 507 testDir + "/baseResults/" + testName + ".out"; 508 std::string input, expectedOutput; 509 510 tryLoadFile(inputFname, "input", &input); 511 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 512 513 const EShMessages controls = DeriveOptions(source, semantics, target); 514 GlslangResult result = compileAndLink(testName, input, entryPointName, controls, 515 glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, true); 516 517 // Generate the hybrid output in the way of glslangValidator. 518 std::ostringstream stream; 519 outputResultToStream(&stream, result, controls); 520 521 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 522 expectedOutputFname, result.spirvWarningsErrors); 523 } 524 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)525 void loadFileCompileIoMapAndCheck(const std::string& testDir, 526 const std::string& testName, 527 Source source, 528 Semantics semantics, 529 Target target, 530 const std::string& entryPointName, 531 int baseSamplerBinding, 532 int baseTextureBinding, 533 int baseImageBinding, 534 int baseUboBinding, 535 int baseSsboBinding, 536 bool autoMapBindings, 537 bool flattenUniformArrays) 538 { 539 const std::string inputFname = testDir + "/" + testName; 540 const std::string expectedOutputFname = 541 testDir + "/baseResults/" + testName + ".out"; 542 std::string input, expectedOutput; 543 544 tryLoadFile(inputFname, "input", &input); 545 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 546 547 const EShMessages controls = DeriveOptions(source, semantics, target); 548 GlslangResult result = compileLinkIoMap(testName, input, entryPointName, controls, 549 baseSamplerBinding, baseTextureBinding, baseImageBinding, 550 baseUboBinding, baseSsboBinding, 551 autoMapBindings, 552 flattenUniformArrays); 553 554 // Generate the hybrid output in the way of glslangValidator. 555 std::ostringstream stream; 556 outputResultToStream(&stream, result, controls); 557 558 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 559 expectedOutputFname, result.spirvWarningsErrors); 560 } 561 562 void loadFileCompileRemapAndCheck(const std::string& testDir, 563 const std::string& testName, 564 Source source, 565 Semantics semantics, 566 Target target, 567 const std::string& entryPointName="", 568 const unsigned int remapOptions = spv::spirvbin_t::NONE) 569 { 570 const std::string inputFname = testDir + "/" + testName; 571 const std::string expectedOutputFname = 572 testDir + "/baseResults/" + testName + ".out"; 573 std::string input, expectedOutput; 574 575 tryLoadFile(inputFname, "input", &input); 576 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 577 578 const EShMessages controls = DeriveOptions(source, semantics, target); 579 GlslangResult result = compileLinkRemap(testName, input, entryPointName, controls, remapOptions); 580 581 // Generate the hybrid output in the way of glslangValidator. 582 std::ostringstream stream; 583 outputResultToStream(&stream, result, controls); 584 585 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 586 expectedOutputFname, result.spirvWarningsErrors); 587 } 588 589 void loadFileRemapAndCheck(const std::string& testDir, 590 const std::string& testName, 591 Source source, 592 Semantics semantics, 593 Target target, 594 const unsigned int remapOptions = spv::spirvbin_t::NONE) 595 { 596 const std::string inputFname = testDir + "/" + testName; 597 const std::string expectedOutputFname = 598 testDir + "/baseResults/" + testName + ".out"; 599 std::vector<std::uint32_t> input; 600 std::string expectedOutput; 601 602 tryLoadSpvFile(inputFname, "input", input); 603 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 604 605 const EShMessages controls = DeriveOptions(source, semantics, target); 606 GlslangResult result = remap(testName, input, controls, remapOptions); 607 608 // Generate the hybrid output in the way of glslangValidator. 609 std::ostringstream stream; 610 outputResultToStream(&stream, result, controls); 611 612 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 613 expectedOutputFname, result.spirvWarningsErrors); 614 } 615 616 // Preprocesses the given |source| code. On success, returns true, the 617 // preprocessed shader, and warning messages. Otherwise, returns false, an 618 // empty string, and error messages. preprocess(const std::string & source)619 std::tuple<bool, std::string, std::string> preprocess( 620 const std::string& source) 621 { 622 const char* shaderStrings = source.data(); 623 const int shaderLengths = static_cast<int>(source.size()); 624 625 glslang::TShader shader(EShLangVertex); 626 shader.setStringsWithLengths(&shaderStrings, &shaderLengths, 1); 627 std::string ppShader; 628 glslang::TShader::ForbidIncluder includer; 629 const bool success = shader.preprocess( 630 &glslang::DefaultTBuiltInResource, defaultVersion, defaultProfile, 631 forceVersionProfile, isForwardCompatible, (EShMessages)(EShMsgOnlyPreprocessor | EShMsgCascadingErrors), 632 &ppShader, includer); 633 634 std::string log = shader.getInfoLog(); 635 log += shader.getInfoDebugLog(); 636 if (success) { 637 return std::make_tuple(true, ppShader, log); 638 } else { 639 return std::make_tuple(false, "", log); 640 } 641 } 642 loadFilePreprocessAndCheck(const std::string & testDir,const std::string & testName)643 void loadFilePreprocessAndCheck(const std::string& testDir, 644 const std::string& testName) 645 { 646 const std::string inputFname = testDir + "/" + testName; 647 const std::string expectedOutputFname = 648 testDir + "/baseResults/" + testName + ".out"; 649 const std::string expectedErrorFname = 650 testDir + "/baseResults/" + testName + ".err"; 651 std::string input, expectedOutput, expectedError; 652 653 tryLoadFile(inputFname, "input", &input); 654 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 655 tryLoadFile(expectedErrorFname, "expected error", &expectedError); 656 657 bool ppOk; 658 std::string output, error; 659 std::tie(ppOk, output, error) = preprocess(input); 660 if (!output.empty()) output += '\n'; 661 if (!error.empty()) error += '\n'; 662 663 checkEqAndUpdateIfRequested(expectedOutput, output, 664 expectedOutputFname); 665 checkEqAndUpdateIfRequested(expectedError, error, 666 expectedErrorFname); 667 } 668 669 void loadCompileUpgradeTextureToSampledTextureAndDropSamplersAndCheck(const std::string& testDir, 670 const std::string& testName, 671 Source source, 672 Semantics semantics, 673 Target target, 674 const std::string& entryPointName = "") 675 { 676 const std::string inputFname = testDir + "/" + testName; 677 const std::string expectedOutputFname = testDir + "/baseResults/" + testName + ".out"; 678 std::string input, expectedOutput; 679 680 tryLoadFile(inputFname, "input", &input); 681 tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); 682 683 const EShMessages controls = DeriveOptions(source, semantics, target); 684 GlslangResult result = compileAndLink(testName, input, entryPointName, controls, 685 glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, false, 686 EShTexSampTransUpgradeTextureRemoveSampler); 687 688 // Generate the hybrid output in the way of glslangValidator. 689 std::ostringstream stream; 690 outputResultToStream(&stream, result, controls); 691 692 checkEqAndUpdateIfRequested(expectedOutput, stream.str(), 693 expectedOutputFname, result.spirvWarningsErrors); 694 } 695 options()696 glslang::SpvOptions& options() { return spirvOptions; } 697 698 private: 699 const int defaultVersion; 700 const EProfile defaultProfile; 701 const bool forceVersionProfile; 702 const bool isForwardCompatible; 703 glslang::SpvOptions spirvOptions; 704 }; 705 706 } // namespace glslangtest 707 708 #endif // GLSLANG_GTESTS_TEST_FIXTURE_H 709