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