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