yum-mirror/glslang
git clone https://git.yummers.dev/yum-mirror/glslang
e61c8f2e
main
1// 2// Copyright (C) 2016-2017 Google, Inc. 3// Copyright (C) 2020 The Khronos Group Inc. 4// 5// All rights reserved. 6// 7// Redistribution and use in source and binary forms, with or without 8// modification, are permitted provided that the following conditions 9// are met: 10// 11// Redistributions of source code must retain the above copyright 12// notice, this list of conditions and the following disclaimer. 13// 14// Redistributions in binary form must reproduce the above 15// copyright notice, this list of conditions and the following 16// disclaimer in the documentation and/or other materials provided 17// with the distribution. 18// 19// Neither the name of 3Dlabs Inc. Ltd. nor the names of its 20// contributors may be used to endorse or promote products derived 21// from this software without specific prior written permission. 22// 23// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34// POSSIBILITY OF SUCH DAMAGE. 35// 36#include <algorithm> 37 38#include <gtest/gtest.h> 39 40#include "TestFixture.h" 41 42#include "glslang/MachineIndependent/iomapper.h" 43#include "glslang/MachineIndependent/reflection.h" 44 45namespace glslangtest { 46namespace { 47 48struct vkRelaxedData { 49 std::vector < std::string > fileNames ; 50 std::vector < std::vector < std::string >> resourceSetBindings ; 51}; 52 53using VulkanRelaxedTest = GlslangTest < ::testing::TestWithParam < vkRelaxedData >> ; 54 55template < class T > 56std::string interfaceName (T symbol ) { 57return symbol .getType ()-> getBasicType ()== glslang::EbtBlock ? std::string (symbol .getType ()-> getTypeName ().c_str ()) :symbol .name ; 58} 59 60bool verifyIOMapping (std::string & linkingError , glslang::TProgram & program ) { 61bool success = true; 62 63// Verify IO Mapping by generating reflection for each stage individually 64// and comparing layout qualifiers on the results 65 66 67int reflectionOptions = EShReflectionDefault ; 68//reflectionOptions |= EShReflectionStrictArraySuffix; 69//reflectionOptions |= EShReflectionBasicArraySuffix; 70reflectionOptions |=EShReflectionIntermediateIO ; 71reflectionOptions |=EShReflectionSeparateBuffers ; 72reflectionOptions |=EShReflectionAllBlockVariables ; 73//reflectionOptions |= EShReflectionUnwrapIOBlocks; 74 75success &=program .buildReflection (reflectionOptions ); 76 77// check that the reflection output from the individual stages all makes sense.. 78 std::vector < glslang::TReflection > stageReflections ; 79for (int s = 0 ;s < EShLangCount ;++ s ) { 80if (program .getIntermediate ((EShLanguage )s )) { 81stageReflections .emplace_back ((EShReflectionOptions )reflectionOptions , (EShLanguage )s , (EShLanguage )s ); 82success &=stageReflections .back ().addStage ((EShLanguage )s ,* program .getIntermediate ((EShLanguage )s )); 83 } 84 } 85 86// check that input/output locations match between stages 87auto it = stageReflections .begin (); 88auto nextIt = it + 1 ; 89for (;nextIt != stageReflections .end ();it ++ ,nextIt ++ ) { 90int numOut = it -> getNumPipeOutputs (); 91 std::map < std::string ,const glslang::TObjectReflection *> pipeOut ; 92 93for (int i = 0 ;i < numOut ;i ++ ) { 94const glslang::TObjectReflection & out = it -> getPipeOutput (i ); 95 std::string name = interfaceName (out ); 96pipeOut [name ]= & out ; 97 } 98 99int numIn = nextIt -> getNumPipeInputs (); 100for (int i = 0 ;i < numIn ;i ++ ) { 101auto in = nextIt -> getPipeInput (i ); 102 std::string name = interfaceName (in ); 103auto out = pipeOut .find (name ); 104 105if (out != pipeOut .end ()) { 106auto inQualifier = in .getType ()-> getQualifier (); 107auto outQualifier = out -> second -> getType ()-> getQualifier (); 108success &=outQualifier .layoutLocation == inQualifier .layoutLocation ; 109// These are not part of a matched interface. Other cases still need to be added. 110 }else if (name != "gl_FrontFacing" && name != "gl_FragCoord" ) { 111success &= false; 112 } 113 } 114 } 115 116// compare uniforms in each stage to the program 117 { 118int totalUniforms = program .getNumUniformVariables (); 119 std::map < std::string ,const glslang::TObjectReflection *> programUniforms ; 120for (int i = 0 ;i < totalUniforms ;i ++ ) { 121const glslang::TObjectReflection & uniform = program .getUniform (i ); 122 std::string name = interfaceName (uniform ); 123programUniforms [name ]= & uniform ; 124 } 125it = stageReflections .begin (); 126for (;it != stageReflections .end ();it ++ ) { 127int numUniform = it -> getNumUniforms (); 128 std::map < std::string , glslang::TObjectReflection > uniforms ; 129 130for (int i = 0 ;i < numUniform ;i ++ ) { 131 glslang::TObjectReflection uniform = it -> getUniform (i ); 132 std::string name = interfaceName (uniform ); 133auto programUniform = programUniforms .find (name ); 134 135if (programUniform != programUniforms .end ()) { 136auto stageQualifier = uniform .getType ()-> getQualifier (); 137auto programQualifier = programUniform -> second -> getType ()-> getQualifier (); 138 139success &=stageQualifier .layoutLocation == programQualifier .layoutLocation ; 140success &=stageQualifier .layoutBinding == programQualifier .layoutBinding ; 141success &=stageQualifier .layoutSet == programQualifier .layoutSet ; 142 } 143else { 144success &= false; 145 } 146 } 147 } 148 } 149 150// compare uniform blocks in each stage to the program table 151 { 152int totalUniforms = program .getNumUniformBlocks (); 153 std::map < std::string ,const glslang::TObjectReflection *> programUniforms ; 154for (int i = 0 ;i < totalUniforms ;i ++ ) { 155const glslang::TObjectReflection & uniform = program .getUniformBlock (i ); 156 std::string name = interfaceName (uniform ); 157programUniforms [name ]= & uniform ; 158 } 159it = stageReflections .begin (); 160for (;it != stageReflections .end ();it ++ ) { 161int numUniform = it -> getNumUniformBlocks (); 162 std::map < std::string , glslang::TObjectReflection > uniforms ; 163 164for (int i = 0 ;i < numUniform ;i ++ ) { 165 glslang::TObjectReflection uniform = it -> getUniformBlock (i ); 166 std::string name = interfaceName (uniform ); 167auto programUniform = programUniforms .find (name ); 168 169if (programUniform != programUniforms .end ()) { 170auto stageQualifier = uniform .getType ()-> getQualifier (); 171auto programQualifier = programUniform -> second -> getType ()-> getQualifier (); 172 173success &=stageQualifier .layoutLocation == programQualifier .layoutLocation ; 174success &=stageQualifier .layoutBinding == programQualifier .layoutBinding ; 175success &=stageQualifier .layoutSet == programQualifier .layoutSet ; 176 } 177else { 178success &= false; 179 } 180 } 181 } 182 } 183 184if (!success ) { 185linkingError += "Mismatched cross-stage IO\n" ; 186 } 187 188return success ; 189} 190 191TEST_P (VulkanRelaxedTest ,FromFile ) 192{ 193const auto & fileNames = GetParam ().fileNames ; 194const auto & resourceSetBindings = GetParam ().resourceSetBindings ; 195Semantics semantics = Semantics ::Vulkan ; 196const size_t fileCount = fileNames .size (); 197const EShMessages controls = DeriveOptions (Source ::GLSL ,semantics ,Target ::BothASTAndSpv ); 198GlslangResult result ; 199 200// Compile each input shader file. 201bool success = true; 202 std::vector < std::unique_ptr < glslang::TShader >> shaders ; 203for (size_t i = 0 ;i < fileCount ;++ i ) { 204 std::string contents ; 205tryLoadFile (GlobalTestSettings .testRoot + "/" + fileNames [i ], 206"input" ,& contents ); 207shaders .emplace_back ( 208new glslang::TShader (GetShaderStage (GetSuffix (fileNames [i ])))); 209auto * shader = shaders .back ().get (); 210 211shader -> setAutoMapLocations (true); 212shader -> setAutoMapBindings (true); 213 214shader -> setEnvInput (glslang::EShSourceGlsl ,shader -> getStage (), glslang::EShClientVulkan ,100 ); 215shader -> setEnvClient (glslang::EShClientVulkan , glslang::EShTargetVulkan_1_1 ); 216shader -> setEnvTarget (glslang::EShTargetSpv , glslang::EShTargetSpv_1_0 ); 217 218// Use vulkan relaxed option 219shader -> setEnvInputVulkanRulesRelaxed (); 220 221success &=compile (shader ,contents ,"" ,controls ); 222 223result .shaderResults .push_back ( 224 {fileNames [i ],shader -> getInfoLog (),shader -> getInfoDebugLog () }); 225 } 226 227// Link all of them. 228 glslang::TProgram program ; 229for (const auto & shader :shaders )program .addShader (shader .get ()); 230success &=program .link (controls ); 231result .linkingOutput = program .getInfoLog (); 232result .linkingError = program .getInfoDebugLog (); 233 234if (!resourceSetBindings .empty ()) { 235assert (resourceSetBindings .size ()== fileNames .size ()); 236for (size_t i = 0 ;i < shaders .size ();i ++ ) 237shaders [i ]-> setResourceSetBinding (resourceSetBindings [i ]); 238 } 239 240 glslang::TIoMapResolver * resolver ; 241for (unsigned stage = 0 ;stage < EShLangCount ;stage ++ ) { 242resolver = program .getGlslIoResolver ((EShLanguage )stage ); 243if (resolver ) 244break ; 245 } 246 glslang::TIoMapper * ioMapper = glslang::GetGlslIoMapper (); 247 248if (success ) { 249success &=program .mapIO (resolver ,ioMapper ); 250result .linkingOutput = program .getInfoLog (); 251result .linkingError = program .getInfoDebugLog (); 252 } 253delete ioMapper ; 254delete resolver ; 255 256success &=verifyIOMapping (result .linkingError ,program ); 257result .validationResult = success ; 258 259if (success && (controls & EShMsgSpvRules )) { 260for (int stage = 0 ;stage < EShLangCount ;++ stage ) { 261if (program .getIntermediate ((EShLanguage )stage )) { 262 spv::SpvBuildLogger logger ; 263 std::vector < uint32_t > spirv_binary ; 264options ().disableOptimizer = false; 265 glslang::GlslangToSpv (* program .getIntermediate ((EShLanguage )stage ), 266spirv_binary ,& logger ,& options ()); 267 268 std::ostringstream disassembly_stream ; 269 spv::Disassemble (disassembly_stream ,spirv_binary ); 270result .spirvWarningsErrors += logger .getAllMessages (); 271result .spirv += disassembly_stream .str (); 272result .validationResult &= !options ().validate || logger .getAllMessages ().empty (); 273 } 274 } 275 } 276 277 std::ostringstream stream ; 278outputResultToStream (& stream ,result ,controls ); 279 280// Check with expected results. 281const std::string expectedOutputFname = 282GlobalTestSettings .testRoot + "/baseResults/" + fileNames .front ()+ ".out" ; 283 std::string expectedOutput ; 284tryLoadFile (expectedOutputFname ,"expected output" ,& expectedOutput ); 285 286checkEqAndUpdateIfRequested (expectedOutput ,stream .str (),expectedOutputFname , 287result .spirvWarningsErrors ); 288} 289 290// clang-format off 291INSTANTIATE_TEST_SUITE_P ( 292Glsl ,VulkanRelaxedTest , 293 ::testing::ValuesIn (std::vector < vkRelaxedData > ({ 294 {{"vk.relaxed.frag" }, {}}, 295 {{"vk.relaxed.link1.frag" ,"vk.relaxed.link2.frag" }, {}}, 296 {{"vk.relaxed.stagelink.0.0.vert" ,"vk.relaxed.stagelink.0.1.vert" ,"vk.relaxed.stagelink.0.2.vert" ,"vk.relaxed.stagelink.0.0.frag" ,"vk.relaxed.stagelink.0.1.frag" ,"vk.relaxed.stagelink.0.2.frag" }, {}}, 297 {{"vk.relaxed.stagelink.vert" ,"vk.relaxed.stagelink.frag" }, {}}, 298 {{"vk.relaxed.errorcheck.vert" ,"vk.relaxed.errorcheck.frag" }, {}}, 299 {{"vk.relaxed.changeSet.vert" ,"vk.relaxed.changeSet.frag" }, { {"0" }, {"1" } } }, 300 {{"vk.relaxed.syntaxerror.vert" ,"vk.relaxed.syntaxerror.frag" }, {}}, 301 {{"vk.relaxed.buffer_reference.frag" }, {}}, 302 })) 303); 304// clang-format on 305 306}// anonymous namespace 307}// namespace glslangtest