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 Test procedural geometry with complex bouding box sets
22 *//*--------------------------------------------------------------------*/
23
24#include "vktRayQueryProceduralGeometryTests.hpp"
25#include "vkDefs.hpp"
26#include "vktTestCase.hpp"
27#include "vktTestGroupUtil.hpp"
28#include "vkCmdUtil.hpp"
29#include "vkObjUtil.hpp"
30#include "vkBuilderUtil.hpp"
31#include "vkBarrierUtil.hpp"
32#include "vkBufferWithMemory.hpp"
33#include "vkImageWithMemory.hpp"
34#include "vkTypeUtil.hpp"
35#include "vkImageUtil.hpp"
36#include "vkRayTracingUtil.hpp"
37#include "tcuVectorUtil.hpp"
38#include "tcuTexture.hpp"
39#include "tcuTestLog.hpp"
40#include "tcuImageCompare.hpp"
41#include "tcuFloat.hpp"
42
43namespace vkt
44{
45namespace RayQuery
46{
47namespace
48{
49using namespace vk;
50using namespace vkt;
51
52enum class TestType
53{
54	OBJECT_BEHIND_BOUNDING_BOX = 0,
55	TRIANGLE_IN_BETWEEN
56};
57
58class RayQueryProceduralGeometryTestBase : public TestInstance
59{
60public:
61
62	RayQueryProceduralGeometryTestBase	(Context& context);
63	~RayQueryProceduralGeometryTestBase	(void) = default;
64
65	tcu::TestStatus iterate				(void) override;
66
67protected:
68
69	virtual void setupAccelerationStructures() = 0;
70
71private:
72
73	VkWriteDescriptorSetAccelerationStructureKHR	makeASWriteDescriptorSet	(const VkAccelerationStructureKHR* pAccelerationStructure);
74	void											clearBuffer					(de::SharedPtr<BufferWithMemory> buffer, VkDeviceSize bufferSize);
75
76protected:
77
78	Move<VkCommandPool>		m_cmdPool;
79	Move<VkCommandBuffer>	m_cmdBuffer;
80
81	std::vector<de::SharedPtr<BottomLevelAccelerationStructure> >	m_blasVect;
82	de::SharedPtr<TopLevelAccelerationStructure>					m_referenceTLAS;
83	de::SharedPtr<TopLevelAccelerationStructure>					m_resultTLAS;
84};
85
86RayQueryProceduralGeometryTestBase::RayQueryProceduralGeometryTestBase(Context& context)
87	: vkt::TestInstance	(context)
88	, m_referenceTLAS	(makeTopLevelAccelerationStructure().release())
89	, m_resultTLAS		(makeTopLevelAccelerationStructure().release())
90{
91}
92
93tcu::TestStatus RayQueryProceduralGeometryTestBase::iterate(void)
94{
95	const DeviceInterface&	vkd					= m_context.getDeviceInterface();
96	const VkDevice			device				= m_context.getDevice();
97	const deUint32			queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
98	const VkQueue			queue				= m_context.getUniversalQueue();
99	Allocator&				allocator			= m_context.getDefaultAllocator();
100	const deUint32			imageSize			= 64u;
101
102	const Move<VkDescriptorPool> descriptorPool = DescriptorPoolBuilder()
103		.addType(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 2u)
104		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2u)
105		.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 2u);
106
107	Move<VkDescriptorSetLayout> descriptorSetLayout = DescriptorSetLayoutBuilder()
108		.addSingleBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, VK_SHADER_STAGE_COMPUTE_BIT)	// as with single/four aabb's
109		.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT)				// ssbo with result/reference values
110		.build(vkd, device);
111
112	const Move<VkDescriptorSet>			referenceDescriptorSet	= makeDescriptorSet(vkd, device, *descriptorPool, *descriptorSetLayout);
113	const Move<VkDescriptorSet>			resultDescriptorSet		= makeDescriptorSet(vkd, device, *descriptorPool, *descriptorSetLayout);
114
115	const VkDeviceSize					resultBufferSize		= imageSize * imageSize * sizeof(int);
116	const VkBufferCreateInfo			resultBufferCreateInfo	= makeBufferCreateInfo(resultBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
117	de::SharedPtr<BufferWithMemory>		referenceBuffer			= de::SharedPtr<BufferWithMemory>(new BufferWithMemory(vkd, device, allocator, resultBufferCreateInfo, MemoryRequirement::HostVisible));
118	de::SharedPtr<BufferWithMemory>		resultBuffer			= de::SharedPtr<BufferWithMemory>(new BufferWithMemory(vkd, device, allocator, resultBufferCreateInfo, MemoryRequirement::HostVisible));
119
120	Move<VkShaderModule>				shaderModule		= createShaderModule(vkd, device, m_context.getBinaryCollection().get("comp"), 0u);
121	const Move<VkPipelineLayout>		pipelineLayout		= makePipelineLayout(vkd, device, descriptorSetLayout.get());
122	const VkComputePipelineCreateInfo	pipelineCreateInfo
123	{
124		VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,			// VkStructureType						sType
125		DE_NULL,												// const void*							pNext
126		0u,														// VkPipelineCreateFlags				flags
127		{														// VkPipelineShaderStageCreateInfo		stage
128			VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
129			DE_NULL,
130			(VkPipelineShaderStageCreateFlags)0,
131			VK_SHADER_STAGE_COMPUTE_BIT,
132			*shaderModule,
133			"main",
134			DE_NULL
135		},
136		*pipelineLayout,										// VkPipelineLayout						layout
137		DE_NULL,												// VkPipeline							basePipelineHandle
138		0,														// deInt32								basePipelineIndex
139	};
140	Move<VkPipeline> pipeline = createComputePipeline(vkd, device, DE_NULL, &pipelineCreateInfo);
141
142	m_cmdPool	= createCommandPool(vkd, device, 0, queueFamilyIndex);
143	m_cmdBuffer	= allocateCommandBuffer(vkd, device, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
144
145	// clear result and reference buffers
146	clearBuffer(resultBuffer, resultBufferSize);
147	clearBuffer(referenceBuffer, resultBufferSize);
148
149	beginCommandBuffer(vkd, *m_cmdBuffer, 0u);
150	{
151		setupAccelerationStructures();
152
153		// update descriptor sets
154		{
155			typedef DescriptorSetUpdateBuilder::Location DSL;
156
157			const VkWriteDescriptorSetAccelerationStructureKHR	referenceAS		= makeASWriteDescriptorSet(m_referenceTLAS->getPtr());
158			const VkDescriptorBufferInfo						referenceSSBO	= makeDescriptorBufferInfo(**referenceBuffer, 0u, VK_WHOLE_SIZE);
159			DescriptorSetUpdateBuilder()
160				.writeSingle(*referenceDescriptorSet, DSL::binding(0u), VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, &referenceAS)
161				.writeSingle(*referenceDescriptorSet, DSL::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &referenceSSBO)
162				.update(vkd, device);
163
164			const VkWriteDescriptorSetAccelerationStructureKHR	resultAS	= makeASWriteDescriptorSet(m_resultTLAS->getPtr());
165			const VkDescriptorBufferInfo						resultSSBO	= makeDescriptorBufferInfo(**resultBuffer, 0u, VK_WHOLE_SIZE);
166			DescriptorSetUpdateBuilder()
167				.writeSingle(*resultDescriptorSet, DSL::binding(0u), VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, &resultAS)
168				.writeSingle(*resultDescriptorSet, DSL::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultSSBO)
169				.update(vkd, device);
170		}
171
172		// wait for data transfers
173		const VkMemoryBarrier bufferUploadBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
174		cmdPipelineMemoryBarrier(vkd, *m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, &bufferUploadBarrier, 1u);
175
176		// wait for as build
177		const VkMemoryBarrier asBuildBarrier = makeMemoryBarrier(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_SHADER_READ_BIT);
178		cmdPipelineMemoryBarrier(vkd, *m_cmdBuffer, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, &asBuildBarrier, 1u);
179
180		vkd.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
181
182		// generate reference
183		vkd.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0, 1, &referenceDescriptorSet.get(), 0, DE_NULL);
184		vkd.cmdDispatch(*m_cmdBuffer, imageSize, imageSize, 1);
185
186		// generate result
187		vkd.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0, 1, &resultDescriptorSet.get(), 0, DE_NULL);
188		vkd.cmdDispatch(*m_cmdBuffer, imageSize, imageSize, 1);
189
190		const VkMemoryBarrier postTraceMemoryBarrier = makeMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
191		cmdPipelineMemoryBarrier(vkd, *m_cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, &postTraceMemoryBarrier);
192	}
193	endCommandBuffer(vkd, *m_cmdBuffer);
194
195	submitCommandsAndWait(vkd, device, queue, m_cmdBuffer.get());
196
197	// verify result buffer
198	auto referenceAllocation = referenceBuffer->getAllocation();
199	invalidateMappedMemoryRange(vkd, device, referenceAllocation.getMemory(), referenceAllocation.getOffset(), resultBufferSize);
200
201	auto resultAllocation = resultBuffer->getAllocation();
202	invalidateMappedMemoryRange(vkd, device, resultAllocation.getMemory(), resultAllocation.getOffset(), resultBufferSize);
203
204	tcu::TextureFormat		imageFormat		(vk::mapVkFormat(VK_FORMAT_R8G8B8A8_UNORM));
205	tcu::PixelBufferAccess	referenceAccess	(imageFormat, imageSize, imageSize, 1, referenceAllocation.getHostPtr());
206	tcu::PixelBufferAccess	resultAccess	(imageFormat, imageSize, imageSize, 1, resultAllocation.getHostPtr());
207
208	if (tcu::intThresholdCompare(m_context.getTestContext().getLog(), "Result comparison", "", referenceAccess, resultAccess, tcu::UVec4(0), tcu::COMPARE_LOG_EVERYTHING))
209		return tcu::TestStatus::pass("Pass");
210	return tcu::TestStatus::fail("Fail");
211}
212
213VkWriteDescriptorSetAccelerationStructureKHR RayQueryProceduralGeometryTestBase::makeASWriteDescriptorSet(const VkAccelerationStructureKHR* pAccelerationStructure)
214{
215	return
216	{
217		VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR,	// VkStructureType						sType
218		DE_NULL,															// const void*							pNext
219		1u,																	// deUint32								accelerationStructureCount
220		pAccelerationStructure												// const VkAccelerationStructureKHR*	pAccelerationStructures
221	};
222}
223
224void RayQueryProceduralGeometryTestBase::clearBuffer(de::SharedPtr<BufferWithMemory> buffer, VkDeviceSize bufferSize)
225{
226	const DeviceInterface&	vkd				= m_context.getDeviceInterface();
227	const VkDevice			device			= m_context.getDevice();
228	auto&					bufferAlloc		= buffer->getAllocation();
229	void*					bufferPtr		= bufferAlloc.getHostPtr();
230
231	deMemset(bufferPtr, 1, static_cast<size_t>(bufferSize));
232	vk::flushAlloc(vkd, device, bufferAlloc);
233}
234
235class ObjectBehindBoundingBoxInstance : public RayQueryProceduralGeometryTestBase
236{
237public:
238
239	ObjectBehindBoundingBoxInstance(Context& context);
240	void setupAccelerationStructures() override;
241};
242
243ObjectBehindBoundingBoxInstance::ObjectBehindBoundingBoxInstance(Context& context)
244	: RayQueryProceduralGeometryTestBase(context)
245{
246}
247
248void ObjectBehindBoundingBoxInstance::setupAccelerationStructures()
249{
250	const DeviceInterface&	vkd			= m_context.getDeviceInterface();
251	const VkDevice			device		= m_context.getDevice();
252	Allocator&				allocator	= m_context.getDefaultAllocator();
253
254	// build reference acceleration structure - single aabb big enough to fit whole procedural geometry
255	de::SharedPtr<BottomLevelAccelerationStructure> referenceBLAS(makeBottomLevelAccelerationStructure().release());
256	referenceBLAS->setGeometryData(
257		{
258			{  0.0,  0.0, -64.0 },
259			{ 64.0, 64.0, -16.0 },
260		},
261		false,
262		0
263		);
264	referenceBLAS->createAndBuild(vkd, device, *m_cmdBuffer, allocator);
265	m_blasVect.push_back(referenceBLAS);
266
267	m_referenceTLAS->setInstanceCount(1);
268	m_referenceTLAS->addInstance(m_blasVect.back());
269	m_referenceTLAS->createAndBuild(vkd, device, *m_cmdBuffer, allocator);
270
271	// build result acceleration structure - wall of 4 aabb's and generated object is actualy behind it (as it is just 1.0 unit thick)
272	de::SharedPtr<BottomLevelAccelerationStructure> resultBLAS(makeBottomLevelAccelerationStructure().release());
273	resultBLAS->setGeometryData(
274		{
275			{  0.0,  0.0, 0.0 },	// |  |
276			{ 32.0, 32.0, 1.0 },	// |* |
277			{ 32.0,  0.0, 0.0 },	//    |  |
278			{ 64.0, 32.0, 1.0 },	//    | *|
279			{  0.0, 32.0, 0.0 },	// |* |
280			{ 32.0, 64.0, 1.0 },	// |  |
281			{ 32.0, 32.0, 0.0 },	//    | *|
282			{ 64.0, 64.0, 1.0 },	//    |  |
283		},
284		false,
285		0
286		);
287	resultBLAS->createAndBuild(vkd, device, *m_cmdBuffer, allocator);
288	m_blasVect.push_back(resultBLAS);
289
290	m_resultTLAS->setInstanceCount(1);
291	m_resultTLAS->addInstance(m_blasVect.back());
292	m_resultTLAS->createAndBuild(vkd, device, *m_cmdBuffer, allocator);
293}
294
295class TriangleInBeteenInstance : public RayQueryProceduralGeometryTestBase
296{
297public:
298
299	TriangleInBeteenInstance(Context& context);
300	void setupAccelerationStructures() override;
301};
302
303TriangleInBeteenInstance::TriangleInBeteenInstance(Context& context)
304	: RayQueryProceduralGeometryTestBase(context)
305{
306}
307
308void TriangleInBeteenInstance::setupAccelerationStructures()
309{
310	const DeviceInterface&	vkd			= m_context.getDeviceInterface();
311	const VkDevice			device		= m_context.getDevice();
312	Allocator&				allocator	= m_context.getDefaultAllocator();
313
314	de::SharedPtr<BottomLevelAccelerationStructure> triangleBLAS(makeBottomLevelAccelerationStructure().release());
315	triangleBLAS->setGeometryData(
316		{
317			{ 16.0, 16.0, -8.0 },
318			{ 56.0, 32.0, -8.0 },
319			{ 32.0, 48.0, -8.0 },
320		},
321		true,
322		VK_GEOMETRY_OPAQUE_BIT_KHR
323		);
324	triangleBLAS->createAndBuild(vkd, device, *m_cmdBuffer, allocator);
325	m_blasVect.push_back(triangleBLAS);
326
327	de::SharedPtr<BottomLevelAccelerationStructure> fullElipsoidBLAS(makeBottomLevelAccelerationStructure().release());
328	fullElipsoidBLAS->setGeometryData(
329		{
330			{  0.0,  0.0, -64.0 },
331			{ 64.0, 64.0, -16.0 },
332		},
333		false,
334		0
335		);
336	fullElipsoidBLAS->createAndBuild(vkd, device, *m_cmdBuffer, allocator);
337	m_blasVect.push_back(fullElipsoidBLAS);
338
339	// build reference acceleration structure - triangle and a single aabb big enough to fit whole procedural geometry
340	m_referenceTLAS->setInstanceCount(2);
341	m_referenceTLAS->addInstance(fullElipsoidBLAS);
342	m_referenceTLAS->addInstance(triangleBLAS);
343	m_referenceTLAS->createAndBuild(vkd, device, *m_cmdBuffer, allocator);
344
345	de::SharedPtr<BottomLevelAccelerationStructure> elipsoidWallBLAS(makeBottomLevelAccelerationStructure().release());
346	elipsoidWallBLAS->setGeometryData(
347		{
348			{  0.0,  0.0, 0.0 },	// |*  |
349			{ 20.0, 64.0, 1.0 },
350			{ 20.0,  0.0, 0.0 },	// | * |
351			{ 44.0, 64.0, 1.0 },
352			{ 44.0,  0.0, 0.0 },	// |  *|
353			{ 64.0, 64.0, 1.0 },
354		},
355		false,
356		0
357		);
358	elipsoidWallBLAS->createAndBuild(vkd, device, *m_cmdBuffer, allocator);
359	m_blasVect.push_back(elipsoidWallBLAS);
360
361	// build result acceleration structure - triangle and a three aabb's (they are in front of triangle but generate intersections behind it)
362	m_resultTLAS->setInstanceCount(2);
363	m_resultTLAS->addInstance(elipsoidWallBLAS);
364	m_resultTLAS->addInstance(triangleBLAS);
365	m_resultTLAS->createAndBuild(vkd, device, *m_cmdBuffer, allocator);
366}
367
368class RayQueryProceduralGeometryTestCase : public TestCase
369{
370public:
371	RayQueryProceduralGeometryTestCase		(tcu::TestContext& context, const char* name, TestType testType);
372	~RayQueryProceduralGeometryTestCase		(void) = default;
373
374	void				checkSupport			(Context& context) const override;
375	void				initPrograms			(SourceCollections& programCollection) const override;
376	TestInstance*		createInstance			(Context& context) const override;
377
378protected:
379	TestType m_testType;
380};
381
382RayQueryProceduralGeometryTestCase::RayQueryProceduralGeometryTestCase(tcu::TestContext& context, const char* name, TestType testType)
383	: TestCase		(context, name)
384	, m_testType	(testType)
385{
386}
387
388void RayQueryProceduralGeometryTestCase::checkSupport(Context& context) const
389{
390	context.requireDeviceFunctionality("VK_KHR_acceleration_structure");
391	context.requireDeviceFunctionality("VK_KHR_ray_query");
392
393	if (!context.getRayQueryFeatures().rayQuery)
394		TCU_THROW(NotSupportedError, "Requires VkPhysicalDeviceRayQueryFeaturesKHR.rayQuery");
395
396	if (!context.getAccelerationStructureFeatures().accelerationStructure)
397		TCU_THROW(TestError, "Requires VkPhysicalDeviceAccelerationStructureFeaturesKHR.accelerationStructure");
398}
399
400void RayQueryProceduralGeometryTestCase::initPrograms(SourceCollections& programCollection) const
401{
402	const vk::ShaderBuildOptions glslBuildOptions(programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, 0u, true);
403
404	std::string compSource =
405		"#version 460 core\n"
406		"#extension GL_EXT_ray_query : require\n"
407
408		"layout(set = 0, binding = 0) uniform accelerationStructureEXT tlas;\n"
409		"layout(set = 0, binding = 1, std430) writeonly buffer Result {\n"
410		"    int value[];\n"
411		"} result;\n"
412
413		"void main()\n"
414		"{\n"
415		"  float tmin          = 0.0;\n"
416		"  float tmax          = 50.0;\n"
417		"  vec3  rayOrigin     = vec3(float(gl_GlobalInvocationID.x) + 0.5f, float(gl_GlobalInvocationID.y) + 0.5f, 2.0);\n"
418		"  vec3  rayDir        = vec3(0.0,0.0,-1.0);\n"
419		"  uint  resultIndex   = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * gl_NumWorkGroups.x;\n"
420		"  int   payload       = 30;\n"
421
422		// elipsoid center and radii
423		"  vec3 elipsoidOrigin = vec3(32.0, 32.0, -30.0);\n"
424		"  vec3 elipsoidRadii  = vec3(30.0, 15.0, 5.0);\n"
425
426		"  rayQueryEXT rq;\n"
427		"  rayQueryInitializeEXT(rq, tlas, gl_RayFlagsCullBackFacingTrianglesEXT, 0xFF, rayOrigin, tmin, rayDir, tmax);\n"
428
429		"  while (rayQueryProceedEXT(rq))\n"
430		"  {\n"
431		"    uint intersectionType = rayQueryGetIntersectionTypeEXT(rq, false);\n"
432		"    if (intersectionType == gl_RayQueryCandidateIntersectionAABBEXT)\n"
433		"    {\n"
434		// simplify to ray sphere intersection
435		"      vec3  eliDir = rayOrigin - elipsoidOrigin;\n"
436		"      vec3  eliS   = eliDir / elipsoidRadii;\n"
437		"      vec3  rayS   = rayDir / elipsoidRadii;\n"
438
439		"      float a = dot(rayS, rayS);\n"
440		"      float b = dot(eliS, rayS);\n"
441		"      float c = dot(eliS, eliS);\n"
442		"      float h = b * b - a * (c - 1.0);\n"
443		"      if (h >= 0.0)\n"
444		"        rayQueryGenerateIntersectionEXT(rq, (-b - sqrt(h)) / a);\n"
445		"    }\n"
446		"    else if (intersectionType == gl_RayQueryCandidateIntersectionTriangleEXT)\n"
447		"    {\n"
448		"      payload = 250;\n"
449		"      rayQueryConfirmIntersectionEXT(rq);\n"
450		"    }\n"
451		"  }\n"
452		"  if (rayQueryGetIntersectionTypeEXT(rq, true) != gl_RayQueryCommittedIntersectionNoneEXT)\n"
453		"  {\n"
454		"    int instanceId = rayQueryGetIntersectionInstanceIdEXT(rq, true);\n"
455		"    if (instanceId > -1)\n"
456		"    {\n"
457		"      float hitT      = rayQueryGetIntersectionTEXT(rq, true);\n"
458		"      vec3  lightDir  = normalize(vec3(0.0, 0.0, 1.0));\n"
459		"      vec3  hitPos    = rayOrigin + hitT * rayDir;\n"
460		"      vec3  hitNormal = normalize((hitPos - elipsoidOrigin) / elipsoidRadii);\n"
461		"      payload = 50 + int(200.0 * clamp(dot(hitNormal, lightDir), 0.0, 1.0));\n"
462		"    }\n"
463		"  }\n"
464
465		// to be able to display result in cherry this is interpreated as r8g8b8a8 during verification
466		// we are using only red but we need to add alpha (note: r and a may be swapped depending on endianness)
467		"  result.value[resultIndex] = payload + 0xFF000000;\n"
468		"};\n";
469	programCollection.glslSources.add("comp") << glu::ComputeSource(compSource) << glslBuildOptions;
470}
471
472TestInstance* RayQueryProceduralGeometryTestCase::createInstance(Context& context) const
473{
474	if (m_testType == TestType::TRIANGLE_IN_BETWEEN)
475		return new TriangleInBeteenInstance(context);
476
477	// TestType::OBJECT_BEHIND_BOUNDING_BOX
478	return new ObjectBehindBoundingBoxInstance(context);
479}
480
481}	// anonymous
482
483tcu::TestCaseGroup*	createProceduralGeometryTests(tcu::TestContext& testCtx)
484{
485	// Test procedural geometry with complex bouding box sets
486	de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "procedural_geometry"));
487
488	group->addChild(new RayQueryProceduralGeometryTestCase(testCtx, "object_behind_bounding_boxes",	TestType::OBJECT_BEHIND_BOUNDING_BOX));
489	group->addChild(new RayQueryProceduralGeometryTestCase(testCtx, "triangle_in_between",			TestType::TRIANGLE_IN_BETWEEN));
490
491	return group.release();
492}
493
494}	// RayQuery
495
496}	// vkt
497