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