Home | History | Annotate | Download | only in tessellation
      1 /*------------------------------------------------------------------------
      2  * Vulkan Conformance Tests
      3  * ------------------------
      4  *
      5  * Copyright (c) 2014 The Android Open Source Project
      6  * Copyright (c) 2016 The Khronos Group Inc.
      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 Tessellation Geometry Interaction - Point Size
     23 *//*--------------------------------------------------------------------*/
     24 
     25 #include "vktTessellationGeometryPassthroughTests.hpp"
     26 #include "vktTestCaseUtil.hpp"
     27 #include "vktTessellationUtil.hpp"
     28 
     29 #include "tcuTestLog.hpp"
     30 
     31 #include "vkDefs.hpp"
     32 #include "vkBarrierUtil.hpp"
     33 #include "vkQueryUtil.hpp"
     34 #include "vkBuilderUtil.hpp"
     35 #include "vkTypeUtil.hpp"
     36 #include "vkImageUtil.hpp"
     37 #include "vkCmdUtil.hpp"
     38 #include "vkObjUtil.hpp"
     39 
     40 #include "deUniquePtr.hpp"
     41 
     42 #include <string>
     43 #include <vector>
     44 
     45 namespace vkt
     46 {
     47 namespace tessellation
     48 {
     49 
     50 using namespace vk;
     51 
     52 namespace
     53 {
     54 
     55 enum Constants
     56 {
     57 	RENDER_SIZE = 32,
     58 };
     59 
     60 enum FlagBits
     61 {
     62 	FLAG_VERTEX_SET						= 1u << 0,		// !< set gl_PointSize in vertex shader
     63 	FLAG_TESSELLATION_EVALUATION_SET	= 1u << 1,		// !< set gl_PointSize in tessellation evaluation shader
     64 	FLAG_TESSELLATION_ADD				= 1u << 2,		// !< read and add to gl_PointSize in tessellation shader pair
     65 	FLAG_GEOMETRY_SET					= 1u << 3,		// !< set gl_PointSize in geometry shader
     66 	FLAG_GEOMETRY_ADD					= 1u << 4,		// !< read and add to gl_PointSize in geometry shader
     67 };
     68 typedef deUint32 Flags;
     69 
     70 void checkPointSizeRequirements (const InstanceInterface& vki, const VkPhysicalDevice physDevice, const int maxPointSize)
     71 {
     72 	const VkPhysicalDeviceProperties properties = getPhysicalDeviceProperties(vki, physDevice);
     73 	if (maxPointSize > static_cast<int>(properties.limits.pointSizeRange[1]))
     74 		throw tcu::NotSupportedError("Test requires point size " + de::toString(maxPointSize));
     75 	// Point size granularity must be 1.0 at most, so no need to check it for this test.
     76 }
     77 
     78 int getExpectedPointSize (const Flags flags)
     79 {
     80 	int addition = 0;
     81 
     82 	// geometry
     83 	if (flags & FLAG_GEOMETRY_SET)
     84 		return 6;
     85 	else if (flags & FLAG_GEOMETRY_ADD)
     86 		addition += 2;
     87 
     88 	// tessellation
     89 	if (flags & FLAG_TESSELLATION_EVALUATION_SET)
     90 		return 4 + addition;
     91 	else if (flags & FLAG_TESSELLATION_ADD)
     92 		addition += 2;
     93 
     94 	// vertex
     95 	if (flags & FLAG_VERTEX_SET)
     96 		return 2 + addition;
     97 
     98 	// undefined
     99 	DE_ASSERT(false);
    100 	return -1;
    101 }
    102 
    103 inline bool isTessellationStage (const Flags flags)
    104 {
    105 	return (flags & (FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD)) != 0;
    106 }
    107 
    108 inline bool isGeometryStage (const Flags flags)
    109 {
    110 	return (flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD)) != 0;
    111 }
    112 
    113 bool verifyImage (tcu::TestLog& log, const tcu::ConstPixelBufferAccess image, const int expectedSize)
    114 {
    115 	log << tcu::TestLog::Message << "Verifying rendered point size. Expecting " << expectedSize << " pixels." << tcu::TestLog::EndMessage;
    116 
    117 	bool			resultAreaFound	= false;
    118 	tcu::IVec4		resultArea;
    119 	const tcu::Vec4	black(0.0, 0.0, 0.0, 1.0);
    120 
    121 	// Find rasterization output area
    122 
    123 	for (int y = 0; y < image.getHeight(); ++y)
    124 	for (int x = 0; x < image.getWidth();  ++x)
    125 		if (image.getPixel(x, y) != black)
    126 		{
    127 			if (!resultAreaFound)
    128 			{
    129 				// first fragment
    130 				resultArea = tcu::IVec4(x, y, x + 1, y + 1);
    131 				resultAreaFound = true;
    132 			}
    133 			else
    134 			{
    135 				// union area
    136 				resultArea.x() = de::min(resultArea.x(), x);
    137 				resultArea.y() = de::min(resultArea.y(), y);
    138 				resultArea.z() = de::max(resultArea.z(), x+1);
    139 				resultArea.w() = de::max(resultArea.w(), y+1);
    140 			}
    141 		}
    142 
    143 	if (!resultAreaFound)
    144 	{
    145 		log << tcu::TestLog::Message << "Verification failed, could not find any point fragments." << tcu::TestLog::EndMessage;
    146 		return false;
    147 	}
    148 
    149 	const tcu::IVec2 pointSize = resultArea.swizzle(2,3) - resultArea.swizzle(0, 1);
    150 
    151 	if (pointSize.x() != pointSize.y())
    152 	{
    153 		log << tcu::TestLog::Message << "ERROR! Rasterized point is not a square. Point size was " << pointSize << tcu::TestLog::EndMessage;
    154 		return false;
    155 	}
    156 
    157 	if (pointSize.x() != expectedSize)
    158 	{
    159 		log << tcu::TestLog::Message << "ERROR! Point size invalid, expected " << expectedSize << ", got " << pointSize.x() << tcu::TestLog::EndMessage;
    160 		return false;
    161 	}
    162 
    163 	return true;
    164 }
    165 
    166 void initPrograms (vk::SourceCollections& programCollection, const Flags flags)
    167 {
    168 	// Vertex shader
    169 	{
    170 		std::ostringstream src;
    171 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    172 			<< "\n"
    173 			<< "void main (void)\n"
    174 			<< "{\n"
    175 			<< "    gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n";
    176 
    177 		if (flags & FLAG_VERTEX_SET)
    178 			src << "    gl_PointSize = 2.0;\n";
    179 
    180 		src << "}\n";
    181 
    182 		programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
    183 	}
    184 
    185 	// Fragment shader
    186 	{
    187 		std::ostringstream src;
    188 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    189 			<< "layout(location = 0) out mediump vec4 fragColor;\n"
    190 			<< "\n"
    191 			<< "void main (void)\n"
    192 			<< "{\n"
    193 			<< "    fragColor = vec4(1.0);\n"
    194 			<< "}\n";
    195 
    196 		programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
    197 	}
    198 
    199 	if (isTessellationStage(flags))
    200 	{
    201 		// Tessellation control shader
    202 		{
    203 			std::ostringstream src;
    204 			src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    205 				<< "#extension GL_EXT_tessellation_shader : require\n"
    206 				<< "#extension GL_EXT_tessellation_point_size : require\n"
    207 				<< "layout(vertices = 1) out;\n"
    208 				<< "\n"
    209 				<< "void main (void)\n"
    210 				<< "{\n"
    211 				<< "    gl_TessLevelOuter[0] = 3.0;\n"
    212 				<< "    gl_TessLevelOuter[1] = 3.0;\n"
    213 				<< "    gl_TessLevelOuter[2] = 3.0;\n"
    214 				<< "    gl_TessLevelInner[0] = 3.0;\n"
    215 				<< "\n"
    216 				<< "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n";
    217 
    218 			if (flags & FLAG_TESSELLATION_ADD)
    219 				src << "    // pass as is to eval\n"
    220 					<< "    gl_out[gl_InvocationID].gl_PointSize = gl_in[gl_InvocationID].gl_PointSize;\n";
    221 
    222 			src << "}\n";
    223 
    224 			programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
    225 		}
    226 
    227 		// Tessellation evaluation shader
    228 		{
    229 			std::ostringstream src;
    230 			src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    231 				<< "#extension GL_EXT_tessellation_shader : require\n"
    232 				<< "#extension GL_EXT_tessellation_point_size : require\n"
    233 				<< "layout(triangles, point_mode) in;\n"
    234 				<< "\n"
    235 				<< "void main (void)\n"
    236 				<< "{\n"
    237 				<< "    // hide all but one vertex\n"
    238 				<< "    if (gl_TessCoord.x < 0.99)\n"
    239 				<< "        gl_Position = vec4(-2.0, 0.0, 0.0, 1.0);\n"
    240 				<< "    else\n"
    241 				<< "        gl_Position = gl_in[0].gl_Position;\n";
    242 
    243 			if (flags & FLAG_TESSELLATION_ADD)
    244 				src << "\n"
    245 					<< "    // add to point size\n"
    246 					<< "    gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
    247 			else if (flags & FLAG_TESSELLATION_EVALUATION_SET)
    248 				src << "\n"
    249 					<< "    // set point size\n"
    250 					<< "    gl_PointSize = 4.0;\n";
    251 
    252 			src << "}\n";
    253 
    254 			programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
    255 		}
    256 	}
    257 
    258 	if (isGeometryStage(flags))
    259 	{
    260 		// Geometry shader
    261 		std::ostringstream src;
    262 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
    263 			<< "#extension GL_EXT_geometry_shader : require\n"
    264 			<< "#extension GL_EXT_geometry_point_size : require\n"
    265 			<< "layout(points) in;\n"
    266 			<< "layout(points, max_vertices = 1) out;\n"
    267 			<< "\n"
    268 			<< "void main (void)\n"
    269 			<< "{\n"
    270 			<< "    gl_Position  = gl_in[0].gl_Position;\n";
    271 
    272 		if (flags & FLAG_GEOMETRY_SET)
    273 			src << "    gl_PointSize = 6.0;\n";
    274 		else if (flags & FLAG_GEOMETRY_ADD)
    275 			src << "    gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
    276 
    277 		src << "\n"
    278 			<< "    EmitVertex();\n"
    279 			<< "}\n";
    280 
    281 		programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
    282 	}
    283 }
    284 
    285 tcu::TestStatus test (Context& context, const Flags flags)
    286 {
    287 	const int expectedPointSize = getExpectedPointSize(flags);
    288 	{
    289 		const InstanceInterface& vki        = context.getInstanceInterface();
    290 		const VkPhysicalDevice   physDevice = context.getPhysicalDevice();
    291 
    292 		requireFeatures           (vki, physDevice, FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER | FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE);
    293 		checkPointSizeRequirements(vki, physDevice, expectedPointSize);
    294 	}
    295 	{
    296 		tcu::TestLog& log = context.getTestContext().getLog();
    297 
    298 		if (flags & FLAG_VERTEX_SET)
    299 			log << tcu::TestLog::Message << "Setting point size in vertex shader to 2.0." << tcu::TestLog::EndMessage;
    300 		if (flags & FLAG_TESSELLATION_EVALUATION_SET)
    301 			log << tcu::TestLog::Message << "Setting point size in tessellation evaluation shader to 4.0." << tcu::TestLog::EndMessage;
    302 		if (flags & FLAG_TESSELLATION_ADD)
    303 			log << tcu::TestLog::Message << "Reading point size in tessellation control shader and adding 2.0 to it in evaluation." << tcu::TestLog::EndMessage;
    304 		if (flags & FLAG_GEOMETRY_SET)
    305 			log << tcu::TestLog::Message << "Setting point size in geometry shader to 6.0." << tcu::TestLog::EndMessage;
    306 		if (flags & FLAG_GEOMETRY_ADD)
    307 			log << tcu::TestLog::Message << "Reading point size in geometry shader and adding 2.0." << tcu::TestLog::EndMessage;
    308 	}
    309 
    310 	const DeviceInterface&	vk					= context.getDeviceInterface();
    311 	const VkDevice			device				= context.getDevice();
    312 	const VkQueue			queue				= context.getUniversalQueue();
    313 	const deUint32			queueFamilyIndex	= context.getUniversalQueueFamilyIndex();
    314 	Allocator&				allocator			= context.getDefaultAllocator();
    315 
    316 	// Color attachment
    317 
    318 	const tcu::IVec2			  renderSize				 = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
    319 	const VkFormat				  colorFormat				 = VK_FORMAT_R8G8B8A8_UNORM;
    320 	const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
    321 	const Image					  colorAttachmentImage		 (vk, device, allocator,
    322 															 makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
    323 															 MemoryRequirement::Any);
    324 
    325 	// Color output buffer
    326 
    327 	const VkDeviceSize	colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
    328 	const Buffer		colorBuffer          (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
    329 
    330 	// Pipeline
    331 
    332 	const Unique<VkImageView>		colorAttachmentView(makeImageView						(vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
    333 	const Unique<VkRenderPass>		renderPass		   (makeRenderPass						(vk, device, colorFormat));
    334 	const Unique<VkFramebuffer>		framebuffer		   (makeFramebuffer						(vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
    335 	const Unique<VkPipelineLayout>	pipelineLayout	   (makePipelineLayoutWithoutDescriptors(vk, device));
    336 	const Unique<VkCommandPool>		cmdPool			   (makeCommandPool						(vk, device, queueFamilyIndex));
    337 	const Unique<VkCommandBuffer>	cmdBuffer		   (allocateCommandBuffer				(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
    338 
    339 	GraphicsPipelineBuilder			pipelineBuilder;
    340 
    341 	pipelineBuilder
    342 		.setPrimitiveTopology		  (VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
    343 		.setRenderSize				  (renderSize)
    344 		.setPatchControlPoints		  (1)
    345 		.setShader					  (vk, device, VK_SHADER_STAGE_VERTEX_BIT,					context.getBinaryCollection().get("vert"), DE_NULL)
    346 		.setShader					  (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,				context.getBinaryCollection().get("frag"), DE_NULL);
    347 
    348 	if (isTessellationStage(flags))
    349 		pipelineBuilder
    350 			.setShader				  (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,	context.getBinaryCollection().get("tesc"), DE_NULL)
    351 			.setShader				  (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL);
    352 
    353 	if (isGeometryStage(flags))
    354 		pipelineBuilder
    355 			.setShader				  (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT,				context.getBinaryCollection().get("geom"), DE_NULL);
    356 
    357 	const Unique<VkPipeline> pipeline(pipelineBuilder.build(vk, device, *pipelineLayout, *renderPass));
    358 
    359 	// Draw commands
    360 
    361 	beginCommandBuffer(vk, *cmdBuffer);
    362 
    363 	{
    364 		const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
    365 			(VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
    366 			VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    367 			*colorAttachmentImage, colorImageSubresourceRange);
    368 
    369 		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u,
    370 			0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
    371 	}
    372 
    373 	// Begin render pass
    374 	{
    375 		const VkRect2D	renderArea	= makeRect2D(renderSize);
    376 		const tcu::Vec4	clearColor	(0.0f, 0.0f, 0.0f, 1.0f);
    377 
    378 		beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
    379 	}
    380 
    381 	vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
    382 
    383 	vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
    384 	endRenderPass(vk, *cmdBuffer);
    385 
    386 	// Copy render result to a host-visible buffer
    387 	copyImageToBuffer(vk, *cmdBuffer, *colorAttachmentImage, *colorBuffer, renderSize);
    388 
    389 	endCommandBuffer(vk, *cmdBuffer);
    390 	submitCommandsAndWait(vk, device, queue, *cmdBuffer);
    391 
    392 	// Verify results
    393 	{
    394 		const Allocation& alloc = colorBuffer.getAllocation();
    395 		invalidateMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), colorBufferSizeBytes);
    396 		tcu::ConstPixelBufferAccess image(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, alloc.getHostPtr());
    397 
    398 		tcu::TestLog& log = context.getTestContext().getLog();
    399 		log << tcu::LogImage("color0", "", image);
    400 
    401 		if (verifyImage(log, image, expectedPointSize))
    402 			return tcu::TestStatus::pass("OK");
    403 		else
    404 			return tcu::TestStatus::fail("Didn't render expected point");
    405 	}
    406 }
    407 
    408 std::string getTestCaseName (const Flags flags)
    409 {
    410 	std::ostringstream buf;
    411 
    412 	// join per-bit descriptions into a single string with '_' separator
    413 	if (flags & FLAG_VERTEX_SET)					buf																		<< "vertex_set";
    414 	if (flags & FLAG_TESSELLATION_EVALUATION_SET)	buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1))	? ("_") : (""))	<< "evaluation_set";
    415 	if (flags & FLAG_TESSELLATION_ADD)				buf << ((flags & (FLAG_TESSELLATION_ADD-1))				? ("_") : (""))	<< "control_pass_eval_add";
    416 	if (flags & FLAG_GEOMETRY_SET)					buf << ((flags & (FLAG_GEOMETRY_SET-1))					? ("_") : (""))	<< "geometry_set";
    417 	if (flags & FLAG_GEOMETRY_ADD)					buf << ((flags & (FLAG_GEOMETRY_ADD-1))					? ("_") : (""))	<< "geometry_add";
    418 
    419 	return buf.str();
    420 }
    421 
    422 std::string getTestCaseDescription (const Flags flags)
    423 {
    424 	std::ostringstream buf;
    425 
    426 	// join per-bit descriptions into a single string with ", " separator
    427 	if (flags & FLAG_VERTEX_SET)					buf																			<< "set point size in vertex shader";
    428 	if (flags & FLAG_TESSELLATION_EVALUATION_SET)	buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1))	? (", ") : (""))	<< "set point size in tessellation evaluation shader";
    429 	if (flags & FLAG_TESSELLATION_ADD)				buf << ((flags & (FLAG_TESSELLATION_ADD-1))				? (", ") : (""))	<< "add to point size in tessellation shader";
    430 	if (flags & FLAG_GEOMETRY_SET)					buf << ((flags & (FLAG_GEOMETRY_SET-1))					? (", ") : (""))	<< "set point size in geometry shader";
    431 	if (flags & FLAG_GEOMETRY_ADD)					buf << ((flags & (FLAG_GEOMETRY_ADD-1))					? (", ") : (""))	<< "add to point size in geometry shader";
    432 
    433 	return buf.str();
    434 }
    435 
    436 } // anonymous
    437 
    438 //! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.point_size.*
    439 //! with the exception of the default 1.0 point size cases (not valid in Vulkan).
    440 tcu::TestCaseGroup* createGeometryPointSizeTests (tcu::TestContext& testCtx)
    441 {
    442 	de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "point_size", "Test point size"));
    443 
    444 	static const Flags caseFlags[] =
    445 	{
    446 		FLAG_VERTEX_SET,
    447 							FLAG_TESSELLATION_EVALUATION_SET,
    448 																	FLAG_GEOMETRY_SET,
    449 		FLAG_VERTEX_SET	|	FLAG_TESSELLATION_EVALUATION_SET,
    450 		FLAG_VERTEX_SET |											FLAG_GEOMETRY_SET,
    451 		FLAG_VERTEX_SET	|	FLAG_TESSELLATION_EVALUATION_SET	|	FLAG_GEOMETRY_SET,
    452 		FLAG_VERTEX_SET	|	FLAG_TESSELLATION_ADD				|	FLAG_GEOMETRY_ADD,
    453 	};
    454 
    455 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseFlags); ++ndx)
    456 	{
    457 		const std::string name = getTestCaseName       (caseFlags[ndx]);
    458 		const std::string desc = getTestCaseDescription(caseFlags[ndx]);
    459 
    460 		addFunctionCaseWithPrograms(group.get(), name, desc, initPrograms, test, caseFlags[ndx]);
    461 	}
    462 
    463 	return group.release();
    464 }
    465 
    466 } // tessellation
    467 } // vkt
    468