1e5c31af7Sopenharmony_ci/*-------------------------------------------------------------------------
2e5c31af7Sopenharmony_ci * Vulkan CTS Framework
3e5c31af7Sopenharmony_ci * --------------------
4e5c31af7Sopenharmony_ci *
5e5c31af7Sopenharmony_ci * Copyright (c) 2019 Google Inc.
6e5c31af7Sopenharmony_ci *
7e5c31af7Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
8e5c31af7Sopenharmony_ci * you may not use this file except in compliance with the License.
9e5c31af7Sopenharmony_ci * You may obtain a copy of the License at
10e5c31af7Sopenharmony_ci *
11e5c31af7Sopenharmony_ci *      http://www.apache.org/licenses/LICENSE-2.0
12e5c31af7Sopenharmony_ci *
13e5c31af7Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
14e5c31af7Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
15e5c31af7Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16e5c31af7Sopenharmony_ci * See the License for the specific language governing permissions and
17e5c31af7Sopenharmony_ci * limitations under the License.
18e5c31af7Sopenharmony_ci *
19e5c31af7Sopenharmony_ci *//*!
20e5c31af7Sopenharmony_ci * \file
21e5c31af7Sopenharmony_ci * \brief Program utilities.
22e5c31af7Sopenharmony_ci *//*--------------------------------------------------------------------*/
23e5c31af7Sopenharmony_ci
24e5c31af7Sopenharmony_ci#include "spirv-tools/optimizer.hpp"
25e5c31af7Sopenharmony_ci
26e5c31af7Sopenharmony_ci#include "qpInfo.h"
27e5c31af7Sopenharmony_ci
28e5c31af7Sopenharmony_ci#include "vkPrograms.hpp"
29e5c31af7Sopenharmony_ci#include "vkShaderToSpirV.hpp"
30e5c31af7Sopenharmony_ci#include "vkSpirVAsm.hpp"
31e5c31af7Sopenharmony_ci#include "vkRefUtil.hpp"
32e5c31af7Sopenharmony_ci
33e5c31af7Sopenharmony_ci#include "deMutex.hpp"
34e5c31af7Sopenharmony_ci#include "deFilePath.hpp"
35e5c31af7Sopenharmony_ci#include "deArrayUtil.hpp"
36e5c31af7Sopenharmony_ci#include "deMemory.h"
37e5c31af7Sopenharmony_ci#include "deInt32.h"
38e5c31af7Sopenharmony_ci
39e5c31af7Sopenharmony_ci#include "tcuCommandLine.hpp"
40e5c31af7Sopenharmony_ci
41e5c31af7Sopenharmony_ci#include <map>
42e5c31af7Sopenharmony_ci#include <mutex>
43e5c31af7Sopenharmony_ci
44e5c31af7Sopenharmony_ci#if DE_OS == DE_OS_ANDROID
45e5c31af7Sopenharmony_ci#define DISABLE_SHADERCACHE_IPC
46e5c31af7Sopenharmony_ci#endif
47e5c31af7Sopenharmony_ci
48e5c31af7Sopenharmony_cinamespace vk
49e5c31af7Sopenharmony_ci{
50e5c31af7Sopenharmony_ci
51e5c31af7Sopenharmony_ciusing std::string;
52e5c31af7Sopenharmony_ciusing std::vector;
53e5c31af7Sopenharmony_ciusing std::map;
54e5c31af7Sopenharmony_ci
55e5c31af7Sopenharmony_ci#if defined(DE_DEBUG)
56e5c31af7Sopenharmony_ci#	define VALIDATE_BINARIES	true
57e5c31af7Sopenharmony_ci#else
58e5c31af7Sopenharmony_ci#	define VALIDATE_BINARIES	false
59e5c31af7Sopenharmony_ci#endif
60e5c31af7Sopenharmony_ci
61e5c31af7Sopenharmony_ci#define SPIRV_BINARY_ENDIANNESS DE_LITTLE_ENDIAN
62e5c31af7Sopenharmony_ci
63e5c31af7Sopenharmony_ci// ProgramBinary
64e5c31af7Sopenharmony_ci
65e5c31af7Sopenharmony_ciProgramBinary::ProgramBinary (ProgramFormat format, size_t binarySize, const deUint8* binary)
66e5c31af7Sopenharmony_ci	: m_format	(format)
67e5c31af7Sopenharmony_ci	, m_binary	(binary, binary+binarySize)
68e5c31af7Sopenharmony_ci	, m_used	(false)
69e5c31af7Sopenharmony_ci{
70e5c31af7Sopenharmony_ci}
71e5c31af7Sopenharmony_ci
72e5c31af7Sopenharmony_ci// Utils
73e5c31af7Sopenharmony_ci
74e5c31af7Sopenharmony_cinamespace
75e5c31af7Sopenharmony_ci{
76e5c31af7Sopenharmony_ci
77e5c31af7Sopenharmony_cibool isNativeSpirVBinaryEndianness (void)
78e5c31af7Sopenharmony_ci{
79e5c31af7Sopenharmony_ci#if (DE_ENDIANNESS == SPIRV_BINARY_ENDIANNESS)
80e5c31af7Sopenharmony_ci	return true;
81e5c31af7Sopenharmony_ci#else
82e5c31af7Sopenharmony_ci	return false;
83e5c31af7Sopenharmony_ci#endif
84e5c31af7Sopenharmony_ci}
85e5c31af7Sopenharmony_ci
86e5c31af7Sopenharmony_cibool isSaneSpirVBinary (const ProgramBinary& binary)
87e5c31af7Sopenharmony_ci{
88e5c31af7Sopenharmony_ci	const deUint32	spirvMagicWord	= 0x07230203;
89e5c31af7Sopenharmony_ci	const deUint32	spirvMagicBytes	= isNativeSpirVBinaryEndianness()
90e5c31af7Sopenharmony_ci									? spirvMagicWord
91e5c31af7Sopenharmony_ci									: deReverseBytes32(spirvMagicWord);
92e5c31af7Sopenharmony_ci
93e5c31af7Sopenharmony_ci	DE_ASSERT(binary.getFormat() == PROGRAM_FORMAT_SPIRV);
94e5c31af7Sopenharmony_ci
95e5c31af7Sopenharmony_ci	if (binary.getSize() % sizeof(deUint32) != 0)
96e5c31af7Sopenharmony_ci		return false;
97e5c31af7Sopenharmony_ci
98e5c31af7Sopenharmony_ci	if (binary.getSize() < sizeof(deUint32))
99e5c31af7Sopenharmony_ci		return false;
100e5c31af7Sopenharmony_ci
101e5c31af7Sopenharmony_ci	if (*(const deUint32*)binary.getBinary() != spirvMagicBytes)
102e5c31af7Sopenharmony_ci		return false;
103e5c31af7Sopenharmony_ci
104e5c31af7Sopenharmony_ci	return true;
105e5c31af7Sopenharmony_ci}
106e5c31af7Sopenharmony_ci
107e5c31af7Sopenharmony_civoid optimizeCompiledBinary (vector<deUint32>& binary, int optimizationRecipe, const SpirvVersion spirvVersion)
108e5c31af7Sopenharmony_ci{
109e5c31af7Sopenharmony_ci	spv_target_env targetEnv = SPV_ENV_VULKAN_1_0;
110e5c31af7Sopenharmony_ci
111e5c31af7Sopenharmony_ci	// Map SpirvVersion with spv_target_env:
112e5c31af7Sopenharmony_ci	switch (spirvVersion)
113e5c31af7Sopenharmony_ci	{
114e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_0: targetEnv = SPV_ENV_VULKAN_1_0;	break;
115e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_1:
116e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_2:
117e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_3: targetEnv = SPV_ENV_VULKAN_1_1;	break;
118e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_4: targetEnv = SPV_ENV_VULKAN_1_1_SPIRV_1_4;	break;
119e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_5: targetEnv = SPV_ENV_VULKAN_1_2;	break;
120e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_6: targetEnv = SPV_ENV_VULKAN_1_3;	break;
121e5c31af7Sopenharmony_ci		default:
122e5c31af7Sopenharmony_ci			TCU_THROW(InternalError, "Unexpected SPIR-V version requested");
123e5c31af7Sopenharmony_ci	}
124e5c31af7Sopenharmony_ci
125e5c31af7Sopenharmony_ci	spvtools::Optimizer optimizer(targetEnv);
126e5c31af7Sopenharmony_ci
127e5c31af7Sopenharmony_ci	switch (optimizationRecipe)
128e5c31af7Sopenharmony_ci	{
129e5c31af7Sopenharmony_ci		case 1:
130e5c31af7Sopenharmony_ci			optimizer.RegisterPerformancePasses();
131e5c31af7Sopenharmony_ci			break;
132e5c31af7Sopenharmony_ci		case 2:
133e5c31af7Sopenharmony_ci			optimizer.RegisterSizePasses();
134e5c31af7Sopenharmony_ci			break;
135e5c31af7Sopenharmony_ci		default:
136e5c31af7Sopenharmony_ci			TCU_THROW(InternalError, "Unknown optimization recipe requested");
137e5c31af7Sopenharmony_ci	}
138e5c31af7Sopenharmony_ci
139e5c31af7Sopenharmony_ci	spvtools::OptimizerOptions optimizer_options;
140e5c31af7Sopenharmony_ci	optimizer_options.set_run_validator(false);
141e5c31af7Sopenharmony_ci	const bool ok = optimizer.Run(binary.data(), binary.size(), &binary, optimizer_options);
142e5c31af7Sopenharmony_ci
143e5c31af7Sopenharmony_ci	if (!ok)
144e5c31af7Sopenharmony_ci		TCU_THROW(InternalError, "Optimizer call failed");
145e5c31af7Sopenharmony_ci}
146e5c31af7Sopenharmony_ci
147e5c31af7Sopenharmony_ciProgramBinary* createProgramBinaryFromSpirV (const vector<deUint32>& binary)
148e5c31af7Sopenharmony_ci{
149e5c31af7Sopenharmony_ci	DE_ASSERT(!binary.empty());
150e5c31af7Sopenharmony_ci
151e5c31af7Sopenharmony_ci	if (isNativeSpirVBinaryEndianness())
152e5c31af7Sopenharmony_ci		return new ProgramBinary(PROGRAM_FORMAT_SPIRV, binary.size()*sizeof(deUint32), (const deUint8*)&binary[0]);
153e5c31af7Sopenharmony_ci	else
154e5c31af7Sopenharmony_ci		TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
155e5c31af7Sopenharmony_ci}
156e5c31af7Sopenharmony_ci
157e5c31af7Sopenharmony_ci} // anonymous
158e5c31af7Sopenharmony_ci
159e5c31af7Sopenharmony_civoid validateCompiledBinary(const vector<deUint32>& binary, glu::ShaderProgramInfo* buildInfo, const SpirvValidatorOptions& options)
160e5c31af7Sopenharmony_ci{
161e5c31af7Sopenharmony_ci	std::ostringstream validationLog;
162e5c31af7Sopenharmony_ci
163e5c31af7Sopenharmony_ci	if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
164e5c31af7Sopenharmony_ci	{
165e5c31af7Sopenharmony_ci		buildInfo->program.linkOk	 = false;
166e5c31af7Sopenharmony_ci		buildInfo->program.infoLog	+= "\n" + validationLog.str();
167e5c31af7Sopenharmony_ci
168e5c31af7Sopenharmony_ci		TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
169e5c31af7Sopenharmony_ci	}
170e5c31af7Sopenharmony_ci}
171e5c31af7Sopenharmony_ci
172e5c31af7Sopenharmony_civoid validateCompiledBinary(const vector<deUint32>& binary, SpirVProgramInfo* buildInfo, const SpirvValidatorOptions& options)
173e5c31af7Sopenharmony_ci{
174e5c31af7Sopenharmony_ci	std::ostringstream validationLog;
175e5c31af7Sopenharmony_ci
176e5c31af7Sopenharmony_ci	if (!validateSpirV(binary.size(), &binary[0], &validationLog, options))
177e5c31af7Sopenharmony_ci	{
178e5c31af7Sopenharmony_ci		buildInfo->compileOk = false;
179e5c31af7Sopenharmony_ci		buildInfo->infoLog += "\n" + validationLog.str();
180e5c31af7Sopenharmony_ci
181e5c31af7Sopenharmony_ci		TCU_THROW(InternalError, "Validation failed for compiled SPIR-V binary");
182e5c31af7Sopenharmony_ci	}
183e5c31af7Sopenharmony_ci}
184e5c31af7Sopenharmony_ci
185e5c31af7Sopenharmony_ci// IPC functions
186e5c31af7Sopenharmony_ci#ifndef DISABLE_SHADERCACHE_IPC
187e5c31af7Sopenharmony_ci#include "vkIPC.inl"
188e5c31af7Sopenharmony_ci#endif
189e5c31af7Sopenharmony_ci
190e5c31af7Sopenharmony_ci// Overridable wrapper for de::Mutex
191e5c31af7Sopenharmony_ciclass cacheMutex
192e5c31af7Sopenharmony_ci{
193e5c31af7Sopenharmony_cipublic:
194e5c31af7Sopenharmony_ci	cacheMutex () {}
195e5c31af7Sopenharmony_ci	virtual ~cacheMutex () {}
196e5c31af7Sopenharmony_ci	virtual void lock () { localMutex.lock(); }
197e5c31af7Sopenharmony_ci	virtual void unlock () { localMutex.unlock(); }
198e5c31af7Sopenharmony_ciprivate:
199e5c31af7Sopenharmony_ci	de::Mutex localMutex;
200e5c31af7Sopenharmony_ci};
201e5c31af7Sopenharmony_ci
202e5c31af7Sopenharmony_ci#ifndef DISABLE_SHADERCACHE_IPC
203e5c31af7Sopenharmony_ci// Overriden cacheMutex that uses IPC instead
204e5c31af7Sopenharmony_ciclass cacheMutexIPC : public cacheMutex
205e5c31af7Sopenharmony_ci{
206e5c31af7Sopenharmony_cipublic:
207e5c31af7Sopenharmony_ci	cacheMutexIPC ()
208e5c31af7Sopenharmony_ci	{
209e5c31af7Sopenharmony_ci		ipc_sem_init(&guard, "cts_shadercache_ipc_guard");
210e5c31af7Sopenharmony_ci		ipc_sem_create(&guard, 1);
211e5c31af7Sopenharmony_ci	}
212e5c31af7Sopenharmony_ci	virtual ~cacheMutexIPC ()
213e5c31af7Sopenharmony_ci	{
214e5c31af7Sopenharmony_ci		ipc_sem_close(&guard);
215e5c31af7Sopenharmony_ci	}
216e5c31af7Sopenharmony_ci	virtual void lock () { ipc_sem_decrement(&guard); }
217e5c31af7Sopenharmony_ci	virtual void unlock () { ipc_sem_increment(&guard); }
218e5c31af7Sopenharmony_ciprivate:
219e5c31af7Sopenharmony_ci	ipc_sharedsemaphore guard;
220e5c31af7Sopenharmony_ci};
221e5c31af7Sopenharmony_ci#endif
222e5c31af7Sopenharmony_ci
223e5c31af7Sopenharmony_ci// Each cache node takes 4 * 4 = 16 bytes; 1M items takes 16M memory.
224e5c31af7Sopenharmony_ciconst deUint32						cacheMaxItems		= 1024 * 1024;
225e5c31af7Sopenharmony_cicacheMutex*							cacheFileMutex		= nullptr;
226e5c31af7Sopenharmony_cideUint32*							cacheMempool		= nullptr;
227e5c31af7Sopenharmony_ci#ifndef DISABLE_SHADERCACHE_IPC
228e5c31af7Sopenharmony_ciipc_sharedmemory					cacheIPCMemory;
229e5c31af7Sopenharmony_ci#endif
230e5c31af7Sopenharmony_ci
231e5c31af7Sopenharmony_cistruct cacheNode
232e5c31af7Sopenharmony_ci{
233e5c31af7Sopenharmony_ci	deUint32 key;
234e5c31af7Sopenharmony_ci	deUint32 data;
235e5c31af7Sopenharmony_ci	deUint32 right_child;
236e5c31af7Sopenharmony_ci	deUint32 left_child;
237e5c31af7Sopenharmony_ci};
238e5c31af7Sopenharmony_ci
239e5c31af7Sopenharmony_cicacheNode* cacheSearch (deUint32 key)
240e5c31af7Sopenharmony_ci{
241e5c31af7Sopenharmony_ci	cacheNode*		r		= (cacheNode*)(cacheMempool + 1);
242e5c31af7Sopenharmony_ci	int*			tail	= (int*)cacheMempool;
243e5c31af7Sopenharmony_ci	unsigned int	p		= 0;
244e5c31af7Sopenharmony_ci
245e5c31af7Sopenharmony_ci	if (!*tail) {
246e5c31af7Sopenharmony_ci		// Cache is empty.
247e5c31af7Sopenharmony_ci		return 0;
248e5c31af7Sopenharmony_ci	}
249e5c31af7Sopenharmony_ci
250e5c31af7Sopenharmony_ci	while (1)
251e5c31af7Sopenharmony_ci	{
252e5c31af7Sopenharmony_ci		if (r[p].key == key)
253e5c31af7Sopenharmony_ci			return &r[p];
254e5c31af7Sopenharmony_ci
255e5c31af7Sopenharmony_ci		if (key > r[p].key)
256e5c31af7Sopenharmony_ci			p = r[p].right_child;
257e5c31af7Sopenharmony_ci		else
258e5c31af7Sopenharmony_ci			p = r[p].left_child;
259e5c31af7Sopenharmony_ci
260e5c31af7Sopenharmony_ci		if (p == 0)
261e5c31af7Sopenharmony_ci			return 0;
262e5c31af7Sopenharmony_ci	}
263e5c31af7Sopenharmony_ci}
264e5c31af7Sopenharmony_ci
265e5c31af7Sopenharmony_civoid cacheInsert (deUint32 key, deUint32 data)
266e5c31af7Sopenharmony_ci{
267e5c31af7Sopenharmony_ci	cacheNode*	r		= (cacheNode*)(cacheMempool + 1);
268e5c31af7Sopenharmony_ci	int*		tail	= (int*)cacheMempool;
269e5c31af7Sopenharmony_ci	int			newnode	= *tail;
270e5c31af7Sopenharmony_ci
271e5c31af7Sopenharmony_ci	DE_ASSERT(newnode < cacheMaxItems);
272e5c31af7Sopenharmony_ci
273e5c31af7Sopenharmony_ci	// If we run out of cache space, reset the cache index.
274e5c31af7Sopenharmony_ci	if (newnode >= cacheMaxItems)
275e5c31af7Sopenharmony_ci	{
276e5c31af7Sopenharmony_ci		*tail = 0;
277e5c31af7Sopenharmony_ci		newnode = 0;
278e5c31af7Sopenharmony_ci	}
279e5c31af7Sopenharmony_ci
280e5c31af7Sopenharmony_ci	r[*tail].data = data;
281e5c31af7Sopenharmony_ci	r[*tail].key = key;
282e5c31af7Sopenharmony_ci	r[*tail].left_child = 0;
283e5c31af7Sopenharmony_ci	r[*tail].right_child = 0;
284e5c31af7Sopenharmony_ci
285e5c31af7Sopenharmony_ci	(*tail)++;
286e5c31af7Sopenharmony_ci
287e5c31af7Sopenharmony_ci	if (newnode == 0)
288e5c31af7Sopenharmony_ci	{
289e5c31af7Sopenharmony_ci		// first
290e5c31af7Sopenharmony_ci		return;
291e5c31af7Sopenharmony_ci	}
292e5c31af7Sopenharmony_ci
293e5c31af7Sopenharmony_ci	int p = 0;
294e5c31af7Sopenharmony_ci	while (1)
295e5c31af7Sopenharmony_ci	{
296e5c31af7Sopenharmony_ci		if (r[p].key == key)
297e5c31af7Sopenharmony_ci		{
298e5c31af7Sopenharmony_ci			// collision; use the latest data
299e5c31af7Sopenharmony_ci			r[p].data = data;
300e5c31af7Sopenharmony_ci			(*tail)--;
301e5c31af7Sopenharmony_ci			return;
302e5c31af7Sopenharmony_ci		}
303e5c31af7Sopenharmony_ci
304e5c31af7Sopenharmony_ci		if (key > r[p].key)
305e5c31af7Sopenharmony_ci		{
306e5c31af7Sopenharmony_ci			if (r[p].right_child != 0)
307e5c31af7Sopenharmony_ci			{
308e5c31af7Sopenharmony_ci				p = r[p].right_child;
309e5c31af7Sopenharmony_ci			}
310e5c31af7Sopenharmony_ci			else
311e5c31af7Sopenharmony_ci			{
312e5c31af7Sopenharmony_ci				r[p].right_child = newnode;
313e5c31af7Sopenharmony_ci				return;
314e5c31af7Sopenharmony_ci			}
315e5c31af7Sopenharmony_ci		}
316e5c31af7Sopenharmony_ci		else
317e5c31af7Sopenharmony_ci		{
318e5c31af7Sopenharmony_ci			if (r[p].left_child != 0)
319e5c31af7Sopenharmony_ci			{
320e5c31af7Sopenharmony_ci				p = r[p].left_child;
321e5c31af7Sopenharmony_ci			}
322e5c31af7Sopenharmony_ci			else
323e5c31af7Sopenharmony_ci			{
324e5c31af7Sopenharmony_ci				r[p].left_child = newnode;
325e5c31af7Sopenharmony_ci				return;
326e5c31af7Sopenharmony_ci			}
327e5c31af7Sopenharmony_ci		}
328e5c31af7Sopenharmony_ci	}
329e5c31af7Sopenharmony_ci}
330e5c31af7Sopenharmony_ci
331e5c31af7Sopenharmony_ci// Called via atexit()
332e5c31af7Sopenharmony_civoid shaderCacheClean ()
333e5c31af7Sopenharmony_ci{
334e5c31af7Sopenharmony_ci	delete cacheFileMutex;
335e5c31af7Sopenharmony_ci	delete[] cacheMempool;
336e5c31af7Sopenharmony_ci}
337e5c31af7Sopenharmony_ci
338e5c31af7Sopenharmony_ci#ifndef DISABLE_SHADERCACHE_IPC
339e5c31af7Sopenharmony_ci// Called via atexit()
340e5c31af7Sopenharmony_civoid shaderCacheCleanIPC ()
341e5c31af7Sopenharmony_ci{
342e5c31af7Sopenharmony_ci	delete cacheFileMutex;
343e5c31af7Sopenharmony_ci	ipc_mem_close(&cacheIPCMemory);
344e5c31af7Sopenharmony_ci}
345e5c31af7Sopenharmony_ci#endif
346e5c31af7Sopenharmony_ci
347e5c31af7Sopenharmony_civoid shaderCacheFirstRunCheck (const tcu::CommandLine& commandLine)
348e5c31af7Sopenharmony_ci{
349e5c31af7Sopenharmony_ci	bool first = true;
350e5c31af7Sopenharmony_ci
351e5c31af7Sopenharmony_ci	// We need to solve two problems here:
352e5c31af7Sopenharmony_ci	// 1) The cache and cache mutex only have to be initialized once by the first thread that arrives here.
353e5c31af7Sopenharmony_ci	// 2) We must prevent other threads from exiting early from this function thinking they don't have to initialize the cache and
354e5c31af7Sopenharmony_ci	//    cache mutex, only to try to lock the cache mutex while the first thread is still initializing it. To prevent this, we must
355e5c31af7Sopenharmony_ci	//    hold an initialization mutex (declared below) while initializing the cache and cache mutex, making other threads wait.
356e5c31af7Sopenharmony_ci
357e5c31af7Sopenharmony_ci	// Used to check and set cacheFileFirstRun. We make it static, and C++11 guarantees it will only be initialized once.
358e5c31af7Sopenharmony_ci	static std::mutex cacheFileFirstRunMutex;
359e5c31af7Sopenharmony_ci	static bool cacheFileFirstRun = true;
360e5c31af7Sopenharmony_ci
361e5c31af7Sopenharmony_ci	// Is cacheFileFirstRun true for this thread?
362e5c31af7Sopenharmony_ci	bool needInit = false;
363e5c31af7Sopenharmony_ci
364e5c31af7Sopenharmony_ci	// Check cacheFileFirstRun only while holding the mutex, and hold it while initializing the cache.
365e5c31af7Sopenharmony_ci	const std::lock_guard<std::mutex> lock(cacheFileFirstRunMutex);
366e5c31af7Sopenharmony_ci	if (cacheFileFirstRun)
367e5c31af7Sopenharmony_ci	{
368e5c31af7Sopenharmony_ci		needInit = true;
369e5c31af7Sopenharmony_ci		cacheFileFirstRun = false;
370e5c31af7Sopenharmony_ci	}
371e5c31af7Sopenharmony_ci
372e5c31af7Sopenharmony_ci	if (needInit)
373e5c31af7Sopenharmony_ci	{
374e5c31af7Sopenharmony_ci#ifndef DISABLE_SHADERCACHE_IPC
375e5c31af7Sopenharmony_ci		if (commandLine.isShaderCacheIPCEnabled())
376e5c31af7Sopenharmony_ci		{
377e5c31af7Sopenharmony_ci			// IPC path, allocate shared mutex and shared memory
378e5c31af7Sopenharmony_ci			cacheFileMutex = new cacheMutexIPC;
379e5c31af7Sopenharmony_ci			cacheFileMutex->lock();
380e5c31af7Sopenharmony_ci			ipc_mem_init(&cacheIPCMemory, "cts_shadercache_memory", sizeof(deUint32) * (cacheMaxItems * 4 + 1));
381e5c31af7Sopenharmony_ci			if (ipc_mem_open_existing(&cacheIPCMemory) != 0)
382e5c31af7Sopenharmony_ci			{
383e5c31af7Sopenharmony_ci				ipc_mem_create(&cacheIPCMemory);
384e5c31af7Sopenharmony_ci				cacheMempool = (deUint32*)ipc_mem_access(&cacheIPCMemory);
385e5c31af7Sopenharmony_ci				cacheMempool[0] = 0;
386e5c31af7Sopenharmony_ci			}
387e5c31af7Sopenharmony_ci			else
388e5c31af7Sopenharmony_ci			{
389e5c31af7Sopenharmony_ci				cacheMempool = (deUint32*)ipc_mem_access(&cacheIPCMemory);
390e5c31af7Sopenharmony_ci				first = false;
391e5c31af7Sopenharmony_ci			}
392e5c31af7Sopenharmony_ci			atexit(shaderCacheCleanIPC);
393e5c31af7Sopenharmony_ci		}
394e5c31af7Sopenharmony_ci		else
395e5c31af7Sopenharmony_ci#endif
396e5c31af7Sopenharmony_ci		{
397e5c31af7Sopenharmony_ci			// Non-IPC path, allocate local mutex and memory
398e5c31af7Sopenharmony_ci			cacheFileMutex = new cacheMutex;
399e5c31af7Sopenharmony_ci			cacheFileMutex->lock();
400e5c31af7Sopenharmony_ci			cacheMempool = new deUint32[cacheMaxItems * 4 + 1];
401e5c31af7Sopenharmony_ci			cacheMempool[0] = 0;
402e5c31af7Sopenharmony_ci
403e5c31af7Sopenharmony_ci			atexit(shaderCacheClean);
404e5c31af7Sopenharmony_ci		}
405e5c31af7Sopenharmony_ci
406e5c31af7Sopenharmony_ci		if (first)
407e5c31af7Sopenharmony_ci		{
408e5c31af7Sopenharmony_ci			if (commandLine.isShaderCacheTruncateEnabled())
409e5c31af7Sopenharmony_ci			{
410e5c31af7Sopenharmony_ci				// Open file with "w" access to truncate it
411e5c31af7Sopenharmony_ci				FILE* f = fopen(commandLine.getShaderCacheFilename(), "wb");
412e5c31af7Sopenharmony_ci				if (f)
413e5c31af7Sopenharmony_ci					fclose(f);
414e5c31af7Sopenharmony_ci			}
415e5c31af7Sopenharmony_ci			else
416e5c31af7Sopenharmony_ci			{
417e5c31af7Sopenharmony_ci				// Parse chunked shader cache file for hashes and offsets
418e5c31af7Sopenharmony_ci				FILE* file	= fopen(commandLine.getShaderCacheFilename(), "rb");
419e5c31af7Sopenharmony_ci				int count	= 0;
420e5c31af7Sopenharmony_ci				if (file)
421e5c31af7Sopenharmony_ci				{
422e5c31af7Sopenharmony_ci					deUint32 chunksize	= 0;
423e5c31af7Sopenharmony_ci					deUint32 hash		= 0;
424e5c31af7Sopenharmony_ci					deUint32 offset		= 0;
425e5c31af7Sopenharmony_ci					bool ok				= true;
426e5c31af7Sopenharmony_ci					while (ok)
427e5c31af7Sopenharmony_ci					{
428e5c31af7Sopenharmony_ci						offset = (deUint32)ftell(file);
429e5c31af7Sopenharmony_ci						if (ok) ok = fread(&chunksize, 1, 4, file)				== 4;
430e5c31af7Sopenharmony_ci						if (ok) ok = fread(&hash, 1, 4, file)					== 4;
431e5c31af7Sopenharmony_ci						if (ok) cacheInsert(hash, offset);
432e5c31af7Sopenharmony_ci						if (ok) ok = fseek(file, offset + chunksize, SEEK_SET)	== 0;
433e5c31af7Sopenharmony_ci						count++;
434e5c31af7Sopenharmony_ci					}
435e5c31af7Sopenharmony_ci					fclose(file);
436e5c31af7Sopenharmony_ci				}
437e5c31af7Sopenharmony_ci			}
438e5c31af7Sopenharmony_ci		}
439e5c31af7Sopenharmony_ci		cacheFileMutex->unlock();
440e5c31af7Sopenharmony_ci	}
441e5c31af7Sopenharmony_ci}
442e5c31af7Sopenharmony_ci
443e5c31af7Sopenharmony_cistd::string intToString (deUint32 integer)
444e5c31af7Sopenharmony_ci{
445e5c31af7Sopenharmony_ci	std::stringstream temp_sstream;
446e5c31af7Sopenharmony_ci
447e5c31af7Sopenharmony_ci	temp_sstream << integer;
448e5c31af7Sopenharmony_ci
449e5c31af7Sopenharmony_ci	return temp_sstream.str();
450e5c31af7Sopenharmony_ci}
451e5c31af7Sopenharmony_ci
452e5c31af7Sopenharmony_ci// 32-bit FNV-1 hash
453e5c31af7Sopenharmony_cideUint32 shadercacheHash (const char* str)
454e5c31af7Sopenharmony_ci{
455e5c31af7Sopenharmony_ci	deUint32 hash = 0x811c9dc5;
456e5c31af7Sopenharmony_ci	deUint32 c;
457e5c31af7Sopenharmony_ci	while ((c = (deUint32)*str++) != 0)
458e5c31af7Sopenharmony_ci	{
459e5c31af7Sopenharmony_ci		hash *= 16777619;
460e5c31af7Sopenharmony_ci		hash ^= c;
461e5c31af7Sopenharmony_ci	}
462e5c31af7Sopenharmony_ci	return hash;
463e5c31af7Sopenharmony_ci}
464e5c31af7Sopenharmony_ci
465e5c31af7Sopenharmony_civk::ProgramBinary* shadercacheLoad (const std::string& shaderstring, const char* shaderCacheFilename, deUint32 hash)
466e5c31af7Sopenharmony_ci{
467e5c31af7Sopenharmony_ci	deInt32			format;
468e5c31af7Sopenharmony_ci	deInt32			length;
469e5c31af7Sopenharmony_ci	deInt32			sourcelength;
470e5c31af7Sopenharmony_ci	deUint32		temp;
471e5c31af7Sopenharmony_ci	deUint8*		bin			= 0;
472e5c31af7Sopenharmony_ci	char*			source		= 0;
473e5c31af7Sopenharmony_ci	deBool			ok			= true;
474e5c31af7Sopenharmony_ci	deBool			diff		= true;
475e5c31af7Sopenharmony_ci	cacheNode*		node		= 0;
476e5c31af7Sopenharmony_ci	cacheFileMutex->lock();
477e5c31af7Sopenharmony_ci
478e5c31af7Sopenharmony_ci	node = cacheSearch(hash);
479e5c31af7Sopenharmony_ci	if (node == 0)
480e5c31af7Sopenharmony_ci	{
481e5c31af7Sopenharmony_ci		cacheFileMutex->unlock();
482e5c31af7Sopenharmony_ci		return 0;
483e5c31af7Sopenharmony_ci	}
484e5c31af7Sopenharmony_ci	FILE*			file		= fopen(shaderCacheFilename, "rb");
485e5c31af7Sopenharmony_ci	ok				= file										!= 0;
486e5c31af7Sopenharmony_ci
487e5c31af7Sopenharmony_ci	if (ok) ok = fseek(file, node->data, SEEK_SET)				== 0;
488e5c31af7Sopenharmony_ci	if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Chunk size (skip)
489e5c31af7Sopenharmony_ci	if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Stored hash
490e5c31af7Sopenharmony_ci	if (ok) ok = temp											== hash; // Double check
491e5c31af7Sopenharmony_ci	if (ok) ok = fread(&format, 1, 4, file)						== 4;
492e5c31af7Sopenharmony_ci	if (ok) ok = fread(&length, 1, 4, file)						== 4;
493e5c31af7Sopenharmony_ci	if (ok) ok = length											> 0; // Quick check
494e5c31af7Sopenharmony_ci	if (ok) bin = new deUint8[length];
495e5c31af7Sopenharmony_ci	if (ok) ok = fread(bin, 1, length, file)					== (size_t)length;
496e5c31af7Sopenharmony_ci	if (ok) ok = fread(&sourcelength, 1, 4, file)				== 4;
497e5c31af7Sopenharmony_ci	if (ok && sourcelength > 0)
498e5c31af7Sopenharmony_ci	{
499e5c31af7Sopenharmony_ci		source = new char[sourcelength + 1];
500e5c31af7Sopenharmony_ci		ok = fread(source, 1, sourcelength, file)				== (size_t)sourcelength;
501e5c31af7Sopenharmony_ci		source[sourcelength] = 0;
502e5c31af7Sopenharmony_ci		diff = shaderstring != std::string(source);
503e5c31af7Sopenharmony_ci	}
504e5c31af7Sopenharmony_ci	if (!ok || diff)
505e5c31af7Sopenharmony_ci	{
506e5c31af7Sopenharmony_ci		// Mismatch
507e5c31af7Sopenharmony_ci		delete[] source;
508e5c31af7Sopenharmony_ci		delete[] bin;
509e5c31af7Sopenharmony_ci	}
510e5c31af7Sopenharmony_ci	else
511e5c31af7Sopenharmony_ci	{
512e5c31af7Sopenharmony_ci		delete[] source;
513e5c31af7Sopenharmony_ci		if (file) fclose(file);
514e5c31af7Sopenharmony_ci		cacheFileMutex->unlock();
515e5c31af7Sopenharmony_ci		vk::ProgramBinary* res = new vk::ProgramBinary((vk::ProgramFormat)format, length, bin);
516e5c31af7Sopenharmony_ci		delete[] bin;
517e5c31af7Sopenharmony_ci		return res;
518e5c31af7Sopenharmony_ci	}
519e5c31af7Sopenharmony_ci	if (file) fclose(file);
520e5c31af7Sopenharmony_ci	cacheFileMutex->unlock();
521e5c31af7Sopenharmony_ci	return 0;
522e5c31af7Sopenharmony_ci}
523e5c31af7Sopenharmony_ci
524e5c31af7Sopenharmony_civoid shadercacheSave (const vk::ProgramBinary* binary, const std::string& shaderstring, const char* shaderCacheFilename, deUint32 hash)
525e5c31af7Sopenharmony_ci{
526e5c31af7Sopenharmony_ci	if (binary == 0)
527e5c31af7Sopenharmony_ci		return;
528e5c31af7Sopenharmony_ci	deInt32				format		= binary->getFormat();
529e5c31af7Sopenharmony_ci	deUint32			length		= (deUint32)binary->getSize();
530e5c31af7Sopenharmony_ci	deUint32			chunksize;
531e5c31af7Sopenharmony_ci	deUint32			offset;
532e5c31af7Sopenharmony_ci	const deUint8*		bin			= binary->getBinary();
533e5c31af7Sopenharmony_ci	const de::FilePath	filePath	(shaderCacheFilename);
534e5c31af7Sopenharmony_ci	cacheNode*			node		= 0;
535e5c31af7Sopenharmony_ci
536e5c31af7Sopenharmony_ci	cacheFileMutex->lock();
537e5c31af7Sopenharmony_ci
538e5c31af7Sopenharmony_ci	node = cacheSearch(hash);
539e5c31af7Sopenharmony_ci
540e5c31af7Sopenharmony_ci	if (node)
541e5c31af7Sopenharmony_ci	{
542e5c31af7Sopenharmony_ci		FILE*			file		= fopen(shaderCacheFilename, "rb");
543e5c31af7Sopenharmony_ci		deBool			ok			= (file != 0);
544e5c31af7Sopenharmony_ci		deBool			diff		= DE_TRUE;
545e5c31af7Sopenharmony_ci		deInt32			sourcelength;
546e5c31af7Sopenharmony_ci		deUint32		temp;
547e5c31af7Sopenharmony_ci
548e5c31af7Sopenharmony_ci		deUint32	cachedLength	= 0;
549e5c31af7Sopenharmony_ci
550e5c31af7Sopenharmony_ci		if (ok) ok = fseek(file, node->data, SEEK_SET)				== 0;
551e5c31af7Sopenharmony_ci		if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Chunk size (skip)
552e5c31af7Sopenharmony_ci		if (ok) ok = fread(&temp, 1, 4, file)						== 4; // Stored hash
553e5c31af7Sopenharmony_ci		if (ok) ok = temp											== hash; // Double check
554e5c31af7Sopenharmony_ci		if (ok) ok = fread(&temp, 1, 4, file)						== 4;
555e5c31af7Sopenharmony_ci		if (ok) ok = fread(&cachedLength, 1, 4, file)				== 4;
556e5c31af7Sopenharmony_ci		if (ok) ok = cachedLength									> 0; // Quick check
557e5c31af7Sopenharmony_ci		if (ok) fseek(file, cachedLength, SEEK_CUR); // skip binary
558e5c31af7Sopenharmony_ci		if (ok) ok = fread(&sourcelength, 1, 4, file)				== 4;
559e5c31af7Sopenharmony_ci
560e5c31af7Sopenharmony_ci		if (ok && sourcelength > 0)
561e5c31af7Sopenharmony_ci		{
562e5c31af7Sopenharmony_ci			char* source;
563e5c31af7Sopenharmony_ci			source	= new char[sourcelength + 1];
564e5c31af7Sopenharmony_ci			ok		= fread(source, 1, sourcelength, file)			== (size_t)sourcelength;
565e5c31af7Sopenharmony_ci			source[sourcelength] = 0;
566e5c31af7Sopenharmony_ci			diff	= shaderstring != std::string(source);
567e5c31af7Sopenharmony_ci			delete[] source;
568e5c31af7Sopenharmony_ci		}
569e5c31af7Sopenharmony_ci
570e5c31af7Sopenharmony_ci		if (ok && !diff)
571e5c31af7Sopenharmony_ci		{
572e5c31af7Sopenharmony_ci			// Already in cache (written by another thread, probably)
573e5c31af7Sopenharmony_ci			fclose(file);
574e5c31af7Sopenharmony_ci			cacheFileMutex->unlock();
575e5c31af7Sopenharmony_ci			return;
576e5c31af7Sopenharmony_ci		}
577e5c31af7Sopenharmony_ci		fclose(file);
578e5c31af7Sopenharmony_ci	}
579e5c31af7Sopenharmony_ci
580e5c31af7Sopenharmony_ci	if (!de::FilePath(filePath.getDirName()).exists())
581e5c31af7Sopenharmony_ci		de::createDirectoryAndParents(filePath.getDirName().c_str());
582e5c31af7Sopenharmony_ci
583e5c31af7Sopenharmony_ci	FILE*				file		= fopen(shaderCacheFilename, "ab");
584e5c31af7Sopenharmony_ci	if (!file)
585e5c31af7Sopenharmony_ci	{
586e5c31af7Sopenharmony_ci		cacheFileMutex->unlock();
587e5c31af7Sopenharmony_ci		return;
588e5c31af7Sopenharmony_ci	}
589e5c31af7Sopenharmony_ci	// Append mode starts writing from the end of the file,
590e5c31af7Sopenharmony_ci	// but unless we do a seek, ftell returns 0.
591e5c31af7Sopenharmony_ci	fseek(file, 0, SEEK_END);
592e5c31af7Sopenharmony_ci	offset		= (deUint32)ftell(file);
593e5c31af7Sopenharmony_ci	chunksize	= 4 + 4 + 4 + 4 + length + 4 + (deUint32)shaderstring.length();
594e5c31af7Sopenharmony_ci	fwrite(&chunksize, 1, 4, file);
595e5c31af7Sopenharmony_ci	fwrite(&hash, 1, 4, file);
596e5c31af7Sopenharmony_ci	fwrite(&format, 1, 4, file);
597e5c31af7Sopenharmony_ci	fwrite(&length, 1, 4, file);
598e5c31af7Sopenharmony_ci	fwrite(bin, 1, length, file);
599e5c31af7Sopenharmony_ci	length = (deUint32)shaderstring.length();
600e5c31af7Sopenharmony_ci	fwrite(&length, 1, 4, file);
601e5c31af7Sopenharmony_ci	fwrite(shaderstring.c_str(), 1, length, file);
602e5c31af7Sopenharmony_ci	fclose(file);
603e5c31af7Sopenharmony_ci	cacheInsert(hash, offset);
604e5c31af7Sopenharmony_ci
605e5c31af7Sopenharmony_ci	cacheFileMutex->unlock();
606e5c31af7Sopenharmony_ci}
607e5c31af7Sopenharmony_ci
608e5c31af7Sopenharmony_ci// Insert any information that may affect compilation into the shader string.
609e5c31af7Sopenharmony_civoid getCompileEnvironment (std::string& shaderstring)
610e5c31af7Sopenharmony_ci{
611e5c31af7Sopenharmony_ci	shaderstring += "GLSL:";
612e5c31af7Sopenharmony_ci	shaderstring += qpGetReleaseGlslName();
613e5c31af7Sopenharmony_ci	shaderstring += "\nSpir-v Tools:";
614e5c31af7Sopenharmony_ci	shaderstring += qpGetReleaseSpirvToolsName();
615e5c31af7Sopenharmony_ci	shaderstring += "\nSpir-v Headers:";
616e5c31af7Sopenharmony_ci	shaderstring += qpGetReleaseSpirvHeadersName();
617e5c31af7Sopenharmony_ci	shaderstring += "\n";
618e5c31af7Sopenharmony_ci}
619e5c31af7Sopenharmony_ci
620e5c31af7Sopenharmony_ci// Insert compilation options into the shader string.
621e5c31af7Sopenharmony_civoid getBuildOptions (std::string& shaderstring, const ShaderBuildOptions& buildOptions, int optimizationRecipe)
622e5c31af7Sopenharmony_ci{
623e5c31af7Sopenharmony_ci	shaderstring += "Target Spir-V ";
624e5c31af7Sopenharmony_ci	shaderstring += getSpirvVersionName(buildOptions.targetVersion);
625e5c31af7Sopenharmony_ci	shaderstring += "\n";
626e5c31af7Sopenharmony_ci	if (buildOptions.flags & ShaderBuildOptions::FLAG_ALLOW_RELAXED_OFFSETS)
627e5c31af7Sopenharmony_ci		shaderstring += "Flag:Allow relaxed offsets\n";
628e5c31af7Sopenharmony_ci	if (buildOptions.flags & ShaderBuildOptions::FLAG_USE_STORAGE_BUFFER_STORAGE_CLASS)
629e5c31af7Sopenharmony_ci		shaderstring += "Flag:Use storage buffer storage class\n";
630e5c31af7Sopenharmony_ci	if (optimizationRecipe != 0)
631e5c31af7Sopenharmony_ci	{
632e5c31af7Sopenharmony_ci		shaderstring += "Optimization recipe ";
633e5c31af7Sopenharmony_ci		shaderstring += de::toString(optimizationRecipe);
634e5c31af7Sopenharmony_ci		shaderstring += "\n";
635e5c31af7Sopenharmony_ci	}
636e5c31af7Sopenharmony_ci}
637e5c31af7Sopenharmony_ci
638e5c31af7Sopenharmony_ciProgramBinary* buildProgram (const GlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
639e5c31af7Sopenharmony_ci{
640e5c31af7Sopenharmony_ci	const SpirvVersion	spirvVersion		= program.buildOptions.targetVersion;
641e5c31af7Sopenharmony_ci	const bool			validateBinary		= VALIDATE_BINARIES;
642e5c31af7Sopenharmony_ci	vector<deUint32>	binary;
643e5c31af7Sopenharmony_ci	std::string			cachekey;
644e5c31af7Sopenharmony_ci	std::string			shaderstring;
645e5c31af7Sopenharmony_ci	vk::ProgramBinary*	res					= 0;
646e5c31af7Sopenharmony_ci	const int			optimizationRecipe	= commandLine.getOptimizationRecipe();
647e5c31af7Sopenharmony_ci	deUint32			hash				= 0;
648e5c31af7Sopenharmony_ci
649e5c31af7Sopenharmony_ci	if (commandLine.isShadercacheEnabled())
650e5c31af7Sopenharmony_ci	{
651e5c31af7Sopenharmony_ci		shaderCacheFirstRunCheck(commandLine);
652e5c31af7Sopenharmony_ci		getCompileEnvironment(cachekey);
653e5c31af7Sopenharmony_ci		getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
654e5c31af7Sopenharmony_ci
655e5c31af7Sopenharmony_ci		for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
656e5c31af7Sopenharmony_ci		{
657e5c31af7Sopenharmony_ci			if (!program.sources[i].empty())
658e5c31af7Sopenharmony_ci			{
659e5c31af7Sopenharmony_ci				cachekey += glu::getShaderTypeName((glu::ShaderType)i);
660e5c31af7Sopenharmony_ci
661e5c31af7Sopenharmony_ci				for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
662e5c31af7Sopenharmony_ci					shaderstring += *it;
663e5c31af7Sopenharmony_ci			}
664e5c31af7Sopenharmony_ci		}
665e5c31af7Sopenharmony_ci
666e5c31af7Sopenharmony_ci		cachekey = cachekey + shaderstring;
667e5c31af7Sopenharmony_ci
668e5c31af7Sopenharmony_ci		hash = shadercacheHash(cachekey.c_str());
669e5c31af7Sopenharmony_ci
670e5c31af7Sopenharmony_ci		res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename(), hash);
671e5c31af7Sopenharmony_ci
672e5c31af7Sopenharmony_ci		if (res)
673e5c31af7Sopenharmony_ci		{
674e5c31af7Sopenharmony_ci			buildInfo->program.infoLog		= "Loaded from cache";
675e5c31af7Sopenharmony_ci			buildInfo->program.linkOk		= true;
676e5c31af7Sopenharmony_ci			buildInfo->program.linkTimeUs	= 0;
677e5c31af7Sopenharmony_ci
678e5c31af7Sopenharmony_ci			for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
679e5c31af7Sopenharmony_ci			{
680e5c31af7Sopenharmony_ci				if (!program.sources[shaderType].empty())
681e5c31af7Sopenharmony_ci				{
682e5c31af7Sopenharmony_ci					glu::ShaderInfo	shaderBuildInfo;
683e5c31af7Sopenharmony_ci
684e5c31af7Sopenharmony_ci					shaderBuildInfo.type			= (glu::ShaderType)shaderType;
685e5c31af7Sopenharmony_ci					shaderBuildInfo.source			= shaderstring;
686e5c31af7Sopenharmony_ci					shaderBuildInfo.compileTimeUs	= 0;
687e5c31af7Sopenharmony_ci					shaderBuildInfo.compileOk		= true;
688e5c31af7Sopenharmony_ci
689e5c31af7Sopenharmony_ci					buildInfo->shaders.push_back(shaderBuildInfo);
690e5c31af7Sopenharmony_ci				}
691e5c31af7Sopenharmony_ci			}
692e5c31af7Sopenharmony_ci		}
693e5c31af7Sopenharmony_ci	}
694e5c31af7Sopenharmony_ci
695e5c31af7Sopenharmony_ci	if (!res)
696e5c31af7Sopenharmony_ci	{
697e5c31af7Sopenharmony_ci		{
698e5c31af7Sopenharmony_ci			vector<deUint32> nonStrippedBinary;
699e5c31af7Sopenharmony_ci
700e5c31af7Sopenharmony_ci			if (!compileGlslToSpirV(program, &nonStrippedBinary, buildInfo))
701e5c31af7Sopenharmony_ci				TCU_THROW(InternalError, "Compiling GLSL to SPIR-V failed");
702e5c31af7Sopenharmony_ci
703e5c31af7Sopenharmony_ci			TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
704e5c31af7Sopenharmony_ci			stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
705e5c31af7Sopenharmony_ci			TCU_CHECK_INTERNAL(!binary.empty());
706e5c31af7Sopenharmony_ci		}
707e5c31af7Sopenharmony_ci
708e5c31af7Sopenharmony_ci		if (optimizationRecipe != 0)
709e5c31af7Sopenharmony_ci		{
710e5c31af7Sopenharmony_ci			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
711e5c31af7Sopenharmony_ci			optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
712e5c31af7Sopenharmony_ci		}
713e5c31af7Sopenharmony_ci
714e5c31af7Sopenharmony_ci		if (validateBinary)
715e5c31af7Sopenharmony_ci		{
716e5c31af7Sopenharmony_ci			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
717e5c31af7Sopenharmony_ci		}
718e5c31af7Sopenharmony_ci
719e5c31af7Sopenharmony_ci		res = createProgramBinaryFromSpirV(binary);
720e5c31af7Sopenharmony_ci		if (commandLine.isShadercacheEnabled())
721e5c31af7Sopenharmony_ci			shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename(), hash);
722e5c31af7Sopenharmony_ci	}
723e5c31af7Sopenharmony_ci	return res;
724e5c31af7Sopenharmony_ci}
725e5c31af7Sopenharmony_ci
726e5c31af7Sopenharmony_ciProgramBinary* buildProgram (const HlslSource& program, glu::ShaderProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
727e5c31af7Sopenharmony_ci{
728e5c31af7Sopenharmony_ci	const SpirvVersion	spirvVersion		= program.buildOptions.targetVersion;
729e5c31af7Sopenharmony_ci	const bool			validateBinary		= VALIDATE_BINARIES;
730e5c31af7Sopenharmony_ci	vector<deUint32>	binary;
731e5c31af7Sopenharmony_ci	std::string			cachekey;
732e5c31af7Sopenharmony_ci	std::string			shaderstring;
733e5c31af7Sopenharmony_ci	vk::ProgramBinary*	res					= 0;
734e5c31af7Sopenharmony_ci	const int			optimizationRecipe	= commandLine.getOptimizationRecipe();
735e5c31af7Sopenharmony_ci	deInt32				hash				= 0;
736e5c31af7Sopenharmony_ci
737e5c31af7Sopenharmony_ci	if (commandLine.isShadercacheEnabled())
738e5c31af7Sopenharmony_ci	{
739e5c31af7Sopenharmony_ci		shaderCacheFirstRunCheck(commandLine);
740e5c31af7Sopenharmony_ci		getCompileEnvironment(cachekey);
741e5c31af7Sopenharmony_ci		getBuildOptions(cachekey, program.buildOptions, optimizationRecipe);
742e5c31af7Sopenharmony_ci
743e5c31af7Sopenharmony_ci		for (int i = 0; i < glu::SHADERTYPE_LAST; i++)
744e5c31af7Sopenharmony_ci		{
745e5c31af7Sopenharmony_ci			if (!program.sources[i].empty())
746e5c31af7Sopenharmony_ci			{
747e5c31af7Sopenharmony_ci				cachekey += glu::getShaderTypeName((glu::ShaderType)i);
748e5c31af7Sopenharmony_ci
749e5c31af7Sopenharmony_ci				for (std::vector<std::string>::const_iterator it = program.sources[i].begin(); it != program.sources[i].end(); ++it)
750e5c31af7Sopenharmony_ci					shaderstring += *it;
751e5c31af7Sopenharmony_ci			}
752e5c31af7Sopenharmony_ci		}
753e5c31af7Sopenharmony_ci
754e5c31af7Sopenharmony_ci		cachekey = cachekey + shaderstring;
755e5c31af7Sopenharmony_ci
756e5c31af7Sopenharmony_ci		hash = shadercacheHash(cachekey.c_str());
757e5c31af7Sopenharmony_ci
758e5c31af7Sopenharmony_ci		res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename(), hash);
759e5c31af7Sopenharmony_ci
760e5c31af7Sopenharmony_ci		if (res)
761e5c31af7Sopenharmony_ci		{
762e5c31af7Sopenharmony_ci			buildInfo->program.infoLog		= "Loaded from cache";
763e5c31af7Sopenharmony_ci			buildInfo->program.linkOk		= true;
764e5c31af7Sopenharmony_ci			buildInfo->program.linkTimeUs	= 0;
765e5c31af7Sopenharmony_ci
766e5c31af7Sopenharmony_ci			for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
767e5c31af7Sopenharmony_ci			{
768e5c31af7Sopenharmony_ci				if (!program.sources[shaderType].empty())
769e5c31af7Sopenharmony_ci				{
770e5c31af7Sopenharmony_ci					glu::ShaderInfo	shaderBuildInfo;
771e5c31af7Sopenharmony_ci
772e5c31af7Sopenharmony_ci					shaderBuildInfo.type			= (glu::ShaderType)shaderType;
773e5c31af7Sopenharmony_ci					shaderBuildInfo.source			= shaderstring;
774e5c31af7Sopenharmony_ci					shaderBuildInfo.compileTimeUs	= 0;
775e5c31af7Sopenharmony_ci					shaderBuildInfo.compileOk		= true;
776e5c31af7Sopenharmony_ci
777e5c31af7Sopenharmony_ci					buildInfo->shaders.push_back(shaderBuildInfo);
778e5c31af7Sopenharmony_ci				}
779e5c31af7Sopenharmony_ci			}
780e5c31af7Sopenharmony_ci		}
781e5c31af7Sopenharmony_ci	}
782e5c31af7Sopenharmony_ci
783e5c31af7Sopenharmony_ci	if (!res)
784e5c31af7Sopenharmony_ci	{
785e5c31af7Sopenharmony_ci		{
786e5c31af7Sopenharmony_ci			vector<deUint32> nonStrippedBinary;
787e5c31af7Sopenharmony_ci
788e5c31af7Sopenharmony_ci			if (!compileHlslToSpirV(program, &nonStrippedBinary, buildInfo))
789e5c31af7Sopenharmony_ci				TCU_THROW(InternalError, "Compiling HLSL to SPIR-V failed");
790e5c31af7Sopenharmony_ci
791e5c31af7Sopenharmony_ci			TCU_CHECK_INTERNAL(!nonStrippedBinary.empty());
792e5c31af7Sopenharmony_ci			stripSpirVDebugInfo(nonStrippedBinary.size(), &nonStrippedBinary[0], &binary);
793e5c31af7Sopenharmony_ci			TCU_CHECK_INTERNAL(!binary.empty());
794e5c31af7Sopenharmony_ci		}
795e5c31af7Sopenharmony_ci
796e5c31af7Sopenharmony_ci		if (optimizationRecipe != 0)
797e5c31af7Sopenharmony_ci		{
798e5c31af7Sopenharmony_ci			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
799e5c31af7Sopenharmony_ci			optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
800e5c31af7Sopenharmony_ci		}
801e5c31af7Sopenharmony_ci
802e5c31af7Sopenharmony_ci		if (validateBinary)
803e5c31af7Sopenharmony_ci		{
804e5c31af7Sopenharmony_ci			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
805e5c31af7Sopenharmony_ci		}
806e5c31af7Sopenharmony_ci
807e5c31af7Sopenharmony_ci		res = createProgramBinaryFromSpirV(binary);
808e5c31af7Sopenharmony_ci		if (commandLine.isShadercacheEnabled())
809e5c31af7Sopenharmony_ci		{
810e5c31af7Sopenharmony_ci			shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename(), hash);
811e5c31af7Sopenharmony_ci		}
812e5c31af7Sopenharmony_ci	}
813e5c31af7Sopenharmony_ci	return res;
814e5c31af7Sopenharmony_ci}
815e5c31af7Sopenharmony_ci
816e5c31af7Sopenharmony_ciProgramBinary* assembleProgram (const SpirVAsmSource& program, SpirVProgramInfo* buildInfo, const tcu::CommandLine& commandLine)
817e5c31af7Sopenharmony_ci{
818e5c31af7Sopenharmony_ci	const SpirvVersion	spirvVersion		= program.buildOptions.targetVersion;
819e5c31af7Sopenharmony_ci	const bool			validateBinary		= VALIDATE_BINARIES;
820e5c31af7Sopenharmony_ci	vector<deUint32>	binary;
821e5c31af7Sopenharmony_ci	vk::ProgramBinary*	res					= 0;
822e5c31af7Sopenharmony_ci	std::string			cachekey;
823e5c31af7Sopenharmony_ci	const int			optimizationRecipe	= commandLine.isSpirvOptimizationEnabled() ? commandLine.getOptimizationRecipe() : 0;
824e5c31af7Sopenharmony_ci	deUint32			hash				= 0;
825e5c31af7Sopenharmony_ci
826e5c31af7Sopenharmony_ci	if (commandLine.isShadercacheEnabled())
827e5c31af7Sopenharmony_ci	{
828e5c31af7Sopenharmony_ci		shaderCacheFirstRunCheck(commandLine);
829e5c31af7Sopenharmony_ci		getCompileEnvironment(cachekey);
830e5c31af7Sopenharmony_ci		cachekey += "Target Spir-V ";
831e5c31af7Sopenharmony_ci		cachekey += getSpirvVersionName(spirvVersion);
832e5c31af7Sopenharmony_ci		cachekey += "\n";
833e5c31af7Sopenharmony_ci		if (optimizationRecipe != 0)
834e5c31af7Sopenharmony_ci		{
835e5c31af7Sopenharmony_ci			cachekey += "Optimization recipe ";
836e5c31af7Sopenharmony_ci			cachekey += de::toString(optimizationRecipe);
837e5c31af7Sopenharmony_ci			cachekey += "\n";
838e5c31af7Sopenharmony_ci		}
839e5c31af7Sopenharmony_ci
840e5c31af7Sopenharmony_ci		cachekey += program.source;
841e5c31af7Sopenharmony_ci
842e5c31af7Sopenharmony_ci		hash = shadercacheHash(cachekey.c_str());
843e5c31af7Sopenharmony_ci
844e5c31af7Sopenharmony_ci		res = shadercacheLoad(cachekey, commandLine.getShaderCacheFilename(), hash);
845e5c31af7Sopenharmony_ci
846e5c31af7Sopenharmony_ci		if (res)
847e5c31af7Sopenharmony_ci		{
848e5c31af7Sopenharmony_ci			buildInfo->source			= program.source;
849e5c31af7Sopenharmony_ci			buildInfo->compileOk		= true;
850e5c31af7Sopenharmony_ci			buildInfo->compileTimeUs	= 0;
851e5c31af7Sopenharmony_ci			buildInfo->infoLog			= "Loaded from cache";
852e5c31af7Sopenharmony_ci		}
853e5c31af7Sopenharmony_ci	}
854e5c31af7Sopenharmony_ci
855e5c31af7Sopenharmony_ci	if (!res)
856e5c31af7Sopenharmony_ci	{
857e5c31af7Sopenharmony_ci
858e5c31af7Sopenharmony_ci		if (!assembleSpirV(&program, &binary, buildInfo, spirvVersion))
859e5c31af7Sopenharmony_ci			TCU_THROW(InternalError, "Failed to assemble SPIR-V");
860e5c31af7Sopenharmony_ci
861e5c31af7Sopenharmony_ci		if (optimizationRecipe != 0)
862e5c31af7Sopenharmony_ci		{
863e5c31af7Sopenharmony_ci			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
864e5c31af7Sopenharmony_ci			optimizeCompiledBinary(binary, optimizationRecipe, spirvVersion);
865e5c31af7Sopenharmony_ci		}
866e5c31af7Sopenharmony_ci
867e5c31af7Sopenharmony_ci		if (validateBinary)
868e5c31af7Sopenharmony_ci		{
869e5c31af7Sopenharmony_ci			validateCompiledBinary(binary, buildInfo, program.buildOptions.getSpirvValidatorOptions());
870e5c31af7Sopenharmony_ci		}
871e5c31af7Sopenharmony_ci
872e5c31af7Sopenharmony_ci		res = createProgramBinaryFromSpirV(binary);
873e5c31af7Sopenharmony_ci		if (commandLine.isShadercacheEnabled())
874e5c31af7Sopenharmony_ci		{
875e5c31af7Sopenharmony_ci			shadercacheSave(res, cachekey, commandLine.getShaderCacheFilename(), hash);
876e5c31af7Sopenharmony_ci		}
877e5c31af7Sopenharmony_ci	}
878e5c31af7Sopenharmony_ci	return res;
879e5c31af7Sopenharmony_ci}
880e5c31af7Sopenharmony_ci
881e5c31af7Sopenharmony_civoid disassembleProgram (const ProgramBinary& program, std::ostream* dst)
882e5c31af7Sopenharmony_ci{
883e5c31af7Sopenharmony_ci	if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
884e5c31af7Sopenharmony_ci	{
885e5c31af7Sopenharmony_ci		TCU_CHECK_INTERNAL(isSaneSpirVBinary(program));
886e5c31af7Sopenharmony_ci
887e5c31af7Sopenharmony_ci		if (isNativeSpirVBinaryEndianness())
888e5c31af7Sopenharmony_ci			disassembleSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst,
889e5c31af7Sopenharmony_ci							 extractSpirvVersion(program));
890e5c31af7Sopenharmony_ci		else
891e5c31af7Sopenharmony_ci			TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
892e5c31af7Sopenharmony_ci	}
893e5c31af7Sopenharmony_ci	else
894e5c31af7Sopenharmony_ci		TCU_THROW(NotSupportedError, "Unsupported program format");
895e5c31af7Sopenharmony_ci}
896e5c31af7Sopenharmony_ci
897e5c31af7Sopenharmony_cibool validateProgram (const ProgramBinary& program, std::ostream* dst, const SpirvValidatorOptions& options)
898e5c31af7Sopenharmony_ci{
899e5c31af7Sopenharmony_ci	if (program.getFormat() == PROGRAM_FORMAT_SPIRV)
900e5c31af7Sopenharmony_ci	{
901e5c31af7Sopenharmony_ci		if (!isSaneSpirVBinary(program))
902e5c31af7Sopenharmony_ci		{
903e5c31af7Sopenharmony_ci			*dst << "Binary doesn't look like SPIR-V at all";
904e5c31af7Sopenharmony_ci			return false;
905e5c31af7Sopenharmony_ci		}
906e5c31af7Sopenharmony_ci
907e5c31af7Sopenharmony_ci		if (isNativeSpirVBinaryEndianness())
908e5c31af7Sopenharmony_ci			return validateSpirV(program.getSize()/sizeof(deUint32), (const deUint32*)program.getBinary(), dst, options);
909e5c31af7Sopenharmony_ci		else
910e5c31af7Sopenharmony_ci			TCU_THROW(InternalError, "SPIR-V endianness translation not supported");
911e5c31af7Sopenharmony_ci	}
912e5c31af7Sopenharmony_ci	else
913e5c31af7Sopenharmony_ci		TCU_THROW(NotSupportedError, "Unsupported program format");
914e5c31af7Sopenharmony_ci}
915e5c31af7Sopenharmony_ci
916e5c31af7Sopenharmony_ciMove<VkShaderModule> createShaderModule (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags)
917e5c31af7Sopenharmony_ci{
918e5c31af7Sopenharmony_ci	if (binary.getFormat() == PROGRAM_FORMAT_SPIRV)
919e5c31af7Sopenharmony_ci	{
920e5c31af7Sopenharmony_ci		const struct VkShaderModuleCreateInfo		shaderModuleInfo	=
921e5c31af7Sopenharmony_ci		{
922e5c31af7Sopenharmony_ci			VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
923e5c31af7Sopenharmony_ci			DE_NULL,
924e5c31af7Sopenharmony_ci			flags,
925e5c31af7Sopenharmony_ci			(deUintptr)binary.getSize(),
926e5c31af7Sopenharmony_ci			(const deUint32*)binary.getBinary(),
927e5c31af7Sopenharmony_ci		};
928e5c31af7Sopenharmony_ci
929e5c31af7Sopenharmony_ci		binary.setUsed();
930e5c31af7Sopenharmony_ci
931e5c31af7Sopenharmony_ci		return createShaderModule(deviceInterface, device, &shaderModuleInfo);
932e5c31af7Sopenharmony_ci	}
933e5c31af7Sopenharmony_ci	else
934e5c31af7Sopenharmony_ci		TCU_THROW(NotSupportedError, "Unsupported program format");
935e5c31af7Sopenharmony_ci}
936e5c31af7Sopenharmony_ci
937e5c31af7Sopenharmony_ciglu::ShaderType getGluShaderType (VkShaderStageFlagBits shaderStage)
938e5c31af7Sopenharmony_ci{
939e5c31af7Sopenharmony_ci	switch (shaderStage)
940e5c31af7Sopenharmony_ci	{
941e5c31af7Sopenharmony_ci		case VK_SHADER_STAGE_VERTEX_BIT:					return glu::SHADERTYPE_VERTEX;
942e5c31af7Sopenharmony_ci		case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:		return glu::SHADERTYPE_TESSELLATION_CONTROL;
943e5c31af7Sopenharmony_ci		case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:	return glu::SHADERTYPE_TESSELLATION_EVALUATION;
944e5c31af7Sopenharmony_ci		case VK_SHADER_STAGE_GEOMETRY_BIT:					return glu::SHADERTYPE_GEOMETRY;
945e5c31af7Sopenharmony_ci		case VK_SHADER_STAGE_FRAGMENT_BIT:					return glu::SHADERTYPE_FRAGMENT;
946e5c31af7Sopenharmony_ci		case VK_SHADER_STAGE_COMPUTE_BIT:					return glu::SHADERTYPE_COMPUTE;
947e5c31af7Sopenharmony_ci		default:
948e5c31af7Sopenharmony_ci			DE_FATAL("Unknown shader stage");
949e5c31af7Sopenharmony_ci			return glu::SHADERTYPE_LAST;
950e5c31af7Sopenharmony_ci	}
951e5c31af7Sopenharmony_ci}
952e5c31af7Sopenharmony_ci
953e5c31af7Sopenharmony_ciVkShaderStageFlagBits getVkShaderStage (glu::ShaderType shaderType)
954e5c31af7Sopenharmony_ci{
955e5c31af7Sopenharmony_ci	static const VkShaderStageFlagBits s_shaderStages[] =
956e5c31af7Sopenharmony_ci	{
957e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_VERTEX_BIT,
958e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_FRAGMENT_BIT,
959e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_GEOMETRY_BIT,
960e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
961e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
962e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_COMPUTE_BIT,
963e5c31af7Sopenharmony_ci#ifndef CTS_USES_VULKANSC
964e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_RAYGEN_BIT_NV,
965e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_ANY_HIT_BIT_NV,
966e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV,
967e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_MISS_BIT_NV,
968e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_INTERSECTION_BIT_NV,
969e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_CALLABLE_BIT_NV,
970e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_TASK_BIT_NV,
971e5c31af7Sopenharmony_ci		VK_SHADER_STAGE_MESH_BIT_NV,
972e5c31af7Sopenharmony_ci#else // CTS_USES_VULKANSC
973e5c31af7Sopenharmony_ci		(VkShaderStageFlagBits)64u,
974e5c31af7Sopenharmony_ci		(VkShaderStageFlagBits)128u,
975e5c31af7Sopenharmony_ci		(VkShaderStageFlagBits)256u,
976e5c31af7Sopenharmony_ci		(VkShaderStageFlagBits)512u,
977e5c31af7Sopenharmony_ci		(VkShaderStageFlagBits)1024u,
978e5c31af7Sopenharmony_ci		(VkShaderStageFlagBits)2048u,
979e5c31af7Sopenharmony_ci		(VkShaderStageFlagBits)4096u,
980e5c31af7Sopenharmony_ci		(VkShaderStageFlagBits)8192u
981e5c31af7Sopenharmony_ci#endif // CTS_USES_VULKANSC
982e5c31af7Sopenharmony_ci	};
983e5c31af7Sopenharmony_ci
984e5c31af7Sopenharmony_ci	return de::getSizedArrayElement<glu::SHADERTYPE_LAST>(s_shaderStages, shaderType);
985e5c31af7Sopenharmony_ci}
986e5c31af7Sopenharmony_ci
987e5c31af7Sopenharmony_ci// Baseline version, to be used for shaders which don't specify a version
988e5c31af7Sopenharmony_civk::SpirvVersion getBaselineSpirvVersion (const deUint32 /* vulkanVersion */)
989e5c31af7Sopenharmony_ci{
990e5c31af7Sopenharmony_ci	return vk::SPIRV_VERSION_1_0;
991e5c31af7Sopenharmony_ci}
992e5c31af7Sopenharmony_ci
993e5c31af7Sopenharmony_ci// Max supported versions for each Vulkan version, without requiring a Vulkan extension.
994e5c31af7Sopenharmony_civk::SpirvVersion getMaxSpirvVersionForVulkan (const deUint32 vulkanVersion)
995e5c31af7Sopenharmony_ci{
996e5c31af7Sopenharmony_ci	vk::SpirvVersion	result			= vk::SPIRV_VERSION_LAST;
997e5c31af7Sopenharmony_ci
998e5c31af7Sopenharmony_ci	deUint32 vulkanVersionVariantMajorMinor = VK_MAKE_API_VERSION(VK_API_VERSION_VARIANT(vulkanVersion), VK_API_VERSION_MAJOR(vulkanVersion), VK_API_VERSION_MINOR(vulkanVersion), 0);
999e5c31af7Sopenharmony_ci	if (vulkanVersionVariantMajorMinor == VK_API_VERSION_1_0)
1000e5c31af7Sopenharmony_ci		result = vk::SPIRV_VERSION_1_0;
1001e5c31af7Sopenharmony_ci	else if (vulkanVersionVariantMajorMinor == VK_API_VERSION_1_1)
1002e5c31af7Sopenharmony_ci		result = vk::SPIRV_VERSION_1_3;
1003e5c31af7Sopenharmony_ci#ifndef CTS_USES_VULKANSC
1004e5c31af7Sopenharmony_ci	else if (vulkanVersionVariantMajorMinor == VK_API_VERSION_1_2)
1005e5c31af7Sopenharmony_ci		result = vk::SPIRV_VERSION_1_5;
1006e5c31af7Sopenharmony_ci	else if (vulkanVersionVariantMajorMinor >= VK_API_VERSION_1_3)
1007e5c31af7Sopenharmony_ci		result = vk::SPIRV_VERSION_1_6;
1008e5c31af7Sopenharmony_ci#else
1009e5c31af7Sopenharmony_ci	else if (vulkanVersionVariantMajorMinor >= VK_API_VERSION_1_2)
1010e5c31af7Sopenharmony_ci		result = vk::SPIRV_VERSION_1_5;
1011e5c31af7Sopenharmony_ci#endif // CTS_USES_VULKANSC
1012e5c31af7Sopenharmony_ci
1013e5c31af7Sopenharmony_ci	DE_ASSERT(result < vk::SPIRV_VERSION_LAST);
1014e5c31af7Sopenharmony_ci
1015e5c31af7Sopenharmony_ci	return result;
1016e5c31af7Sopenharmony_ci}
1017e5c31af7Sopenharmony_ci
1018e5c31af7Sopenharmony_civk::SpirvVersion getMaxSpirvVersionForAsm (const deUint32 vulkanVersion)
1019e5c31af7Sopenharmony_ci{
1020e5c31af7Sopenharmony_ci	return getMaxSpirvVersionForVulkan(vulkanVersion);
1021e5c31af7Sopenharmony_ci}
1022e5c31af7Sopenharmony_ci
1023e5c31af7Sopenharmony_civk::SpirvVersion getMaxSpirvVersionForGlsl (const deUint32 vulkanVersion)
1024e5c31af7Sopenharmony_ci{
1025e5c31af7Sopenharmony_ci	return getMaxSpirvVersionForVulkan(vulkanVersion);
1026e5c31af7Sopenharmony_ci}
1027e5c31af7Sopenharmony_ci
1028e5c31af7Sopenharmony_ciSpirvVersion extractSpirvVersion (const ProgramBinary& binary)
1029e5c31af7Sopenharmony_ci{
1030e5c31af7Sopenharmony_ci	DE_STATIC_ASSERT(SPIRV_VERSION_1_6 + 1 == SPIRV_VERSION_LAST);
1031e5c31af7Sopenharmony_ci
1032e5c31af7Sopenharmony_ci	if (binary.getFormat() != PROGRAM_FORMAT_SPIRV)
1033e5c31af7Sopenharmony_ci		TCU_THROW(InternalError, "Binary is not in SPIR-V format");
1034e5c31af7Sopenharmony_ci
1035e5c31af7Sopenharmony_ci	if (!isSaneSpirVBinary(binary) || binary.getSize() < sizeof(SpirvBinaryHeader))
1036e5c31af7Sopenharmony_ci		TCU_THROW(InternalError, "Invalid SPIR-V header format");
1037e5c31af7Sopenharmony_ci
1038e5c31af7Sopenharmony_ci	const deUint32				spirvBinaryVersion10	= 0x00010000;
1039e5c31af7Sopenharmony_ci	const deUint32				spirvBinaryVersion11	= 0x00010100;
1040e5c31af7Sopenharmony_ci	const deUint32				spirvBinaryVersion12	= 0x00010200;
1041e5c31af7Sopenharmony_ci	const deUint32				spirvBinaryVersion13	= 0x00010300;
1042e5c31af7Sopenharmony_ci	const deUint32				spirvBinaryVersion14	= 0x00010400;
1043e5c31af7Sopenharmony_ci	const deUint32				spirvBinaryVersion15	= 0x00010500;
1044e5c31af7Sopenharmony_ci	const deUint32				spirvBinaryVersion16	= 0x00010600;
1045e5c31af7Sopenharmony_ci	const SpirvBinaryHeader*	header					= reinterpret_cast<const SpirvBinaryHeader*>(binary.getBinary());
1046e5c31af7Sopenharmony_ci	const deUint32				spirvVersion			= isNativeSpirVBinaryEndianness()
1047e5c31af7Sopenharmony_ci														? header->version
1048e5c31af7Sopenharmony_ci														: deReverseBytes32(header->version);
1049e5c31af7Sopenharmony_ci	SpirvVersion				result					= SPIRV_VERSION_LAST;
1050e5c31af7Sopenharmony_ci
1051e5c31af7Sopenharmony_ci	switch (spirvVersion)
1052e5c31af7Sopenharmony_ci	{
1053e5c31af7Sopenharmony_ci		case spirvBinaryVersion10:	result = SPIRV_VERSION_1_0; break; //!< SPIR-V 1.0
1054e5c31af7Sopenharmony_ci		case spirvBinaryVersion11:	result = SPIRV_VERSION_1_1; break; //!< SPIR-V 1.1
1055e5c31af7Sopenharmony_ci		case spirvBinaryVersion12:	result = SPIRV_VERSION_1_2; break; //!< SPIR-V 1.2
1056e5c31af7Sopenharmony_ci		case spirvBinaryVersion13:	result = SPIRV_VERSION_1_3; break; //!< SPIR-V 1.3
1057e5c31af7Sopenharmony_ci		case spirvBinaryVersion14:	result = SPIRV_VERSION_1_4; break; //!< SPIR-V 1.4
1058e5c31af7Sopenharmony_ci		case spirvBinaryVersion15:	result = SPIRV_VERSION_1_5; break; //!< SPIR-V 1.5
1059e5c31af7Sopenharmony_ci		case spirvBinaryVersion16:	result = SPIRV_VERSION_1_6; break; //!< SPIR-V 1.6
1060e5c31af7Sopenharmony_ci		default:					TCU_THROW(InternalError, "Unknown SPIR-V version detected in binary");
1061e5c31af7Sopenharmony_ci	}
1062e5c31af7Sopenharmony_ci
1063e5c31af7Sopenharmony_ci	return result;
1064e5c31af7Sopenharmony_ci}
1065e5c31af7Sopenharmony_ci
1066e5c31af7Sopenharmony_cistd::string getSpirvVersionName (const SpirvVersion spirvVersion)
1067e5c31af7Sopenharmony_ci{
1068e5c31af7Sopenharmony_ci	DE_STATIC_ASSERT(SPIRV_VERSION_1_6 + 1 == SPIRV_VERSION_LAST);
1069e5c31af7Sopenharmony_ci	DE_ASSERT(spirvVersion < SPIRV_VERSION_LAST);
1070e5c31af7Sopenharmony_ci
1071e5c31af7Sopenharmony_ci	std::string result;
1072e5c31af7Sopenharmony_ci
1073e5c31af7Sopenharmony_ci	switch (spirvVersion)
1074e5c31af7Sopenharmony_ci	{
1075e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_0: result = "1.0"; break; //!< SPIR-V 1.0
1076e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_1: result = "1.1"; break; //!< SPIR-V 1.1
1077e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_2: result = "1.2"; break; //!< SPIR-V 1.2
1078e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_3: result = "1.3"; break; //!< SPIR-V 1.3
1079e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_4: result = "1.4"; break; //!< SPIR-V 1.4
1080e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_5: result = "1.5"; break; //!< SPIR-V 1.5
1081e5c31af7Sopenharmony_ci		case SPIRV_VERSION_1_6: result = "1.6"; break; //!< SPIR-V 1.6
1082e5c31af7Sopenharmony_ci		default:				result = "Unknown";
1083e5c31af7Sopenharmony_ci	}
1084e5c31af7Sopenharmony_ci
1085e5c31af7Sopenharmony_ci	return result;
1086e5c31af7Sopenharmony_ci}
1087e5c31af7Sopenharmony_ci
1088e5c31af7Sopenharmony_ciSpirvVersion& operator++(SpirvVersion& spirvVersion)
1089e5c31af7Sopenharmony_ci{
1090e5c31af7Sopenharmony_ci	if (spirvVersion == SPIRV_VERSION_LAST)
1091e5c31af7Sopenharmony_ci		spirvVersion = SPIRV_VERSION_1_0;
1092e5c31af7Sopenharmony_ci	else
1093e5c31af7Sopenharmony_ci		spirvVersion = static_cast<SpirvVersion>(static_cast<deUint32>(spirvVersion) + 1);
1094e5c31af7Sopenharmony_ci
1095e5c31af7Sopenharmony_ci	return spirvVersion;
1096e5c31af7Sopenharmony_ci}
1097e5c31af7Sopenharmony_ci
1098e5c31af7Sopenharmony_ci} // vk
1099