1/*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2020 The Khronos Group Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *	  http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Ray Tracing Pipeline Flags tests
22 *//*--------------------------------------------------------------------*/
23
24#include "vktRayTracingPipelineFlagsTests.hpp"
25
26#include "vkDefs.hpp"
27#include "vktTestCase.hpp"
28#include "vktTestGroupUtil.hpp"
29#include "vktCustomInstancesDevices.hpp"
30#include "vkCmdUtil.hpp"
31#include "vkObjUtil.hpp"
32#include "vkBuilderUtil.hpp"
33#include "vkBarrierUtil.hpp"
34#include "vkBufferWithMemory.hpp"
35#include "vkImageWithMemory.hpp"
36#include "vkTypeUtil.hpp"
37#include "vkImageUtil.hpp"
38#include "vkRayTracingUtil.hpp"
39#include "tcuCommandLine.hpp"
40#include "tcuTextureUtil.hpp"
41#include "tcuStringTemplate.hpp"
42
43#include <algorithm>
44#include <array>
45#include <cmath>
46#include <functional>
47#include <iterator>
48#include <memory>
49#include <set>
50#include <tuple>
51
52#ifdef INTERNAL_DEBUG
53#include <iostream>
54#endif
55
56#define ALL_RAY_TRACING_STAGES	(VK_SHADER_STAGE_RAYGEN_BIT_KHR			\
57								| VK_SHADER_STAGE_ANY_HIT_BIT_KHR		\
58								| VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR	\
59								| VK_SHADER_STAGE_MISS_BIT_KHR			\
60								| VK_SHADER_STAGE_INTERSECTION_BIT_KHR	\
61								| VK_SHADER_STAGE_CALLABLE_BIT_KHR)
62namespace vkt
63{
64namespace RayTracing
65{
66namespace
67{
68using namespace vk;
69using namespace vkt;
70
71#define ALIGN_STD430(type_) alignas(sizeof(type_)) type_
72
73#define INRANGE(x_, a_, b_) (((x_) >= (a_) && (x_) <= (b_)) || ((x_) >= (b_) && (x_) <= (a_)))
74
75enum class GeometryTypes : deUint32
76{
77	None			= 0x0,
78	Triangle		= 0x1,
79	Box				= 0x2,
80	TriangleAndBox	= Triangle | Box
81};
82
83struct TestParams
84{
85	deUint32				width;
86	deUint32				height;
87	VkBool32				onHhost;
88	VkPipelineCreateFlags	flags;
89	bool					useLibs;
90	bool					useMaintenance5;
91	deUint32				instCount;
92	GeometryTypes			geomTypes;
93	deUint32				geomCount;
94	deUint32				stbRecStride;
95	deUint32				stbRecOffset;
96	float					accuracy;
97#define ENABLED(val_, mask_) (((val_)&(mask_))==(mask_))
98	bool miss()		const {	return ENABLED(flags, VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_MISS_SHADERS_BIT_KHR);	}
99	bool ahit()		const { return ENABLED(flags, VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_ANY_HIT_SHADERS_BIT_KHR); }
100	bool chit()		const { return ENABLED(flags, VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_CLOSEST_HIT_SHADERS_BIT_KHR); }
101	bool isect()	const { return ENABLED(flags, VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_BIT_KHR); }
102};
103
104tcu::Vec3 rotateCcwZ (const tcu::Vec3& p, const tcu::Vec3& center, const float& radians)
105{
106	const float s = std::sin(radians);
107	const float c = std::cos(radians);
108	const auto  t = p - center;
109	return tcu::Vec3(c * t.x() - s * t.y(), s * t.x() + c * t.y(), t.z()) + center;
110}
111
112bool pointInRect2D(const tcu::Vec3& p, const tcu::Vec3& p0, const tcu::Vec3& p1)
113{
114	return INRANGE(p.x(), p0.x(), p1.x()) && INRANGE(p.y(), p0.y(), p1.y());
115}
116
117deUint32 computeEffectiveShaderGroupCount(const TestParams& p)
118{
119	DE_ASSERT(p.instCount && p.geomCount);
120	return (p.geomCount * p.stbRecStride + p.stbRecOffset + 1);
121}
122
123class RayTracingTestPipeline;
124
125class PipelineFlagsCase : public TestCase
126{
127public:
128							PipelineFlagsCase		(tcu::TestContext&	testCtx,
129													 const std::string&	name,
130													 const TestParams&	testParams);
131	virtual					~PipelineFlagsCase		(void) = default;
132	virtual void			initPrograms			(SourceCollections&	programCollection) const override;
133	virtual TestInstance*	createInstance			(Context&			context) const override;
134	virtual void			checkSupport			(Context&			context) const override;
135
136	const deUint32&			shaderGroupHandleSize;
137	const deUint32&			shaderGroupBaseAlignment;
138
139private:
140	const TestParams		m_params;
141
142	const tcu::IVec4		m_rgenPayload;
143	const deInt32			m_defMissRetGreenComp;
144	const deInt32			m_defTriRetGreenComp;
145	const deInt32			m_defBoxRetGreenComp;
146	static	deInt32			calcDefBoxRetGreenComp	(const TestParams&	params,
147													 deInt32			defTriRetGreenComp);
148
149	static deUint32			m_shaderGroupHandleSize;
150	static deUint32			m_shaderGroupBaseAlignment;
151};
152
153deInt32 PipelineFlagsCase::calcDefBoxRetGreenComp (const TestParams& params, deInt32 defTriRetGreenComp)
154{
155	const deUint32	nameCount		= params.stbRecStride ? (params.geomCount * params.instCount) : params.instCount;
156	const deUint32	triangleCount	= (params.geomTypes == GeometryTypes::Triangle || params.geomTypes == GeometryTypes::TriangleAndBox) ? nameCount : 0u;
157	return defTriRetGreenComp + std::max(triangleCount, 32u);
158}
159deUint32 PipelineFlagsCase::m_shaderGroupHandleSize;
160deUint32 PipelineFlagsCase::m_shaderGroupBaseAlignment;
161
162class PipelineFlagsInstance : public TestInstance
163{
164	using TopLevelASPtr		= de::SharedPtr<TopLevelAccelerationStructure>;
165	using BottomLevelASPtr	= de::SharedPtr<BottomLevelAccelerationStructure>;
166	using BottomLevelASPtrs	= std::vector<BottomLevelASPtr>;
167	using TriGeometry		= std::array<tcu::Vec3, 3>;
168	using BoxGeometry		= std::array<tcu::Vec3, 2>;
169
170	friend class RayTracingTestPipeline;
171public:
172									PipelineFlagsInstance					(Context&					context,
173																			 const TestParams&			params,
174																			 const deUint32&			shaderGroupHandleSize_,
175																			 const deUint32&			shaderGroupBaseAlignment_,
176																			 const tcu::IVec4&			rgenPayload_,
177																			 deInt32					defMissRetGreenComp_,
178																			 deInt32					defTriRetGreenComp_,
179																			 deInt32					defBoxRetGreenComp_);
180	virtual							~PipelineFlagsInstance					(void) =  default;
181
182	struct ShaderRecordEXT
183	{
184		ALIGN_STD430(GeometryTypes)	geomType;
185		ALIGN_STD430(deUint32)		geomIndex;
186		ALIGN_STD430(tcu::IVec4)	retValue;
187
188		ShaderRecordEXT	();
189		ShaderRecordEXT	(GeometryTypes type, deUint32 index, const tcu::IVec4& ret);
190	};
191
192	virtual tcu::TestStatus			iterate									(void) override;
193
194	const tcu::IVec4				rgenPayload;
195	const deInt32					defMissRetGreenComp;
196	const deInt32					defTriRetGreenComp;
197	const deInt32					defBoxRetGreenComp;
198
199	const deUint32					shaderGroupHandleSize;
200	const deUint32					shaderGroupBaseAlignment;
201
202private:
203	struct HitGroup;
204	using ShaderRecordEntry	= std::tuple<VkShaderStageFlags, HitGroup, ShaderRecordEXT, bool /* initalized */>;
205
206	VkImageCreateInfo				makeImageCreateInfo						() const;
207	std::vector<TriGeometry>		prepareTriGeometries					(const float						zCoord)			const;
208	std::vector<BoxGeometry>		prepareBoxGeometries					(const float						zFront,
209																			 const float						zBack)			const;
210	std::vector<ShaderRecordEntry>	prepareShaderBindingTable				(void)												const;
211	BottomLevelASPtrs				createBottomLevelAccelerationStructs	(VkCommandBuffer					cmdBuffer)		const;
212	TopLevelASPtr					createTopLevelAccelerationStruct		(VkCommandBuffer					cmdBuffer,
213																			 const BottomLevelASPtrs&			blasPtrs)		const;
214	bool							verifyResult							(const BufferWithMemory*			resultBuffer)	const;
215#ifdef INTERNAL_DEBUG
216	void							printImage								(const tcu::IVec4*					image)			const;
217#endif
218
219	template<class rayPayloadEXT, class shaderRecordEXT>
220	struct Shader
221	{
222		virtual bool			ignoreIntersection	(const rayPayloadEXT&, const shaderRecordEXT&) const { return false; }
223		virtual	rayPayloadEXT	invoke				(const rayPayloadEXT&, const shaderRecordEXT&) const = 0;
224	};
225	struct ShaderBase : Shader<tcu::IVec4, ShaderRecordEXT>
226	{
227		typedef tcu::IVec4			rayPayloadEXT;
228		typedef ShaderRecordEXT		shaderRecordEXT;
229		static const rayPayloadEXT	dummyPayload;
230		virtual	rayPayloadEXT	invoke				(const rayPayloadEXT&, const shaderRecordEXT&) const override {
231			return rayPayloadEXT();
232		}
233	};
234	struct ClosestHitShader : ShaderBase
235	{
236		virtual	rayPayloadEXT	invoke				(const rayPayloadEXT& hitAttr, const shaderRecordEXT& rec) const override {
237			return (rec.geomType == GeometryTypes::Triangle) ? rec.retValue : hitAttr;
238		}
239	};
240	struct AnyHitShader : ShaderBase
241	{
242		virtual bool			ignoreIntersection(const rayPayloadEXT&, const shaderRecordEXT& rec) const override {
243			return (rec.geomIndex % 2 == 1);
244		}
245	};
246	struct IntersectionShader : ShaderBase
247	{
248		virtual	rayPayloadEXT	invoke(const rayPayloadEXT&, const shaderRecordEXT& rec) const override {
249			return (rec.retValue + tcu::IVec4(0, 2, 3, 4));
250		}
251	};
252	struct MissShader : ShaderBase
253	{
254		virtual	rayPayloadEXT	invoke(const rayPayloadEXT&, const shaderRecordEXT& rec) const override {
255			return rec.retValue; }
256	};
257	struct HitGroup
258	{
259		de::SharedPtr<AnyHitShader>			ahit;
260		de::SharedPtr<ClosestHitShader>		chit;
261		de::SharedPtr<IntersectionShader>	isect;
262	};
263
264	tcu::IVec2					rayToImage								(const tcu::Vec2&								rayCoords) const;
265	tcu::Vec2					imageToRay								(const tcu::IVec2&								imageCoords) const;
266	deUint32					computeSamePixelCount					(const std::vector<tcu::IVec4>&					image,
267																		 const tcu::Vec2								pixelCoords,
268																		 const tcu::IVec4&								requiredColor,
269																		 const tcu::IVec4&								floodColor,
270																		 const std::function<bool(const tcu::Vec3&)>&	pointInGeometry,
271																		 std::vector<std::pair<tcu::IVec4, tcu::IVec2>>	&auxBuffer) const;
272	void						travelRay								(std::vector<tcu::IVec4>&						outImage,
273																		 const deUint32									glLaunchIdExtX,
274																		 const deUint32									glLaunchIdExtY,
275																		 const std::vector<ShaderRecordEntry>&			shaderBindingTable,
276																		 const MissShader&								missShader,
277																		 const std::vector<TriGeometry>&				triangleGeometries,
278																		 const std::vector<BoxGeometry>&				boxGeometries) const;
279	const TestParams			m_params;
280	const VkFormat				m_format;
281};
282PipelineFlagsInstance::ShaderBase::rayPayloadEXT const PipelineFlagsInstance::ShaderBase::dummyPayload{};
283
284PipelineFlagsInstance::ShaderRecordEXT::ShaderRecordEXT ()
285	: geomType	(GeometryTypes::None)
286	, geomIndex	(~0u)
287	, retValue	()
288{
289}
290
291PipelineFlagsInstance::ShaderRecordEXT::ShaderRecordEXT (GeometryTypes type, deUint32 index, const tcu::IVec4& ret)
292	: geomType(type)
293	, geomIndex(index)
294	, retValue(ret)
295{
296}
297
298class RayTracingTestPipeline : protected RayTracingPipeline
299{
300public:
301	RayTracingTestPipeline (Context& context, const PipelineFlagsInstance& testInstance, const TestParams& params)
302		: m_context			(context)
303		, m_vkd				(context.getDeviceInterface())
304		, m_device			(context.getDevice())
305		, m_allocator		(context.getDefaultAllocator())
306		, m_testInstance	(testInstance)
307		, m_params			(params)
308	{
309		m_rgenModule = createShaderModule(m_vkd, m_device, m_context.getBinaryCollection().get("rgen"), 0);
310
311		// miss shader is loaded into each test regardless m_params.miss() is set
312		m_missModule = createShaderModule(m_vkd, m_device, m_context.getBinaryCollection().get("miss"), 0);
313
314		// cloest hit shader is loaded into each test regardless m_params.chit() is set
315		m_chitModule = createShaderModule(m_vkd, m_device, m_context.getBinaryCollection().get("chit"), 0);
316
317		if (m_params.ahit())
318			m_ahitModule = createShaderModule(m_vkd, m_device, m_context.getBinaryCollection().get("ahit"), 0);
319
320		if (m_params.isect() || (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox))
321			m_isectModule = createShaderModule(m_vkd, m_device, m_context.getBinaryCollection().get("isect"), 0);
322
323		setCreateFlags(m_params.flags);
324		if (m_params.useMaintenance5)
325			setCreateFlags2(translateCreateFlag(m_params.flags));
326
327		setMaxPayloadSize(sizeof(PipelineFlagsInstance::ShaderRecordEXT::retValue));
328		setMaxAttributeSize(sizeof(PipelineFlagsInstance::ShaderRecordEXT::retValue));
329	}
330
331	template<class ShaderRecord> struct SBT
332	{
333		static const deUint32 recordSize = static_cast<deUint32>(sizeof(ShaderRecord));
334
335		const DeviceInterface&			m_vkd;
336		const VkDevice					m_dev;
337		const VkPipeline				m_pipeline;
338		const deUint32					m_groupCount;
339		const deUint32					m_handleSize;
340		const deUint32					m_alignment;
341		de::MovePtr<BufferWithMemory>	m_buffer;
342		deUint8*						m_content;
343
344		SBT (const DeviceInterface& vkd, VkDevice dev, Allocator& allocator, VkPipeline pipeline,
345			 deUint32 groupCount, deUint32 shaderGroupHandleSize,  deUint32 shaderGroupBaseAlignment)
346			: m_vkd(vkd), m_dev(dev), m_pipeline(pipeline)
347			, m_groupCount(groupCount), m_handleSize(shaderGroupHandleSize)
348			, m_alignment(deAlign32(shaderGroupHandleSize + recordSize, shaderGroupBaseAlignment))
349			, m_buffer(), m_content()
350		{
351			const deUint32				size		= groupCount * m_alignment;
352			const VkBufferUsageFlags	flags		= VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
353			VkBufferCreateInfo			info		= makeBufferCreateInfo(size, flags);
354			const MemoryRequirement		memReq		= MemoryRequirement::HostVisible | MemoryRequirement::Coherent | MemoryRequirement::DeviceAddress;
355			m_buffer	= de::MovePtr<BufferWithMemory>(new BufferWithMemory(m_vkd, m_dev, allocator, info, memReq));
356			m_content	= (deUint8*)m_buffer->getAllocation().getHostPtr();
357		}
358
359		void updateAt (deUint32 index, const deUint8* handle, const ShaderRecord& rec)
360		{
361			DE_ASSERT(index < m_groupCount);
362			deUint8* groupPos = m_content + index * m_alignment;
363			deMemcpy(groupPos, handle, m_handleSize);
364			deMemcpy(groupPos + m_handleSize, &rec, recordSize);
365		}
366
367		void flush ()
368		{
369			Allocation&					alloc	= m_buffer->getAllocation();
370			flushMappedMemoryRange(m_vkd, m_dev, alloc.getMemory(), alloc.getOffset(), VK_WHOLE_SIZE);
371		}
372
373		deUint32	getAlignment () const { return m_alignment; }
374
375		de::MovePtr<BufferWithMemory> get () { return m_buffer; }
376	};
377
378	struct PLDeleter
379	{
380		const RayTracingTestPipeline*				pipeline;
381		std::function<void(RayTracingPipeline*)>	whenDestroying;
382		PLDeleter (RayTracingTestPipeline* pl, std::function<void(RayTracingPipeline*)>	doWhenDestroying)
383			: pipeline(pl), whenDestroying(doWhenDestroying) {}
384		void operator()(RayTracingPipeline* pl)
385		{
386			if (pipeline != pl && pipeline->m_params.useLibs)
387			{
388				if (whenDestroying) whenDestroying(pl);
389				delete pl;
390			}
391		}
392	};
393
394	auto createLibraryPipeline (std::function<void(RayTracingPipeline*)> doWhenDestroying)
395		-> de::UniquePtr<RayTracingPipeline, PLDeleter>
396	{
397		RayTracingPipeline* pl = this;
398		if (m_params.useLibs) {
399			pl = new RayTracingPipeline;
400			pl->setCreateFlags(m_params.flags | VK_PIPELINE_CREATE_LIBRARY_BIT_KHR);
401			pl->setMaxPayloadSize(sizeof(PipelineFlagsInstance::ShaderRecordEXT::retValue));
402			pl->setMaxAttributeSize(sizeof(PipelineFlagsInstance::ShaderRecordEXT::retValue));
403		}
404		return de::UniquePtr<RayTracingPipeline, PLDeleter>(pl, PLDeleter(this, doWhenDestroying));
405	}
406
407	Move<VkPipeline>	createPipeline (const VkPipelineLayout pipelineLayout)
408	{
409		deUint32	groupIndex	= 0;
410		const bool	checkIsect	= (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox);
411
412		auto		appendPipelineLibrary	= [this, &pipelineLayout](RayTracingPipeline* pl) -> void
413		{
414			m_libraries.emplace_back(makeVkSharedPtr(pl->createPipeline(m_vkd, m_device, pipelineLayout)));
415		};
416
417		DE_ASSERT(						(VkShaderModule(0) != *m_rgenModule));
418		DE_ASSERT(						(VkShaderModule(0) != *m_missModule));
419		DE_ASSERT(m_params.ahit()	==	(VkShaderModule(0) != *m_ahitModule));
420		DE_ASSERT(						(VkShaderModule(0) != *m_chitModule));
421		DE_ASSERT(checkIsect		==	(VkShaderModule(0) != *m_isectModule));
422
423		// rgen in the main pipeline only
424		addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, *m_rgenModule, groupIndex++);
425
426		createLibraryPipeline(appendPipelineLibrary)->addShader(VK_SHADER_STAGE_MISS_BIT_KHR, *m_missModule, (m_params.useLibs ? 0 : groupIndex++));
427
428		{
429			const deUint32 hitGroupIndex = m_params.useLibs ? 0 : groupIndex;
430			auto pipeline = createLibraryPipeline(appendPipelineLibrary);
431			if (m_params.ahit())	pipeline->addShader(VK_SHADER_STAGE_ANY_HIT_BIT_KHR,		*m_ahitModule, hitGroupIndex);
432			pipeline->addShader(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR,	*m_chitModule, hitGroupIndex);
433			if (checkIsect)			pipeline->addShader(VK_SHADER_STAGE_INTERSECTION_BIT_KHR,	*m_isectModule, hitGroupIndex);
434		}
435
436		for (const auto& sg : m_shadersGroupCreateInfos)
437		{
438			static_cast<void>(sg);
439			DE_ASSERT(sg.type != VK_RAY_TRACING_SHADER_GROUP_TYPE_MAX_ENUM_KHR);
440		}
441
442		return RayTracingPipeline::createPipeline(m_vkd, m_device, pipelineLayout, m_libraries);
443	}
444
445	std::pair<de::SharedPtr<BufferWithMemory>, VkStridedDeviceAddressRegionKHR>	createRaygenShaderBindingTable (VkPipeline pipeline)
446	{
447		de::MovePtr<BufferWithMemory>	sbt	= createShaderBindingTable(m_vkd, m_device, pipeline, m_allocator,
448																	   m_testInstance.shaderGroupHandleSize,
449																	   m_testInstance.shaderGroupBaseAlignment, 0, 1);
450		VkStridedDeviceAddressRegionKHR	rgn	= makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(m_vkd, m_device, **sbt, 0),
451																				m_testInstance.shaderGroupHandleSize,
452																				m_testInstance.shaderGroupHandleSize);
453		return { de::SharedPtr<BufferWithMemory>(sbt.release()), rgn };
454	}
455
456	std::pair<de::SharedPtr<BufferWithMemory>, VkStridedDeviceAddressRegionKHR>	createMissShaderBindingTable (VkPipeline pipeline)
457	{
458		const auto		entries			= m_testInstance.prepareShaderBindingTable();
459		const void*		shaderRecPtr	= static_cast<PipelineFlagsInstance::ShaderRecordEXT const*>(&std::get<2>(entries[1]));
460		const deUint32	shaderRecSize	= static_cast<deUint32>(sizeof(PipelineFlagsInstance::ShaderRecordEXT));
461		const deUint32	alignment		= deAlign32(m_testInstance.shaderGroupHandleSize + shaderRecSize, m_testInstance.shaderGroupBaseAlignment);
462		const deUint32	sbtOffset		= 0;
463
464		de::MovePtr<BufferWithMemory>	sbt	= createShaderBindingTable(m_vkd, m_device, pipeline, m_allocator,
465																	   m_testInstance.shaderGroupHandleSize,
466																	   m_testInstance.shaderGroupBaseAlignment,
467																	   1, 1,
468																	   VkBufferCreateFlags(0u),
469																	   VkBufferUsageFlags(0u),
470																	   MemoryRequirement::Any,
471																	   VkDeviceAddress(0),
472																	   sbtOffset,
473																	   shaderRecSize,
474																	   &shaderRecPtr);
475
476		VkStridedDeviceAddressRegionKHR	rgn	= makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(m_vkd, m_device, **sbt, 0),
477																				alignment,
478																				m_testInstance.shaderGroupHandleSize);
479		return { de::SharedPtr<BufferWithMemory>(sbt.release()), rgn };
480	}
481
482	std::pair<de::SharedPtr<BufferWithMemory>, VkStridedDeviceAddressRegionKHR>	createHitShaderBindingTable (VkPipeline pipeline)
483	{
484		de::MovePtr<BufferWithMemory>	buf;
485		VkStridedDeviceAddressRegionKHR	rgn;
486
487		std::vector<deUint8>	handles			(m_testInstance.shaderGroupHandleSize);
488		auto					records			= m_testInstance.prepareShaderBindingTable();
489		const deUint32			hitGroupCount	= deUint32(records.size() - 2);
490
491		SBT<PipelineFlagsInstance::ShaderRecordEXT> sbt(m_vkd, m_device, m_allocator, pipeline, hitGroupCount,
492														m_testInstance.shaderGroupHandleSize, m_testInstance.shaderGroupBaseAlignment);
493
494		VK_CHECK(m_vkd.getRayTracingShaderGroupHandlesKHR(m_device, pipeline, 2, 1, handles.size(), handles.data()));
495
496		for (deUint32 i = 0; i < hitGroupCount; ++i)
497		{
498			// copy the SBT record if it was initialized in prepareShaderBindingTable()
499			if (std::get<3>(records[i + 2]))
500			{
501				const PipelineFlagsInstance::ShaderRecordEXT& rec = std::get<2>(records[i + 2]);
502				sbt.updateAt(i, handles.data(), rec);
503			}
504		}
505
506		sbt.flush();
507		buf	= sbt.get();
508		rgn = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(m_vkd, m_device, **buf, 0),
509																		sbt.getAlignment(),	m_testInstance.shaderGroupHandleSize);
510
511		return { de::SharedPtr<BufferWithMemory>(buf.release()), rgn };
512	}
513
514private:
515	Context&										m_context;
516	const DeviceInterface&							m_vkd;
517	const VkDevice									m_device;
518	Allocator&										m_allocator;
519	const PipelineFlagsInstance&					m_testInstance;
520	const TestParams								m_params;
521	Move<VkShaderModule>							m_rgenModule;
522	Move<VkShaderModule>							m_chitModule;
523	Move<VkShaderModule>							m_ahitModule;
524	Move<VkShaderModule>							m_isectModule;
525	Move<VkShaderModule>							m_missModule;
526	Move<VkShaderModule>							m_gapModule;
527	std::vector<de::SharedPtr<Move<VkPipeline>>>	m_libraries;
528};
529
530template<class T, class P = T(*)[1], class R = decltype(std::begin(*std::declval<P>()))>
531auto makeStdBeginEnd(T* p, deUint32 n) -> std::pair<R, R>
532{
533	auto tmp = std::begin(*P(p));
534	auto begin = tmp;
535	std::advance(tmp, n);
536	return { begin, tmp };
537}
538
539PipelineFlagsCase::PipelineFlagsCase (tcu::TestContext& testCtx, const std::string& name, const TestParams& params)
540	: TestCase					(testCtx, name)
541	, shaderGroupHandleSize		(m_shaderGroupHandleSize)
542	, shaderGroupBaseAlignment	(m_shaderGroupBaseAlignment)
543	, m_params					(params)
544	, m_rgenPayload				(0, ':', 0, 0)
545	, m_defMissRetGreenComp		('-')
546	, m_defTriRetGreenComp		('A')
547	, m_defBoxRetGreenComp		(calcDefBoxRetGreenComp(params, m_defTriRetGreenComp))
548{
549}
550
551void PipelineFlagsCase::checkSupport (Context& context) const
552{
553	if ((VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_BIT_KHR & m_params.flags)
554		&& (GeometryTypes::Triangle == m_params.geomTypes))
555	{
556		TCU_THROW(InternalError, "Illegal params combination: VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_BIT_KHR and Triangles");
557	}
558
559	if (!context.isDeviceFunctionalitySupported("VK_KHR_ray_tracing_pipeline"))
560		TCU_THROW(NotSupportedError, "VK_KHR_ray_tracing_pipeline not supported");
561
562	// VK_KHR_acceleration_structure is required by VK_KHR_ray_tracing_pipeline.
563	if (!context.isDeviceFunctionalitySupported("VK_KHR_acceleration_structure"))
564		TCU_FAIL("VK_KHR_acceleration_structure not supported but VK_KHR_ray_tracing_pipeline supported");
565
566	// The same for VK_KHR_buffer_device_address.
567	if (!context.isDeviceFunctionalitySupported("VK_KHR_buffer_device_address"))
568		TCU_FAIL("VK_KHR_buffer_device_address not supported but VK_KHR_acceleration_structure supported");
569
570	if (m_params.useLibs && !context.isDeviceFunctionalitySupported("VK_KHR_pipeline_library"))
571		TCU_FAIL("VK_KHR_pipeline_library not supported but VK_KHR_ray_tracing_pipeline supported");
572
573	if (m_params.useMaintenance5)
574		context.requireDeviceFunctionality("VK_KHR_maintenance5");
575
576	const VkPhysicalDeviceRayTracingPipelineFeaturesKHR& rayTracingPipelineFeaturesKHR = context.getRayTracingPipelineFeatures();
577	if (rayTracingPipelineFeaturesKHR.rayTracingPipeline == DE_FALSE)
578		TCU_THROW(NotSupportedError, "Requires VkPhysicalDeviceRayTracingPipelineFeaturesKHR.rayTracingPipeline");
579
580	const VkPhysicalDeviceAccelerationStructureFeaturesKHR& accelerationStructureFeaturesKHR = context.getAccelerationStructureFeatures();
581	if (accelerationStructureFeaturesKHR.accelerationStructure == DE_FALSE)
582		TCU_THROW(TestError, "VK_KHR_ray_tracing_pipeline requires VkPhysicalDeviceAccelerationStructureFeaturesKHR.accelerationStructure");
583
584	if (m_params.onHhost && accelerationStructureFeaturesKHR.accelerationStructureHostCommands == DE_FALSE)
585		TCU_THROW(NotSupportedError, "Requires VkPhysicalDeviceAccelerationStructureFeaturesKHR.accelerationStructureHostCommands");
586
587	checkAccelerationStructureVertexBufferFormat(context.getInstanceInterface(), context.getPhysicalDevice(), VK_FORMAT_R32G32B32_SFLOAT);
588
589	auto rayTracingProperties	= makeRayTracingProperties(context.getInstanceInterface(), context.getPhysicalDevice());
590	m_shaderGroupHandleSize		= rayTracingProperties->getShaderGroupHandleSize();
591	m_shaderGroupBaseAlignment	= rayTracingProperties->getShaderGroupBaseAlignment();
592}
593
594void PipelineFlagsCase::initPrograms (SourceCollections& programCollection) const
595{
596	const vk::ShaderBuildOptions	buildOptions	(programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, 0u, true);
597	const char						endl			= '\n';
598	const deUint32					missIdx			= 0;
599
600	const std::string				payloadInDecl	=
601		"layout(location = 0) rayPayloadInEXT ivec4 payload;";
602
603	const std::string				recordDecl		=
604		"layout(shaderRecordEXT, std430) buffer Rec {\n"
605		"  uint  geomType;\n"
606		"  uint  geomIndex;\n"
607		"  ivec4 retValue;\n"
608		"} record;";
609
610	{
611		std::stringstream str;
612		str << "#version 460 core"																			<< endl
613			<< "#extension GL_EXT_ray_tracing : require"													<< endl
614			<< "layout(location = 0) rayPayloadEXT ivec4 payload;"											<< endl
615			<< "layout(rgba32i, set = 0, binding = 0) uniform iimage2D result;"								<< endl
616			<< "layout(set = 0, binding = 1) uniform accelerationStructureEXT topLevelAS;"					<< endl
617			<< "void main()"																				<< endl
618			<< "{"																							<< endl
619			<< "  float rx           = (float(gl_LaunchIDEXT.x * 2) / float(gl_LaunchSizeEXT.x)) - 1.0;"	<< endl
620			<< "  float ry           = (float(gl_LaunchIDEXT.y) + 0.5) / float(gl_LaunchSizeEXT.y);"		<< endl
621			<< "  payload            = ivec4" << m_rgenPayload << ";"										<< endl
622			<< "  uint  rayFlags     = gl_RayFlagsNoneEXT;"													<< endl
623			<< "  uint  cullMask     = 0xFFu;"																<< endl
624			<< "  uint  stbRecOffset = " << m_params.stbRecOffset << "u;"									<< endl
625			<< "  uint  stbRecStride = " << m_params.stbRecStride << "u;"									<< endl
626			<< "  uint  missIdx      = " << missIdx << "u;"													<< endl
627			<< "  vec3  orig         = vec3(rx, ry, 1.0);"													<< endl
628			<< "  float tmin         = 0.0;"																<< endl
629			<< "  vec3  dir          = vec3(0.0, 0.0, -1.0);"												<< endl
630			<< "  float tmax         = 1000.0;"																<< endl
631			<< "  traceRayEXT(topLevelAS, rayFlags, cullMask, stbRecOffset, stbRecStride, missIdx, orig, tmin, dir, tmax, 0);"	<< endl
632			<< "  imageStore(result, ivec2(gl_LaunchIDEXT.xy), payload);"									<< endl
633			<< "}";
634		programCollection.glslSources.add("rgen") << glu::RaygenSource(str.str()) << buildOptions;
635	}
636
637	// miss shader is created in each test regardless the m_params.miss() is set
638	{
639		std::stringstream str;
640		str	<< "#version 460 core"							<< endl
641			<< "#extension GL_EXT_ray_tracing : require"	<< endl
642			<< payloadInDecl								<< endl
643			<< recordDecl									<< endl
644			<< "void main()"								<< endl
645			<< "{"											<< endl
646			<< "  payload = record.retValue;"				<< endl
647			<< "}";
648		programCollection.glslSources.add("miss") << glu::MissSource(str.str()) << buildOptions;
649	}
650
651	// closest hit shader is created in each test regardless the m_params.chit() is set
652	{
653		std::stringstream str;
654		str << "#version 460 core"														<< endl
655			<< "#extension GL_EXT_ray_tracing : require"								<< endl
656			<< "hitAttributeEXT ivec4 hitAttribute;"									<< endl
657			<< payloadInDecl															<< endl
658			<< recordDecl																<< endl
659			<< "void main()"															<< endl
660			<< "{"																		<< endl
661			<< "  if (record.geomType == " << deUint32(GeometryTypes::Triangle) << ")"	<< endl
662			<< "    payload = record.retValue;"											<< endl
663			<< "  else payload = hitAttribute;"											<< endl
664			<< "}";
665		programCollection.glslSources.add("chit") << glu::ClosestHitSource(str.str()) << buildOptions;
666	}
667
668	if (m_params.ahit())
669	{
670		std::stringstream str;
671		str << "#version 460 core"							<< endl
672			<< "#extension GL_EXT_ray_tracing : require"	<< endl
673			<< recordDecl									<< endl
674			<< "void main()"								<< endl
675			<< "{"											<< endl
676			<< "  if (record.geomIndex % 2 == 1)"			<< endl
677			<< "    ignoreIntersectionEXT;"					<< endl
678			<< "}";
679		programCollection.glslSources.add("ahit") << glu::AnyHitSource(str.str()) << buildOptions;
680	}
681
682	if (m_params.isect() || (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox))
683	{
684		std::stringstream str;
685		str << "#version 460 core"								<< endl
686			<< "#extension GL_EXT_ray_tracing : require"		<< endl
687			<< "hitAttributeEXT ivec4 hitAttribute;"			<< endl
688			<< recordDecl										<< endl
689			<< "void main()"									<< endl
690			<< "{"												<< endl
691			<< "  hitAttribute = ivec4(record.retValue.x + 0"	<< endl
692			<< "                      ,record.retValue.y + 2"	<< endl
693			<< "                      ,record.retValue.z + 3"	<< endl
694			<< "                      ,record.retValue.w + 4);"	<< endl
695			<< "  reportIntersectionEXT(0.0, 0);"				<< endl
696			<< "}";
697		programCollection.glslSources.add("isect") << glu::IntersectionSource(str.str()) << buildOptions;
698	}
699}
700
701TestInstance* PipelineFlagsCase::createInstance (Context& context) const
702{
703	return new PipelineFlagsInstance(context, m_params, shaderGroupHandleSize, shaderGroupBaseAlignment,
704													m_rgenPayload, m_defMissRetGreenComp, m_defTriRetGreenComp, m_defBoxRetGreenComp);
705}
706
707PipelineFlagsInstance::PipelineFlagsInstance (Context&			context,
708											  const TestParams&	params,
709											  const deUint32&	shaderGroupHandleSize_,
710											  const deUint32&	shaderGroupBaseAlignment_,
711											  const tcu::IVec4& rgenPayload_,
712											  deInt32			defMissRetGreenComp_,
713											  deInt32			defTriRetGreenComp_,
714											  deInt32			defBoxRetGreenComp_)
715	: TestInstance				(context)
716	, rgenPayload				(rgenPayload_)
717	, defMissRetGreenComp		(defMissRetGreenComp_)
718	, defTriRetGreenComp		(defTriRetGreenComp_)
719	, defBoxRetGreenComp		(defBoxRetGreenComp_)
720	, shaderGroupHandleSize		(shaderGroupHandleSize_)
721	, shaderGroupBaseAlignment	(shaderGroupBaseAlignment_)
722	, m_params					(params)
723	, m_format					(VK_FORMAT_R32G32B32A32_SINT)
724{
725}
726
727VkImageCreateInfo PipelineFlagsInstance::makeImageCreateInfo () const
728{
729	const deUint32				familyIndex		= m_context.getUniversalQueueFamilyIndex();
730	const VkImageCreateInfo		imageCreateInfo	=
731	{
732		VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,																// VkStructureType			sType;
733		DE_NULL,																							// const void*				pNext;
734		(VkImageCreateFlags)0u,																				// VkImageCreateFlags		flags;
735		VK_IMAGE_TYPE_2D,																					// VkImageType				imageType;
736		m_format,																							// VkFormat					format;
737		makeExtent3D(m_params.width, m_params.height, 1u),													// VkExtent3D				extent;
738		1u,																									// deUint32					mipLevels;
739		1u,																									// deUint32					arrayLayers;
740		VK_SAMPLE_COUNT_1_BIT,																				// VkSampleCountFlagBits	samples;
741		VK_IMAGE_TILING_OPTIMAL,																			// VkImageTiling			tiling;
742		VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,		// VkImageUsageFlags		usage;
743		VK_SHARING_MODE_EXCLUSIVE,																			// VkSharingMode			sharingMode;
744		1u,																									// deUint32					queueFamilyIndexCount;
745		&familyIndex,																						// const deUint32*			pQueueFamilyIndices;
746		VK_IMAGE_LAYOUT_UNDEFINED																			// VkImageLayout			initialLayout;
747	};
748
749	return imageCreateInfo;
750}
751
752std::vector<PipelineFlagsInstance::TriGeometry>
753PipelineFlagsInstance::prepareTriGeometries (const float zCoord) const
754{
755	const tcu::Vec3	center(-0.5f, 0.5f, zCoord);
756	const tcu::Vec3	start(0.0f, 0.5f, zCoord);
757	const deUint32 maxTriangles = m_params.instCount * m_params.geomCount;
758	const deUint32 trianglesCount = deMaxu32(maxTriangles, 3);
759	const float angle = (4.0f * std::acos(0.0f)) / float(trianglesCount);
760
761	tcu::Vec3	point(start);
762	std::vector<TriGeometry> geometries(maxTriangles);
763	for (deUint32 inst = 0, idx = 0; inst < m_params.instCount; ++inst)
764	{
765		for (deUint32 geom = 0; geom < m_params.geomCount; ++geom, ++idx)
766		{
767			TriGeometry& geometry = geometries[idx];
768
769			geometry[0] = center;
770			geometry[1] = point;
771			geometry[2] = (maxTriangles >= 3 && trianglesCount - idx == 1u) ? start : rotateCcwZ(point, center, angle);
772
773			point = geometry[2];
774		}
775	}
776
777	return geometries;
778}
779
780std::vector<PipelineFlagsInstance::BoxGeometry>
781PipelineFlagsInstance::prepareBoxGeometries (const float zFront, const float zBack) const
782{
783	const deUint32				maxBoxes	= m_params.instCount * m_params.geomCount;
784
785	std::vector<BoxGeometry>	boxes		(maxBoxes);
786	deUint32					boxesPerDim	= 0u;
787	float						boxWidth	= 0.0f;
788	float						boxHeight	= 0.0f;
789
790	// find nearest square ceil number
791	do
792	{
793		++boxesPerDim;
794		boxWidth = 1.0f / float(boxesPerDim);
795		boxHeight = 1.0f / float(boxesPerDim);
796	} while (boxesPerDim * boxesPerDim < maxBoxes);
797
798	for (deUint32 boxY = 0, boxIdx = 0; boxY < boxesPerDim && boxIdx < maxBoxes; ++boxY)
799	{
800		for (deUint32 boxX = 0; boxX < boxesPerDim && boxIdx < maxBoxes; ++boxX, ++boxIdx)
801		{
802			const float x = float(boxX) * boxWidth;
803			const float y = float(boxY) * boxHeight;
804			BoxGeometry box = { { tcu::Vec3(x, y, zFront), tcu::Vec3((x + boxWidth), (y + boxHeight), zBack) } };
805			boxes[boxIdx].swap(box);
806		}
807	}
808
809	return boxes;
810}
811
812PipelineFlagsInstance::BottomLevelASPtrs
813PipelineFlagsInstance::createBottomLevelAccelerationStructs (VkCommandBuffer cmdBuffer) const
814{
815	const DeviceInterface&		vkd			= m_context.getDeviceInterface();
816	const VkDevice				device		= m_context.getDevice();
817	Allocator&					allocator	= m_context.getDefaultAllocator();
818	const VkGeometryFlagsKHR	geomFlags	= m_params.ahit() ? VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR : VK_GEOMETRY_OPAQUE_BIT_KHR;
819
820	BottomLevelASPtrs			result;
821
822	if (!m_params.isect() && ((m_params.geomTypes == GeometryTypes::Triangle) || (m_params.geomTypes == GeometryTypes::TriangleAndBox)))
823	{
824		const auto	geometries = prepareTriGeometries(0.0f);
825
826		for (deUint32 inst = 0, idx = 0; inst < m_params.instCount; ++inst)
827		{
828			auto blas = makeBottomLevelAccelerationStructure();
829			blas->setBuildType(m_params.onHhost ? VK_ACCELERATION_STRUCTURE_BUILD_TYPE_HOST_KHR : VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR);
830
831			for (deUint32 geom = 0; geom < m_params.geomCount; ++geom, ++idx)
832			{
833				const TriGeometry& triangle = geometries[idx];
834				blas->addGeometry(std::vector<tcu::Vec3>(triangle.begin(), triangle.end()), true, geomFlags);
835			}
836
837			blas->createAndBuild(vkd, device, cmdBuffer, allocator);
838			result.emplace_back(de::SharedPtr<BottomLevelAccelerationStructure>(blas.release()));
839		}
840	}
841
842	if (m_params.isect() || (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox))
843	{
844		const auto	geometries = prepareBoxGeometries(0.0f, 0.0f);
845
846		for (deUint32 inst = 0, idx = 0; inst < m_params.instCount; ++inst)
847		{
848			auto blas = makeBottomLevelAccelerationStructure();
849			blas->setUseMaintenance5(m_params.useMaintenance5);
850			blas->setBuildType(m_params.onHhost ? VK_ACCELERATION_STRUCTURE_BUILD_TYPE_HOST_KHR : VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR);
851
852			for (deUint32 geom = 0; geom < m_params.geomCount; ++geom, ++idx)
853			{
854				const BoxGeometry& box = geometries[idx];
855				blas->addGeometry(std::vector<tcu::Vec3>(box.begin(), box.end()), false, geomFlags);
856			}
857
858			blas->createAndBuild(vkd, device, cmdBuffer, allocator);
859			result.emplace_back(de::SharedPtr<BottomLevelAccelerationStructure>(blas.release()));
860		}
861	}
862
863	return result;
864}
865
866PipelineFlagsInstance::TopLevelASPtr
867PipelineFlagsInstance::createTopLevelAccelerationStruct (VkCommandBuffer cmdBuffer, const BottomLevelASPtrs& blasPtrs) const
868{
869	const DeviceInterface&	vkd							= m_context.getDeviceInterface();
870	const VkDevice			device						= m_context.getDevice();
871	Allocator&				allocator					= m_context.getDefaultAllocator();
872	const deUint32			groupsAndGapsPerInstance	= computeEffectiveShaderGroupCount(m_params);
873
874	auto					tlas		= makeTopLevelAccelerationStructure();
875
876	tlas->setBuildType(m_params.onHhost ? VK_ACCELERATION_STRUCTURE_BUILD_TYPE_HOST_KHR : VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR);
877	tlas->setInstanceCount(blasPtrs.size());
878	for (auto begin = blasPtrs.begin(), end = blasPtrs.end(), i = begin; i != end; ++i)
879	{
880		const deUint32 instanceShaderBindingTableRecordOffset = static_cast<deUint32>(std::distance(begin, i) * groupsAndGapsPerInstance);
881		tlas->addInstance(*i, identityMatrix3x4, 0, 0xFF, instanceShaderBindingTableRecordOffset);
882	}
883	tlas->createAndBuild(vkd, device, cmdBuffer, allocator);
884
885	return TopLevelASPtr(tlas.release());
886}
887
888std::vector<PipelineFlagsInstance::ShaderRecordEntry> PipelineFlagsInstance::prepareShaderBindingTable() const
889{
890	const bool						includeTriangles = (!m_params.isect() && ((m_params.geomTypes == GeometryTypes::Triangle) || (m_params.geomTypes == GeometryTypes::TriangleAndBox)));
891	const bool						includeBoxes = (m_params.isect() || (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox));
892	const deUint32					groupsAndGapsPerInstance = computeEffectiveShaderGroupCount(m_params);
893	const deUint32					commonGroupCount = 2; // general groups for rgen and miss
894	const deUint32					triangleGroupCount = includeTriangles ? (groupsAndGapsPerInstance * m_params.instCount) : 0;
895	const deUint32					proceduralGroupCount = includeBoxes ? (groupsAndGapsPerInstance * m_params.instCount) : 0;
896	const deUint32					totalGroupCount = commonGroupCount + triangleGroupCount + proceduralGroupCount;
897
898	std::vector<ShaderRecordEntry>	shaderRecords(totalGroupCount);
899
900	shaderRecords[0] = std::tuple<VkShaderStageFlags, HitGroup, ShaderRecordEXT, bool>( VK_SHADER_STAGE_RAYGEN_BIT_KHR, {}, {}, true );
901	shaderRecords[1] = std::tuple<VkShaderStageFlags, HitGroup, ShaderRecordEXT, bool>( VK_SHADER_STAGE_MISS_BIT_KHR, {}, { GeometryTypes::Box, (~0u), tcu::IVec4(0, defMissRetGreenComp, 0, 0) }, true );
902
903	de::SharedPtr<AnyHitShader>			ahit(new AnyHitShader);
904	de::SharedPtr<ClosestHitShader>		chit(new ClosestHitShader);
905	de::SharedPtr<IntersectionShader>	isect(new IntersectionShader);
906
907	if (includeTriangles)
908	{
909		std::set<deUint32>	usedIndexes;
910		deInt32	greenComp = defTriRetGreenComp;
911
912		const deUint32 recordsToSkip = commonGroupCount;
913
914		for (deUint32 instance = 0; instance < m_params.instCount; ++instance)
915		{
916			const deUint32 instanceShaderBindingTableRecordOffset = recordsToSkip + instance * groupsAndGapsPerInstance;
917			for (deUint32 geometryIndex = 0; geometryIndex < m_params.geomCount; ++geometryIndex)
918			{
919				const deUint32 shaderGroupIndex = instanceShaderBindingTableRecordOffset + geometryIndex * m_params.stbRecStride + m_params.stbRecOffset;
920				if (usedIndexes.find(shaderGroupIndex) == usedIndexes.end())
921				{
922					HitGroup			hitGroup;
923					VkShaderStageFlags	flags = 0;
924					if (m_params.ahit()) {
925						flags |= VK_SHADER_STAGE_ANY_HIT_BIT_KHR;
926						hitGroup.ahit = ahit;
927					}
928					flags |= VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
929					hitGroup.chit = chit;
930					shaderRecords[shaderGroupIndex] = std::tuple<VkShaderStageFlags, HitGroup, ShaderRecordEXT, bool>( flags, hitGroup, { GeometryTypes::Triangle, geometryIndex, tcu::IVec4(0, greenComp++, 0, 0) }, true );
931					usedIndexes.insert(shaderGroupIndex);
932				}
933			}
934		}
935	}
936
937	if (includeBoxes)
938	{
939		std::set<deUint32>	usedIndexes;
940		deInt32	greenComp = defBoxRetGreenComp;
941
942		const deUint32 recordsToSkip = triangleGroupCount + commonGroupCount;
943
944		for (deUint32 instance = 0; instance < m_params.instCount; ++instance)
945		{
946			const deUint32 instanceShaderBindingTableRecordOffset = recordsToSkip + instance * groupsAndGapsPerInstance;
947			for (deUint32 geometryIndex = 0; geometryIndex < m_params.geomCount; ++geometryIndex)
948			{
949				const deUint32 shaderGroupIndex = instanceShaderBindingTableRecordOffset + geometryIndex * m_params.stbRecStride + m_params.stbRecOffset;
950				if (usedIndexes.find(shaderGroupIndex) == usedIndexes.end())
951				{
952					HitGroup			hitGroup;
953					VkShaderStageFlags	flags = 0;
954					if (m_params.ahit()) {
955						flags |= VK_SHADER_STAGE_ANY_HIT_BIT_KHR;
956						hitGroup.ahit = ahit;
957					}
958					flags |= VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
959					hitGroup.chit = chit;
960					{ //In the case of AABB isect must be provided, otherwise we will process AABB with TRIANGLES_HIT_GROUP
961						flags |= VK_SHADER_STAGE_INTERSECTION_BIT_KHR;
962						hitGroup.isect = isect;
963					}
964					shaderRecords[shaderGroupIndex] = std::tuple<VkShaderStageFlags, HitGroup, ShaderRecordEXT, bool>( flags, hitGroup, { GeometryTypes::Box, geometryIndex, tcu::IVec4(0, greenComp++, 0, 0) }, true );
965					usedIndexes.insert(shaderGroupIndex);
966				}
967			}
968		}
969	}
970
971	return shaderRecords;
972}
973
974tcu::IVec2 PipelineFlagsInstance::rayToImage(const tcu::Vec2& rayCoords) const
975{
976	return tcu::IVec2(
977		deInt32(((rayCoords.x() + 1.0f) * float(m_params.width)) / 2.0f),
978		deInt32((rayCoords.y() * float(m_params.height)) - 0.5f)
979	);
980}
981
982tcu::Vec2 PipelineFlagsInstance::imageToRay(const tcu::IVec2& imageCoords) const
983{
984	const float		rx = (float(imageCoords.x() * 2) / float(m_params.width)) - 1.0f;
985	const float		ry = (float(imageCoords.y()) + 0.5f) / float(m_params.height);
986	return tcu::Vec2(rx, ry);
987}
988
989deUint32 PipelineFlagsInstance::computeSamePixelCount (const std::vector<tcu::IVec4>&						image,
990																     const tcu::Vec2									pixelCoords,
991																	 const tcu::IVec4&									requiredColor,
992																	 const tcu::IVec4&									floodColor,
993																	 const std::function<bool(const tcu::Vec3&)>&		pointInGeometry,
994																	 std::vector<std::pair<tcu::IVec4, tcu::IVec2>>&	auxBuffer) const
995{
996	if (!pointInGeometry(tcu::Vec3(pixelCoords.x(), pixelCoords.y(), 0.0f))) return 0;
997
998	auxBuffer.resize(image.size() * 4);
999	std::transform(image.begin(), image.end(), auxBuffer.begin(), [](const tcu::IVec4& c) -> std::pair<tcu::IVec4, tcu::IVec2> { return { c, tcu::IVec2() }; });
1000
1001	tcu::Vec2		rayCoord;
1002	tcu::IVec2		imageCoords = rayToImage(pixelCoords);
1003	deUint32		pixelIndex	= imageCoords.y() * m_params.width + imageCoords.x();
1004	tcu::IVec4		imageColor	= image[pixelIndex];
1005
1006	if (requiredColor != imageColor) return 0;
1007
1008	deInt32			stackIndex	= 0;
1009	deUint32		sameCount	= 1;
1010	auxBuffer[stackIndex].second = imageCoords;
1011
1012	while (stackIndex >= 0)
1013	{
1014		imageCoords = auxBuffer[stackIndex].second;		--stackIndex;
1015
1016		if (imageCoords.x() < 0 || imageCoords.x() >= deInt32(m_params.width)
1017			|| imageCoords.y() < 0 || imageCoords.y() >= deInt32(m_params.height)) continue;
1018
1019		rayCoord = imageToRay(imageCoords);
1020		if (!pointInGeometry(tcu::Vec3(rayCoord.x(), rayCoord.y(), 0.0f))) continue;
1021
1022		pixelIndex = imageCoords.y() * m_params.width + imageCoords.x();
1023		imageColor = auxBuffer[pixelIndex].first;
1024		if (requiredColor != imageColor) continue;
1025
1026		auxBuffer[pixelIndex].first = floodColor;
1027		sameCount += 1;
1028
1029		auxBuffer[++stackIndex].second = tcu::IVec2(imageCoords.x() - 1, imageCoords.y());
1030		auxBuffer[++stackIndex].second = tcu::IVec2(imageCoords.x() + 1, imageCoords.y());
1031		auxBuffer[++stackIndex].second = tcu::IVec2(imageCoords.x(), imageCoords.y() - 1);
1032		auxBuffer[++stackIndex].second = tcu::IVec2(imageCoords.x(), imageCoords.y() + 1);
1033	}
1034
1035	return sameCount;
1036}
1037
1038void PipelineFlagsInstance::travelRay (std::vector<tcu::IVec4>&					outImage,
1039									   const deUint32							glLaunchIdExtX,
1040									   const deUint32							glLaunchIdExtY,
1041									   const std::vector<ShaderRecordEntry>&	shaderBindingTable,
1042									   const MissShader&						missShader,
1043									   const std::vector<TriGeometry>&			triangleGeometries,
1044									   const std::vector<BoxGeometry>&			boxGeometries) const
1045{
1046	const tcu::Vec2	rayCoords					= imageToRay(tcu::IVec2(glLaunchIdExtX, glLaunchIdExtY));
1047	const bool		includeTriangles			= (!m_params.isect() && ((m_params.geomTypes == GeometryTypes::Triangle) || (m_params.geomTypes == GeometryTypes::TriangleAndBox)));
1048	const bool		includeBoxes				= (m_params.isect() || (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox));
1049	const deUint32	commonGroupCount			= 2; // general groups for rgen and miss
1050	const deUint32	groupsAndGapsPerInstance	= computeEffectiveShaderGroupCount(m_params);
1051	const deUint32	triangleGroupCount			= includeTriangles ? (groupsAndGapsPerInstance * m_params.instCount) : 0;
1052
1053	bool			hitHappened					(false);
1054	deUint32		shaderGroupIndex			(~0u);
1055	tcu::IVec4		payload						(rgenPayload);
1056	const tcu::Vec3	origin						(rayCoords.x(), rayCoords.y(), 1.0f);
1057
1058	if (includeTriangles)
1059	{
1060		const deUint32 recordsToSkip = commonGroupCount;
1061		for (deUint32 instance = 0; !hitHappened && (instance < m_params.instCount); ++instance)
1062		{
1063			const deUint32 instanceShaderBindingTableRecordOffset = recordsToSkip + instance * groupsAndGapsPerInstance;
1064			for (deUint32 geometryIndex = 0; !hitHappened && (geometryIndex < m_params.geomCount); ++geometryIndex)
1065			{
1066				const TriGeometry& geometry = triangleGeometries[instance * m_params.geomCount + geometryIndex];
1067				shaderGroupIndex = instanceShaderBindingTableRecordOffset + geometryIndex * m_params.stbRecStride + m_params.stbRecOffset;
1068				if (pointInTriangle2D(origin, geometry[0], geometry[1], geometry[2]))
1069				{
1070					hitHappened = true;
1071				}
1072			}
1073		}
1074	}
1075
1076	if (includeBoxes)
1077	{
1078		const deUint32 recordsToSkip = triangleGroupCount + commonGroupCount;
1079		for (deUint32 instance = 0; !hitHappened && (instance < m_params.instCount); ++instance)
1080		{
1081			const deUint32 instanceShaderBindingTableRecordOffset = recordsToSkip + instance * groupsAndGapsPerInstance;
1082			for (deUint32 geometryIndex = 0; !hitHappened && (geometryIndex < m_params.geomCount); ++geometryIndex)
1083			{
1084				const BoxGeometry& geometry = boxGeometries[instance * m_params.geomCount + geometryIndex];
1085				shaderGroupIndex = instanceShaderBindingTableRecordOffset + geometryIndex * m_params.stbRecStride + m_params.stbRecOffset;
1086				if (pointInRect2D(origin, geometry[0], geometry[1]))
1087				{
1088					hitHappened = true;
1089				}
1090			}
1091		}
1092	}
1093
1094	if (hitHappened)
1095	{
1096		const ShaderRecordEXT&		shaderRecord	= std::get<2>(shaderBindingTable[shaderGroupIndex]);
1097		const HitGroup&				hitGroup		= std::get<1>(shaderBindingTable[shaderGroupIndex]);
1098		const VkShaderStageFlags	flags			= std::get<0>(shaderBindingTable[shaderGroupIndex]);
1099		auto						hitAttribute	= rgenPayload;
1100		bool						ignoreIsect		= false;
1101
1102		// check if the SBT entry was was initialized
1103		DE_ASSERT(std::get<3>(shaderBindingTable[shaderGroupIndex]));
1104
1105		if (flags & VK_SHADER_STAGE_INTERSECTION_BIT_KHR)
1106		{
1107			hitAttribute = hitGroup.isect->invoke(IntersectionShader::dummyPayload, shaderRecord);
1108		}
1109		if (flags & VK_SHADER_STAGE_ANY_HIT_BIT_KHR)
1110		{
1111			ignoreIsect = hitGroup.ahit->ignoreIntersection(AnyHitShader::dummyPayload, shaderRecord);
1112		}
1113		if (ignoreIsect)
1114		{
1115			payload = missShader.invoke(MissShader::dummyPayload, std::get<2>(shaderBindingTable[1]));
1116		}
1117		else if (flags & VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR)
1118		{
1119			payload = hitGroup.chit->invoke(hitAttribute, shaderRecord);
1120		}
1121	}
1122	else
1123	{
1124		payload = missShader.invoke(MissShader::dummyPayload, std::get<2>(shaderBindingTable[1]));
1125	}
1126
1127	outImage[glLaunchIdExtY * m_params.width + glLaunchIdExtX] = payload;
1128}
1129
1130#ifdef INTERNAL_DEBUG
1131void PipelineFlagsInstance::printImage(const tcu::IVec4* image) const
1132{
1133	for (deUint32 y = 0; y < m_params.height; ++y)
1134	{
1135		for (deUint32 x = 0; x < m_params.width; ++x)
1136			std::cout << static_cast<char>(image[(m_params.height - y - 1) * m_params.width + x].y());
1137		std::cout << std::endl;
1138	}
1139}
1140#endif
1141
1142bool PipelineFlagsInstance::verifyResult (const BufferWithMemory* resultBuffer) const
1143{
1144	const auto								triangleGeometries	= prepareTriGeometries(0.0f);
1145	const auto								boxGeometries		= prepareBoxGeometries(0.0f, 0.0f);
1146
1147	const bool								includeTriangles	= (!m_params.isect() && ((m_params.geomTypes == GeometryTypes::Triangle) || (m_params.geomTypes == GeometryTypes::TriangleAndBox)));
1148	const bool								includeBoxes		= (m_params.isect() || (m_params.geomTypes == GeometryTypes::Box) || (m_params.geomTypes == GeometryTypes::TriangleAndBox));
1149
1150	const tcu::IVec4*						resultImageData		= (tcu::IVec4*)(resultBuffer->getAllocation().getHostPtr());
1151	auto									resultImageBounds	= makeStdBeginEnd(resultImageData, (m_params.width * m_params.height));
1152	const std::vector<tcu::IVec4>			resultImage			(resultImageBounds.first, resultImageBounds.second);
1153	std::vector<tcu::IVec4>					referenceImage		(m_params.width * m_params.height);
1154
1155	const std::vector<ShaderRecordEntry>	shaderBindingTable	= prepareShaderBindingTable();
1156
1157	MissShader								missShader{};
1158
1159	// perform offline ray-tracing
1160	for (deUint32 glLaunchIdExtY = 0; glLaunchIdExtY < m_params.height; ++glLaunchIdExtY)
1161	{
1162		for (deUint32 glLaunchIdExtX = 0; glLaunchIdExtX < m_params.width; ++glLaunchIdExtX)
1163		{
1164			travelRay(referenceImage, glLaunchIdExtX, glLaunchIdExtY,
1165					 shaderBindingTable, missShader,
1166					 triangleGeometries, boxGeometries);
1167		}
1168	}
1169
1170#ifdef INTERNAL_DEBUG
1171	std::cout << "===== RES =====" << std::endl;
1172	printImage(resultImageData);
1173	std::cout << std::endl;
1174	std::cout << "===== REF =====" << std::endl;
1175	printImage(referenceImage.data());
1176	std::cout << std::endl;
1177#endif
1178
1179	const tcu::IVec4								floodColor(0, '*', 0, 0);
1180	std::vector<std::pair<tcu::IVec4, tcu::IVec2>>	auxBuffer(referenceImage.size() * 4);
1181
1182	if (includeTriangles)
1183	{
1184		TriGeometry								tri;
1185		std::function<bool(const tcu::Vec3&)>	pointInGeometry = std::bind(pointInTriangle2D, std::placeholders::_1, std::ref(tri[0]), std::ref(tri[1]), std::ref(tri[2]));
1186
1187		for (deUint32 instance = 0; instance < m_params.instCount; ++instance)
1188		{
1189			for (deUint32 geometryIndex = 0; geometryIndex < m_params.geomCount; ++geometryIndex)
1190			{
1191				if (!(m_params.ahit() && (geometryIndex % 2 == 1)))
1192				{
1193					tri = triangleGeometries[instance * m_params.geomCount + geometryIndex];
1194					const tcu::Vec2 center((tri[0].x() + tri[1].x() + tri[2].x()) / 3.0f, (tri[0].y() + tri[1].y() + tri[2].y()) / 3.0f);
1195
1196					const tcu::IVec2	refImageCoords	= rayToImage(center);
1197					const tcu::IVec4	requiredColor	= referenceImage[refImageCoords.y() * m_params.width + refImageCoords.x()];
1198
1199					deUint32 resultPixelCount = computeSamePixelCount(resultImage, center, requiredColor, floodColor, pointInGeometry, auxBuffer);
1200					deUint32 referencePixelCount = computeSamePixelCount(referenceImage, center, requiredColor, floodColor, pointInGeometry, auxBuffer);
1201
1202					if (!resultPixelCount || !referencePixelCount) return false;
1203					if (resultPixelCount > referencePixelCount) std::swap(resultPixelCount, referencePixelCount);
1204
1205					const float similarity = float(resultPixelCount) / float(referencePixelCount);
1206					if (similarity < m_params.accuracy) return false;
1207				}
1208			}
1209		}
1210	}
1211
1212	if (includeBoxes)
1213	{
1214		BoxGeometry								box;
1215		std::function<bool(const tcu::Vec3&)>	pointInGeometry = std::bind(pointInRect2D, std::placeholders::_1, std::ref(box[0]), std::ref(box[1]));
1216
1217		for (deUint32 instance = 0; instance < m_params.instCount; ++instance)
1218		{
1219			for (deUint32 geometryIndex = 0; geometryIndex < m_params.geomCount; ++geometryIndex)
1220			{
1221				if (!(m_params.ahit() && (geometryIndex % 2 == 1)))
1222				{
1223					box = boxGeometries[instance * m_params.geomCount + geometryIndex];
1224					const tcu::Vec2 center((box[0].x() + box[1].x()) / 2.0f, (box[0].y() + box[1].y()) / 2.0f);
1225
1226					const tcu::IVec2	refImageCoords = rayToImage(center);
1227					const tcu::IVec4	requiredColor = referenceImage[refImageCoords.y() * m_params.width + refImageCoords.x()];
1228
1229					deUint32 resultPixelCount = computeSamePixelCount(resultImage, center, requiredColor, floodColor, pointInGeometry, auxBuffer);
1230					deUint32 referencePixelCount = computeSamePixelCount(referenceImage, center, requiredColor, floodColor, pointInGeometry, auxBuffer);
1231
1232					if (!resultPixelCount || !referencePixelCount) return false;
1233					if (resultPixelCount > referencePixelCount) std::swap(resultPixelCount, referencePixelCount);
1234
1235					const float similarity = float(resultPixelCount) / float(referencePixelCount);
1236					if (similarity < m_params.accuracy) return false;
1237				}
1238			}
1239		}
1240	}
1241
1242	return true;
1243}
1244
1245tcu::TestStatus PipelineFlagsInstance::iterate(void)
1246{
1247	const DeviceInterface& vkd = m_context.getDeviceInterface();
1248	const VkDevice							device = m_context.getDevice();
1249	const deUint32							familyIndex = m_context.getUniversalQueueFamilyIndex();
1250	const VkQueue							queue = m_context.getUniversalQueue();
1251	Allocator& allocator = m_context.getDefaultAllocator();
1252
1253	const VkImageCreateInfo					imageCreateInfo = makeImageCreateInfo();
1254	const VkImageSubresourceRange			imageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0, 1u);
1255	const de::MovePtr<ImageWithMemory>		image = de::MovePtr<ImageWithMemory>(new ImageWithMemory(vkd, device, allocator, imageCreateInfo, MemoryRequirement::Any));
1256	const Move<VkImageView>					imageView = makeImageView(vkd, device, **image, VK_IMAGE_VIEW_TYPE_2D, m_format, imageSubresourceRange);
1257	const VkDescriptorImageInfo				descriptorImageInfo = makeDescriptorImageInfo(DE_NULL, *imageView, VK_IMAGE_LAYOUT_GENERAL);
1258
1259	const deUint32							resultBufferSize = (m_params.width * m_params.height * mapVkFormat(m_format).getPixelSize());
1260	const VkBufferCreateInfo				resultBufferCreateInfo = makeBufferCreateInfo(resultBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
1261	const VkImageSubresourceLayers			resultBufferImageSubresourceLayers = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u);
1262	const VkBufferImageCopy					resultBufferImageRegion = makeBufferImageCopy(makeExtent3D(m_params.width, m_params.height, 1u), resultBufferImageSubresourceLayers);
1263	de::MovePtr<BufferWithMemory>			resultBuffer = de::MovePtr<BufferWithMemory>(new BufferWithMemory(vkd, device, allocator, resultBufferCreateInfo, MemoryRequirement::HostVisible));
1264
1265	const Move<VkDescriptorSetLayout>		descriptorSetLayout = DescriptorSetLayoutBuilder()
1266		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, ALL_RAY_TRACING_STAGES)
1267		.addSingleBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, ALL_RAY_TRACING_STAGES)
1268		.build(vkd, device);
1269	const Move<VkDescriptorPool>			descriptorPool = DescriptorPoolBuilder()
1270		.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
1271		.addType(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR)
1272		.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
1273	const Move<VkDescriptorSet>				descriptorSet = makeDescriptorSet(vkd, device, *descriptorPool, *descriptorSetLayout);
1274
1275	de::MovePtr<RayTracingTestPipeline>		rayTracingPipeline = de::newMovePtr<RayTracingTestPipeline>(std::ref(m_context), std::cref(*this), m_params);
1276	const Move<VkPipelineLayout>			pipelineLayout = makePipelineLayout(vkd, device, *descriptorSetLayout);
1277	Move<VkPipeline>						pipeline = rayTracingPipeline->createPipeline(*pipelineLayout);
1278
1279	de::SharedPtr<BufferWithMemory>			raygenShaderBindingTable;
1280	VkStridedDeviceAddressRegionKHR			raygenShaderBindingTableRegion;
1281	std::tie(raygenShaderBindingTable, raygenShaderBindingTableRegion) = rayTracingPipeline->createRaygenShaderBindingTable(*pipeline);
1282
1283	de::SharedPtr<BufferWithMemory>			missShaderBindingTable;
1284	VkStridedDeviceAddressRegionKHR			missShaderBindingTableRegion;
1285	std::tie(missShaderBindingTable, missShaderBindingTableRegion) = rayTracingPipeline->createMissShaderBindingTable(*pipeline);
1286
1287	de::SharedPtr<BufferWithMemory>			hitShaderBindingTable;
1288	VkStridedDeviceAddressRegionKHR			hitShaderBindingTableRegion;
1289	std::tie(hitShaderBindingTable, hitShaderBindingTableRegion) = rayTracingPipeline->createHitShaderBindingTable(*pipeline);
1290
1291	const VkStridedDeviceAddressRegionKHR	callableShaderBindingTableRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0, 0);
1292
1293	const Move<VkCommandPool>				cmdPool = createCommandPool(vkd, device, 0, familyIndex);
1294	const Move<VkCommandBuffer>				cmdBuffer = allocateCommandBuffer(vkd, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
1295
1296	beginCommandBuffer(vkd, *cmdBuffer);
1297
1298	BottomLevelASPtrs						blasPtrs = createBottomLevelAccelerationStructs(*cmdBuffer);
1299	TopLevelASPtr							tlasPtr = createTopLevelAccelerationStruct(*cmdBuffer, blasPtrs);
1300
1301	VkWriteDescriptorSetAccelerationStructureKHR	accelerationStructureWriteDescriptorSet =
1302	{
1303		VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR,	//  VkStructureType						sType;
1304		DE_NULL,															//  const void*							pNext;
1305		1u,																	//  deUint32							accelerationStructureCount;
1306		tlasPtr->getPtr()													//  const VkAccelerationStructureKHR*	pAccelerationStructures;
1307	};
1308
1309	DescriptorSetUpdateBuilder()
1310		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descriptorImageInfo)
1311		.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, &accelerationStructureWriteDescriptorSet)
1312		.update(vkd, device);
1313
1314	vkd.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, *pipelineLayout, 0, 1, &descriptorSet.get(), 0, DE_NULL);
1315
1316	vkd.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, *pipeline);
1317
1318	const VkImageSubresourceRange	subresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
1319	const VkImageMemoryBarrier				imageMemoryBarrier = makeImageMemoryBarrier(VK_ACCESS_NONE, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, image->get(), subresourceRange);
1320	cmdPipelineImageMemoryBarrier(vkd, *cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, &imageMemoryBarrier);
1321
1322	cmdTraceRays(vkd,
1323		*cmdBuffer,
1324		&raygenShaderBindingTableRegion,	// rgen
1325		&missShaderBindingTableRegion,		// miss
1326		&hitShaderBindingTableRegion,		// hit
1327		&callableShaderBindingTableRegion,	// call
1328		m_params.width, m_params.height, 1);
1329
1330	const VkMemoryBarrier				postTraceMemoryBarrier = makeMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
1331	const VkMemoryBarrier				postCopyMemoryBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
1332	cmdPipelineMemoryBarrier(vkd, *cmdBuffer, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_PIPELINE_STAGE_TRANSFER_BIT, &postTraceMemoryBarrier);
1333
1334	vkd.cmdCopyImageToBuffer(*cmdBuffer, **image, VK_IMAGE_LAYOUT_GENERAL, **resultBuffer, 1u, &resultBufferImageRegion);
1335
1336	cmdPipelineMemoryBarrier(vkd, *cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, &postCopyMemoryBarrier);
1337
1338	endCommandBuffer(vkd, *cmdBuffer);
1339
1340	submitCommandsAndWait(vkd, device, queue, *cmdBuffer);
1341
1342	invalidateMappedMemoryRange(vkd, device, resultBuffer->getAllocation().getMemory(), resultBuffer->getAllocation().getOffset(), resultBufferSize);
1343
1344	return verifyResult(resultBuffer.get()) ? tcu::TestStatus::pass("") : tcu::TestStatus::fail("");
1345}
1346
1347class NoNullShadersFlagGenerator
1348{
1349public:
1350	using FlagsSet = std::set<VkPipelineCreateFlags>;
1351	struct BitAndName {
1352		VkPipelineCreateFlagBits	bit;
1353		const char* name;
1354	};
1355	static const BitAndName	bits[4];
1356	static std::string	name (VkPipelineCreateFlags flags) {
1357		int count = 0;
1358		std::stringstream ss;
1359		for (auto begin = std::begin(bits), end = std::end(bits), i = begin; i != end; ++i) {
1360			if (flags & i->bit) {
1361				if (count++) ss << "_or_";
1362				ss << i->name;
1363			}
1364		}
1365		return count ? ss.str() : "none";
1366	}
1367	static VkPipelineCreateFlags mask (const FlagsSet& flags) {
1368		VkPipelineCreateFlags result{};
1369		for (auto f : flags) result |= f;
1370		return result;
1371	}
1372	NoNullShadersFlagGenerator() : m_next(0) {
1373		FlagsSet fs;
1374		for (const auto& b : bits) fs.insert(b.bit);
1375		combine(m_combs, fs);
1376	}
1377	void	reset() { m_next = 0; }
1378	bool	next(VkPipelineCreateFlags& flags) {
1379		if (m_next < m_combs.size()) {
1380			flags = mask(m_combs[m_next++]);
1381			return true;
1382		}
1383		return false;
1384	}
1385	void combine(std::vector<FlagsSet>& result, const FlagsSet& v) {
1386		if (v.empty() || result.end() != std::find(result.begin(), result.end(), v)) return;
1387		result.push_back(v);
1388		for (deUint32 i = 0; i < v.size(); ++i) {
1389			FlagsSet w(v);
1390			w.erase(std::next(w.begin(), i));
1391			combine(result, w);
1392		}
1393	}
1394private:
1395	size_t					m_next;
1396	std::vector<FlagsSet>	m_combs;
1397};
1398const NoNullShadersFlagGenerator::BitAndName	NoNullShadersFlagGenerator::bits[] = {
1399	{ VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_ANY_HIT_SHADERS_BIT_KHR,		"any"	},
1400	{ VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_CLOSEST_HIT_SHADERS_BIT_KHR,	"chit"	},
1401	{ VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_BIT_KHR,	"isect"	},
1402	{ VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_MISS_SHADERS_BIT_KHR,			"miss"	},
1403};
1404
1405} // unnamed
1406
1407tcu::TestCaseGroup*	createPipelineFlagsTests (tcu::TestContext& testCtx)
1408{
1409	const deUint32 strides[] = { 3, 5 };
1410	const deUint32 offsets[] = { 7 };
1411
1412	struct {
1413		bool		type;
1414		const char*	name;
1415	} const processors[] = { { false, "gpu" }, { true, "cpu" } };
1416	struct {
1417		bool		type;
1418		const char* name;
1419	} const libs[]{ { true, "use_libs" }, { false, "no_libs" } };
1420	struct {
1421		GeometryTypes	type;
1422		const char*		name;
1423	} const geometries[] = { { GeometryTypes::Triangle, "triangles" }, { GeometryTypes::Box, "boxes" }, { GeometryTypes::TriangleAndBox, "tri_and_box" } };
1424
1425	NoNullShadersFlagGenerator	flagsGenerator;
1426
1427	TestParams		p;
1428#ifdef INTERNAL_DEBUG
1429	p.width				= 30;
1430	p.height			= 8;
1431	p.accuracy			= 0.80f;
1432#else
1433	p.width				= 256;
1434	p.height			= 256;
1435	p.accuracy			= 0.95f;
1436#endif
1437	p.onHhost			= false;
1438	p.useLibs			= false;
1439	p.useMaintenance5	= false;
1440	p.flags				= 0;
1441	p.geomTypes			= GeometryTypes::None;
1442	p.instCount			= 3;
1443	p.geomCount			= 2;
1444	p.stbRecStride		= 0;
1445	p.stbRecOffset		= 0;
1446
1447	auto group = new tcu::TestCaseGroup(testCtx, "pipeline_no_null_shaders_flag");
1448
1449	for (auto& processor : processors)
1450	{
1451		auto processorGroup = new tcu::TestCaseGroup(testCtx, processor.name);
1452
1453		for (auto& geometry : geometries)
1454		{
1455			auto geometryGroup = new tcu::TestCaseGroup(testCtx, geometry.name);
1456
1457			for (auto& stride : strides)
1458			{
1459				auto strideGroup = new tcu::TestCaseGroup(testCtx, ("stride_" + std::to_string(stride)).c_str());
1460
1461				for (auto& offset : offsets)
1462				{
1463					auto offsetGroup = new tcu::TestCaseGroup(testCtx, ("offset_" + std::to_string(offset)).c_str());
1464
1465					for (auto& lib : libs)
1466					{
1467						auto libGroup = new tcu::TestCaseGroup(testCtx, lib.name);
1468
1469						VkPipelineCreateFlags	flags;
1470
1471						flagsGenerator.reset();
1472
1473						while (flagsGenerator.next(flags))
1474						{
1475							if ((VK_PIPELINE_CREATE_RAY_TRACING_NO_NULL_INTERSECTION_SHADERS_BIT_KHR & flags)
1476								&& (GeometryTypes::Triangle == geometry.type)) continue;
1477
1478							p.onHhost		= processor.type;
1479							p.geomTypes		= geometry.type;
1480							p.stbRecStride	= stride;
1481							p.stbRecOffset	= offset;
1482							p.flags			= flags;
1483							p.useLibs		= lib.type;
1484
1485							libGroup->addChild(new PipelineFlagsCase(testCtx, flagsGenerator.name(flags), p));
1486						}
1487						offsetGroup->addChild(libGroup);
1488					}
1489					strideGroup->addChild(offsetGroup);
1490				}
1491				geometryGroup->addChild(strideGroup);
1492			}
1493			processorGroup->addChild(geometryGroup);
1494		}
1495		group->addChild(processorGroup);
1496	}
1497
1498	de::MovePtr<tcu::TestCaseGroup> miscGroup(new tcu::TestCaseGroup(testCtx, "misc", ""));
1499
1500	p.onHhost			= false;
1501	p.geomTypes			= GeometryTypes::Box;
1502	p.stbRecStride		= 3;
1503	p.stbRecOffset		= 7;
1504	p.useLibs			= true;
1505	p.useMaintenance5	= true;
1506
1507	for(const auto flag : NoNullShadersFlagGenerator::bits)
1508	{
1509		p.flags = flag.bit;
1510		miscGroup->addChild(new PipelineFlagsCase(testCtx, std::string(flag.name) + "_maintenance5", p));
1511	}
1512
1513	group->addChild(miscGroup.release());
1514
1515	return group;
1516}
1517
1518}	// RayTracing
1519}	// vkt
1520