Home | History | Annotate | Download | only in vulkan
      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 
     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 
     60 bool isNativeSpirVBinaryEndianness (void)
     61 {
     62 #if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
     63 	return true;
     64 #else
     65 	return false;
     66 #endif
     67 }
     68 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    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
    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 
    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 
    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 
    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 
    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