123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- //
- // Copyright (C) 2016-2017 Google, Inc.
- // Copyright (C) 2020 The Khronos Group Inc.
- //
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions
- // are met:
- //
- // Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- //
- // Redistributions in binary form must reproduce the above
- // copyright notice, this list of conditions and the following
- // disclaimer in the documentation and/or other materials provided
- // with the distribution.
- //
- // Neither the name of 3Dlabs Inc. Ltd. nor the names of its
- // contributors may be used to endorse or promote products derived
- // from this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- // POSSIBILITY OF SUCH DAMAGE.
- //
- #include <algorithm>
- #include <gtest/gtest.h>
- #include "TestFixture.h"
- #include "glslang/MachineIndependent/localintermediate.h"
- #include "glslang/MachineIndependent/iomapper.h"
- #include "glslang/MachineIndependent/reflection.h"
- namespace glslangtest {
- namespace {
- struct IoMapData {
- std::vector<std::string> fileNames;
- Semantics semantics;
- };
- using GlslMapIOTest = GlslangTest <::testing::TestWithParam<IoMapData>>;
- template<class T>
- std::string interfaceName(T symbol) {
- return symbol.getType()->getBasicType() == glslang::EbtBlock ? std::string(symbol.getType()->getTypeName().c_str()) : symbol.name;
- }
- bool verifyIOMapping(std::string& linkingError, glslang::TProgram& program) {
- bool success = true;
- // Verify IO Mapping by generating reflection for each stage individually
- // and comparing layout qualifiers on the results
- int reflectionOptions = EShReflectionDefault;
- //reflectionOptions |= EShReflectionStrictArraySuffix;
- //reflectionOptions |= EShReflectionBasicArraySuffix;
- reflectionOptions |= EShReflectionIntermediateIO;
- reflectionOptions |= EShReflectionSeparateBuffers;
- reflectionOptions |= EShReflectionAllBlockVariables;
- //reflectionOptions |= EShReflectionUnwrapIOBlocks;
- success &= program.buildReflection(reflectionOptions);
- // check that the reflection output from the individual stages all makes sense..
- std::vector<glslang::TReflection> stageReflections;
- for (int s = 0; s < EShLangCount; ++s) {
- if (program.getIntermediate((EShLanguage)s)) {
- stageReflections.emplace_back((EShReflectionOptions)reflectionOptions, (EShLanguage)s, (EShLanguage)s);
- success &= stageReflections.back().addStage((EShLanguage)s, *program.getIntermediate((EShLanguage)s));
- }
- }
- // check that input/output locations match between stages
- auto it = stageReflections.begin();
- auto nextIt = it + 1;
- for (; nextIt != stageReflections.end(); it++, nextIt++) {
- int numOut = it->getNumPipeOutputs();
- std::map<std::string, const glslang::TObjectReflection*> pipeOut;
- for (int i = 0; i < numOut; i++) {
- const glslang::TObjectReflection& out = it->getPipeOutput(i);
- std::string name = interfaceName(out);
- pipeOut[name] = &out;
- }
- int numIn = nextIt->getNumPipeInputs();
- for (int i = 0; i < numIn; i++) {
- auto in = nextIt->getPipeInput(i);
- std::string name = interfaceName(in);
- auto out = pipeOut.find(name);
- if (out != pipeOut.end()) {
- auto inQualifier = in.getType()->getQualifier();
- auto outQualifier = out->second->getType()->getQualifier();
- success &= outQualifier.layoutLocation == inQualifier.layoutLocation;
- }
- else {
- if (!in.getType()->isStruct()) {
- bool found = false;
- for (auto outIt : pipeOut) {
- if (outIt.second->getType()->isStruct()) {
- unsigned int baseLoc = outIt.second->getType()->getQualifier().hasLocation() ?
- outIt.second->getType()->getQualifier().layoutLocation :
- std::numeric_limits<unsigned int>::max();
- for (size_t j = 0; j < outIt.second->getType()->getStruct()->size(); j++) {
- baseLoc = (*outIt.second->getType()->getStruct())[j].type->getQualifier().hasLocation() ?
- (*outIt.second->getType()->getStruct())[j].type->getQualifier().layoutLocation : baseLoc;
- if (baseLoc != std::numeric_limits<unsigned int>::max()) {
- if (baseLoc == in.getType()->getQualifier().layoutLocation) {
- found = true;
- break;
- }
- baseLoc += glslang::TIntermediate::computeTypeLocationSize(*(*outIt.second->getType()->getStruct())[j].type, EShLangVertex);
- }
- }
- if (found) {
- break;
- }
- }
- }
- success &= found;
- }
- else {
- unsigned int baseLoc = in.getType()->getQualifier().hasLocation() ? in.getType()->getQualifier().layoutLocation : -1;
- for (size_t j = 0; j < in.getType()->getStruct()->size(); j++) {
- baseLoc = (*in.getType()->getStruct())[j].type->getQualifier().hasLocation() ?
- (*in.getType()->getStruct())[j].type->getQualifier().layoutLocation : baseLoc;
- if (baseLoc != std::numeric_limits<unsigned int>::max()) {
- bool isMemberFound = false;
- for (auto outIt : pipeOut) {
- if (baseLoc == outIt.second->getType()->getQualifier().layoutLocation) {
- isMemberFound = true;
- break;
- }
- }
- if (!isMemberFound) {
- success &= false;
- break;
- }
- baseLoc += glslang::TIntermediate::computeTypeLocationSize(*(*in.getType()->getStruct())[j].type, EShLangVertex);
- }
- }
- }
- }
- }
- }
- // compare uniforms in each stage to the program
- {
- int totalUniforms = program.getNumUniformVariables();
- std::map<std::string, const glslang::TObjectReflection*> programUniforms;
- for (int i = 0; i < totalUniforms; i++) {
- const glslang::TObjectReflection& uniform = program.getUniform(i);
- std::string name = interfaceName(uniform);
- programUniforms[name] = &uniform;
- }
- it = stageReflections.begin();
- for (; it != stageReflections.end(); it++) {
- int numUniform = it->getNumUniforms();
- std::map<std::string, glslang::TObjectReflection> uniforms;
- for (int i = 0; i < numUniform; i++) {
- glslang::TObjectReflection uniform = it->getUniform(i);
- std::string name = interfaceName(uniform);
- auto programUniform = programUniforms.find(name);
- if (programUniform != programUniforms.end()) {
- auto stageQualifier = uniform.getType()->getQualifier();
- auto programQualifier = programUniform->second->getType()->getQualifier();
- success &= stageQualifier.layoutLocation == programQualifier.layoutLocation;
- success &= stageQualifier.layoutBinding == programQualifier.layoutBinding;
- success &= stageQualifier.layoutSet == programQualifier.layoutSet;
- }
- else {
- success &= false;
- }
- }
- }
- }
- // compare uniform blocks in each stage to the program table
- {
- int totalUniforms = program.getNumUniformBlocks();
- std::map<std::string, const glslang::TObjectReflection*> programUniforms;
- for (int i = 0; i < totalUniforms; i++) {
- const glslang::TObjectReflection& uniform = program.getUniformBlock(i);
- std::string name = interfaceName(uniform);
- programUniforms[name] = &uniform;
- }
- it = stageReflections.begin();
- for (; it != stageReflections.end(); it++) {
- int numUniform = it->getNumUniformBlocks();
- std::map<std::string, glslang::TObjectReflection> uniforms;
- for (int i = 0; i < numUniform; i++) {
- glslang::TObjectReflection uniform = it->getUniformBlock(i);
- std::string name = interfaceName(uniform);
- auto programUniform = programUniforms.find(name);
- if (programUniform != programUniforms.end()) {
- auto stageQualifier = uniform.getType()->getQualifier();
- auto programQualifier = programUniform->second->getType()->getQualifier();
- success &= stageQualifier.layoutLocation == programQualifier.layoutLocation;
- success &= stageQualifier.layoutBinding == programQualifier.layoutBinding;
- success &= stageQualifier.layoutSet == programQualifier.layoutSet;
- }
- else {
- success &= false;
- }
- }
- }
- }
- if (!success) {
- linkingError += "Mismatched cross-stage IO\n";
- }
- return success;
- }
- TEST_P(GlslMapIOTest, FromFile)
- {
- const auto& fileNames = GetParam().fileNames;
- Semantics semantics = GetParam().semantics;
- const size_t fileCount = fileNames.size();
- const EShMessages controls = DeriveOptions(Source::GLSL, semantics, Target::BothASTAndSpv);
- GlslangResult result;
- // Compile each input shader file.
- bool success = true;
- std::vector<std::unique_ptr<glslang::TShader>> shaders;
- for (size_t i = 0; i < fileCount; ++i) {
- std::string contents;
- tryLoadFile(GlobalTestSettings.testRoot + "/" + fileNames[i],
- "input", &contents);
- shaders.emplace_back(
- new glslang::TShader(GetShaderStage(GetSuffix(fileNames[i]))));
- auto* shader = shaders.back().get();
-
- shader->setAutoMapLocations(true);
- shader->setAutoMapBindings(true);
-
- if (controls & EShMsgSpvRules) {
- if (controls & EShMsgVulkanRules) {
- shader->setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl
- : glslang::EShSourceGlsl,
- shader->getStage(), glslang::EShClientVulkan, 100);
- shader->setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_1);
- shader->setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0);
- } else {
- shader->setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl
- : glslang::EShSourceGlsl,
- shader->getStage(), glslang::EShClientOpenGL, 100);
- shader->setEnvClient(glslang::EShClientOpenGL, glslang::EShTargetOpenGL_450);
- shader->setEnvTarget(glslang::EshTargetSpv, glslang::EShTargetSpv_1_0);
- }
- }
- success &= compile(shader, contents, "", controls);
-
- result.shaderResults.push_back(
- { fileNames[i], shader->getInfoLog(), shader->getInfoDebugLog() });
- }
- // Link all of them.
- glslang::TProgram program;
- for (const auto& shader : shaders) program.addShader(shader.get());
- success &= program.link(controls);
- result.linkingOutput = program.getInfoLog();
- result.linkingError = program.getInfoDebugLog();
- glslang::TIoMapResolver *resolver;
- for (unsigned stage = 0; stage < EShLangCount; stage++) {
- resolver = program.getGlslIoResolver((EShLanguage)stage);
- if (resolver)
- break;
- }
- glslang::TIoMapper *ioMapper = glslang::GetGlslIoMapper();
- if (success) {
- success &= program.mapIO(resolver, ioMapper);
- result.linkingOutput = program.getInfoLog();
- result.linkingError = program.getInfoDebugLog();
- }
- delete ioMapper;
- delete resolver;
- success &= verifyIOMapping(result.linkingError, program);
- result.validationResult = success;
- if (success && (controls & EShMsgSpvRules)) {
- for (int stage = 0; stage < EShLangCount; ++stage) {
- if (program.getIntermediate((EShLanguage)stage)) {
- spv::SpvBuildLogger logger;
- std::vector<uint32_t> spirv_binary;
- options().disableOptimizer = false;
- glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage),
- spirv_binary, &logger, &options());
- std::ostringstream disassembly_stream;
- spv::Disassemble(disassembly_stream, spirv_binary);
- result.spirvWarningsErrors += logger.getAllMessages();
- result.spirv += disassembly_stream.str();
- result.validationResult &= !options().validate || logger.getAllMessages().empty();
- }
- }
- }
- std::ostringstream stream;
- outputResultToStream(&stream, result, controls);
- // Check with expected results.
- const std::string expectedOutputFname =
- GlobalTestSettings.testRoot + "/baseResults/" + fileNames.front() + ".out";
- std::string expectedOutput;
- tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
- checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname,
- result.spirvWarningsErrors);
- }
- // clang-format off
- INSTANTIATE_TEST_SUITE_P(
- Glsl, GlslMapIOTest,
- ::testing::ValuesIn(std::vector<IoMapData>({
- {{"iomap.crossStage.vert", "iomap.crossStage.frag" }, Semantics::OpenGL},
- {{"iomap.crossStage.2.vert", "iomap.crossStage.2.geom", "iomap.crossStage.2.frag" }, Semantics::OpenGL},
- {{"iomap.blockOutVariableIn.vert", "iomap.blockOutVariableIn.frag"}, Semantics::OpenGL},
- {{"iomap.variableOutBlockIn.vert", "iomap.variableOutBlockIn.frag"}, Semantics::OpenGL},
- {{"iomap.blockOutVariableIn.2.vert", "iomap.blockOutVariableIn.geom"}, Semantics::OpenGL},
- {{"iomap.variableOutBlockIn.2.vert", "iomap.variableOutBlockIn.geom"}, Semantics::OpenGL},
- {{"iomap.mismatchedBufferTypes.vert", "iomap.mismatchedBufferTypes.frag"}, Semantics::OpenGL},
- // vulkan semantics
- {{"iomap.crossStage.vk.vert", "iomap.crossStage.vk.geom", "iomap.crossStage.vk.frag" }, Semantics::Vulkan},
- }))
- );
- // clang-format on
- } // anonymous namespace
- } // namespace glslangtest
|