1/*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2021 The Khronos Group Inc.
6 * Copyright (c) 2021 Valve Corporation.
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 * \file
22 * \brief Ray Query Direction Tests
23 *//*--------------------------------------------------------------------*/
24
25#include "vktRayQueryDirectionTests.hpp"
26#include "vktTestCase.hpp"
27
28#include "vkObjUtil.hpp"
29#include "vkCmdUtil.hpp"
30#include "vkTypeUtil.hpp"
31#include "vkBuilderUtil.hpp"
32#include "vkRayTracingUtil.hpp"
33#include "vkBufferWithMemory.hpp"
34#include "vkBarrierUtil.hpp"
35
36#include "tcuVector.hpp"
37#include "tcuMatrix.hpp"
38
39#include "deUniquePtr.hpp"
40#include "deRandom.hpp"
41#include "deStringUtil.hpp"
42#include "deDefs.hpp"
43
44#include <vector>
45#include <cmath>
46#include <sstream>
47#include <utility>
48#include <algorithm>
49#include <limits>
50
51namespace vkt
52{
53namespace RayQuery
54{
55
56namespace
57{
58
59using namespace vk;
60
61using GeometryData = std::vector<tcu::Vec3>;
62
63// Should rays be shot from inside the geometry or not?
64enum class RayOriginType
65{
66	OUTSIDE = 0,	// Works with AABBs and triangles.
67	INSIDE,			// Works with AABBs only.
68};
69
70// When rays are shot from the outside, they are expected to cross the geometry.
71// When shot from the inside, they can end inside, at the edge or outside the geometry.
72enum class RayEndType
73{
74	CROSS = 0,		// For RayOriginType::OUTSIDE.
75	ZERO,			// For RayOriginType::INSIDE.
76	INSIDE,			// For RayOriginType::INSIDE.
77	EDGE,			// For RayOriginType::INSIDE.
78	OUTSIDE,		// For RayOriginType::INSIDE.
79};
80
81struct SpaceObjects
82{
83	tcu::Vec3		origin;
84	tcu::Vec3		direction;
85	GeometryData	geometry;
86
87	SpaceObjects (RayOriginType rayOriginType, VkGeometryTypeKHR geometryType)
88		: origin	(0.0f, 0.0f, 1.0f)	// Origin of the ray at (0, 0, 1).
89		, direction	(0.0f, 0.0f, 1.0f)	// Shooting towards (0, 0, 1).
90		, geometry	()
91	{
92		DE_ASSERT(geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR || geometryType == VK_GEOMETRY_TYPE_AABBS_KHR);
93		DE_ASSERT(rayOriginType == RayOriginType::OUTSIDE || geometryType == VK_GEOMETRY_TYPE_AABBS_KHR);
94
95		if (geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR)
96		{
97			// Triangle around (0, 0, 5).
98			geometry.reserve(3u);
99			geometry.push_back(tcu::Vec3( 0.0f,  0.5f, 5.0f));
100			geometry.push_back(tcu::Vec3(-0.5f, -0.5f, 5.0f));
101			geometry.push_back(tcu::Vec3( 0.5f, -0.5f, 5.0f));
102		}
103		else
104		{
105			// AABB around (0, 0, 5) or with its back side at that distance when shot from the inside.
106			geometry.reserve(2u);
107			geometry.push_back(tcu::Vec3(-0.5f, -0.5f, ((rayOriginType == RayOriginType::INSIDE) ? 0.0f : 5.0f)));
108			geometry.push_back(tcu::Vec3( 0.5f,  0.5f, 5.0f));
109		}
110	}
111
112	static float getDefaultDistance (void)
113	{
114		// Consistent with the Z coordinates of the origin, direction and points in constructors.
115		return 4.0f;
116	}
117
118	// Calculates distance to geometry edge given the direction scaling factor.
119	static float getDistanceToEdge (float directionScale)
120	{
121		return getDefaultDistance() / directionScale;
122	}
123};
124
125// Default test tolerance for distance values.
126constexpr float kDefaultTolerance = 0.001f;
127
128// Calculates appropriate values for Tmin/Tmax given the distance to the geometry edge.
129std::pair<float, float> calcTminTmax (RayOriginType rayOriginType, RayEndType rayEndType, float distanceToEdge)
130{
131	std::pair<float, float> result;
132
133	if (rayOriginType == RayOriginType::OUTSIDE)
134	{
135		DE_ASSERT(rayEndType == RayEndType::CROSS);
136		const auto margin = kDefaultTolerance / 2.0f;
137		result = std::make_pair(de::max(distanceToEdge - margin, 0.0f), distanceToEdge + margin);
138	}
139	else
140	{
141		result.first = 0.0f;
142		switch (rayEndType)
143		{
144		case RayEndType::ZERO:		result.second = 0.0f;					break;
145		case RayEndType::INSIDE:	result.second = distanceToEdge / 2.0f;	break;
146		case RayEndType::EDGE:		result.second = distanceToEdge;			break;
147		case RayEndType::OUTSIDE:	result.second = distanceToEdge + 1.0f;	break;
148		default: DE_ASSERT(false); break;
149		}
150	}
151
152	return result;
153}
154
155// Get matrix to scale a point with the given scale factor.
156tcu::Mat3 getScaleMatrix (float scaleFactor)
157{
158	const float scaleDirectionMatrixData[] =
159	{
160		scaleFactor,		0.f,			0.f,
161		0.f,				scaleFactor,	0.f,
162		0.f,				0.f,			scaleFactor,
163	};
164	return tcu::Mat3(scaleDirectionMatrixData);
165}
166
167// Get a matrix to rotate a point around the X and Y axis by the given angles in radians.
168tcu::Mat3 getRotationMatrix (float rotationX, float rotationY)
169{
170	const float cosA = std::cos(rotationX);
171	const float sinA = std::sin(rotationX);
172
173	const float cosB = std::cos(rotationY);
174	const float sinB = std::sin(rotationY);
175
176	const float rotationMatrixDataX[] =
177	{
178		1.0f, 0.0f, 0.0f,
179		0.0f, cosA,-sinA,
180		0.0f, sinA, cosA,
181	};
182	const tcu::Mat3 rotationMatrixX (rotationMatrixDataX);
183
184	const float rotationMatrixDataY[] =
185	{
186		cosB, 0.0f,-sinB,
187		0.0f, 1.0f, 0.0f,
188		sinB, 0.0f, cosB,
189	};
190	const tcu::Mat3 rotationMatrixY (rotationMatrixDataY);
191
192	return rotationMatrixX * rotationMatrixY;
193}
194
195// Converts transformation matrix to the expected KHR format.
196VkTransformMatrixKHR toTransformMatrixKHR (const tcu::Mat3& mat3)
197{
198	VkTransformMatrixKHR result;
199
200	deMemset(result.matrix, 0, sizeof(result.matrix));
201	for (int y = 0; y < 3; ++y)
202	for (int x = 0; x < 3; ++x)
203		result.matrix[x][y] = mat3[x][y];
204
205	return result;
206}
207
208struct TestParams
209{
210	SpaceObjects			spaceObjects;
211	float					directionScale;
212	float					rotationX;
213	float					rotationY;
214	VkGeometryTypeKHR		geometryType;
215	bool					useArraysOfPointers;
216	bool					updateMatrixAfterBuild;
217	RayOriginType			rayOriginType;
218	RayEndType				rayEndtype;
219};
220
221class DirectionTestCase : public vkt::TestCase
222{
223public:
224							DirectionTestCase		(tcu::TestContext& testCtx, const std::string& name, const TestParams& params);
225	virtual					~DirectionTestCase		(void) {}
226
227	virtual void			checkSupport			(Context& context) const;
228	virtual void			initPrograms			(vk::SourceCollections& programCollection) const;
229	virtual TestInstance*	createInstance			(Context& context) const;
230
231protected:
232	TestParams				m_params;
233};
234
235class DirectionTestInstance : public vkt::TestInstance
236{
237public:
238								DirectionTestInstance	(Context& context, const TestParams& params);
239	virtual						~DirectionTestInstance	(void) {}
240
241	virtual tcu::TestStatus		iterate					(void);
242
243protected:
244	TestParams					m_params;
245};
246
247
248DirectionTestCase::DirectionTestCase(tcu::TestContext& testCtx, const std::string& name, const TestParams& params)
249	: vkt::TestCase	(testCtx, name)
250	, m_params		(params)
251{}
252
253void DirectionTestCase::checkSupport (Context& context) const
254{
255	context.requireDeviceFunctionality("VK_KHR_acceleration_structure");
256	context.requireDeviceFunctionality("VK_KHR_ray_query");
257}
258
259// Push constants. They need to match the shaders.
260// Note: origin and direction will be used as a Vec3. Declaring them as Vec4 eases matching alignments.
261struct PushConstants
262{
263	tcu::Vec4	origin;
264	tcu::Vec4	direction;
265	float		tmix;
266	float		tmax;
267};
268
269tcu::Vec4 toVec4 (const tcu::Vec3& vec3)
270{
271	return tcu::Vec4(vec3.x(), vec3.y(), vec3.z(), 0.0f);
272}
273
274void DirectionTestCase::initPrograms (vk::SourceCollections& programCollection) const
275{
276	const vk::ShaderBuildOptions buildOptions (programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, 0u, true);
277
278	std::ostringstream comp;
279	comp
280		<< "#version 460 core\n"
281		<< "#extension GL_EXT_ray_query : require\n"
282		<< "\n"
283		<< "layout(local_size_x=1, local_size_y=1, local_size_z=1) in;\n"
284		<< "\n"
285		<< "layout(set=0, binding=0) uniform accelerationStructureEXT topLevelAS;\n"
286		<< "layout(set=0, binding=1, std430) buffer OutBuffer { float val; } outBuffer;\n"
287		// Needs to match the PushConstants struct above.
288		<< "layout(push_constant, std430) uniform PushConstants {\n"
289		<< "  vec4 origin;\n"
290		<< "  vec4 direction;\n"
291		<< "  float tmin;\n"
292		<< "  float tmax;\n"
293		<< "} pc;\n"
294		<< "\n"
295		<< "void main()\n"
296		<< "{\n"
297		<< "  const uint  cullMask = 0xFF;\n"
298		<< "  float       outVal   = -10000.0f;\n"
299		<< "  rayQueryEXT rq;\n"
300		<< "  rayQueryInitializeEXT(rq, topLevelAS, gl_RayFlagsNoneEXT, cullMask, pc.origin.xyz, pc.tmin, pc.direction.xyz, pc.tmax);\n"
301		<< "  while (rayQueryProceedEXT(rq)) {\n"
302		<< "    const uint candidateType = rayQueryGetIntersectionTypeEXT(rq, false);\n"
303		<< "    if (candidateType == gl_RayQueryCandidateIntersectionTriangleEXT) {\n"
304		<< "      outVal = rayQueryGetIntersectionTEXT(rq, false);\n"
305		<< "    }\n"
306		<< "    else if (candidateType == gl_RayQueryCandidateIntersectionAABBEXT) {\n"
307		<< "      outVal = pc.tmin;\n"
308		<< "    }\n"
309		<< "  }\n"
310		<< "  outBuffer.val = outVal;\n"
311		<< "}\n"
312		;
313
314	programCollection.glslSources.add("comp") << glu::ComputeSource(updateRayTracingGLSL(comp.str())) << buildOptions;
315}
316
317TestInstance* DirectionTestCase::createInstance (Context& context) const
318{
319	return new DirectionTestInstance(context, m_params);
320}
321
322DirectionTestInstance::DirectionTestInstance (Context& context, const TestParams& params)
323	: vkt::TestInstance	(context)
324	, m_params			(params)
325{}
326
327tcu::TestStatus DirectionTestInstance::iterate (void)
328{
329	const auto&	vkd		= m_context.getDeviceInterface();
330	const auto	device	= m_context.getDevice();
331	auto&		alloc	= m_context.getDefaultAllocator();
332	const auto	qIndex	= m_context.getUniversalQueueFamilyIndex();
333	const auto	queue	= m_context.getUniversalQueue();
334	const auto	stages	= VK_SHADER_STAGE_COMPUTE_BIT;
335	const auto	pcSize	= static_cast<deUint32>(sizeof(PushConstants));
336
337	const auto	scaleMatrix		= getScaleMatrix(m_params.directionScale);
338	const auto	rotationMatrix	= getRotationMatrix(m_params.rotationX, m_params.rotationY);
339	const auto	transformMatrix	= toTransformMatrixKHR(rotationMatrix);
340
341	// Command pool and buffer.
342	const auto cmdPool		= makeCommandPool(vkd, device, qIndex);
343	const auto cmdBufferPtr	= allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
344	const auto cmdBuffer	= cmdBufferPtr.get();
345
346	beginCommandBuffer(vkd, cmdBuffer);
347
348	// Build acceleration structures.
349	auto topLevelAS		= makeTopLevelAccelerationStructure();
350	auto bottomLevelAS	= makeBottomLevelAccelerationStructure();
351
352	const bool							isTriangles		= (m_params.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR);
353	const VkGeometryInstanceFlagsKHR	instanceFlags	= (isTriangles ? VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR : 0);
354
355	bottomLevelAS->addGeometry(m_params.spaceObjects.geometry, isTriangles, VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR);
356	bottomLevelAS->createAndBuild(vkd, device, cmdBuffer, alloc);
357
358	de::SharedPtr<BottomLevelAccelerationStructure> blasSharedPtr (bottomLevelAS.release());
359	topLevelAS->setUseArrayOfPointers(m_params.useArraysOfPointers);
360	topLevelAS->setUsePPGeometries(m_params.useArraysOfPointers);
361	topLevelAS->setInstanceCount(1);
362	{
363		const auto& initialMatrix = (m_params.updateMatrixAfterBuild ? identityMatrix3x4 : transformMatrix);
364		topLevelAS->addInstance(blasSharedPtr, initialMatrix, 0, 0xFFu, 0u, instanceFlags);
365	}
366	topLevelAS->createAndBuild(vkd, device, cmdBuffer, alloc);
367	if (m_params.updateMatrixAfterBuild)
368		topLevelAS->updateInstanceMatrix(vkd, device, 0u, transformMatrix);
369
370	// Create output buffer.
371	const auto			bufferSize			= static_cast<VkDeviceSize>(sizeof(float));
372	const auto			bufferCreateInfo	= makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
373	BufferWithMemory	buffer				(vkd, device, alloc, bufferCreateInfo, MemoryRequirement::HostVisible);
374	auto&				bufferAlloc			= buffer.getAllocation();
375
376	// Fill output buffer with an initial value.
377	deMemset(bufferAlloc.getHostPtr(), 0, sizeof(float));
378	flushAlloc(vkd, device, bufferAlloc);
379
380	// Descriptor set layout and pipeline layout.
381	DescriptorSetLayoutBuilder setLayoutBuilder;
382	setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, stages);
383	setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, stages);
384	const auto setLayout = setLayoutBuilder.build(vkd, device);
385
386	const auto pcRange = makePushConstantRange(stages, 0u, pcSize);
387	const auto pipelineLayout = makePipelineLayout(vkd, device, 1u, &setLayout.get(), 1u, &pcRange);
388
389	// Descriptor pool and set.
390	DescriptorPoolBuilder poolBuilder;
391	poolBuilder.addType(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR);
392	poolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u);
393	const auto descriptorPool	= poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
394	const auto descriptorSet	= makeDescriptorSet(vkd, device, descriptorPool.get(), setLayout.get());
395
396	// Update descriptor set.
397	{
398		const VkWriteDescriptorSetAccelerationStructureKHR accelDescInfo =
399		{
400			VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR,
401			nullptr,
402			1u,
403			topLevelAS.get()->getPtr(),
404		};
405
406		const auto bufferDescInfo = makeDescriptorBufferInfo(buffer.get(), 0ull, VK_WHOLE_SIZE);
407
408		DescriptorSetUpdateBuilder updateBuilder;
409		updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, &accelDescInfo);
410		updateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &bufferDescInfo);
411		updateBuilder.update(vkd, device);
412	}
413
414	// Shader module and pipeline.
415	const auto compModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("comp"), 0);
416
417	const VkPipelineShaderStageCreateInfo shaderInfo =
418	{
419		VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,	//	VkStructureType						sType;
420		nullptr,												//	const void*							pNext;
421		0u,														//	VkPipelineShaderStageCreateFlags	flags;
422		VK_SHADER_STAGE_COMPUTE_BIT,							//	VkShaderStageFlagBits				stage;
423		compModule.get(),										//	VkShaderModule						module;
424		"main",													//	const char*							pName;
425		nullptr,												//	const VkSpecializationInfo*			pSpecializationInfo;
426	};
427	const VkComputePipelineCreateInfo pipelineInfo =
428	{
429		VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,			//	VkStructureType					sType;
430		nullptr,												//	const void*						pNext;
431		0u,														//	VkPipelineCreateFlags			flags;
432		shaderInfo,												//	VkPipelineShaderStageCreateInfo	stage;
433		pipelineLayout.get(),									//	VkPipelineLayout				layout;
434		DE_NULL,												//	VkPipeline						basePipelineHandle;
435		0,														//	deInt32							basePipelineIndex;
436	};
437	const auto pipeline = createComputePipeline(vkd, device, DE_NULL, &pipelineInfo);
438
439	// Push constants.
440	const auto			rotatedOrigin		= m_params.spaceObjects.origin * rotationMatrix;
441	const auto			finalDirection		= m_params.spaceObjects.direction * scaleMatrix * rotationMatrix;
442	const auto			distanceToEdge		= SpaceObjects::getDistanceToEdge(m_params.directionScale);
443	const auto			tMinMax				= calcTminTmax(m_params.rayOriginType, m_params.rayEndtype, distanceToEdge);
444	const PushConstants	pcData				=
445	{
446		toVec4(rotatedOrigin),	//	tcu::Vec4	origin;
447		toVec4(finalDirection),	//	tcu::Vec4	direction;
448		tMinMax.first,			//	float		tmix;
449		tMinMax.second,			//	float		tmax;
450	};
451
452	// Trace rays.
453	vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.get());
454	vkd.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout.get(), 0u, 1u, &descriptorSet.get(), 0u, nullptr);
455	vkd.cmdPushConstants(cmdBuffer, pipelineLayout.get(), stages, 0u, pcSize, &pcData);
456	vkd.cmdDispatch(cmdBuffer, 1u, 1u, 1u);
457
458	// Barrier for the output buffer.
459	const auto bufferBarrier = makeMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
460	vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &bufferBarrier, 0u, nullptr, 0u, nullptr);
461
462	endCommandBuffer(vkd, cmdBuffer);
463	submitCommandsAndWait(vkd, device, queue, cmdBuffer);
464
465	// Read value back from the buffer.
466	float bufferValue = 0.0f;
467	invalidateAlloc(vkd, device, bufferAlloc);
468	deMemcpy(&bufferValue, bufferAlloc.getHostPtr(), sizeof(bufferValue));
469
470	if (m_params.rayEndtype == RayEndType::CROSS)
471	{
472		// Shooting from the ouside.
473		if (de::abs(bufferValue - distanceToEdge) > kDefaultTolerance)
474		{
475			std::ostringstream msg;
476			msg << "Result distance (" << bufferValue << ") differs from expected distance (" << distanceToEdge << ", tolerance " << kDefaultTolerance << ")";
477			TCU_FAIL(msg.str());
478		}
479	}
480	else
481	{
482		// Rays are shot from inside AABBs, rayTMin should be zero and the reported hit distance.
483		if (bufferValue != 0.0f)
484		{
485			std::ostringstream msg;
486			msg << "Result distance nonzero (" << bufferValue << ")";
487			TCU_FAIL(msg.str());
488		}
489	}
490
491	return tcu::TestStatus::pass("Pass");
492}
493
494using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
495
496// Generate a list of scaling factors suitable for the tests.
497std::vector<float> generateScalingFactors (de::Random& rnd)
498{
499	const float	kMinScalingFactor			= 0.5f;
500	const float	kMaxScalingFactor			= 10.0f;
501	const int	kNumRandomScalingFactors	= 5;
502
503	// Scaling factors: 1.0 and some randomly-generated ones.
504	std::vector<float> scalingFactors;
505
506	scalingFactors.reserve(kNumRandomScalingFactors + 1);
507	scalingFactors.push_back(1.0f);
508
509	for (int i = 0; i < kNumRandomScalingFactors; ++i)
510		scalingFactors.push_back(rnd.getFloat() * (kMaxScalingFactor - kMinScalingFactor) + kMinScalingFactor);
511
512	return scalingFactors;
513}
514
515// Generate a list of rotation angles suitable for the tests.
516std::vector<std::pair<float, float>> generateRotationAngles (de::Random& rnd)
517{
518	const float	kPi2				= DE_PI * 2.0f;
519	const int	kNumRandomRotations	= 4;
520
521	// Rotations: 0.0 on both axis and some randomly-generated ones.
522	std::vector<std::pair<float, float>> rotationAngles;
523
524	rotationAngles.reserve(kNumRandomRotations + 1);
525	rotationAngles.push_back(std::make_pair(0.0f, 0.0f));
526
527	for (int i = 0; i < kNumRandomRotations; ++i)
528		rotationAngles.push_back(std::make_pair(rnd.getFloat() * kPi2, rnd.getFloat() * kPi2));
529
530	return rotationAngles;
531}
532
533} // anonymous
534
535tcu::TestCaseGroup*	createDirectionLengthTests (tcu::TestContext& testCtx)
536{
537	// Test direction vector length when using ray queries
538	GroupPtr directionGroup (new tcu::TestCaseGroup(testCtx, "direction_length"));
539
540	struct
541	{
542		VkGeometryTypeKHR		geometryType;
543		const char*				name;
544	} geometryTypes[] =
545	{
546		{ VK_GEOMETRY_TYPE_TRIANGLES_KHR,	"triangles"	},
547		{ VK_GEOMETRY_TYPE_AABBS_KHR,		"aabbs"		},
548	};
549
550	de::Random	rnd(1614686501u);
551	deUint32	caseCounter = 0u;
552
553	// Scaling factors: 1.0 and some randomly-generated ones.
554	// Scaling factors and rotation angles.
555	const auto scalingFactors = generateScalingFactors(rnd);
556	const auto rotationAngles = generateRotationAngles(rnd);
557
558	for (int geometryTypeIdx = 0; geometryTypeIdx < DE_LENGTH_OF_ARRAY(geometryTypes); ++geometryTypeIdx)
559	{
560		const auto& gType = geometryTypes[geometryTypeIdx];
561
562		GroupPtr geomGroup (new tcu::TestCaseGroup(testCtx, gType.name));
563
564		for (size_t scalingIdx = 0; scalingIdx < scalingFactors.size(); ++scalingIdx)
565		{
566			const auto scale		= scalingFactors[scalingIdx];
567			const auto scaleName	= "scaling_factor_" + de::toString(scalingIdx);
568			GroupPtr factorGroup (new tcu::TestCaseGroup(testCtx, scaleName.c_str()));
569
570			for (size_t rotationIdx = 0; rotationIdx < rotationAngles.size(); ++rotationIdx)
571			{
572				const auto angles		= rotationAngles[rotationIdx];
573				const auto angleName	= "rotation_" + de::toString(rotationIdx);
574				const auto geometryType	= gType.geometryType;
575				const auto rayOrigType	= RayOriginType::OUTSIDE;
576				const auto rayEndType	= RayEndType::CROSS;
577
578				SpaceObjects spaceObjects(rayOrigType, geometryType);
579
580				TestParams params =
581				{
582					spaceObjects,				//		SpaceObjects			spaceObjects;
583					scale,						//		float					directionScale;
584					angles.first,				//		float					rotationX;
585					angles.second,				//		float					rotationY;
586					geometryType,				//		VkGeometryTypeKHR		geometryType;
587					// Use arrays of pointers when building the TLAS in every other test.
588					(caseCounter % 2u == 0u),	//		bool					useArraysOfPointers;
589					// Sometimes, update matrix after building the lop level AS and before submitting the command buffer.
590					(caseCounter % 3u == 0u),	//		bool					updateMatrixAfterBuild;
591					rayOrigType,				//		RayOriginType			rayOriginType;
592					rayEndType,					//		RayEndType				rayEndType;
593				};
594				++caseCounter;
595
596				factorGroup->addChild(new DirectionTestCase(testCtx, angleName, params));
597			}
598
599			geomGroup->addChild(factorGroup.release());
600		}
601
602		directionGroup->addChild(geomGroup.release());
603	}
604
605	return directionGroup.release();
606}
607
608tcu::TestCaseGroup*	createInsideAABBsTests (tcu::TestContext& testCtx)
609{
610	GroupPtr insideAABBsGroup (new tcu::TestCaseGroup(testCtx, "inside_aabbs", "Test shooting rays that start inside AABBs"));
611
612	struct
613	{
614		RayEndType				rayEndType;
615		const char*				name;
616	} rayEndCases[] =
617	{
618		{	RayEndType::ZERO,			"tmax_zero"	},
619		{	RayEndType::INSIDE,			"inside"	},
620		{	RayEndType::EDGE,			"edge"		},
621		{	RayEndType::OUTSIDE,		"outside"	},
622	};
623
624	de::Random rnd(1621948244u);
625
626	// Scaling factors: 1.0 and some randomly-generated ones.
627	// Scaling factors and rotation angles.
628	const auto scalingFactors = generateScalingFactors(rnd);
629	const auto rotationAngles = generateRotationAngles(rnd);
630
631	for (int rayEndCaseIdx = 0; rayEndCaseIdx < DE_LENGTH_OF_ARRAY(rayEndCases); ++rayEndCaseIdx)
632	{
633		const auto&			rayEndCase	= rayEndCases[rayEndCaseIdx];
634		const std::string	rayEndName	= std::string("ray_end_") + rayEndCase.name;
635		GroupPtr			rayEndGroup	(new tcu::TestCaseGroup(testCtx, rayEndName.c_str()));
636
637		for (size_t scalingIdx = 0; scalingIdx < scalingFactors.size(); ++scalingIdx)
638		{
639			const auto scale		= scalingFactors[scalingIdx];
640			const auto scaleName	= "scaling_factor_" + de::toString(scalingIdx);
641			GroupPtr factorGroup (new tcu::TestCaseGroup(testCtx, scaleName.c_str()));
642
643			for (size_t rotationIdx = 0; rotationIdx < rotationAngles.size(); ++rotationIdx)
644			{
645				const auto angles		= rotationAngles[rotationIdx];
646				const auto angleName	= "rotation_" + de::toString(rotationIdx);
647				const auto geometryType	= VK_GEOMETRY_TYPE_AABBS_KHR;
648				const auto rayOrigType	= RayOriginType::INSIDE;
649
650				SpaceObjects spaceObjects(rayOrigType, geometryType);
651
652				TestParams params =
653				{
654					spaceObjects,			//		SpaceObjects			spaceObjects;
655					scale,					//		float					directionScale;
656					angles.first,			//		float					rotationX;
657					angles.second,			//		float					rotationY;
658					geometryType,			//		VkGeometryTypeKHR		geometryType;
659					false,					//		bool					useArraysOfPointers;
660					false,					//		bool					updateMatrixAfterBuild;
661					rayOrigType,			//		RayOriginType			rayOriginType;
662					rayEndCase.rayEndType,	//		RayEndType				rayEndType;
663				};
664
665				factorGroup->addChild(new DirectionTestCase(testCtx, angleName, params));
666			}
667
668			rayEndGroup->addChild(factorGroup.release());
669		}
670
671		insideAABBsGroup->addChild(rayEndGroup.release());
672	}
673
674	return insideAABBsGroup.release();
675}
676
677} // RayQuery
678} // vkt
679