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