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 
43 namespace vkt
44 {
45 namespace RayQuery
46 {
47 namespace
48 {
49 using namespace vk;
50 using namespace vkt;
51 
52 enum class TestType
53 {
54 	OBJECT_BEHIND_BOUNDING_BOX = 0,
55 	TRIANGLE_IN_BETWEEN
56 };
57 
58 class RayQueryProceduralGeometryTestBase : public TestInstance
59 {
60 public:
61 
62 	RayQueryProceduralGeometryTestBase	(Context& context);
63 	~RayQueryProceduralGeometryTestBase	(void) = default;
64 
65 	tcu::TestStatus iterate				(void) override;
66 
67 protected:
68 
69 	virtual void setupAccelerationStructures() = 0;
70 
71 private:
72 
73 	VkWriteDescriptorSetAccelerationStructureKHR	makeASWriteDescriptorSet	(const VkAccelerationStructureKHR* pAccelerationStructure);
74 	void											clearBuffer					(de::SharedPtr<BufferWithMemory> buffer, VkDeviceSize bufferSize);
75 
76 protected:
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 
RayQueryProceduralGeometryTestBase(Context& context)86 RayQueryProceduralGeometryTestBase::RayQueryProceduralGeometryTestBase(Context& context)
87 	: vkt::TestInstance	(context)
88 	, m_referenceTLAS	(makeTopLevelAccelerationStructure().release())
89 	, m_resultTLAS		(makeTopLevelAccelerationStructure().release())
90 {
91 }
92 
iterate(void)93 tcu::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 
makeASWriteDescriptorSet(const VkAccelerationStructureKHR* pAccelerationStructure)213 VkWriteDescriptorSetAccelerationStructureKHR 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 
clearBuffer(de::SharedPtr<BufferWithMemory> buffer, VkDeviceSize bufferSize)224 void 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 
235 class ObjectBehindBoundingBoxInstance : public RayQueryProceduralGeometryTestBase
236 {
237 public:
238 
239 	ObjectBehindBoundingBoxInstance(Context& context);
240 	void setupAccelerationStructures() override;
241 };
242 
ObjectBehindBoundingBoxInstance(Context& context)243 ObjectBehindBoundingBoxInstance::ObjectBehindBoundingBoxInstance(Context& context)
244 	: RayQueryProceduralGeometryTestBase(context)
245 {
246 }
247 
setupAccelerationStructures()248 void 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 
295 class TriangleInBeteenInstance : public RayQueryProceduralGeometryTestBase
296 {
297 public:
298 
299 	TriangleInBeteenInstance(Context& context);
300 	void setupAccelerationStructures() override;
301 };
302 
TriangleInBeteenInstance(Context& context)303 TriangleInBeteenInstance::TriangleInBeteenInstance(Context& context)
304 	: RayQueryProceduralGeometryTestBase(context)
305 {
306 }
307 
setupAccelerationStructures()308 void 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 
368 class RayQueryProceduralGeometryTestCase : public TestCase
369 {
370 public:
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 
378 protected:
379 	TestType m_testType;
380 };
381 
RayQueryProceduralGeometryTestCase(tcu::TestContext& context, const char* name, TestType testType)382 RayQueryProceduralGeometryTestCase::RayQueryProceduralGeometryTestCase(tcu::TestContext& context, const char* name, TestType testType)
383 	: TestCase		(context, name)
384 	, m_testType	(testType)
385 {
386 }
387 
checkSupport(Context& context) const388 void 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 
initPrograms(SourceCollections& programCollection) const400 void 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 
createInstance(Context& context) const472 TestInstance* 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 
createProceduralGeometryTests(tcu::TestContext& testCtx)483 tcu::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