Home | History | Annotate | Download | only in protected_memory
      1 /*------------------------------------------------------------------------
      2  * Vulkan Conformance Tests
      3  * ------------------------
      4  *
      5  * Copyright (c) 2017 The Khronos Group Inc.
      6  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
      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 Protected content copy buffer to image tests
     23  *//*--------------------------------------------------------------------*/
     24 
     25 #include "vktProtectedMemCopyBufferToImageTests.hpp"
     26 
     27 #include "deRandom.hpp"
     28 #include "tcuTestLog.hpp"
     29 #include "tcuVector.hpp"
     30 
     31 #include "vkPrograms.hpp"
     32 #include "vktTestCase.hpp"
     33 #include "vktTestGroupUtil.hpp"
     34 #include "vkTypeUtil.hpp"
     35 #include "vkBuilderUtil.hpp"
     36 #include "vkCmdUtil.hpp"
     37 
     38 #include "vktProtectedMemContext.hpp"
     39 #include "vktProtectedMemUtils.hpp"
     40 #include "vktProtectedMemImageValidator.hpp"
     41 
     42 namespace vkt
     43 {
     44 namespace ProtectedMem
     45 {
     46 
     47 namespace
     48 {
     49 
     50 enum {
     51 	BUFFER_SIZE		= 256,
     52 	RENDER_WIDTH	= 8,
     53 	RENDER_HEIGHT	= 8,
     54 };
     55 
     56 class CopyBufferToImageTestInstance : public ProtectedTestInstance
     57 {
     58 public:
     59 									CopyBufferToImageTestInstance	(Context&					ctx,
     60 																	 const deUint32				fillValue,
     61 																	 const ValidationData&		refData,
     62 																	 const ImageValidator&		validator,
     63 																	 const CmdBufferType		cmdBufferType);
     64 virtual tcu::TestStatus				iterate							 (void);
     65 
     66 private:
     67 	const vk::VkFormat				m_imageFormat;
     68 	const deUint32					m_fillValue;
     69 	const ValidationData&			m_refData;
     70 	const ImageValidator&			m_validator;
     71 	const CmdBufferType				m_cmdBufferType;
     72 };
     73 
     74 class CopyBufferToImageTestCase : public TestCase
     75 {
     76 public:
     77 								CopyBufferToImageTestCase	(tcu::TestContext&			testCtx,
     78 															 const std::string&			name,
     79 															 deUint32					fillValue,
     80 															 ValidationData				data,
     81 															 CmdBufferType				cmdBufferType)
     82 									: TestCase				(testCtx, name, "Copy buffer to image.")
     83 									, m_fillValue			(fillValue)
     84 									, m_refData				(data)
     85 									, m_validator			(vk::VK_FORMAT_R32G32B32A32_SFLOAT)
     86 									, m_cmdBufferType		(cmdBufferType)
     87 								{
     88 								}
     89 
     90 	virtual						~CopyBufferToImageTestCase	(void) {}
     91 	virtual TestInstance*		createInstance				(Context& ctx) const
     92 								{
     93 									return new CopyBufferToImageTestInstance(ctx, m_fillValue, m_refData, m_validator, m_cmdBufferType);
     94 								}
     95 	virtual void				initPrograms				(vk::SourceCollections& programCollection) const
     96 								{
     97 									m_validator.initPrograms(programCollection);
     98 								}
     99 private:
    100 	deUint32					m_fillValue;
    101 	ValidationData				m_refData;
    102 	ImageValidator				m_validator;
    103 	CmdBufferType				m_cmdBufferType;
    104 };
    105 
    106 CopyBufferToImageTestInstance::CopyBufferToImageTestInstance	(Context&					ctx,
    107 																 const deUint32				fillValue,
    108 																 const ValidationData&		refData,
    109 																 const ImageValidator&		validator,
    110 																 const CmdBufferType		cmdBufferType)
    111 	: ProtectedTestInstance		(ctx)
    112 	, m_imageFormat				(vk::VK_FORMAT_R32G32B32A32_SFLOAT)
    113 	, m_fillValue				(fillValue)
    114 	, m_refData					(refData)
    115 	, m_validator				(validator)
    116 	, m_cmdBufferType			(cmdBufferType)
    117 {
    118 }
    119 
    120 tcu::TestStatus CopyBufferToImageTestInstance::iterate()
    121 {
    122 	ProtectedContext&					ctx					(m_protectedContext);
    123 	const vk::DeviceInterface&			vk					= ctx.getDeviceInterface();
    124 	const vk::VkDevice					device				= ctx.getDevice();
    125 	const vk::VkQueue					queue				= ctx.getQueue();
    126 	const deUint32						queueFamilyIndex	= ctx.getQueueFamilyIndex();
    127 
    128 	// Create destination image
    129 	de::MovePtr<vk::ImageWithMemory>	colorImage			= createImage2D(ctx, PROTECTION_ENABLED, queueFamilyIndex,
    130 																			RENDER_WIDTH, RENDER_HEIGHT,
    131 																			m_imageFormat,
    132 																			vk::VK_IMAGE_USAGE_SAMPLED_BIT|vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT);
    133 	de::MovePtr<vk::BufferWithMemory>	srcBuffer			(makeBuffer(ctx,
    134 																		PROTECTION_ENABLED,
    135 																		queueFamilyIndex,
    136 																		(deUint32)(BUFFER_SIZE * sizeof(deUint32)),
    137 																		vk::VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT
    138 																			| vk::VK_BUFFER_USAGE_TRANSFER_SRC_BIT
    139 																			| vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT,
    140 																		vk::MemoryRequirement::Protected));
    141 
    142 	vk::Unique<vk::VkCommandPool>		cmdPool				(makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex));
    143 	vk::Unique<vk::VkCommandBuffer>		cmdBuffer			(vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
    144 	vk::Unique<vk::VkCommandBuffer>		secondaryCmdBuffer	(vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_SECONDARY));
    145 	vk::VkCommandBuffer					targetCmdBuffer		= (m_cmdBufferType == CMD_BUFFER_SECONDARY) ? *secondaryCmdBuffer : *cmdBuffer;
    146 
    147 	// Begin cmd buffer
    148 	beginCommandBuffer(vk, *cmdBuffer);
    149 
    150 	if (m_cmdBufferType == CMD_BUFFER_SECONDARY)
    151 	{
    152 		// Begin secondary command buffer
    153 		const vk::VkCommandBufferInheritanceInfo	bufferInheritanceInfo	=
    154 		{
    155 			vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,		// sType
    156 			DE_NULL,													// pNext
    157 			DE_NULL,													// renderPass
    158 			0u,															// subpass
    159 			DE_NULL,													// framebuffer
    160 			VK_FALSE,													// occlusionQueryEnable
    161 			(vk::VkQueryControlFlags)0u,								// queryFlags
    162 			(vk::VkQueryPipelineStatisticFlags)0u,						// pipelineStatistics
    163 		};
    164 		beginSecondaryCommandBuffer(vk, *secondaryCmdBuffer, bufferInheritanceInfo);
    165 	}
    166 
    167 	// Start src buffer barrier
    168 	{
    169 		const vk::VkBufferMemoryBarrier startBufferBarrier =
    170 		{
    171 			vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,	// VkStructureType		sType
    172 			DE_NULL,										// const void*			pNext
    173 			0,												// VkAccessFlags		srcAccessMask
    174 			vk::VK_ACCESS_TRANSFER_WRITE_BIT,				// VkAccessFlags		dstAccessMask
    175 			queueFamilyIndex,								// uint32_t				srcQueueFamilyIndex
    176 			queueFamilyIndex,								// uint32_t				dstQueueFamilyIndex
    177 			**srcBuffer,									// VkBuffer				buffer
    178 			0u,												// VkDeviceSize			offset
    179 			VK_WHOLE_SIZE,									// VkDeviceSize			size
    180 		};
    181 		vk.cmdPipelineBarrier(targetCmdBuffer,
    182 							  vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
    183 							  vk::VK_PIPELINE_STAGE_TRANSFER_BIT,
    184 							  (vk::VkDependencyFlags) 0,
    185 							  0, (const vk::VkMemoryBarrier *) DE_NULL,
    186 							  1, &startBufferBarrier,
    187 							  0, (const vk::VkImageMemoryBarrier *) DE_NULL);
    188 	}
    189 	vk.cmdFillBuffer(targetCmdBuffer, **srcBuffer, 0u, VK_WHOLE_SIZE, m_fillValue);
    190 
    191 	{
    192 		// Barrier to change accessMask to transfer read bit for source buffer
    193 		const vk::VkBufferMemoryBarrier startCopyBufferBarrier =
    194 		{
    195 			vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,		// VkStructureType		sType
    196 			DE_NULL,											// const void*			pNext
    197 			vk::VK_ACCESS_TRANSFER_WRITE_BIT,					// VkAccessFlags		srcAccessMask
    198 			vk::VK_ACCESS_TRANSFER_READ_BIT,					// VkAccessFlags		dstAccessMask
    199 			queueFamilyIndex,									// uint32_t				srcQueueFamilyIndex
    200 			queueFamilyIndex,									// uint32_t				dstQueueFamilyIndex
    201 			**srcBuffer,										// VkBuffer				buffer
    202 			0u,													// VkDeviceSize			offset
    203 			VK_WHOLE_SIZE,										// VkDeviceSize			size
    204 		};
    205 
    206 		// Start image barrier for destination image.
    207 		const vk::VkImageMemoryBarrier	startImgBarrier		=
    208 		{
    209 			vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,			// VkStructureType		sType
    210 			DE_NULL,											// const void*			pNext
    211 			0,													// VkAccessFlags		srcAccessMask
    212 			vk::VK_ACCESS_TRANSFER_WRITE_BIT,					// VkAccessFlags		dstAccessMask
    213 			vk::VK_IMAGE_LAYOUT_UNDEFINED,						// VkImageLayout		oldLayout
    214 			vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,			// VkImageLayout		newLayout
    215 			queueFamilyIndex,									// uint32_t				srcQueueFamilyIndex
    216 			queueFamilyIndex,									// uint32_t				dstQueueFamilyIndex
    217 			**colorImage,										// VkImage				image
    218 			{
    219 				vk::VK_IMAGE_ASPECT_COLOR_BIT,					// VkImageAspectFlags	aspectMask
    220 				0u,												// uint32_t				baseMipLevel
    221 				1u,												// uint32_t				mipLevels
    222 				0u,												// uint32_t				baseArraySlice
    223 				1u,												// uint32_t				subresourceRange
    224 			}
    225 		};
    226 
    227 		vk.cmdPipelineBarrier(targetCmdBuffer,
    228 							  vk::VK_PIPELINE_STAGE_TRANSFER_BIT,
    229 							  vk::VK_PIPELINE_STAGE_TRANSFER_BIT,
    230 							  (vk::VkDependencyFlags)0,
    231 							  0, (const vk::VkMemoryBarrier*)DE_NULL,
    232 							  1, &startCopyBufferBarrier,
    233 							  1, &startImgBarrier);
    234 	}
    235 
    236 	// Copy buffer to image
    237 	const vk::VkImageSubresourceLayers	subresourceLayers	=
    238 	{
    239 		vk::VK_IMAGE_ASPECT_COLOR_BIT,	// VkImageAspectFlags		aspectMask
    240 		0u,								// uint32_t					mipLevel
    241 		0u,								// uint32_t					baseArrayLayer
    242 		1u,								// uint32_t					layerCount
    243 	};
    244 	const vk::VkOffset3D				nullOffset			= {0u, 0u, 0u};
    245 	const vk::VkExtent3D				imageExtent			= {(deUint32)RENDER_WIDTH, (deUint32)RENDER_HEIGHT, 1u};
    246 	const vk::VkBufferImageCopy			copyRegion			=
    247 	{
    248 		0ull,							// VkDeviceSize				srcOffset;
    249 		0,								// uint32_t					bufferRowLength
    250 		0,								// uint32_t					bufferImageHeight
    251 		subresourceLayers,				// VkImageSubresourceLayers	imageSubresource
    252 		nullOffset,						// VkOffset3D				imageOffset
    253 		imageExtent,					// VkExtent3D				imageExtent
    254 	};
    255 	vk.cmdCopyBufferToImage(targetCmdBuffer, **srcBuffer, **colorImage, vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1u, &copyRegion);
    256 
    257 	{
    258 		const vk::VkImageMemoryBarrier	endImgBarrier		=
    259 		{
    260 			vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,			// VkStructureType		sType
    261 			DE_NULL,											// const void*			pNext
    262 			vk::VK_ACCESS_TRANSFER_WRITE_BIT,					// VkAccessFlags		srcAccessMask
    263 			vk::VK_ACCESS_SHADER_READ_BIT,						// VkAccessFlags		dstAccessMask
    264 			vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,			// VkImageLayout		oldLayout
    265 			vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,		// VkImageLayout		newLayout
    266 			queueFamilyIndex,									// uint32_t				srcQueueFamilyIndex
    267 			queueFamilyIndex,									// uint32_t				dstQueueFamilyIndex
    268 			**colorImage,										// VkImage				image
    269 			{
    270 				vk::VK_IMAGE_ASPECT_COLOR_BIT,					// VkImageAspectFlags	aspectMask
    271 				0u,												// uint32_t				baseMipLevel
    272 				1u,												// uint32_t				mipLevels
    273 				0u,												// uint32_t				baseArraySlice
    274 				1u,												// uint32_t				subresourceRange
    275 			}
    276 		};
    277 		vk.cmdPipelineBarrier(targetCmdBuffer,
    278 							  vk::VK_PIPELINE_STAGE_TRANSFER_BIT,
    279 							  vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
    280 							  (vk::VkDependencyFlags)0,
    281 							  0, (const vk::VkMemoryBarrier*)DE_NULL,
    282 							  0, (const vk::VkBufferMemoryBarrier*)DE_NULL,
    283 							  1, &endImgBarrier);
    284 	}
    285 
    286 	if (m_cmdBufferType == CMD_BUFFER_SECONDARY)
    287 	{
    288 		endCommandBuffer(vk, *secondaryCmdBuffer);
    289 		vk.cmdExecuteCommands(*cmdBuffer, 1u, &secondaryCmdBuffer.get());
    290 	}
    291 
    292 	endCommandBuffer(vk, *cmdBuffer);
    293 
    294 	// Submit command buffer
    295 	const vk::Unique<vk::VkFence>	fence		(vk::createFence(vk, device));
    296 	VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull));
    297 
    298 	// Log out test data
    299 	ctx.getTestContext().getLog()
    300 		<< tcu::TestLog::Message << "Fill value: " << m_fillValue << tcu::TestLog::EndMessage;
    301 
    302 	// Validate resulting image
    303 	if (m_validator.validateImage(ctx, m_refData, **colorImage, m_imageFormat, vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL))
    304 		return tcu::TestStatus::pass("Everything went OK");
    305 	else
    306 		return tcu::TestStatus::fail("Something went really wrong");
    307 }
    308 
    309 tcu::TestCaseGroup*	createCopyBufferToImageTests (tcu::TestContext& testCtx, CmdBufferType cmdBufferType)
    310 {
    311 	struct {
    312 		const union {
    313 			float		flt;
    314 			deUint32	uint;
    315 		}						fillValue;
    316 		const ValidationData	data;
    317 	} testData[] = {
    318 		{	{ 0.0f },
    319 			{
    320 				{ tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 0.0f, 0.0f),
    321 				  tcu::Vec4(0.1f, 0.1f, 0.0f, 0.0f), tcu::Vec4(0.5f, 0.5f, 0.0f, 0.0f), },
    322 				{ tcu::Vec4(0.0f), tcu::Vec4(0.0f),
    323 				  tcu::Vec4(0.0f), tcu::Vec4(0.0f), }
    324 			}
    325 		},
    326 		{	{ 1.0f },
    327 			{
    328 				{ tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 0.0f, 0.0f),
    329 				  tcu::Vec4(0.1f, 0.1f, 0.0f, 0.0f), tcu::Vec4(0.5f, 0.5f, 0.0f, 0.0f), },
    330 				{ tcu::Vec4(1.0f), tcu::Vec4(1.0f),
    331 				  tcu::Vec4(1.0f), tcu::Vec4(1.0f), }
    332 			}
    333 		},
    334 		{	{ 0.2f },
    335 			{
    336 				{ tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 0.0f, 0.0f),
    337 				  tcu::Vec4(0.1f, 0.1f, 0.0f, 0.0f), tcu::Vec4(0.5f, 0.5f, 0.0f, 0.0f), },
    338 				{ tcu::Vec4(0.2f), tcu::Vec4(0.2f),
    339 				  tcu::Vec4(0.2f), tcu::Vec4(0.2f), }
    340 			}
    341 		},
    342 		{	{ 0.55f },
    343 			{
    344 				{ tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 0.0f, 0.0f),
    345 				  tcu::Vec4(0.1f, 0.1f, 0.0f, 0.0f), tcu::Vec4(0.5f, 0.5f, 0.0f, 0.0f), },
    346 				{ tcu::Vec4(0.55f), tcu::Vec4(0.55f),
    347 				  tcu::Vec4(0.55f), tcu::Vec4(0.55f), }
    348 			}
    349 		},
    350 		{	{ 0.82f },
    351 			{
    352 				{ tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 0.0f, 0.0f),
    353 				  tcu::Vec4(0.1f, 0.1f, 0.0f, 0.0f), tcu::Vec4(0.5f, 0.5f, 0.0f, 0.0f), },
    354 				{ tcu::Vec4(0.82f), tcu::Vec4(0.82f),
    355 				  tcu::Vec4(0.82f), tcu::Vec4(0.82f), }
    356 			}
    357 		},
    358 		{	{ 0.96f },
    359 			{
    360 				{ tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), tcu::Vec4(1.0f, 1.0f, 0.0f, 0.0f),
    361 				  tcu::Vec4(0.1f, 0.1f, 0.0f, 0.0f), tcu::Vec4(0.5f, 0.5f, 0.0f, 0.0f), },
    362 				{ tcu::Vec4(0.96f), tcu::Vec4(0.96f),
    363 				  tcu::Vec4(0.96f), tcu::Vec4(0.96f), }
    364 			}
    365 		},
    366 	};
    367 
    368 	de::MovePtr<tcu::TestCaseGroup>	copyStaticTests		(new tcu::TestCaseGroup(testCtx, "static", "Copy Buffer To Image Tests with static input"));
    369 
    370 	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(testData); ++ndx)
    371 	{
    372 		const std::string name = "copy_" + de::toString(ndx + 1);
    373 		copyStaticTests->addChild(new CopyBufferToImageTestCase(testCtx, name.c_str(), testData[ndx].fillValue.uint, testData[ndx].data, cmdBufferType));
    374 	}
    375 
    376 	/* Add a few randomized tests */
    377 	de::MovePtr<tcu::TestCaseGroup>	copyRandomTests		(new tcu::TestCaseGroup(testCtx, "random", "Copy Buffer To Image Tests with random input"));
    378 	const int						testCount			= 10;
    379 	de::Random						rnd					(testCtx.getCommandLine().getBaseSeed());
    380 	for (int ndx = 0; ndx < testCount; ++ndx)
    381 	{
    382 		const std::string	name		= "copy_" + de::toString(ndx + 1);
    383 
    384 		const union {
    385 			float		flt;
    386 			deUint32	uint;
    387 		}					fillValue	= { rnd.getFloat(0.0, 1.0f) };
    388 
    389 		tcu::Vec4			refValue	(fillValue.flt);
    390 		ValidationData		data		=
    391 		{
    392 			{ tcu::Vec4(rnd.getFloat(0.0f, 1.0f), rnd.getFloat(0.0f, 1.0f), rnd.getFloat(0.0f, 1.0f), rnd.getFloat(0.0f, 1.0f)),
    393 			  tcu::Vec4(rnd.getFloat(0.0f, 1.0f), rnd.getFloat(0.0f, 1.0f), rnd.getFloat(0.0f, 1.0f), rnd.getFloat(0.0f, 1.0f)),
    394 			  tcu::Vec4(rnd.getFloat(0.0f, 1.0f), rnd.getFloat(0.0f, 1.0f), rnd.getFloat(0.0f, 1.0f), rnd.getFloat(0.0f, 1.0f)),
    395 			  tcu::Vec4(rnd.getFloat(0.0f, 1.0f), rnd.getFloat(0.0f, 1.0f), rnd.getFloat(0.0f, 1.0f), rnd.getFloat(0.0f, 1.0f)) },
    396 			{ refValue, refValue, refValue, refValue }
    397 		};
    398 		copyRandomTests->addChild(new CopyBufferToImageTestCase(testCtx, name.c_str(), fillValue.uint, data, cmdBufferType));
    399 	}
    400 
    401 	std::string groupName = getCmdBufferTypeStr(cmdBufferType);
    402 	std::string groupDesc = "Copy Buffer To Image Tests with " + groupName + " command buffer";
    403 	de::MovePtr<tcu::TestCaseGroup> copyTests (new tcu::TestCaseGroup(testCtx, groupName.c_str(), groupDesc.c_str()));
    404 	copyTests->addChild(copyStaticTests.release());
    405 	copyTests->addChild(copyRandomTests.release());
    406 	return copyTests.release();
    407 }
    408 
    409 } // anonymous
    410 
    411 tcu::TestCaseGroup*	createCopyBufferToImageTests (tcu::TestContext& testCtx)
    412 {
    413 	de::MovePtr<tcu::TestCaseGroup> clearTests (new tcu::TestCaseGroup(testCtx, "copy_buffer_to_image", "Copy Buffer To Image Tests"));
    414 
    415 	clearTests->addChild(createCopyBufferToImageTests(testCtx, CMD_BUFFER_PRIMARY));
    416 	clearTests->addChild(createCopyBufferToImageTests(testCtx, CMD_BUFFER_SECONDARY));
    417 
    418 	return clearTests.release();
    419 }
    420 
    421 } // ProtectedMem
    422 } // vkt
    423