• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2020 The Khronos Group Inc.
6  * Copyright (c) 2020 Advanced Micro Devices, Inc.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21 /*!
22  * \file
23  * \brief Pipeline Cache Tests
24  */
25 /*--------------------------------------------------------------------*/
26 
27 #include "vktPipelineCreationCacheControlTests.hpp"
28 
29 #include "deRandom.hpp"
30 #include "deUniquePtr.hpp"
31 #include "tcuStringTemplate.hpp"
32 #include "vkDeviceUtil.hpp"
33 #include "vkRefUtil.hpp"
34 #include "vktConstexprVectorUtil.hpp"
35 #include "vktTestCase.hpp"
36 #include "vktTestCaseUtil.hpp"
37 
38 #include <chrono>
39 #include <random>
40 #include <string>
41 #include <vector>
42 
43 namespace vkt
44 {
45 namespace pipeline
46 {
47 namespace
48 {
49 using namespace vk;
50 
51 using tcu::StringTemplate;
52 using tcu::TestCaseGroup;
53 using tcu::TestContext;
54 using tcu::TestStatus;
55 
56 using ::std::array;
57 using ::std::string;
58 using ::std::vector;
59 
60 /*--------------------------------------------------------------------*//*!
61  * Elements common to all test types
62  *//*--------------------------------------------------------------------*/
63 namespace test_common
64 {
65 static constexpr auto VK_NULL_HANDLE = DE_NULL;
66 
67 using ::std::chrono::high_resolution_clock;
68 using ::std::chrono::microseconds;
69 
70 using duration			 = high_resolution_clock::duration;
71 using UniquePipeline	 = Move<VkPipeline>;
72 using UniqueShaderModule = Move<VkShaderModule>;
73 
74 /*--------------------------------------------------------------------*//*!
75  * \brief Paired Vulkan API result with elapsed duration
76  *//*--------------------------------------------------------------------*/
77 struct TimedResult
78 {
79 	VkResult result;
80 	duration elapsed;
81 };
82 
83 /*--------------------------------------------------------------------*//*!
84  * \brief Validation function type output from vkCreate*Pipelines()
85  *
86  * \param result - VkResult returned from API call
87  * \param pipeliens - vector of pipelines created
88  * \param elapsed - high_resolution_clock::duration of time elapsed in API
89  * \param reason - output string to give the reason for failure
90  *
91  * \return QP_TEST_RESULT_PASS on success QP_TEST_RESULT_FAIL otherwise
92  *//*--------------------------------------------------------------------*/
93 using Validator = qpTestResult (*)(VkResult, const vector<UniquePipeline>&, duration, string&);
94 
95 static constexpr size_t VALIDATOR_ARRAY_MAX = 4;
96 using ValidatorArray						= ConstexprVector<Validator, VALIDATOR_ARRAY_MAX>;
97 
98 /*--------------------------------------------------------------------*//*!
99  * \brief Run a loop of validation tests and return the result
100  *//*--------------------------------------------------------------------*/
101 template <typename pipelines_t, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL>
validateResults(VkResult result,const pipelines_t & pipelines,duration elapsed,const ValidatorArray & validators)102 TestStatus validateResults(VkResult				 result,
103 						   const pipelines_t&	 pipelines,
104 						   duration				 elapsed,
105 						   const ValidatorArray& validators)
106 {
107 	using de::contains;
108 	static constexpr VkResult ALLOWED_RESULTS[] = {VK_SUCCESS, VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT};
109 
110 	string reason;
111 
112 	if (contains(DE_ARRAY_BEGIN(ALLOWED_RESULTS), DE_ARRAY_END(ALLOWED_RESULTS), result) == DE_FALSE)
113 	{
114 		static const StringTemplate ERROR_MSG = {"Pipeline creation returned an error result: ${0}"};
115 		TCU_THROW(InternalError, ERROR_MSG.format(result).c_str());
116 	}
117 
118 	for (const auto& validator : validators)
119 	{
120 		const auto qpResult = validator(result, pipelines, elapsed, reason);
121 		if (qpResult != QP_TEST_RESULT_PASS)
122 		{
123 			return {qpResult, reason};
124 		}
125 	}
126 
127 	return TestStatus::pass("Test passed.");
128 }
129 
130 /*--------------------------------------------------------------------*//*!
131  * \brief Generate an error if result does not match VK_RESULT
132  *//*--------------------------------------------------------------------*/
133 template <VkResult VK_RESULT, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL>
checkResult(VkResult result,const vector<UniquePipeline> &,duration,string & reason)134 qpTestResult checkResult(VkResult result, const vector<UniquePipeline>&, duration, string& reason)
135 {
136 	if (VK_RESULT != result)
137 	{
138 		static const StringTemplate ERROR_MSG = {"Got ${0}, Expected ${1}"};
139 		reason								  = ERROR_MSG.format(result, VK_RESULT);
140 		return FAIL_RESULT;
141 	}
142 
143 	return QP_TEST_RESULT_PASS;
144 }
145 
146 /*--------------------------------------------------------------------*//*!
147  * \brief Generate an error if pipeline[INDEX] is not valid
148  *//*--------------------------------------------------------------------*/
149 template <size_t INDEX, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL>
checkPipelineMustBeValid(VkResult,const vector<UniquePipeline> & pipelines,duration,string & reason)150 qpTestResult checkPipelineMustBeValid(VkResult, const vector<UniquePipeline>& pipelines, duration, string& reason)
151 {
152 	if (pipelines.size() <= INDEX)
153 	{
154 		static const StringTemplate ERROR_MSG = {"Index ${0} is not in created pipelines (pipelines.size(): ${1})"};
155 		TCU_THROW(TestError, ERROR_MSG.format(INDEX, pipelines.size()));
156 	}
157 
158 	if (*pipelines[INDEX] == VK_NULL_HANDLE)
159 	{
160 		static const StringTemplate ERROR_MSG = {"pipelines[${0}] is not a valid VkPipeline object"};
161 		reason								  = ERROR_MSG.format(INDEX);
162 		return FAIL_RESULT;
163 	}
164 
165 	return QP_TEST_RESULT_PASS;
166 }
167 
168 /*--------------------------------------------------------------------*//*!
169  * \brief Generate an error if pipeline[INDEX] is not VK_NULL_HANDLE
170  *//*--------------------------------------------------------------------*/
171 template <size_t INDEX, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL>
checkPipelineMustBeNull(VkResult,const vector<UniquePipeline> & pipelines,duration,string & reason)172 qpTestResult checkPipelineMustBeNull(VkResult, const vector<UniquePipeline>& pipelines, duration, string& reason)
173 {
174 	if (pipelines.size() <= INDEX)
175 	{
176 		static const StringTemplate ERROR_MSG = {"Index ${0} is not in created pipelines (pipelines.size(): ${1})"};
177 		TCU_THROW(TestError, ERROR_MSG.format(INDEX, pipelines.size()));
178 	}
179 
180 	if (*pipelines[INDEX] != VK_NULL_HANDLE)
181 	{
182 		static const StringTemplate ERROR_MSG = {"pipelines[${0}] is not VK_NULL_HANDLE"};
183 		reason								  = ERROR_MSG.format(INDEX);
184 		return FAIL_RESULT;
185 	}
186 
187 	return QP_TEST_RESULT_PASS;
188 }
189 
190 /*--------------------------------------------------------------------*//*!
191  * \brief Generate an error if any pipeline is valid after an early-return failure
192  *//*--------------------------------------------------------------------*/
193 template <size_t INDEX, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL>
checkPipelineNullAfterIndex(VkResult,const vector<UniquePipeline> & pipelines,duration,string & reason)194 qpTestResult checkPipelineNullAfterIndex(VkResult, const vector<UniquePipeline>& pipelines, duration, string& reason)
195 {
196 	if (pipelines.size() <= INDEX)
197 	{
198 		static const StringTemplate ERROR_MSG = {"Index ${0} is not in created pipelines (pipelines.size(): ${1})"};
199 		TCU_THROW(TestError, ERROR_MSG.format(INDEX, pipelines.size()));
200 	}
201 
202 	if (pipelines.size() - 1 == INDEX)
203 	{
204 		static const StringTemplate ERROR_MSG = {"Index ${0} is the last pipeline, likely a malformed test case"};
205 		TCU_THROW(TestError, ERROR_MSG.format(INDEX));
206 	}
207 
208 	// Only have to iterate through if the requested index is null
209 	if (*pipelines[INDEX] == VK_NULL_HANDLE)
210 	{
211 		for (size_t i = INDEX + 1; i < pipelines.size(); ++i)
212 		{
213 			if (*pipelines[i] != VK_NULL_HANDLE)
214 			{
215 				static const StringTemplate ERROR_MSG = {
216 					"pipelines[${0}] is not VK_NULL_HANDLE after a explicit early return index"};
217 				reason = ERROR_MSG.format(i);
218 				return FAIL_RESULT;
219 			}
220 		}
221 	}
222 
223 	return QP_TEST_RESULT_PASS;
224 }
225 
226 /*--------------------------------------------------------------------*//*!
227  * Time limit constants
228  *//*--------------------------------------------------------------------*/
229 enum ElapsedTime
230 {
231 	ELAPSED_TIME_INFINITE  = microseconds{-1}.count(),
232 	ELAPSED_TIME_IMMEDIATE = microseconds{500}.count(),
233 	ELAPSED_TIME_FAST	   = microseconds{1000}.count()
234 };
235 
236 /*--------------------------------------------------------------------*//*!
237  * \brief Generate an error if elapsed time exceeds MAX_TIME
238  *//*--------------------------------------------------------------------*/
239 template <ElapsedTime MAX_TIME, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL>
checkElapsedTime(VkResult,const vector<UniquePipeline> &,duration elapsed,string & reason)240 qpTestResult checkElapsedTime(VkResult, const vector<UniquePipeline>&, duration elapsed, string& reason)
241 {
242 #if defined(DE_DEBUG)
243 	DE_UNREF(elapsed);
244 	DE_UNREF(reason);
245 
246 	// In debug mode timing is not likely to be accurate
247 	return QP_TEST_RESULT_PASS;
248 #else
249 
250 	using ::std::chrono::duration_cast;
251 
252 	static constexpr microseconds ALLOWED_TIME = microseconds{MAX_TIME};
253 
254 	if (elapsed > ALLOWED_TIME)
255 	{
256 		static const StringTemplate ERROR_MSG = {"pipeline creation took longer than ${0}us (actual time: ${1}us)"};
257 		reason = ERROR_MSG.format(ALLOWED_TIME.count(), duration_cast<microseconds>(elapsed).count());
258 		return FAIL_RESULT;
259 	}
260 
261 	return QP_TEST_RESULT_PASS;
262 #endif
263 }
264 
265 /*--------------------------------------------------------------------*//*!
266  * \brief Test case parameters
267  *//*--------------------------------------------------------------------*/
268 struct TestParams
269 {
270 	enum CacheType
271 	{
272 		NO_CACHE = 0,
273 		EXPLICIT_CACHE,
274 		DERIVATIVE_HANDLE,
275 		DERIVATIVE_INDEX
276 	};
277 
278 	struct Iteration
279 	{
280 		static constexpr size_t MAX_VARIANTS = 4;
281 		using Variant						 = VkPipelineCreateFlags;
282 		using VariantArray					 = ConstexprVector<Variant, MAX_VARIANTS>;
283 
284 		static constexpr Variant NORMAL		  = 0;
285 		static constexpr Variant NO_COMPILE	  = VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT;
286 		static constexpr Variant EARLY_RETURN = NO_COMPILE | VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT_EXT;
287 
288 		static constexpr VariantArray SINGLE_NORMAL						= VariantArray{NORMAL};
289 		static constexpr VariantArray SINGLE_NOCOMPILE					= VariantArray{NO_COMPILE};
290 		static constexpr VariantArray BATCH_NOCOMPILE_COMPILE_NOCOMPILE = VariantArray{NO_COMPILE, NORMAL, NO_COMPILE};
291 		static constexpr VariantArray BATCH_RETURN_COMPILE_NOCOMPILE = VariantArray{EARLY_RETURN, NORMAL, NO_COMPILE};
292 
Iterationvkt::pipeline::__anon3f94ae130111::test_common::TestParams::Iteration293 		inline constexpr Iteration() : variants{}, validators{} {};
Iterationvkt::pipeline::__anon3f94ae130111::test_common::TestParams::Iteration294 		inline constexpr Iteration(const VariantArray& v, const ValidatorArray& f) : variants{v}, validators{f} {};
295 
296 		VariantArray   variants;
297 		ValidatorArray validators;
298 	};
299 
300 	static constexpr size_t MAX_ITERATIONS = 4;
301 	using IterationArray				   = ConstexprVector<Iteration, MAX_ITERATIONS>;
302 
303 	const char*	   name;
304 	const char*	   description;
305 	CacheType	   cacheType;
306 	IterationArray iterations;
307 };
308 
309 /*--------------------------------------------------------------------*//*!
310  * \brief Verify extension and feature support
311  *//*--------------------------------------------------------------------*/
checkSupport(Context & context,const TestParams &)312 void checkSupport(Context& context, const TestParams&)
313 {
314 	static constexpr char EXT_NAME[] = "VK_EXT_pipeline_creation_cache_control";
315 	if (!context.requireDeviceFunctionality(EXT_NAME))
316 	{
317 		TCU_THROW(NotSupportedError, "Extension 'VK_EXT_pipeline_creation_cache_control' is not supported");
318 	}
319 
320 	const auto features = context.getPipelineCreationCacheControlFeaturesEXT();
321 	if (features.pipelineCreationCacheControl == DE_FALSE)
322 	{
323 		TCU_THROW(NotSupportedError, "Feature 'pipelineCreationCacheControl' is not enabled");
324 	}
325 }
326 
327 /*--------------------------------------------------------------------*//*!
328  * \brief Generate a random floating point number as a string
329  *//*--------------------------------------------------------------------*/
randomFloat()330 float randomFloat()
331 {
332 #if !defined(DE_DEBUG)
333 	static de::Random state = {::std::random_device{}()};
334 #else
335 	static de::Random state = {0xDEADBEEF};
336 #endif
337 
338 	return state.getFloat();
339 }
340 
341 /*--------------------------------------------------------------------*//*!
342  * \brief Get a string of VkResults from a vector
343  *//*--------------------------------------------------------------------*/
getResultsString(const vector<VkResult> & results)344 string getResultsString(const vector<VkResult>& results)
345 {
346 	using ::std::ostringstream;
347 
348 	ostringstream output;
349 
350 	output << "results[" << results.size() << "]={ ";
351 
352 	if (!results.empty())
353 	{
354 		output << results[0];
355 	}
356 
357 	for (size_t i = 1; i < results.size(); ++i)
358 	{
359 		output << ", " << results[i];
360 	}
361 
362 	output << " }";
363 
364 	return output.str();
365 }
366 
367 /*--------------------------------------------------------------------*//*!
368  * \brief Cast a pointer to an the expected SPIRV type
369  *//*--------------------------------------------------------------------*/
370 template <typename _t>
shader_cast(const _t * ptr)371 inline const deUint32* shader_cast(const _t* ptr)
372 {
373 	return reinterpret_cast<const deUint32*>(ptr);
374 }
375 
376 /*--------------------------------------------------------------------*//*!
377  * \brief Capture a container of Vulkan handles into Move<> types
378  *//*--------------------------------------------------------------------*/
379 template <typename input_container_t,
380 		  typename handle_t	 = typename input_container_t::value_type,
381 		  typename move_t	 = Move<handle_t>,
382 		  typename deleter_t = Deleter<handle_t>,
383 		  typename output_t	 = vector<move_t>>
wrapHandles(const DeviceInterface & vk,VkDevice device,const input_container_t & input,const VkAllocationCallbacks * allocator=DE_NULL)384 output_t wrapHandles(const DeviceInterface&		  vk,
385 					 VkDevice					  device,
386 					 const input_container_t&	  input,
387 					 const VkAllocationCallbacks* allocator = DE_NULL)
388 {
389 	using ::std::begin;
390 	using ::std::end;
391 	using ::std::transform;
392 
393 	auto output = output_t{};
394 	output.resize(input.size());
395 
396 	struct Predicate
397 	{
398 		deleter_t deleter;
399 		move_t	  operator()(handle_t v)
400 		{
401 			return (v != VK_NULL_HANDLE) ? move_t{check(v), deleter} : move_t{};
402 		}
403 	};
404 
405 	const auto wrapHandle = Predicate{deleter_t{vk, device, allocator}};
406 
407 	transform(begin(input), end(input), begin(output), wrapHandle);
408 
409 	return output;
410 }
411 
412 /*--------------------------------------------------------------------*//*!
413  * \brief create vkPipelineCache for test params
414  *//*--------------------------------------------------------------------*/
createPipelineCache(const DeviceInterface & vk,VkDevice device,const TestParams & params)415 Move<VkPipelineCache> createPipelineCache(const DeviceInterface& vk, VkDevice device, const TestParams& params)
416 {
417 	if (params.cacheType != TestParams::EXPLICIT_CACHE)
418 	{
419 		return {};
420 	}
421 
422 	static constexpr auto cacheInfo = VkPipelineCacheCreateInfo{
423 		VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, //sType
424 		DE_NULL,									  //pNext
425 		VkPipelineCacheCreateFlags{},				  //flags
426 		deUintptr{0},								  //initialDataSize
427 		DE_NULL										  //pInitialData
428 	};
429 
430 	return createPipelineCache(vk, device, &cacheInfo);
431 }
432 
433 /*--------------------------------------------------------------------*//*!
434  * \brief create VkPipelineLayout with descriptor sets from test parameters
435  *//*--------------------------------------------------------------------*/
createPipelineLayout(const DeviceInterface & vk,VkDevice device,const vector<VkDescriptorSetLayout> & setLayouts,const TestParams &)436 Move<VkPipelineLayout> createPipelineLayout(const DeviceInterface&				 vk,
437 											VkDevice							 device,
438 											const vector<VkDescriptorSetLayout>& setLayouts,
439 											const TestParams&)
440 {
441 	const auto layoutCreateInfo = VkPipelineLayoutCreateInfo{
442 		VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType
443 		DE_NULL,									   // pNext
444 		VkPipelineLayoutCreateFlags{},				   // flags
445 		static_cast<deUint32>(setLayouts.size()),	   // setLayoutCount
446 		setLayouts.data(),							   // pSetLayouts
447 		deUint32{0u},								   // pushConstantRangeCount
448 		DE_NULL,									   // pPushConstantRanges
449 	};
450 
451 	return createPipelineLayout(vk, device, &layoutCreateInfo);
452 }
453 
454 /*--------------------------------------------------------------------*//*!
455  * \brief create basic VkPipelineLayout from test parameters
456  *//*--------------------------------------------------------------------*/
createPipelineLayout(const DeviceInterface & vk,VkDevice device,const TestParams &)457 Move<VkPipelineLayout> createPipelineLayout(const DeviceInterface& vk, VkDevice device, const TestParams&)
458 {
459 	static constexpr auto layoutCreateInfo = VkPipelineLayoutCreateInfo{
460 		VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType
461 		DE_NULL,									   // pNext
462 		VkPipelineLayoutCreateFlags{},				   // flags
463 		deUint32{0u},								   // setLayoutCount
464 		DE_NULL,									   // pSetLayouts
465 		deUint32{0u},								   // pushConstantRangeCount
466 		DE_NULL,									   // pPushConstantRanges
467 	};
468 
469 	return createPipelineLayout(vk, device, &layoutCreateInfo);
470 }
471 
472 /*--------------------------------------------------------------------*//*!
473  * \brief Create array of shader modules
474  *//*--------------------------------------------------------------------*/
createShaderModules(const DeviceInterface & vk,VkDevice device,const BinaryCollection & collection,const vector<const char * > & names)475 vector<UniqueShaderModule> createShaderModules(const DeviceInterface&	  vk,
476 											   VkDevice					  device,
477 											   const BinaryCollection&	  collection,
478 											   const vector<const char*>& names)
479 {
480 	auto output = vector<UniqueShaderModule>{};
481 	output.reserve(names.size());
482 
483 	for (const auto& name : names)
484 	{
485 		const auto& binary	   = collection.get(name);
486 		const auto	createInfo = VkShaderModuleCreateInfo{
487 			 VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, // sType
488 			 DE_NULL,									  // pNext
489 			 VkShaderModuleCreateFlags{},				  // flags
490 			 binary.getSize(),							  // codeSize
491 			 shader_cast(binary.getBinary())			  // pCode
492 		 };
493 
494 		output.push_back(createShaderModule(vk, device, &createInfo));
495 	}
496 
497 	return output;
498 }
499 
500 /*--------------------------------------------------------------------*//*!
501  * \brief Create array of shader binding stages
502  *//*--------------------------------------------------------------------*/
createShaderStages(const vector<Move<VkShaderModule>> & modules,const vector<VkShaderStageFlagBits> & stages)503 vector<VkPipelineShaderStageCreateInfo> createShaderStages(const vector<Move<VkShaderModule>>&	modules,
504 														   const vector<VkShaderStageFlagBits>& stages)
505 {
506 	DE_ASSERT(modules.size() == stages.size());
507 
508 	auto output = vector<VkPipelineShaderStageCreateInfo>{};
509 	output.reserve(modules.size());
510 
511 	int i = 0;
512 
513 	for (const auto& module : modules)
514 	{
515 		const auto stageInfo = VkPipelineShaderStageCreateInfo{
516 			VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // sType
517 			DE_NULL,											 // pNext
518 			VkPipelineShaderStageCreateFlags{},					 // flags
519 			stages[i++],										 // stage
520 			*module,											 // module
521 			"main",												 // pName
522 			DE_NULL												 // pSpecializationInfo
523 		};
524 
525 		output.push_back(stageInfo);
526 	}
527 
528 	return output;
529 }
530 
531 } // namespace test_common
532 
533 /*--------------------------------------------------------------------*//*!
534  * \brief Graphics pipeline specific testing
535  *//*--------------------------------------------------------------------*/
536 namespace graphics_tests
537 {
538 using namespace test_common;
539 
540 /*--------------------------------------------------------------------*//*!
541  * \brief Common graphics pipeline create info initialization
542  *//*--------------------------------------------------------------------*/
getPipelineCreateInfoCommon()543 VkGraphicsPipelineCreateInfo getPipelineCreateInfoCommon()
544 {
545 	static constexpr auto VERTEX_BINDING = VkVertexInputBindingDescription{
546 		deUint32{0u},				// binding
547 		sizeof(float[4]),			// stride
548 		VK_VERTEX_INPUT_RATE_VERTEX // inputRate
549 	};
550 
551 	static constexpr auto VERTEX_ATTRIBUTE = VkVertexInputAttributeDescription{
552 		deUint32{0u},				   // location
553 		deUint32{0u},				   // binding
554 		VK_FORMAT_R32G32B32A32_SFLOAT, // format
555 		deUint32{0u}				   // offset
556 	};
557 
558 	static constexpr auto VERTEX_INPUT_STATE = VkPipelineVertexInputStateCreateInfo{
559 		VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // sType
560 		DE_NULL,												   // pNext
561 		VkPipelineVertexInputStateCreateFlags{},				   // flags
562 		deUint32{1u},											   // vertexBindingDescriptionCount
563 		&VERTEX_BINDING,										   // pVertexBindingDescriptions
564 		deUint32{1u},											   // vertexAttributeDescriptionCount
565 		&VERTEX_ATTRIBUTE										   // pVertexAttributeDescriptions
566 	};
567 
568 	static constexpr auto IA_STATE = VkPipelineInputAssemblyStateCreateInfo{
569 		VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // sType
570 		DE_NULL,													 // pNext
571 		VkPipelineInputAssemblyStateCreateFlags{},					 // flags
572 		VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,						 // topology
573 		VK_TRUE														 // primitiveRestartEnable
574 	};
575 
576 	static constexpr auto TESSALATION_STATE = VkPipelineTessellationStateCreateInfo{
577 		VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO, // sType
578 		DE_NULL,																 // pNext
579 		VkPipelineTessellationStateCreateFlags{},								 // flags
580 		deUint32{0u}															 // patchControlPoints
581 	};
582 
583 	static constexpr auto VIEWPORT = VkViewport{
584 		0.f, // x
585 		0.f, // y
586 		1.f, // width
587 		1.f, // height
588 		0.f, // minDepth
589 		1.f	 // maxDept
590 	};
591 
592 	static constexpr auto SCISSOR_RECT = VkRect2D{
593 		{0, 0},	   // offset
594 		{256, 256} // extent
595 	};
596 
597 	static constexpr auto VIEWPORT_STATE = VkPipelineViewportStateCreateInfo{
598 		VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // sType
599 		DE_NULL,											   // pNext
600 		VkPipelineViewportStateCreateFlags{},				   // flags
601 		deUint32{1u},										   // viewportCount
602 		&VIEWPORT,											   // pViewports
603 		deUint32{1u},										   // scissorCount
604 		&SCISSOR_RECT										   // pScissors
605 	};
606 
607 	static constexpr auto RASTERIZATION_STATE = VkPipelineRasterizationStateCreateInfo{
608 		VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // sType
609 		DE_NULL,													// pNext
610 		VkPipelineRasterizationStateCreateFlags{},					// flags
611 		VK_TRUE,													// depthClampEnable
612 		VK_TRUE,													// rasterizerDiscardEnable
613 		VK_POLYGON_MODE_FILL,										// polygonMode
614 		VK_CULL_MODE_NONE,											// cullMode
615 		VK_FRONT_FACE_CLOCKWISE,									// frontFace
616 		VK_FALSE,													// depthBiasEnable
617 		0.f,														// depthBiasConstantFactor
618 		0.f,														// depthBiasClamp
619 		0.f,														// depthBiasSlopeFactor
620 		1.f															// lineWidth
621 	};
622 
623 	static constexpr auto SAMPLE_MASK = VkSampleMask{};
624 
625 	static constexpr auto MULTISAMPLE_STATE = VkPipelineMultisampleStateCreateInfo{
626 		VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // sType
627 		DE_NULL,												  // pNext
628 		VkPipelineMultisampleStateCreateFlags{},				  // flags
629 		VK_SAMPLE_COUNT_1_BIT,									  // rasterizationSamples
630 		VK_FALSE,												  // sampleShadingEnable
631 		0.f,													  // minSampleShading
632 		&SAMPLE_MASK,											  // pSampleMask
633 		VK_FALSE,												  // alphaToCoverageEnable
634 		VK_FALSE												  // alphaToOneEnable
635 	};
636 
637 	static constexpr auto STENCIL_OP_STATE = VkStencilOpState{
638 		VK_STENCIL_OP_ZERO,	  // failOp
639 		VK_STENCIL_OP_ZERO,	  // passOp
640 		VK_STENCIL_OP_ZERO,	  // depthFailOp
641 		VK_COMPARE_OP_ALWAYS, // compareOp
642 		deUint32{0u},		  // compareMask
643 		deUint32{0u},		  // writeMask
644 		deUint32{0u}		  // reference
645 	};
646 
647 	static constexpr auto DEPTH_STENCIL_STATE = VkPipelineDepthStencilStateCreateInfo{
648 		VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // sType
649 		DE_NULL,													// pNext
650 		VkPipelineDepthStencilStateCreateFlags{},					// flags
651 		VK_FALSE,													// depthTestEnable
652 		VK_FALSE,													// depthWriteEnable
653 		VK_COMPARE_OP_ALWAYS,										// depthCompareOp
654 		VK_FALSE,													// depthBoundsTestEnable
655 		VK_FALSE,													// stencilTestEnable
656 		STENCIL_OP_STATE,											// front
657 		STENCIL_OP_STATE,											// back
658 		0.f,														// minDepthBounds
659 		1.f															// maxDepthBounds
660 	};
661 
662 	static constexpr auto COLOR_FLAGS_ALL = VkColorComponentFlags{VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
663 																  VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT};
664 
665 	static constexpr auto COLOR_BLEND_ATTACH_STATE = VkPipelineColorBlendAttachmentState{
666 		VK_FALSE,			  // blendEnable
667 		VK_BLEND_FACTOR_ONE,  // srcColorBlendFactor
668 		VK_BLEND_FACTOR_ZERO, // dstColorBlendFactor
669 		VK_BLEND_OP_ADD,	  // colorBlendOp
670 		VK_BLEND_FACTOR_ONE,  // srcAlphaBlendFactor
671 		VK_BLEND_FACTOR_ZERO, // dstAlphaBlendFactor
672 		VK_BLEND_OP_ADD,	  // alphaBlendOp
673 		COLOR_FLAGS_ALL		  // colorWriteMask
674 	};
675 
676 	static constexpr auto COLOR_BLEND_STATE = VkPipelineColorBlendStateCreateInfo{
677 		VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // sType
678 		DE_NULL,												  // pNext
679 		VkPipelineColorBlendStateCreateFlags{},					  // flags
680 		VK_FALSE,												  // logicOpEnable
681 		VK_LOGIC_OP_SET,										  // logicOp
682 		deUint32{1u},											  // attachmentCount
683 		&COLOR_BLEND_ATTACH_STATE,								  // pAttachments
684 		{0.f, 0.f, 0.f, 0.f}									  // blendConstants[4]
685 	};
686 
687 	static constexpr auto DYNAMIC_STATE = VkPipelineDynamicStateCreateInfo{
688 		VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // sType;
689 		DE_NULL,											  // pNext;
690 		VkPipelineDynamicStateCreateFlags{},				  // flags;
691 		deUint32{0u},										  // dynamicStateCount;
692 		DE_NULL												  // pDynamicStates;
693 	};
694 
695 	return VkGraphicsPipelineCreateInfo{
696 		VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, // sType
697 		DE_NULL,										 // pNext
698 		VkPipelineCreateFlags{},						 // flags
699 		deUint32{0u},									 // stageCount
700 		DE_NULL,										 // pStages
701 		&VERTEX_INPUT_STATE,							 // pVertexInputState
702 		&IA_STATE,										 // pInputAssemblyState
703 		&TESSALATION_STATE,								 // pTessellationState
704 		&VIEWPORT_STATE,								 // pViewportState
705 		&RASTERIZATION_STATE,							 // pRasterizationState
706 		&MULTISAMPLE_STATE,								 // pMultisampleState
707 		&DEPTH_STENCIL_STATE,							 // pDepthStencilState
708 		&COLOR_BLEND_STATE,								 // pColorBlendState
709 		&DYNAMIC_STATE,									 // pDynamicState
710 		VK_NULL_HANDLE,									 // layout
711 		VK_NULL_HANDLE,									 // renderPass
712 		deUint32{0u},									 // subpass
713 		VK_NULL_HANDLE,									 // basePipelineHandle
714 		deInt32{-1}										 // basePipelineIndex
715 	};
716 }
717 
718 /*--------------------------------------------------------------------*//*!
719  * \brief create VkGraphicsPipelineCreateInfo structs from test iteration
720  *//*--------------------------------------------------------------------*/
createPipelineCreateInfos(const TestParams::Iteration & iteration,const VkGraphicsPipelineCreateInfo & base,VkPipeline basePipeline,const TestParams & testParameter)721 vector<VkGraphicsPipelineCreateInfo> createPipelineCreateInfos(const TestParams::Iteration&		   iteration,
722 															   const VkGraphicsPipelineCreateInfo& base,
723 															   VkPipeline						   basePipeline,
724 															   const TestParams&				   testParameter)
725 {
726 	auto output = vector<VkGraphicsPipelineCreateInfo>{};
727 	output.reserve(iteration.variants.size());
728 
729 	deInt32 count			  = 0;
730 	deInt32 basePipelineIndex = -1;
731 
732 	for (VkPipelineCreateFlags flags : iteration.variants)
733 	{
734 		const auto curIndex	  = count++;
735 		auto	   createInfo = base;
736 
737 		if (testParameter.cacheType == TestParams::DERIVATIVE_INDEX)
738 		{
739 			if (flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT)
740 			{
741 				if (basePipelineIndex != -1)
742 				{
743 					flags |= VK_PIPELINE_CREATE_DERIVATIVE_BIT;
744 				}
745 			}
746 			else
747 			{
748 				flags |= VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT;
749 
750 				if (basePipelineIndex == -1)
751 				{
752 					basePipelineIndex = curIndex;
753 				}
754 			}
755 		}
756 
757 		createInfo.flags			  = flags;
758 		createInfo.basePipelineHandle = basePipeline;
759 		createInfo.basePipelineIndex  = basePipelineIndex;
760 
761 		output.push_back(createInfo);
762 	}
763 
764 	return output;
765 }
766 
767 /*--------------------------------------------------------------------*//*!
768  * \brief create VkRenderPass object for Graphics test
769  *//*--------------------------------------------------------------------*/
createRenderPass(const DeviceInterface & vk,VkDevice device,const TestParams &)770 Move<VkRenderPass> createRenderPass(const DeviceInterface& vk, VkDevice device, const TestParams&)
771 {
772 	static constexpr auto COLOR_FORMAT = VK_FORMAT_R8G8B8A8_UNORM;
773 
774 	static constexpr auto COLOR_ATTACHMENT_REF = VkAttachmentReference{
775 		deUint32{0u},							 // attachment
776 		VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // layout
777 	};
778 
779 	static constexpr auto SUBPASS = VkSubpassDescription{
780 		VkSubpassDescriptionFlags{},	 // flags
781 		VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint
782 		deUint32{0u},					 // inputAttachmentCount
783 		DE_NULL,						 // pInputAttachments
784 		deUint32{1u},					 // colorAttachmentCount
785 		&COLOR_ATTACHMENT_REF,			 // pColorAttachments
786 		DE_NULL,						 // pResolveAttachments
787 		DE_NULL,						 // pDepthStencilAttachment
788 		deUint32{0u},					 // preserveAttachmentCount
789 		DE_NULL							 // pPreserveAttachments
790 	};
791 
792 	static constexpr auto COLOR_ATTACHMENT = VkAttachmentDescription{
793 		VkAttachmentDescriptionFlags{},	  // flags
794 		COLOR_FORMAT,					  // format
795 		VK_SAMPLE_COUNT_1_BIT,			  // samples
796 		VK_ATTACHMENT_LOAD_OP_CLEAR,	  // loadOp
797 		VK_ATTACHMENT_STORE_OP_STORE,	  // storeOp
798 		VK_ATTACHMENT_LOAD_OP_DONT_CARE,  // stencilLoadOp
799 		VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilStoreOp
800 		VK_IMAGE_LAYOUT_UNDEFINED,		  // initialLayout
801 		VK_IMAGE_LAYOUT_PRESENT_SRC_KHR	  // finalLayout
802 	};
803 
804 	static constexpr auto RENDER_PASS_CREATE_INFO = VkRenderPassCreateInfo{
805 		VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // sType
806 		DE_NULL,								   // pNext
807 		VkRenderPassCreateFlags{},				   // flags
808 		deUint32{1u},							   // attachmentCount
809 		&COLOR_ATTACHMENT,						   // pAttachments
810 		deUint32{1u},							   // subpassCount
811 		&SUBPASS,								   // pSubpasses
812 		deUint32{0u},							   // dependencyCount
813 		DE_NULL									   // pDependencies
814 	};
815 
816 	return createRenderPass(vk, device, &RENDER_PASS_CREATE_INFO);
817 }
818 
819 /*--------------------------------------------------------------------*//*!
820  * \brief Initialize shader programs
821  *//*--------------------------------------------------------------------*/
initPrograms(SourceCollections & dst,const TestParams &)822 void initPrograms(SourceCollections& dst, const TestParams&)
823 {
824 	using ::glu::FragmentSource;
825 	using ::glu::VertexSource;
826 
827 	// Vertex Shader
828 	static const StringTemplate VS_TEXT = {"#version 310 es\n"
829 										   "layout(location = 0) in vec4 position;\n"
830 										   "layout(location = 0) out vec3 vertColor;\n"
831 										   "void main (void)\n"
832 										   "{\n"
833 										   "  gl_Position = position;\n"
834 										   "  vertColor = vec3(${0}, ${1}, ${2});\n"
835 										   "}\n"};
836 
837 	// Fragment Shader
838 	static const StringTemplate FS_TEXT = {"#version 310 es\n"
839 										   "precision highp float;\n"
840 										   "layout(location = 0) in vec3 vertColor;\n"
841 										   "layout(location = 0) out vec4 outColor;\n"
842 										   "void main (void)\n"
843 										   "{\n"
844 										   "  const vec3 fragColor = vec3(${0}, ${1}, ${2});\n"
845 										   "  outColor = vec4((fragColor + vertColor) * 0.5, 1.0);\n"
846 										   "}\n"};
847 
848 	dst.glslSources.add("vertex") << VertexSource{VS_TEXT.format(randomFloat(), randomFloat(), randomFloat())};
849 	dst.glslSources.add("fragment") << FragmentSource{FS_TEXT.format(randomFloat(), randomFloat(), randomFloat())};
850 }
851 
852 /*--------------------------------------------------------------------*//*!
853  * \brief return both result and elapsed time from pipeline creation
854  *//*--------------------------------------------------------------------*/
855 template <typename create_infos_t, typename pipelines_t>
timePipelineCreation(const DeviceInterface & vk,const VkDevice device,const VkPipelineCache cache,const create_infos_t & createInfos,pipelines_t & pipelines,const VkAllocationCallbacks * pAllocator=DE_NULL)856 TimedResult timePipelineCreation(const DeviceInterface&		  vk,
857 								 const VkDevice				  device,
858 								 const VkPipelineCache		  cache,
859 								 const create_infos_t&		  createInfos,
860 								 pipelines_t&				  pipelines,
861 								 const VkAllocationCallbacks* pAllocator = DE_NULL)
862 {
863 	DE_ASSERT(createInfos.size() <= pipelines.size());
864 
865 	const auto timeStart = high_resolution_clock::now();
866 	const auto result	 = vk.createGraphicsPipelines(
867 		   device, cache, static_cast<deUint32>(createInfos.size()), createInfos.data(), pAllocator, pipelines.data());
868 	const auto elapsed = high_resolution_clock::now() - timeStart;
869 	return {result, elapsed};
870 }
871 
872 /*--------------------------------------------------------------------*//*!
873  * \brief Test instance function
874  *//*--------------------------------------------------------------------*/
testInstance(Context & context,const TestParams & testParameter)875 TestStatus testInstance(Context& context, const TestParams& testParameter)
876 {
877 	const auto& vk			  = context.getDeviceInterface();
878 	const auto	device		  = context.getDevice();
879 	const auto	pipelineCache = createPipelineCache(vk, device, testParameter);
880 	const auto	layout		  = createPipelineLayout(vk, device, testParameter);
881 	const auto	renderPass	  = createRenderPass(vk, device, testParameter);
882 	const auto	modules		  = createShaderModules(vk, device, context.getBinaryCollection(), {"vertex", "fragment"});
883 	const auto	shaderStages  = createShaderStages(modules, {VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_FRAGMENT_BIT});
884 
885 	// Placeholder for base pipeline if using cacheType == DERIVATIVE_HANDLE
886 	auto basePipeline = UniquePipeline{};
887 
888 	auto baseCreateInfo		  = getPipelineCreateInfoCommon();
889 	baseCreateInfo.layout	  = layout.get();
890 	baseCreateInfo.renderPass = renderPass.get();
891 	baseCreateInfo.stageCount = static_cast<deUint32>(shaderStages.size());
892 	baseCreateInfo.pStages	  = shaderStages.data();
893 
894 	auto results = vector<VkResult>{};
895 	results.reserve(testParameter.iterations.size());
896 
897 	for (const auto& i : testParameter.iterations)
898 	{
899 		const auto createInfos = createPipelineCreateInfos(i, baseCreateInfo, basePipeline.get(), testParameter);
900 		auto	   created	   = vector<VkPipeline>{};
901 		created.resize(createInfos.size());
902 
903 		const auto timedResult = timePipelineCreation(vk, device, pipelineCache.get(), createInfos, created);
904 		auto	   pipelines   = wrapHandles(vk, device, created);
905 
906 		const auto status = validateResults(timedResult.result, pipelines, timedResult.elapsed, i.validators);
907 		if (status.getCode() != QP_TEST_RESULT_PASS)
908 		{
909 			return status;
910 		}
911 
912 		if ((testParameter.cacheType == TestParams::DERIVATIVE_HANDLE) && (*basePipeline == VK_NULL_HANDLE))
913 		{
914 			for (auto& pipeline : pipelines)
915 			{
916 				if (*pipeline != VK_NULL_HANDLE)
917 				{
918 					basePipeline = pipeline;
919 					break;
920 				}
921 			}
922 		}
923 
924 		results.push_back(timedResult.result);
925 	}
926 
927 	static const StringTemplate PASS_MSG = {"Test Passed. ${0}"};
928 	return TestStatus::pass(PASS_MSG.format(getResultsString(results)));
929 }
930 
931 } // namespace graphics_tests
932 
933 /*--------------------------------------------------------------------*//*!
934  * \brief Compute pipeline specific testing
935  *//*--------------------------------------------------------------------*/
936 namespace compute_tests
937 {
938 using namespace test_common;
939 
940 /*--------------------------------------------------------------------*//*!
941  * \brief create VkComputePipelineCreateInfo structs from test iteration
942  *//*--------------------------------------------------------------------*/
createPipelineCreateInfos(const TestParams::Iteration & iteration,const VkComputePipelineCreateInfo & base,VkPipeline basePipeline,const TestParams & testParameter)943 vector<VkComputePipelineCreateInfo> createPipelineCreateInfos(const TestParams::Iteration&		 iteration,
944 															  const VkComputePipelineCreateInfo& base,
945 															  VkPipeline						 basePipeline,
946 															  const TestParams&					 testParameter)
947 {
948 	auto output = vector<VkComputePipelineCreateInfo>{};
949 	output.reserve(iteration.variants.size());
950 
951 	deInt32 count			  = 0;
952 	deInt32 basePipelineIndex = -1;
953 
954 	for (VkPipelineCreateFlags flags : iteration.variants)
955 	{
956 		const auto curIndex	  = count++;
957 		auto	   createInfo = base;
958 
959 		if (testParameter.cacheType == TestParams::DERIVATIVE_INDEX)
960 		{
961 			if (flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT)
962 			{
963 				if (basePipelineIndex != -1)
964 				{
965 					flags |= VK_PIPELINE_CREATE_DERIVATIVE_BIT;
966 				}
967 			}
968 			else
969 			{
970 				flags |= VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT;
971 
972 				if (basePipelineIndex == -1)
973 				{
974 					basePipelineIndex = curIndex;
975 				}
976 			}
977 		}
978 
979 		createInfo.flags			  = flags;
980 		createInfo.basePipelineHandle = basePipeline;
981 		createInfo.basePipelineIndex  = basePipelineIndex;
982 
983 		output.push_back(createInfo);
984 	}
985 
986 	return output;
987 }
988 
989 /*--------------------------------------------------------------------*//*!
990  * \brief create compute descriptor set layout
991  *//*--------------------------------------------------------------------*/
createDescriptorSetLayout(const DeviceInterface & vk,VkDevice device,const TestParams &)992 Move<VkDescriptorSetLayout> createDescriptorSetLayout(const DeviceInterface& vk, VkDevice device, const TestParams&)
993 {
994 	static constexpr auto DESCRIPTOR_SET_LAYOUT_BINDING = VkDescriptorSetLayoutBinding{
995 		deUint32{0u},					   // binding
996 		VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // descriptorType
997 		deUint32{1u},					   // descriptorCount
998 		VK_SHADER_STAGE_COMPUTE_BIT,	   // stageFlags
999 		DE_NULL							   // pImmutableSamplers
1000 	};
1001 
1002 	static constexpr auto DESCRIPTOR_SET_LAYOUT_CREATE_INFO = VkDescriptorSetLayoutCreateInfo{
1003 		VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, // sType
1004 		DE_NULL,											 // pNext
1005 		VkDescriptorSetLayoutCreateFlags{},					 // flags
1006 		deUint32{1u},										 // bindingCount
1007 		&DESCRIPTOR_SET_LAYOUT_BINDING						 // pBindings
1008 	};
1009 
1010 	return createDescriptorSetLayout(vk, device, &DESCRIPTOR_SET_LAYOUT_CREATE_INFO);
1011 }
1012 
1013 /*--------------------------------------------------------------------*//*!
1014  * \brief Initialize shader programs
1015  *//*--------------------------------------------------------------------*/
initPrograms(SourceCollections & dst,const TestParams &)1016 void initPrograms(SourceCollections& dst, const TestParams&)
1017 {
1018 	using ::glu::ComputeSource;
1019 
1020 	static const StringTemplate CS_TEXT = {"#version 450\n"
1021 										   "precision highp float;\n"
1022 										   "layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in;\n"
1023 										   "layout (std140, binding = 0) buffer buf { vec3 data[]; };\n"
1024 										   "void main (void)\n"
1025 										   "{\n"
1026 										   "  data[gl_GlobalInvocationID.x] = vec3(${0}, ${1}, ${2});\n"
1027 										   "}\n"};
1028 
1029 	dst.glslSources.add("compute")
1030 		<< ComputeSource{CS_TEXT.format(randomFloat(), randomFloat(), randomFloat())};
1031 }
1032 
1033 /*--------------------------------------------------------------------*//*!
1034  * \brief return both result and elapsed time from pipeline creation
1035  *//*--------------------------------------------------------------------*/
1036 template <typename create_infos_t, typename pipelines_t>
timePipelineCreation(const DeviceInterface & vk,const VkDevice device,const VkPipelineCache cache,const create_infos_t & createInfos,pipelines_t & pipelines,const VkAllocationCallbacks * pAllocator=DE_NULL)1037 TimedResult timePipelineCreation(const DeviceInterface&		  vk,
1038 								 const VkDevice				  device,
1039 								 const VkPipelineCache		  cache,
1040 								 const create_infos_t&		  createInfos,
1041 								 pipelines_t&				  pipelines,
1042 								 const VkAllocationCallbacks* pAllocator = DE_NULL)
1043 {
1044 	DE_ASSERT(createInfos.size() <= pipelines.size());
1045 
1046 	const auto timeStart = high_resolution_clock::now();
1047 	const auto result	 = vk.createComputePipelines(
1048 		   device, cache, static_cast<deUint32>(createInfos.size()), createInfos.data(), pAllocator, pipelines.data());
1049 	const auto elapsed = high_resolution_clock::now() - timeStart;
1050 	return {result, elapsed};
1051 }
1052 
1053 /*--------------------------------------------------------------------*//*!
1054  * \brief Test instance function
1055  *//*--------------------------------------------------------------------*/
testInstance(Context & context,const TestParams & testParameter)1056 TestStatus testInstance(Context& context, const TestParams& testParameter)
1057 {
1058 	const auto& vk					= context.getDeviceInterface();
1059 	const auto	device				= context.getDevice();
1060 	const auto	pipelineCache		= createPipelineCache(vk, device, testParameter);
1061 	const auto	descriptorSetLayout = createDescriptorSetLayout(vk, device, testParameter);
1062 	const auto	pipelineLayout		= createPipelineLayout(vk, device, {descriptorSetLayout.get()}, testParameter);
1063 	const auto	modules				= createShaderModules(vk, device, context.getBinaryCollection(), {"compute"});
1064 	const auto	shaderStages		= createShaderStages(modules, {VK_SHADER_STAGE_COMPUTE_BIT});
1065 
1066 	// Placeholder for base pipeline if using cacheType == DERIVATIVE_HANDLE
1067 	auto basePipeline = UniquePipeline{};
1068 
1069 	const auto baseCreateInfo = VkComputePipelineCreateInfo{
1070 		VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // sType
1071 		DE_NULL,										// pNext
1072 		VkPipelineCreateFlags{},						// flags
1073 		shaderStages[0],								// stage
1074 		pipelineLayout.get(),							// layout
1075 		VK_NULL_HANDLE,									// basePipelineHandle
1076 		deInt32{-1}										// basePipelineIndex
1077 	};
1078 
1079 	auto results = vector<VkResult>{};
1080 	results.reserve(testParameter.iterations.size());
1081 
1082 	for (const auto& i : testParameter.iterations)
1083 	{
1084 		const auto createInfos = createPipelineCreateInfos(i, baseCreateInfo, basePipeline.get(), testParameter);
1085 		auto	   created	   = vector<VkPipeline>{};
1086 		created.resize(createInfos.size());
1087 
1088 		const auto timedResult = timePipelineCreation(vk, device, pipelineCache.get(), createInfos, created);
1089 		auto	   pipelines   = wrapHandles(vk, device, created);
1090 
1091 		const auto status = validateResults(timedResult.result, pipelines, timedResult.elapsed, i.validators);
1092 		if (status.getCode() != QP_TEST_RESULT_PASS)
1093 		{
1094 			return status;
1095 		}
1096 
1097 		if ((testParameter.cacheType == TestParams::DERIVATIVE_HANDLE) && (*basePipeline == VK_NULL_HANDLE))
1098 		{
1099 			for (auto& pipeline : pipelines)
1100 			{
1101 				if (*pipeline != VK_NULL_HANDLE)
1102 				{
1103 					basePipeline = pipeline;
1104 					break;
1105 				}
1106 			}
1107 		}
1108 
1109 		results.push_back(timedResult.result);
1110 	}
1111 
1112 	static const StringTemplate PASS_MSG = {"Test Passed. ${0}"};
1113 	return TestStatus::pass(PASS_MSG.format(getResultsString(results)));
1114 }
1115 
1116 } // namespace compute_tests
1117 
1118 using namespace test_common;
1119 
1120 // Disable formatting on this next block for readability
1121 // clang-format off
1122 /*--------------------------------------------------------------------*//*!
1123  * \brief Duplicate single pipeline recreation with explicit caching
1124  *//*--------------------------------------------------------------------*/
1125 static constexpr TestParams DUPLICATE_SINGLE_RECREATE_EXPLICIT_CACHING =
1126 {
1127 	"duplicate_single_recreate_explicit_caching",
1128 	"Duplicate single pipeline recreation with explicit caching",
1129 	TestParams::EXPLICIT_CACHE,
1130 	TestParams::IterationArray
1131 	{
1132 		TestParams::Iteration{
1133 			// Iteration [0]: Force compilation of pipeline
1134 			TestParams::Iteration::SINGLE_NORMAL,
1135 			ValidatorArray{
1136 				// Fail if result is not VK_SUCCESS
1137 				checkResult<VK_SUCCESS>,
1138 				// Fail if pipeline is not valid
1139 				checkPipelineMustBeValid<0>
1140 			}
1141 		},
1142 		TestParams::Iteration{
1143 			// Iteration [1]: Request compilation of same pipeline without compile
1144 			TestParams::Iteration::SINGLE_NOCOMPILE,
1145 			ValidatorArray{
1146 				// Warn if result is not VK_SUCCESS
1147 				checkResult<VK_SUCCESS, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
1148 				// Warn if pipeline is not valid
1149 				checkPipelineMustBeValid<0, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
1150 				// Warn if pipeline took too long
1151 				checkElapsedTime<ELAPSED_TIME_FAST, QP_TEST_RESULT_QUALITY_WARNING>
1152 			}
1153 		}
1154 	}
1155 };
1156 
1157 /*--------------------------------------------------------------------*//*!
1158  * \brief Duplicate single pipeline recreation with no explicit cache
1159  *//*--------------------------------------------------------------------*/
1160 static constexpr TestParams DUPLICATE_SINGLE_RECREATE_NO_CACHING =
1161 {
1162 	"duplicate_single_recreate_no_caching",
1163 	"Duplicate single pipeline recreation with no explicit cache",
1164 	TestParams::NO_CACHE,
1165 	TestParams::IterationArray{
1166 		TestParams::Iteration{
1167 			// Iteration [0]: Force compilation of pipeline
1168 			TestParams::Iteration::SINGLE_NORMAL,
1169 			ValidatorArray{
1170 				// Fail if result is not VK_SUCCESS
1171 				checkResult<VK_SUCCESS>,
1172 				// Fail if pipeline is not valid
1173 				checkPipelineMustBeValid<0>
1174 			}
1175 		},
1176 		TestParams::Iteration{
1177 			// Iteration [1]: Request compilation of same pipeline without compile
1178 			TestParams::Iteration::SINGLE_NOCOMPILE,
1179 			ValidatorArray{
1180 				// Warn if pipeline took too long
1181 				checkElapsedTime<ELAPSED_TIME_FAST, QP_TEST_RESULT_QUALITY_WARNING>
1182 			}
1183 		}
1184 	}
1185 };
1186 
1187 /*--------------------------------------------------------------------*//*!
1188  * \brief Duplicate single pipeline recreation using derivative pipelines
1189  *//*--------------------------------------------------------------------*/
1190 static constexpr TestParams DUPLICATE_SINGLE_RECREATE_DERIVATIVE =
1191 {
1192 	"duplicate_single_recreate_derivative",
1193 	"Duplicate single pipeline recreation using derivative pipelines",
1194 	TestParams::DERIVATIVE_HANDLE,
1195 	TestParams::IterationArray{
1196 		TestParams::Iteration{
1197 			// Iteration [0]: Force compilation of pipeline
1198 			TestParams::Iteration::SINGLE_NORMAL,
1199 			ValidatorArray{
1200 				// Fail if result is not VK_SUCCESS
1201 				checkResult<VK_SUCCESS>,
1202 				// Fail if pipeline is not valid
1203 				checkPipelineMustBeValid<0>
1204 				}
1205 			},
1206 		TestParams::Iteration{
1207 			// Iteration [1]: Request compilation of same pipeline without compile
1208 			TestParams::Iteration::SINGLE_NOCOMPILE,
1209 			ValidatorArray{
1210 				// Warn if pipeline took too long
1211 				checkElapsedTime<ELAPSED_TIME_FAST, QP_TEST_RESULT_QUALITY_WARNING>
1212 			}
1213 		}
1214 	}
1215 };
1216 
1217 /*--------------------------------------------------------------------*//*!
1218  * \brief Single creation of never before seen pipeline without compile
1219  *//*--------------------------------------------------------------------*/
1220 static constexpr TestParams SINGLE_PIPELINE_NO_COMPILE =
1221 {
1222 	"single_pipeline_no_compile",
1223 	"Single creation of never before seen pipeline without compile",
1224 	TestParams::NO_CACHE,
1225 	TestParams::IterationArray{
1226 		TestParams::Iteration{
1227 			TestParams::Iteration::SINGLE_NOCOMPILE,
1228 			ValidatorArray{
1229 				// Warn if pipeline took too long
1230 				checkElapsedTime<ELAPSED_TIME_IMMEDIATE, QP_TEST_RESULT_QUALITY_WARNING>
1231 			}
1232 		}
1233 	}
1234 };
1235 
1236 /*--------------------------------------------------------------------*//*!
1237  * \brief Batch creation of duplicate pipelines with explicit caching
1238  *//*--------------------------------------------------------------------*/
1239 static constexpr TestParams DUPLICATE_BATCH_PIPELINES_EXPLICIT_CACHE =
1240 {
1241 	"duplicate_batch_pipelines_explicit_cache",
1242 	"Batch creation of duplicate pipelines with explicit caching",
1243 	TestParams::EXPLICIT_CACHE,
1244 	TestParams::IterationArray{
1245 		TestParams::Iteration{
1246 			TestParams::Iteration::BATCH_NOCOMPILE_COMPILE_NOCOMPILE,
1247 			ValidatorArray{
1248 				// Fail if pipeline[1] is not valid
1249 				checkPipelineMustBeValid<1>,
1250 				// Warn if result is not VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT
1251 				checkResult<VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
1252 				// Warn if pipelines[0] is not VK_NULL_HANDLE
1253 				checkPipelineMustBeNull<0, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
1254 				// Warn if pipelines[2] is not valid
1255 				checkPipelineMustBeValid<2, QP_TEST_RESULT_COMPATIBILITY_WARNING>
1256 			}
1257 		}
1258 	}
1259 };
1260 
1261 /*--------------------------------------------------------------------*//*!
1262  * \brief Batch creation of duplicate pipelines with no caching
1263  *//*--------------------------------------------------------------------*/
1264 static constexpr TestParams DUPLICATE_BATCH_PIPELINES_NO_CACHE =
1265 {
1266 	"duplicate_batch_pipelines_no_cache",
1267 	"Batch creation of duplicate pipelines with no caching",
1268 	TestParams::EXPLICIT_CACHE,
1269 	TestParams::IterationArray{
1270 		TestParams::Iteration{
1271 			TestParams::Iteration::BATCH_NOCOMPILE_COMPILE_NOCOMPILE,
1272 			ValidatorArray{
1273 				// Fail if pipeline[1] is not valid
1274 				checkPipelineMustBeValid<1>,
1275 				// Warn if result is not VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT
1276 				checkResult<VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
1277 				// Warn if pipelines[0] is not VK_NULL_HANDLE
1278 				checkPipelineMustBeNull<0, QP_TEST_RESULT_COMPATIBILITY_WARNING>
1279 			}
1280 		}
1281 	}
1282 };
1283 
1284 /*--------------------------------------------------------------------*//*!
1285  * \brief Batch creation of duplicate pipelines with derivative pipeline index
1286  *//*--------------------------------------------------------------------*/
1287 static constexpr TestParams DUPLICATE_BATCH_PIPELINES_DERIVATIVE_INDEX =
1288 {
1289 	"duplicate_batch_pipelines_derivative_index",
1290 	"Batch creation of duplicate pipelines with derivative pipeline index",
1291 	TestParams::DERIVATIVE_INDEX,
1292 	TestParams::IterationArray{
1293 		TestParams::Iteration{
1294 			TestParams::Iteration::BATCH_NOCOMPILE_COMPILE_NOCOMPILE,
1295 			ValidatorArray{
1296 				// Fail if pipeline[1] is not valid
1297 				checkPipelineMustBeValid<1>,
1298 				// Warn if result is not VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT
1299 				checkResult<VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
1300 				// Warn if pipelines[0] is not VK_NULL_HANDLE
1301 				checkPipelineMustBeNull<0, QP_TEST_RESULT_COMPATIBILITY_WARNING>
1302 			}
1303 		}
1304 	}
1305 };
1306 
1307 /*--------------------------------------------------------------------*//*!
1308  * \brief Batch creation of pipelines with early return
1309  *//*--------------------------------------------------------------------*/
1310 static constexpr TestParams BATCH_PIPELINES_EARLY_RETURN =
1311 {
1312 	"batch_pipelines_early_return",
1313 	"Batch creation of pipelines with early return",
1314 	TestParams::NO_CACHE,
1315 	TestParams::IterationArray{
1316 		TestParams::Iteration{
1317 			TestParams::Iteration::BATCH_RETURN_COMPILE_NOCOMPILE,
1318 			ValidatorArray{
1319 				// fail if a valid pipeline follows the early-return failure
1320 				checkPipelineNullAfterIndex<0>,
1321 				// Warn if return was not immediate
1322 				checkElapsedTime<ELAPSED_TIME_IMMEDIATE, QP_TEST_RESULT_QUALITY_WARNING>,
1323 				// Warn if pipelines[0] is not VK_NULL_HANDLE
1324 				checkPipelineMustBeNull<0, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
1325 				// Warn if result is not VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT
1326 				checkResult<VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT, QP_TEST_RESULT_COMPATIBILITY_WARNING>
1327 			}
1328 		}
1329 	}
1330 };
1331 
1332 /*--------------------------------------------------------------------*//*!
1333  * \brief Full array of test cases
1334  *//*--------------------------------------------------------------------*/
1335 static constexpr TestParams TEST_CASES[] =
1336 {
1337 	SINGLE_PIPELINE_NO_COMPILE,
1338 	BATCH_PIPELINES_EARLY_RETURN,
1339 	DUPLICATE_SINGLE_RECREATE_EXPLICIT_CACHING,
1340 	DUPLICATE_SINGLE_RECREATE_NO_CACHING,
1341 	DUPLICATE_SINGLE_RECREATE_DERIVATIVE,
1342 	DUPLICATE_BATCH_PIPELINES_EXPLICIT_CACHE,
1343 	DUPLICATE_BATCH_PIPELINES_NO_CACHE,
1344 	DUPLICATE_BATCH_PIPELINES_DERIVATIVE_INDEX
1345 };
1346 // clang-format on
1347 
1348 /*--------------------------------------------------------------------*//*!
1349  * \brief Variadic version of de::newMovePtr
1350  *//*--------------------------------------------------------------------*/
1351 template <typename T, typename... args_t>
newMovePtr(args_t &&...args)1352 inline de::MovePtr<T> newMovePtr(args_t&&... args)
1353 {
1354 	return de::MovePtr<T>(new T(::std::forward<args_t>(args)...));
1355 }
1356 
1357 /*--------------------------------------------------------------------*//*!
1358  * \brief Make test group consisting of graphics pipeline tests
1359  *//*--------------------------------------------------------------------*/
addGraphicsPipelineTests(TestCaseGroup & group)1360 void addGraphicsPipelineTests(TestCaseGroup& group)
1361 {
1362 	using namespace graphics_tests;
1363 
1364 	auto tests = newMovePtr<TestCaseGroup>(
1365 		group.getTestContext(), "graphics_pipelines", "Test pipeline creation cache control with graphics pipelines");
1366 
1367 	for (const auto& params : TEST_CASES)
1368 	{
1369 		addFunctionCaseWithPrograms<const TestParams&>(
1370 			tests.get(), params.name, params.description, checkSupport, initPrograms, testInstance, params);
1371 	}
1372 
1373 	group.addChild(tests.release());
1374 }
1375 
1376 /*--------------------------------------------------------------------*//*!
1377  * \brief Make test group consisting of compute pipeline tests
1378  *//*--------------------------------------------------------------------*/
addComputePipelineTests(TestCaseGroup & group)1379 void addComputePipelineTests(TestCaseGroup& group)
1380 {
1381 	using namespace compute_tests;
1382 
1383 	auto tests = newMovePtr<TestCaseGroup>(
1384 		group.getTestContext(), "compute_pipelines", "Test pipeline creation cache control with compute pipelines");
1385 
1386 	for (const auto& params : TEST_CASES)
1387 	{
1388 		addFunctionCaseWithPrograms<const TestParams&>(
1389 			tests.get(), params.name, params.description, checkSupport, initPrograms, testInstance, params);
1390 	}
1391 
1392 	group.addChild(tests.release());
1393 }
1394 
1395 } // namespace
1396 
1397 /*--------------------------------------------------------------------*//*!
1398  * \brief Make pipeline creation cache control test group
1399  *//*--------------------------------------------------------------------*/
createCacheControlTests(TestContext & testCtx)1400 TestCaseGroup* createCacheControlTests(TestContext& testCtx)
1401 {
1402 	auto tests = newMovePtr<TestCaseGroup>(testCtx, "creation_cache_control", "pipeline creation cache control tests");
1403 
1404 	addGraphicsPipelineTests(*tests);
1405 	addComputePipelineTests(*tests);
1406 
1407 	return tests.release();
1408 }
1409 
1410 } // namespace pipeline
1411 
1412 } // namespace vkt
1413