GlslMapIO.FromFile.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  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. #include <gtest/gtest.h>
  38. #include "TestFixture.h"
  39. #include "glslang/MachineIndependent/localintermediate.h"
  40. #include "glslang/MachineIndependent/iomapper.h"
  41. #include "glslang/MachineIndependent/reflection.h"
  42. namespace glslangtest {
  43. namespace {
  44. struct IoMapData {
  45. std::vector<std::string> fileNames;
  46. Semantics semantics;
  47. };
  48. using GlslMapIOTest = GlslangTest <::testing::TestWithParam<IoMapData>>;
  49. template<class T>
  50. std::string interfaceName(T symbol) {
  51. return symbol.getType()->getBasicType() == glslang::EbtBlock ? std::string(symbol.getType()->getTypeName().c_str()) : symbol.name;
  52. }
  53. bool verifyIOMapping(std::string& linkingError, glslang::TProgram& program) {
  54. bool success = true;
  55. // Verify IO Mapping by generating reflection for each stage individually
  56. // and comparing layout qualifiers on the results
  57. int reflectionOptions = EShReflectionDefault;
  58. //reflectionOptions |= EShReflectionStrictArraySuffix;
  59. //reflectionOptions |= EShReflectionBasicArraySuffix;
  60. reflectionOptions |= EShReflectionIntermediateIO;
  61. reflectionOptions |= EShReflectionSeparateBuffers;
  62. reflectionOptions |= EShReflectionAllBlockVariables;
  63. //reflectionOptions |= EShReflectionUnwrapIOBlocks;
  64. success &= program.buildReflection(reflectionOptions);
  65. // check that the reflection output from the individual stages all makes sense..
  66. std::vector<glslang::TReflection> stageReflections;
  67. for (int s = 0; s < EShLangCount; ++s) {
  68. if (program.getIntermediate((EShLanguage)s)) {
  69. stageReflections.emplace_back((EShReflectionOptions)reflectionOptions, (EShLanguage)s, (EShLanguage)s);
  70. success &= stageReflections.back().addStage((EShLanguage)s, *program.getIntermediate((EShLanguage)s));
  71. }
  72. }
  73. // check that input/output locations match between stages
  74. auto it = stageReflections.begin();
  75. auto nextIt = it + 1;
  76. for (; nextIt != stageReflections.end(); it++, nextIt++) {
  77. int numOut = it->getNumPipeOutputs();
  78. std::map<std::string, const glslang::TObjectReflection*> pipeOut;
  79. for (int i = 0; i < numOut; i++) {
  80. const glslang::TObjectReflection& out = it->getPipeOutput(i);
  81. std::string name = interfaceName(out);
  82. pipeOut[name] = &out;
  83. }
  84. int numIn = nextIt->getNumPipeInputs();
  85. for (int i = 0; i < numIn; i++) {
  86. auto in = nextIt->getPipeInput(i);
  87. std::string name = interfaceName(in);
  88. auto out = pipeOut.find(name);
  89. if (out != pipeOut.end()) {
  90. auto inQualifier = in.getType()->getQualifier();
  91. auto outQualifier = out->second->getType()->getQualifier();
  92. success &= outQualifier.layoutLocation == inQualifier.layoutLocation;
  93. }
  94. else {
  95. if (!in.getType()->isStruct()) {
  96. bool found = false;
  97. for (auto outIt : pipeOut) {
  98. if (outIt.second->getType()->isStruct()) {
  99. unsigned int baseLoc = outIt.second->getType()->getQualifier().hasLocation() ?
  100. outIt.second->getType()->getQualifier().layoutLocation :
  101. std::numeric_limits<unsigned int>::max();
  102. for (size_t j = 0; j < outIt.second->getType()->getStruct()->size(); j++) {
  103. baseLoc = (*outIt.second->getType()->getStruct())[j].type->getQualifier().hasLocation() ?
  104. (*outIt.second->getType()->getStruct())[j].type->getQualifier().layoutLocation : baseLoc;
  105. if (baseLoc != std::numeric_limits<unsigned int>::max()) {
  106. if (baseLoc == in.getType()->getQualifier().layoutLocation) {
  107. found = true;
  108. break;
  109. }
  110. baseLoc += glslang::TIntermediate::computeTypeLocationSize(*(*outIt.second->getType()->getStruct())[j].type, EShLangVertex);
  111. }
  112. }
  113. if (found) {
  114. break;
  115. }
  116. }
  117. }
  118. success &= found;
  119. }
  120. else {
  121. unsigned int baseLoc = in.getType()->getQualifier().hasLocation() ? in.getType()->getQualifier().layoutLocation : -1;
  122. for (size_t j = 0; j < in.getType()->getStruct()->size(); j++) {
  123. baseLoc = (*in.getType()->getStruct())[j].type->getQualifier().hasLocation() ?
  124. (*in.getType()->getStruct())[j].type->getQualifier().layoutLocation : baseLoc;
  125. if (baseLoc != std::numeric_limits<unsigned int>::max()) {
  126. bool isMemberFound = false;
  127. for (auto outIt : pipeOut) {
  128. if (baseLoc == outIt.second->getType()->getQualifier().layoutLocation) {
  129. isMemberFound = true;
  130. break;
  131. }
  132. }
  133. if (!isMemberFound) {
  134. success &= false;
  135. break;
  136. }
  137. baseLoc += glslang::TIntermediate::computeTypeLocationSize(*(*in.getType()->getStruct())[j].type, EShLangVertex);
  138. }
  139. }
  140. }
  141. }
  142. }
  143. }
  144. // compare uniforms in each stage to the program
  145. {
  146. int totalUniforms = program.getNumUniformVariables();
  147. std::map<std::string, const glslang::TObjectReflection*> programUniforms;
  148. for (int i = 0; i < totalUniforms; i++) {
  149. const glslang::TObjectReflection& uniform = program.getUniform(i);
  150. std::string name = interfaceName(uniform);
  151. programUniforms[name] = &uniform;
  152. }
  153. it = stageReflections.begin();
  154. for (; it != stageReflections.end(); it++) {
  155. int numUniform = it->getNumUniforms();
  156. std::map<std::string, glslang::TObjectReflection> uniforms;
  157. for (int i = 0; i < numUniform; i++) {
  158. glslang::TObjectReflection uniform = it->getUniform(i);
  159. std::string name = interfaceName(uniform);
  160. auto programUniform = programUniforms.find(name);
  161. if (programUniform != programUniforms.end()) {
  162. auto stageQualifier = uniform.getType()->getQualifier();
  163. auto programQualifier = programUniform->second->getType()->getQualifier();
  164. success &= stageQualifier.layoutLocation == programQualifier.layoutLocation;
  165. success &= stageQualifier.layoutBinding == programQualifier.layoutBinding;
  166. success &= stageQualifier.layoutSet == programQualifier.layoutSet;
  167. }
  168. else {
  169. success &= false;
  170. }
  171. }
  172. }
  173. }
  174. // compare uniform blocks in each stage to the program table
  175. {
  176. int totalUniforms = program.getNumUniformBlocks();
  177. std::map<std::string, const glslang::TObjectReflection*> programUniforms;
  178. for (int i = 0; i < totalUniforms; i++) {
  179. const glslang::TObjectReflection& uniform = program.getUniformBlock(i);
  180. std::string name = interfaceName(uniform);
  181. programUniforms[name] = &uniform;
  182. }
  183. it = stageReflections.begin();
  184. for (; it != stageReflections.end(); it++) {
  185. int numUniform = it->getNumUniformBlocks();
  186. std::map<std::string, glslang::TObjectReflection> uniforms;
  187. for (int i = 0; i < numUniform; i++) {
  188. glslang::TObjectReflection uniform = it->getUniformBlock(i);
  189. std::string name = interfaceName(uniform);
  190. auto programUniform = programUniforms.find(name);
  191. if (programUniform != programUniforms.end()) {
  192. auto stageQualifier = uniform.getType()->getQualifier();
  193. auto programQualifier = programUniform->second->getType()->getQualifier();
  194. success &= stageQualifier.layoutLocation == programQualifier.layoutLocation;
  195. success &= stageQualifier.layoutBinding == programQualifier.layoutBinding;
  196. success &= stageQualifier.layoutSet == programQualifier.layoutSet;
  197. }
  198. else {
  199. success &= false;
  200. }
  201. }
  202. }
  203. }
  204. if (!success) {
  205. linkingError += "Mismatched cross-stage IO\n";
  206. }
  207. return success;
  208. }
  209. TEST_P(GlslMapIOTest, FromFile)
  210. {
  211. const auto& fileNames = GetParam().fileNames;
  212. Semantics semantics = GetParam().semantics;
  213. const size_t fileCount = fileNames.size();
  214. const EShMessages controls = DeriveOptions(Source::GLSL, semantics, Target::BothASTAndSpv);
  215. GlslangResult result;
  216. // Compile each input shader file.
  217. bool success = true;
  218. std::vector<std::unique_ptr<glslang::TShader>> shaders;
  219. for (size_t i = 0; i < fileCount; ++i) {
  220. std::string contents;
  221. tryLoadFile(GlobalTestSettings.testRoot + "/" + fileNames[i],
  222. "input", &contents);
  223. shaders.emplace_back(
  224. new glslang::TShader(GetShaderStage(GetSuffix(fileNames[i]))));
  225. auto* shader = shaders.back().get();
  226. shader->setAutoMapLocations(true);
  227. shader->setAutoMapBindings(true);
  228. if (controls & EShMsgSpvRules) {
  229. if (controls & EShMsgVulkanRules) {
  230. shader->setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl
  231. : glslang::EShSourceGlsl,
  232. shader->getStage(), glslang::EShClientVulkan, 100);
  233. shader->setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_1);
  234. shader->setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0);
  235. } else {
  236. shader->setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl
  237. : glslang::EShSourceGlsl,
  238. shader->getStage(), glslang::EShClientOpenGL, 100);
  239. shader->setEnvClient(glslang::EShClientOpenGL, glslang::EShTargetOpenGL_450);
  240. shader->setEnvTarget(glslang::EshTargetSpv, glslang::EShTargetSpv_1_0);
  241. }
  242. }
  243. success &= compile(shader, contents, "", controls);
  244. result.shaderResults.push_back(
  245. { fileNames[i], shader->getInfoLog(), shader->getInfoDebugLog() });
  246. }
  247. // Link all of them.
  248. glslang::TProgram program;
  249. for (const auto& shader : shaders) program.addShader(shader.get());
  250. success &= program.link(controls);
  251. result.linkingOutput = program.getInfoLog();
  252. result.linkingError = program.getInfoDebugLog();
  253. glslang::TIoMapResolver *resolver;
  254. for (unsigned stage = 0; stage < EShLangCount; stage++) {
  255. resolver = program.getGlslIoResolver((EShLanguage)stage);
  256. if (resolver)
  257. break;
  258. }
  259. glslang::TIoMapper *ioMapper = glslang::GetGlslIoMapper();
  260. if (success) {
  261. success &= program.mapIO(resolver, ioMapper);
  262. result.linkingOutput = program.getInfoLog();
  263. result.linkingError = program.getInfoDebugLog();
  264. }
  265. delete ioMapper;
  266. delete resolver;
  267. success &= verifyIOMapping(result.linkingError, program);
  268. result.validationResult = success;
  269. if (success && (controls & EShMsgSpvRules)) {
  270. for (int stage = 0; stage < EShLangCount; ++stage) {
  271. if (program.getIntermediate((EShLanguage)stage)) {
  272. spv::SpvBuildLogger logger;
  273. std::vector<uint32_t> spirv_binary;
  274. options().disableOptimizer = false;
  275. glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage),
  276. spirv_binary, &logger, &options());
  277. std::ostringstream disassembly_stream;
  278. spv::Disassemble(disassembly_stream, spirv_binary);
  279. result.spirvWarningsErrors += logger.getAllMessages();
  280. result.spirv += disassembly_stream.str();
  281. result.validationResult &= !options().validate || logger.getAllMessages().empty();
  282. }
  283. }
  284. }
  285. std::ostringstream stream;
  286. outputResultToStream(&stream, result, controls);
  287. // Check with expected results.
  288. const std::string expectedOutputFname =
  289. GlobalTestSettings.testRoot + "/baseResults/" + fileNames.front() + ".out";
  290. std::string expectedOutput;
  291. tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
  292. checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname,
  293. result.spirvWarningsErrors);
  294. }
  295. // clang-format off
  296. INSTANTIATE_TEST_SUITE_P(
  297. Glsl, GlslMapIOTest,
  298. ::testing::ValuesIn(std::vector<IoMapData>({
  299. {{"iomap.crossStage.vert", "iomap.crossStage.frag" }, Semantics::OpenGL},
  300. {{"iomap.crossStage.2.vert", "iomap.crossStage.2.geom", "iomap.crossStage.2.frag" }, Semantics::OpenGL},
  301. {{"iomap.blockOutVariableIn.vert", "iomap.blockOutVariableIn.frag"}, Semantics::OpenGL},
  302. {{"iomap.variableOutBlockIn.vert", "iomap.variableOutBlockIn.frag"}, Semantics::OpenGL},
  303. {{"iomap.blockOutVariableIn.2.vert", "iomap.blockOutVariableIn.geom"}, Semantics::OpenGL},
  304. {{"iomap.variableOutBlockIn.2.vert", "iomap.variableOutBlockIn.geom"}, Semantics::OpenGL},
  305. {{"iomap.mismatchedBufferTypes.vert", "iomap.mismatchedBufferTypes.frag"}, Semantics::OpenGL},
  306. // vulkan semantics
  307. {{"iomap.crossStage.vk.vert", "iomap.crossStage.vk.geom", "iomap.crossStage.vk.frag" }, Semantics::Vulkan},
  308. }))
  309. );
  310. // clang-format on
  311. } // anonymous namespace
  312. } // namespace glslangtest