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 "vkGlslToSpirV.hpp"
26#include "vkSpirVAsm.hpp"
27#include "vkRefUtil.hpp"
28
29#include "tcuTestLog.hpp"
30
31#include "deArrayUtil.hpp"
32#include "deMemory.h"
33#include "deInt32.h"
34
35namespace vk
36{
37
38using std::string;
39using std::vector;
40using tcu::TestLog;
41
42#if defined(DE_DEBUG) && defined(DEQP_HAVE_SPIRV_TOOLS)
43#	define VALIDATE_BINARIES	true
44#else
45#	define VALIDATE_BINARIES	false
46#endif
47
48#define SPIRV_BINARY_ENDIANNESS DE_LITTLE_ENDIAN
49
50// ProgramBinary
51
52ProgramBinary::ProgramBinary (ProgramFormat format, size_t binarySize, const deUint8* binary)
53	: m_format	(format)
54	, m_binary	(binary, binary+binarySize)
55{
56}
57
58// Utils
59
60namespace
61{
62
63bool isNativeSpirVBinaryEndianness (void)
64{
65#if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
66	return true;
67#else
68	return false;
69#endif
70}
71
72bool isSaneSpirVBinary (const ProgramBinary& binary)
73{
74	const deUint32	spirvMagicWord	= 0x07230203;
75	const deUint32	spirvMagicBytes	= isNativeSpirVBinaryEndianness()
76									? spirvMagicWord
77									: deReverseBytes32(spirvMagicWord);
78
79	DE_ASSERT(binary.getFormat() == PROGRAM_FORMAT_SPIRV);
80
81	if (binary.getSize() % sizeof(deUint32) != 0)
82		return false;
83
84	if (binary.getSize() < sizeof(deUint32))
85		return false;
86
87	if (*(const deUint32*)binary.getBinary() != spirvMagicBytes)
88		return false;
89
90	return true;
91}
92
93ProgramBinary* createProgramBinaryFromSpirV (const vector<deUint32>& binary)
94{
95	DE_ASSERT(!binary.empty());
96
97	if (isNativeSpirVBinaryEndianness())
98		return new ProgramBinary(PROGRAM_FORMAT_SPIRV, binary.size()*sizeof(deUint32), (const deUint8*)&binary[0]);
99	else
100		TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
101}
102
103} // anonymous
104
105ProgramBinary* buildProgram (const glu::ProgramSources& program, ProgramFormat binaryFormat, glu::ShaderProgramInfo* buildInfo)
106{
107	const bool	validateBinary	= VALIDATE_BINARIES;
108
109	if (binaryFormat == PROGRAM_FORMAT_SPIRV)
110	{
111		vector<deUint32> binary;
112
113		if (!compileGlslToSpirV(program, &binary, buildInfo))
114			TCU_THROW(InternalError, "Compiling GLSL to SPIR-V failed");
115
116		if (validateBinary)
117		{
118			std::ostringstream validationLog;
119
120			if (!validateSpirV(binary.size(), &binary[0], &validationLog))
121			{
122				buildInfo->program.linkOk 	 = false;
123				buildInfo->program.infoLog	+= "\n" + validationLog.str();
124
125				TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
126			}
127		}
128
129		return createProgramBinaryFromSpirV(binary);
130	}
131	else
132		TCU_THROW(NotSupportedError, "Unsupported program format");
133}
134
135ProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo* buildInfo)
136{
137	const bool			validateBinary		= VALIDATE_BINARIES;
138	vector<deUint32>	binary;
139
140	if (!assembleSpirV(&program, &binary, buildInfo))
141		TCU_THROW(InternalError, "Failed to assemble SPIR-V");
142
143	if (validateBinary)
144	{
145		std::ostringstream	validationLog;
146
147		if (!validateSpirV(binary.size(), &binary[0], &validationLog))
148		{
149			buildInfo->compileOk	 = false;
150			buildInfo->infoLog		+= "\n" + validationLog.str();
151
152			TCU_THROW(InternalError, "Validation failed for assembled SPIR-V binary");
153		}
154	}
155
156	return createProgramBinaryFromSpirV(binary);
157}
158
159void disassembleProgram (const ProgramBinary& program, std::ostream* dst)
160{
161	if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
162	{
163		TCU_CHECK_INTERNAL(isSaneSpirVBinary(program));
164
165		if (isNativeSpirVBinaryEndianness())
166			disassembleSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst);
167		else
168			TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
169	}
170	else
171		TCU_THROW(NotSupportedError, "Unsupported program format");
172}
173
174bool validateProgram (const ProgramBinary& program, std::ostream* dst)
175{
176	if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
177	{
178		if (!isSaneSpirVBinary(program))
179		{
180			*dst << "Binary doesn't look like SPIR-V at all";
181			return false;
182		}
183
184		if (isNativeSpirVBinaryEndianness())
185			return validateSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst);
186		else
187			TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
188	}
189	else
190		TCU_THROW(NotSupportedError, "Unsupported program format");
191}
192
193Move<VkShaderModule> createShaderModule (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags)
194{
195	if (binary.getFormat() == PROGRAM_FORMAT_SPIRV)
196	{
197		const struct VkShaderModuleCreateInfo		shaderModuleInfo	=
198		{
199			VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
200			DE_NULL,
201			flags,
202			(deUintptr)binary.getSize(),
203			(const deUint32*)binary.getBinary(),
204		};
205
206		return createShaderModule(deviceInterface, device, &shaderModuleInfo);
207	}
208	else
209		TCU_THROW(NotSupportedError, "Unsupported program format");
210}
211
212glu::ShaderType getGluShaderType (VkShaderStageFlagBits shaderStage)
213{
214	switch (shaderStage)
215	{
216		case VK_SHADER_STAGE_VERTEX_BIT:					return glu::SHADERTYPE_VERTEX;
217		case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:		return glu::SHADERTYPE_TESSELLATION_CONTROL;
218		case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:	return glu::SHADERTYPE_TESSELLATION_EVALUATION;
219		case VK_SHADER_STAGE_GEOMETRY_BIT:					return glu::SHADERTYPE_GEOMETRY;
220		case VK_SHADER_STAGE_FRAGMENT_BIT:					return glu::SHADERTYPE_FRAGMENT;
221		case VK_SHADER_STAGE_COMPUTE_BIT:					return glu::SHADERTYPE_COMPUTE;
222		default:
223			DE_FATAL("Unknown shader stage");
224			return glu::SHADERTYPE_LAST;
225	}
226}
227
228VkShaderStageFlagBits getVkShaderStage (glu::ShaderType shaderType)
229{
230	static const VkShaderStageFlagBits s_shaderStages[] =
231	{
232		VK_SHADER_STAGE_VERTEX_BIT,
233		VK_SHADER_STAGE_FRAGMENT_BIT,
234		VK_SHADER_STAGE_GEOMETRY_BIT,
235		VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
236		VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
237		VK_SHADER_STAGE_COMPUTE_BIT
238	};
239
240	return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(s_shaderStages, shaderType);
241}
242
243} // vk
244