• 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 for VK_EXT_mesh_shader
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktMeshShaderBuiltinTestsEXT.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 
48 namespace vkt
49 {
50 namespace MeshShader
51 {
52 
53 namespace
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.
58 class CoordKey
59 {
60 public:
CoordKey(const tcu::IVec2 & coords)61 	CoordKey (const tcu::IVec2& coords)
62 		: m_coords(coords)
63 	{}
64 
operator tcu::IVec2() const65 	operator tcu::IVec2 () const
66 	{
67 		return m_coords;
68 	}
69 
operator <(const CoordKey & other) const70 	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 
86 private:
87 	const tcu::IVec2 m_coords;
88 };
89 
90 using namespace vk;
91 
92 using GroupPtr				= de::MovePtr<tcu::TestCaseGroup>;
93 using DrawCommandVec		= std::vector<VkDrawMeshTasksIndirectCommandEXT>;
94 using ImageWithMemoryPtr	= de::MovePtr<ImageWithMemory>;
95 using BufferWithMemoryPtr	= de::MovePtr<BufferWithMemory>;
96 using ViewportVec			= std::vector<VkViewport>;
97 using ColorVec				= std::vector<tcu::Vec4>;
98 using PixelMap				= std::map<CoordKey, tcu::Vec4>; // Coordinates to color.
99 
getClearColor()100 tcu::Vec4 getClearColor ()
101 {
102 	return tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
103 }
104 
getDefaultExtent()105 VkExtent2D getDefaultExtent ()
106 {
107 	return makeExtent2D(8u, 8u);
108 }
109 
getLinearExtent()110 VkExtent2D getLinearExtent ()
111 {
112 	return makeExtent2D(8u, 1u);
113 }
114 
115 struct JobSize
116 {
117 	uint32_t numTasks;
118 	uint32_t localSize;
119 };
120 
getLargeJobSize()121 JobSize getLargeJobSize ()
122 {
123 	return JobSize{8u, 8u};
124 }
125 
126 // Single draw command with the given number of tasks, 1 by default.
getDefaultDrawCommands(uint32_t taskCount=1u)127 DrawCommandVec getDefaultDrawCommands (uint32_t taskCount = 1u)
128 {
129 	return DrawCommandVec(1u, makeDrawMeshTasksIndirectCommandEXT(taskCount, 1u, 1u));
130 }
131 
132 // Basic fragment shader that draws fragments in blue.
getBasicFragShader()133 std::string getBasicFragShader ()
134 {
135 	return
136 		"#version 460\n"
137 		"layout (location=0) out vec4 outColor;\n"
138 		"void main ()\n"
139 		"{\n"
140 		"    outColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
141 		"}\n"
142 		;
143 }
144 
145 struct IterationParams
146 {
147 	VkExtent2D					colorExtent;
148 	uint32_t					numLayers;
149 	bool						multiview;
150 	bool						indirect;
151 	tcu::Maybe<FragmentSize>	fragmentSize;
152 	DrawCommandVec				drawArgs;
153 	ViewportVec					viewports;	// If empty, a single default viewport is used.
154 };
155 
156 class MeshShaderBuiltinInstance : public vkt::TestInstance
157 {
158 public:
MeshShaderBuiltinInstance(Context & context,const IterationParams & params)159 						MeshShaderBuiltinInstance	(Context& context, const IterationParams& params)
160 							: vkt::TestInstance	(context)
161 							, m_params			(params)
162 							{}
~MeshShaderBuiltinInstance(void)163 	virtual				~MeshShaderBuiltinInstance	(void) {}
164 
165 	tcu::TestStatus		iterate						() override;
166 	virtual void		verifyResults				(const tcu::ConstPixelBufferAccess& result) = 0;
167 
168 protected:
169 	IterationParams		m_params;
170 };
171 
createCustomRenderPass(const DeviceInterface & vkd,VkDevice device,VkFormat format,bool multiview,uint32_t numLayers)172 Move<VkRenderPass> createCustomRenderPass (const DeviceInterface& vkd, VkDevice device, VkFormat format, bool multiview, uint32_t numLayers)
173 {
174 	DE_ASSERT(numLayers > 0u);
175 	const uint32_t numSubpasses = (multiview ? numLayers : 1u);
176 
177 	const VkAttachmentDescription colorAttachmentDescription =
178 	{
179 		0u,											// VkAttachmentDescriptionFlags    flags
180 		format,										// VkFormat                        format
181 		VK_SAMPLE_COUNT_1_BIT,						// VkSampleCountFlagBits           samples
182 		VK_ATTACHMENT_LOAD_OP_CLEAR,				// VkAttachmentLoadOp              loadOp
183 		VK_ATTACHMENT_STORE_OP_STORE,				// VkAttachmentStoreOp             storeOp
184 		VK_ATTACHMENT_LOAD_OP_DONT_CARE,			// VkAttachmentLoadOp              stencilLoadOp
185 		VK_ATTACHMENT_STORE_OP_DONT_CARE,			// VkAttachmentStoreOp             stencilStoreOp
186 		VK_IMAGE_LAYOUT_UNDEFINED,					// VkImageLayout                   initialLayout
187 		VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	// VkImageLayout                   finalLayout
188 	};
189 
190 	const VkAttachmentReference colorAttachmentRef = makeAttachmentReference(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
191 
192 	const VkSubpassDescription subpassDescription =
193 	{
194 		0u,									// VkSubpassDescriptionFlags       flags
195 		VK_PIPELINE_BIND_POINT_GRAPHICS,	// VkPipelineBindPoint             pipelineBindPoint
196 		0u,									// deUint32                        inputAttachmentCount
197 		nullptr,							// const VkAttachmentReference*    pInputAttachments
198 		1u,									// deUint32                        colorAttachmentCount
199 		&colorAttachmentRef,				// const VkAttachmentReference*    pColorAttachments
200 		nullptr,							// const VkAttachmentReference*    pResolveAttachments
201 		nullptr,							// const VkAttachmentReference*    pDepthStencilAttachment
202 		0u,									// deUint32                        preserveAttachmentCount
203 		nullptr								// const deUint32*                 pPreserveAttachments
204 	};
205 
206 	std::vector<VkSubpassDescription> subpassDescriptions;
207 
208 	subpassDescriptions.reserve(numSubpasses);
209 	for (uint32_t i = 0; i < numSubpasses; ++i)
210 		subpassDescriptions.push_back(subpassDescription);
211 
212 	std::vector<VkSubpassDependency> dependencies;
213 
214 	for (uint32_t subpassIdx = 1u; subpassIdx < numSubpasses; ++subpassIdx)
215 	{
216 		const uint32_t				prev		= subpassIdx - 1u;
217 		const VkSubpassDependency	colorDep	=
218 		{
219 			prev,																			//	deUint32				srcSubpass;
220 			subpassIdx,																		//	deUint32				dstSubpass;
221 			VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,									//	VkPipelineStageFlags	srcStageMask;
222 			VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,									//	VkPipelineStageFlags	dstStageMask;
223 			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,											//	VkAccessFlags			srcAccessMask;
224 			(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT),	//	VkAccessFlags			dstAccessMask;
225 			VK_DEPENDENCY_BY_REGION_BIT,													//	VkDependencyFlags		dependencyFlags;
226 		};
227 		dependencies.push_back(colorDep);
228 	}
229 
230 	using MultiviewInfoPtr = de::MovePtr<VkRenderPassMultiviewCreateInfo>;
231 
232 	MultiviewInfoPtr multiviewCreateInfo;
233 	std::vector<deUint32> viewMasks;
234 
235 	if (multiview)
236 	{
237 		multiviewCreateInfo		= MultiviewInfoPtr(new VkRenderPassMultiviewCreateInfo);
238 		*multiviewCreateInfo	= initVulkanStructure();
239 
240 		viewMasks.resize(subpassDescriptions.size());
241 		for (deUint32 subpassIdx = 0u; subpassIdx < static_cast<deUint32>(viewMasks.size()); ++subpassIdx)
242 			viewMasks[subpassIdx] = (1u << subpassIdx);
243 
244 		multiviewCreateInfo->subpassCount	= static_cast<deUint32>(viewMasks.size());
245 		multiviewCreateInfo->pViewMasks		= de::dataOrNull(viewMasks);
246 	}
247 
248 	const VkRenderPassCreateInfo renderPassInfo =
249 	{
250 		VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,				// VkStructureType                   sType
251 		multiviewCreateInfo.get(),								// const void*                       pNext
252 		0u,														// VkRenderPassCreateFlags           flags
253 		1u,														// deUint32                          attachmentCount
254 		&colorAttachmentDescription,							// const VkAttachmentDescription*    pAttachments
255 		static_cast<uint32_t>(subpassDescriptions.size()),		// deUint32                          subpassCount
256 		de::dataOrNull(subpassDescriptions),					// const VkSubpassDescription*       pSubpasses
257 		static_cast<uint32_t>(dependencies.size()),				// deUint32                          dependencyCount
258 		de::dataOrNull(dependencies),							// const VkSubpassDependency*        pDependencies
259 	};
260 
261 	return createRenderPass(vkd, device, &renderPassInfo);
262 }
263 
iterate()264 tcu::TestStatus MeshShaderBuiltinInstance::iterate ()
265 {
266 	const auto&		vkd			= m_context.getDeviceInterface();
267 	const auto		device		= m_context.getDevice();
268 	auto&			alloc		= m_context.getDefaultAllocator();
269 	const auto		queueIndex	= m_context.getUniversalQueueFamilyIndex();
270 	const auto		queue		= m_context.getUniversalQueue();
271 	const auto&		binaries	= m_context.getBinaryCollection();
272 
273 	const auto		useTask		= binaries.contains("task");
274 	const auto		useFrag		= binaries.contains("frag");
275 	const auto		extent		= makeExtent3D(m_params.colorExtent.width, m_params.colorExtent.height, 1u);
276 	const auto		iExtent3D	= tcu::IVec3(static_cast<int>(extent.width), static_cast<int>(extent.height), static_cast<int>(m_params.numLayers));
277 	const auto		format		= VK_FORMAT_R8G8B8A8_UNORM;
278 	const auto		tcuFormat	= mapVkFormat(format);
279 	const auto		colorUsage	= (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
280 	const auto		viewType	= ((m_params.numLayers > 1u) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D);
281 	const auto		colorSRR	= makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_params.numLayers);
282 	const auto		colorSRL	= makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, m_params.numLayers);
283 	const auto		numPasses	= (m_params.multiview ? m_params.numLayers : 1u);
284 	const tcu::Vec4	clearColor	= getClearColor();
285 
286 	ImageWithMemoryPtr	colorBuffer;
287 	Move<VkImageView>	colorBufferView;
288 	{
289 		const VkImageCreateInfo colorBufferInfo =
290 		{
291 			VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,	//	VkStructureType			sType;
292 			nullptr,								//	const void*				pNext;
293 			0u,										//	VkImageCreateFlags		flags;
294 			VK_IMAGE_TYPE_2D,						//	VkImageType				imageType;
295 			format,									//	VkFormat				format;
296 			extent,									//	VkExtent3D				extent;
297 			1u,										//	uint32_t				mipLevels;
298 			m_params.numLayers,						//	uint32_t				arrayLayers;
299 			VK_SAMPLE_COUNT_1_BIT,					//	VkSampleCountFlagBits	samples;
300 			VK_IMAGE_TILING_OPTIMAL,				//	VkImageTiling			tiling;
301 			colorUsage,								//	VkImageUsageFlags		usage;
302 			VK_SHARING_MODE_EXCLUSIVE,				//	VkSharingMode			sharingMode;
303 			0u,										//	uint32_t				queueFamilyIndexCount;
304 			nullptr,								//	const uint32_t*			pQueueFamilyIndices;
305 			VK_IMAGE_LAYOUT_UNDEFINED,				//	VkImageLayout			initialLayout;
306 		};
307 		colorBuffer = ImageWithMemoryPtr(new ImageWithMemory(vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any));
308 		colorBufferView = makeImageView(vkd, device, colorBuffer->get(), viewType, format, colorSRR);
309 	}
310 
311 	// Empty descriptor set layout.
312 	DescriptorSetLayoutBuilder layoutBuilder;
313 	const auto setLayout = layoutBuilder.build(vkd, device);
314 
315 	// Pipeline layout.
316 	const auto pipelineLayout = makePipelineLayout(vkd, device, setLayout.get());
317 
318 	// Render pass and framebuffer.
319 	const auto renderPass	= createCustomRenderPass(vkd, device, format, m_params.multiview, m_params.numLayers);
320 	const auto framebuffer	= makeFramebuffer(vkd, device, renderPass.get(), colorBufferView.get(), extent.width, extent.height, (m_params.multiview ? 1u : m_params.numLayers));
321 
322 	// Pipeline.
323 	Move<VkShaderModule> taskModule;
324 	Move<VkShaderModule> meshModule;
325 	Move<VkShaderModule> fragModule;
326 
327 	if (useTask)
328 		taskModule = createShaderModule(vkd, device, binaries.get("task"));
329 	if (useFrag)
330 		fragModule = createShaderModule(vkd, device, binaries.get("frag"));
331 	meshModule = createShaderModule(vkd, device, binaries.get("mesh"));
332 
333 	std::vector<VkViewport>	viewports;
334 	std::vector<VkRect2D>	scissors;
335 	if (m_params.viewports.empty())
336 	{
337 		// Default ones.
338 		viewports.push_back(makeViewport(extent));
339 		scissors.push_back(makeRect2D(extent));
340 	}
341 	else
342 	{
343 		// The desired viewports and the same number of default scissors.
344 		viewports.reserve(m_params.viewports.size());
345 		std::copy(begin(m_params.viewports), end(m_params.viewports), std::back_inserter(viewports));
346 		scissors.resize(viewports.size(), makeRect2D(extent));
347 	}
348 
349 	using ShadingRateInfoPtr = de::MovePtr<VkPipelineFragmentShadingRateStateCreateInfoKHR>;
350 	ShadingRateInfoPtr pNext;
351 	if (static_cast<bool>(m_params.fragmentSize))
352 	{
353 		pNext = ShadingRateInfoPtr(new VkPipelineFragmentShadingRateStateCreateInfoKHR);
354 		*pNext = initVulkanStructure();
355 
356 		pNext->fragmentSize		= getShadingRateSize(m_params.fragmentSize.get());
357 		pNext->combinerOps[0]	= VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR;
358 		pNext->combinerOps[1]	= VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR;
359 	}
360 
361 	// Pipelines.
362 	std::vector<Move<VkPipeline>> pipelines;
363 	pipelines.reserve(numPasses);
364 	for (deUint32 subpassIdx = 0u; subpassIdx < numPasses; ++subpassIdx)
365 	{
366 		pipelines.emplace_back(makeGraphicsPipeline(vkd, device, pipelineLayout.get(),
367 			taskModule.get(), meshModule.get(), fragModule.get(),
368 			renderPass.get(), viewports, scissors, subpassIdx,
369 			nullptr, nullptr, nullptr, nullptr, nullptr, 0u, pNext.get()));
370 	}
371 
372 	// Command pool and buffer.
373 	const auto cmdPool		= makeCommandPool(vkd, device, queueIndex);
374 	const auto cmdBufferPtr	= allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
375 	const auto cmdBuffer	= cmdBufferPtr.get();
376 
377 	// Indirect buffer if needed.
378 	BufferWithMemoryPtr indirectBuffer;
379 
380 	DE_ASSERT(!m_params.drawArgs.empty());
381 	if (m_params.indirect)
382 	{
383 		// Indirect draws.
384 		const auto indirectBufferSize	= static_cast<VkDeviceSize>(de::dataSize(m_params.drawArgs));
385 		const auto indirectBufferUsage	= (VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
386 		const auto indirectBufferInfo	= makeBufferCreateInfo(indirectBufferSize, indirectBufferUsage);
387 		indirectBuffer					= BufferWithMemoryPtr(new BufferWithMemory(vkd, device, alloc, indirectBufferInfo, MemoryRequirement::HostVisible));
388 		auto& indirectBufferAlloc		= indirectBuffer->getAllocation();
389 		void* indirectBufferData		= indirectBufferAlloc.getHostPtr();
390 
391 		deMemcpy(indirectBufferData, m_params.drawArgs.data(), static_cast<size_t>(indirectBufferSize));
392 		flushAlloc(vkd, device, indirectBufferAlloc);
393 	}
394 
395 	// Submit commands.
396 	beginCommandBuffer(vkd, cmdBuffer);
397 	beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0), clearColor);
398 
399 	for (uint32_t subpassIdx = 0u; subpassIdx < numPasses; ++subpassIdx)
400 	{
401 		if (subpassIdx > 0u)
402 			vkd.cmdNextSubpass(cmdBuffer, VK_SUBPASS_CONTENTS_INLINE);
403 
404 		vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[subpassIdx].get());
405 
406 		if (!m_params.indirect)
407 		{
408 			for (const auto& command : m_params.drawArgs)
409 				vkd.cmdDrawMeshTasksEXT(cmdBuffer, command.groupCountX, command.groupCountY, command.groupCountZ);
410 		}
411 		else
412 		{
413 			const auto numDraws	= static_cast<uint32_t>(m_params.drawArgs.size());
414 			const auto stride	= static_cast<uint32_t>(sizeof(decltype(m_params.drawArgs)::value_type));
415 			vkd.cmdDrawMeshTasksIndirectEXT(cmdBuffer, indirectBuffer->get(), 0ull, numDraws, stride);
416 		}
417 	}
418 
419 	endRenderPass(vkd, cmdBuffer);
420 
421 	// Output buffer to extract the color buffer contents.
422 	BufferWithMemoryPtr	outBuffer;
423 	void*				outBufferData	= nullptr;
424 	{
425 		const auto	layerSize			= static_cast<VkDeviceSize>(static_cast<uint32_t>(tcu::getPixelSize(tcuFormat)) * extent.width * extent.height);
426 		const auto	outBufferSize		= layerSize * m_params.numLayers;
427 		const auto	outBufferUsage		= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
428 		const auto	outBufferInfo		= makeBufferCreateInfo(outBufferSize, outBufferUsage);
429 
430 		outBuffer						= BufferWithMemoryPtr(new BufferWithMemory(vkd, device, alloc, outBufferInfo, MemoryRequirement::HostVisible));
431 		outBufferData					= outBuffer->getAllocation().getHostPtr();
432 	}
433 
434 	// Transition image layout.
435 	const auto preTransferBarrier = makeImageMemoryBarrier(
436 		(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), VK_ACCESS_TRANSFER_READ_BIT,
437 		VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
438 		colorBuffer->get(), colorSRR);
439 
440 	vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &preTransferBarrier);
441 
442 	// Copy image to output buffer.
443 	const std::vector<VkBufferImageCopy> regions (1u, makeBufferImageCopy(extent, colorSRL));
444 	vkd.cmdCopyImageToBuffer(cmdBuffer, colorBuffer->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, outBuffer->get(), static_cast<uint32_t>(regions.size()), de::dataOrNull(regions));
445 
446 	// Transfer to host barrier.
447 	const auto postTransferBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
448 	vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &postTransferBarrier, 0u, nullptr, 0u, nullptr);
449 
450 	endCommandBuffer(vkd, cmdBuffer);
451 	submitCommandsAndWait(vkd, device, queue, cmdBuffer);
452 
453 	// Invalidate alloc and verify result.
454 	{
455 		auto& outBufferAlloc = outBuffer->getAllocation();
456 		invalidateAlloc(vkd, device, outBufferAlloc);
457 
458 		tcu::ConstPixelBufferAccess	result (tcuFormat, iExtent3D, outBufferData);
459 		verifyResults(result);
460 	}
461 
462 	return tcu::TestStatus::pass("Pass");
463 }
464 
465 // Abstract case that implements the generic checkSupport method.
466 class MeshShaderBuiltinCase : public vkt::TestCase
467 {
468 public:
MeshShaderBuiltinCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool taskNeeded)469 					MeshShaderBuiltinCase	(tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded)
470 						: vkt::TestCase	(testCtx, name, description)
471 						, m_taskNeeded	(taskNeeded)
472 						{}
~MeshShaderBuiltinCase(void)473 	virtual			~MeshShaderBuiltinCase	(void) {}
474 
475 	void			checkSupport			(Context& context) const override;
476 
477 protected:
478 	const bool		m_taskNeeded;
479 };
480 
checkSupport(Context & context) const481 void MeshShaderBuiltinCase::checkSupport (Context& context) const
482 {
483 	checkTaskMeshShaderSupportEXT(context, m_taskNeeded, true);
484 }
485 
486 // Instance that verifies color layers.
487 class FullScreenColorInstance : public MeshShaderBuiltinInstance
488 {
489 public:
FullScreenColorInstance(Context & context,const IterationParams & params,const ColorVec & expectedColors)490 				FullScreenColorInstance		(Context& context, const IterationParams& params, const ColorVec& expectedColors)
491 					: MeshShaderBuiltinInstance (context, params)
492 					, m_expectedColors			(expectedColors)
493 					{}
~FullScreenColorInstance(void)494 	virtual		~FullScreenColorInstance	(void) {}
495 
496 	void		verifyResults				(const tcu::ConstPixelBufferAccess& result) override;
497 
498 protected:
499 	const ColorVec m_expectedColors;
500 };
501 
verifyResults(const tcu::ConstPixelBufferAccess & result)502 void FullScreenColorInstance::verifyResults (const tcu::ConstPixelBufferAccess& result)
503 {
504 	auto&		log		= m_context.getTestContext().getLog();
505 	bool		fail	= false;
506 	const auto	width	= result.getWidth();
507 	const auto	height	= result.getHeight();
508 	const auto	depth	= result.getDepth();
509 
510 	for (int z = 0; z < depth; ++z)
511 	{
512 		const auto& expected = m_expectedColors.at(z);
513 
514 		for (int y = 0; y < height; ++y)
515 		for (int x = 0; x < width; ++x)
516 		{
517 			const auto resultColor = result.getPixel(x, y, z);
518 			if (resultColor != expected)
519 			{
520 				std::ostringstream msg;
521 				msg << "Pixel (" << x << ", " << y << ", " << z << ") failed: expected " << expected << " and found " << resultColor;
522 				log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
523 				fail = true;
524 			}
525 		}
526 	}
527 
528 	if (fail)
529 	{
530 		log << tcu::TestLog::Image("Result", "", result);
531 		TCU_FAIL("Check log for details");
532 	}
533 }
534 
535 // Instance that verifies single-layer framebuffers divided into 4 quadrants.
536 class QuadrantsInstance : public MeshShaderBuiltinInstance
537 {
538 public:
QuadrantsInstance(Context & context,const IterationParams & params,const tcu::Vec4 topLeft,const tcu::Vec4 topRight,const tcu::Vec4 bottomLeft,const tcu::Vec4 bottomRight)539 				QuadrantsInstance	(Context& context, const IterationParams& params,
540 									 const tcu::Vec4 topLeft,
541 									 const tcu::Vec4 topRight,
542 									 const tcu::Vec4 bottomLeft,
543 									 const tcu::Vec4 bottomRight)
544 					: MeshShaderBuiltinInstance (context, params)
545 					, m_topLeft					(topLeft)
546 					, m_topRight				(topRight)
547 					, m_bottomLeft				(bottomLeft)
548 					, m_bottomRight				(bottomRight)
549 					{}
~QuadrantsInstance(void)550 	virtual		~QuadrantsInstance	(void) {}
551 
552 	void		verifyResults				(const tcu::ConstPixelBufferAccess& result) override;
553 
554 protected:
555 	const tcu::Vec4 m_topLeft;
556 	const tcu::Vec4 m_topRight;
557 	const tcu::Vec4 m_bottomLeft;
558 	const tcu::Vec4 m_bottomRight;
559 };
560 
verifyResults(const tcu::ConstPixelBufferAccess & result)561 void QuadrantsInstance::verifyResults (const tcu::ConstPixelBufferAccess& result)
562 {
563 	auto&		log		= m_context.getTestContext().getLog();
564 	bool		fail	= false;
565 	const auto	width	= result.getWidth();
566 	const auto	height	= result.getHeight();
567 	const auto	depth	= result.getDepth();
568 
569 	DE_ASSERT(depth == 1);
570 	DE_ASSERT(width > 0 && width % 2 == 0);
571 	DE_ASSERT(height > 0 && height % 2 == 0);
572 	DE_UNREF(depth); // For release builds.
573 
574 	const auto	halfWidth	= width / 2;
575 	const auto	halfHeight	= height / 2;
576 	tcu::Vec4	expected;
577 
578 	for (int y = 0; y < height; ++y)
579 	for (int x = 0; x < width; ++x)
580 	{
581 		// Choose the right quadrant
582 		if (y < halfHeight)
583 			expected = ((x < halfWidth) ? m_topLeft : m_topRight);
584 		else
585 			expected = ((x < halfWidth) ? m_bottomLeft : m_bottomRight);
586 
587 		const auto resultColor = result.getPixel(x, y);
588 		if (resultColor != expected)
589 		{
590 			std::ostringstream msg;
591 			msg << "Pixel (" << x << ", " << y  << ") failed: expected " << expected << " and found " << resultColor;
592 			log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
593 			fail = true;
594 		}
595 	}
596 
597 	if (fail)
598 	{
599 		log << tcu::TestLog::Image("Result", "", result);
600 		TCU_FAIL("Check log for details");
601 	}
602 }
603 
604 // Instance that verifies single-layer framebuffers with specific pixels set to some color.
605 struct PixelVerifierParams
606 {
607 	const tcu::Vec4		background;
608 	const PixelMap		pixelMap;
609 };
610 
611 class PixelsInstance : public MeshShaderBuiltinInstance
612 {
613 public:
PixelsInstance(Context & context,const IterationParams & params,const PixelVerifierParams & pixelParams)614 				PixelsInstance	(Context& context, const IterationParams& params, const PixelVerifierParams& pixelParams)
615 					: MeshShaderBuiltinInstance	(context, params)
616 					, m_pixelParams				(pixelParams)
617 					{}
~PixelsInstance(void)618 	virtual		~PixelsInstance	(void) {}
619 
620 	void		verifyResults	(const tcu::ConstPixelBufferAccess& result) override;
621 
622 protected:
623 	const PixelVerifierParams m_pixelParams;
624 };
625 
verifyResults(const tcu::ConstPixelBufferAccess & result)626 void PixelsInstance::verifyResults (const tcu::ConstPixelBufferAccess& result)
627 {
628 	auto&		log		= m_context.getTestContext().getLog();
629 	bool		fail	= false;
630 	const auto	width	= result.getWidth();
631 	const auto	height	= result.getHeight();
632 	const auto	depth	= result.getDepth();
633 
634 	DE_ASSERT(depth == 1);
635 	DE_UNREF(depth); // For release builds.
636 
637 	for (int y = 0; y < height; ++y)
638 	for (int x = 0; x < width; ++x)
639 	{
640 		const tcu::IVec2	coords		(x, y);
641 		const auto			iter		= m_pixelParams.pixelMap.find(coords);
642 		const auto			expected	= ((iter == m_pixelParams.pixelMap.end()) ? m_pixelParams.background : iter->second);
643 		const auto			resultColor	= result.getPixel(x, y);
644 
645 		if (resultColor != expected)
646 		{
647 			std::ostringstream msg;
648 			msg << "Pixel (" << x << ", " << y << ") failed: expected " << expected << " and found " << resultColor;
649 			log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
650 			fail = true;
651 		}
652 	}
653 
654 	if (fail)
655 	{
656 		log << tcu::TestLog::Image("Result", "", result);
657 		TCU_FAIL("Check log for details");
658 	}
659 }
660 
661 // Primitive ID case.
662 class PrimitiveIdCase : public MeshShaderBuiltinCase
663 {
664 public:
PrimitiveIdCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool glslFrag)665 					PrimitiveIdCase		(tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool glslFrag)
666 						: MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/)
667 						, m_glslFrag			(glslFrag)
668 						{}
~PrimitiveIdCase(void)669 	virtual			~PrimitiveIdCase	(void) {}
670 
671 	void			initPrograms		(vk::SourceCollections& programCollection) const override;
672 	void			checkSupport		(Context& context) const override;
673 	TestInstance*	createInstance		(Context& context) const override;
674 
675 protected:
676 	// Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID.
677 	const bool		m_glslFrag;
678 };
679 
initPrograms(vk::SourceCollections & programCollection) const680 void PrimitiveIdCase::initPrograms (vk::SourceCollections& programCollection) const
681 {
682 	const auto buildOptions		= getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
683 	const auto spvBuildOptions	= getMinMeshEXTSpvBuildOptions(programCollection.usedVulkanVersion);
684 
685 	// Mesh shader.
686 	{
687 		std::ostringstream mesh;
688 		mesh
689 			<< "#version 460\n"
690 			<< "#extension GL_EXT_mesh_shader : enable\n"
691 			<< "\n"
692 			<< "layout (local_size_x=1) in;\n"
693 			<< "layout (triangles) out;\n"
694 			<< "layout (max_vertices=3, max_primitives=1) out;\n"
695 			<< "\n"
696 			<< "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
697 			<< "   int gl_PrimitiveID;\n"
698 			<< "} gl_MeshPrimitivesEXT[];\n"
699 			<< "\n"
700 			<< "void main ()\n"
701 			<< "{\n"
702 			<< "    SetMeshOutputsEXT(3u, 1u);\n"
703 			<< "\n"
704 			<< "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 2u);\n"
705 			<< "\n"
706 			<< "    gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
707 			<< "    gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
708 			<< "    gl_MeshVerticesEXT[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
709 			<< "\n"
710 			// Sets an arbitrary primitive id.
711 			<< "    gl_MeshPrimitivesEXT[0].gl_PrimitiveID = 1629198956;\n"
712 			<< "}\n"
713 			;
714 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
715 	}
716 
717 	// Frag shader.
718 	if (m_glslFrag)
719 	{
720 		std::ostringstream frag;
721 		frag
722 			<< "#version 460\n"
723 			<< "#extension GL_EXT_mesh_shader : enable\n"
724 			<< "\n"
725 			<< "layout (location=0) out vec4 outColor;\n"
726 			<< "\n"
727 			<< "void main ()\n"
728 			<< "{\n"
729 			// Checks the primitive id matches.
730 			<< "    outColor = ((gl_PrimitiveID == 1629198956) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, 1.0));\n"
731 			<< "}\n"
732 			;
733 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
734 	}
735 	else
736 	{
737 		// This is the same shader as above, but OpCapability Geometry has been replaced by OpCapability MeshShadingEXT in order to
738 		// access gl_PrimitiveID. This also needs the SPV_EXT_mesh_shader extension.
739 		std::ostringstream frag;
740 		frag
741 			<< "; Version: 1.0\n"
742 			<< "; Generator: Khronos Glslang Reference Front End; 10\n"
743 			<< "; Bound: 24\n"
744 			<< "; Schema: 0\n"
745 			<< "      OpCapability Shader\n"
746 
747 			// Manual change in these lines.
748 			//<< "      OpCapability Geometry\n"
749 			<< "      OpCapability MeshShadingEXT\n"
750 			<< "      OpExtension \"SPV_EXT_mesh_shader\"\n"
751 
752 			<< " %1 = OpExtInstImport \"GLSL.std.450\"\n"
753 			<< "      OpMemoryModel Logical GLSL450\n"
754 			<< "      OpEntryPoint Fragment %4 \"main\" %9 %12\n"
755 			<< "      OpExecutionMode %4 OriginUpperLeft\n"
756 			<< "      OpDecorate %9 Location 0\n"
757 			<< "      OpDecorate %12 Flat\n"
758 			<< "      OpDecorate %12 BuiltIn PrimitiveId\n"
759 			<< " %2 = OpTypeVoid\n"
760 			<< " %3 = OpTypeFunction %2\n"
761 			<< " %6 = OpTypeFloat 32\n"
762 			<< " %7 = OpTypeVector %6 4\n"
763 			<< " %8 = OpTypePointer Output %7\n"
764 			<< " %9 = OpVariable %8 Output\n"
765 			<< "%10 = OpTypeInt 32 1\n"
766 			<< "%11 = OpTypePointer Input %10\n"
767 			<< "%12 = OpVariable %11 Input\n"
768 			<< "%14 = OpConstant %10 1629198956\n"
769 			<< "%15 = OpTypeBool\n"
770 			<< "%17 = OpConstant %6 0\n"
771 			<< "%18 = OpConstant %6 1\n"
772 			<< "%19 = OpConstantComposite %7 %17 %17 %18 %18\n"
773 			<< "%20 = OpConstantComposite %7 %17 %17 %17 %18\n"
774 			<< "%21 = OpTypeVector %15 4\n"
775 			<< " %4 = OpFunction %2 None %3\n"
776 			<< " %5 = OpLabel\n"
777 			<< "%13 = OpLoad %10 %12\n"
778 			<< "%16 = OpIEqual %15 %13 %14\n"
779 			<< "%22 = OpCompositeConstruct %21 %16 %16 %16 %16\n"
780 			<< "%23 = OpSelect %7 %22 %19 %20\n"
781 			<< "      OpStore %9 %23\n"
782 			<< "      OpReturn\n"
783 			<< "      OpFunctionEnd\n"
784 			;
785 		programCollection.spirvAsmSources.add("frag") << frag.str() << spvBuildOptions;
786 	}
787 }
788 
checkSupport(Context & context) const789 void PrimitiveIdCase::checkSupport (Context& context) const
790 {
791 	MeshShaderBuiltinCase::checkSupport(context);
792 
793 	// Fragment shader in GLSL means glslang will use the Geometry capability due to gl_PrimitiveID.
794 	if (m_glslFrag)
795 		context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
796 }
797 
createInstance(Context & context) const798 TestInstance* PrimitiveIdCase::createInstance (Context& context) const
799 {
800 	const ColorVec			expectedColors	(1u, tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
801 	const IterationParams	iterationParams	=
802 	{
803 		getDefaultExtent(),			//	VkExtent2D					colorExtent;
804 		1u,							//	uint32_t					numLayers;
805 		false,						//	bool						multiview;
806 		false,						//	bool						indirect;
807 		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
808 		getDefaultDrawCommands(),	//	DrawCommandVec				drawArgs;
809 		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
810 	};
811 	return new FullScreenColorInstance(context, iterationParams, expectedColors);
812 }
813 
814 // Layer builtin case.
815 class LayerCase : public MeshShaderBuiltinCase
816 {
817 public:
LayerCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool writeVal,bool shareVertices)818 					LayerCase		(tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool writeVal, bool shareVertices)
819 						: MeshShaderBuiltinCase	(testCtx, name, description, false/*taskNeeded*/)
820 						, m_shareVertices		(shareVertices)
821 						, m_writeVal			(writeVal)
822 						{}
~LayerCase(void)823 	virtual			~LayerCase	(void) {}
824 
825 	void			initPrograms	(vk::SourceCollections& programCollection) const override;
826 	void			checkSupport	(Context& context) const override;
827 	TestInstance*	createInstance	(Context& context) const override;
828 
829 	static constexpr uint32_t kNumLayers = 4u;
830 
831 protected:
832 	const bool m_shareVertices;
833 	const bool m_writeVal;
834 };
835 
initPrograms(vk::SourceCollections & programCollection) const836 void LayerCase::initPrograms (vk::SourceCollections& programCollection) const
837 {
838 	const auto buildOptions		= getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
839 	const auto localSize		= (m_shareVertices ? kNumLayers : 1u);
840 	const auto numPrimitives	= (m_shareVertices ? kNumLayers : 1u);
841 	const auto layerNumber		= (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x");
842 
843 	// One layer per local invocation or work group (shared vertices or not, respectively).
844 	{
845 		std::ostringstream mesh;
846 		mesh
847 			<< "#version 460\n"
848 			<< "#extension GL_EXT_mesh_shader : enable\n"
849 			<< "\n"
850 			<< "layout (local_size_x=" << localSize << ") in;\n"
851 			<< "layout (triangles) out;\n"
852 			<< "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n"
853 			<< "\n"
854 			<< "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
855 			<< "   int gl_Layer;\n"
856 			<< "} gl_MeshPrimitivesEXT[];\n"
857 			<< "\n"
858 			<< "void main ()\n"
859 			<< "{\n"
860 			<< "    SetMeshOutputsEXT(3u, " << numPrimitives << ");\n"
861 			<< "\n"
862 			<< "    if (gl_LocalInvocationIndex == 0u)\n"
863 			<< "    {\n"
864 			<< "        gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
865 			<< "        gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
866 			<< "        gl_MeshVerticesEXT[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
867 			<< "    }\n"
868 			<< "\n"
869 			<< "    gl_PrimitiveTriangleIndicesEXT[gl_LocalInvocationIndex] = uvec3(0, 1, 2);\n"
870 			;
871 
872 		if (m_writeVal)
873 			mesh << "    gl_MeshPrimitivesEXT[gl_LocalInvocationIndex].gl_Layer = int(" << layerNumber << ");\n";
874 
875 		mesh << "}\n";
876 
877 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
878 	}
879 
880 	// Fragment shader chooses one color per layer.
881 	{
882 		std::ostringstream frag;
883 		frag
884 			<< "#version 460\n"
885 			<< "#extension GL_EXT_mesh_shader : enable\n"
886 			<< "\n"
887 			<< "layout (location=0) out vec4 outColor;\n"
888 			<< "\n"
889 			<< "vec4 colors[" << kNumLayers << "] = vec4[](\n"
890 			<< "    vec4(0.0, 0.0, 1.0, 1.0),\n"
891 			<< "    vec4(1.0, 0.0, 1.0, 1.0),\n"
892 			<< "    vec4(0.0, 1.0, 1.0, 1.0),\n"
893 			<< "    vec4(1.0, 1.0, 0.0, 1.0)\n"
894 			<< ");\n"
895 			<< "\n"
896 			<< "void main ()\n"
897 			<< "{\n"
898 			<< "    outColor = colors[gl_Layer];\n"
899 			<< "}\n"
900 			;
901 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
902 	}
903 }
904 
checkSupport(Context & context) const905 void LayerCase::checkSupport (Context& context) const
906 {
907 	MeshShaderBuiltinCase::checkSupport(context);
908 
909 	if (!context.contextSupports(vk::ApiVersion(0u, 1u, 2u, 0u)))
910 		context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer");
911 	else
912 	{
913 		const auto& features = context.getDeviceVulkan12Features();
914 		if (!features.shaderOutputLayer)
915 			TCU_THROW(NotSupportedError, "shaderOutputLayer feature not supported");
916 	}
917 }
918 
createInstance(Context & context) const919 TestInstance* LayerCase::createInstance (Context& context) const
920 {
921 	ColorVec expectedColors;
922 
923 	const auto usedLayers		= (m_writeVal ? kNumLayers : 1u);
924 	const auto numWorkGroups	= (m_shareVertices ? 1u : kNumLayers);
925 
926 	expectedColors.reserve(usedLayers);
927 	expectedColors.push_back(tcu::Vec4(0.0, 0.0, 1.0, 1.0));
928 
929 	if (m_writeVal)
930 	{
931 		expectedColors.push_back(tcu::Vec4(1.0, 0.0, 1.0, 1.0));
932 		expectedColors.push_back(tcu::Vec4(0.0, 1.0, 1.0, 1.0));
933 		expectedColors.push_back(tcu::Vec4(1.0, 1.0, 0.0, 1.0));
934 	}
935 
936 	const IterationParams iterationParams =
937 	{
938 		getDefaultExtent(),						//	VkExtent2D					colorExtent;
939 		usedLayers,								//	uint32_t					numLayers;
940 		false,									//	bool						multiview;
941 		false,									//	bool						indirect;
942 		tcu::Nothing,							//	tcu::Maybe<FragmentSize>	fragmentSize;
943 		getDefaultDrawCommands(numWorkGroups),	//	DrawCommandVec				drawArgs;
944 		{},										//	ViewportVec					viewports;	// If empty, a single default viewport is used.
945 	};
946 	return new FullScreenColorInstance(context, iterationParams, expectedColors);
947 }
948 
949 // ViewportIndex builtin case.
950 class ViewportIndexCase : public MeshShaderBuiltinCase
951 {
952 public:
ViewportIndexCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool writeVal,bool shareVertices)953 					ViewportIndexCase	(tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool writeVal, bool shareVertices)
954 						: MeshShaderBuiltinCase	(testCtx, name, description, false/*taskNeeded*/)
955 						, m_shareVertices		(shareVertices)
956 						, m_writeVal			(writeVal)
957 						{}
~ViewportIndexCase(void)958 	virtual			~ViewportIndexCase	(void) {}
959 
960 	void			initPrograms		(vk::SourceCollections& programCollection) const override;
961 	void			checkSupport		(Context& context) const override;
962 	TestInstance*	createInstance		(Context& context) const override;
963 
964 	static constexpr uint32_t kQuadrants = 4u;
965 
966 protected:
967 	const bool m_shareVertices;
968 	const bool m_writeVal;
969 };
970 
initPrograms(vk::SourceCollections & programCollection) const971 void ViewportIndexCase::initPrograms (vk::SourceCollections& programCollection) const
972 {
973 	const auto buildOptions		= getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
974 	const auto localSize		= (m_shareVertices ? kQuadrants : 1u);
975 	const auto numPrimitives	= (m_shareVertices ? kQuadrants : 1u);
976 	const auto viewportIndex	= (m_shareVertices ? "gl_LocalInvocationIndex" : "gl_WorkGroupID.x");
977 
978 	// One viewport per local invocation or work group (sharing vertices or not, respectively).
979 	{
980 		std::ostringstream mesh;
981 		mesh
982 			<< "#version 460\n"
983 			<< "#extension GL_EXT_mesh_shader : enable\n"
984 			<< "\n"
985 			<< "layout (local_size_x=" << localSize << ") in;\n"
986 			<< "layout (triangles) out;\n"
987 			<< "layout (max_vertices=3, max_primitives=" << numPrimitives << ") out;\n"
988 			<< "\n"
989 			<< "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
990 			<< "   int gl_ViewportIndex;\n"
991 			<< "} gl_MeshPrimitivesEXT[];\n"
992 			<< "\n"
993 			<< "void main ()\n"
994 			<< "{\n"
995 			<< "    SetMeshOutputsEXT(3u, " << numPrimitives << ");\n"
996 			<< "\n"
997 			<< "    if (gl_LocalInvocationIndex == 0u)\n"
998 			<< "    {\n"
999 			<< "        gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1000 			<< "        gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
1001 			<< "        gl_MeshVerticesEXT[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
1002 			<< "    }\n"
1003 			<< "\n"
1004 			<< "    gl_PrimitiveTriangleIndicesEXT[gl_LocalInvocationIndex] = uvec3(0, 1, 2);\n"
1005 			;
1006 
1007 		if (m_writeVal)
1008 			mesh << "    gl_MeshPrimitivesEXT[gl_LocalInvocationIndex].gl_ViewportIndex = int(" << viewportIndex << ");\n";
1009 
1010 		mesh << "}\n";
1011 
1012 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1013 	}
1014 
1015 	// Fragment shader chooses one color per viewport.
1016 	{
1017 		std::ostringstream frag;
1018 		frag
1019 			<< "#version 460\n"
1020 			<< "#extension GL_EXT_mesh_shader : enable\n"
1021 			<< "\n"
1022 			<< "layout (location=0) out vec4 outColor;\n"
1023 			<< "\n"
1024 			<< "vec4 colors[" << kQuadrants << "] = vec4[](\n"
1025 			<< "    vec4(0.0, 0.0, 1.0, 1.0),\n"
1026 			<< "    vec4(1.0, 0.0, 1.0, 1.0),\n"
1027 			<< "    vec4(0.0, 1.0, 1.0, 1.0),\n"
1028 			<< "    vec4(1.0, 1.0, 0.0, 1.0)\n"
1029 			<< ");\n"
1030 			<< "\n"
1031 			<< "void main ()\n"
1032 			<< "{\n"
1033 			<< "    outColor = colors[gl_ViewportIndex];\n"
1034 			<< "}\n"
1035 			;
1036 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
1037 	}
1038 }
1039 
checkSupport(Context & context) const1040 void ViewportIndexCase::checkSupport (Context& context) const
1041 {
1042 	MeshShaderBuiltinCase::checkSupport(context);
1043 	context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_MULTI_VIEWPORT);
1044 
1045 	if (!context.contextSupports(vk::ApiVersion(0u, 1u, 2u, 0u)))
1046 		context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer");
1047 	else
1048 	{
1049 		const auto& features = context.getDeviceVulkan12Features();
1050 		if (!features.shaderOutputViewportIndex)
1051 			TCU_THROW(NotSupportedError, "shaderOutputViewportIndex feature not supported");
1052 	}
1053 }
1054 
createInstance(Context & context) const1055 TestInstance* ViewportIndexCase::createInstance (Context& context) const
1056 {
1057 	const auto extent = getDefaultExtent();
1058 
1059 	DE_ASSERT(extent.width > 0u && extent.width % 2u == 0u);
1060 	DE_ASSERT(extent.height > 0u && extent.height % 2u == 0u);
1061 
1062 	const auto halfWidth	= static_cast<float>(extent.width) / 2.0f;
1063 	const auto halfHeight	= static_cast<float>(extent.height) / 2.0f;
1064 
1065 	const auto topLeft		= tcu::Vec4(0.0, 0.0, 1.0, 1.0);
1066 	const auto topRight		= (m_writeVal ? tcu::Vec4(1.0, 0.0, 1.0, 1.0) : getClearColor());
1067 	const auto bottomLeft	= (m_writeVal ? tcu::Vec4(0.0, 1.0, 1.0, 1.0) : getClearColor());
1068 	const auto bottomRight	= (m_writeVal ? tcu::Vec4(1.0, 1.0, 0.0, 1.0) : getClearColor());
1069 
1070 	ViewportVec viewports;
1071 	viewports.reserve(kQuadrants);
1072 	viewports.emplace_back(makeViewport(0.0f,		0.0f,		halfWidth, halfHeight, 0.0f, 1.0f));
1073 	viewports.emplace_back(makeViewport(halfWidth,	0.0f,		halfWidth, halfHeight, 0.0f, 1.0f));
1074 	viewports.emplace_back(makeViewport(0.0f,		halfHeight,	halfWidth, halfHeight, 0.0f, 1.0f));
1075 	viewports.emplace_back(makeViewport(halfWidth,	halfHeight,	halfWidth, halfHeight, 0.0f, 1.0f));
1076 
1077 	const auto numWorkGroups = (m_shareVertices ? 1u : kQuadrants);
1078 	const IterationParams iterationParams =
1079 	{
1080 		getDefaultExtent(),						//	VkExtent2D					colorExtent;
1081 		1u,										//	uint32_t					numLayers;
1082 		false,									//	bool						multiview;
1083 		false,									//	bool						indirect;
1084 		tcu::Nothing,							//	tcu::Maybe<FragmentSize>	fragmentSize;
1085 		getDefaultDrawCommands(numWorkGroups),	//	DrawCommandVec				drawArgs;
1086 		std::move(viewports),					//	ViewportVec					viewports;
1087 	};
1088 	return new QuadrantsInstance(context, iterationParams, topLeft, topRight, bottomLeft, bottomRight);
1089 }
1090 
1091 // Position builtin case.
1092 class PositionCase : public MeshShaderBuiltinCase
1093 {
1094 public:
PositionCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)1095 					PositionCase		(tcu::TestContext& testCtx, const std::string& name, const std::string& description)
1096 						: MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/)
1097 						{}
~PositionCase(void)1098 	virtual			~PositionCase	(void) {}
1099 
1100 	void			initPrograms		(vk::SourceCollections& programCollection) const override;
1101 	TestInstance*	createInstance		(Context& context) const override;
1102 };
1103 
initPrograms(vk::SourceCollections & programCollection) const1104 void PositionCase::initPrograms (vk::SourceCollections& programCollection) const
1105 {
1106 	const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1107 
1108 	// Mesh shader: emit single triangle around the center of the top left pixel.
1109 	{
1110 		const auto extent	= getDefaultExtent();
1111 		const auto fWidth	= static_cast<float>(extent.width);
1112 		const auto fHeight	= static_cast<float>(extent.height);
1113 
1114 		const auto pxWidth	= 2.0f / fWidth;
1115 		const auto pxHeight = 2.0f / fHeight;
1116 
1117 		const auto halfXPix	= pxWidth / 2.0f;
1118 		const auto halfYPix	= pxHeight / 2.0f;
1119 
1120 		// Center of top left pixel.
1121 		const auto x		= -1.0f + halfXPix;
1122 		const auto y		= -1.0f + halfYPix;
1123 
1124 		std::ostringstream mesh;
1125 		mesh
1126 			<< "#version 460\n"
1127 			<< "#extension GL_EXT_mesh_shader : enable\n"
1128 			<< "\n"
1129 			<< "layout (local_size_x=1) in;\n"
1130 			<< "layout (triangles) out;\n"
1131 			<< "layout (max_vertices=3, max_primitives=1) out;\n"
1132 			<< "\n"
1133 			<< "void main ()\n"
1134 			<< "{\n"
1135 			<< "    SetMeshOutputsEXT(3u, 1u);\n"
1136 			<< "\n"
1137 			<< "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 2u);\n"
1138 			<< "\n"
1139 			<< "    gl_MeshVerticesEXT[0].gl_Position = vec4(" << (x - halfXPix) << ", " << (y + halfYPix) << ", 0.0, 1.0);\n"
1140 			<< "    gl_MeshVerticesEXT[1].gl_Position = vec4(" << (x + halfXPix) << ", " << (y + halfYPix) << ", 0.0, 1.0);\n"
1141 			<< "    gl_MeshVerticesEXT[2].gl_Position = vec4(" << x << ", " << (y - halfYPix) << ", 0.0, 1.0);\n"
1142 			<< "}\n"
1143 			;
1144 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1145 	}
1146 
1147 	// Basic fragment shader.
1148 	{
1149 		const auto frag = getBasicFragShader();
1150 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1151 	}
1152 }
1153 
createInstance(Context & context) const1154 TestInstance* PositionCase::createInstance (Context& context) const
1155 {
1156 	const IterationParams iterationParams =
1157 	{
1158 		getDefaultExtent(),			//	VkExtent2D					colorExtent;
1159 		1u,							//	uint32_t					numLayers;
1160 		false,						//	bool						multiview;
1161 		false,						//	bool						indirect;
1162 		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
1163 		getDefaultDrawCommands(),	//	DrawCommandVec				drawArgs;
1164 		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1165 	};
1166 
1167 	// Must match the shader.
1168 	PixelMap pixelMap;
1169 	pixelMap[tcu::IVec2(0, 0)] = tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f);
1170 
1171 	const PixelVerifierParams verifierParams =
1172 	{
1173 		getClearColor(),					//	const tcu::Vec4		background;
1174 		std::move(pixelMap),				//	const PixelMap		pixelMap;
1175 	};
1176 	return new PixelsInstance(context, iterationParams, verifierParams);
1177 }
1178 
1179 // PointSize builtin case.
1180 class PointSizeCase : public MeshShaderBuiltinCase
1181 {
1182 public:
PointSizeCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)1183 					PointSizeCase		(tcu::TestContext& testCtx, const std::string& name, const std::string& description)
1184 						: MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/)
1185 						{}
~PointSizeCase(void)1186 	virtual			~PointSizeCase	(void) {}
1187 
1188 	void			initPrograms		(vk::SourceCollections& programCollection) const override;
1189 	TestInstance*	createInstance		(Context& context) const override;
1190 	void			checkSupport		(Context& context) const override;
1191 
1192 	static constexpr float kPointSize = 4.0f;
1193 };
1194 
initPrograms(vk::SourceCollections & programCollection) const1195 void PointSizeCase::initPrograms (vk::SourceCollections& programCollection) const
1196 {
1197 	const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1198 
1199 	// Mesh shader: large point covering the top left quadrant.
1200 	{
1201 		std::ostringstream mesh;
1202 		mesh
1203 			<< "#version 460\n"
1204 			<< "#extension GL_EXT_mesh_shader : enable\n"
1205 			<< "\n"
1206 			<< "layout (local_size_x=1) in;\n"
1207 			<< "layout (points) out;\n"
1208 			<< "layout (max_vertices=1, max_primitives=1) out;\n"
1209 			<< "\n"
1210 			<< "void main ()\n"
1211 			<< "{\n"
1212 			<< "    SetMeshOutputsEXT(1u, 1u);\n"
1213 			<< "\n"
1214 			<< "    gl_PrimitivePointIndicesEXT[0] = 0u;\n"
1215 			<< "\n"
1216 			<< "    gl_MeshVerticesEXT[0].gl_Position = vec4(-0.5, -0.5, 0.0, 1.0);\n"
1217 			<< "    gl_MeshVerticesEXT[0].gl_PointSize = " << kPointSize << ";\n"
1218 			<< "}\n"
1219 			;
1220 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1221 	}
1222 
1223 	// Basic fragment shader.
1224 	{
1225 		const auto frag = getBasicFragShader();
1226 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1227 	}
1228 }
1229 
createInstance(Context & context) const1230 TestInstance* PointSizeCase::createInstance (Context& context) const
1231 {
1232 	const IterationParams iterationParams =
1233 	{
1234 		getDefaultExtent(),			//	VkExtent2D					colorExtent;
1235 		1u,							//	uint32_t					numLayers;
1236 		false,						//	bool						multiview;
1237 		false,						//	bool						indirect;
1238 		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
1239 		getDefaultDrawCommands(),	//	DrawCommandVec				drawArgs;
1240 		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1241 	};
1242 
1243 	// Must match the shader.
1244 	const tcu::Vec4 black	= getClearColor();
1245 	const tcu::Vec4 blue	(0.0f, 0.0f, 1.0f, 1.0f);
1246 
1247 	return new QuadrantsInstance(context, iterationParams, blue, black, black, black);
1248 }
1249 
checkSupport(Context & context) const1250 void PointSizeCase::checkSupport (Context& context) const
1251 {
1252 	MeshShaderBuiltinCase::checkSupport(context);
1253 	context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_LARGE_POINTS);
1254 
1255 	const auto& properties = context.getDeviceProperties();
1256 	if (kPointSize < properties.limits.pointSizeRange[0] || kPointSize > properties.limits.pointSizeRange[1])
1257 		TCU_THROW(NotSupportedError, "Required point size outside point size range");
1258 }
1259 
1260 // ClipDistance builtin case.
1261 class ClipDistanceCase : public MeshShaderBuiltinCase
1262 {
1263 public:
ClipDistanceCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)1264 					ClipDistanceCase		(tcu::TestContext& testCtx, const std::string& name, const std::string& description)
1265 						: MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/)
1266 						{}
~ClipDistanceCase(void)1267 	virtual			~ClipDistanceCase	(void) {}
1268 
1269 	void			initPrograms		(vk::SourceCollections& programCollection) const override;
1270 	TestInstance*	createInstance		(Context& context) const override;
1271 	void			checkSupport		(Context& context) const override;
1272 };
1273 
initPrograms(vk::SourceCollections & programCollection) const1274 void ClipDistanceCase::initPrograms (vk::SourceCollections& programCollection) const
1275 {
1276 	const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1277 
1278 	// Mesh shader: full-screen quad using different clip distances.
1279 	{
1280 		std::ostringstream mesh;
1281 		mesh
1282 			<< "#version 460\n"
1283 			<< "#extension GL_EXT_mesh_shader : enable\n"
1284 			<< "\n"
1285 			<< "layout (local_size_x=1) in;\n"
1286 			<< "layout (triangles) out;\n"
1287 			<< "layout (max_vertices=4, max_primitives=2) out;\n"
1288 			<< "\n"
1289 			<< "out gl_MeshPerVertexEXT {\n"
1290 			<< "    vec4  gl_Position;\n"
1291 			<< "    float gl_ClipDistance[2];\n"
1292 			<< "} gl_MeshVerticesEXT[];\n"
1293 			<< "\n"
1294 			<< "void main ()\n"
1295 			<< "{\n"
1296 			<< "    SetMeshOutputsEXT(4u, 2u);\n"
1297 			<< "\n"
1298 			<< "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 2u);\n"
1299 			<< "    gl_PrimitiveTriangleIndicesEXT[1] = uvec3(1u, 3u, 2u);\n"
1300 			<< "\n"
1301 			<< "    gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1302 			<< "    gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0,  1.0, 0.0, 1.0);\n"
1303 			<< "    gl_MeshVerticesEXT[2].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n"
1304 			<< "    gl_MeshVerticesEXT[3].gl_Position = vec4( 1.0,  1.0, 0.0, 1.0);\n"
1305 			<< "\n"
1306 			// The first clip plane keeps the left half of the frame buffer.
1307 			<< "    gl_MeshVerticesEXT[0].gl_ClipDistance[0] =  1.0;\n"
1308 			<< "    gl_MeshVerticesEXT[1].gl_ClipDistance[0] =  1.0;\n"
1309 			<< "    gl_MeshVerticesEXT[2].gl_ClipDistance[0] = -1.0;\n"
1310 			<< "    gl_MeshVerticesEXT[3].gl_ClipDistance[0] = -1.0;\n"
1311 			<< "\n"
1312 			// The second clip plane keeps the top half of the frame buffer.
1313 			<< "    gl_MeshVerticesEXT[0].gl_ClipDistance[1] =  1.0;\n"
1314 			<< "    gl_MeshVerticesEXT[1].gl_ClipDistance[1] = -1.0;\n"
1315 			<< "    gl_MeshVerticesEXT[2].gl_ClipDistance[1] =  1.0;\n"
1316 			<< "    gl_MeshVerticesEXT[3].gl_ClipDistance[1] = -1.0;\n"
1317 			<< "}\n"
1318 			;
1319 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1320 	}
1321 
1322 	// Fragment shader chooses a constant color.
1323 	{
1324 		std::ostringstream frag;
1325 		frag
1326 			<< "#version 460\n"
1327 			<< "#extension GL_EXT_mesh_shader : enable\n"
1328 			<< "\n"
1329 			<< "layout (location=0) out vec4 outColor;\n"
1330 			<< "\n"
1331 			<< "void main ()\n"
1332 			<< "{\n"
1333 			// White color should not actually be used, as those fragments are supposed to be discarded.
1334 			<< "    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"
1335 			<< "}\n"
1336 			;
1337 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
1338 	}
1339 }
1340 
createInstance(Context & context) const1341 TestInstance* ClipDistanceCase::createInstance (Context& context) const
1342 {
1343 	const IterationParams iterationParams =
1344 	{
1345 		getDefaultExtent(),			//	VkExtent2D					colorExtent;
1346 		1u,							//	uint32_t					numLayers;
1347 		false,						//	bool						multiview;
1348 		false,						//	bool						indirect;
1349 		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
1350 		getDefaultDrawCommands(),	//	DrawCommandVec				drawArgs;
1351 		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1352 	};
1353 
1354 	// Must match the shader.
1355 	const tcu::Vec4 black	= getClearColor();
1356 	const tcu::Vec4 blue	(0.0f, 0.0f, 1.0f, 1.0f);
1357 
1358 	return new QuadrantsInstance(context, iterationParams, blue, black, black, black);
1359 }
1360 
checkSupport(Context & context) const1361 void ClipDistanceCase::checkSupport (Context& context) const
1362 {
1363 	MeshShaderBuiltinCase::checkSupport(context);
1364 	context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CLIP_DISTANCE);
1365 }
1366 
1367 // CullDistance builtin case.
1368 class CullDistanceCase : public MeshShaderBuiltinCase
1369 {
1370 public:
CullDistanceCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)1371 					CullDistanceCase		(tcu::TestContext& testCtx, const std::string& name, const std::string& description)
1372 						: MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/)
1373 						{}
~CullDistanceCase(void)1374 	virtual			~CullDistanceCase	(void) {}
1375 
1376 	void			initPrograms		(vk::SourceCollections& programCollection) const override;
1377 	TestInstance*	createInstance		(Context& context) const override;
1378 	void			checkSupport		(Context& context) const override;
1379 };
1380 
initPrograms(vk::SourceCollections & programCollection) const1381 void CullDistanceCase::initPrograms (vk::SourceCollections& programCollection) const
1382 {
1383 	const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1384 
1385 	// Mesh shader: two quads covering the whole screen, one on top of the other.
1386 	// Use cull distances to discard the bottom quad.
1387 	// Use cull distances to paint the top one in two colors: blue on the left, white on the right.
1388 	{
1389 		std::ostringstream mesh;
1390 		mesh
1391 			<< "#version 460\n"
1392 			<< "#extension GL_EXT_mesh_shader : enable\n"
1393 			<< "\n"
1394 			<< "layout (local_size_x=1) in;\n"
1395 			<< "layout (triangles) out;\n"
1396 			<< "layout (max_vertices=6, max_primitives=4) out;\n"
1397 			<< "\n"
1398 			<< "out gl_MeshPerVertexEXT {\n"
1399 			<< "    vec4  gl_Position;\n"
1400 			<< "    float gl_CullDistance[2];\n"
1401 			<< "} gl_MeshVerticesEXT[];\n"
1402 			<< "\n"
1403 			<< "void main ()\n"
1404 			<< "{\n"
1405 			<< "    SetMeshOutputsEXT(6u, 4u);\n"
1406 			<< "\n"
1407 			<< "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 3u);\n"
1408 			<< "    gl_PrimitiveTriangleIndicesEXT[1] = uvec3(1u, 4u, 3u);\n"
1409 			<< "    gl_PrimitiveTriangleIndicesEXT[2] = uvec3(1u, 2u, 4u);\n"
1410 			<< "    gl_PrimitiveTriangleIndicesEXT[3] = uvec3(2u, 5u, 4u);\n"
1411 			<< "\n"
1412 			<< "    gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
1413 			<< "    gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0,  0.0, 0.0, 1.0);\n"
1414 			<< "    gl_MeshVerticesEXT[2].gl_Position = vec4(-1.0,  1.0, 0.0, 1.0);\n"
1415 			<< "    gl_MeshVerticesEXT[3].gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n"
1416 			<< "    gl_MeshVerticesEXT[4].gl_Position = vec4( 1.0,  0.0, 0.0, 1.0);\n"
1417 			<< "    gl_MeshVerticesEXT[5].gl_Position = vec4( 1.0,  1.0, 0.0, 1.0);\n"
1418 			<< "\n"
1419 			// The first cull plane discards the bottom quad
1420 			<< "    gl_MeshVerticesEXT[0].gl_CullDistance[0] =  1.0;\n"
1421 			<< "    gl_MeshVerticesEXT[1].gl_CullDistance[0] = -1.0;\n"
1422 			<< "    gl_MeshVerticesEXT[2].gl_CullDistance[0] = -2.0;\n"
1423 			<< "    gl_MeshVerticesEXT[3].gl_CullDistance[0] =  1.0;\n"
1424 			<< "    gl_MeshVerticesEXT[4].gl_CullDistance[0] = -1.0;\n"
1425 			<< "    gl_MeshVerticesEXT[5].gl_CullDistance[0] = -2.0;\n"
1426 			<< "\n"
1427 			// The second cull plane helps paint left and right different.
1428 			<< "    gl_MeshVerticesEXT[0].gl_CullDistance[1] =  1.0;\n"
1429 			<< "    gl_MeshVerticesEXT[1].gl_CullDistance[1] =  1.0;\n"
1430 			<< "    gl_MeshVerticesEXT[2].gl_CullDistance[1] =  1.0;\n"
1431 			<< "    gl_MeshVerticesEXT[3].gl_CullDistance[1] = -1.0;\n"
1432 			<< "    gl_MeshVerticesEXT[4].gl_CullDistance[1] = -1.0;\n"
1433 			<< "    gl_MeshVerticesEXT[5].gl_CullDistance[1] = -1.0;\n"
1434 			<< "}\n"
1435 			;
1436 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1437 	}
1438 
1439 	// Fragment shader chooses color based on the second cull distance.
1440 	{
1441 		std::ostringstream frag;
1442 		frag
1443 			<< "#version 460\n"
1444 			<< "#extension GL_EXT_mesh_shader : enable\n"
1445 			<< "\n"
1446 			<< "layout (location=0) out vec4 outColor;\n"
1447 			<< "\n"
1448 			<< "void main ()\n"
1449 			<< "{\n"
1450 			<< "    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"
1451 			<< "}\n"
1452 			;
1453 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
1454 	}
1455 }
1456 
createInstance(Context & context) const1457 TestInstance* CullDistanceCase::createInstance (Context& context) const
1458 {
1459 	const IterationParams iterationParams =
1460 	{
1461 		getDefaultExtent(),			//	VkExtent2D					colorExtent;
1462 		1u,							//	uint32_t					numLayers;
1463 		false,						//	bool						multiview;
1464 		false,						//	bool						indirect;
1465 		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
1466 		getDefaultDrawCommands(),	//	DrawCommandVec				drawArgs;
1467 		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1468 	};
1469 
1470 	// Must match the shader.
1471 	const tcu::Vec4 black	= getClearColor();
1472 	const tcu::Vec4 blue	(0.0f, 0.0f, 1.0f, 1.0f);
1473 	const tcu::Vec4 white	(1.0f, 1.0f, 1.0f, 1.0f);
1474 
1475 	return new QuadrantsInstance(context, iterationParams, blue, white, black, black);
1476 }
1477 
checkSupport(Context & context) const1478 void CullDistanceCase::checkSupport (Context& context) const
1479 {
1480 	MeshShaderBuiltinCase::checkSupport(context);
1481 	context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_CULL_DISTANCE);
1482 }
1483 
1484 // Generates statements to draw a triangle around the given pixel number, knowing the framebuffer width (len).
1485 // Supposes the height of the framebuffer is 1.
triangleForPixel(const std::string & pixel,const std::string & len,const std::string & primitiveIndex)1486 std::string triangleForPixel(const std::string& pixel, const std::string& len, const std::string& primitiveIndex)
1487 {
1488 	std::ostringstream statements;
1489 	statements
1490 		<< "    const float imgWidth = float(" << len << ");\n"
1491 		<< "    const float pixWidth = (2.0 / imgWidth);\n"
1492 		<< "    const float halfPix  = (pixWidth / 2.0);\n"
1493 		<< "    const float xCenter  = (((float(" << pixel << ") + 0.5) / imgWidth) * 2.0 - 1.0);\n"
1494 		<< "    const float xLeft    = (xCenter - halfPix);\n"
1495 		<< "    const float xRight   = (xCenter + halfPix);\n"
1496 		<< "    const uint  vindex   = (" << primitiveIndex << " * 3u);\n"
1497 		<< "    const uvec3 indices  = uvec3(vindex + 0, vindex + 1, vindex + 2);\n"
1498 		<< "\n"
1499 		<< "    gl_PrimitiveTriangleIndicesEXT[" << primitiveIndex << "] = indices;\n"
1500 		<< "\n"
1501 		<< "    gl_MeshVerticesEXT[indices.x].gl_Position = vec4(xLeft,    0.5, 0.0, 1.0);\n"
1502 		<< "    gl_MeshVerticesEXT[indices.y].gl_Position = vec4(xRight,   0.5, 0.0, 1.0);\n"
1503 		<< "    gl_MeshVerticesEXT[indices.z].gl_Position = vec4(xCenter, -0.5, 0.0, 1.0);\n"
1504 		;
1505 	return statements.str();
1506 }
1507 
1508 // WorkGroupID builtin case.
1509 class WorkGroupIdCase : public MeshShaderBuiltinCase
1510 {
1511 public:
WorkGroupIdCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool taskNeeded)1512 					WorkGroupIdCase		(tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded)
1513 						: MeshShaderBuiltinCase (testCtx, name, description, taskNeeded)
1514 						, m_extent				(getLinearExtent())
1515 						{}
~WorkGroupIdCase(void)1516 	virtual			~WorkGroupIdCase	(void) {}
1517 
1518 	void			initPrograms		(vk::SourceCollections& programCollection) const override;
1519 	TestInstance*	createInstance		(Context& context) const override;
1520 
1521 protected:
1522 	const VkExtent2D m_extent;
1523 };
1524 
initPrograms(vk::SourceCollections & programCollection) const1525 void WorkGroupIdCase::initPrograms (vk::SourceCollections& programCollection) const
1526 {
1527 	const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1528 
1529 	const std::string taskDataDecl =
1530 		"struct TaskData {\n"
1531 		"    uint id;\n"
1532 		"    uint size;\n"
1533 		"};\n"
1534 		"taskPayloadSharedEXT TaskData td;\n"
1535 		;
1536 
1537 	// Mesh shader: each work group fills one pixel.
1538 	{
1539 		const std::string pixel = (m_taskNeeded ? "td.id"   : "gl_WorkGroupID.x"           );
1540 		const std::string len   = (m_taskNeeded ? "td.size" : de::toString(m_extent.width) );
1541 
1542 		std::ostringstream mesh;
1543 		mesh
1544 			<< "#version 460\n"
1545 			<< "#extension GL_EXT_mesh_shader : enable\n"
1546 			<< "\n"
1547 			<< "layout (local_size_x=1) in;\n"
1548 			<< "layout (triangles) out;\n"
1549 			<< "layout (max_vertices=3, max_primitives=1) out;\n"
1550 			<< "\n"
1551 			<< (m_taskNeeded ? taskDataDecl : "")
1552 			<< "\n"
1553 			<< "void main ()\n"
1554 			<< "{\n"
1555 			<< "    SetMeshOutputsEXT(3u, 1u);\n"
1556 			<< "\n"
1557 			<< triangleForPixel(pixel, len, "0")
1558 			<< "}\n"
1559 			;
1560 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1561 	}
1562 
1563 	if (m_taskNeeded)
1564 	{
1565 		std::ostringstream task;
1566 		task
1567 			<< "#version 460\n"
1568 			<< "#extension GL_EXT_mesh_shader : enable\n"
1569 			<< "\n"
1570 			<< "layout (local_size_x=1) in;\n"
1571 			<< "\n"
1572 			<< taskDataDecl
1573 			<< "\n"
1574 			<< "void main ()\n"
1575 			<< "{\n"
1576 			<< "    td.id          = gl_WorkGroupID.x;\n"
1577 			<< "    td.size        = " << m_extent.width << ";\n"
1578 			<< "    EmitMeshTasksEXT(1u, 1u, 1u);\n"
1579 			<< "}\n"
1580 			;
1581 		programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
1582 	}
1583 
1584 	// Basic fragment shader.
1585 	{
1586 		const auto frag = getBasicFragShader();
1587 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1588 	}
1589 }
1590 
createInstance(Context & context) const1591 TestInstance* WorkGroupIdCase::createInstance (Context& context) const
1592 {
1593 	// Must match the shader.
1594 	const ColorVec			expectedColors	(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1595 	const IterationParams	iterationParams	=
1596 	{
1597 		m_extent,								//	VkExtent2D					colorExtent;
1598 		1u,										//	uint32_t					numLayers;
1599 		false,									//	bool						multiview;
1600 		false,									//	bool						indirect;
1601 		tcu::Nothing,							//	tcu::Maybe<FragmentSize>	fragmentSize;
1602 		getDefaultDrawCommands(m_extent.width),	//	DrawCommandVec				drawArgs;
1603 		{},										//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1604 	};
1605 	return new FullScreenColorInstance(context, iterationParams, expectedColors);
1606 }
1607 
1608 // Variable to use.
1609 enum class LocalInvocation { ID=0, INDEX };
1610 
1611 // LocalInvocationId and LocalInvocationIndex builtin cases. These are also used to test WorkGroupSize.
1612 class LocalInvocationCase : public MeshShaderBuiltinCase
1613 {
1614 public:
LocalInvocationCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool taskNeeded,LocalInvocation variable)1615 					LocalInvocationCase		(tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded, LocalInvocation variable)
1616 						: MeshShaderBuiltinCase (testCtx, name, description, taskNeeded)
1617 						, m_extent				(getLinearExtent())
1618 						, m_variable			(variable)
1619 						{}
~LocalInvocationCase(void)1620 	virtual			~LocalInvocationCase	(void) {}
1621 
1622 	void			initPrograms			(vk::SourceCollections& programCollection) const override;
1623 	TestInstance*	createInstance			(Context& context) const override;
1624 
1625 protected:
1626 	const VkExtent2D      m_extent;
1627 	const LocalInvocation m_variable;
1628 };
1629 
initPrograms(vk::SourceCollections & programCollection) const1630 void LocalInvocationCase::initPrograms (vk::SourceCollections& programCollection) const
1631 {
1632 	const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1633 
1634 	// Invocation index to use.
1635 	const std::string localIndex = ((m_variable == LocalInvocation::ID) ? "gl_LocalInvocationID.x" : "gl_LocalInvocationIndex");
1636 
1637 	// Task data.
1638 	std::ostringstream taskDataDecl;
1639 	taskDataDecl
1640 		<< "struct TaskData {\n"
1641 		// indexNumber[x] == x
1642 		<< "    uint indexNumber[" << m_extent.width << "];\n"
1643 		<< "    uint size;\n"
1644 		<< "};\n"
1645 		<< "taskPayloadSharedEXT TaskData td;\n"
1646 		;
1647 	const auto taskDataDeclStr = taskDataDecl.str();
1648 
1649 	// Mesh shader: each work group fills one pixel.
1650 	{
1651 		const std::string pixel          = (m_taskNeeded ? "td.indexNumber[gl_WorkGroupID.x]" : localIndex);
1652 		const std::string len            = (m_taskNeeded ? "td.size" : "gl_WorkGroupSize.x");
1653 		const auto        localSize      = (m_taskNeeded ? 1u : m_extent.width);
1654 		const auto        maxVert        = localSize * 3u;
1655 		const std::string primitiveIndex = (m_taskNeeded ? "0" : localIndex);
1656 
1657 		std::ostringstream mesh;
1658 		mesh
1659 			<< "#version 460\n"
1660 			<< "#extension GL_EXT_mesh_shader : enable\n"
1661 			<< "\n"
1662 			<< "layout (local_size_x=" << localSize << ") in;\n"
1663 			<< "layout (triangles) out;\n"
1664 			<< "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n"
1665 			<< "\n"
1666 			<< (m_taskNeeded ? taskDataDeclStr : "")
1667 			<< "\n"
1668 			<< "void main ()\n"
1669 			<< "{\n"
1670 			<< "    SetMeshOutputsEXT(" << maxVert << ", " << localSize << ");\n"
1671 			<< "\n"
1672 			<< triangleForPixel(pixel, len, primitiveIndex)
1673 			<< "}\n"
1674 			;
1675 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1676 	}
1677 
1678 	if (m_taskNeeded)
1679 	{
1680 		std::ostringstream task;
1681 		task
1682 			<< "#version 460\n"
1683 			<< "#extension GL_EXT_mesh_shader : enable\n"
1684 			<< "\n"
1685 			<< "layout (local_size_x=" << m_extent.width << ") in;\n"
1686 			<< "\n"
1687 			<< taskDataDeclStr
1688 			<< "\n"
1689 			<< "void main ()\n"
1690 			<< "{\n"
1691 			<< "    td.indexNumber[" << localIndex << "] = " << localIndex << ";\n"
1692 			<< "    td.size = gl_WorkGroupSize.x;\n"
1693 			<< "    EmitMeshTasksEXT(" << m_extent.width << ", 1u, 1u);\n"
1694 			<< "}\n"
1695 			;
1696 		programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
1697 	}
1698 
1699 	// Basic fragment shader.
1700 	{
1701 		const auto frag = getBasicFragShader();
1702 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1703 	}
1704 }
1705 
createInstance(Context & context) const1706 TestInstance* LocalInvocationCase::createInstance (Context& context) const
1707 {
1708 	// Must match the shader.
1709 	const ColorVec			expectedColors	(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1710 	const IterationParams	iterationParams	=
1711 	{
1712 		m_extent,					//	VkExtent2D					colorExtent;
1713 		1u,							//	uint32_t					numLayers;
1714 		false,						//	bool						multiview;
1715 		false,						//	bool						indirect;
1716 		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
1717 		getDefaultDrawCommands(),	//	DrawCommandVec				drawArgs;
1718 		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1719 	};
1720 	return new FullScreenColorInstance(context, iterationParams, expectedColors);
1721 }
1722 
1723 // NumWorkgroups case.
toGLSL(const tcu::UVec3 & v)1724 std::string toGLSL (const tcu::UVec3& v)
1725 {
1726 	return "uvec3(" + std::to_string(v.x()) + ", " + std::to_string(v.y()) + ", " + std::to_string(v.z()) + ")";
1727 }
1728 
1729 class NumWorkgroupsCase : public MeshShaderBuiltinCase
1730 {
1731 public:
NumWorkgroupsCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const tcu::Maybe<tcu::UVec3> & taskGroups,const tcu::UVec3 & meshGroups)1732 					NumWorkgroupsCase		(tcu::TestContext& testCtx, const std::string& name, const std::string& description, const tcu::Maybe<tcu::UVec3>& taskGroups, const tcu::UVec3& meshGroups)
1733 						: MeshShaderBuiltinCase	(testCtx, name, description, static_cast<bool>(taskGroups))
1734 						, m_taskGroups			(taskGroups)
1735 						, m_meshGroups			(meshGroups)
1736 						{}
~NumWorkgroupsCase(void)1737 	virtual			~NumWorkgroupsCase		(void) {}
1738 
1739 	void			initPrograms			(vk::SourceCollections& programCollection) const override;
1740 	TestInstance*	createInstance			(Context& context) const override;
1741 
1742 protected:
1743 	VkExtent2D		imageExtent				() const;
1744 	tcu::UVec3		drawArgs				() const;
1745 
1746 	const tcu::Maybe<tcu::UVec3>	m_taskGroups;
1747 	const tcu::UVec3				m_meshGroups;
1748 };
1749 
imageExtent() const1750 VkExtent2D NumWorkgroupsCase::imageExtent () const
1751 {
1752 	uint32_t taskMultiplier = 1u;
1753 
1754 	if (m_taskNeeded)
1755 	{
1756 		const auto& tg = m_taskGroups.get();
1757 		taskMultiplier = tg.x() * tg.y() * tg.z();
1758 	}
1759 
1760 	const uint32_t meshFactor	= m_meshGroups.x() * m_meshGroups.y() * m_meshGroups.z();
1761 	const uint32_t width		= meshFactor * taskMultiplier;
1762 
1763 	return makeExtent2D(width, 1u);
1764 }
1765 
drawArgs() const1766 tcu::UVec3 NumWorkgroupsCase::drawArgs () const
1767 {
1768 	if (m_taskNeeded)
1769 		return m_taskGroups.get();
1770 	return m_meshGroups;
1771 }
1772 
initPrograms(vk::SourceCollections & programCollection) const1773 void NumWorkgroupsCase::initPrograms (vk::SourceCollections& programCollection) const
1774 {
1775 	const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1776 
1777 	// Task data.
1778 	std::ostringstream taskDataDecl;
1779 
1780 	if (m_taskNeeded)
1781 	{
1782 		taskDataDecl
1783 			<< "struct TaskData {\n"
1784 			<< "    uvec3 parentId;\n"
1785 			<< "    uvec3 parentSize;\n"
1786 			<< "};\n"
1787 			<< "taskPayloadSharedEXT TaskData td;\n"
1788 			;
1789 	}
1790 
1791 	const auto	taskDataDeclStr	= taskDataDecl.str();
1792 	const auto	extent			= imageExtent();
1793 	const auto&	width			= extent.width;
1794 	DE_ASSERT(extent.height == 1u);
1795 
1796 	// Mesh shader: each work group fills one pixel.
1797 	{
1798 		const std::string parentId			= (m_taskNeeded ? "td.parentId"   : "uvec3(0, 0, 0)");
1799 		const std::string parentSize		= (m_taskNeeded ? "td.parentSize" : "uvec3(1, 1, 1)");
1800 		const std::string parentOffset		= "(" + parentSize + ".x * " + parentSize + ".y * " + parentId + ".z + " + parentId + ".y * " + parentSize + ".x + " + parentId + ".x)";
1801 		const std::string meshGroupsPerTask	= std::to_string(m_meshGroups.x() * m_meshGroups.y() * m_meshGroups.z());
1802 		const std::string meshGroupIndex	= "(gl_NumWorkGroups.x * gl_NumWorkGroups.y * gl_WorkGroupID.z + gl_WorkGroupID.y * gl_NumWorkGroups.x + gl_WorkGroupID.x)";
1803 		const std::string pixel				= "((" + parentOffset + " * " + meshGroupsPerTask + ") + " + meshGroupIndex + ")";
1804 		const std::string len				= std::to_string(width);
1805 
1806 		std::ostringstream mesh;
1807 		mesh
1808 			<< "#version 460\n"
1809 			<< "#extension GL_EXT_mesh_shader : enable\n"
1810 			<< "\n"
1811 			<< "layout (local_size_x=1) in;\n"
1812 			<< "layout (triangles) out;\n"
1813 			<< "layout (max_vertices=3, max_primitives=1) out;\n"
1814 			<< "\n"
1815 			<< taskDataDeclStr
1816 			<< "\n"
1817 			<< "void main ()\n"
1818 			<< "{\n"
1819 			<< "    uint numVertices = 3u;\n"
1820 			<< "    uint numPrimitives = 1u;\n"
1821 			<< "    if (gl_NumWorkGroups != " << toGLSL(m_meshGroups) << ") {\n"
1822 			<< "        numVertices = 0u;\n"
1823 			<< "        numPrimitives = 0u;\n"
1824 			<< "    }\n"
1825 			<< "    SetMeshOutputsEXT(numVertices, numPrimitives);\n"
1826 			<< "    if (numPrimitives == 0u) {\n"
1827 			<< "        return;\n"
1828 			<< "    }\n"
1829 			<< "\n"
1830 			<< triangleForPixel(pixel, len, "0")
1831 			<< "}\n"
1832 			;
1833 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1834 	}
1835 
1836 	if (m_taskNeeded)
1837 	{
1838 		std::ostringstream task;
1839 		task
1840 			<< "#version 460\n"
1841 			<< "#extension GL_EXT_mesh_shader : enable\n"
1842 			<< "\n"
1843 			<< "layout (local_size_x=1) in;\n"
1844 			<< "\n"
1845 			<< taskDataDeclStr
1846 			<< "\n"
1847 			<< "void main ()\n"
1848 			<< "{\n"
1849 			<< "    uvec3 meshGroups = " << toGLSL(m_meshGroups) << ";\n"
1850 			<< "    if (gl_NumWorkGroups != " << toGLSL(m_taskGroups.get()) << ") {\n"
1851 			<< "        meshGroups = uvec3(0, 0, 0);\n"
1852 			<< "    }\n"
1853 			<< "    td.parentSize = gl_NumWorkGroups;\n"
1854 			<< "    td.parentId   = gl_WorkGroupID;\n"
1855 			<< "    EmitMeshTasksEXT(meshGroups.x, meshGroups.y, meshGroups.z);\n"
1856 			<< "}\n"
1857 			;
1858 		programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
1859 	}
1860 
1861 	// Basic fragment shader.
1862 	{
1863 		const auto frag = getBasicFragShader();
1864 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1865 	}
1866 }
1867 
createInstance(Context & context) const1868 TestInstance* NumWorkgroupsCase::createInstance (Context& context) const
1869 {
1870 	// Must match the shader.
1871 	const ColorVec			expectedColors	(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1872 	const auto				extent			= imageExtent();
1873 	const auto				drawCmdArgs		= drawArgs();
1874 	const DrawCommandVec	drawCommands	(1u, makeDrawMeshTasksIndirectCommandEXT(drawCmdArgs.x(), drawCmdArgs.y(), drawCmdArgs.z()));
1875 	const IterationParams	iterationParams	=
1876 	{
1877 		extent,						//	VkExtent2D					colorExtent;
1878 		1u,							//	uint32_t					numLayers;
1879 		false,						//	bool						multiview;
1880 		false,						//	bool						indirect;
1881 		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
1882 		drawCommands,				//	DrawCommandVec				drawArgs;
1883 		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1884 	};
1885 	return new FullScreenColorInstance(context, iterationParams, expectedColors);
1886 }
1887 
1888 // GlobalInvocationId builtin case.
1889 class GlobalInvocationIdCase : public MeshShaderBuiltinCase
1890 {
1891 public:
GlobalInvocationIdCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool taskNeeded)1892 					GlobalInvocationIdCase		(tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded)
1893 						: MeshShaderBuiltinCase (testCtx, name, description, taskNeeded)
1894 						, m_jobSize				(getLargeJobSize())
1895 						, m_extent				{m_jobSize.numTasks * m_jobSize.localSize, 1u}
1896 						{}
~GlobalInvocationIdCase(void)1897 	virtual			~GlobalInvocationIdCase		(void) {}
1898 
1899 	void			initPrograms				(vk::SourceCollections& programCollection) const override;
1900 	TestInstance*	createInstance				(Context& context) const override;
1901 
1902 protected:
1903 	const JobSize    m_jobSize;
1904 	const VkExtent2D m_extent;
1905 };
1906 
initPrograms(vk::SourceCollections & programCollection) const1907 void GlobalInvocationIdCase::initPrograms (vk::SourceCollections& programCollection) const
1908 {
1909 	const auto	buildOptions	= getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
1910 	const auto&	localSize		= m_jobSize.localSize;
1911 
1912 	// Task data.
1913 	std::ostringstream taskDataDecl;
1914 	taskDataDecl
1915 		<< "struct TaskData {\n"
1916 		<< "    uint pixelId[" << localSize << "];\n"
1917 		<< "    uint size;\n"
1918 		<< "};\n"
1919 		<< "taskPayloadSharedEXT TaskData td;\n"
1920 		;
1921 	const auto taskDataDeclStr = taskDataDecl.str();
1922 
1923 	// Mesh shader: each work group fills one pixel.
1924 	{
1925 		const std::string pixel          = (m_taskNeeded ? "td.pixelId[gl_LocalInvocationIndex]" : "gl_GlobalInvocationID.x");
1926 		const std::string len            = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
1927 		const std::string primitiveIndex = "gl_LocalInvocationIndex";
1928 		const auto        maxVert        = localSize * 3u;
1929 
1930 		std::ostringstream mesh;
1931 		mesh
1932 			<< "#version 460\n"
1933 			<< "#extension GL_EXT_mesh_shader : enable\n"
1934 			<< "\n"
1935 			<< "layout (local_size_x=" << localSize << ") in;\n"
1936 			<< "layout (triangles) out;\n"
1937 			<< "layout (max_vertices=" << maxVert << ", max_primitives=" << localSize << ") out;\n"
1938 			<< "\n"
1939 			<< (m_taskNeeded ? taskDataDeclStr : "")
1940 			<< "\n"
1941 			<< "void main ()\n"
1942 			<< "{\n"
1943 			<< "    SetMeshOutputsEXT(" << maxVert << ", " << localSize << ");\n"
1944 			<< "\n"
1945 			<< triangleForPixel(pixel, len, primitiveIndex)
1946 			<< "}\n"
1947 			;
1948 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
1949 	}
1950 
1951 	if (m_taskNeeded)
1952 	{
1953 		std::ostringstream task;
1954 		task
1955 			<< "#version 460\n"
1956 			<< "#extension GL_EXT_mesh_shader : enable\n"
1957 			<< "\n"
1958 			<< "layout (local_size_x=" << localSize << ") in;\n"
1959 			<< "\n"
1960 			<< taskDataDeclStr
1961 			<< "\n"
1962 			<< "void main ()\n"
1963 			<< "{\n"
1964 			<< "    td.pixelId[gl_LocalInvocationIndex] = gl_GlobalInvocationID.x;\n"
1965 			<< "    td.size = " << m_extent.width << ";\n"
1966 			<< "    EmitMeshTasksEXT(1u, 1u, 1u);\n"
1967 			<< "}\n"
1968 			;
1969 		programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
1970 	}
1971 
1972 	// Basic fragment shader.
1973 	{
1974 		const auto frag = getBasicFragShader();
1975 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
1976 	}
1977 }
1978 
createInstance(Context & context) const1979 TestInstance* GlobalInvocationIdCase::createInstance (Context& context) const
1980 {
1981 	// Must match the shader.
1982 	const ColorVec			expectedColors	(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
1983 	const IterationParams	iterationParams	=
1984 	{
1985 		m_extent,									//	VkExtent2D					colorExtent;
1986 		1u,											//	uint32_t					numLayers;
1987 		false,										//	bool						multiview;
1988 		false,										//	bool						indirect;
1989 		tcu::Nothing,								//	tcu::Maybe<FragmentSize>	fragmentSize;
1990 		getDefaultDrawCommands(m_jobSize.numTasks),	//	DrawCommandVec				drawArgs;
1991 		{},											//	ViewportVec					viewports;	// If empty, a single default viewport is used.
1992 	};
1993 	return new FullScreenColorInstance(context, iterationParams, expectedColors);
1994 }
1995 
1996 // DrawIndex builtin case.
1997 class DrawIndexCase : public MeshShaderBuiltinCase
1998 {
1999 public:
DrawIndexCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,bool taskNeeded)2000 					DrawIndexCase		(tcu::TestContext& testCtx, const std::string& name, const std::string& description, bool taskNeeded)
2001 						: MeshShaderBuiltinCase (testCtx, name, description, taskNeeded)
2002 						, m_extent				(getLinearExtent())
2003 						{}
~DrawIndexCase(void)2004 	virtual			~DrawIndexCase	(void) {}
2005 
2006 	void			initPrograms		(vk::SourceCollections& programCollection) const override;
2007 	TestInstance*	createInstance		(Context& context) const override;
2008 
2009 protected:
2010 	const VkExtent2D m_extent;
2011 };
2012 
initPrograms(vk::SourceCollections & programCollection) const2013 void DrawIndexCase::initPrograms (vk::SourceCollections& programCollection) const
2014 {
2015 	const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
2016 
2017 	const std::string taskDataDecl =
2018 		"struct TaskData {\n"
2019 		"    uint id;\n"
2020 		"    uint size;\n"
2021 		"};\n"
2022 		"taskPayloadSharedEXT TaskData td;\n"
2023 		;
2024 
2025 	const auto drawIndex = "uint(gl_DrawID)";
2026 
2027 	// Mesh shader: each work group fills one pixel.
2028 	{
2029 		const std::string pixel = (m_taskNeeded ? "td.id"   : drawIndex);
2030 		const std::string len   = (m_taskNeeded ? "td.size" : de::toString(m_extent.width));
2031 
2032 		std::ostringstream mesh;
2033 		mesh
2034 			<< "#version 460\n"
2035 			<< "#extension GL_EXT_mesh_shader : enable\n"
2036 			<< "\n"
2037 			<< "layout (local_size_x=1) in;\n"
2038 			<< "layout (triangles) out;\n"
2039 			<< "layout (max_vertices=3, max_primitives=1) out;\n"
2040 			<< "\n"
2041 			<< (m_taskNeeded ? taskDataDecl : "")
2042 			<< "\n"
2043 			<< "void main ()\n"
2044 			<< "{\n"
2045 			<< "    SetMeshOutputsEXT(3u, 1u);\n"
2046 			<< "\n"
2047 			<< triangleForPixel(pixel, len, "0")
2048 			<< "}\n"
2049 			;
2050 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
2051 	}
2052 
2053 	if (m_taskNeeded)
2054 	{
2055 		std::ostringstream task;
2056 		task
2057 			<< "#version 460\n"
2058 			<< "#extension GL_EXT_mesh_shader : enable\n"
2059 			<< "\n"
2060 			<< "layout (local_size_x=1) in;\n"
2061 			<< "\n"
2062 			<< taskDataDecl
2063 			<< "\n"
2064 			<< "void main ()\n"
2065 			<< "{\n"
2066 			<< "    td.id          = " << drawIndex << ";\n"
2067 			<< "    td.size        = " << m_extent.width << ";\n"
2068 			<< "    EmitMeshTasksEXT(1u, 1u, 1u);\n"
2069 			<< "}\n"
2070 			;
2071 		programCollection.glslSources.add("task") << glu::TaskSource(task.str()) << buildOptions;
2072 	}
2073 
2074 	// Basic fragment shader.
2075 	{
2076 		const auto frag = getBasicFragShader();
2077 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
2078 	}
2079 }
2080 
createInstance(Context & context) const2081 TestInstance* DrawIndexCase::createInstance (Context& context) const
2082 {
2083 	// Must match the shader.
2084 	const ColorVec			expectedColors	(1u, tcu::Vec4(0.0, 0.0, 1.0, 1.0));
2085 	const DrawCommandVec	commands		(m_extent.width, makeDrawMeshTasksIndirectCommandEXT(1u, 1u, 1u));
2086 	const IterationParams	iterationParams	=
2087 	{
2088 		m_extent,		//	VkExtent2D					colorExtent;
2089 		1u,				//	uint32_t					numLayers;
2090 		false,			//	bool						multiview;
2091 		true,			//	bool						indirect;
2092 		tcu::Nothing,	//	tcu::Maybe<FragmentSize>	fragmentSize;
2093 		commands,		//	DrawCommandVec				drawArgs;
2094 		{},				//	ViewportVec					viewports;	// If empty, a single default viewport is used.
2095 	};
2096 	return new FullScreenColorInstance(context, iterationParams, expectedColors);
2097 }
2098 
2099 // ViewIndex builtin case.
2100 class ViewIndexCase : public MeshShaderBuiltinCase
2101 {
2102 public:
ViewIndexCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)2103 					ViewIndexCase	(tcu::TestContext& testCtx, const std::string& name, const std::string& description)
2104 						: MeshShaderBuiltinCase (testCtx, name, description, false)
2105 						, m_extent				(getDefaultExtent())
2106 						{}
~ViewIndexCase(void)2107 	virtual			~ViewIndexCase	(void) {}
2108 
2109 	void			checkSupport		(Context& context) const override;
2110 	void			initPrograms		(vk::SourceCollections& programCollection) const override;
2111 	TestInstance*	createInstance		(Context& context) const override;
2112 
2113 	static constexpr uint32_t kNumLayers = 4u;
2114 
2115 protected:
2116 	const VkExtent2D m_extent;
2117 };
2118 
checkSupport(Context & context) const2119 void ViewIndexCase::checkSupport(Context &context) const
2120 {
2121 	MeshShaderBuiltinCase::checkSupport(context);
2122 
2123 	const auto& multiviewFeatures = context.getMultiviewFeatures();
2124 	if (!multiviewFeatures.multiview)
2125 		TCU_THROW(NotSupportedError, "Multiview not supported");
2126 
2127 	const auto& meshFeatures = context.getMeshShaderFeaturesEXT();
2128 	if (!meshFeatures.multiviewMeshShader)
2129 		TCU_THROW(NotSupportedError, "Multiview not supported for mesh shaders");
2130 
2131 	const auto& meshProperties = context.getMeshShaderPropertiesEXT();
2132 	if (kNumLayers > meshProperties.maxMeshMultiviewViewCount)
2133 	{
2134 		std::ostringstream msg;
2135 		msg << "maxMeshMultiviewViewCount too low: " << meshProperties.maxMeshMultiviewViewCount << " and the test needs " << kNumLayers;
2136 		TCU_THROW(NotSupportedError, msg.str());
2137 	}
2138 }
2139 
initPrograms(vk::SourceCollections & programCollection) const2140 void ViewIndexCase::initPrograms (vk::SourceCollections& programCollection) const
2141 {
2142 	const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
2143 
2144 	DE_ASSERT(!m_taskNeeded);
2145 
2146 	// Mesh shader: choose output color depending on the view index.
2147 	{
2148 		std::ostringstream mesh;
2149 		mesh
2150 			<< "#version 460\n"
2151 			<< "#extension GL_EXT_mesh_shader : enable\n"
2152 			<< "#extension GL_EXT_multiview : enable\n"
2153 			<< "\n"
2154 			<< "layout (local_size_x=1) in;\n"
2155 			<< "layout (triangles) out;\n"
2156 			<< "layout (max_vertices=3, max_primitives=1) out;\n"
2157 			<< "\n"
2158 			<< "vec4 colors[" << kNumLayers << "] = vec4[](\n"
2159 			<< "    vec4(0.0, 0.0, 1.0, 1.0),\n"
2160 			<< "    vec4(1.0, 0.0, 1.0, 1.0),\n"
2161 			<< "    vec4(0.0, 1.0, 1.0, 1.0),\n"
2162 			<< "    vec4(1.0, 1.0, 0.0, 1.0)\n"
2163 			<< ");\n"
2164 			<< "\n"
2165 			<< "layout (location=0) perprimitiveEXT out vec4 primitiveColor[];\n"
2166 			<< "\n"
2167 			<< "void main ()\n"
2168 			<< "{\n"
2169 			<< "    SetMeshOutputsEXT(3u, 1u);\n"
2170 			<< "\n"
2171 			<< "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 2u);\n"
2172 			<< "    primitiveColor[0] = colors[gl_ViewIndex];\n"
2173 			<< "\n"
2174 			<< "    gl_MeshVerticesEXT[0].gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
2175 			<< "    gl_MeshVerticesEXT[1].gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
2176 			<< "    gl_MeshVerticesEXT[2].gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
2177 			<< "}\n"
2178 			;
2179 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
2180 	}
2181 
2182 	// Fragment shader writes its output using the primitive color from the mesh shader.
2183 	{
2184 		std::ostringstream frag;
2185 		frag
2186 			<< "#version 460\n"
2187 			<< "#extension GL_EXT_mesh_shader : enable\n"
2188 			<< "#extension GL_EXT_multiview : enable\n"
2189 			<< "\n"
2190 			<< "layout (location=0) perprimitiveEXT in vec4 primitiveColor;\n"
2191 			<< "layout (location=0) out vec4 outColor;\n"
2192 			<< "\n"
2193 			<< "void main ()\n"
2194 			<< "{\n"
2195 			<< "    outColor = primitiveColor;\n"
2196 			<< "}\n"
2197 			;
2198 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
2199 	}
2200 }
2201 
createInstance(Context & context) const2202 TestInstance* ViewIndexCase::createInstance (Context& context) const
2203 {
2204 	// Must match the shader.
2205 	ColorVec expectedColors;
2206 
2207 	expectedColors.reserve(kNumLayers);
2208 	expectedColors.push_back(tcu::Vec4(0.0, 0.0, 1.0, 1.0));
2209 	expectedColors.push_back(tcu::Vec4(1.0, 0.0, 1.0, 1.0));
2210 	expectedColors.push_back(tcu::Vec4(0.0, 1.0, 1.0, 1.0));
2211 	expectedColors.push_back(tcu::Vec4(1.0, 1.0, 0.0, 1.0));
2212 
2213 	const IterationParams iterationParams =
2214 	{
2215 		getDefaultExtent(),					//	VkExtent2D					colorExtent;
2216 		kNumLayers,							//	uint32_t					numLayers;
2217 		true,								//	bool						multiview;
2218 		false,								//	bool						indirect;
2219 		tcu::Nothing,						//	tcu::Maybe<FragmentSize>	fragmentSize;
2220 		getDefaultDrawCommands(),			//	DrawCommandVec				drawArgs;
2221 		{},									//	ViewportVec					viewports;	// If empty, a single default viewport is used.
2222 	};
2223 	return new FullScreenColorInstance(context, iterationParams, expectedColors);
2224 }
2225 
2226 // Primitive Shading Rate case.
2227 class PrimitiveShadingRateCase : public MeshShaderBuiltinCase
2228 {
2229 public:
PrimitiveShadingRateCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,FragmentSize topSize,FragmentSize bottomSize)2230 					PrimitiveShadingRateCase	(tcu::TestContext& testCtx, const std::string& name, const std::string& description, FragmentSize topSize, FragmentSize bottomSize)
2231 						: MeshShaderBuiltinCase	(testCtx, name, description, false/*taskNeeded*/)
2232 						, m_topSize				(topSize)
2233 						, m_bottomSize			(bottomSize)
2234 						{}
~PrimitiveShadingRateCase(void)2235 	virtual			~PrimitiveShadingRateCase	(void) {}
2236 
2237 	void			initPrograms				(vk::SourceCollections& programCollection) const override;
2238 	void			checkSupport				(Context& context) const override;
2239 	TestInstance*	createInstance				(Context& context) const override;
2240 
2241 protected:
2242 	const FragmentSize m_topSize;
2243 	const FragmentSize m_bottomSize;
2244 };
2245 
initPrograms(vk::SourceCollections & programCollection) const2246 void PrimitiveShadingRateCase::initPrograms (vk::SourceCollections& programCollection) const
2247 {
2248 	const auto buildOptions	= getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
2249 
2250 	// Shading rate masks to use.
2251 	const auto topMask		= getGLSLShadingRateMask(m_topSize);
2252 	const auto bottomMask	= getGLSLShadingRateMask(m_bottomSize);
2253 
2254 	// Mesh shader.
2255 	{
2256 		std::ostringstream mesh;
2257 		mesh
2258 			<< "#version 460\n"
2259 			<< "#extension GL_EXT_mesh_shader : enable\n"
2260 			<< "#extension GL_EXT_fragment_shading_rate : enable\n"
2261 			<< "\n"
2262 			<< "layout (local_size_x=1) in;\n"
2263 			<< "layout (triangles) out;\n"
2264 			<< "layout (max_vertices=6, max_primitives=4) out;\n"
2265 			<< "\n"
2266 			<< "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
2267 			<< "   int gl_PrimitiveShadingRateEXT;\n"
2268 			<< "} gl_MeshPrimitivesEXT[];\n"
2269 			<< "\n"
2270 			<< "void main ()\n"
2271 			<< "{\n"
2272 			<< "    SetMeshOutputsEXT(6u, 4u);\n"
2273 			<< "\n"
2274 			<< "    const vec4 topLeft  = vec4(-1.0, -1.0, 0.0, 1.0);\n"
2275 			<< "    const vec4 midLeft  = vec4(-1.0,  0.0, 0.0, 1.0);\n"
2276 			<< "    const vec4 botLeft  = vec4(-1.0,  1.0, 0.0, 1.0);\n"
2277 			<< "\n"
2278 			<< "    const vec4 topRight = vec4( 1.0, -1.0, 0.0, 1.0);\n"
2279 			<< "    const vec4 midRight = vec4( 1.0,  0.0, 0.0, 1.0);\n"
2280 			<< "    const vec4 botRight = vec4( 1.0,  1.0, 0.0, 1.0);\n"
2281 			<< "\n"
2282 			<< "    gl_MeshVerticesEXT[0].gl_Position = topLeft;\n"
2283 			<< "    gl_MeshVerticesEXT[1].gl_Position = midLeft;\n"
2284 			<< "    gl_MeshVerticesEXT[2].gl_Position = botLeft;\n"
2285 			<< "\n"
2286 			<< "    gl_MeshVerticesEXT[3].gl_Position = topRight;\n"
2287 			<< "    gl_MeshVerticesEXT[4].gl_Position = midRight;\n"
2288 			<< "    gl_MeshVerticesEXT[5].gl_Position = botRight;\n"
2289 			<< "\n"
2290 			<< "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 3u);\n"
2291 			<< "    gl_PrimitiveTriangleIndicesEXT[1] = uvec3(1u, 4u, 3u);\n"
2292 			<< "    gl_PrimitiveTriangleIndicesEXT[2] = uvec3(1u, 2u, 4u);\n"
2293 			<< "    gl_PrimitiveTriangleIndicesEXT[3] = uvec3(2u, 5u, 4u);\n"
2294 			<< "\n"
2295 			<< "    gl_MeshPrimitivesEXT[0].gl_PrimitiveShadingRateEXT = " << topMask << ";\n"
2296 			<< "    gl_MeshPrimitivesEXT[1].gl_PrimitiveShadingRateEXT = " << topMask << ";\n"
2297 			<< "    gl_MeshPrimitivesEXT[2].gl_PrimitiveShadingRateEXT = " << bottomMask << ";\n"
2298 			<< "    gl_MeshPrimitivesEXT[3].gl_PrimitiveShadingRateEXT = " << bottomMask << ";\n"
2299 			<< "}\n"
2300 			;
2301 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
2302 	}
2303 
2304 	// Frag shader.
2305 	{
2306 		const auto extent		= getDefaultExtent();
2307 		const auto halfHeight	= static_cast<float>(extent.height) / 2.0f;
2308 
2309 		std::ostringstream frag;
2310 		frag
2311 			<< "#version 460\n"
2312 			<< "#extension GL_EXT_mesh_shader : enable\n"
2313 			<< "#extension GL_EXT_fragment_shading_rate : enable\n"
2314 			<< "\n"
2315 			<< "layout (location=0) out vec4 outColor;\n"
2316 			<< "\n"
2317 			<< "void main ()\n"
2318 			<< "{\n"
2319 			// Checks the shading rate matches.
2320 			<< "    const int expectedRate = ((gl_FragCoord.y < " << halfHeight << ")? " << topMask << " : " << bottomMask << ");\n"
2321 			<< "    outColor = ((gl_ShadingRateEXT == expectedRate) ? vec4(0.0, 0.0, 1.0, 1.0) : vec4(0.0, 0.0, 0.0, 1.0));\n"
2322 			<< "}\n"
2323 			;
2324 		programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()) << buildOptions;
2325 	}
2326 }
2327 
checkSupport(Context & context) const2328 void PrimitiveShadingRateCase::checkSupport (Context& context) const
2329 {
2330 	MeshShaderBuiltinCase::checkSupport(context);
2331 
2332 	context.requireDeviceFunctionality("VK_KHR_fragment_shading_rate");
2333 
2334 	const auto& meshShaderFeatures = context.getMeshShaderFeaturesEXT();
2335 	if (!meshShaderFeatures.primitiveFragmentShadingRateMeshShader)
2336 		TCU_THROW(NotSupportedError, "Primitive fragment shading rate not supported in mesh shaders");
2337 }
2338 
createInstance(Context & context) const2339 TestInstance* PrimitiveShadingRateCase::createInstance (Context& context) const
2340 {
2341 	const ColorVec			expectedColors	(1u, tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
2342 	FragmentSizeVector		fsInUse			{m_topSize, m_bottomSize};
2343 	const IterationParams	iterationParams	=
2344 	{
2345 		getDefaultExtent(),												//	VkExtent2D					colorExtent;
2346 		1u,																//	uint32_t					numLayers;
2347 		false,															//	bool						multiview;
2348 		false,															//	bool						indirect;
2349 		tcu::just(getBadShadingRateSize(begin(fsInUse), end(fsInUse))),	//	tcu::Maybe<FragmentSize>	fragmentSize;
2350 		getDefaultDrawCommands(),										//	DrawCommandVec				drawArgs;
2351 		{},																//	ViewportVec					viewports;	// If empty, a single default viewport is used.
2352 	};
2353 	return new FullScreenColorInstance(context, iterationParams, expectedColors);
2354 }
2355 
2356 // Cull Primitives case.
2357 class CullPrimitivesCase : public MeshShaderBuiltinCase
2358 {
2359 public:
CullPrimitivesCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description)2360 					CullPrimitivesCase	(tcu::TestContext& testCtx, const std::string& name, const std::string& description)
2361 						: MeshShaderBuiltinCase (testCtx, name, description, false/*taskNeeded*/)
2362 						{}
~CullPrimitivesCase(void)2363 	virtual			~CullPrimitivesCase	(void) {}
2364 
2365 	void			initPrograms		(vk::SourceCollections& programCollection) const override;
2366 	TestInstance*	createInstance		(Context& context) const override;
2367 };
2368 
initPrograms(vk::SourceCollections & programCollection) const2369 void CullPrimitivesCase::initPrograms (vk::SourceCollections& programCollection) const
2370 {
2371 	const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
2372 
2373 	// Mesh shader.
2374 	{
2375 		std::ostringstream mesh;
2376 		mesh
2377 			<< "#version 460\n"
2378 			<< "#extension GL_EXT_mesh_shader : enable\n"
2379 			<< "\n"
2380 			<< "layout (local_size_x=1) in;\n"
2381 			<< "layout (triangles) out;\n"
2382 			<< "layout (max_vertices=6, max_primitives=4) out;\n"
2383 			<< "\n"
2384 			<< "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
2385 			<< "   bool gl_CullPrimitiveEXT;\n"
2386 			<< "} gl_MeshPrimitivesEXT[];\n"
2387 			<< "\n"
2388 			<< "void main ()\n"
2389 			<< "{\n"
2390 			<< "    SetMeshOutputsEXT(6u, 4u);\n"
2391 			<< "\n"
2392 			<< "    const vec4 topLeft  = vec4(-1.0, -1.0, 0.0, 1.0);\n"
2393 			<< "    const vec4 midLeft  = vec4(-1.0,  0.0, 0.0, 1.0);\n"
2394 			<< "    const vec4 botLeft  = vec4(-1.0,  1.0, 0.0, 1.0);\n"
2395 			<< "\n"
2396 			<< "    const vec4 topRight = vec4( 1.0, -1.0, 0.0, 1.0);\n"
2397 			<< "    const vec4 midRight = vec4( 1.0,  0.0, 0.0, 1.0);\n"
2398 			<< "    const vec4 botRight = vec4( 1.0,  1.0, 0.0, 1.0);\n"
2399 			<< "\n"
2400 			<< "    gl_MeshVerticesEXT[0].gl_Position = topLeft;\n"
2401 			<< "    gl_MeshVerticesEXT[1].gl_Position = midLeft;\n"
2402 			<< "    gl_MeshVerticesEXT[2].gl_Position = botLeft;\n"
2403 			<< "\n"
2404 			<< "    gl_MeshVerticesEXT[3].gl_Position = topRight;\n"
2405 			<< "    gl_MeshVerticesEXT[4].gl_Position = midRight;\n"
2406 			<< "    gl_MeshVerticesEXT[5].gl_Position = botRight;\n"
2407 			<< "\n"
2408 			<< "    gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0u, 1u, 3u);\n"
2409 			<< "    gl_PrimitiveTriangleIndicesEXT[1] = uvec3(1u, 4u, 3u);\n"
2410 			<< "    gl_PrimitiveTriangleIndicesEXT[2] = uvec3(1u, 2u, 4u);\n"
2411 			<< "    gl_PrimitiveTriangleIndicesEXT[3] = uvec3(2u, 5u, 4u);\n"
2412 			<< "\n"
2413 			<< "    gl_MeshPrimitivesEXT[0].gl_CullPrimitiveEXT = false;\n"
2414 			<< "    gl_MeshPrimitivesEXT[1].gl_CullPrimitiveEXT = false;\n"
2415 			<< "    gl_MeshPrimitivesEXT[2].gl_CullPrimitiveEXT = true;\n"
2416 			<< "    gl_MeshPrimitivesEXT[3].gl_CullPrimitiveEXT = true;\n"
2417 			<< "}\n"
2418 			;
2419 		programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
2420 	}
2421 
2422 	// Frag shader.
2423 	programCollection.glslSources.add("frag") << glu::FragmentSource(getBasicFragShader());
2424 }
2425 
createInstance(Context & context) const2426 TestInstance* CullPrimitivesCase::createInstance (Context& context) const
2427 {
2428 	const tcu::Vec4 blue	(0.0f, 0.0f, 1.0f, 1.0f);
2429 	const tcu::Vec4 black	= getClearColor();
2430 
2431 	const IterationParams	iterationParams	=
2432 	{
2433 		getDefaultExtent(),			//	VkExtent2D					colorExtent;
2434 		1u,							//	uint32_t					numLayers;
2435 		false,						//	bool						multiview;
2436 		false,						//	bool						indirect;
2437 		tcu::Nothing,				//	tcu::Maybe<FragmentSize>	fragmentSize;
2438 		getDefaultDrawCommands(),	//	DrawCommandVec				drawArgs;
2439 		{},							//	ViewportVec					viewports;	// If empty, a single default viewport is used.
2440 	};
2441 	return new QuadrantsInstance(context, iterationParams, blue, blue, black, black);
2442 }
2443 
2444 } // anonymous
2445 
createMeshShaderBuiltinTestsEXT(tcu::TestContext & testCtx)2446 tcu::TestCaseGroup* createMeshShaderBuiltinTestsEXT (tcu::TestContext& testCtx)
2447 {
2448 	GroupPtr mainGroup (new tcu::TestCaseGroup(testCtx, "builtin", "Mesh Shader Builtin Tests"));
2449 
2450 	mainGroup->addChild(new PositionCase				(testCtx, "position", ""));
2451 	mainGroup->addChild(new PointSizeCase				(testCtx, "point_size", ""));
2452 	mainGroup->addChild(new ClipDistanceCase			(testCtx, "clip_distance", ""));
2453 	mainGroup->addChild(new CullDistanceCase			(testCtx, "cull_distance", ""));
2454 	mainGroup->addChild(new PrimitiveIdCase				(testCtx, "primitive_id_glsl", "", true/*glslFrag*/));
2455 	mainGroup->addChild(new PrimitiveIdCase				(testCtx, "primitive_id_spirv", "", false/*glslFrag*/));
2456 	mainGroup->addChild(new LayerCase					(testCtx, "layer", "", true/*writeval*/, false/*shareVertices*/));
2457 	mainGroup->addChild(new LayerCase					(testCtx, "layer_shared", "", true/*writeval*/, true/*shareVertices*/));
2458 	mainGroup->addChild(new LayerCase					(testCtx, "layer_no_write", "", false/*writeval*/, false/*shareVertices*/));
2459 	mainGroup->addChild(new ViewportIndexCase			(testCtx, "viewport_index", "", true/*writeVal*/, false/*shareVertices*/));
2460 	mainGroup->addChild(new ViewportIndexCase			(testCtx, "viewport_index_shared", "", true/*writeVal*/, true/*shareVertices*/));
2461 	mainGroup->addChild(new ViewportIndexCase			(testCtx, "viewport_index_no_write", "", false/*writeVal*/, false/*shareVertices*/));
2462 	mainGroup->addChild(new WorkGroupIdCase				(testCtx, "work_group_id_in_mesh", "", false/*taskNeeded*/));
2463 	mainGroup->addChild(new WorkGroupIdCase				(testCtx, "work_group_id_in_task", "", true/*taskNeeded*/));
2464 	mainGroup->addChild(new NumWorkgroupsCase			(testCtx, "num_work_groups_mesh", "", tcu::Nothing, tcu::UVec3(5u, 6u, 7u)));
2465 	mainGroup->addChild(new NumWorkgroupsCase			(testCtx, "num_work_groups_task_and_mesh", "", tcu::just(tcu::UVec3(2u, 3u, 4u)), tcu::UVec3(3u, 4u, 2u)));
2466 	mainGroup->addChild(new LocalInvocationCase			(testCtx, "local_invocation_id_in_mesh", "", false/*taskNeeded*/, LocalInvocation::ID));
2467 	mainGroup->addChild(new LocalInvocationCase			(testCtx, "local_invocation_id_in_task", "", true/*taskNeeded*/, LocalInvocation::ID));
2468 	mainGroup->addChild(new LocalInvocationCase			(testCtx, "local_invocation_index_in_task", "", true/*taskNeeded*/, LocalInvocation::INDEX));
2469 	mainGroup->addChild(new LocalInvocationCase			(testCtx, "local_invocation_index_in_mesh", "", false/*taskNeeded*/, LocalInvocation::INDEX));
2470 	mainGroup->addChild(new GlobalInvocationIdCase		(testCtx, "global_invocation_id_in_mesh", "", false/*taskNeeded*/));
2471 	mainGroup->addChild(new GlobalInvocationIdCase		(testCtx, "global_invocation_id_in_task", "", true/*taskNeeded*/));
2472 	mainGroup->addChild(new DrawIndexCase				(testCtx, "draw_index_in_mesh", "", false/*taskNeeded*/));
2473 	mainGroup->addChild(new DrawIndexCase				(testCtx, "draw_index_in_task", "", true/*taskNeeded*/));
2474 	mainGroup->addChild(new ViewIndexCase				(testCtx, "view_index", ""));
2475 	mainGroup->addChild(new CullPrimitivesCase			(testCtx, "cull_primitives", ""));
2476 
2477 	// Primitive shading rate tests.
2478 	{
2479 		const auto sizeCount = static_cast<int>(FragmentSize::SIZE_COUNT);
2480 
2481 		for (int i = 0; i < sizeCount; ++i)
2482 		for (int j = 0; j < sizeCount; ++j)
2483 		{
2484 			const auto topSize		= static_cast<FragmentSize>(i);
2485 			const auto bottomSize	= static_cast<FragmentSize>(j);
2486 
2487 			const auto topExtent	= getShadingRateSize(topSize);
2488 			const auto bottomExtent	= getShadingRateSize(bottomSize);
2489 
2490 			const auto testName		= "primitive_shading_rate_"
2491 										+ std::to_string(topExtent.width) + "x" + std::to_string(topExtent.height)
2492 										+ "_"
2493 										+ std::to_string(bottomExtent.width) + "x" + std::to_string(bottomExtent.height)
2494 									;
2495 
2496 			mainGroup->addChild(new PrimitiveShadingRateCase(testCtx, testName, "", topSize, bottomSize));
2497 		}
2498 	}
2499 
2500 	return mainGroup.release();
2501 }
2502 
2503 } // MeshShader
2504 } // vkt
2505