1 /*-------------------------------------------------------------------------
2 * OpenGL Conformance Test Suite
3 * -----------------------------
4 *
5 * Copyright (c) 2017-2019 The Khronos Group Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 */ /*!
20 * \file glcSpirvUtils.cpp
21 * \brief Utility functions for using Glslang and Spirv-tools to work with
22 * SPIR-V shaders.
23 */ /*-------------------------------------------------------------------*/
24
25 #include "glcSpirvUtils.hpp"
26 #include "deArrayUtil.hpp"
27 #include "deSingleton.h"
28 #include "deStringUtil.hpp"
29 #include "gluContextInfo.hpp"
30 #include "tcuTestLog.hpp"
31
32 #include "SPIRV/GlslangToSpv.h"
33 #include "SPIRV/disassemble.h"
34 #include "SPIRV/doc.h"
35 #include "glslang/MachineIndependent/localintermediate.h"
36 #include "glslang/Public/ShaderLang.h"
37
38 #include "spirv-tools/libspirv.hpp"
39 #include "spirv-tools/optimizer.hpp"
40
41 using namespace glu;
42
43 namespace glc
44 {
45
46 namespace spirvUtils
47 {
48
checkGlSpirvSupported(deqp::Context & m_context)49 void checkGlSpirvSupported(deqp::Context &m_context)
50 {
51 bool is_at_least_gl_46 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 6)));
52 bool is_arb_gl_spirv = m_context.getContextInfo().isExtensionSupported("GL_ARB_gl_spirv");
53
54 if ((!is_at_least_gl_46) && (!is_arb_gl_spirv))
55 TCU_THROW(NotSupportedError, "GL 4.6 or GL_ARB_gl_spirv is not supported");
56 }
57
getGlslangStage(glu::ShaderType type)58 EShLanguage getGlslangStage(glu::ShaderType type)
59 {
60 static const EShLanguage stageMap[] = {EShLangVertex, EShLangFragment, EShLangGeometry, EShLangTessControl,
61 EShLangTessEvaluation, EShLangCompute, EShLangRayGen, EShLangAnyHit,
62 EShLangClosestHit, EShLangMiss, EShLangIntersect, EShLangCallable,
63 EShLangTaskNV, EShLangMeshNV};
64
65 return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(stageMap, type);
66 }
67
68 static volatile deSingletonState s_glslangInitState = DE_SINGLETON_STATE_NOT_INITIALIZED;
69
initGlslang(void *)70 void initGlslang(void *)
71 {
72 // Main compiler
73 glslang::InitializeProcess();
74
75 // SPIR-V disassembly
76 spv::Parameterize();
77 }
78
prepareGlslang(void)79 void prepareGlslang(void)
80 {
81 deInitSingleton(&s_glslangInitState, initGlslang, DE_NULL);
82 }
83
getDefaultLimits(TLimits * limits)84 void getDefaultLimits(TLimits *limits)
85 {
86 limits->nonInductiveForLoops = true;
87 limits->whileLoops = true;
88 limits->doWhileLoops = true;
89 limits->generalUniformIndexing = true;
90 limits->generalAttributeMatrixVectorIndexing = true;
91 limits->generalVaryingIndexing = true;
92 limits->generalSamplerIndexing = true;
93 limits->generalVariableIndexing = true;
94 limits->generalConstantMatrixVectorIndexing = true;
95 }
96
getDefaultBuiltInResources(TBuiltInResource * builtin)97 void getDefaultBuiltInResources(TBuiltInResource *builtin)
98 {
99 getDefaultLimits(&builtin->limits);
100
101 builtin->maxLights = 32;
102 builtin->maxClipPlanes = 6;
103 builtin->maxTextureUnits = 32;
104 builtin->maxTextureCoords = 32;
105 builtin->maxVertexAttribs = 64;
106 builtin->maxVertexUniformComponents = 4096;
107 builtin->maxVaryingFloats = 64;
108 builtin->maxVertexTextureImageUnits = 32;
109 builtin->maxCombinedTextureImageUnits = 80;
110 builtin->maxTextureImageUnits = 32;
111 builtin->maxFragmentUniformComponents = 4096;
112 builtin->maxDrawBuffers = 32;
113 builtin->maxVertexUniformVectors = 128;
114 builtin->maxVaryingVectors = 8;
115 builtin->maxFragmentUniformVectors = 16;
116 builtin->maxVertexOutputVectors = 16;
117 builtin->maxFragmentInputVectors = 15;
118 builtin->minProgramTexelOffset = -8;
119 builtin->maxProgramTexelOffset = 7;
120 builtin->maxClipDistances = 8;
121 builtin->maxComputeWorkGroupCountX = 65535;
122 builtin->maxComputeWorkGroupCountY = 65535;
123 builtin->maxComputeWorkGroupCountZ = 65535;
124 builtin->maxComputeWorkGroupSizeX = 1024;
125 builtin->maxComputeWorkGroupSizeY = 1024;
126 builtin->maxComputeWorkGroupSizeZ = 64;
127 builtin->maxComputeUniformComponents = 1024;
128 builtin->maxComputeTextureImageUnits = 16;
129 builtin->maxComputeImageUniforms = 8;
130 builtin->maxComputeAtomicCounters = 8;
131 builtin->maxComputeAtomicCounterBuffers = 1;
132 builtin->maxVaryingComponents = 60;
133 builtin->maxVertexOutputComponents = 64;
134 builtin->maxGeometryInputComponents = 64;
135 builtin->maxGeometryOutputComponents = 128;
136 builtin->maxFragmentInputComponents = 128;
137 builtin->maxImageUnits = 8;
138 builtin->maxCombinedImageUnitsAndFragmentOutputs = 8;
139 builtin->maxCombinedShaderOutputResources = 8;
140 builtin->maxImageSamples = 0;
141 builtin->maxVertexImageUniforms = 0;
142 builtin->maxTessControlImageUniforms = 0;
143 builtin->maxTessEvaluationImageUniforms = 0;
144 builtin->maxGeometryImageUniforms = 0;
145 builtin->maxFragmentImageUniforms = 8;
146 builtin->maxCombinedImageUniforms = 8;
147 builtin->maxGeometryTextureImageUnits = 16;
148 builtin->maxGeometryOutputVertices = 256;
149 builtin->maxGeometryTotalOutputComponents = 1024;
150 builtin->maxGeometryUniformComponents = 1024;
151 builtin->maxGeometryVaryingComponents = 64;
152 builtin->maxTessControlInputComponents = 128;
153 builtin->maxTessControlOutputComponents = 128;
154 builtin->maxTessControlTextureImageUnits = 16;
155 builtin->maxTessControlUniformComponents = 1024;
156 builtin->maxTessControlTotalOutputComponents = 4096;
157 builtin->maxTessEvaluationInputComponents = 128;
158 builtin->maxTessEvaluationOutputComponents = 128;
159 builtin->maxTessEvaluationTextureImageUnits = 16;
160 builtin->maxTessEvaluationUniformComponents = 1024;
161 builtin->maxTessPatchComponents = 120;
162 builtin->maxPatchVertices = 32;
163 builtin->maxTessGenLevel = 64;
164 builtin->maxViewports = 16;
165 builtin->maxVertexAtomicCounters = 0;
166 builtin->maxTessControlAtomicCounters = 0;
167 builtin->maxTessEvaluationAtomicCounters = 0;
168 builtin->maxGeometryAtomicCounters = 0;
169 builtin->maxFragmentAtomicCounters = 8;
170 builtin->maxCombinedAtomicCounters = 8;
171 builtin->maxAtomicCounterBindings = 1;
172 builtin->maxVertexAtomicCounterBuffers = 0;
173 builtin->maxTessControlAtomicCounterBuffers = 0;
174 builtin->maxTessEvaluationAtomicCounterBuffers = 0;
175 builtin->maxGeometryAtomicCounterBuffers = 0;
176 builtin->maxFragmentAtomicCounterBuffers = 1;
177 builtin->maxCombinedAtomicCounterBuffers = 1;
178 builtin->maxAtomicCounterBufferSize = 16384;
179 builtin->maxTransformFeedbackBuffers = 4;
180 builtin->maxTransformFeedbackInterleavedComponents = 64;
181 builtin->maxCullDistances = 8;
182 builtin->maxCombinedClipAndCullDistances = 8;
183 builtin->maxSamples = 4;
184 builtin->maxMeshOutputVerticesNV = 256;
185 builtin->maxMeshOutputPrimitivesNV = 256;
186 builtin->maxMeshWorkGroupSizeX_NV = 32;
187 builtin->maxMeshWorkGroupSizeY_NV = 1;
188 builtin->maxMeshWorkGroupSizeZ_NV = 1;
189 builtin->maxTaskWorkGroupSizeX_NV = 32;
190 builtin->maxTaskWorkGroupSizeY_NV = 1;
191 builtin->maxTaskWorkGroupSizeZ_NV = 1;
192 builtin->maxMeshViewCountNV = 4;
193 builtin->maxDualSourceDrawBuffersEXT = 1;
194 };
195
getSpirvTargetVersion(SpirvVersion version)196 glslang::EShTargetLanguageVersion getSpirvTargetVersion(SpirvVersion version)
197 {
198 switch (version)
199 {
200 default:
201 DE_FATAL("unhandled SPIRV target version");
202 // fall-through
203 case SPIRV_VERSION_1_0:
204 return glslang::EShTargetSpv_1_0;
205 case SPIRV_VERSION_1_1:
206 return glslang::EShTargetSpv_1_1;
207 case SPIRV_VERSION_1_2:
208 return glslang::EShTargetSpv_1_2;
209 case SPIRV_VERSION_1_3:
210 return glslang::EShTargetSpv_1_3;
211 }
212 }
213
compileGlslToSpirV(tcu::TestLog & log,std::string source,glu::ShaderType type,ShaderBinaryDataType * dst,SpirvVersion version)214 bool compileGlslToSpirV(tcu::TestLog &log, std::string source, glu::ShaderType type, ShaderBinaryDataType *dst,
215 SpirvVersion version)
216 {
217 TBuiltInResource builtinRes;
218
219 prepareGlslang();
220 getDefaultBuiltInResources(&builtinRes);
221
222 const EShLanguage shaderStage = getGlslangStage(type);
223
224 glslang::TShader shader(shaderStage);
225 glslang::TProgram program;
226
227 const char *src[] = {source.c_str()};
228
229 shader.setStrings(src, 1);
230 shader.setEnvTarget(glslang::EshTargetSpv, getSpirvTargetVersion(version));
231 program.addShader(&shader);
232
233 const int compileRes = shader.parse(&builtinRes, 100, false, EShMsgSpvRules);
234 if (compileRes != 0)
235 {
236 const int linkRes = program.link(EShMsgSpvRules);
237
238 if (linkRes != 0)
239 {
240 const glslang::TIntermediate *const intermediate = program.getIntermediate(shaderStage);
241 glslang::GlslangToSpv(*intermediate, *dst);
242
243 return true;
244 }
245 else
246 {
247 log << tcu::TestLog::Message << "Program linking error:\n"
248 << program.getInfoLog() << "\n"
249 << "Source:\n"
250 << source << "\n"
251 << tcu::TestLog::EndMessage;
252 }
253 }
254 else
255 {
256 log << tcu::TestLog::Message << "Shader compilation error:\n"
257 << shader.getInfoLog() << "\n"
258 << "Source:\n"
259 << source << "\n"
260 << tcu::TestLog::EndMessage;
261 }
262
263 return false;
264 }
265
consumer(spv_message_level_t,const char *,const spv_position_t &,const char * m)266 void consumer(spv_message_level_t, const char *, const spv_position_t &, const char *m)
267 {
268 std::cerr << "error: " << m << std::endl;
269 }
270
spirvAssemble(ShaderBinaryDataType & dst,const std::string & src)271 void spirvAssemble(ShaderBinaryDataType &dst, const std::string &src)
272 {
273 spvtools::SpirvTools core(SPV_ENV_OPENGL_4_5);
274
275 core.SetMessageConsumer(consumer);
276
277 if (!core.Assemble(src, &dst))
278 TCU_THROW(InternalError, "Failed to assemble Spir-V source.");
279 }
280
spirvDisassemble(std::string & dst,const ShaderBinaryDataType & src)281 void spirvDisassemble(std::string &dst, const ShaderBinaryDataType &src)
282 {
283 spvtools::SpirvTools core(SPV_ENV_OPENGL_4_5);
284
285 core.SetMessageConsumer(consumer);
286
287 if (!core.Disassemble(src, &dst))
288 TCU_THROW(InternalError, "Failed to disassemble Spir-V module.");
289 }
290
spirvValidate(ShaderBinaryDataType & dst,bool throwOnError)291 bool spirvValidate(ShaderBinaryDataType &dst, bool throwOnError)
292 {
293 spvtools::SpirvTools core(SPV_ENV_OPENGL_4_5);
294
295 if (throwOnError)
296 core.SetMessageConsumer(consumer);
297
298 if (!core.Validate(dst))
299 {
300 if (throwOnError)
301 TCU_THROW(InternalError, "Failed to validate Spir-V module.");
302 return false;
303 }
304
305 return true;
306 }
307
makeSpirV(tcu::TestLog & log,ShaderSource source,SpirvVersion version)308 ShaderBinary makeSpirV(tcu::TestLog &log, ShaderSource source, SpirvVersion version)
309 {
310 ShaderBinary binary;
311
312 if (!spirvUtils::compileGlslToSpirV(log, source.source, source.shaderType, &binary.binary, version))
313 TCU_THROW(InternalError, "Failed to convert GLSL to Spir-V");
314
315 binary << source.shaderType << "main";
316
317 return binary;
318 }
319
320 /** Verifying if GLSL to SpirV mapping was performed correctly
321 *
322 * @param glslSource GLSL shader template
323 * @param spirVSource SpirV disassembled source
324 * @param mappings Glsl to SpirV mappings vector
325 * @param anyOf any occurence indicator
326 *
327 * @return true if GLSL code occurs as many times as all of SpirV code for each mapping if anyOf is false
328 * or true if SpirV code occurs at least once if GLSL code found, false otherwise.
329 **/
verifyMappings(std::string glslSource,std::string spirVSource,SpirVMapping & mappings,bool anyOf)330 bool verifyMappings(std::string glslSource, std::string spirVSource, SpirVMapping &mappings, bool anyOf)
331 {
332 std::vector<std::string> spirVSourceLines = de::splitString(spirVSource, '\n');
333
334 // Iterate through all glsl functions
335 for (SpirVMapping::iterator it = mappings.begin(); it != mappings.end(); it++)
336 {
337 int glslCodeCount = 0;
338 int spirVCodeCount = 0;
339
340 // To avoid finding functions with similar names (ie. "cos", "acos", "cosh")
341 // add characteristic characters that delimits finding results
342 std::string glslCode = it->first;
343
344 // Count GLSL code occurrences in GLSL source
345 size_t codePosition = glslSource.find(glslCode);
346 while (codePosition != std::string::npos)
347 {
348 glslCodeCount++;
349 codePosition = glslSource.find(glslCode, codePosition + 1);
350 }
351
352 if (glslCodeCount > 0)
353 {
354 // Count all SpirV code variants occurrences in SpirV source
355 for (int s = 0; s < (signed)it->second.size(); ++s)
356 {
357 std::vector<std::string> spirVCodes = de::splitString(it->second[s], ' ');
358
359 for (int v = 0; v < (signed)spirVSourceLines.size(); ++v)
360 {
361 std::vector<std::string> spirVLineCodes = de::splitString(spirVSourceLines[v], ' ');
362
363 bool matchAll = true;
364 for (int j = 0; j < (signed)spirVCodes.size(); ++j)
365 {
366 bool match = false;
367 for (int i = 0; i < (signed)spirVLineCodes.size(); ++i)
368 {
369 if (spirVLineCodes[i] == spirVCodes[j])
370 match = true;
371 }
372
373 matchAll = matchAll && match;
374 }
375
376 if (matchAll)
377 spirVCodeCount++;
378 }
379 }
380
381 // Check if both counts match
382 if (anyOf && (glslCodeCount > 0 && spirVCodeCount == 0))
383 return false;
384 else if (!anyOf && glslCodeCount != spirVCodeCount)
385 return false;
386 }
387 }
388
389 return true;
390 }
391
392 } // namespace spirvUtils
393
394 } // namespace glc
395