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
41using namespace glu;
42
43namespace glc
44{
45
46namespace spirvUtils
47{
48
49void 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
58EShLanguage getGlslangStage(glu::ShaderType type)
59{
60	static const EShLanguage stageMap[] = {
61		EShLangVertex, EShLangFragment, EShLangGeometry, EShLangTessControl, EShLangTessEvaluation, EShLangCompute,
62		EShLangRayGen, EShLangAnyHit, EShLangClosestHit, EShLangMiss, EShLangIntersect, EShLangCallable, EShLangTaskNV,
63		EShLangMeshNV
64	};
65
66	return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(stageMap, type);
67}
68
69static volatile deSingletonState s_glslangInitState = DE_SINGLETON_STATE_NOT_INITIALIZED;
70
71void initGlslang(void*)
72{
73	// Main compiler
74	glslang::InitializeProcess();
75
76	// SPIR-V disassembly
77	spv::Parameterize();
78}
79
80void prepareGlslang(void)
81{
82	deInitSingleton(&s_glslangInitState, initGlslang, DE_NULL);
83}
84
85void getDefaultLimits(TLimits* limits)
86{
87	limits->nonInductiveForLoops				 = true;
88	limits->whileLoops							 = true;
89	limits->doWhileLoops						 = true;
90	limits->generalUniformIndexing				 = true;
91	limits->generalAttributeMatrixVectorIndexing = true;
92	limits->generalVaryingIndexing				 = true;
93	limits->generalSamplerIndexing				 = true;
94	limits->generalVariableIndexing				 = true;
95	limits->generalConstantMatrixVectorIndexing  = true;
96}
97
98void getDefaultBuiltInResources(TBuiltInResource* builtin)
99{
100	getDefaultLimits(&builtin->limits);
101
102	builtin->maxLights								   = 32;
103	builtin->maxClipPlanes							   = 6;
104	builtin->maxTextureUnits						   = 32;
105	builtin->maxTextureCoords						   = 32;
106	builtin->maxVertexAttribs						   = 64;
107	builtin->maxVertexUniformComponents				   = 4096;
108	builtin->maxVaryingFloats						   = 64;
109	builtin->maxVertexTextureImageUnits				   = 32;
110	builtin->maxCombinedTextureImageUnits			   = 80;
111	builtin->maxTextureImageUnits					   = 32;
112	builtin->maxFragmentUniformComponents			   = 4096;
113	builtin->maxDrawBuffers							   = 32;
114	builtin->maxVertexUniformVectors				   = 128;
115	builtin->maxVaryingVectors						   = 8;
116	builtin->maxFragmentUniformVectors				   = 16;
117	builtin->maxVertexOutputVectors					   = 16;
118	builtin->maxFragmentInputVectors				   = 15;
119	builtin->minProgramTexelOffset					   = -8;
120	builtin->maxProgramTexelOffset					   = 7;
121	builtin->maxClipDistances						   = 8;
122	builtin->maxComputeWorkGroupCountX				   = 65535;
123	builtin->maxComputeWorkGroupCountY				   = 65535;
124	builtin->maxComputeWorkGroupCountZ				   = 65535;
125	builtin->maxComputeWorkGroupSizeX				   = 1024;
126	builtin->maxComputeWorkGroupSizeY				   = 1024;
127	builtin->maxComputeWorkGroupSizeZ				   = 64;
128	builtin->maxComputeUniformComponents			   = 1024;
129	builtin->maxComputeTextureImageUnits			   = 16;
130	builtin->maxComputeImageUniforms				   = 8;
131	builtin->maxComputeAtomicCounters				   = 8;
132	builtin->maxComputeAtomicCounterBuffers			   = 1;
133	builtin->maxVaryingComponents					   = 60;
134	builtin->maxVertexOutputComponents				   = 64;
135	builtin->maxGeometryInputComponents				   = 64;
136	builtin->maxGeometryOutputComponents			   = 128;
137	builtin->maxFragmentInputComponents				   = 128;
138	builtin->maxImageUnits							   = 8;
139	builtin->maxCombinedImageUnitsAndFragmentOutputs   = 8;
140	builtin->maxCombinedShaderOutputResources		   = 8;
141	builtin->maxImageSamples						   = 0;
142	builtin->maxVertexImageUniforms					   = 0;
143	builtin->maxTessControlImageUniforms			   = 0;
144	builtin->maxTessEvaluationImageUniforms			   = 0;
145	builtin->maxGeometryImageUniforms				   = 0;
146	builtin->maxFragmentImageUniforms				   = 8;
147	builtin->maxCombinedImageUniforms				   = 8;
148	builtin->maxGeometryTextureImageUnits			   = 16;
149	builtin->maxGeometryOutputVertices				   = 256;
150	builtin->maxGeometryTotalOutputComponents		   = 1024;
151	builtin->maxGeometryUniformComponents			   = 1024;
152	builtin->maxGeometryVaryingComponents			   = 64;
153	builtin->maxTessControlInputComponents			   = 128;
154	builtin->maxTessControlOutputComponents			   = 128;
155	builtin->maxTessControlTextureImageUnits		   = 16;
156	builtin->maxTessControlUniformComponents		   = 1024;
157	builtin->maxTessControlTotalOutputComponents	   = 4096;
158	builtin->maxTessEvaluationInputComponents		   = 128;
159	builtin->maxTessEvaluationOutputComponents		   = 128;
160	builtin->maxTessEvaluationTextureImageUnits		   = 16;
161	builtin->maxTessEvaluationUniformComponents		   = 1024;
162	builtin->maxTessPatchComponents					   = 120;
163	builtin->maxPatchVertices						   = 32;
164	builtin->maxTessGenLevel						   = 64;
165	builtin->maxViewports							   = 16;
166	builtin->maxVertexAtomicCounters				   = 0;
167	builtin->maxTessControlAtomicCounters			   = 0;
168	builtin->maxTessEvaluationAtomicCounters		   = 0;
169	builtin->maxGeometryAtomicCounters				   = 0;
170	builtin->maxFragmentAtomicCounters				   = 8;
171	builtin->maxCombinedAtomicCounters				   = 8;
172	builtin->maxAtomicCounterBindings				   = 1;
173	builtin->maxVertexAtomicCounterBuffers			   = 0;
174	builtin->maxTessControlAtomicCounterBuffers		   = 0;
175	builtin->maxTessEvaluationAtomicCounterBuffers	 = 0;
176	builtin->maxGeometryAtomicCounterBuffers		   = 0;
177	builtin->maxFragmentAtomicCounterBuffers		   = 1;
178	builtin->maxCombinedAtomicCounterBuffers		   = 1;
179	builtin->maxAtomicCounterBufferSize				   = 16384;
180	builtin->maxTransformFeedbackBuffers			   = 4;
181	builtin->maxTransformFeedbackInterleavedComponents = 64;
182	builtin->maxCullDistances						   = 8;
183	builtin->maxCombinedClipAndCullDistances		   = 8;
184	builtin->maxSamples								   = 4;
185	builtin->maxMeshOutputVerticesNV				   = 256;
186	builtin->maxMeshOutputPrimitivesNV				   = 256;
187	builtin->maxMeshWorkGroupSizeX_NV				   = 32;
188	builtin->maxMeshWorkGroupSizeY_NV				   = 1;
189	builtin->maxMeshWorkGroupSizeZ_NV				   = 1;
190	builtin->maxTaskWorkGroupSizeX_NV				   = 32;
191	builtin->maxTaskWorkGroupSizeY_NV				   = 1;
192	builtin->maxTaskWorkGroupSizeZ_NV				   = 1;
193	builtin->maxMeshViewCountNV						   = 4;
194	builtin->maxDualSourceDrawBuffersEXT			   = 1;
195};
196
197glslang::EShTargetLanguageVersion getSpirvTargetVersion(SpirvVersion version)
198{
199    switch(version)
200    {
201    default:
202        DE_FATAL("unhandled SPIRV target version");
203        // fall-through
204    case SPIRV_VERSION_1_0:
205        return glslang::EShTargetSpv_1_0;
206    case SPIRV_VERSION_1_1:
207        return glslang::EShTargetSpv_1_1;
208    case SPIRV_VERSION_1_2:
209        return glslang::EShTargetSpv_1_2;
210    case SPIRV_VERSION_1_3:
211        return glslang::EShTargetSpv_1_3;
212    }
213}
214
215bool compileGlslToSpirV(tcu::TestLog& log, std::string source, glu::ShaderType type, ShaderBinaryDataType* dst, 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
266void consumer(spv_message_level_t, const char*, const spv_position_t&, const char* m)
267{
268	std::cerr << "error: " << m << std::endl;
269}
270
271void 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
281void 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
291bool 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
308ShaderBinary 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 **/
330bool 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