• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * Vulkan CTS Framework
3  * --------------------
4  *
5  * Copyright (c) 2015 Google 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
21  * \brief Program utilities.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vkPrograms.hpp"
25 #include "vkShaderToSpirV.hpp"
26 #include "vkSpirVAsm.hpp"
27 #include "vkRefUtil.hpp"
28 
29 #include "deArrayUtil.hpp"
30 #include "deMemory.h"
31 #include "deInt32.h"
32 
33 namespace vk
34 {
35 
36 using std::string;
37 using std::vector;
38 
39 #if defined(DE_DEBUG) && defined(DEQP_HAVE_SPIRV_TOOLS)
40 #	define VALIDATE_BINARIES	true
41 #else
42 #	define VALIDATE_BINARIES	false
43 #endif
44 
45 #define SPIRV_BINARY_ENDIANNESS DE_LITTLE_ENDIAN
46 
47 // ProgramBinary
48 
ProgramBinary(ProgramFormat format,size_t binarySize,const deUint8 * binary)49 ProgramBinary::ProgramBinary (ProgramFormat format, size_t binarySize, const deUint8* binary)
50 	: m_format	(format)
51 	, m_binary	(binary, binary+binarySize)
52 {
53 }
54 
55 // Utils
56 
57 namespace
58 {
59 
isNativeSpirVBinaryEndianness(void)60 bool isNativeSpirVBinaryEndianness (void)
61 {
62 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
63 	return true;
64 #else
65 	return false;
66 #endif
67 }
68 
isSaneSpirVBinary(const ProgramBinary & binary)69 bool isSaneSpirVBinary (const ProgramBinary& binary)
70 {
71 	const deUint32	spirvMagicWord	= 0x07230203;
72 	const deUint32	spirvMagicBytes	= isNativeSpirVBinaryEndianness()
73 									? spirvMagicWord
74 									: deReverseBytes32(spirvMagicWord);
75 
76 	DE_ASSERT(binary.getFormat() == PROGRAM_FORMAT_SPIRV);
77 
78 	if (binary.getSize() % sizeof(deUint32) != 0)
79 		return false;
80 
81 	if (binary.getSize() < sizeof(deUint32))
82 		return false;
83 
84 	if (*(const deUint32*)binary.getBinary() != spirvMagicBytes)
85 		return false;
86 
87 	return true;
88 }
89 
createProgramBinaryFromSpirV(const vector<deUint32> & binary)90 ProgramBinary* createProgramBinaryFromSpirV (const vector<deUint32>& binary)
91 {
92 	DE_ASSERT(!binary.empty());
93 
94 	if (isNativeSpirVBinaryEndianness())
95 		return new ProgramBinary(PROGRAM_FORMAT_SPIRV, binary.size()*sizeof(deUint32), (const deUint8*)&binary[0]);
96 	else
97 		TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
98 }
99 
100 } // anonymous
101 
validateCompiledBinary(const vector<deUint32> & binary,glu::ShaderProgramInfo * buildInfo,const SpirvVersion spirvVersion)102 void validateCompiledBinary(const vector<deUint32>& binary, glu::ShaderProgramInfo* buildInfo, const SpirvVersion spirvVersion)
103 {
104 	std::ostringstream validationLog;
105 
106 	if (!validateSpirV(binary.size(), &binary[0], &validationLog, spirvVersion))
107 	{
108 		buildInfo->program.linkOk	 = false;
109 		buildInfo->program.infoLog	+= "\n" + validationLog.str();
110 
111 		TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
112 	}
113 }
114 
buildProgram(const GlslSource & program,glu::ShaderProgramInfo * buildInfo)115 ProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo* buildInfo)
116 {
117 	const SpirvVersion	spirvVersion	= program.buildOptions.targetVersion;
118 	const bool			validateBinary	= VALIDATE_BINARIES;
119 	vector<deUint32>	binary;
120 
121 	{
122 		vector<deUint32> nonStrippedBinary;
123 
124 		if (!compileGlslToSpirV(program, &nonStrippedBinary, buildInfo))
125 			TCU_THROW(InternalError, "Compiling GLSL to SPIR-V failed");
126 
127 		TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
128 		stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
129 		TCU_CHECK_INTERNAL(!binary.empty());
130 	}
131 
132 	if (validateBinary)
133 		validateCompiledBinary(binary, buildInfo, spirvVersion);
134 
135 	return createProgramBinaryFromSpirV(binary);
136 }
137 
buildProgram(const HlslSource & program,glu::ShaderProgramInfo * buildInfo)138 ProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo* buildInfo)
139 {
140 	const SpirvVersion	spirvVersion	= program.buildOptions.targetVersion;
141 	const bool			validateBinary	= VALIDATE_BINARIES;
142 	vector<deUint32>	binary;
143 
144 	{
145 		vector<deUint32> nonStrippedBinary;
146 
147 		if (!compileHlslToSpirV(program, &nonStrippedBinary, buildInfo))
148 			TCU_THROW(InternalError, "Compiling HLSL to SPIR-V failed");
149 
150 		TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
151 		stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
152 		TCU_CHECK_INTERNAL(!binary.empty());
153 	}
154 
155 	if (validateBinary)
156 		validateCompiledBinary(binary, buildInfo, spirvVersion);
157 
158 	return createProgramBinaryFromSpirV(binary);
159 }
160 
assembleProgram(const SpirVAsmSource & program,SpirVProgramInfo * buildInfo)161 ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo* buildInfo)
162 {
163 	const SpirvVersion	spirvVersion		= program.buildOptions.targetVersion;
164 	const bool			validateBinary		= VALIDATE_BINARIES;
165 	vector<deUint32>	binary;
166 
167 	if (!assembleSpirV(&program, &binary, buildInfo, spirvVersion))
168 		TCU_THROW(InternalError, "Failed to assemble SPIR-V");
169 
170 	if (validateBinary)
171 	{
172 		std::ostringstream	validationLog;
173 
174 		if (!validateSpirV(binary.size(), &binary[0], &validationLog, spirvVersion))
175 		{
176 			buildInfo->compileOk	 = false;
177 			buildInfo->infoLog		+= "\n" + validationLog.str();
178 
179 			TCU_THROW(InternalError, "Validation failed for assembled SPIR-V binary");
180 		}
181 	}
182 
183 	return createProgramBinaryFromSpirV(binary);
184 }
185 
disassembleProgram(const ProgramBinary & program,std::ostream * dst)186 void disassembleProgram (const ProgramBinary& program, std::ostream* dst)
187 {
188 	if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
189 	{
190 		TCU_CHECK_INTERNAL(isSaneSpirVBinary(program));
191 
192 		if (isNativeSpirVBinaryEndianness())
193 			disassembleSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst,
194 							 extractSpirvVersion(program));
195 		else
196 			TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
197 	}
198 	else
199 		TCU_THROW(NotSupportedError, "Unsupported program format");
200 }
201 
validateProgram(const ProgramBinary & program,std::ostream * dst)202 bool validateProgram (const ProgramBinary& program, std::ostream* dst)
203 {
204 	if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
205 	{
206 		if (!isSaneSpirVBinary(program))
207 		{
208 			*dst << "Binary doesn't look like SPIR-V at all";
209 			return false;
210 		}
211 
212 		if (isNativeSpirVBinaryEndianness())
213 			return validateSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst,
214 								 extractSpirvVersion(program));
215 		else
216 			TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
217 	}
218 	else
219 		TCU_THROW(NotSupportedError, "Unsupported program format");
220 }
221 
createShaderModule(const DeviceInterface & deviceInterface,VkDevice device,const ProgramBinary & binary,VkShaderModuleCreateFlags flags)222 Move<VkShaderModule> createShaderModule (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags)
223 {
224 	if (binary.getFormat() == PROGRAM_FORMAT_SPIRV)
225 	{
226 		const struct VkShaderModuleCreateInfo		shaderModuleInfo	=
227 		{
228 			VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
229 			DE_NULL,
230 			flags,
231 			(deUintptr)binary.getSize(),
232 			(const deUint32*)binary.getBinary(),
233 		};
234 
235 		return createShaderModule(deviceInterface, device, &shaderModuleInfo);
236 	}
237 	else
238 		TCU_THROW(NotSupportedError, "Unsupported program format");
239 }
240 
getGluShaderType(VkShaderStageFlagBits shaderStage)241 glu::ShaderType getGluShaderType (VkShaderStageFlagBits shaderStage)
242 {
243 	switch (shaderStage)
244 	{
245 		case VK_SHADER_STAGE_VERTEX_BIT:					return glu::SHADERTYPE_VERTEX;
246 		case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:		return glu::SHADERTYPE_TESSELLATION_CONTROL;
247 		case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:	return glu::SHADERTYPE_TESSELLATION_EVALUATION;
248 		case VK_SHADER_STAGE_GEOMETRY_BIT:					return glu::SHADERTYPE_GEOMETRY;
249 		case VK_SHADER_STAGE_FRAGMENT_BIT:					return glu::SHADERTYPE_FRAGMENT;
250 		case VK_SHADER_STAGE_COMPUTE_BIT:					return glu::SHADERTYPE_COMPUTE;
251 		default:
252 			DE_FATAL("Unknown shader stage");
253 			return glu::SHADERTYPE_LAST;
254 	}
255 }
256 
getVkShaderStage(glu::ShaderType shaderType)257 VkShaderStageFlagBits getVkShaderStage (glu::ShaderType shaderType)
258 {
259 	static const VkShaderStageFlagBits s_shaderStages[] =
260 	{
261 		VK_SHADER_STAGE_VERTEX_BIT,
262 		VK_SHADER_STAGE_FRAGMENT_BIT,
263 		VK_SHADER_STAGE_GEOMETRY_BIT,
264 		VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
265 		VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
266 		VK_SHADER_STAGE_COMPUTE_BIT
267 	};
268 
269 	return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(s_shaderStages, shaderType);
270 }
271 
272 // Baseline version, to be used for shaders which don't specify a version
getBaselineSpirvVersion(const deUint32)273 vk::SpirvVersion getBaselineSpirvVersion (const deUint32 /* vulkanVersion */)
274 {
275 	return vk::SPIRV_VERSION_1_0;
276 }
277 
278 // Max supported versions for each vulkan version
getMaxSpirvVersionForAsm(const deUint32 vulkanVersion)279 vk::SpirvVersion getMaxSpirvVersionForAsm (const deUint32 vulkanVersion)
280 {
281 	vk::SpirvVersion	result			= vk::SPIRV_VERSION_LAST;
282 
283 	deUint32 vulkanVersionMajorMinor = VK_MAKE_VERSION(VK_VERSION_MAJOR(vulkanVersion), VK_VERSION_MINOR(vulkanVersion), 0);
284 	if (vulkanVersionMajorMinor == VK_API_VERSION_1_0)
285 		result = vk::SPIRV_VERSION_1_0;
286 	else if (vulkanVersionMajorMinor >= VK_API_VERSION_1_1)
287 		result = vk::SPIRV_VERSION_1_3;
288 
289 	DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
290 
291 	return result;
292 }
293 
getMaxSpirvVersionForGlsl(const deUint32 vulkanVersion)294 vk::SpirvVersion getMaxSpirvVersionForGlsl (const deUint32 vulkanVersion)
295 {
296 	vk::SpirvVersion	result			= vk::SPIRV_VERSION_LAST;
297 
298 	deUint32 vulkanVersionMajorMinor = VK_MAKE_VERSION(VK_VERSION_MAJOR(vulkanVersion), VK_VERSION_MINOR(vulkanVersion), 0);
299 	if (vulkanVersionMajorMinor == VK_API_VERSION_1_0)
300 		result = vk::SPIRV_VERSION_1_0;
301 	else if (vulkanVersionMajorMinor >= VK_API_VERSION_1_1)
302 		result = vk::SPIRV_VERSION_1_3;
303 
304 	DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
305 
306 	return result;
307 }
308 
extractSpirvVersion(const ProgramBinary & binary)309 SpirvVersion extractSpirvVersion (const ProgramBinary& binary)
310 {
311 	DE_STATIC_ASSERT(SPIRV_VERSION_1_3 + 1 == SPIRV_VERSION_LAST);
312 
313 	if (binary.getFormat() != PROGRAM_FORMAT_SPIRV)
314 		TCU_THROW(InternalError, "Binary is not in SPIR-V format");
315 
316 	if (!isSaneSpirVBinary(binary) || binary.getSize() < sizeof(SpirvBinaryHeader))
317 		TCU_THROW(InternalError, "Invalid SPIR-V header format");
318 
319 	const deUint32				spirvBinaryVersion10	= 0x00010000;
320 	const deUint32				spirvBinaryVersion11	= 0x00010100;
321 	const deUint32				spirvBinaryVersion12	= 0x00010200;
322 	const deUint32				spirvBinaryVersion13	= 0x00010300;
323 	const SpirvBinaryHeader*	header					= reinterpret_cast<const SpirvBinaryHeader*>(binary.getBinary());
324 	const deUint32				spirvVersion			= isNativeSpirVBinaryEndianness()
325 														? header->version
326 														: deReverseBytes32(header->version);
327 	SpirvVersion				result					= SPIRV_VERSION_LAST;
328 
329 	switch (spirvVersion)
330 	{
331 		case spirvBinaryVersion10:	result = SPIRV_VERSION_1_0; break; //!< SPIR-V 1.0
332 		case spirvBinaryVersion11:	result = SPIRV_VERSION_1_1; break; //!< SPIR-V 1.1
333 		case spirvBinaryVersion12:	result = SPIRV_VERSION_1_2; break; //!< SPIR-V 1.2
334 		case spirvBinaryVersion13:	result = SPIRV_VERSION_1_3; break; //!< SPIR-V 1.3
335 		default:					TCU_THROW(InternalError, "Unknown SPIR-V version detected in binary");
336 	}
337 
338 	return result;
339 }
340 
getSpirvVersionName(const SpirvVersion spirvVersion)341 std::string getSpirvVersionName (const SpirvVersion spirvVersion)
342 {
343 	DE_STATIC_ASSERT(SPIRV_VERSION_1_3 + 1 == SPIRV_VERSION_LAST);
344 	DE_ASSERT(spirvVersion < SPIRV_VERSION_LAST);
345 
346 	std::string result;
347 
348 	switch (spirvVersion)
349 	{
350 		case SPIRV_VERSION_1_0: result = "1.0"; break; //!< SPIR-V 1.0
351 		case SPIRV_VERSION_1_1: result = "1.1"; break; //!< SPIR-V 1.1
352 		case SPIRV_VERSION_1_2: result = "1.2"; break; //!< SPIR-V 1.2
353 		case SPIRV_VERSION_1_3: result = "1.3"; break; //!< SPIR-V 1.3
354 		default:				result = "Unknown";
355 	}
356 
357 	return result;
358 }
359 
operator ++(SpirvVersion & spirvVersion)360 SpirvVersion& operator++(SpirvVersion& spirvVersion)
361 {
362 	if (spirvVersion == SPIRV_VERSION_LAST)
363 		spirvVersion = SPIRV_VERSION_1_0;
364 	else
365 		spirvVersion = static_cast<SpirvVersion>(static_cast<deUint32>(spirvVersion) + 1);
366 
367 	return spirvVersion;
368 }
369 
370 } // vk
371