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