yum-mirror/glslang

git clone https://git.yummers.dev/yum-mirror/glslang

Guang-035Test relaxSetBindingLimits for parser validation and GLSL IoMap5336e80d

main
28.8 KiB678 linesraw
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 <algorithm>
39#include <cstdint>
40#include <fstream>
41#include <sstream>
42#include <streambuf>
43#include <tuple>
44#include <string>
45
46#include <gtest/gtest.h>
47
48#include "SPIRV/GlslangToSpv.h"
49#include "SPIRV/disassemble.h"
50#include "SPIRV/doc.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
58namespace 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.
63std::string FileNameAsCustomTestSuffix(
64    const ::testing::TestParamInfo<std::string>& info);
65
66enum class Source {
67  GLSL,
68  HLSL,
69};
70
71// Enum for shader compilation semantics.
72enum class Semantics {
73    OpenGL,
74    Vulkan
75};
76
77// Enum for compilation target.
78enum class Target {
79    AST,
80    Spv,
81    BothASTAndSpv,
82};
83
84EShLanguage GetShaderStage(const std::string& stage);
85
86EShMessages 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.
90std::pair<bool, std::string> ReadFile(const std::string& path);
91std::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.
95bool WriteFile(const std::string& path, const std::string& contents);
96
97// Returns the suffix of the given |name|.
98std::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>;
108template <typename GT>
109class GlslangTest : public GT {
110public:
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.
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.
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
203        // A includer that always assumes header name is a relative path to the test folder.
204        class GlslangTestIncluder : public glslang::TShader::Includer {
205        public:
206            virtual IncludeResult* includeLocal(const char* headerName, const char* /*includerName*/,
207                                                size_t /*inclusionDepth*/) override
208            {
209                std::string path = GLSLANG_TEST_DIRECTORY;
210                path += '/';
211                path += headerName;
212                std::replace(path.begin(), path.end(), '\\', '/');
213
214                auto [success, fileContent] = ReadFile(path);
215                if (success) {
216                    auto buffer = new char[fileContent.size() + 1];
217                    std::copy(fileContent.begin(), fileContent.end(), buffer);
218                    buffer[fileContent.size()] = '\0';
219
220                    return new IncludeResult(headerName, buffer, fileContent.size(), buffer);
221                }
222
223                return nullptr;
224            }
225
226            virtual void releaseInclude(IncludeResult* result) override
227            {
228                if (result != nullptr) {
229                    delete[] static_cast<char*>(result->userData);
230                    delete result;
231                }
232            }
233        };
234
235        GlslangTestIncluder includer;
236        return shader->parse((resources ? resources : GetDefaultResources()), defaultVersion, isForwardCompatible,
237                             controls, includer);
238    }
239
240    // Compiles and links the given source |code| of the given shader
241    // |stage| into the target under the semantics specified via |controls|.
242    // Returns a GlslangResult instance containing all the information generated
243    // during the process. If the target includes SPIR-V, also disassembles
244    // the result and returns disassembly text.
245    GlslangResult compileAndLink(
246            const std::string& shaderName, const std::string& code,
247            const std::string& entryPointName, EShMessages controls,
248            glslang::EShTargetClientVersion clientTargetVersion,
249            glslang::EShTargetLanguageVersion targetLanguageVersion,
250            bool flattenUniformArrays = false,
251            EShTextureSamplerTransformMode texSampTransMode = EShTexSampTransKeep,
252            bool enableOptimizer = false,
253            bool enableDebug = false,
254            bool enableNonSemanticShaderDebugInfo = false,
255            bool automap = true)
256    {
257        const EShLanguage stage = GetShaderStage(GetSuffix(shaderName));
258
259        glslang::TShader shader(stage);
260        if (automap) {
261            shader.setAutoMapLocations(true);
262            shader.setAutoMapBindings(true);
263        }
264
265        if (enableDebug) {
266            shader.setDebugInfo(true);
267        }
268        if (enableNonSemanticShaderDebugInfo) {
269            assert(enableDebug && "Debug must be on for non-semantic debug info");
270        }
271
272        shader.setTextureSamplerTransformMode(texSampTransMode);
273#ifdef ENABLE_HLSL
274        shader.setFlattenUniformArrays(flattenUniformArrays);
275#endif
276
277        if (controls & EShMsgSpvRules) {
278            if (controls & EShMsgVulkanRules) {
279                shader.setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl
280                                                               : glslang::EShSourceGlsl,
281                                    stage, glslang::EShClientVulkan, 100);
282                shader.setEnvClient(glslang::EShClientVulkan, clientTargetVersion);
283                shader.setEnvTarget(glslang::EShTargetSpv, targetLanguageVersion);
284            } else {
285                shader.setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl
286                                                               : glslang::EShSourceGlsl,
287                                    stage, glslang::EShClientOpenGL, 100);
288                shader.setEnvClient(glslang::EShClientOpenGL, clientTargetVersion);
289                shader.setEnvTarget(glslang::EshTargetSpv, glslang::EShTargetSpv_1_0);
290            }
291        }
292
293        if (options().compileOnly)
294            shader.setCompileOnly();
295
296        bool success = compile(
297                &shader, code, entryPointName, controls, nullptr, &shaderName);
298
299        glslang::TProgram program;
300        spv::SpvBuildLogger logger;
301        std::vector<uint32_t> spirv_binary;
302
303        if (!options().compileOnly) {
304            program.addShader(&shader);
305            success &= program.link(controls);
306            if (success)
307                program.mapIO();
308
309            if (success && (controls & EShMsgSpvRules)) {
310                options().disableOptimizer = !enableOptimizer;
311                options().generateDebugInfo = enableDebug;
312                options().emitNonSemanticShaderDebugInfo = enableNonSemanticShaderDebugInfo;
313                options().emitNonSemanticShaderDebugSource = enableNonSemanticShaderDebugInfo;
314                glslang::GlslangToSpv(*program.getIntermediate(stage), spirv_binary, &logger, &options());
315            } else {
316                return {{
317                            {shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},
318                        },
319                        program.getInfoLog(),
320                        program.getInfoDebugLog(),
321                        true,
322                        "",
323                        ""};
324            }
325        } else {
326            options().disableOptimizer = !enableOptimizer;
327            options().generateDebugInfo = enableDebug;
328            options().emitNonSemanticShaderDebugInfo = enableNonSemanticShaderDebugInfo;
329            options().emitNonSemanticShaderDebugSource = enableNonSemanticShaderDebugInfo;
330            glslang::GlslangToSpv(*shader.getIntermediate(), spirv_binary, &logger, &options());
331        }
332
333        std::ostringstream disassembly_stream;
334        spv::Disassemble(disassembly_stream, spirv_binary);
335        bool validation_result = !options().validate || logger.getAllMessages().empty();
336        return {{
337                    {shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},
338                },
339                program.getInfoLog(),
340                program.getInfoDebugLog(),
341                validation_result,
342                logger.getAllMessages(),
343                disassembly_stream.str()};
344    }
345
346    // Compiles and links the given source |code| of the given shader
347    // |stage| into the target under the semantics specified via |controls|.
348    // Returns a GlslangResult instance containing all the information generated
349    // during the process. If the target includes SPIR-V, also disassembles
350    // the result and returns disassembly text.
351    GlslangResult compileLinkIoMap(
352            const std::string shaderName, const std::string& code,
353            const std::string& entryPointName, EShMessages controls,
354            int baseSamplerBinding,
355            int baseTextureBinding,
356            int baseImageBinding,
357            int baseUboBinding,
358            int baseSsboBinding,
359            bool autoMapBindings,
360            bool flattenUniformArrays)
361    {
362        const EShLanguage stage = GetShaderStage(GetSuffix(shaderName));
363
364        glslang::TShader shader(stage);
365        shader.setShiftSamplerBinding(baseSamplerBinding);
366        shader.setShiftTextureBinding(baseTextureBinding);
367        shader.setShiftImageBinding(baseImageBinding);
368        shader.setShiftUboBinding(baseUboBinding);
369        shader.setShiftSsboBinding(baseSsboBinding);
370        shader.setAutoMapBindings(autoMapBindings);
371        shader.setAutoMapLocations(true);
372#ifdef ENABLE_HLSL
373        shader.setFlattenUniformArrays(flattenUniformArrays);
374#endif
375
376        bool success = compile(&shader, code, entryPointName, controls);
377
378        glslang::TProgram program;
379        program.addShader(&shader);
380
381        success &= program.link(controls);
382        if (success)
383            program.mapIO();
384
385        spv::SpvBuildLogger logger;
386
387        if (success && (controls & EShMsgSpvRules)) {
388            std::vector<uint32_t> spirv_binary;
389            glslang::GlslangToSpv(*program.getIntermediate(stage),
390                                  spirv_binary, &logger, &options());
391
392            std::ostringstream disassembly_stream;
393            spv::Disassemble(disassembly_stream, spirv_binary);
394            bool validation_result = !options().validate || logger.getAllMessages().empty();
395            return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
396                    program.getInfoLog(), program.getInfoDebugLog(),
397                    validation_result, logger.getAllMessages(), disassembly_stream.str()};
398        } else {
399            return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},},
400                    program.getInfoLog(), program.getInfoDebugLog(), true, "", ""};
401        }
402    }
403
404    void outputResultToStream(std::ostringstream* stream,
405                              const GlslangResult& result,
406                              EShMessages controls)
407    {
408        const auto outputIfNotEmpty = [&stream](const std::string& str) {
409            if (!str.empty()) *stream << str << "\n";
410        };
411
412        for (const auto& shaderResult : result.shaderResults) {
413            *stream << shaderResult.shaderName << "\n";
414            outputIfNotEmpty(shaderResult.output);
415            outputIfNotEmpty(shaderResult.error);
416        }
417        outputIfNotEmpty(result.linkingOutput);
418        outputIfNotEmpty(result.linkingError);
419        if (!result.validationResult) {
420          *stream << "Validation failed\n";
421        }
422
423        if (controls & EShMsgSpvRules) {
424            *stream
425                << (result.spirv.empty()
426                        ? "SPIR-V is not generated for failed compile or link\n"
427                        : result.spirv);
428        }
429    }
430
431    void loadFileCompileAndCheck(const std::string& testDir,
432                                 const std::string& testName,
433                                 Source source,
434                                 Semantics semantics,
435                                 glslang::EShTargetClientVersion clientTargetVersion,
436                                 glslang::EShTargetLanguageVersion targetLanguageVersion,
437                                 Target target,
438                                 bool automap = true,
439                                 const std::string& entryPointName="",
440                                 const std::string& baseDir="/baseResults/",
441                                 const bool enableOptimizer = false,
442                                 const bool enableDebug = false,
443                                 const bool enableNonSemanticShaderDebugInfo = false)
444    {
445#ifndef ENABLE_HLSL
446        if (source == Source::HLSL) {
447            return;
448        }
449#endif
450        const std::string inputFname = testDir + "/" + testName;
451        const std::string expectedOutputFname =
452            testDir + baseDir + testName + ".out";
453        std::string input, expectedOutput;
454
455        tryLoadFile(inputFname, "input", &input);
456        tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
457
458        EShMessages controls = DeriveOptions(source, semantics, target);
459        if (enableOptimizer)
460            controls = static_cast<EShMessages>(controls & ~EShMsgHlslLegalization);
461        if (enableDebug)
462            controls = static_cast<EShMessages>(controls | EShMsgDebugInfo);
463        GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion,
464            targetLanguageVersion, false, EShTexSampTransKeep, enableOptimizer, enableDebug,
465            enableNonSemanticShaderDebugInfo, automap);
466
467        // Generate the hybrid output in the way of glslang.
468        std::ostringstream stream;
469        outputResultToStream(&stream, result, controls);
470
471        checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
472                                    expectedOutputFname, result.spirvWarningsErrors);
473    }
474
475    void loadFileCompileAndCheckWithOptions(const std::string &testDir,
476                                            const std::string &testName,
477                                            Source source,
478                                            Semantics semantics,
479                                            glslang::EShTargetClientVersion clientTargetVersion,
480                                            glslang::EShTargetLanguageVersion targetLanguageVersion,
481                                            Target target, bool automap = true, const std::string &entryPointName = "",
482                                            const std::string &baseDir = "/baseResults/",
483                                            const EShMessages additionalOptions = EShMessages::EShMsgDefault)
484    {
485#ifndef ENABLE_HLSL
486        if (source == Source::HLSL) {
487            return;
488        }
489#endif
490        const std::string inputFname = testDir + "/" + testName;
491        const std::string expectedOutputFname = testDir + baseDir + testName + ".out";
492        std::string input, expectedOutput;
493
494        tryLoadFile(inputFname, "input", &input);
495        tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
496
497        EShMessages controls = DeriveOptions(source, semantics, target);
498        controls = static_cast<EShMessages>(controls | additionalOptions);
499        GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion,
500            targetLanguageVersion, false, EShTexSampTransKeep, false, false, false, automap);
501
502        // Generate the hybrid output in the way of glslang.
503        std::ostringstream stream;
504        outputResultToStream(&stream, result, controls);
505
506        checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname);
507    }
508
509    void loadFileCompileFlattenUniformsAndCheck(const std::string& testDir,
510                                                const std::string& testName,
511                                                Source source,
512                                                Semantics semantics,
513                                                Target target,
514                                                const std::string& entryPointName="")
515    {
516#ifndef ENABLE_HLSL
517        if (source == Source::HLSL) {
518            return;
519        }
520#endif
521        const std::string inputFname = testDir + "/" + testName;
522        const std::string expectedOutputFname =
523            testDir + "/baseResults/" + testName + ".out";
524        std::string input, expectedOutput;
525
526        tryLoadFile(inputFname, "input", &input);
527        tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
528
529        const EShMessages controls = DeriveOptions(source, semantics, target);
530        GlslangResult result = compileAndLink(testName, input, entryPointName, controls,
531                                              glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, true);
532
533        // Generate the hybrid output in the way of glslang.
534        std::ostringstream stream;
535        outputResultToStream(&stream, result, controls);
536
537        checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
538                                    expectedOutputFname, result.spirvWarningsErrors);
539    }
540
541    void loadFileCompileIoMapAndCheck(const std::string& testDir,
542                                      const std::string& testName,
543                                      Source source,
544                                      Semantics semantics,
545                                      Target target,
546                                      const std::string& entryPointName,
547                                      int baseSamplerBinding,
548                                      int baseTextureBinding,
549                                      int baseImageBinding,
550                                      int baseUboBinding,
551                                      int baseSsboBinding,
552                                      bool autoMapBindings,
553                                      bool flattenUniformArrays,
554                                      bool relaxSetBindingLimits)
555    {
556#ifndef ENABLE_HLSL
557        if (source == Source::HLSL) {
558            return;
559        }
560#endif
561        const std::string inputFname = testDir + "/" + testName;
562        const std::string expectedOutputFname =
563            testDir + "/baseResults/" + testName + ".out";
564        std::string input, expectedOutput;
565
566        tryLoadFile(inputFname, "input", &input);
567        tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
568
569        EShMessages controls = DeriveOptions(source, semantics, target);
570        if (relaxSetBindingLimits)
571            controls = static_cast<EShMessages>(controls | EShMsgRelaxSetBindingLimits);
572        GlslangResult result = compileLinkIoMap(testName, input, entryPointName, controls,
573                                                baseSamplerBinding, baseTextureBinding, baseImageBinding,
574                                                baseUboBinding, baseSsboBinding,
575                                                autoMapBindings,
576                                                flattenUniformArrays);
577
578        // Generate the hybrid output in the way of glslang.
579        std::ostringstream stream;
580        outputResultToStream(&stream, result, controls);
581
582        checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
583                                    expectedOutputFname, result.spirvWarningsErrors);
584    }
585
586    // Preprocesses the given |source| code. On success, returns true, the
587    // preprocessed shader, and warning messages. Otherwise, returns false, an
588    // empty string, and error messages.
589    std::tuple<bool, std::string, std::string> preprocess(
590        const std::string& source)
591    {
592        const char* shaderStrings = source.data();
593        const int shaderLengths = static_cast<int>(source.size());
594
595        glslang::TShader shader(EShLangVertex);
596        shader.setStringsWithLengths(&shaderStrings, &shaderLengths, 1);
597        std::string ppShader;
598        glslang::TShader::ForbidIncluder includer;
599        const bool success = shader.preprocess(
600            GetDefaultResources(), defaultVersion, defaultProfile, forceVersionProfile, isForwardCompatible,
601            (EShMessages)(EShMsgOnlyPreprocessor | EShMsgCascadingErrors),
602            &ppShader, includer);
603
604        std::string log = shader.getInfoLog();
605        log += shader.getInfoDebugLog();
606        if (success) {
607            return std::make_tuple(true, ppShader, log);
608        } else {
609            return std::make_tuple(false, "", log);
610        }
611    }
612
613    void loadFilePreprocessAndCheck(const std::string& testDir,
614                                    const std::string& testName)
615    {
616        const std::string inputFname = testDir + "/" + testName;
617        const std::string expectedOutputFname =
618            testDir + "/baseResults/" + testName + ".out";
619        const std::string expectedErrorFname =
620            testDir + "/baseResults/" + testName + ".err";
621        std::string input, expectedOutput, expectedError;
622
623        tryLoadFile(inputFname, "input", &input);
624        tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
625        tryLoadFile(expectedErrorFname, "expected error", &expectedError);
626
627        bool ppOk;
628        std::string output, error;
629        std::tie(ppOk, output, error) = preprocess(input);
630        if (!output.empty()) output += '\n';
631        if (!error.empty()) error += '\n';
632
633        checkEqAndUpdateIfRequested(expectedOutput, output,
634                                    expectedOutputFname);
635        checkEqAndUpdateIfRequested(expectedError, error,
636                                    expectedErrorFname);
637    }
638
639    void loadCompileUpgradeTextureToSampledTextureAndDropSamplersAndCheck(const std::string& testDir,
640                                                                          const std::string& testName,
641                                                                          Source source,
642                                                                          Semantics semantics,
643                                                                          Target target,
644                                                                          const std::string& entryPointName = "")
645    {
646        const std::string inputFname = testDir + "/" + testName;
647        const std::string expectedOutputFname = testDir + "/baseResults/" + testName + ".out";
648        std::string input, expectedOutput;
649
650        tryLoadFile(inputFname, "input", &input);
651        tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
652
653        const EShMessages controls = DeriveOptions(source, semantics, target);
654        GlslangResult result = compileAndLink(testName, input, entryPointName, controls,
655                                              glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, false,
656                                              EShTexSampTransUpgradeTextureRemoveSampler);
657
658        // Generate the hybrid output in the way of glslang.
659        std::ostringstream stream;
660        outputResultToStream(&stream, result, controls);
661
662        checkEqAndUpdateIfRequested(expectedOutput, stream.str(),
663                                    expectedOutputFname, result.spirvWarningsErrors);
664    }
665
666    glslang::SpvOptions& options() { return spirvOptions; }
667
668private:
669    const int defaultVersion;
670    const EProfile defaultProfile;
671    const bool forceVersionProfile;
672    const bool isForwardCompatible;
673    glslang::SpvOptions spirvOptions;
674};
675
676}  // namespace glslangtest
677
678#endif  // GLSLANG_GTESTS_TEST_FIXTURE_H