• 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 Tests mixing VK_EXT_mesh_shader and VK_EXT_provoking_vertex
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktMeshShaderProvokingVertexTestsEXT.hpp"
26 #include "vktTestCase.hpp"
27 #include "vktMeshShaderUtil.hpp"
28 #include "vkImageWithMemory.hpp"
29 #include "vkTypeUtil.hpp"
30 #include "vkImageUtil.hpp"
31 #include "vkObjUtil.hpp"
32 #include "vkBufferWithMemory.hpp"
33 #include "vkCmdUtil.hpp"
34 #include "vkBarrierUtil.hpp"
35 
36 #include "deUniquePtr.hpp"
37 
38 #include <sstream>
39 #include <vector>
40 #include <string>
41 
42 namespace vkt
43 {
44 namespace MeshShader
45 {
46 
47 namespace
48 {
49 
50 using namespace vk;
51 
52 enum class Geometry
53 {
54 	LINES = 0,
55 	TRIANGLES,
56 };
57 
58 using ProvokingVertexModeVec = std::vector<VkProvokingVertexModeEXT>;
59 
getLineColors(void)60 std::vector<tcu::UVec4>	getLineColors			(void)
61 {
62 	return std::vector<tcu::UVec4>{
63 		tcu::UVec4(1, 1, 0, 1),
64 		tcu::UVec4(1, 0, 1, 1),
65 	};
66 }
67 
getTriangleColors(void)68 std::vector<tcu::UVec4>	getTriangleColors		(void)
69 {
70 	return std::vector<tcu::UVec4>{
71 		tcu::UVec4(1, 1, 0, 1),
72 		tcu::UVec4(0, 1, 1, 1),
73 		tcu::UVec4(1, 0, 1, 1),
74 	};
75 }
76 
getLinePositions(void)77 std::vector<tcu::Vec4>	getLinePositions		(void)
78 {
79 	return std::vector<tcu::Vec4>{
80 		tcu::Vec4(-1.0, 0.0, 0.0, 1.0),
81 		tcu::Vec4( 1.0, 0.0, 0.0, 1.0),
82 	};
83 }
84 
getTrianglePositions(void)85 std::vector<tcu::Vec4>	getTrianglePositions	(void)
86 {
87 	return std::vector<tcu::Vec4>{
88 		tcu::Vec4(-1.0, -1.0, 0.0, 1.0),
89 		tcu::Vec4(-1.0,  1.0, 0.0, 1.0),
90 		tcu::Vec4( 3.0, -1.0, 0.0, 1.0),
91 	};
92 }
93 
getClearColor(void)94 tcu::Vec4				getClearColor			(void)
95 {
96 	return tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
97 }
98 
getCaseName(Geometry geometry)99 std::string getCaseName (Geometry geometry)
100 {
101 	switch (geometry)
102 	{
103 	case Geometry::LINES:		return "lines";
104 	case Geometry::TRIANGLES:	return "triangles";
105 	default:
106 		DE_ASSERT(false);
107 		break;
108 	}
109 	// Unreachable.
110 	return "";
111 }
112 
getCaseName(const ProvokingVertexModeVec & modes)113 std::string getCaseName (const ProvokingVertexModeVec& modes)
114 {
115 	std::string caseName;
116 
117 	for (const auto& mode : modes)
118 	{
119 		caseName += (caseName.empty() ? "" : "_");
120 		switch (mode)
121 		{
122 		case VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT:		caseName += "first";	break;
123 		case VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT:		caseName += "last";		break;
124 		default:
125 			DE_ASSERT(false);
126 			break;
127 		}
128 	}
129 
130 	return caseName;
131 }
132 
133 struct TestParams
134 {
135 	ProvokingVertexModeVec	provokingVertices;	// In the same render pass. In practice 1 or 2 elements.
136 	Geometry				geometryType;
137 };
138 
139 class ProvokingVertexCase : public vkt::TestCase
140 {
141 public:
ProvokingVertexCase(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TestParams & params)142 					ProvokingVertexCase		(tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
143 						: vkt::TestCase	(testCtx, name, description)
144 						, m_params		(params)
145 						{
146 							DE_ASSERT(m_params.provokingVertices.size() <= 2);
147 						}
148 
~ProvokingVertexCase(void)149 	virtual			~ProvokingVertexCase	(void) {}
150 
151 	void			initPrograms			(vk::SourceCollections& programCollection) const override;
152 	TestInstance*	createInstance			(Context& context) const override;
153 	void			checkSupport			(Context& context) const override;
154 
155 protected:
156 	TestParams		m_params;
157 };
158 
159 class ProvokingVertexInstance : public vkt::TestInstance
160 {
161 public:
ProvokingVertexInstance(Context & context,const TestParams & params)162 						ProvokingVertexInstance		(Context& context, const TestParams& params)
163 							: vkt::TestInstance	(context)
164 							, m_params			(&params)
165 							{}
~ProvokingVertexInstance(void)166 	virtual				~ProvokingVertexInstance	(void) {}
167 
168 	tcu::TestStatus		iterate						(void) override;
169 
170 protected:
171 	const TestParams*	m_params;
172 };
173 
createInstance(Context & context) const174 TestInstance* ProvokingVertexCase::createInstance (Context& context) const
175 {
176 	return new ProvokingVertexInstance(context, m_params);
177 }
178 
initPrograms(vk::SourceCollections & programCollection) const179 void ProvokingVertexCase::initPrograms (vk::SourceCollections& programCollection) const
180 {
181 	const auto buildOptions = getMinMeshEXTBuildOptions(programCollection.usedVulkanVersion);
182 
183 	std::ostringstream frag;
184 	frag
185 		<< "#version 460\n"
186 		<< "layout (location=0) flat in uvec4 inColor;\n"
187 		<< "layout (location=0) out vec4 outColor;\n"
188 		<< "void main ()\n"
189 		<< "{\n"
190 		<< "    outColor = vec4(inColor);\n"
191 		<< "}\n"
192 		;
193 	programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
194 
195 	const auto isLines		= (m_params.geometryType == Geometry::LINES);
196 	const auto vertCount	= (isLines ? 2u : 3u);
197 	const auto geometryName	= (isLines ? "lines" : "triangles");
198 	const auto primIndices	= (isLines
199 							? "gl_PrimitiveLineIndicesEXT[0] = uvec2(0, 1);"
200 							: "gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0, 1, 2);");
201 	const auto colors		= (isLines ? getLineColors() : getTriangleColors());
202 	const auto positions	= (isLines ? getLinePositions() : getTrianglePositions());
203 
204 	std::ostringstream mesh;
205 	mesh
206 		<< "#version 460\n"
207 		<< "#extension GL_EXT_mesh_shader : enable\n"
208 		<< "\n"
209 		<< "layout (local_size_x=" << vertCount << ", local_size_y=1, local_size_z=1) in;\n"
210 		<< "layout (" << geometryName << ") out;\n"
211 		<< "layout (max_vertices=" << vertCount << ", max_primitives=1) out;\n"
212 		<< "\n"
213 		<< "layout (push_constant, std430) uniform PushConstantBlock {\n"
214 		<< "    int layer;\n"
215 		<< "} pc;\n"
216 		<< "\n"
217 		<< "perprimitiveEXT out gl_MeshPerPrimitiveEXT {\n"
218 		<< "   int gl_Layer;\n"
219 		<< "} gl_MeshPrimitivesEXT[];\n"
220 		<< "\n"
221 		<< "uvec4 colors[] = uvec4[](\n"
222 		;
223 
224 	for (size_t i = 0; i < colors.size(); ++i)
225 		mesh << "    uvec4" << colors[i] << ((i < colors.size() - 1) ? "," : "") << "\n";
226 
227 	mesh
228 		<< ");\n"
229 		<< "\n"
230 		<< "vec4 vertices[] = vec4[](\n"
231 		;
232 
233 	for (size_t i = 0; i < positions.size(); ++i)
234 		mesh << "    vec4" << positions[i] << ((i < positions.size() - 1) ? "," : "") << "\n";
235 
236 	mesh
237 		<< ");\n"
238 		<< "\n"
239 		<< "layout (location=0) flat out uvec4 vtxColor[];\n"
240 		<< "\n"
241 		<< "void main ()\n"
242 		<< "{\n"
243 		<< "    SetMeshOutputsEXT(" << vertCount << ", 1);\n"
244 		<< "    gl_MeshVerticesEXT[gl_LocalInvocationIndex].gl_Position = vertices[gl_LocalInvocationIndex];\n"
245 		<< "    vtxColor[gl_LocalInvocationIndex] = colors[gl_LocalInvocationIndex];\n"
246 		<< "\n"
247 		<< "    if (gl_LocalInvocationIndex == 0u) {\n"
248 		<< "        " << primIndices << "\n"
249 		<< "        gl_MeshPrimitivesEXT[0].gl_Layer = pc.layer;\n"
250 		<< "    }\n"
251 		<< "}\n"
252 		;
253 	programCollection.glslSources.add("mesh") << glu::MeshSource(mesh.str()) << buildOptions;
254 }
255 
checkSupport(Context & context) const256 void ProvokingVertexCase::checkSupport (Context& context) const
257 {
258 	checkTaskMeshShaderSupportEXT(context, false/*requireTask*/, true/*requireMesh*/);
259 
260 	context.requireDeviceFunctionality("VK_EXT_provoking_vertex");
261 
262 	if (m_params.provokingVertices.size() > 1)
263 	{
264 		const auto& pvProperties = context.getProvokingVertexPropertiesEXT();
265 		if (!pvProperties.provokingVertexModePerPipeline)
266 			TCU_THROW(NotSupportedError, "Switching provoking vertex modes in the same render pass not supported");
267 	}
268 }
269 
iterate(void)270 tcu::TestStatus ProvokingVertexInstance::iterate (void)
271 {
272 	const auto&			vkd				= m_context.getDeviceInterface();
273 	const auto			device			= m_context.getDevice();
274 	auto&				alloc			= m_context.getDefaultAllocator();
275 	const auto			queueIndex		= m_context.getUniversalQueueFamilyIndex();
276 	const auto			queue			= m_context.getUniversalQueue();
277 	const auto			colorExtent		= makeExtent3D(1u, 1u, 1u);
278 	const auto			colorLayers		= static_cast<uint32_t>(m_params->provokingVertices.size());
279 	const auto			colorFormat		= VK_FORMAT_R8G8B8A8_UNORM;
280 	const auto			colorUsage		= (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
281 	const auto			tcuFormat		= mapVkFormat(colorFormat);
282 	const auto			pixelSize		= static_cast<uint32_t>(tcu::getPixelSize(tcuFormat));
283 	const auto			viewType		= ((colorLayers > 1u) ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D);
284 	const auto			clearColor		= getClearColor();
285 
286 	// Color attachment.
287 	const VkImageCreateInfo colorBufferInfo =
288 	{
289 		VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,	//	VkStructureType			sType;
290 		nullptr,								//	const void*				pNext;
291 		0u,										//	VkImageCreateFlags		flags;
292 		VK_IMAGE_TYPE_2D,						//	VkImageType				imageType;
293 		colorFormat,							//	VkFormat				format;
294 		colorExtent,							//	VkExtent3D				extent;
295 		1u,										//	uint32_t				mipLevels;
296 		colorLayers,										//	uint32_t				arrayLayers;
297 		VK_SAMPLE_COUNT_1_BIT,					//	VkSampleCountFlagBits	samples;
298 		VK_IMAGE_TILING_OPTIMAL,				//	VkImageTiling			tiling;
299 		colorUsage,								//	VkImageUsageFlags		usage;
300 		VK_SHARING_MODE_EXCLUSIVE,				//	VkSharingMode			sharingMode;
301 		0u,										//	uint32_t				queueFamilyIndexCount;
302 		nullptr,								//	const uint32_t*			pQueueFamilyIndices;
303 		VK_IMAGE_LAYOUT_UNDEFINED,				//	VkImageLayout			initialLayout;
304 	};
305 	ImageWithMemory	colorBuffer	(vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any);
306 	const auto		colorSRR	= makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, colorLayers);
307 	const auto		colorSRL	= makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, colorLayers);
308 	const auto		colorView	= makeImageView(vkd, device, colorBuffer.get(), viewType, colorFormat, colorSRR);
309 
310 	// Verification buffer.
311 	const auto			verificationBufferSize	= (pixelSize * colorExtent.width * colorExtent.height * colorLayers);
312 	const auto			verificationBufferInfo	= makeBufferCreateInfo(verificationBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
313 	BufferWithMemory	verificationBuffer		(vkd, device, alloc, verificationBufferInfo, MemoryRequirement::HostVisible);
314 
315 	// Push constant range.
316 	const auto pcSize	= static_cast<uint32_t>(sizeof(int32_t));
317 	const auto pcStages	= VK_SHADER_STAGE_MESH_BIT_EXT;
318 	const auto pcRange	= makePushConstantRange(pcStages, 0u, pcSize);
319 
320 	// Pipeline layout.
321 	const auto pipelineLayout = makePipelineLayout(vkd, device, DE_NULL, &pcRange);
322 
323 	// Modules.
324 	const auto&	binaries	= m_context.getBinaryCollection();
325 	const auto	meshModule	= createShaderModule(vkd, device, binaries.get("mesh"));
326 	const auto	fragModule	= createShaderModule(vkd, device, binaries.get("frag"));
327 
328 	// Render pass and framebuffer.
329 	const auto renderPass	= makeRenderPass(vkd, device, colorFormat);
330 	const auto framebuffer	= makeFramebuffer(vkd, device, renderPass.get(), colorView.get(), colorExtent.width, colorExtent.height, colorLayers);
331 
332 	// Viewports and scissors.
333 	const std::vector<VkViewport>	viewports	(1u, makeViewport(colorExtent));
334 	const std::vector<VkRect2D>		scissors	(1u, makeRect2D(colorExtent));
335 
336 	// Pipelines with different provoking vertex modes.
337 	std::vector<Move<VkPipeline>>	pipelines;
338 
339 	VkPipelineRasterizationProvokingVertexStateCreateInfoEXT pvInfo = initVulkanStructure();
340 
341 	const VkPipelineRasterizationStateCreateInfo rasterState =
342 	{
343 		VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,		//	VkStructureType							sType;
344 		&pvInfo,														//	const void*								pNext;
345 		0u,																//	VkPipelineRasterizationStateCreateFlags	flags;
346 		VK_FALSE,														//	VkBool32								depthClampEnable;
347 		VK_FALSE,														//	VkBool32								rasterizerDiscardEnable;
348 		VK_POLYGON_MODE_FILL,											//	VkPolygonMode							polygonMode;
349 		VK_CULL_MODE_BACK_BIT,											//	VkCullModeFlags							cullMode;
350 		VK_FRONT_FACE_COUNTER_CLOCKWISE,								//	VkFrontFace								frontFace;
351 		VK_FALSE,														//	VkBool32								depthBiasEnable;
352 		0.0f,															//	float									depthBiasConstantFactor;
353 		0.0f,															//	float									depthBiasClamp;
354 		0.0f,															//	float									depthBiasSlopeFactor;
355 		1.0f,															//	float									lineWidth;
356 	};
357 
358 	for (const auto& pvMode : m_params->provokingVertices)
359 	{
360 		pvInfo.provokingVertexMode = pvMode;
361 
362 		pipelines.push_back(makeGraphicsPipeline(vkd, device, pipelineLayout.get(),
363 			DE_NULL, meshModule.get(), fragModule.get(),
364 			renderPass.get(), viewports, scissors, 0u, &rasterState));
365 	}
366 
367 	// Command pool and buffer.
368 	const auto cmdPool		= makeCommandPool(vkd, device, queueIndex);
369 	const auto cmdBufferPtr	= allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
370 	const auto cmdBuffer	= cmdBufferPtr.get();
371 
372 	beginCommandBuffer(vkd, cmdBuffer);
373 	beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0), clearColor);
374 	for (int32_t layer = 0; layer < static_cast<int32_t>(pipelines.size()); ++layer)
375 	{
376 		const auto& pipeline = pipelines.at(static_cast<size_t>(layer));
377 		vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
378 		vkd.cmdPushConstants(cmdBuffer, pipelineLayout.get(), pcStages, 0u, pcSize, &layer);
379 		vkd.cmdDrawMeshTasksEXT(cmdBuffer, 1u, 1u, 1u);
380 	}
381 	endRenderPass(vkd, cmdBuffer);
382 	{
383 		// Copy data to verification buffer.
384 		const auto preTransferBarrier = makeImageMemoryBarrier(
385 			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
386 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
387 			colorBuffer.get(), colorSRR);
388 
389 		cmdPipelineImageMemoryBarrier(vkd, cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, &preTransferBarrier);
390 
391 		const auto copyRegion = makeBufferImageCopy(colorExtent, colorSRL);
392 		vkd.cmdCopyImageToBuffer(cmdBuffer, colorBuffer.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, verificationBuffer.get(), 1u, &copyRegion);
393 
394 		const auto postTransferBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
395 
396 		cmdPipelineMemoryBarrier(vkd, cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, &postTransferBarrier);
397 	}
398 	endCommandBuffer(vkd, cmdBuffer);
399 	submitCommandsAndWait(vkd, device, queue, cmdBuffer);
400 
401 	// Verify colors.
402 	auto& verificationBufferAlloc	= verificationBuffer.getAllocation();
403 	void* verificationBufferData	= verificationBufferAlloc.getHostPtr();
404 	invalidateAlloc(vkd, device, verificationBufferAlloc);
405 
406 	const tcu::IVec3					iExtent			(static_cast<int>(colorExtent.width), static_cast<int>(colorExtent.height), static_cast<int>(colorLayers));
407 	const tcu::ConstPixelBufferAccess	resultAccess	(tcuFormat, iExtent, verificationBufferData);
408 
409 	const auto isLines		= (m_params->geometryType == Geometry::LINES);
410 	const auto colors		= (isLines ? getLineColors() : getTriangleColors());
411 
412 	bool	fail	= false;
413 	auto&	log		= m_context.getTestContext().getLog();
414 
415 	for (int z = 0; z < iExtent.z(); ++z)
416 	{
417 		const auto		pvMode				= m_params->provokingVertices.at(static_cast<size_t>(z));
418 		const auto		expectedIntColor	= (pvMode == VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT ? colors.front() : colors.back());
419 		const tcu::Vec4	expectedColor		(static_cast<float>(expectedIntColor.x()),
420 											 static_cast<float>(expectedIntColor.y()),
421 											 static_cast<float>(expectedIntColor.z()),
422 											 static_cast<float>(expectedIntColor.w()));
423 
424 		for (int y = 0; y < iExtent.y(); ++y)
425 			for (int x = 0; x < iExtent.x(); ++x)
426 			{
427 				const auto resultColor = resultAccess.getPixel(x, y, z);
428 				if (resultColor != expectedColor)
429 				{
430 					fail = true;
431 					std::ostringstream msg;
432 					msg << "Unexpected color found at layer " << z << " coordinates (" << x << ", " << y << "): expected " << expectedColor << " found " << resultColor;
433 					log << tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
434 				}
435 			}
436 	}
437 
438 	if (fail)
439 		return tcu::TestStatus::fail("Failed -- check log for details");
440 	return tcu::TestStatus::pass("Pass");
441 }
442 
443 using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
444 
445 } // anonymous namespace
446 
createMeshShaderProvokingVertexTestsEXT(tcu::TestContext & testCtx)447 tcu::TestCaseGroup* createMeshShaderProvokingVertexTestsEXT (tcu::TestContext& testCtx)
448 {
449 	const std::vector<Geometry> geometries { Geometry::LINES, Geometry::TRIANGLES };
450 
451 	const std::vector<ProvokingVertexModeVec> testModeCases
452 	{
453 		ProvokingVertexModeVec{VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT},
454 		ProvokingVertexModeVec{VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT},
455 		ProvokingVertexModeVec{VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT, VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT},
456 		ProvokingVertexModeVec{VK_PROVOKING_VERTEX_MODE_LAST_VERTEX_EXT, VK_PROVOKING_VERTEX_MODE_FIRST_VERTEX_EXT},
457 	};
458 
459 	GroupPtr provokingVertexGroup (new tcu::TestCaseGroup(testCtx, "provoking_vertex", ""));
460 
461 	for (const auto& geometry : geometries)
462 	{
463 		const auto	geometryName	= getCaseName(geometry);
464 		GroupPtr	geometryGroup	(new tcu::TestCaseGroup(testCtx, geometryName.c_str(), ""));
465 
466 		for (const auto& testModes : testModeCases)
467 		{
468 			const auto	modeName = getCaseName(testModes);
469 			TestParams	params
470 			{
471 				testModes,	//	ProvokingVertexModeVec	provokingVertices;
472 				geometry,	//	Geometry				geometryType;
473 			};
474 
475 			geometryGroup->addChild(new ProvokingVertexCase(testCtx, modeName, "", params));
476 		}
477 
478 		provokingVertexGroup->addChild(geometryGroup.release());
479 	}
480 
481 	return provokingVertexGroup.release();
482 }
483 
484 } // MeshShader
485 } // vkt
486