• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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