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 Builtin Tests
23 *//*--------------------------------------------------------------------*/
24
25#include "vktMeshShaderBuiltinTests.hpp"
26#include "vktMeshShaderUtil.hpp"
27#include "vktTestCase.hpp"
28
29#include "vkTypeUtil.hpp"
30#include "vkImageUtil.hpp"
31#include "vkObjUtil.hpp"
32#include "vkBuilderUtil.hpp"
33#include "vkImageWithMemory.hpp"
34#include "vkBufferWithMemory.hpp"
35#include "vkCmdUtil.hpp"
36#include "vkBarrierUtil.hpp"
37
38#include "tcuTexture.hpp"
39#include "tcuTestLog.hpp"
40
41#include <vector>
42#include <algorithm>
43#include <sstream>
44#include <map>
45#include <utility>
46#include <sstream>
47
48namespace vkt
49{
50namespace MeshShader
51{
52
53namespace
54{
55
56// Wraps a tcu::IVec2 with a custom operator< that uses the X and Y components in component order so it can be used as a map key.
57// Can be converted to and from a tcu::IVec2 automatically.
58class CoordKey
59{
60public:
61	CoordKey (const tcu::IVec2& coords)
62		: m_coords(coords)
63	{}
64
65	operator tcu::IVec2 () const
66	{
67		return m_coords;
68	}
69
70	bool operator< (const CoordKey& other) const
71	{
72		const auto& a = this->m_coords;
73		const auto& b = other.m_coords;
74
75		for (int i = 0; i < tcu::IVec2::SIZE; ++i)
76		{
77			if (a[i] < b[i])
78				return true;
79			if (a[i] > b[i])
80				return false;
81		}
82
83		return false;
84	}
85
86private:
87	const tcu::IVec2 m_coords;
88};
89
90using namespace vk;
91
92using GroupPtr				= de::MovePtr<tcu::TestCaseGroup>;
93using DrawCommandVec		= std::vector<VkDrawMeshTasksIndirectCommandNV>;
94using ImageWithMemoryPtr	= de::MovePtr<ImageWithMemory>;
95using BufferWithMemoryPtr	= de::MovePtr<BufferWithMemory>;
96using ViewportVec			= std::vector<VkViewport>;
97using ColorVec				= std::vector<tcu::Vec4>;
98using PixelMap				= std::map<CoordKey, tcu::Vec4>; // Coordinates to color.
99
100VkExtent2D getDefaultExtent ()
101{
102	return makeExtent2D(8u, 8u);
103}
104
105VkExtent2D getLinearExtent ()
106{
107	return makeExtent2D(8u, 1u);
108}
109
110struct JobSize
111{
112	uint32_t numTasks;
113	uint32_t localSize;
114};
115
116JobSize getLargeJobSize ()
117{
118	return JobSize{8u, 8u};
119}
120
121// Single draw command with the given number of tasks, 1 by default.
122DrawCommandVec getDefaultDrawCommands (uint32_t taskCount = 1u)
123{
124	return DrawCommandVec(1u, makeDrawMeshTasksIndirectCommandNV(taskCount, 0u));
125}
126
127// Basic fragment shader that draws fragments in blue.
128std::string getBasicFragShader ()
129{
130	return
131		"#version 460\n"
132		"#extension GL_NV_mesh_shader : enable\n"
133		"\n"
134		"layout (location=0) out vec4 outColor;\n"
135		"\n"
136		"void main ()\n"
137		"{\n"
138		"    outColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
139		"}\n"
140		;
141}
142
143struct IterationParams
144{
145	VkExtent2D					colorExtent;
146	uint32_t					numLayers;
147	DrawCommandVec				drawArgs;
148	bool						indirect;
149	ViewportVec					viewports;	// If empty, a single default viewport is used.
150	tcu::Maybe<FragmentSize>	fragmentSize;
151};
152
153class MeshShaderBuiltinInstance : public vkt::TestInstance
154{
155public:
156						MeshShaderBuiltinInstance	(Context& context, const IterationParams& params)
157							: vkt::TestInstance	(context)
158							, m_params			(params)
159							{}
160	virtual				~MeshShaderBuiltinInstance	(void) {}
161
162	tcu::TestStatus		iterate						() override;
163	virtual void		verifyResults				(const tcu::ConstPixelBufferAccess& result) = 0;
164
165protected:
166	IterationParams		m_params;
167};
168
169tcu::TestStatus MeshShaderBuiltinInstance::iterate ()
170{
171	const auto&		vkd			= m_context.getDeviceInterface();
172	const auto		device		= m_context.getDevice();
173	auto&			alloc		= m_context.getDefaultAllocator();
174	const auto		queueIndex	= m_context.getUniversalQueueFamilyIndex();
175	const auto		queue		= m_context.getUniversalQueue();
176	const auto&		binaries	= m_context.getBinaryCollection();
177
178	const auto		useTask		= binaries.contains("task");
179	const auto		useFrag		= binaries.contains("frag");
180	const auto		extent		= makeExtent3D(m_params.colorExtent.width, m_params.colorExtent.height, 1u);
181	const auto		iExtent3D	= tcu::IVec3(static_cast<int>(extent.width), static_cast<int>(extent.height), static_cast<int>(m_params.numLayers));
182	const auto		format		= VK_FORMAT_R8G8B8A8_UNORM;
183	const auto		tcuFormat	= mapVkFormat(format);
184	const auto		colorUsage	= (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
185	const auto		viewType	= ((m_params.numLayers > 1u) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D);
186	const auto		colorSRR	= makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_params.numLayers);
187	const auto		colorSRL	= makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, m_params.numLayers);
188	const tcu::Vec4	clearColor	(0.0f, 0.0f, 0.0f, 1.0f);
189
190	ImageWithMemoryPtr	colorBuffer;
191	Move<VkImageView>	colorBufferView;
192	{
193		const VkImageCreateInfo colorBufferInfo =
194		{
195			VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,	//	VkStructureType			sType;
196			nullptr,								//	const void*				pNext;
197			0u,										//	VkImageCreateFlags		flags;
198			VK_IMAGE_TYPE_2D,						//	VkImageType				imageType;
199			format,									//	VkFormat				format;
200			extent,									//	VkExtent3D				extent;
201			1u,										//	uint32_t				mipLevels;
202			m_params.numLayers,						//	uint32_t				arrayLayers;
203			VK_SAMPLE_COUNT_1_BIT,					//	VkSampleCountFlagBits	samples;
204			VK_IMAGE_TILING_OPTIMAL,				//	VkImageTiling			tiling;
205			colorUsage,								//	VkImageUsageFlags		usage;
206			VK_SHARING_MODE_EXCLUSIVE,				//	VkSharingMode			sharingMode;
207			0u,										//	uint32_t				queueFamilyIndexCount;
208			nullptr,								//	const uint32_t*			pQueueFamilyIndices;
209			VK_IMAGE_LAYOUT_UNDEFINED,				//	VkImageLayout			initialLayout;
210		};
211		colorBuffer = ImageWithMemoryPtr(new ImageWithMemory(vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any));
212		colorBufferView = makeImageView(vkd, device, colorBuffer->get(), viewType, format, colorSRR);
213	}
214
215	// Empty descriptor set layout.
216	DescriptorSetLayoutBuilder layoutBuilder;
217	const auto setLayout = layoutBuilder.build(vkd, device);
218
219	// Pipeline layout.
220	const auto pipelineLayout = makePipelineLayout(vkd, device, setLayout.get());
221
222	// Render pass and framebuffer.
223	const auto renderPass	= makeRenderPass(vkd, device, format);
224	const auto framebuffer	= makeFramebuffer(vkd, device, renderPass.get(), colorBufferView.get(), extent.width, extent.height, m_params.numLayers);
225
226	// Pipeline.
227	Move<VkShaderModule> taskModule;
228	Move<VkShaderModule> meshModule;
229	Move<VkShaderModule> fragModule;
230
231	if (useTask)
232		taskModule = createShaderModule(vkd, device, binaries.get("task"));
233	if (useFrag)
234		fragModule = createShaderModule(vkd, device, binaries.get("frag"));
235	meshModule = createShaderModule(vkd, device, binaries.get("mesh"));
236
237	std::vector<VkViewport>	viewports;
238	std::vector<VkRect2D>	scissors;
239	if (m_params.viewports.empty())
240	{
241		// Default ones.
242		viewports.push_back(makeViewport(extent));
243		scissors.push_back(makeRect2D(extent));
244	}
245	else
246	{
247		// The desired viewports and the same number of default scissors.
248		viewports.reserve(m_params.viewports.size());
249		std::copy(begin(m_params.viewports), end(m_params.viewports), std::back_inserter(viewports));
250		scissors.resize(viewports.size(), makeRect2D(extent));
251	}
252
253	using ShadingRateInfoPtr = de::MovePtr<VkPipelineFragmentShadingRateStateCreateInfoKHR>;
254	ShadingRateInfoPtr pNext;
255	if (static_cast<bool>(m_params.fragmentSize))
256	{
257		pNext = ShadingRateInfoPtr(new VkPipelineFragmentShadingRateStateCreateInfoKHR);
258		*pNext = initVulkanStructure();
259
260		pNext->fragmentSize		= getShadingRateSize(m_params.fragmentSize.get());
261		pNext->combinerOps[0]	= VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR;
262		pNext->combinerOps[1]	= VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR;
263	}
264
265	const auto pipeline = makeGraphicsPipeline(vkd, device, pipelineLayout.get(),
266		taskModule.get(), meshModule.get(), fragModule.get(),
267		renderPass.get(), viewports, scissors, 0u, nullptr, nullptr, nullptr, nullptr, nullptr, 0u, pNext.get());
268
269	// Command pool and buffer.
270	const auto cmdPool		= makeCommandPool(vkd, device, queueIndex);
271	const auto cmdBufferPtr	= allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
272	const auto cmdBuffer	= cmdBufferPtr.get();
273
274	// Indirect buffer if needed.
275	BufferWithMemoryPtr indirectBuffer;
276
277	DE_ASSERT(!m_params.drawArgs.empty());
278	if (m_params.indirect)
279	{
280		// Indirect draws.
281		const auto indirectBufferSize	= static_cast<VkDeviceSize>(de::dataSize(m_params.drawArgs));
282		const auto indirectBufferUsage	= (VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
283		const auto indirectBufferInfo	= makeBufferCreateInfo(indirectBufferSize, indirectBufferUsage);
284		indirectBuffer					= BufferWithMemoryPtr(new BufferWithMemory(vkd, device, alloc, indirectBufferInfo, MemoryRequirement::HostVisible));
285		auto& indirectBufferAlloc		= indirectBuffer->getAllocation();
286		void* indirectBufferData		= indirectBufferAlloc.getHostPtr();
287
288		deMemcpy(indirectBufferData, m_params.drawArgs.data(), static_cast<size_t>(indirectBufferSize));
289		flushAlloc(vkd, device, indirectBufferAlloc);
290	}
291
292	// Submit commands.
293	beginCommandBuffer(vkd, cmdBuffer);
294	beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0), clearColor);
295	vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
296
297	if (!m_params.indirect)
298	{
299		for (const auto& command : m_params.drawArgs)
300			vkd.cmdDrawMeshTasksNV(cmdBuffer, command.taskCount, command.firstTask);
301	}
302	else
303	{
304		const auto numDraws	= static_cast<uint32_t>(m_params.drawArgs.size());
305		const auto stride	= static_cast<uint32_t>(sizeof(decltype(m_params.drawArgs)::value_type));
306		vkd.cmdDrawMeshTasksIndirectNV(cmdBuffer, indirectBuffer->get(), 0ull, numDraws, stride);
307	}
308
309	endRenderPass(vkd, cmdBuffer);
310
311	// Output buffer to extract the color buffer contents.
312	BufferWithMemoryPtr	outBuffer;
313	void*				outBufferData	= nullptr;
314	{
315		const auto	layerSize			= static_cast<VkDeviceSize>(static_cast<uint32_t>(tcu::getPixelSize(tcuFormat)) * extent.width * extent.height);
316		const auto	outBufferSize		= layerSize * m_params.numLayers;
317		const auto	outBufferUsage		= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
318		const auto	outBufferInfo		= makeBufferCreateInfo(outBufferSize, outBufferUsage);
319
320		outBuffer						= BufferWithMemoryPtr(new BufferWithMemory(vkd, device, alloc, outBufferInfo, MemoryRequirement::HostVisible));
321		outBufferData					= outBuffer->getAllocation().getHostPtr();
322	}
323
324	// Transition image layout.
325	const auto preTransferBarrier = makeImageMemoryBarrier(
326		(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), VK_ACCESS_TRANSFER_READ_BIT,
327		VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
328		colorBuffer->get(), colorSRR);
329
330	vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &preTransferBarrier);
331
332	// Copy image to output buffer.
333	const std::vector<VkBufferImageCopy> regions (1u, makeBufferImageCopy(extent, colorSRL));
334	vkd.cmdCopyImageToBuffer(cmdBuffer, colorBuffer->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, outBuffer->get(), static_cast<uint32_t>(regions.size()), de::dataOrNull(regions));
335
336	// Transfer to host barrier.
337	const auto postTransferBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
338	vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &postTransferBarrier, 0u, nullptr, 0u, nullptr);
339
340	endCommandBuffer(vkd, cmdBuffer);
341	submitCommandsAndWait(vkd, device, queue, cmdBuffer);
342
343	// Invalidate alloc and verify result.
344	{
345		auto& outBufferAlloc = outBuffer->getAllocation();
346		invalidateAlloc(vkd, device, outBufferAlloc);
347
348		tcu::ConstPixelBufferAccess	result (tcuFormat, iExtent3D, outBufferData);
349		verifyResults(result);
350	}
351
352	return tcu::TestStatus::pass("Pass");
353}
354
355// Abstract case that implements the generic checkSupport method.
356class MeshShaderBuiltinCase : public vkt::TestCase
357{
358public:
359					MeshShaderBuiltinCase	(tcu::TestContext& testCtx, const std::string& name, bool taskNeeded)
360						: vkt::TestCase	(testCtx, name)
361						, m_taskNeeded	(taskNeeded)
362						{}
363	virtual			~MeshShaderBuiltinCase	(void) {}
364
365	void			checkSupport			(Context& context) const override;
366
367protected:
368	const bool		m_taskNeeded;
369};
370
371void MeshShaderBuiltinCase::checkSupport (Context& context) const
372{
373	checkTaskMeshShaderSupportNV(context, m_taskNeeded, true);
374}
375
376// Instance that verifies color layers.
377class FullScreenColorInstance : public MeshShaderBuiltinInstance
378{
379public:
380				FullScreenColorInstance		(Context& context, const IterationParams& params, const ColorVec& expectedColors)
381					: MeshShaderBuiltinInstance (context, params)
382					, m_expectedColors			(expectedColors)
383					{}
384	virtual		~FullScreenColorInstance	(void) {}
385
386	void		verifyResults				(const tcu::ConstPixelBufferAccess& result) override;
387
388protected:
389	const ColorVec m_expectedColors;
390};
391
392void FullScreenColorInstance::verifyResults (const tcu::ConstPixelBufferAccess& result)
393{
394	auto&		log		= m_context.getTestContext().getLog();
395	bool		fail	= false;
396	const auto	width	= result.getWidth();
397	const auto	height	= result.getHeight();
398	const auto	depth	= result.getDepth();
399
400	for (int z = 0; z < depth; ++z)
401	{
402		const auto& expected = m_expectedColors.at(z);
403
404		for (int y = 0; y < height; ++y)
405		for (int x = 0; x < width; ++x)
406		{
407			const auto resultColor = result.getPixel(x, y, z);
408			if (resultColor != expected)
409			{
410				std::ostringstream msg;
411				msg << "Pixel (" << x << ", " << y << ", " << z << ") failed: expected " << expected << " and found " << resultColor;
412				log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
413				fail = true;
414			}
415		}
416	}
417
418	if (fail)
419	{
420		log << tcu::TestLog::Image("Result", "", result);
421		TCU_FAIL("Check log for details");
422	}
423}
424
425// Instance that verifies single-layer framebuffers divided into 4 quadrants.
426class QuadrantsInstance : public MeshShaderBuiltinInstance
427{
428public:
429				QuadrantsInstance	(Context& context, const IterationParams& params,
430									 const tcu::Vec4 topLeft,
431									 const tcu::Vec4 topRight,
432									 const tcu::Vec4 bottomLeft,
433									 const tcu::Vec4 bottomRight)
434					: MeshShaderBuiltinInstance (context, params)
435					, m_topLeft					(topLeft)
436					, m_topRight				(topRight)
437					, m_bottomLeft				(bottomLeft)
438					, m_bottomRight				(bottomRight)
439					{}
440	virtual		~QuadrantsInstance	(void) {}
441
442	void		verifyResults		(const tcu::ConstPixelBufferAccess& result) override;
443
444protected:
445	const tcu::Vec4 m_topLeft;
446	const tcu::Vec4 m_topRight;
447	const tcu::Vec4 m_bottomLeft;
448	const tcu::Vec4 m_bottomRight;
449};
450
451void QuadrantsInstance::verifyResults (const tcu::ConstPixelBufferAccess& result)
452{
453	const auto width	= result.getWidth();
454	const auto height	= result.getHeight();
455	const auto depth	= result.getDepth();
456
457	DE_ASSERT(depth == 1);
458	DE_ASSERT(width > 0 && width % 2 == 0);
459	DE_ASSERT(height > 0 && height % 2 == 0);
460	DE_UNREF(depth); // For release builds.
461
462	const auto	halfWidth	= width / 2;
463	const auto	halfHeight	= height / 2;
464	tcu::Vec4	expected;
465
466	for (int y = 0; y < height; ++y)
467	for (int x = 0; x < width; ++x)
468	{
469		// Choose the right quadrant
470		if (y < halfHeight)
471			expected = ((x < halfWidth) ? m_topLeft : m_topRight);
472		else
473			expected = ((x < halfWidth) ? m_bottomLeft : m_bottomRight);
474
475		const auto resultColor = result.getPixel(x, y);
476		if (resultColor != expected)
477		{
478			std::ostringstream msg;
479			msg << "Pixel (" << x << ", " << y  << ") failed: expected " << expected << " and found " << resultColor;
480			TCU_FAIL(msg.str());
481		}
482	}
483}
484
485// Instance that verifies single-layer framebuffers with specific pixels set to some color.
486struct PixelVerifierParams
487{
488	const tcu::Vec4		background;
489	const PixelMap		pixelMap;
490};
491
492class PixelsInstance : public MeshShaderBuiltinInstance
493{
494public:
495				PixelsInstance	(Context& context, const IterationParams& params, const PixelVerifierParams& pixelParams)
496					: MeshShaderBuiltinInstance	(context, params)
497					, m_pixelParams				(pixelParams)
498					{}
499	virtual		~PixelsInstance	(void) {}
500
501	void		verifyResults	(const tcu::ConstPixelBufferAccess& result) override;
502
503protected:
504	const PixelVerifierParams m_pixelParams;
505};
506
507void PixelsInstance::verifyResults (const tcu::ConstPixelBufferAccess& result)
508{
509	const auto width	= result.getWidth();
510	const auto height	= result.getHeight();
511	const auto depth	= result.getDepth();
512
513	DE_ASSERT(depth == 1);
514	DE_UNREF(depth); // For release builds.
515
516	for (int y = 0; y < height; ++y)
517	for (int x = 0; x < width; ++x)
518	{
519		const tcu::IVec2	coords		(x, y);
520		const auto			iter		= m_pixelParams.pixelMap.find(coords);
521		const auto			expected	= ((iter == m_pixelParams.pixelMap.end()) ? m_pixelParams.background : iter->second);
522		const auto			resultColor	= result.getPixel(x, y);
523
524		if (resultColor != expected)
525		{
526			std::ostringstream msg;
527			msg << "Pixel (" << x << ", " << y << ") failed: expected " << expected << " and found " << resultColor;
528			TCU_FAIL(msg.str());
529		}
530	}
531}
532
533// Primitive ID cases.
534class PrimitiveIdCase : public MeshShaderBuiltinCase
535{
536public:
537					PrimitiveIdCase		(tcu::TestContext& testCtx, const std::string& name, bool glslFrag)
538						: MeshShaderBuiltinCase (testCtx, name, false/*taskNeeded*/)
539						, m_glslFrag			(glslFrag)
540						{}
541	virtual			~PrimitiveIdCase	(void) {}
542
543	void			initPrograms		(vk::SourceCollections& programCollection) const override;
544	void			checkSupport		(Context& context) const override;
545	TestInstance*	createInstance		(Context& context) const override;
546
547protected:
548	// Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID.
549	const bool		m_glslFrag;
550};
551
552void PrimitiveIdCase::initPrograms (vk::SourceCollections& programCollection) const
553{
554	// Mesh shader.
555	{
556		std::ostringstream mesh;
557		mesh
558			<< "#version 460\n"
559			<< "#extension GL_NV_mesh_shader : enable\n"
560			<< "\n"
561			<< "layout (local_size_x=1) in;\n"
562			<< "layout (triangles) out;\n"
563			<< "layout (max_vertices=3, max_primitives=1) out;\n"
564			<< "\n"
565			<< "perprimitiveNV out gl_MeshPerPrimitiveNV {\n"
566			<< "   int gl_PrimitiveID;\n"
567			<< "} gl_MeshPrimitivesNV[];\n"
568			<< "\n"
569			<< "void main ()\n"
570			<< "{\n"
571			<< "    gl_PrimitiveCountNV = 1u;\n"
572			<< "\n"
573			<< "    gl_PrimitiveIndicesNV[0] = 0;\n"
574			<< "    gl_PrimitiveIndicesNV[1] = 1;\n"
575			<< "    gl_PrimitiveIndicesNV[2] = 2;\n"
576			<< "\n"
577			<< "    gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
578			<< "    gl_MeshVerticesNV[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
579			<< "    gl_MeshVerticesNV[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
580			<< "\n"
581			// Sets an arbitrary primitive id.
582			<< "    gl_MeshPrimitivesNV[0].gl_PrimitiveID = 1629198956;\n"
583			<< "}\n"
584			;
585		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
586	}
587
588	// Frag shader.
589	if (m_glslFrag)
590	{
591		std::ostringstream frag;
592		frag
593			<< "#version 460\n"
594			<< "#extension GL_NV_mesh_shader : enable\n"
595			<< "\n"
596			<< "layout (location=0) out vec4 outColor;\n"
597			<< "\n"
598			<< "void main ()\n"
599			<< "{\n"
600			// Checks the primitive id matches.
601			<< "    outColor = ((gl_PrimitiveID == 1629198956) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, 1.0));\n"
602			<< "}\n"
603			;
604		programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
605	}
606	else
607	{
608		// This is the same shader as above, but OpCapability Geometry has been replaced by OpCapability MeshShadingNV in order to
609		// access gl_PrimitiveID. This also needs the SPV_NV_mesh_shader extension.
610		std::ostringstream frag;
611		frag
612			<< "; Version: 1.0\n"
613			<< "; Generator: Khronos Glslang Reference Front End; 10\n"
614			<< "; Bound: 24\n"
615			<< "; Schema: 0\n"
616			<< "      OpCapability Shader\n"
617
618			// Manual change in these lines.
619			//<< "      OpCapability Geometry\n"
620			<< "      OpCapability MeshShadingNV\n"
621			<< "      OpExtension \"SPV_NV_mesh_shader\"\n"
622
623			<< " %1 = OpExtInstImport \"GLSL.std.450\"\n"
624			<< "      OpMemoryModel Logical GLSL450\n"
625			<< "      OpEntryPoint Fragment %4 \"main\" %9 %12\n"
626			<< "      OpExecutionMode %4 OriginUpperLeft\n"
627			<< "      OpDecorate %9 Location 0\n"
628			<< "      OpDecorate %12 Flat\n"
629			<< "      OpDecorate %12 BuiltIn PrimitiveId\n"
630			<< " %2 = OpTypeVoid\n"
631			<< " %3 = OpTypeFunction %2\n"
632			<< " %6 = OpTypeFloat 32\n"
633			<< " %7 = OpTypeVector %6 4\n"
634			<< " %8 = OpTypePointer Output %7\n"
635			<< " %9 = OpVariable %8 Output\n"
636			<< "%10 = OpTypeInt 32 1\n"
637			<< "%11 = OpTypePointer Input %10\n"
638			<< "%12 = OpVariable %11 Input\n"
639			<< "%14 = OpConstant %10 1629198956\n"
640			<< "%15 = OpTypeBool\n"
641			<< "%17 = OpConstant %6 0\n"
642			<< "%18 = OpConstant %6 1\n"
643			<< "%19 = OpConstantComposite %7 %17 %17 %18 %18\n"
644			<< "%20 = OpConstantComposite %7 %17 %17 %17 %18\n"
645			<< "%21 = OpTypeVector %15 4\n"
646			<< " %4 = OpFunction %2 None %3\n"
647			<< " %5 = OpLabel\n"
648			<< "%13 = OpLoad %10 %12\n"
649			<< "%16 = OpIEqual %15 %13 %14\n"
650			<< "%22 = OpCompositeConstruct %21 %16 %16 %16 %16\n"
651			<< "%23 = OpSelect %7 %22 %19 %20\n"
652			<< "      OpStore %9 %23\n"
653			<< "      OpReturn\n"
654			<< "      OpFunctionEnd\n"
655			;
656		programCollection.spirvAsmSources.add("frag") << frag.str();
657	}
658}
659
660void PrimitiveIdCase::checkSupport (Context& context) const
661{
662	MeshShaderBuiltinCase::checkSupport(context);
663
664	// Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID.
665	if (m_glslFrag)
666		context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
667}
668
669TestInstance* PrimitiveIdCase::createInstance (Context& context) const
670{
671	const ColorVec			expectedColors	(1u, tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
672	const IterationParams	iterationParams	=
673	{
674		getDefaultExtent(),			//	VkExtent2D					colorExtent;
675		1u,							//	uint32_t					numLayers;
676		getDefaultDrawCommands(),	//	DrawCommandVec				drawArgs;
677		false,						//	bool						indirect;
678		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
679		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
680	};
681	return new FullScreenColorInstance(context, iterationParams, expectedColors);
682}
683
684// Layer builtin case.
685class LayerCase : public MeshShaderBuiltinCase
686{
687public:
688					LayerCase		(tcu::TestContext& testCtx, const std::string& name, bool shareVertices)
689						: MeshShaderBuiltinCase	(testCtx, name, false/*taskNeeded*/)
690						, m_shareVertices		(shareVertices)
691						{}
692	virtual			~LayerCase	(void) {}
693
694	void			initPrograms	(vk::SourceCollections& programCollection) const override;
695	void			checkSupport	(Context& context) const override;
696	TestInstance*	createInstance	(Context& context) const override;
697
698	static constexpr uint32_t kNumLayers = 4u;
699
700protected:
701	const bool m_shareVertices;
702};
703
704void LayerCase::initPrograms (vk::SourceCollections& programCollection) const
705{
706	const auto localSize		= (m_shareVertices ? kNumLayers : 1u);
707	const auto numPrimitives	= (m_shareVertices ? kNumLayers : 1u);
708	const auto layerNumber		= (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x");
709
710	// One layer per local invocation or work group (shared vertices or not, respectively).
711	{
712		std::ostringstream mesh;
713		mesh
714			<< "#version 460\n"
715			<< "#extension GL_NV_mesh_shader : enable\n"
716			<< "\n"
717			<< "layout (local_size_x=" << localSize << ") in;\n"
718			<< "layout (triangles) out;\n"
719			<< "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n"
720			<< "\n"
721			<< "perprimitiveNV out gl_MeshPerPrimitiveNV {\n"
722			<< "   int gl_Layer;\n"
723			<< "} gl_MeshPrimitivesNV[];\n"
724			<< "\n"
725			<< "void main ()\n"
726			<< "{\n"
727			<< "    gl_PrimitiveCountNV = " << numPrimitives << ";\n"
728			<< "\n"
729			<< "    if (gl_LocalInvocationIndex == 0u)\n"
730			<< "    {\n"
731			<< "        gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
732			<< "        gl_MeshVerticesNV[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
733			<< "        gl_MeshVerticesNV[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
734			<< "    }\n"
735			<< "\n"
736			<< "    const uint baseIndex = gl_LocalInvocationIndex * 3u;\n"
737			<< "    gl_PrimitiveIndicesNV[baseIndex + 0] = 0;\n"
738			<< "    gl_PrimitiveIndicesNV[baseIndex + 1] = 1;\n"
739			<< "    gl_PrimitiveIndicesNV[baseIndex + 2] = 2;\n"
740			<< "\n"
741			<< "    gl_MeshPrimitivesNV[gl_LocalInvocationIndex].gl_Layer = int(" << layerNumber << ");\n"
742			<< "}\n"
743			;
744		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
745	}
746
747	// Fragment shader chooses one color per layer.
748	{
749		std::ostringstream frag;
750		frag
751			<< "#version 460\n"
752			<< "#extension GL_NV_mesh_shader : enable\n"
753			<< "\n"
754			<< "layout (location=0) out vec4 outColor;\n"
755			<< "\n"
756			<< "vec4 colors[" << kNumLayers << "] = vec4[](\n"
757			<< "    vec4(0.0, 0.0, 1.0, 1.0),\n"
758			<< "    vec4(1.0, 0.0, 1.0, 1.0),\n"
759			<< "    vec4(0.0, 1.0, 1.0, 1.0),\n"
760			<< "    vec4(1.0, 1.0, 0.0, 1.0)\n"
761			<< ");\n"
762			<< "\n"
763			<< "void main ()\n"
764			<< "{\n"
765			<< "    outColor = colors[gl_Layer];\n"
766			<< "}\n"
767			;
768		programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
769	}
770}
771
772void LayerCase::checkSupport (Context& context) const
773{
774	MeshShaderBuiltinCase::checkSupport(context);
775
776	if (!context.contextSupports(vk::ApiVersion(0u, 1u, 2u, 0u)))
777		context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer");
778	else
779	{
780		const auto& features = context.getDeviceVulkan12Features();
781		if (!features.shaderOutputLayer)
782			TCU_THROW(NotSupportedError, "shaderOutputLayer feature not supported");
783	}
784}
785
786TestInstance* LayerCase::createInstance (Context& context) const
787{
788	ColorVec expectedColors;
789
790	expectedColors.reserve(kNumLayers);
791	expectedColors.push_back(tcu::Vec4(0.0, 0.0, 1.0, 1.0));
792	expectedColors.push_back(tcu::Vec4(1.0, 0.0, 1.0, 1.0));
793	expectedColors.push_back(tcu::Vec4(0.0, 1.0, 1.0, 1.0));
794	expectedColors.push_back(tcu::Vec4(1.0, 1.0, 0.0, 1.0));
795
796	const auto numWorkGroups = (m_shareVertices ? 1u : kNumLayers);
797	const IterationParams iterationParams =
798	{
799		getDefaultExtent(),						//	VkExtent2D					colorExtent;
800		kNumLayers,								//	uint32_t					numLayers;
801		getDefaultDrawCommands(numWorkGroups),	//	DrawCommandVec				drawArgs;
802		false,									//	bool						indirect;
803		{},										//	ViewportVec					viewports;	// If empty, a single default viewport is used.
804		tcu::Nothing,							//	tcu::Maybe<FragmentSize>	fragmentSize;
805	};
806	return new FullScreenColorInstance(context, iterationParams, expectedColors);
807}
808
809// ViewportIndex builtin case.
810class ViewportIndexCase : public MeshShaderBuiltinCase
811{
812public:
813					ViewportIndexCase	(tcu::TestContext& testCtx, const std::string& name, bool shareVertices)
814						: MeshShaderBuiltinCase	(testCtx, name, false/*taskNeeded*/)
815						, m_shareVertices		(shareVertices)
816						{}
817	virtual			~ViewportIndexCase	(void) {}
818
819	void			initPrograms		(vk::SourceCollections& programCollection) const override;
820	void			checkSupport		(Context& context) const override;
821	TestInstance*	createInstance		(Context& context) const override;
822
823	static constexpr uint32_t kQuadrants = 4u;
824
825protected:
826	const bool m_shareVertices;
827};
828
829void ViewportIndexCase::initPrograms (vk::SourceCollections& programCollection) const
830{
831	const auto localSize		= (m_shareVertices ? kQuadrants : 1u);
832	const auto numPrimitives	= (m_shareVertices ? kQuadrants : 1u);
833	const auto viewportIndex	= (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x");
834
835	// One viewport per local invocation or work group (sharing vertices or not, respectively).
836	{
837		std::ostringstream mesh;
838		mesh
839			<< "#version 460\n"
840			<< "#extension GL_NV_mesh_shader : enable\n"
841			<< "\n"
842			<< "layout (local_size_x=" << localSize << ") in;\n"
843			<< "layout (triangles) out;\n"
844			<< "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n"
845			<< "\n"
846			<< "perprimitiveNV out gl_MeshPerPrimitiveNV {\n"
847			<< "   int gl_ViewportIndex;\n"
848			<< "} gl_MeshPrimitivesNV[];\n"
849			<< "\n"
850			<< "void main ()\n"
851			<< "{\n"
852			<< "    gl_PrimitiveCountNV = " << numPrimitives << ";\n"
853			<< "\n"
854			<< "    if (gl_LocalInvocationIndex == 0u)\n"
855			<< "    {\n"
856			<< "        gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
857			<< "        gl_MeshVerticesNV[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
858			<< "        gl_MeshVerticesNV[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
859			<< "    }\n"
860			<< "\n"
861			<< "    const uint baseIndex = gl_LocalInvocationIndex * 3u;\n"
862			<< "    gl_PrimitiveIndicesNV[baseIndex + 0] = 0;\n"
863			<< "    gl_PrimitiveIndicesNV[baseIndex + 1] = 1;\n"
864			<< "    gl_PrimitiveIndicesNV[baseIndex + 2] = 2;\n"
865			<< "\n"
866			<< "    gl_MeshPrimitivesNV[gl_LocalInvocationIndex].gl_ViewportIndex = int(" << viewportIndex << ");\n"
867			<< "}\n"
868			;
869		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
870	}
871
872	// Fragment shader chooses one color per viewport.
873	{
874		std::ostringstream frag;
875		frag
876			<< "#version 460\n"
877			<< "#extension GL_NV_mesh_shader : enable\n"
878			<< "\n"
879			<< "layout (location=0) out vec4 outColor;\n"
880			<< "\n"
881			<< "vec4 colors[" << kQuadrants << "] = vec4[](\n"
882			<< "    vec4(0.0, 0.0, 1.0, 1.0),\n"
883			<< "    vec4(1.0, 0.0, 1.0, 1.0),\n"
884			<< "    vec4(0.0, 1.0, 1.0, 1.0),\n"
885			<< "    vec4(1.0, 1.0, 0.0, 1.0)\n"
886			<< ");\n"
887			<< "\n"
888			<< "void main ()\n"
889			<< "{\n"
890			<< "    outColor = colors[gl_ViewportIndex];\n"
891			<< "}\n"
892			;
893		programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
894	}
895}
896
897void ViewportIndexCase::checkSupport (Context& context) const
898{
899	MeshShaderBuiltinCase::checkSupport(context);
900	context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_MULTI_VIEWPORT);
901
902	if (!context.contextSupports(vk::ApiVersion(0u, 1u, 2u, 0u)))
903		context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer");
904	else
905	{
906		const auto& features = context.getDeviceVulkan12Features();
907		if (!features.shaderOutputViewportIndex)
908			TCU_THROW(NotSupportedError, "shaderOutputViewportIndex feature not supported");
909	}
910}
911
912TestInstance* ViewportIndexCase::createInstance (Context& context) const
913{
914	const auto extent = getDefaultExtent();
915
916	DE_ASSERT(extent.width > 0u && extent.width % 2u == 0u);
917	DE_ASSERT(extent.height > 0u && extent.height % 2u == 0u);
918
919	const auto halfWidth	= static_cast<float>(extent.width) / 2.0f;
920	const auto halfHeight	= static_cast<float>(extent.height) / 2.0f;
921
922	const auto topLeft		= tcu::Vec4(0.0, 0.0, 1.0, 1.0);
923	const auto topRight		= tcu::Vec4(1.0, 0.0, 1.0, 1.0);
924	const auto bottomLeft	= tcu::Vec4(0.0, 1.0, 1.0, 1.0);
925	const auto bottomRight	= tcu::Vec4(1.0, 1.0, 0.0, 1.0);
926
927	ViewportVec viewports;
928	viewports.reserve(kQuadrants);
929	viewports.emplace_back(makeViewport(0.0f,		0.0f,		halfWidth, halfHeight, 0.0f, 1.0f));
930	viewports.emplace_back(makeViewport(halfWidth,	0.0f,		halfWidth, halfHeight, 0.0f, 1.0f));
931	viewports.emplace_back(makeViewport(0.0f,		halfHeight,	halfWidth, halfHeight, 0.0f, 1.0f));
932	viewports.emplace_back(makeViewport(halfWidth,	halfHeight,	halfWidth, halfHeight, 0.0f, 1.0f));
933
934	const auto numWorkGroups = (m_shareVertices ? 1u : kQuadrants);
935	const IterationParams iterationParams =
936	{
937		getDefaultExtent(),						//	VkExtent2D					colorExtent;
938		1u,										//	uint32_t					numLayers;
939		getDefaultDrawCommands(numWorkGroups),	//	DrawCommandVec				drawArgs;
940		false,									//	bool						indirect;
941		std::move(viewports),					//	ViewportVec					viewports;
942		tcu::Nothing,							//	tcu::Maybe<FragmentSize>	fragmentSize;
943	};
944	return new QuadrantsInstance(context, iterationParams, topLeft, topRight, bottomLeft, bottomRight);
945}
946
947// Position builtin case.
948class PositionCase : public MeshShaderBuiltinCase
949{
950public:
951					PositionCase		(tcu::TestContext& testCtx, const std::string& name)
952						: MeshShaderBuiltinCase (testCtx, name, false/*taskNeeded*/)
953						{}
954	virtual			~PositionCase	(void) {}
955
956	void			initPrograms		(vk::SourceCollections& programCollection) const override;
957	TestInstance*	createInstance		(Context& context) const override;
958};
959
960void PositionCase::initPrograms (vk::SourceCollections& programCollection) const
961{
962	// Mesh shader: emit single triangle around the center of the top left pixel.
963	{
964		const auto extent	= getDefaultExtent();
965		const auto fWidth	= static_cast<float>(extent.width);
966		const auto fHeight	= static_cast<float>(extent.height);
967
968		const auto pxWidth	= 2.0f / fWidth;
969		const auto pxHeight = 2.0f / fHeight;
970
971		const auto halfXPix	= pxWidth / 2.0f;
972		const auto halfYPix	= pxHeight / 2.0f;
973
974		// Center of top left pixel.
975		const auto x		= -1.0f + halfXPix;
976		const auto y		= -1.0f + halfYPix;
977
978		std::ostringstream mesh;
979		mesh
980			<< "#version 460\n"
981			<< "#extension GL_NV_mesh_shader : enable\n"
982			<< "\n"
983			<< "layout (local_size_x=1) in;\n"
984			<< "layout (triangles) out;\n"
985			<< "layout (max_vertices=3, max_primitives=1) out;\n"
986			<< "\n"
987			<< "void main ()\n"
988			<< "{\n"
989			<< "    gl_PrimitiveCountNV = 1u;\n"
990			<< "\n"
991			<< "    gl_PrimitiveIndicesNV[0] = 0;\n"
992			<< "    gl_PrimitiveIndicesNV[1] = 1;\n"
993			<< "    gl_PrimitiveIndicesNV[2] = 2;\n"
994			<< "\n"
995			<< "    gl_MeshVerticesNV[0].gl_Position = vec4(" << (x - halfXPix) << ", " << (y + halfYPix) << ", 0.0, 1.0);\n"
996			<< "    gl_MeshVerticesNV[1].gl_Position = vec4(" << (x + halfXPix) << ", " << (y + halfYPix) << ", 0.0, 1.0);\n"
997			<< "    gl_MeshVerticesNV[2].gl_Position = vec4(" << x << ", " << (y - halfYPix) << ", 0.0, 1.0);\n"
998			<< "}\n"
999			;
1000		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1001	}
1002
1003	// Basic fragment shader.
1004	{
1005		const auto frag = getBasicFragShader();
1006		programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1007	}
1008}
1009
1010TestInstance* PositionCase::createInstance (Context& context) const
1011{
1012	const IterationParams iterationParams =
1013	{
1014		getDefaultExtent(),			//	VkExtent2D					colorExtent;
1015		1u,							//	uint32_t					numLayers;
1016		getDefaultDrawCommands(),	//	DrawCommandVec				drawArgs;
1017		false,						//	bool						indirect;
1018		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1019		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
1020	};
1021
1022	// Must match the shader.
1023	PixelMap pixelMap;
1024	pixelMap[tcu::IVec2(0, 0)] = tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f);
1025
1026	const PixelVerifierParams verifierParams =
1027	{
1028		tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f),	//	const tcu::Vec4		background;
1029		std::move(pixelMap),				//	const PixelMap		pixelMap;
1030	};
1031	return new PixelsInstance(context, iterationParams, verifierParams);
1032}
1033
1034// PointSize builtin case.
1035class PointSizeCase : public MeshShaderBuiltinCase
1036{
1037public:
1038					PointSizeCase		(tcu::TestContext& testCtx, const std::string& name)
1039						: MeshShaderBuiltinCase (testCtx, name, false/*taskNeeded*/)
1040						{}
1041	virtual			~PointSizeCase	(void) {}
1042
1043	void			initPrograms		(vk::SourceCollections& programCollection) const override;
1044	TestInstance*	createInstance		(Context& context) const override;
1045	void			checkSupport		(Context& context) const override;
1046
1047	static constexpr float kPointSize = 4.0f;
1048};
1049
1050void PointSizeCase::initPrograms (vk::SourceCollections& programCollection) const
1051{
1052	// Mesh shader: large point covering the top left quadrant.
1053	{
1054		std::ostringstream mesh;
1055		mesh
1056			<< "#version 460\n"
1057			<< "#extension GL_NV_mesh_shader : enable\n"
1058			<< "\n"
1059			<< "layout (local_size_x=1) in;\n"
1060			<< "layout (points) out;\n"
1061			<< "layout (max_vertices=1, max_primitives=1) out;\n"
1062			<< "\n"
1063			<< "void main ()\n"
1064			<< "{\n"
1065			<< "    gl_PrimitiveCountNV = 1u;\n"
1066			<< "    gl_PrimitiveIndicesNV[0] = 0;\n"
1067			<< "    gl_MeshVerticesNV[0].gl_Position = vec4(-0.5, -0.5, 0.0, 1.0);\n"
1068			<< "    gl_MeshVerticesNV[0].gl_PointSize = " << kPointSize << ";\n"
1069			<< "}\n"
1070			;
1071		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1072	}
1073
1074	// Basic fragment shader.
1075	{
1076		const auto frag = getBasicFragShader();
1077		programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1078	}
1079}
1080
1081TestInstance* PointSizeCase::createInstance (Context& context) const
1082{
1083	const IterationParams iterationParams =
1084	{
1085		getDefaultExtent(),			//	VkExtent2D					colorExtent;
1086		1u,							//	uint32_t					numLayers;
1087		getDefaultDrawCommands(),	//	DrawCommandVec				drawArgs;
1088		false,						//	bool						indirect;
1089		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1090		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
1091	};
1092
1093	// Must match the shader.
1094	const tcu::Vec4 black	(0.0f, 0.0f, 0.0f, 1.0f);
1095	const tcu::Vec4 blue	(0.0f, 0.0f, 1.0f, 1.0f);
1096
1097	return new QuadrantsInstance(context, iterationParams, blue, black, black, black);
1098}
1099
1100void PointSizeCase::checkSupport (Context& context) const
1101{
1102	MeshShaderBuiltinCase::checkSupport(context);
1103	context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_LARGE_POINTS);
1104
1105	const auto& properties = context.getDeviceProperties();
1106	if (kPointSize < properties.limits.pointSizeRange[0] || kPointSize > properties.limits.pointSizeRange[1])
1107		TCU_THROW(NotSupportedError, "Required point size outside point size range");
1108}
1109
1110// ClipDistance builtin case.
1111class ClipDistanceCase : public MeshShaderBuiltinCase
1112{
1113public:
1114					ClipDistanceCase		(tcu::TestContext& testCtx, const std::string& name)
1115						: MeshShaderBuiltinCase (testCtx, name, false/*taskNeeded*/)
1116						{}
1117	virtual			~ClipDistanceCase	(void) {}
1118
1119	void			initPrograms		(vk::SourceCollections& programCollection) const override;
1120	TestInstance*	createInstance		(Context& context) const override;
1121	void			checkSupport		(Context& context) const override;
1122};
1123
1124void ClipDistanceCase::initPrograms (vk::SourceCollections& programCollection) const
1125{
1126	// Mesh shader: full-screen quad using different clip distances.
1127	{
1128		std::ostringstream mesh;
1129		mesh
1130			<< "#version 460\n"
1131			<< "#extension GL_NV_mesh_shader : enable\n"
1132			<< "\n"
1133			<< "layout (local_size_x=1) in;\n"
1134			<< "layout (triangles) out;\n"
1135			<< "layout (max_vertices=4, max_primitives=2) out;\n"
1136			<< "\n"
1137			<< "out gl_MeshPerVertexNV {\n"
1138			<< "    vec4  gl_Position;\n"
1139			<< "    float gl_ClipDistance[2];\n"
1140			<< "} gl_MeshVerticesNV[];\n"
1141			<< "\n"
1142			<< "void main ()\n"
1143			<< "{\n"
1144			<< "    gl_PrimitiveCountNV = 2u;\n"
1145			<< "\n"
1146			<< "    gl_PrimitiveIndicesNV[0] = 0;\n"
1147			<< "    gl_PrimitiveIndicesNV[1] = 1;\n"
1148			<< "    gl_PrimitiveIndicesNV[2] = 2;\n"
1149			<< "    gl_PrimitiveIndicesNV[3] = 1;\n"
1150			<< "    gl_PrimitiveIndicesNV[4] = 3;\n"
1151			<< "    gl_PrimitiveIndicesNV[5] = 2;\n"
1152			<< "\n"
1153			<< "    gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1154			<< "    gl_MeshVerticesNV[1].gl_Position = vec4(-1.0,  1.0, 0.0, 1.0);\n"
1155			<< "    gl_MeshVerticesNV[2].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n"
1156			<< "    gl_MeshVerticesNV[3].gl_Position = vec4( 1.0,  1.0, 0.0, 1.0);\n"
1157			<< "\n"
1158			// The first clip plane keeps the left half of the frame buffer.
1159			<< "    gl_MeshVerticesNV[0].gl_ClipDistance[0] =  1.0;\n"
1160			<< "    gl_MeshVerticesNV[1].gl_ClipDistance[0] =  1.0;\n"
1161			<< "    gl_MeshVerticesNV[2].gl_ClipDistance[0] = -1.0;\n"
1162			<< "    gl_MeshVerticesNV[3].gl_ClipDistance[0] = -1.0;\n"
1163			<< "\n"
1164			// The second clip plane keeps the top half of the frame buffer.
1165			<< "    gl_MeshVerticesNV[0].gl_ClipDistance[1] =  1.0;\n"
1166			<< "    gl_MeshVerticesNV[1].gl_ClipDistance[1] = -1.0;\n"
1167			<< "    gl_MeshVerticesNV[2].gl_ClipDistance[1] =  1.0;\n"
1168			<< "    gl_MeshVerticesNV[3].gl_ClipDistance[1] = -1.0;\n"
1169			<< "}\n"
1170			;
1171		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1172	}
1173
1174	// Fragment shader chooses a constant color.
1175	{
1176		std::ostringstream frag;
1177		frag
1178			<< "#version 460\n"
1179			<< "#extension GL_NV_mesh_shader : enable\n"
1180			<< "\n"
1181			<< "layout (location=0) out vec4 outColor;\n"
1182			<< "\n"
1183			<< "void main ()\n"
1184			<< "{\n"
1185			// White color should not actually be used, as those fragments are supposed to be discarded.
1186			<< "    outColor = ((gl_ClipDistance[0] >= 0.0 && gl_ClipDistance[1] >= 0.0) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(1.0, 1.0, 1.0, 1.0));\n"
1187			<< "}\n"
1188			;
1189		programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
1190	}
1191}
1192
1193TestInstance* ClipDistanceCase::createInstance (Context& context) const
1194{
1195	const IterationParams iterationParams =
1196	{
1197		getDefaultExtent(),			//	VkExtent2D					colorExtent;
1198		1u,							//	uint32_t					numLayers;
1199		getDefaultDrawCommands(),	//	DrawCommandVec				drawArgs;
1200		false,						//	bool						indirect;
1201		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1202		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
1203	};
1204
1205	// Must match the shader.
1206	const tcu::Vec4 black	(0.0f, 0.0f, 0.0f, 1.0f);
1207	const tcu::Vec4 blue	(0.0f, 0.0f, 1.0f, 1.0f);
1208
1209	return new QuadrantsInstance(context, iterationParams, blue, black, black, black);
1210}
1211
1212void ClipDistanceCase::checkSupport (Context& context) const
1213{
1214	MeshShaderBuiltinCase::checkSupport(context);
1215	context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CLIP_DISTANCE);
1216}
1217
1218// CullDistance builtin case.
1219class CullDistanceCase : public MeshShaderBuiltinCase
1220{
1221public:
1222					CullDistanceCase		(tcu::TestContext& testCtx, const std::string& name)
1223						: MeshShaderBuiltinCase (testCtx, name, false/*taskNeeded*/)
1224						{}
1225	virtual			~CullDistanceCase	(void) {}
1226
1227	void			initPrograms		(vk::SourceCollections& programCollection) const override;
1228	TestInstance*	createInstance		(Context& context) const override;
1229	void			checkSupport		(Context& context) const override;
1230};
1231
1232void CullDistanceCase::initPrograms (vk::SourceCollections& programCollection) const
1233{
1234	// Mesh shader: two quads covering the whole screen, one on top of the other.
1235	// Use cull distances to discard the bottom quad.
1236	// Use cull distances to paint the top one in two colors: blue on the left, white on the right.
1237	{
1238		std::ostringstream mesh;
1239		mesh
1240			<< "#version 460\n"
1241			<< "#extension GL_NV_mesh_shader : enable\n"
1242			<< "\n"
1243			<< "layout (local_size_x=1) in;\n"
1244			<< "layout (triangles) out;\n"
1245			<< "layout (max_vertices=6, max_primitives=4) out;\n"
1246			<< "\n"
1247			<< "out gl_MeshPerVertexNV {\n"
1248			<< "    vec4  gl_Position;\n"
1249			<< "    float gl_CullDistance[2];\n"
1250			<< "} gl_MeshVerticesNV[];\n"
1251			<< "\n"
1252			<< "void main ()\n"
1253			<< "{\n"
1254			<< "    gl_PrimitiveCountNV = 4u;\n"
1255			<< "\n"
1256			<< "    gl_PrimitiveIndicesNV[0]  = 0;\n"
1257			<< "    gl_PrimitiveIndicesNV[1]  = 1;\n"
1258			<< "    gl_PrimitiveIndicesNV[2]  = 3;\n"
1259			<< "    gl_PrimitiveIndicesNV[3]  = 1;\n"
1260			<< "    gl_PrimitiveIndicesNV[4]  = 4;\n"
1261			<< "    gl_PrimitiveIndicesNV[5]  = 3;\n"
1262			<< "    gl_PrimitiveIndicesNV[6]  = 1;\n"
1263			<< "    gl_PrimitiveIndicesNV[7]  = 2;\n"
1264			<< "    gl_PrimitiveIndicesNV[8]  = 4;\n"
1265			<< "    gl_PrimitiveIndicesNV[9]  = 2;\n"
1266			<< "    gl_PrimitiveIndicesNV[10] = 5;\n"
1267			<< "    gl_PrimitiveIndicesNV[11] = 4;\n"
1268			<< "\n"
1269			<< "    gl_MeshVerticesNV[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1270			<< "    gl_MeshVerticesNV[1].gl_Position = vec4(-1.0,  0.0, 0.0, 1.0);\n"
1271			<< "    gl_MeshVerticesNV[2].gl_Position = vec4(-1.0,  1.0, 0.0, 1.0);\n"
1272			<< "    gl_MeshVerticesNV[3].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n"
1273			<< "    gl_MeshVerticesNV[4].gl_Position = vec4( 1.0,  0.0, 0.0, 1.0);\n"
1274			<< "    gl_MeshVerticesNV[5].gl_Position = vec4( 1.0,  1.0, 0.0, 1.0);\n"
1275			<< "\n"
1276			// The first cull plane discards the bottom quad
1277			<< "    gl_MeshVerticesNV[0].gl_CullDistance[0] =  1.0;\n"
1278			<< "    gl_MeshVerticesNV[1].gl_CullDistance[0] = -1.0;\n"
1279			<< "    gl_MeshVerticesNV[2].gl_CullDistance[0] = -2.0;\n"
1280			<< "    gl_MeshVerticesNV[3].gl_CullDistance[0] =  1.0;\n"
1281			<< "    gl_MeshVerticesNV[4].gl_CullDistance[0] = -1.0;\n"
1282			<< "    gl_MeshVerticesNV[5].gl_CullDistance[0] = -2.0;\n"
1283			<< "\n"
1284			// The second cull plane helps paint left and right different.
1285			<< "    gl_MeshVerticesNV[0].gl_CullDistance[1] =  1.0;\n"
1286			<< "    gl_MeshVerticesNV[1].gl_CullDistance[1] =  1.0;\n"
1287			<< "    gl_MeshVerticesNV[2].gl_CullDistance[1] =  1.0;\n"
1288			<< "    gl_MeshVerticesNV[3].gl_CullDistance[1] = -1.0;\n"
1289			<< "    gl_MeshVerticesNV[4].gl_CullDistance[1] = -1.0;\n"
1290			<< "    gl_MeshVerticesNV[5].gl_CullDistance[1] = -1.0;\n"
1291			<< "}\n"
1292			;
1293		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1294	}
1295
1296	// Fragment shader chooses color based on the second cull distance.
1297	{
1298		std::ostringstream frag;
1299		frag
1300			<< "#version 460\n"
1301			<< "#extension GL_NV_mesh_shader : enable\n"
1302			<< "\n"
1303			<< "layout (location=0) out vec4 outColor;\n"
1304			<< "\n"
1305			<< "void main ()\n"
1306			<< "{\n"
1307			<< "    outColor = ((gl_CullDistance[1] >= 0.0) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(1.0, 1.0, 1.0, 1.0));\n"
1308			<< "}\n"
1309			;
1310		programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
1311	}
1312}
1313
1314TestInstance* CullDistanceCase::createInstance (Context& context) const
1315{
1316	const IterationParams iterationParams =
1317	{
1318		getDefaultExtent(),			//	VkExtent2D					colorExtent;
1319		1u,							//	uint32_t					numLayers;
1320		getDefaultDrawCommands(),	//	DrawCommandVec				drawArgs;
1321		false,						//	bool						indirect;
1322		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1323		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
1324	};
1325
1326	// Must match the shader.
1327	const tcu::Vec4 black	(0.0f, 0.0f, 0.0f, 1.0f);
1328	const tcu::Vec4 blue	(0.0f, 0.0f, 1.0f, 1.0f);
1329	const tcu::Vec4 white	(1.0f, 1.0f, 1.0f, 1.0f);
1330
1331	return new QuadrantsInstance(context, iterationParams, blue, white, black, black);
1332}
1333
1334void CullDistanceCase::checkSupport (Context& context) const
1335{
1336	MeshShaderBuiltinCase::checkSupport(context);
1337	context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CULL_DISTANCE);
1338}
1339
1340// Generates statements to draw a triangle around the given pixel number, knowing the framebuffer width (len).
1341// Supposes the height of the framebuffer is 1.
1342std::string triangleForPixel(const std::string& pixel, const std::string& len, const std::string& baseIndex)
1343{
1344	std::ostringstream statements;
1345	statements
1346		<< "    const float imgWidth = float(" << len << ");\n"
1347		<< "    const float pixWidth = (2.0 / imgWidth);\n"
1348		<< "    const float halfPix  = (pixWidth / 2.0);\n"
1349		<< "    const float xCenter  = (((float(" << pixel << ") + 0.5) / imgWidth) * 2.0 - 1.0);\n"
1350		<< "    const float xLeft    = (xCenter - halfPix);\n"
1351		<< "    const float xRight   = (xCenter + halfPix);\n"
1352		<< "    const uvec3 indices  = uvec3(" << baseIndex << " + 0, " << baseIndex << " + 1, " << baseIndex << " + 2);\n"
1353		<< "\n"
1354		<< "    gl_PrimitiveIndicesNV[indices.x] = indices.x;\n"
1355		<< "    gl_PrimitiveIndicesNV[indices.y] = indices.y;\n"
1356		<< "    gl_PrimitiveIndicesNV[indices.z] = indices.z;\n"
1357		<< "\n"
1358		<< "    gl_MeshVerticesNV[indices.x].gl_Position = vec4(xLeft,    0.5, 0.0, 1.0);\n"
1359		<< "    gl_MeshVerticesNV[indices.y].gl_Position = vec4(xRight,   0.5, 0.0, 1.0);\n"
1360		<< "    gl_MeshVerticesNV[indices.z].gl_Position = vec4(xCenter, -0.5, 0.0, 1.0);\n"
1361		;
1362	return statements.str();
1363}
1364
1365// WorkGroupID builtin case.
1366class WorkGroupIdCase : public MeshShaderBuiltinCase
1367{
1368public:
1369					WorkGroupIdCase		(tcu::TestContext& testCtx, const std::string& name, bool taskNeeded)
1370						: MeshShaderBuiltinCase (testCtx, name, taskNeeded)
1371						, m_extent				(getLinearExtent())
1372						{}
1373	virtual			~WorkGroupIdCase	(void) {}
1374
1375	void			initPrograms		(vk::SourceCollections& programCollection) const override;
1376	TestInstance*	createInstance		(Context& context) const override;
1377
1378protected:
1379	const VkExtent2D m_extent;
1380};
1381
1382void WorkGroupIdCase::initPrograms (vk::SourceCollections& programCollection) const
1383{
1384	const std::string taskDataDecl =
1385		"taskNV TaskData {\n"
1386		"    uint id;\n"
1387		"    uint size;\n"
1388		"} td;\n"
1389		;
1390
1391	// Mesh shader: each work group fills one pixel.
1392	{
1393		const std::string pixel = (m_taskNeeded ? "td.id"   : "gl_WorkGroupID.x"   );
1394		const std::string len   = (m_taskNeeded ? "td.size" : de::toString(m_extent.width) );
1395
1396		std::ostringstream mesh;
1397		mesh
1398			<< "#version 460\n"
1399			<< "#extension GL_NV_mesh_shader : enable\n"
1400			<< "\n"
1401			<< "layout (local_size_x=1) in;\n"
1402			<< "layout (triangles) out;\n"
1403			<< "layout (max_vertices=3, max_primitives=1) out;\n"
1404			<< "\n"
1405			<< (m_taskNeeded ? ("in " + taskDataDecl) : "")
1406			<< "\n"
1407			<< "void main ()\n"
1408			<< "{\n"
1409			<< "    gl_PrimitiveCountNV = 1u;\n"
1410			<< "\n"
1411			<< triangleForPixel(pixel, len, "0")
1412			<< "}\n"
1413			;
1414		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1415	}
1416
1417	if (m_taskNeeded)
1418	{
1419		std::ostringstream task;
1420		task
1421			<< "#version 460\n"
1422			<< "#extension GL_NV_mesh_shader : enable\n"
1423			<< "\n"
1424			<< "layout (local_size_x=1) in;\n"
1425			<< "\n"
1426			<< "out " << taskDataDecl
1427			<< "\n"
1428			<< "void main ()\n"
1429			<< "{\n"
1430			<< "    gl_TaskCountNV = 1u;\n"
1431			<< "    td.id          = gl_WorkGroupID.x;\n"
1432			<< "    td.size        = " << m_extent.width << ";\n"
1433			<< "}\n"
1434			;
1435		programCollection.glslSources.add("task") << glu::TaskSource(task.str());
1436	}
1437
1438	// Basic fragment shader.
1439	{
1440		const auto frag = getBasicFragShader();
1441		programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1442	}
1443}
1444
1445TestInstance* WorkGroupIdCase::createInstance (Context& context) const
1446{
1447	// Must match the shader.
1448	const ColorVec			expectedColors	(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1449	const IterationParams	iterationParams	=
1450	{
1451		m_extent,								//	VkExtent2D					colorExtent;
1452		1u,										//	uint32_t					numLayers;
1453		getDefaultDrawCommands(m_extent.width),	//	DrawCommandVec				drawArgs;
1454		false,									//	bool						indirect;
1455		{},										//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1456		tcu::Nothing,							//	tcu::Maybe<FragmentSize>	fragmentSize;
1457	};
1458	return new FullScreenColorInstance(context, iterationParams, expectedColors);
1459}
1460
1461// Variable to use.
1462enum class LocalInvocation { ID=0, INDEX };
1463
1464// LocalInvocationId and LocalInvocationIndex builtin cases. These are also used to test WorkGroupSize.
1465class LocalInvocationCase : public MeshShaderBuiltinCase
1466{
1467public:
1468					LocalInvocationCase		(tcu::TestContext& testCtx, const std::string& name, bool taskNeeded, LocalInvocation variable)
1469						: MeshShaderBuiltinCase (testCtx, name, taskNeeded)
1470						, m_extent				(getLinearExtent())
1471						, m_variable			(variable)
1472						{}
1473	virtual			~LocalInvocationCase	(void) {}
1474
1475	void			initPrograms			(vk::SourceCollections& programCollection) const override;
1476	TestInstance*	createInstance			(Context& context) const override;
1477
1478protected:
1479	const VkExtent2D      m_extent;
1480	const LocalInvocation m_variable;
1481};
1482
1483void LocalInvocationCase::initPrograms (vk::SourceCollections& programCollection) const
1484{
1485	// Invocation index to use.
1486	const std::string localIndex = ((m_variable == LocalInvocation::ID) ? "gl_LocalInvocationID.x" : "gl_LocalInvocationIndex");
1487
1488	// Task data.
1489	std::ostringstream taskDataDecl;
1490	taskDataDecl
1491		<< "taskNV TaskData {\n"
1492		// indexNumber[x] == x
1493		<< "    uint indexNumber[" << m_extent.width << "];\n"
1494		<< "    uint size;\n"
1495		<< "} td;\n"
1496		;
1497	const auto taskDataDeclStr = taskDataDecl.str();
1498
1499	// Mesh shader: each work group fills one pixel.
1500	{
1501		const std::string pixel     = (m_taskNeeded ? "td.indexNumber[gl_WorkGroupID.x]" : localIndex);
1502		const std::string len       = (m_taskNeeded ? "td.size" : "gl_WorkGroupSize.x");
1503		const auto        localSize = (m_taskNeeded ? 1u : m_extent.width);
1504		const auto        maxVert   = localSize * 3u;
1505		const std::string baseIndex = (m_taskNeeded ? "0" : "(" + localIndex + " * 3u)");
1506
1507		std::ostringstream mesh;
1508		mesh
1509			<< "#version 460\n"
1510			<< "#extension GL_NV_mesh_shader : enable\n"
1511			<< "\n"
1512			<< "layout (local_size_x=" << localSize << ") in;\n"
1513			<< "layout (triangles) out;\n"
1514			<< "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n"
1515			<< "\n"
1516			<< (m_taskNeeded ? ("in " + taskDataDeclStr) : "")
1517			<< "\n"
1518			<< "void main ()\n"
1519			<< "{\n"
1520			<< "    gl_PrimitiveCountNV = " << localSize << ";\n"
1521			<< "\n"
1522			<< triangleForPixel(pixel, len, baseIndex)
1523			<< "}\n"
1524			;
1525		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1526	}
1527
1528	if (m_taskNeeded)
1529	{
1530		std::ostringstream task;
1531		task
1532			<< "#version 460\n"
1533			<< "#extension GL_NV_mesh_shader : enable\n"
1534			<< "\n"
1535			<< "layout (local_size_x=" << m_extent.width << ") in;\n"
1536			<< "\n"
1537			<< "out " << taskDataDeclStr
1538			<< "\n"
1539			<< "void main ()\n"
1540			<< "{\n"
1541			<< "    gl_TaskCountNV = " << m_extent.width << ";\n"
1542			<< "    td.indexNumber[" << localIndex << "] = " << localIndex << ";\n"
1543			<< "    td.size = gl_WorkGroupSize.x;\n"
1544			<< "}\n"
1545			;
1546		programCollection.glslSources.add("task") << glu::TaskSource(task.str());
1547	}
1548
1549	// Basic fragment shader.
1550	{
1551		const auto frag = getBasicFragShader();
1552		programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1553	}
1554}
1555
1556TestInstance* LocalInvocationCase::createInstance (Context& context) const
1557{
1558	// Must match the shader.
1559	const ColorVec			expectedColors	(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1560	const IterationParams	iterationParams	=
1561	{
1562		m_extent,					//	VkExtent2D					colorExtent;
1563		1u,							//	uint32_t					numLayers;
1564		getDefaultDrawCommands(),	//	DrawCommandVec				drawArgs;
1565		false,						//	bool						indirect;
1566		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1567		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
1568	};
1569	return new FullScreenColorInstance(context, iterationParams, expectedColors);
1570}
1571
1572// GlobalInvocationId builtin case.
1573class GlobalInvocationIdCase : public MeshShaderBuiltinCase
1574{
1575public:
1576					GlobalInvocationIdCase		(tcu::TestContext& testCtx, const std::string& name, bool taskNeeded)
1577						: MeshShaderBuiltinCase (testCtx, name, taskNeeded)
1578						, m_jobSize				(getLargeJobSize())
1579						, m_extent				{m_jobSize.numTasks * m_jobSize.localSize, 1u}
1580						{}
1581	virtual			~GlobalInvocationIdCase		(void) {}
1582
1583	void			initPrograms				(vk::SourceCollections& programCollection) const override;
1584	TestInstance*	createInstance				(Context& context) const override;
1585
1586protected:
1587	const JobSize    m_jobSize;
1588	const VkExtent2D m_extent;
1589};
1590
1591void GlobalInvocationIdCase::initPrograms (vk::SourceCollections& programCollection) const
1592{
1593	const auto& localSize = m_jobSize.localSize;
1594
1595	// Task data.
1596	std::ostringstream taskDataDecl;
1597	taskDataDecl
1598		<< "taskNV TaskData {\n"
1599		<< "    uint pixelId[" << localSize << "];\n"
1600		<< "    uint size;\n"
1601		<< "} td;\n"
1602		;
1603	const auto taskDataDeclStr = taskDataDecl.str();
1604
1605	// Mesh shader: each work group fills one pixel.
1606	{
1607		const std::string pixel     = (m_taskNeeded ? "td.pixelId[gl_LocalInvocationIndex]" : "gl_GlobalInvocationID.x");
1608		const std::string len       = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
1609		const std::string baseIndex = "(gl_LocalInvocationIndex * 3u)";
1610		const auto        maxVert   = localSize * 3u;
1611
1612		std::ostringstream mesh;
1613		mesh
1614			<< "#version 460\n"
1615			<< "#extension GL_NV_mesh_shader : enable\n"
1616			<< "\n"
1617			<< "layout (local_size_x=" << localSize << ") in;\n"
1618			<< "layout (triangles) out;\n"
1619			<< "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n"
1620			<< "\n"
1621			<< (m_taskNeeded ? ("in " + taskDataDeclStr) : "")
1622			<< "\n"
1623			<< "void main ()\n"
1624			<< "{\n"
1625			<< "    gl_PrimitiveCountNV = " << localSize << ";\n"
1626			<< "\n"
1627			<< triangleForPixel(pixel, len, baseIndex)
1628			<< "}\n"
1629			;
1630		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1631	}
1632
1633	if (m_taskNeeded)
1634	{
1635		std::ostringstream task;
1636		task
1637			<< "#version 460\n"
1638			<< "#extension GL_NV_mesh_shader : enable\n"
1639			<< "\n"
1640			<< "layout (local_size_x=" << localSize << ") in;\n"
1641			<< "\n"
1642			<< "out " << taskDataDeclStr
1643			<< "\n"
1644			<< "void main ()\n"
1645			<< "{\n"
1646			<< "    gl_TaskCountNV = 1;\n"
1647			<< "    td.pixelId[gl_LocalInvocationIndex] = gl_GlobalInvocationID.x;\n"
1648			<< "    td.size = " << m_extent.width << ";\n"
1649			<< "}\n"
1650			;
1651		programCollection.glslSources.add("task") << glu::TaskSource(task.str());
1652	}
1653
1654	// Basic fragment shader.
1655	{
1656		const auto frag = getBasicFragShader();
1657		programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1658	}
1659}
1660
1661TestInstance* GlobalInvocationIdCase::createInstance (Context& context) const
1662{
1663	// Must match the shader.
1664	const ColorVec			expectedColors	(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1665	const IterationParams	iterationParams	=
1666	{
1667		m_extent,									//	VkExtent2D					colorExtent;
1668		1u,											//	uint32_t					numLayers;
1669		getDefaultDrawCommands(m_jobSize.numTasks),	//	DrawCommandVec				drawArgs;
1670		false,										//	bool						indirect;
1671		{},											//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1672		tcu::Nothing,								//	tcu::Maybe<FragmentSize>	fragmentSize;
1673	};
1674	return new FullScreenColorInstance(context, iterationParams, expectedColors);
1675}
1676
1677// DrawIndex builtin case.
1678class DrawIndexCase : public MeshShaderBuiltinCase
1679{
1680public:
1681					DrawIndexCase		(tcu::TestContext& testCtx, const std::string& name, bool taskNeeded)
1682						: MeshShaderBuiltinCase (testCtx, name, taskNeeded)
1683						, m_extent				(getLinearExtent())
1684						{}
1685	virtual			~DrawIndexCase	(void) {}
1686
1687	void			initPrograms		(vk::SourceCollections& programCollection) const override;
1688	TestInstance*	createInstance		(Context& context) const override;
1689
1690protected:
1691	const VkExtent2D m_extent;
1692};
1693
1694void DrawIndexCase::initPrograms (vk::SourceCollections& programCollection) const
1695{
1696	const std::string taskDataDecl =
1697		"taskNV TaskData {\n"
1698		"    uint id;\n"
1699		"    uint size;\n"
1700		"} td;\n"
1701		;
1702
1703	const auto drawIndex = "uint(gl_DrawID)";
1704
1705	// Mesh shader: each work group fills one pixel.
1706	{
1707		const std::string pixel = (m_taskNeeded ? "td.id"   : drawIndex);
1708		const std::string len   = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
1709
1710		std::ostringstream mesh;
1711		mesh
1712			<< "#version 460\n"
1713			<< "#extension GL_NV_mesh_shader : enable\n"
1714			<< "\n"
1715			<< "layout (local_size_x=1) in;\n"
1716			<< "layout (triangles) out;\n"
1717			<< "layout (max_vertices=3, max_primitives=1) out;\n"
1718			<< "\n"
1719			<< (m_taskNeeded ? ("in " + taskDataDecl) : "")
1720			<< "\n"
1721			<< "void main ()\n"
1722			<< "{\n"
1723			<< "    gl_PrimitiveCountNV = 1u;\n"
1724			<< "\n"
1725			<< triangleForPixel(pixel, len, "0")
1726			<< "}\n"
1727			;
1728		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str());
1729	}
1730
1731	if (m_taskNeeded)
1732	{
1733		std::ostringstream task;
1734		task
1735			<< "#version 460\n"
1736			<< "#extension GL_NV_mesh_shader : enable\n"
1737			<< "\n"
1738			<< "layout (local_size_x=1) in;\n"
1739			<< "\n"
1740			<< "out " << taskDataDecl
1741			<< "\n"
1742			<< "void main ()\n"
1743			<< "{\n"
1744			<< "    gl_TaskCountNV = 1u;\n"
1745			<< "    td.id          = " << drawIndex << ";\n"
1746			<< "    td.size        = " << m_extent.width << ";\n"
1747			<< "}\n"
1748			;
1749		programCollection.glslSources.add("task") << glu::TaskSource(task.str());
1750	}
1751
1752	// Basic fragment shader.
1753	{
1754		const auto frag = getBasicFragShader();
1755		programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1756	}
1757}
1758
1759TestInstance* DrawIndexCase::createInstance (Context& context) const
1760{
1761	// Must match the shader.
1762	const ColorVec			expectedColors	(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1763	const DrawCommandVec	commands		(m_extent.width, makeDrawMeshTasksIndirectCommandNV(1u, 0u));
1764	const IterationParams	iterationParams	=
1765	{
1766		m_extent,		//	VkExtent2D					colorExtent;
1767		1u,				//	uint32_t					numLayers;
1768		commands,		//	DrawCommandVec				drawArgs;
1769		true,			//	bool						indirect;
1770		{},				//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1771		tcu::Nothing,	//	tcu::Maybe<FragmentSize>	fragmentSize;
1772	};
1773	return new FullScreenColorInstance(context, iterationParams, expectedColors);
1774}
1775
1776// Primitive Shading Rate case.
1777class PrimitiveShadingRateCase : public MeshShaderBuiltinCase
1778{
1779public:
1780					PrimitiveShadingRateCase	(tcu::TestContext& testCtx, const std::string& name, FragmentSize topSize, FragmentSize bottomSize)
1781						: MeshShaderBuiltinCase	(testCtx, name, false/*taskNeeded*/)
1782						, m_topSize				(topSize)
1783						, m_bottomSize			(bottomSize)
1784						{}
1785	virtual			~PrimitiveShadingRateCase	(void) {}
1786
1787	void			initPrograms				(vk::SourceCollections& programCollection) const override;
1788	void			checkSupport				(Context& context) const override;
1789	TestInstance*	createInstance				(Context& context) const override;
1790
1791protected:
1792	const FragmentSize m_topSize;
1793	const FragmentSize m_bottomSize;
1794};
1795
1796void PrimitiveShadingRateCase::initPrograms (vk::SourceCollections& programCollection) const
1797{
1798	// Shading rate masks to use.
1799	const auto topMask			= getGLSLShadingRateMask(m_topSize);
1800	const auto bottomMask		= getGLSLShadingRateMask(m_bottomSize);
1801	const auto topMaskVal		= getSPVShadingRateValue(m_topSize);
1802	const auto bottomMaskVal	= getSPVShadingRateValue(m_bottomSize);
1803
1804	// Mesh shader.
1805	{
1806		// Similar to the GLSL code below if glslang accepted it.
1807		// Top quad with two triangles and bottom quad with two triangles.
1808		// One shading rate mask each.
1809#if 0
1810		#version 460
1811		#extension GL_NV_mesh_shader : enable
1812		#extension GL_EXT_fragment_shading_rate : enable
1813
1814		layout (local_size_x=1) in;
1815		layout (triangles) out;
1816		layout (max_vertices=6, max_primitives=4) out;
1817
1818		perprimitiveNV out gl_MeshPerPrimitiveNV {
1819			int gl_PrimitiveShadingRateEXT;
1820		} gl_MeshPrimitivesNV[];
1821
1822		void main ()
1823		{
1824			gl_PrimitiveCountNV = 4u;
1825
1826			const vec4 topLeft  = vec4(-1.0, -1.0, 0.0, 1.0);
1827			const vec4 midLeft  = vec4(-1.0,  0.0, 0.0, 1.0);
1828			const vec4 botLeft  = vec4(-1.0,  1.0, 0.0, 1.0);
1829
1830			const vec4 topRight = vec4( 1.0, -1.0, 0.0, 1.0);
1831			const vec4 midRight = vec4( 1.0,  0.0, 0.0, 1.0);
1832			const vec4 botRight = vec4( 1.0,  1.0, 0.0, 1.0);
1833
1834			gl_MeshVerticesNV[0].gl_Position = topLeft;
1835			gl_MeshVerticesNV[1].gl_Position = midLeft;
1836			gl_MeshVerticesNV[2].gl_Position = botLeft;
1837
1838			gl_MeshVerticesNV[3].gl_Position = topRight;
1839			gl_MeshVerticesNV[4].gl_Position = midRight;
1840			gl_MeshVerticesNV[5].gl_Position = botRight;
1841
1842			gl_PrimitiveIndicesNV[0]  = 0u;
1843			gl_PrimitiveIndicesNV[1]  = 1u;
1844			gl_PrimitiveIndicesNV[2]  = 3u;
1845			gl_PrimitiveIndicesNV[3]  = 1u;
1846			gl_PrimitiveIndicesNV[4]  = 4u;
1847			gl_PrimitiveIndicesNV[5]  = 3u;
1848			gl_PrimitiveIndicesNV[6]  = 1u;
1849			gl_PrimitiveIndicesNV[7]  = 2u;
1850			gl_PrimitiveIndicesNV[8]  = 4u;
1851			gl_PrimitiveIndicesNV[9]  = 2u;
1852			gl_PrimitiveIndicesNV[10] = 5u;
1853			gl_PrimitiveIndicesNV[11] = 4u;
1854
1855			gl_MeshPrimitivesNV[0].gl_PrimitiveShadingRateEXT = TOP_MASK;
1856			gl_MeshPrimitivesNV[1].gl_PrimitiveShadingRateEXT = TOP_MASK;
1857			gl_MeshPrimitivesNV[2].gl_PrimitiveShadingRateEXT = BOTTOM_MASK;
1858			gl_MeshPrimitivesNV[3].gl_PrimitiveShadingRateEXT = BOTTOM_MASK;
1859		}
1860#endif
1861		std::ostringstream meshSPV;
1862		meshSPV
1863			<< "; SPIR-V\n"
1864			<< "; Version: 1.0\n"
1865			<< "; Generator: Khronos Glslang Reference Front End; 10\n"
1866			<< "; Bound: 81\n"
1867			<< "; Schema: 0\n"
1868			<< "      OpCapability MeshShadingNV\n"
1869			<< "      OpCapability FragmentShadingRateKHR\n"					// Added manually.
1870			<< "      OpExtension \"SPV_NV_mesh_shader\"\n"
1871			<< "      OpExtension \"SPV_KHR_fragment_shading_rate\"\n"			// Added manually.
1872			<< " %1 = OpExtInstImport \"GLSL.std.450\"\n"
1873			<< "      OpMemoryModel Logical GLSL450\n"
1874			<< "      OpEntryPoint MeshNV %4 \"main\" %8 %20 %47 %73\n"
1875			<< "      OpExecutionMode %4 LocalSize 1 1 1\n"
1876			<< "      OpExecutionMode %4 OutputVertices 6\n"
1877			<< "      OpExecutionMode %4 OutputPrimitivesNV 4\n"
1878			<< "      OpExecutionMode %4 OutputTrianglesNV\n"
1879			<< "      OpDecorate %8 BuiltIn PrimitiveCountNV\n"
1880			<< "      OpMemberDecorate %16 0 BuiltIn Position\n"
1881			<< "      OpMemberDecorate %16 1 BuiltIn PointSize\n"
1882			<< "      OpMemberDecorate %16 2 BuiltIn ClipDistance\n"
1883			<< "      OpMemberDecorate %16 3 BuiltIn CullDistance\n"
1884			<< "      OpMemberDecorate %16 4 PerViewNV\n"
1885			<< "      OpMemberDecorate %16 4 BuiltIn PositionPerViewNV\n"
1886			<< "      OpMemberDecorate %16 5 PerViewNV\n"
1887			<< "      OpMemberDecorate %16 5 BuiltIn ClipDistancePerViewNV\n"
1888			<< "      OpMemberDecorate %16 6 PerViewNV\n"
1889			<< "      OpMemberDecorate %16 6 BuiltIn CullDistancePerViewNV\n"
1890			<< "      OpDecorate %16 Block\n"
1891			<< "      OpDecorate %47 BuiltIn PrimitiveIndicesNV\n"
1892			<< "      OpMemberDecorate %70 0 PerPrimitiveNV\n"
1893			<< "      OpMemberDecorate %70 0 BuiltIn PrimitiveShadingRateKHR\n"	// Replaced PrimitiveID with this.
1894			<< "      OpDecorate %70 Block\n"
1895			<< "      OpDecorate %80 BuiltIn WorkgroupSize\n"
1896			<< " %2 = OpTypeVoid\n"
1897			<< " %3 = OpTypeFunction %2\n"
1898			<< " %6 = OpTypeInt 32 0\n"
1899			<< " %7 = OpTypePointer Output %6\n"
1900			<< " %8 = OpVariable %7 Output\n"
1901			<< " %9 = OpConstant %6 4\n"
1902			<< "%10 = OpTypeFloat 32\n"
1903			<< "%11 = OpTypeVector %10 4\n"
1904			<< "%12 = OpConstant %6 1\n"
1905			<< "%13 = OpTypeArray %10 %12\n"
1906			<< "%14 = OpTypeArray %11 %9\n"
1907			<< "%15 = OpTypeArray %13 %9\n"
1908			<< "%16 = OpTypeStruct %11 %10 %13 %13 %14 %15 %15\n"
1909			<< "%17 = OpConstant %6 6\n"
1910			<< "%18 = OpTypeArray %16 %17\n"
1911			<< "%19 = OpTypePointer Output %18\n"
1912			<< "%20 = OpVariable %19 Output\n"
1913			<< "%21 = OpTypeInt 32 1\n"
1914			<< "%tm = OpConstant %21 " << topMaskVal << "\n"					// Added mask value line.
1915			<< "%bm = OpConstant %21 " << bottomMaskVal << "\n"					// Ditto.
1916			<< "%22 = OpConstant %21 0\n"
1917			<< "%23 = OpConstant %10 -1\n"
1918			<< "%24 = OpConstant %10 0\n"
1919			<< "%25 = OpConstant %10 1\n"
1920			<< "%26 = OpConstantComposite %11 %23 %23 %24 %25\n"
1921			<< "%27 = OpTypePointer Output %11\n"
1922			<< "%29 = OpConstant %21 1\n"
1923			<< "%30 = OpConstantComposite %11 %23 %24 %24 %25\n"
1924			<< "%32 = OpConstant %21 2\n"
1925			<< "%33 = OpConstantComposite %11 %23 %25 %24 %25\n"
1926			<< "%35 = OpConstant %21 3\n"
1927			<< "%36 = OpConstantComposite %11 %25 %23 %24 %25\n"
1928			<< "%38 = OpConstant %21 4\n"
1929			<< "%39 = OpConstantComposite %11 %25 %24 %24 %25\n"
1930			<< "%41 = OpConstant %21 5\n"
1931			<< "%42 = OpConstantComposite %11 %25 %25 %24 %25\n"
1932			<< "%44 = OpConstant %6 12\n"
1933			<< "%45 = OpTypeArray %6 %44\n"
1934			<< "%46 = OpTypePointer Output %45\n"
1935			<< "%47 = OpVariable %46 Output\n"
1936			<< "%48 = OpConstant %6 0\n"
1937			<< "%51 = OpConstant %6 3\n"
1938			<< "%56 = OpConstant %21 6\n"
1939			<< "%58 = OpConstant %21 7\n"
1940			<< "%59 = OpConstant %6 2\n"
1941			<< "%61 = OpConstant %21 8\n"
1942			<< "%63 = OpConstant %21 9\n"
1943			<< "%65 = OpConstant %21 10\n"
1944			<< "%66 = OpConstant %6 5\n"
1945			<< "%68 = OpConstant %21 11\n"
1946			<< "%70 = OpTypeStruct %21\n"
1947			<< "%71 = OpTypeArray %70 %9\n"
1948			<< "%72 = OpTypePointer Output %71\n"
1949			<< "%73 = OpVariable %72 Output\n"
1950			<< "%74 = OpTypePointer Output %21\n"
1951			<< "%79 = OpTypeVector %6 3\n"
1952			<< "%80 = OpConstantComposite %79 %12 %12 %12\n"
1953			<< " %4 = OpFunction %2 None %3\n"
1954			<< " %5 = OpLabel\n"
1955			<< "      OpStore %8 %9\n"
1956			<< "%28 = OpAccessChain %27 %20 %22 %22\n"
1957			<< "      OpStore %28 %26\n"
1958			<< "%31 = OpAccessChain %27 %20 %29 %22\n"
1959			<< "      OpStore %31 %30\n"
1960			<< "%34 = OpAccessChain %27 %20 %32 %22\n"
1961			<< "      OpStore %34 %33\n"
1962			<< "%37 = OpAccessChain %27 %20 %35 %22\n"
1963			<< "      OpStore %37 %36\n"
1964			<< "%40 = OpAccessChain %27 %20 %38 %22\n"
1965			<< "      OpStore %40 %39\n"
1966			<< "%43 = OpAccessChain %27 %20 %41 %22\n"
1967			<< "      OpStore %43 %42\n"
1968			<< "%49 = OpAccessChain %7 %47 %22\n"
1969			<< "      OpStore %49 %48\n"
1970			<< "%50 = OpAccessChain %7 %47 %29\n"
1971			<< "      OpStore %50 %12\n"
1972			<< "%52 = OpAccessChain %7 %47 %32\n"
1973			<< "      OpStore %52 %51\n"
1974			<< "%53 = OpAccessChain %7 %47 %35\n"
1975			<< "      OpStore %53 %12\n"
1976			<< "%54 = OpAccessChain %7 %47 %38\n"
1977			<< "      OpStore %54 %9\n"
1978			<< "%55 = OpAccessChain %7 %47 %41\n"
1979			<< "      OpStore %55 %51\n"
1980			<< "%57 = OpAccessChain %7 %47 %56\n"
1981			<< "      OpStore %57 %12\n"
1982			<< "%60 = OpAccessChain %7 %47 %58\n"
1983			<< "      OpStore %60 %59\n"
1984			<< "%62 = OpAccessChain %7 %47 %61\n"
1985			<< "      OpStore %62 %9\n"
1986			<< "%64 = OpAccessChain %7 %47 %63\n"
1987			<< "      OpStore %64 %59\n"
1988			<< "%67 = OpAccessChain %7 %47 %65\n"
1989			<< "      OpStore %67 %66\n"
1990			<< "%69 = OpAccessChain %7 %47 %68\n"
1991			<< "      OpStore %69 %9\n"
1992			<< "%75 = OpAccessChain %74 %73 %22 %22\n"
1993			<< "      OpStore %75 %tm\n"										// Store the proper mask here. First triangle, top primitive.
1994			<< "%76 = OpAccessChain %74 %73 %29 %22\n"
1995			<< "      OpStore %76 %tm\n"										// Second triangle, top primitive.
1996			<< "%77 = OpAccessChain %74 %73 %32 %22\n"
1997			<< "      OpStore %77 %bm\n"										// Third triangle, bottom primitive.
1998			<< "%78 = OpAccessChain %74 %73 %35 %22\n"
1999			<< "      OpStore %78 %bm\n"										// Fourth triangle, bottom primitive.
2000			<< "      OpReturn\n"
2001			<< "      OpFunctionEnd\n"
2002			;
2003		programCollection.spirvAsmSources.add("mesh") << meshSPV.str();
2004	}
2005
2006	// Frag shader.
2007	{
2008		const auto extent		= getDefaultExtent();
2009		const auto halfHeight	= static_cast<float>(extent.height) / 2.0f;
2010
2011		std::ostringstream frag;
2012		frag
2013			<< "#version 460\n"
2014			<< "#extension GL_NV_mesh_shader : enable\n"
2015			<< "#extension GL_EXT_fragment_shading_rate : enable\n"
2016			<< "\n"
2017			<< "layout (location=0) out vec4 outColor;\n"
2018			<< "\n"
2019			<< "void main ()\n"
2020			<< "{\n"
2021			// Checks the shading rate matches.
2022			<< "    const int expectedRate = ((gl_FragCoord.y < " << halfHeight << ")? " << topMask << " : " << bottomMask << ");\n"
2023			<< "    outColor = ((gl_ShadingRateEXT == expectedRate) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, 1.0));\n"
2024			<< "}\n"
2025			;
2026		programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
2027	}
2028}
2029
2030void PrimitiveShadingRateCase::checkSupport (Context& context) const
2031{
2032	MeshShaderBuiltinCase::checkSupport(context);
2033
2034	context.requireDeviceFunctionality("VK_KHR_fragment_shading_rate");
2035}
2036
2037TestInstance* PrimitiveShadingRateCase::createInstance (Context& context) const
2038{
2039	const ColorVec			expectedColors	(1u, tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
2040	FragmentSizeVector		fsInUse			{m_topSize, m_bottomSize};
2041	const IterationParams	iterationParams	=
2042	{
2043		getDefaultExtent(),												//	VkExtent2D					colorExtent;
2044		1u,																//	uint32_t					numLayers;
2045		getDefaultDrawCommands(),										//	DrawCommandVec				drawArgs;
2046		false,															//	bool						indirect;
2047		{},																//	ViewportVec					viewports;	// If empty, a single default viewport is used.
2048		tcu::just(getBadShadingRateSize(begin(fsInUse), end(fsInUse))),	//	tcu::Maybe<FragmentSize>	fragmentSize;
2049	};
2050	return new FullScreenColorInstance(context, iterationParams, expectedColors);
2051}
2052
2053} // anonymous
2054
2055tcu::TestCaseGroup* createMeshShaderBuiltinTests (tcu::TestContext& testCtx)
2056{
2057	GroupPtr mainGroup (new tcu::TestCaseGroup(testCtx, "builtin"));
2058
2059	mainGroup->addChild(new PositionCase				(testCtx, "position"));
2060	mainGroup->addChild(new PointSizeCase				(testCtx, "point_size"));
2061	mainGroup->addChild(new ClipDistanceCase			(testCtx, "clip_distance"));
2062	mainGroup->addChild(new CullDistanceCase			(testCtx, "cull_distance"));
2063	mainGroup->addChild(new PrimitiveIdCase				(testCtx, "primitive_id_glsl", true/*glslFrag*/));
2064	mainGroup->addChild(new PrimitiveIdCase				(testCtx, "primitive_id_spirv", false/*glslFrag*/));
2065	mainGroup->addChild(new LayerCase					(testCtx, "layer", false/*shareVertices*/));
2066	mainGroup->addChild(new LayerCase					(testCtx, "layer_shared", true/*shareVertices*/));
2067	mainGroup->addChild(new ViewportIndexCase			(testCtx, "viewport_index", false/*shareVertices*/));
2068	mainGroup->addChild(new ViewportIndexCase			(testCtx, "viewport_index_shared", true/*shareVertices*/));
2069	mainGroup->addChild(new WorkGroupIdCase				(testCtx, "work_group_id_in_mesh", false/*taskNeeded*/));
2070	mainGroup->addChild(new WorkGroupIdCase				(testCtx, "work_group_id_in_task", true/*taskNeeded*/));
2071	mainGroup->addChild(new LocalInvocationCase			(testCtx, "local_invocation_id_in_mesh", false/*taskNeeded*/, LocalInvocation::ID));
2072	mainGroup->addChild(new LocalInvocationCase			(testCtx, "local_invocation_id_in_task", true/*taskNeeded*/, LocalInvocation::ID));
2073	mainGroup->addChild(new LocalInvocationCase			(testCtx, "local_invocation_index_in_task", true/*taskNeeded*/, LocalInvocation::INDEX));
2074	mainGroup->addChild(new LocalInvocationCase			(testCtx, "local_invocation_index_in_mesh", false/*taskNeeded*/, LocalInvocation::INDEX));
2075	mainGroup->addChild(new GlobalInvocationIdCase		(testCtx, "global_invocation_id_in_mesh", false/*taskNeeded*/));
2076	mainGroup->addChild(new GlobalInvocationIdCase		(testCtx, "global_invocation_id_in_task", true/*taskNeeded*/));
2077	mainGroup->addChild(new DrawIndexCase				(testCtx, "draw_index_in_mesh", false/*taskNeeded*/));
2078	mainGroup->addChild(new DrawIndexCase				(testCtx, "draw_index_in_task", true/*taskNeeded*/));
2079
2080	// Primitive shading rate tests.
2081	{
2082		const auto sizeCount = static_cast<int>(FragmentSize::SIZE_COUNT);
2083
2084		for (int i = 0; i < sizeCount; ++i)
2085		for (int j = 0; j < sizeCount; ++j)
2086		{
2087			const auto topSize		= static_cast<FragmentSize>(i);
2088			const auto bottomSize	= static_cast<FragmentSize>(j);
2089
2090			const auto topExtent	= getShadingRateSize(topSize);
2091			const auto bottomExtent	= getShadingRateSize(bottomSize);
2092
2093			const auto testName		= "primitive_shading_rate_"
2094										+ std::to_string(topExtent.width) + "x" + std::to_string(topExtent.height)
2095										+ "_"
2096										+ std::to_string(bottomExtent.width) + "x" + std::to_string(bottomExtent.height)
2097									;
2098
2099			mainGroup->addChild(new PrimitiveShadingRateCase(testCtx, testName, topSize, bottomSize));
2100		}
2101	}
2102
2103	return mainGroup.release();
2104}
2105
2106} // MeshShader
2107} // vkt
2108