• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Mesh Shader Property Tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktMeshShaderPropertyTests.hpp"
26 #include "vktTestCase.hpp"
27 
28 #include "vkBufferWithMemory.hpp"
29 #include "vkObjUtil.hpp"
30 #include "vkBuilderUtil.hpp"
31 #include "vkTypeUtil.hpp"
32 #include "vkCmdUtil.hpp"
33 #include "vkBarrierUtil.hpp"
34 
35 #include "tcuStringTemplate.hpp"
36 
37 #include <vector>
38 #include <string>
39 #include <map>
40 #include <sstream>
41 
42 namespace vkt
43 {
44 namespace MeshShader
45 {
46 
47 namespace
48 {
49 
50 using GroupPtr			= de::MovePtr<tcu::TestCaseGroup>;
51 using ReplacementsMap	= std::map<std::string, std::string>;
52 
53 using namespace vk;
54 
getTaskShaderTemplate()55 tcu::StringTemplate getTaskShaderTemplate ()
56 {
57 	return tcu::StringTemplate(
58 		"#version 460\n"
59 		"#extension GL_NV_mesh_shader : enable\n"
60 		"\n"
61 		"layout (local_size_x=${TASK_LOCAL_SIZE_X:default=1}) in;\n"
62 		"\n"
63 		"${TASK_GLOBAL_DECL:opt}"
64 		"\n"
65 		"${TASK_MESH_INTERFACE_OUT:opt}"
66 		"\n"
67 		"void main ()\n"
68 		"{\n"
69 		"    gl_TaskCountNV = ${TASK_TASK_COUNT:default=0};\n"
70 		"${TASK_BODY:opt}"
71 		"}\n");
72 }
73 
getMeshShaderTemplate()74 tcu::StringTemplate getMeshShaderTemplate ()
75 {
76 	return tcu::StringTemplate(
77 		"#version 460\n"
78 		"#extension GL_NV_mesh_shader : enable\n"
79 		"\n"
80 		"layout (local_size_x=${MESH_LOCAL_SIZE_X:default=1}) in;\n"
81 		"layout (triangles) out;\n"
82 		"layout (max_vertices=3, max_primitives=1) out;\n"
83 		"\n"
84 		"${MESH_GLOBAL_DECL:opt}"
85 		"\n"
86 		"${TASK_MESH_INTERFACE_IN:opt}"
87 		"\n"
88 		"void main ()\n"
89 		"{\n"
90 		"    gl_PrimitiveCountNV = 0u;\n"
91 		"${MESH_BODY:opt}"
92 		"}\n");
93 }
94 
getCommonStorageBufferDecl()95 std::string getCommonStorageBufferDecl ()
96 {
97 	return "layout (set=0, binding=0) buffer OutputBlock { uint values[]; } ov;\n";
98 }
99 
genericCheckSupport(Context & context,bool taskShaderNeeded)100 void genericCheckSupport (Context& context, bool taskShaderNeeded)
101 {
102 	context.requireDeviceFunctionality("VK_NV_mesh_shader");
103 
104 	const auto& meshFeatures = context.getMeshShaderFeatures();
105 
106 	if (!meshFeatures.meshShader)
107 		TCU_THROW(NotSupportedError, "Mesh shaders not supported");
108 
109 	if (taskShaderNeeded && !meshFeatures.taskShader)
110 		TCU_THROW(NotSupportedError, "Task shaders not supported");
111 
112 	const auto& features = context.getDeviceFeatures();
113 	if (!features.vertexPipelineStoresAndAtomics)
114 		TCU_THROW(NotSupportedError, "Vertex pipeline stores and atomics not supported");
115 }
116 
117 struct InstanceParams
118 {
119 	uint32_t	bufferElements;
120 	uint32_t	taskCount;
121 };
122 
123 class MeshShaderPropertyInstance : public vkt::TestInstance
124 {
125 public:
MeshShaderPropertyInstance(Context & context,const InstanceParams & params)126 					MeshShaderPropertyInstance	(Context& context, const InstanceParams& params)
127 						: vkt::TestInstance	(context)
128 						, m_params			(params)
129 						{}
~MeshShaderPropertyInstance(void)130 	virtual			~MeshShaderPropertyInstance	(void) {}
131 
132 	tcu::TestStatus	iterate						() override;
133 
134 protected:
135 	InstanceParams	m_params;
136 };
137 
iterate()138 tcu::TestStatus MeshShaderPropertyInstance::iterate ()
139 {
140 	const auto&		vkd			= m_context.getDeviceInterface();
141 	const auto		device		= m_context.getDevice();
142 	auto&			alloc		= m_context.getDefaultAllocator();
143 	const auto		queueIndex	= m_context.getUniversalQueueFamilyIndex();
144 	const auto		queue		= m_context.getUniversalQueue();
145 	const auto&		binaries	= m_context.getBinaryCollection();
146 	const auto		extent		= makeExtent3D(1u, 1u, 1u);
147 	const auto		bindPoint	= VK_PIPELINE_BIND_POINT_GRAPHICS;
148 	const auto		useTask		= binaries.contains("task");
149 
150 	const auto		storageBufferSize	= static_cast<VkDeviceSize>(m_params.bufferElements) * static_cast<VkDeviceSize>(sizeof(uint32_t));
151 	const auto		storageBufferUsage	= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
152 	const auto		storageBufferType	= VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
153 	const auto		storageBufferStages	= (VK_SHADER_STAGE_MESH_BIT_NV | (useTask ? VK_SHADER_STAGE_TASK_BIT_NV : 0));
154 
155 	// Create storage buffer with the required space.
156 	const auto			storageBufferInfo		= makeBufferCreateInfo(storageBufferSize, storageBufferUsage);
157 	BufferWithMemory	storageBuffer			(vkd, device, alloc, storageBufferInfo, MemoryRequirement::HostVisible);
158 	auto&				storageBufferAlloc		= storageBuffer.getAllocation();
159 	void*				storageBufferDataPtr	= storageBufferAlloc.getHostPtr();
160 	const auto			storageBufferDescInfo	= makeDescriptorBufferInfo(storageBuffer.get(), 0ull, storageBufferSize);
161 
162 	deMemset(storageBufferDataPtr, 0xFF, static_cast<size_t>(storageBufferSize));
163 	flushAlloc(vkd, device, storageBufferAlloc);
164 
165 	// Descriptor pool.
166 	DescriptorPoolBuilder poolBuilder;
167 	poolBuilder.addType(storageBufferType);
168 	const auto descriptorPool = poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
169 
170 	// Descriptor set layout and pipeline layout.
171 	DescriptorSetLayoutBuilder layoutBuilder;
172 	layoutBuilder.addSingleBinding(storageBufferType, storageBufferStages);
173 	const auto setLayout		= layoutBuilder.build(vkd, device);
174 	const auto pipelineLayout	= makePipelineLayout(vkd, device, setLayout.get());
175 
176 	// Allocate and prepare descriptor set.
177 	const auto descriptorSet = makeDescriptorSet(vkd, device, descriptorPool.get(), setLayout.get());
178 
179 	DescriptorSetUpdateBuilder setUpdateBuilder;
180 	setUpdateBuilder.writeSingle(descriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u), storageBufferType, &storageBufferDescInfo);
181 	setUpdateBuilder.update(vkd, device);
182 
183 	// Create empty render pass and framebuffer.
184 	const auto renderPass	= makeRenderPass(vkd, device);
185 	const auto framebuffer	= makeFramebuffer(vkd, device, renderPass.get(), 0u, nullptr, extent.width, extent.height);
186 
187 	// Shader modules and pipeline.
188 	Move<VkShaderModule>		taskModule;
189 	Move<VkShaderModule>		meshModule;
190 	const Move<VkShaderModule>	fragModule;	// No fragment shader.
191 
192 	if (useTask)
193 		taskModule = createShaderModule(vkd, device, binaries.get("task"));
194 	meshModule = createShaderModule(vkd, device, binaries.get("mesh"));
195 
196 	const std::vector<VkViewport>	viewports	(1u, makeViewport(extent));
197 	const std::vector<VkRect2D>		scissors	(1u, makeRect2D(extent));
198 
199 	const auto pipeline = makeGraphicsPipeline(vkd, device, pipelineLayout.get(),
200 		taskModule.get(), meshModule.get(), fragModule.get(),
201 		renderPass.get(), viewports, scissors);
202 
203 	// Command pool and buffer.
204 	const auto cmdPool		= makeCommandPool(vkd, device, queueIndex);
205 	const auto cmdBufferPtr	= allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
206 	const auto cmdBuffer	= cmdBufferPtr.get();
207 
208 	// Run the pipeline.
209 	beginCommandBuffer(vkd, cmdBuffer);
210 
211 	beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0));
212 	vkd.cmdBindDescriptorSets(cmdBuffer, bindPoint, pipelineLayout.get(), 0u, 1u, &descriptorSet.get(), 0u, nullptr);
213 	vkd.cmdBindPipeline(cmdBuffer, bindPoint, pipeline.get());
214 	vkd.cmdDrawMeshTasksNV(cmdBuffer, m_params.taskCount, 0u);
215 	endRenderPass(vkd, cmdBuffer);
216 
217 	const auto shaderToHostBarrier = makeMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
218 	vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &shaderToHostBarrier, 0u, nullptr, 0u, nullptr);
219 
220 	endCommandBuffer(vkd, cmdBuffer);
221 	submitCommandsAndWait(vkd, device, queue, cmdBuffer);
222 
223 	// Verify the storage buffer has the expected results.
224 	invalidateAlloc(vkd, device, storageBufferAlloc);
225 
226 	std::vector<uint32_t> bufferData (m_params.bufferElements);
227 	deMemcpy(bufferData.data(), storageBufferDataPtr, de::dataSize(bufferData));
228 
229 	for (size_t idx = 0u; idx < bufferData.size(); ++idx)
230 	{
231 		const auto	expected	= static_cast<uint32_t>(idx);
232 		const auto&	bufferValue	= bufferData[idx];
233 
234 		if (bufferValue != expected)
235 			TCU_FAIL("Unexpected value found in buffer position " + de::toString(idx) + ": " + de::toString(bufferValue));
236 	}
237 
238 	return tcu::TestStatus::pass("Pass");
239 }
240 
241 class MaxDrawMeshTasksCountCase : public vkt::TestCase
242 {
243 public:
244 	enum class TestType { TASK=0, MESH };
245 
MaxDrawMeshTasksCountCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,TestType testType)246 					MaxDrawMeshTasksCountCase	(tcu::TestContext& testCtx, const std::string& name, const std::string& description, TestType testType)
247 						: vkt::TestCase	(testCtx, name, description)
248 						, m_testType	(testType)
249 						{}
~MaxDrawMeshTasksCountCase(void)250 	virtual			~MaxDrawMeshTasksCountCase	(void) {}
251 
252 	void			initPrograms	(vk::SourceCollections& programCollection) const override;
253 	TestInstance*	createInstance	(Context& context) const override;
254 	void			checkSupport	(Context& context) const override;
255 
256 	static constexpr uint32_t minLimit = 65535u;
257 
258 protected:
259 	TestType		m_testType;
260 };
261 
checkSupport(Context & context) const262 void MaxDrawMeshTasksCountCase::checkSupport (Context& context) const
263 {
264 	genericCheckSupport(context, (m_testType == TestType::TASK));
265 
266 	const auto& properties = context.getMeshShaderProperties();
267 	if (properties.maxDrawMeshTasksCount < minLimit)
268 		TCU_FAIL("maxDrawMeshTasksCount property below the minimum limit");
269 }
270 
createInstance(Context & context) const271 TestInstance* MaxDrawMeshTasksCountCase::createInstance (Context& context) const
272 {
273 	const InstanceParams params =
274 	{
275 		minLimit,						//	uint32_t	bufferElements;
276 		minLimit,						//	uint32_t	taskCount;
277 	};
278 	return new MeshShaderPropertyInstance(context, params);
279 }
280 
initPrograms(vk::SourceCollections & programCollection) const281 void MaxDrawMeshTasksCountCase::initPrograms (vk::SourceCollections& programCollection) const
282 {
283 	ReplacementsMap meshReplacements;
284 	ReplacementsMap taskReplacements;
285 
286 	const auto meshTemplate = getMeshShaderTemplate();
287 
288 	const std::string desc = getCommonStorageBufferDecl();
289 	const std::string body = "    ov.values[gl_WorkGroupID.x] = gl_WorkGroupID.x;\n";
290 
291 	if (m_testType == TestType::TASK)
292 	{
293 		const auto taskTemplate = getTaskShaderTemplate();
294 		taskReplacements["TASK_GLOBAL_DECL"]	= desc;
295 		taskReplacements["TASK_BODY"]			= body;
296 
297 		programCollection.glslSources.add("task") << glu::TaskSource(taskTemplate.specialize(taskReplacements));
298 	}
299 	else
300 	{
301 		meshReplacements["MESH_GLOBAL_DECL"]	= desc;
302 		meshReplacements["MESH_BODY"]			= body;
303 	}
304 
305 	programCollection.glslSources.add("mesh") << glu::MeshSource(meshTemplate.specialize(meshReplacements));
306 }
307 
308 class MaxTaskWorkGroupInvocationsCase : public vkt::TestCase
309 {
310 public:
MaxTaskWorkGroupInvocationsCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)311 					MaxTaskWorkGroupInvocationsCase	(tcu::TestContext& testCtx, const std::string& name, const std::string& description)
312 						: vkt::TestCase	(testCtx, name, description) {}
~MaxTaskWorkGroupInvocationsCase(void)313 	virtual			~MaxTaskWorkGroupInvocationsCase (void) {}
314 
315 	void			initPrograms	(vk::SourceCollections& programCollection) const override;
316 	TestInstance*	createInstance	(Context& context) const override;
317 	void			checkSupport	(Context& context) const override;
318 
319 	static constexpr uint32_t minLimit = 32u;
320 };
321 
checkSupport(Context & context) const322 void MaxTaskWorkGroupInvocationsCase::checkSupport (Context& context) const
323 {
324 	genericCheckSupport(context, true/*taskShaderNeeded*/);
325 
326 	const auto& properties = context.getMeshShaderProperties();
327 	if (properties.maxTaskWorkGroupInvocations < minLimit)
328 		TCU_FAIL("maxTaskWorkGroupInvocations property below the minimum limit");
329 }
330 
createInstance(Context & context) const331 TestInstance* MaxTaskWorkGroupInvocationsCase::createInstance (Context& context) const
332 {
333 	const InstanceParams params =
334 	{
335 		minLimit,	//	uint32_t	bufferElements;
336 		1u,			//	uint32_t	taskCount;
337 	};
338 	return new MeshShaderPropertyInstance(context, params);
339 }
340 
initPrograms(vk::SourceCollections & programCollection) const341 void MaxTaskWorkGroupInvocationsCase::initPrograms (vk::SourceCollections& programCollection) const
342 {
343 	const ReplacementsMap	meshReplacements;
344 	const auto				meshTemplate		= getMeshShaderTemplate();
345 
346 	programCollection.glslSources.add("mesh") << glu::MeshSource(meshTemplate.specialize(meshReplacements));
347 
348 	ReplacementsMap	taskReplacements;
349 	const auto		taskTemplate		= getTaskShaderTemplate();
350 
351 	taskReplacements["TASK_GLOBAL_DECL"]	= getCommonStorageBufferDecl();
352 	taskReplacements["TASK_BODY"]			= "    ov.values[gl_LocalInvocationID.x] = gl_LocalInvocationID.x;\n";
353 	taskReplacements["TASK_LOCAL_SIZE_X"]	= de::toString(uint32_t{minLimit});
354 
355 	programCollection.glslSources.add("task") << glu::TaskSource(taskTemplate.specialize(taskReplacements));
356 }
357 
358 // In the case of the NV extension, this is very similar to the test above. Added for completion.
359 class MaxTaskWorkGroupSizeCase : public MaxTaskWorkGroupInvocationsCase
360 {
361 public:
MaxTaskWorkGroupSizeCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)362 	MaxTaskWorkGroupSizeCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description)
363 		: MaxTaskWorkGroupInvocationsCase (testCtx, name, description) {}
364 
365 	void checkSupport (Context& context) const override;
366 
367 	static constexpr uint32_t minSizeX = 32u;
368 	static constexpr uint32_t minSizeY = 1u;
369 	static constexpr uint32_t minSizeZ = 1u;
370 };
371 
checkSupport(Context & context) const372 void MaxTaskWorkGroupSizeCase::checkSupport (Context& context) const
373 {
374 	genericCheckSupport(context, true/*taskShaderNeeded*/);
375 
376 	const auto& properties = context.getMeshShaderProperties();
377 	if (properties.maxTaskWorkGroupSize[0] < minSizeX ||
378 		properties.maxTaskWorkGroupSize[1] < minSizeY ||
379 		properties.maxTaskWorkGroupSize[2] < minSizeZ)
380 	{
381 		TCU_FAIL("maxTaskWorkGroupSize property below the minimum limit");
382 	}
383 }
384 
385 class MaxTaskOutputCountCase : public vkt::TestCase
386 {
387 public:
MaxTaskOutputCountCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)388 					MaxTaskOutputCountCase	(tcu::TestContext& testCtx, const std::string& name, const std::string& description)
389 						: vkt::TestCase	(testCtx, name, description) {}
~MaxTaskOutputCountCase(void)390 	virtual			~MaxTaskOutputCountCase (void) {}
391 
392 	void			initPrograms	(vk::SourceCollections& programCollection) const override;
393 	TestInstance*	createInstance	(Context& context) const override;
394 	void			checkSupport	(Context& context) const override;
395 
396 	static constexpr uint32_t minLimit = 65535u;
397 };
398 
checkSupport(Context & context) const399 void MaxTaskOutputCountCase::checkSupport (Context& context) const
400 {
401 	genericCheckSupport(context, true/*taskShaderNeeded*/);
402 
403 	const auto& properties = context.getMeshShaderProperties();
404 	if (properties.maxTaskOutputCount < minLimit)
405 		TCU_FAIL("maxTaskOutputCount property below the minimum limit");
406 }
407 
createInstance(Context & context) const408 TestInstance* MaxTaskOutputCountCase::createInstance (Context& context) const
409 {
410 	const InstanceParams params =
411 	{
412 		minLimit,	//	uint32_t	bufferElements;
413 		1u,			//	uint32_t	taskCount;
414 	};
415 	return new MeshShaderPropertyInstance(context, params);
416 }
417 
initPrograms(vk::SourceCollections & programCollection) const418 void MaxTaskOutputCountCase::initPrograms (vk::SourceCollections& programCollection) const
419 {
420 	ReplacementsMap	meshReplacements;
421 	ReplacementsMap	taskReplacements;
422 	const auto		meshTemplate		= getMeshShaderTemplate();
423 	const auto		taskTemplate		= getTaskShaderTemplate();
424 
425 	taskReplacements["TASK_TASK_COUNT"]		= de::toString(uint32_t{minLimit});
426 	meshReplacements["MESH_GLOBAL_DECL"]	= getCommonStorageBufferDecl();
427 	meshReplacements["MESH_BODY"]			= "    ov.values[gl_WorkGroupID.x] = gl_WorkGroupID.x;\n";
428 
429 	programCollection.glslSources.add("task") << glu::TaskSource(taskTemplate.specialize(taskReplacements));
430 	programCollection.glslSources.add("mesh") << glu::MeshSource(meshTemplate.specialize(meshReplacements));
431 }
432 
433 class MaxMeshWorkGroupInvocationsCase : public vkt::TestCase
434 {
435 public:
MaxMeshWorkGroupInvocationsCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)436 					MaxMeshWorkGroupInvocationsCase	(tcu::TestContext& testCtx, const std::string& name, const std::string& description)
437 						: vkt::TestCase	(testCtx, name, description) {}
~MaxMeshWorkGroupInvocationsCase(void)438 	virtual			~MaxMeshWorkGroupInvocationsCase (void) {}
439 
440 	void			initPrograms	(vk::SourceCollections& programCollection) const override;
441 	TestInstance*	createInstance	(Context& context) const override;
442 	void			checkSupport	(Context& context) const override;
443 
444 	static constexpr uint32_t minLimit = 32u;
445 };
446 
checkSupport(Context & context) const447 void MaxMeshWorkGroupInvocationsCase::checkSupport (Context& context) const
448 {
449 	genericCheckSupport(context, false/*taskShaderNeeded*/);
450 
451 	const auto& properties = context.getMeshShaderProperties();
452 	if (properties.maxMeshWorkGroupInvocations < minLimit)
453 		TCU_FAIL("maxMeshWorkGroupInvocations property below the minimum limit");
454 }
455 
createInstance(Context & context) const456 TestInstance* MaxMeshWorkGroupInvocationsCase::createInstance (Context& context) const
457 {
458 	const InstanceParams params =
459 	{
460 		minLimit,	//	uint32_t	bufferElements;
461 		1u,			//	uint32_t	taskCount;
462 	};
463 	return new MeshShaderPropertyInstance(context, params);
464 }
465 
initPrograms(vk::SourceCollections & programCollection) const466 void MaxMeshWorkGroupInvocationsCase::initPrograms (vk::SourceCollections& programCollection) const
467 {
468 	ReplacementsMap	meshReplacements;
469 	const auto		meshTemplate		= getMeshShaderTemplate();
470 
471 	meshReplacements["MESH_LOCAL_SIZE_X"]	= de::toString(uint32_t{minLimit});
472 	meshReplacements["MESH_GLOBAL_DECL"]	= getCommonStorageBufferDecl();
473 	meshReplacements["MESH_BODY"]			= "    ov.values[gl_LocalInvocationID.x] = gl_LocalInvocationID.x;\n";
474 
475 	programCollection.glslSources.add("mesh") << glu::MeshSource(meshTemplate.specialize(meshReplacements));
476 }
477 
478 // In the case of the NV extension, this is very similar to the test above. Added for completion.
479 class MaxMeshWorkGroupSizeCase : public MaxMeshWorkGroupInvocationsCase
480 {
481 public:
MaxMeshWorkGroupSizeCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)482 	MaxMeshWorkGroupSizeCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description)
483 		: MaxMeshWorkGroupInvocationsCase (testCtx, name, description) {}
484 
485 	void checkSupport (Context& context) const override;
486 
487 	static constexpr uint32_t minSizeX = 32u;
488 	static constexpr uint32_t minSizeY = 1u;
489 	static constexpr uint32_t minSizeZ = 1u;
490 };
491 
checkSupport(Context & context) const492 void MaxMeshWorkGroupSizeCase::checkSupport (Context& context) const
493 {
494 	genericCheckSupport(context, false/*taskShaderNeeded*/);
495 
496 	const auto& properties = context.getMeshShaderProperties();
497 	if (properties.maxMeshWorkGroupSize[0] < minSizeX ||
498 		properties.maxMeshWorkGroupSize[1] < minSizeY ||
499 		properties.maxMeshWorkGroupSize[2] < minSizeZ)
500 	{
501 		TCU_FAIL("maxMeshWorkGroupSize property below the minimum limit");
502 	}
503 }
504 
getSharedArrayDecl(uint32_t numElements)505 std::string getSharedArrayDecl (uint32_t numElements)
506 {
507 	std::ostringstream decl;
508 	decl
509 		<< "const uint arrayElements = " << de::toString(numElements) << ";\n"
510 		<< "shared uint sharedArray[arrayElements];\n"
511 		;
512 	return decl.str();
513 }
514 
getSharedMemoryBody(uint32_t localSize)515 std::string getSharedMemoryBody (uint32_t localSize)
516 {
517 	std::ostringstream body;
518 	body
519 		<< "\n"
520 		<< "    if (gl_LocalInvocationID.x == 0u)\n"
521 		<< "    {\n"
522 		<< "        for (uint i = 0; i < arrayElements; ++i)\n"
523 		<< "            sharedArray[i] = 0u;\n"
524 		<< "    }\n"
525 		<< "\n"
526 		<< "    barrier();\n"
527 		<< "\n"
528 		<< "    for (uint i = 0; i < arrayElements; ++i)\n"
529 		<< "        atomicAdd(sharedArray[i], 1u);\n"
530 		<< "\n"
531 		<< "    barrier();\n"
532 		<< "\n"
533 		<< "    uint allGood = 1u;\n"
534 		<< "    for (uint i = 0; i < arrayElements; ++i)\n"
535 		<< "    {\n"
536 		<< "        if (sharedArray[i] != " << localSize << ")\n"
537 		<< "        {\n"
538 		<< "            allGood = 0u;\n"
539 		<< "            break;\n"
540 		<< "        }\n"
541 		<< "    }\n"
542 		<< "\n"
543 		<< "    ov.values[gl_LocalInvocationID.x] = ((allGood == 1u) ? gl_LocalInvocationID.x : gl_WorkGroupSize.x);\n"
544 		;
545 
546 	return body.str();
547 }
548 
549 class MaxTaskTotalMemorySizeCase : public vkt::TestCase
550 {
551 public:
MaxTaskTotalMemorySizeCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)552 					MaxTaskTotalMemorySizeCase	(tcu::TestContext& testCtx, const std::string& name, const std::string& description)
553 						: vkt::TestCase	(testCtx, name, description) {}
~MaxTaskTotalMemorySizeCase(void)554 	virtual			~MaxTaskTotalMemorySizeCase (void) {}
555 
556 	void			initPrograms	(vk::SourceCollections& programCollection) const override;
557 	TestInstance*	createInstance	(Context& context) const override;
558 	void			checkSupport	(Context& context) const override;
559 
560 	static constexpr uint32_t localSize	= 32u;
561 	static constexpr uint32_t minLimit	= 16384u;
562 };
563 
createInstance(Context & context) const564 TestInstance* MaxTaskTotalMemorySizeCase::createInstance (Context& context) const
565 {
566 	const InstanceParams params =
567 	{
568 		localSize,	//	uint32_t	bufferElements;
569 		1u,			//	uint32_t	taskCount;
570 	};
571 	return new MeshShaderPropertyInstance(context, params);
572 }
573 
checkSupport(Context & context) const574 void MaxTaskTotalMemorySizeCase::checkSupport (Context& context) const
575 {
576 	genericCheckSupport(context, true/*taskShaderNeeded*/);
577 
578 	const auto& properties = context.getMeshShaderProperties();
579 	if (properties.maxTaskTotalMemorySize < minLimit)
580 		TCU_FAIL("maxTaskTotalMemorySize property below the minimum limit");
581 }
582 
initPrograms(vk::SourceCollections & programCollection) const583 void MaxTaskTotalMemorySizeCase::initPrograms (vk::SourceCollections& programCollection) const
584 {
585 	const ReplacementsMap	meshReplacements;
586 	const auto				meshTemplate		= getMeshShaderTemplate();
587 
588 	programCollection.glslSources.add("mesh") << glu::MeshSource(meshTemplate.specialize(meshReplacements));
589 
590 	const auto taskTemplate		= getTaskShaderTemplate();
591 	const auto arrayElements	= minLimit / static_cast<uint32_t>(sizeof(uint32_t));
592 
593 	const auto globalDecls		= getCommonStorageBufferDecl() + getSharedArrayDecl(arrayElements);
594 	const auto body				= getSharedMemoryBody(localSize);
595 
596 	ReplacementsMap taskReplacements;
597 	taskReplacements["TASK_LOCAL_SIZE_X"]	= de::toString(uint32_t{localSize});
598 	taskReplacements["TASK_GLOBAL_DECL"]	= globalDecls;
599 	taskReplacements["TASK_BODY"]			= body;
600 
601 	programCollection.glslSources.add("task") << glu::TaskSource(taskTemplate.specialize(taskReplacements));
602 }
603 
604 // Very similar to the previous one in NV.
605 class MaxMeshTotalMemorySizeCase : public vkt::TestCase
606 {
607 public:
MaxMeshTotalMemorySizeCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)608 					MaxMeshTotalMemorySizeCase	(tcu::TestContext& testCtx, const std::string& name, const std::string& description)
609 						: vkt::TestCase	(testCtx, name, description) {}
~MaxMeshTotalMemorySizeCase(void)610 	virtual			~MaxMeshTotalMemorySizeCase (void) {}
611 
612 	void			initPrograms	(vk::SourceCollections& programCollection) const override;
613 	TestInstance*	createInstance	(Context& context) const override;
614 	void			checkSupport	(Context& context) const override;
615 
616 	static constexpr uint32_t localSize	= 32u;
617 	static constexpr uint32_t minLimit	= 16384u;
618 };
619 
createInstance(Context & context) const620 TestInstance* MaxMeshTotalMemorySizeCase::createInstance (Context& context) const
621 {
622 	const InstanceParams params =
623 	{
624 		localSize,	//	uint32_t	bufferElements;
625 		1u,			//	uint32_t	taskCount;
626 	};
627 	return new MeshShaderPropertyInstance(context, params);
628 }
629 
checkSupport(Context & context) const630 void MaxMeshTotalMemorySizeCase::checkSupport (Context& context) const
631 {
632 	genericCheckSupport(context, false/*taskShaderNeeded*/);
633 
634 	const auto& properties = context.getMeshShaderProperties();
635 	if (properties.maxMeshTotalMemorySize < minLimit)
636 		TCU_FAIL("maxMeshTotalMemorySize property below the minimum limit");
637 }
638 
initPrograms(vk::SourceCollections & programCollection) const639 void MaxMeshTotalMemorySizeCase::initPrograms (vk::SourceCollections& programCollection) const
640 {
641 	const auto meshTemplate		= getMeshShaderTemplate();
642 	const auto arrayElements	= minLimit / static_cast<uint32_t>(sizeof(uint32_t));
643 
644 	const auto globalDecls		= getCommonStorageBufferDecl() + getSharedArrayDecl(arrayElements);
645 	const auto body				= getSharedMemoryBody(localSize);
646 
647 	ReplacementsMap meshReplacements;
648 	meshReplacements["MESH_LOCAL_SIZE_X"]	= de::toString(uint32_t{localSize});
649 	meshReplacements["MESH_GLOBAL_DECL"]	= globalDecls;
650 	meshReplacements["MESH_BODY"]			= body;
651 
652 	programCollection.glslSources.add("mesh") << glu::MeshSource(meshTemplate.specialize(meshReplacements));
653 }
654 
655 }
656 
createMeshShaderPropertyTests(tcu::TestContext & testCtx)657 tcu::TestCaseGroup* createMeshShaderPropertyTests (tcu::TestContext& testCtx)
658 {
659 	GroupPtr mainGroup (new tcu::TestCaseGroup(testCtx, "property", "Mesh Shader Property Tests"));
660 
661 	mainGroup->addChild(new MaxDrawMeshTasksCountCase		(testCtx, "max_draw_mesh_tasks_count_with_task",	"", MaxDrawMeshTasksCountCase::TestType::TASK));
662 	mainGroup->addChild(new MaxDrawMeshTasksCountCase		(testCtx, "max_draw_mesh_tasks_count_with_mesh",	"", MaxDrawMeshTasksCountCase::TestType::MESH));
663 	mainGroup->addChild(new MaxTaskWorkGroupInvocationsCase	(testCtx, "max_task_work_group_invocations",		""));
664 	mainGroup->addChild(new MaxTaskWorkGroupSizeCase		(testCtx, "max_task_work_group_size",				""));
665 	mainGroup->addChild(new MaxTaskOutputCountCase			(testCtx, "max_task_output_count",					""));
666 	mainGroup->addChild(new MaxMeshWorkGroupInvocationsCase	(testCtx, "max_mesh_work_group_invocations",		""));
667 	mainGroup->addChild(new MaxMeshWorkGroupSizeCase		(testCtx, "max_mesh_work_group_size",				""));
668 	mainGroup->addChild(new MaxTaskTotalMemorySizeCase		(testCtx, "max_task_total_memory_size",				""));
669 	mainGroup->addChild(new MaxMeshTotalMemorySizeCase		(testCtx, "max_mesh_total_memory_size",				""));
670 
671 	return mainGroup.release();
672 }
673 
674 } // MeshShader
675 } // vkt
676