| /*------------------------------------------------------------------------ |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2017 The Khronos Group Inc. |
| * Copyright (c) 2017 Samsung Electronics Co., Ltd. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Protected Memory Utility methods |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vktProtectedMemUtils.hpp" |
| |
| #include "deString.h" |
| #include "deRandom.hpp" |
| |
| #include "vkDeviceUtil.hpp" |
| #include "vkQueryUtil.hpp" |
| #include "vkTypeUtil.hpp" |
| #include "vkDebugReportUtil.hpp" |
| #include "vkApiVersion.hpp" |
| #include "vkObjUtil.hpp" |
| |
| #include "vkPlatform.hpp" |
| #include "vktProtectedMemContext.hpp" |
| #include "vkWsiUtil.hpp" |
| #include "vkObjUtil.hpp" |
| |
| namespace vkt |
| { |
| |
| using namespace vk; |
| |
| namespace ProtectedMem |
| { |
| |
| typedef std::vector<vk::VkExtensionProperties> Extensions; |
| |
| std::vector<std::string> getValidationLayers (const vk::PlatformInterface& vkp) |
| { |
| static const char* s_magicLayer = "VK_LAYER_LUNARG_standard_validation"; |
| static const char* s_defaultLayers[] = |
| { |
| "VK_LAYER_GOOGLE_threading", |
| "VK_LAYER_LUNARG_parameter_validation", |
| "VK_LAYER_LUNARG_device_limits", |
| "VK_LAYER_LUNARG_object_tracker", |
| "VK_LAYER_LUNARG_image", |
| "VK_LAYER_LUNARG_core_validation", |
| "VK_LAYER_LUNARG_swapchain", |
| "VK_LAYER_GOOGLE_unique_objects" |
| }; |
| const std::vector<vk::VkLayerProperties> supportedLayers (enumerateInstanceLayerProperties(vkp)); |
| std::vector<std::string> enabledLayers; |
| |
| if (isLayerSupported(supportedLayers, vk::RequiredLayer(s_magicLayer))) |
| enabledLayers.push_back(s_magicLayer); |
| else |
| { |
| for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_defaultLayers); ++ndx) |
| { |
| if (isLayerSupported(supportedLayers, vk::RequiredLayer(s_defaultLayers[ndx]))) |
| enabledLayers.push_back(s_defaultLayers[ndx]); |
| } |
| } |
| |
| return enabledLayers; |
| } |
| |
| vk::Move<vk::VkInstance> makeProtectedMemInstance (const vk::PlatformInterface& vkp, const vkt::Context& context, const std::vector<std::string>& extraExtensions) |
| { |
| const Extensions supportedExtensions(vk::enumerateInstanceExtensionProperties(vkp, DE_NULL)); |
| std::vector<std::string> enabledLayers; |
| std::vector<std::string> requiredExtensions = extraExtensions; |
| const bool isValidationEnabled = context.getTestContext().getCommandLine().isValidationEnabled(); |
| |
| if (isValidationEnabled) |
| { |
| if (!vk::isDebugReportSupported(vkp)) |
| TCU_THROW(NotSupportedError, "VK_EXT_debug_report is not supported"); |
| |
| enabledLayers = getValidationLayers(vkp); |
| if (enabledLayers.empty()) |
| TCU_THROW(NotSupportedError, "No validation layers found"); |
| } |
| |
| if (!isCoreInstanceExtension(context.getUsedApiVersion(), "VK_KHR_get_physical_device_properties2")) |
| requiredExtensions.push_back("VK_KHR_get_physical_device_properties2"); |
| |
| for (std::vector<std::string>::const_iterator requiredExtName = requiredExtensions.begin(); |
| requiredExtName != requiredExtensions.end(); |
| ++requiredExtName) |
| { |
| if (!isInstanceExtensionSupported(context.getUsedApiVersion(), supportedExtensions, vk::RequiredExtension(*requiredExtName))) |
| TCU_THROW(NotSupportedError, (*requiredExtName + " is not supported").c_str()); |
| } |
| |
| return vk::createDefaultInstance(vkp, context.getUsedApiVersion(), enabledLayers, requiredExtensions); |
| } |
| |
| deUint32 chooseProtectedMemQueueFamilyIndex (const vk::InstanceDriver& vkd, |
| vk::VkPhysicalDevice physicalDevice, |
| vk::VkSurfaceKHR surface) |
| { |
| std::vector<vk::VkQueueFamilyProperties> properties; |
| deUint32 numFamilies = 0; |
| |
| vkd.getPhysicalDeviceQueueFamilyProperties(physicalDevice, &numFamilies, DE_NULL); |
| DE_ASSERT(numFamilies > 0); |
| properties.resize(numFamilies); |
| |
| vkd.getPhysicalDeviceQueueFamilyProperties(physicalDevice, &numFamilies, properties.data()); |
| |
| // Get a universal protected queue family index |
| vk::VkQueueFlags requiredFlags = vk::VK_QUEUE_GRAPHICS_BIT |
| | vk::VK_QUEUE_COMPUTE_BIT |
| #ifndef NOT_PROTECTED |
| | vk::VK_QUEUE_PROTECTED_BIT |
| #endif |
| ; |
| for (size_t idx = 0; idx < properties.size(); ++idx) |
| { |
| vk::VkQueueFlags flags = properties[idx].queueFlags; |
| |
| if (surface != DE_NULL |
| && vk::wsi::getPhysicalDeviceSurfaceSupport(vkd, physicalDevice, (deUint32)idx, surface) == VK_FALSE) |
| continue; // Skip the queue family index if it does not support the surface |
| |
| if ((flags & requiredFlags) == requiredFlags) |
| return (deUint32)idx; |
| } |
| |
| TCU_THROW(NotSupportedError, "No matching universal protected queue found"); |
| } |
| |
| vk::Move<vk::VkDevice> makeProtectedMemDevice (const vk::PlatformInterface& vkp, |
| vk::VkInstance instance, |
| const vk::InstanceDriver& vkd, |
| vk::VkPhysicalDevice physicalDevice, |
| const deUint32 queueFamilyIndex, |
| const deUint32 apiVersion, |
| const std::vector<std::string>& extraExtensions) |
| { |
| const Extensions supportedExtensions (vk::enumerateDeviceExtensionProperties(vkd, physicalDevice, DE_NULL)); |
| std::vector<std::string> requiredExtensions; |
| std::vector<std::string> extensions = extraExtensions; |
| |
| if (apiVersion < VK_API_VERSION_1_1) |
| TCU_THROW(NotSupportedError, "Vulkan 1.1 is not supported"); |
| |
| bool useYCbCr = de::contains(extensions.begin(), extensions.end(), std::string("VK_KHR_sampler_ycbcr_conversion")); |
| |
| // Check if the physical device supports the protected memory extension name |
| for (deUint32 ndx = 0; ndx < extensions.size(); ++ndx) |
| { |
| if (!isDeviceExtensionSupported(apiVersion, supportedExtensions, vk::RequiredExtension(extensions[ndx]))) |
| TCU_THROW(NotSupportedError, (extensions[ndx] + " is not supported").c_str()); |
| |
| if (!isCoreDeviceExtension(apiVersion, extensions[ndx])) |
| requiredExtensions.push_back(extensions[ndx]); |
| } |
| |
| std::vector<const char*> enabledExts (requiredExtensions.size()); |
| for (size_t idx = 0; idx < requiredExtensions.size(); ++idx) |
| { |
| enabledExts[idx] = requiredExtensions[idx].c_str(); |
| } |
| |
| vk::VkPhysicalDeviceSamplerYcbcrConversionFeatures ycbcrFeature = |
| { |
| vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, |
| DE_NULL, |
| VK_FALSE |
| }; |
| // Check if the protected memory can be enabled on the physical device. |
| vk::VkPhysicalDeviceProtectedMemoryFeatures protectedFeature = |
| { |
| vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES, // sType |
| &ycbcrFeature, // pNext |
| VK_FALSE // protectedMemory |
| }; |
| vk::VkPhysicalDeviceFeatures features; |
| deMemset(&features, 0, sizeof(vk::VkPhysicalDeviceFeatures)); |
| |
| vk::VkPhysicalDeviceFeatures2 featuresExt = |
| { |
| vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, // sType |
| &protectedFeature, // pNext |
| features |
| }; |
| |
| vkd.getPhysicalDeviceFeatures2(physicalDevice, &featuresExt); |
| |
| #ifndef NOT_PROTECTED |
| if (protectedFeature.protectedMemory == VK_FALSE) |
| TCU_THROW(NotSupportedError, "Protected Memory feature not supported by the device"); |
| #endif |
| |
| if (useYCbCr && !ycbcrFeature.samplerYcbcrConversion) |
| TCU_THROW(NotSupportedError, "VK_KHR_sampler_ycbcr_conversion is not supported"); |
| |
| const float queuePriorities[] = { 1.0f }; |
| const vk::VkDeviceQueueCreateInfo queueInfos[] = |
| { |
| { |
| vk::VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, |
| DE_NULL, |
| #ifndef NOT_PROTECTED |
| (vk::VkDeviceQueueCreateFlags)vk::VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT, |
| #else |
| (vk::VkDeviceQueueCreateFlags)0u, |
| #endif |
| queueFamilyIndex, |
| DE_LENGTH_OF_ARRAY(queuePriorities), |
| queuePriorities |
| } |
| }; |
| |
| const vk::VkDeviceCreateInfo deviceParams = |
| { |
| vk::VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // sType |
| &featuresExt, // pNext |
| (vk::VkDeviceCreateFlags)0, // flags |
| DE_LENGTH_OF_ARRAY(queueInfos), // queueCreateInfosCount |
| &queueInfos[0], // pQueueCreateInfos |
| 0u, // enabledLayerCount |
| DE_NULL, // pEnabledLayerNames |
| (deUint32)requiredExtensions.size(), // enabledExtensionCount |
| requiredExtensions.empty() ? DE_NULL : &enabledExts[0], // pEnabledExtensionNames |
| DE_NULL // pEnabledFeatures |
| }; |
| |
| return vk::createDevice(vkp, instance, vkd, physicalDevice, &deviceParams, DE_NULL); |
| } |
| |
| vk::VkQueue getProtectedQueue (const vk::DeviceInterface& vk, |
| vk::VkDevice device, |
| const deUint32 queueFamilyIndex, |
| const deUint32 queueIdx) |
| { |
| const vk::VkDeviceQueueInfo2 queueInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, // sType |
| DE_NULL, // pNext |
| vk::VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT, // flags |
| queueFamilyIndex, // queueFamilyIndex |
| queueIdx, // queueIndex |
| }; |
| |
| (void)queueInfo; |
| vk::VkQueue queue = |
| #ifndef NOT_PROTECTED |
| vk::getDeviceQueue2(vk, device, &queueInfo); |
| #else |
| vk::getDeviceQueue(vk, device, queueFamilyIndex, 0); |
| #endif |
| |
| if (queue == DE_NULL) |
| TCU_THROW(TestError, "Unable to get a protected queue"); |
| |
| return queue; |
| } |
| |
| de::MovePtr<vk::ImageWithMemory> createImage2D (ProtectedContext& context, |
| ProtectionMode protectionMode, |
| const deUint32 queueFamilyIdx, |
| deUint32 width, |
| deUint32 height, |
| vk::VkFormat format, |
| vk::VkImageUsageFlags usageFlags) |
| { |
| const vk::DeviceInterface& vk = context.getDeviceInterface(); |
| const vk::VkDevice& device = context.getDevice(); |
| vk::Allocator& allocator = context.getDefaultAllocator(); |
| |
| #ifndef NOT_PROTECTED |
| deUint32 flags = (protectionMode == PROTECTION_ENABLED) |
| ? vk::VK_IMAGE_CREATE_PROTECTED_BIT |
| : (vk::VkImageCreateFlagBits)0u; |
| #else |
| DE_UNREF(protectionMode); |
| deUint32 flags = 0u; |
| #endif |
| |
| const vk::VkImageCreateInfo params = |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType stype |
| DE_NULL, // const void* pNext |
| (vk::VkImageCreateFlags)flags, // VkImageCreateFlags flags |
| vk::VK_IMAGE_TYPE_2D, // VkImageType imageType |
| format, // VkFormat format |
| { width, height, 1 }, // VkExtent3D extent |
| 1u, // deUint32 mipLevels |
| 1u, // deUint32 arrayLayers |
| vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples |
| vk::VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling |
| usageFlags, // VkImageUsageFlags usage |
| vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode |
| 1u, // deUint32 queueFamilyIndexCount |
| &queueFamilyIdx, // const deUint32* pQueueFamilyIndices |
| vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout |
| }; |
| |
| #ifndef NOT_PROTECTED |
| vk::MemoryRequirement memReq = (protectionMode == PROTECTION_ENABLED) |
| ? vk::MemoryRequirement::Protected |
| : vk::MemoryRequirement::Any; |
| #else |
| vk::MemoryRequirement memReq = vk::MemoryRequirement::Any; |
| #endif |
| |
| return de::MovePtr<vk::ImageWithMemory>(new vk::ImageWithMemory(vk, device, allocator, params, memReq)); |
| } |
| |
| de::MovePtr<vk::BufferWithMemory> makeBuffer (ProtectedContext& context, |
| ProtectionMode protectionMode, |
| const deUint32 queueFamilyIdx, |
| deUint32 size, |
| vk::VkBufferUsageFlags usageFlags, |
| vk::MemoryRequirement memReq) |
| { |
| const vk::DeviceInterface& vk = context.getDeviceInterface(); |
| const vk::VkDevice& device = context.getDevice(); |
| vk::Allocator& allocator = context.getDefaultAllocator(); |
| |
| #ifndef NOT_PROTECTED |
| deUint32 flags = (protectionMode == PROTECTION_ENABLED) |
| ? vk::VK_BUFFER_CREATE_PROTECTED_BIT |
| : (vk::VkBufferCreateFlagBits)0u; |
| vk::MemoryRequirement requirement = memReq; |
| #else |
| DE_UNREF(protectionMode); |
| deUint32 flags = 0u; |
| vk::MemoryRequirement requirement = memReq & ( |
| vk::MemoryRequirement::HostVisible |
| | vk::MemoryRequirement::Coherent |
| | vk::MemoryRequirement::LazilyAllocated); |
| #endif |
| |
| const vk::VkBufferCreateInfo params = |
| { |
| vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| (vk::VkBufferCreateFlags)flags, // flags |
| (vk::VkDeviceSize)size, // size |
| usageFlags, // usage |
| vk::VK_SHARING_MODE_EXCLUSIVE, // sharingMode |
| 1u, // queueFamilyCount |
| &queueFamilyIdx, // pQueueFamilyIndices |
| }; |
| |
| return de::MovePtr<vk::BufferWithMemory>(new vk::BufferWithMemory(vk, device, allocator, params, requirement)); |
| } |
| |
| vk::Move<vk::VkImageView> createImageView (ProtectedContext& context, vk::VkImage image, vk::VkFormat format) |
| { |
| const vk::VkImageViewCreateInfo params = |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| 0u, // flags |
| image, // image |
| vk::VK_IMAGE_VIEW_TYPE_2D, // viewType |
| format, // format |
| vk::makeComponentMappingRGBA(), // components |
| { vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u,1u }, // subresourceRange |
| }; |
| |
| return vk::createImageView(context.getDeviceInterface(), context.getDevice(), ¶ms); |
| } |
| |
| vk::Move<vk::VkRenderPass> createRenderPass (ProtectedContext& context, vk::VkFormat format) |
| { |
| const vk::VkDevice vkDevice = context.getDevice(); |
| const vk::DeviceInterface& vk = context.getDeviceInterface(); |
| |
| return vk::makeRenderPass(vk, vkDevice, format); |
| } |
| |
| vk::Move<vk::VkFramebuffer> createFramebuffer (ProtectedContext& context, deUint32 width, deUint32 height, |
| vk::VkRenderPass renderPass, vk::VkImageView colorImageView) |
| { |
| const vk::VkDevice vkDevice = context.getDevice(); |
| const vk::DeviceInterface& vk = context.getDeviceInterface(); |
| |
| const vk::VkFramebufferCreateInfo framebufferParams = |
| { |
| vk::VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkFramebufferCreateFlags flags; |
| renderPass, // VkRenderPass renderPass; |
| 1u, // deUint32 attachmentCount; |
| &colorImageView, // const VkImageView* pAttachments; |
| width, // deUint32 width; |
| height, // deUint32 height; |
| 1u // deUint32 layers; |
| }; |
| |
| return vk::createFramebuffer(vk, vkDevice, &framebufferParams); |
| } |
| |
| |
| vk::Move<vk::VkPipelineLayout> createPipelineLayout (ProtectedContext& context, deUint32 layoutCount, vk::VkDescriptorSetLayout* setLayouts) |
| { |
| const vk::VkPipelineLayoutCreateInfo params = |
| { |
| vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType |
| DE_NULL, // pNext |
| 0u, // flags |
| layoutCount, // setLayoutCount |
| setLayouts, // pSetLayouts |
| 0u, // pushConstantRangeCount |
| DE_NULL, // pPushContantRanges |
| }; |
| |
| return vk::createPipelineLayout(context.getDeviceInterface(), context.getDevice(), ¶ms); |
| } |
| |
| void beginSecondaryCommandBuffer (const vk::DeviceInterface& vk, |
| const vk::VkCommandBuffer secondaryCmdBuffer, |
| const vk::VkCommandBufferInheritanceInfo bufferInheritanceInfo) |
| { |
| const vk::VkCommandBufferUsageFlags flags = bufferInheritanceInfo.renderPass != DE_NULL |
| ? (vk::VkCommandBufferUsageFlags)vk::VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT |
| : (vk::VkCommandBufferUsageFlags)0u; |
| const vk::VkCommandBufferBeginInfo beginInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // sType |
| DE_NULL, // pNext |
| flags, // flags |
| &bufferInheritanceInfo, // pInheritanceInfo |
| }; |
| VK_CHECK(vk.beginCommandBuffer(secondaryCmdBuffer, &beginInfo)); |
| } |
| |
| vk::VkResult queueSubmit (ProtectedContext& context, |
| ProtectionMode protectionMode, |
| vk::VkQueue queue, |
| vk::VkCommandBuffer cmdBuffer, |
| vk::VkFence fence, |
| deUint64 timeout) |
| { |
| const vk::DeviceInterface& vk = context.getDeviceInterface(); |
| const vk::VkDevice& device = context.getDevice(); |
| |
| // Basic submit info |
| vk::VkSubmitInfo submitInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_SUBMIT_INFO, // sType |
| DE_NULL, // pNext |
| 0u, // waitSemaphoreCount |
| DE_NULL, // pWaitSempahores |
| (const vk::VkPipelineStageFlags*)DE_NULL, // stageFlags |
| 1u, // commandBufferCount |
| &cmdBuffer, // pCommandBuffers |
| 0u, // signalSemaphoreCount |
| DE_NULL, // pSignalSemaphores |
| }; |
| |
| #ifndef NOT_PROTECTED |
| // Protected extension submit info |
| const vk::VkProtectedSubmitInfo protectedInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO, // sType |
| DE_NULL, // pNext |
| VK_TRUE, // protectedSubmit |
| }; |
| if (protectionMode == PROTECTION_ENABLED) { |
| submitInfo.pNext = &protectedInfo; |
| } |
| #else |
| DE_UNREF(protectionMode); |
| #endif |
| |
| VK_CHECK(vk.queueSubmit(queue, 1u, &submitInfo, fence)); |
| return vk.waitForFences(device, 1u, &fence, DE_TRUE, timeout); |
| } |
| |
| vk::Move<vk::VkDescriptorSet> makeDescriptorSet (const vk::DeviceInterface& vk, |
| const vk::VkDevice device, |
| const vk::VkDescriptorPool descriptorPool, |
| const vk::VkDescriptorSetLayout setLayout) |
| { |
| const vk::VkDescriptorSetAllocateInfo allocateParams = |
| { |
| vk::VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| descriptorPool, // VkDescriptorPool descriptorPool; |
| 1u, // deUint32 setLayoutCount; |
| &setLayout, // const VkDescriptorSetLayout* pSetLayouts; |
| }; |
| return vk::allocateDescriptorSet(vk, device, &allocateParams); |
| } |
| |
| vk::Move<vk::VkPipelineLayout> makePipelineLayout (const vk::DeviceInterface& vk, |
| const vk::VkDevice device, |
| const vk::VkDescriptorSetLayout descriptorSetLayout) |
| { |
| const vk::VkPipelineLayoutCreateInfo info = |
| { |
| vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (vk::VkPipelineLayoutCreateFlags)0, // VkPipelineLayoutCreateFlags flags; |
| 1u, // deUint32 setLayoutCount; |
| &descriptorSetLayout, // const VkDescriptorSetLayout* pSetLayouts; |
| 0u, // deUint32 pushConstantRangeCount; |
| DE_NULL, // const VkPushConstantRange* pPushConstantRanges; |
| }; |
| return vk::createPipelineLayout(vk, device, &info); |
| } |
| |
| vk::Move<vk::VkPipeline> makeComputePipeline (const vk::DeviceInterface& vk, |
| const vk::VkDevice device, |
| const vk::VkPipelineLayout pipelineLayout, |
| const vk::VkShaderModule shaderModule, |
| const vk::VkSpecializationInfo* specInfo) |
| { |
| const vk::VkPipelineShaderStageCreateInfo shaderStageInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (vk::VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags; |
| vk::VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage; |
| shaderModule, // VkShaderModule module; |
| "main", // const char* pName; |
| specInfo, // const VkSpecializationInfo* pSpecializationInfo; |
| }; |
| const vk::VkComputePipelineCreateInfo pipelineInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (vk::VkPipelineCreateFlags)0, // VkPipelineCreateFlags flags; |
| shaderStageInfo, // VkPipelineShaderStageCreateInfo stage; |
| pipelineLayout, // VkPipelineLayout layout; |
| DE_NULL, // VkPipeline basePipelineHandle; |
| 0, // deInt32 basePipelineIndex; |
| }; |
| return vk::createComputePipeline(vk, device, DE_NULL , &pipelineInfo); |
| } |
| |
| vk::Move<vk::VkSampler> makeSampler (const vk::DeviceInterface& vk, const vk::VkDevice& device) |
| { |
| const vk::VkSamplerCreateInfo createInfo = |
| { |
| vk::VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, |
| DE_NULL, |
| 0u, |
| |
| vk::VK_FILTER_NEAREST, |
| vk::VK_FILTER_NEAREST, |
| |
| vk::VK_SAMPLER_MIPMAP_MODE_LINEAR, |
| vk::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, |
| vk::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, |
| vk::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, |
| 0.0f, |
| VK_FALSE, |
| 1.0f, |
| VK_FALSE, |
| vk::VK_COMPARE_OP_ALWAYS, |
| 0.0f, |
| 0.0f, |
| vk::VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, |
| VK_FALSE |
| }; |
| |
| return vk::createSampler(vk, device, &createInfo); |
| } |
| |
| vk::Move<vk::VkCommandPool> makeCommandPool (const vk::DeviceInterface& vk, |
| const vk::VkDevice& device, |
| ProtectionMode protectionMode, |
| const deUint32 queueFamilyIdx) |
| { |
| const deUint32 poolFlags = vk::VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT |
| #ifndef NOT_PROTECTED |
| | ((protectionMode == PROTECTION_ENABLED) ? vk::VK_COMMAND_POOL_CREATE_PROTECTED_BIT : 0x0) |
| #endif |
| ; |
| #ifdef NOT_PROTECTED |
| DE_UNREF(protectionMode); |
| #endif |
| |
| return vk::createCommandPool(vk, device, poolFlags, queueFamilyIdx); |
| } |
| |
| vk::Move<vk::VkPipeline> makeGraphicsPipeline (const vk::DeviceInterface& vk, |
| const vk::VkDevice device, |
| const vk::VkPipelineLayout pipelineLayout, |
| const vk::VkRenderPass renderPass, |
| const vk::VkShaderModule vertexShaderModule, |
| const vk::VkShaderModule fragmentShaderModule, |
| const VertexBindings& vertexBindings, |
| const VertexAttribs& vertexAttribs, |
| const tcu::UVec2& renderSize, |
| const vk::VkPrimitiveTopology topology) |
| { |
| const std::vector<VkViewport> viewports (1, makeViewport(renderSize)); |
| const std::vector<VkRect2D> scissors (1, makeRect2D(renderSize)); |
| |
| const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (VkPipelineVertexInputStateCreateFlags)0, // VkPipelineVertexInputStateCreateFlags flags; |
| (deUint32)vertexBindings.size(), // deUint32 vertexBindingDescriptionCount; |
| vertexBindings.data(), // const VkVertexInputBindingDescription* pVertexBindingDescriptions; |
| (deUint32)vertexAttribs.size(), // deUint32 vertexAttributeDescriptionCount; |
| vertexAttribs.data() // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions; |
| }; |
| |
| return vk::makeGraphicsPipeline(vk, // const DeviceInterface& vk |
| device, // const VkDevice device |
| pipelineLayout, // const VkPipelineLayout pipelineLayout |
| vertexShaderModule, // const VkShaderModule vertexShaderModule |
| DE_NULL, // const VkShaderModule tessellationControlModule |
| DE_NULL, // const VkShaderModule tessellationEvalModule |
| DE_NULL, // const VkShaderModule geometryShaderModule |
| fragmentShaderModule, // const VkShaderModule fragmentShaderModule |
| renderPass, // const VkRenderPass renderPass |
| viewports, // const std::vector<VkViewport>& viewports |
| scissors, // const std::vector<VkRect2D>& scissors |
| topology, // const VkPrimitiveTopology topology |
| 0u, // const deUint32 subpass |
| 0u, // const deUint32 patchControlPoints |
| &vertexInputStateCreateInfo); // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo |
| } |
| |
| const char* getCmdBufferTypeStr (const CmdBufferType cmdBufferType) |
| { |
| switch (cmdBufferType) |
| { |
| case CMD_BUFFER_PRIMARY: return "primary"; |
| case CMD_BUFFER_SECONDARY: return "secondary"; |
| |
| default: DE_FATAL("Invalid command buffer type"); return ""; |
| } |
| } |
| |
| void clearImage (ProtectedContext& ctx, vk::VkImage image) |
| { |
| const vk::DeviceInterface& vk = ctx.getDeviceInterface(); |
| const vk::VkDevice device = ctx.getDevice(); |
| const vk::VkQueue queue = ctx.getQueue(); |
| const deUint32 queueFamilyIndex = ctx.getQueueFamilyIndex(); |
| |
| vk::Unique<vk::VkCommandPool> cmdPool (makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex)); |
| vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY)); |
| |
| const vk::VkClearColorValue clearColor = { { 0.0f, 0.0f, 0.0f, 0.0f } }; |
| |
| const vk::VkImageSubresourceRange subresourceRange = |
| { |
| vk::VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask |
| 0u, // uint32_t baseMipLevel |
| 1u, // uint32_t levelCount |
| 0u, // uint32_t baseArrayLayer |
| 1u, // uint32_t layerCount |
| }; |
| |
| const vk::VkImageMemoryBarrier preImageBarrier = |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkAccessFlags srcAccessMask; |
| vk::VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask; |
| vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout; |
| vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout; |
| queueFamilyIndex, // deUint32 srcQueueFamilyIndex; |
| queueFamilyIndex, // deUint32 dstQueueFamilyIndex; |
| image, // VkImage image; |
| subresourceRange // VkImageSubresourceRange subresourceRange; |
| }; |
| |
| const vk::VkImageMemoryBarrier postImageBarrier = |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| vk::VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| vk::VK_ACCESS_SHADER_WRITE_BIT, // VkAccessFlags dstAccessMask; |
| vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout; |
| vk::VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout newLayout; |
| queueFamilyIndex, // deUint32 srcQueueFamilyIndex; |
| queueFamilyIndex, // deUint32 dstQueueFamilyIndex; |
| image, // VkImage image; |
| subresourceRange // VkImageSubresourceRange subresourceRange; |
| }; |
| |
| beginCommandBuffer(vk, *cmdBuffer); |
| vk.cmdPipelineBarrier(*cmdBuffer, |
| vk::VK_PIPELINE_STAGE_HOST_BIT, |
| vk::VK_PIPELINE_STAGE_TRANSFER_BIT, |
| (vk::VkDependencyFlags)0, |
| 0, (const vk::VkMemoryBarrier*)DE_NULL, |
| 0, (const vk::VkBufferMemoryBarrier*)DE_NULL, |
| 1, &preImageBarrier); |
| vk.cmdClearColorImage(*cmdBuffer, image, vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColor, 1, &subresourceRange); |
| vk.cmdPipelineBarrier(*cmdBuffer, |
| vk::VK_PIPELINE_STAGE_TRANSFER_BIT, |
| vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, |
| (vk::VkDependencyFlags)0, |
| 0, (const vk::VkMemoryBarrier*)DE_NULL, |
| 0, (const vk::VkBufferMemoryBarrier*)DE_NULL, |
| 1, &postImageBarrier); |
| endCommandBuffer(vk, *cmdBuffer); |
| |
| { |
| const vk::Unique<vk::VkFence> fence (createFence(vk, device)); |
| VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull)); |
| } |
| } |
| |
| void uploadImage (ProtectedContext& ctx, vk::VkImage image, const tcu::Texture2D& texture2D) |
| { |
| const vk::DeviceInterface& vk = ctx.getDeviceInterface(); |
| const vk::VkDevice device = ctx.getDevice(); |
| const vk::VkQueue queue = ctx.getQueue(); |
| const deUint32 queueFamilyIndex = ctx.getQueueFamilyIndex(); |
| |
| vk::Unique<vk::VkCommandPool> cmdPool (makeCommandPool(vk, device, PROTECTION_DISABLED, queueFamilyIndex)); |
| vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY)); |
| |
| const deUint32 width = (deUint32)texture2D.getWidth(); |
| const deUint32 height = (deUint32)texture2D.getHeight(); |
| const deUint32 stagingBufferSize = width * height * tcu::getPixelSize(texture2D.getFormat()); |
| |
| de::UniquePtr<vk::BufferWithMemory> stagingBuffer (makeBuffer(ctx, |
| PROTECTION_DISABLED, |
| queueFamilyIndex, |
| stagingBufferSize, |
| vk::VK_BUFFER_USAGE_TRANSFER_SRC_BIT, |
| vk::MemoryRequirement::HostVisible)); |
| |
| { |
| const tcu::ConstPixelBufferAccess& access = texture2D.getLevel(0); |
| const tcu::PixelBufferAccess destAccess (access.getFormat(), access.getSize(), stagingBuffer->getAllocation().getHostPtr()); |
| |
| tcu::copy(destAccess, access); |
| |
| vk::flushMappedMemoryRange(vk, device, stagingBuffer->getAllocation().getMemory(), stagingBuffer->getAllocation().getOffset(), stagingBufferSize); |
| } |
| |
| const vk::VkImageSubresourceRange subresourceRange = |
| { |
| vk::VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask |
| 0u, // uint32_t baseMipLevel |
| 1u, // uint32_t levelCount |
| 0u, // uint32_t baseArrayLayer |
| 1u, // uint32_t layerCount |
| }; |
| |
| const vk::VkImageMemoryBarrier preCopyBarrier = |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkAccessFlags srcAccessMask; |
| vk::VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask; |
| vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout; |
| vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout; |
| queueFamilyIndex, // deUint32 srcQueueFamilyIndex; |
| queueFamilyIndex, // deUint32 dstQueueFamilyIndex; |
| image, // VkImage image; |
| subresourceRange // VkImageSubresourceRange subresourceRange; |
| }; |
| |
| const vk::VkImageMemoryBarrier postCopyBarrier = |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| vk::VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| vk::VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask; |
| vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout; |
| vk::VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout newLayout; |
| queueFamilyIndex, // deUint32 srcQueueFamilyIndex; |
| queueFamilyIndex, // deUint32 dstQueueFamilyIndex; |
| image, // VkImage image; |
| subresourceRange // VkImageSubresourceRange subresourceRange; |
| }; |
| |
| const vk::VkImageSubresourceLayers subresourceLayers = |
| { |
| vk::VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask; |
| 0u, // deUint32 mipLevel; |
| 0u, // deUint32 baseArrayLayer; |
| 1u // deUint32 layerCount; |
| }; |
| |
| const vk::VkBufferImageCopy copyRegion = |
| { |
| 0u, // VkDeviceSize bufferOffset; |
| width, // deUint32 bufferRowLength; |
| height, // deUint32 bufferImageHeight; |
| subresourceLayers, // VkImageSubresourceLayers imageSubresource; |
| { 0u, 0u, 0u }, // VkOffset3D imageOffset; |
| { width, height, 1u } // VkExtent3D imageExtent; |
| }; |
| |
| beginCommandBuffer(vk, *cmdBuffer); |
| vk.cmdPipelineBarrier(*cmdBuffer, |
| (vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_HOST_BIT, |
| (vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_TRANSFER_BIT, |
| (vk::VkDependencyFlags)0u, |
| 0u, (const vk::VkMemoryBarrier*)DE_NULL, |
| 0u, (const vk::VkBufferMemoryBarrier*)DE_NULL, |
| 1u, &preCopyBarrier); |
| vk.cmdCopyBufferToImage(*cmdBuffer, **stagingBuffer, image, vk::VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1u, ©Region); |
| vk.cmdPipelineBarrier(*cmdBuffer, |
| (vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_TRANSFER_BIT, |
| (vk::VkPipelineStageFlags)vk::VK_PIPELINE_STAGE_TRANSFER_BIT, |
| (vk::VkDependencyFlags)0u, |
| 0u, (const vk::VkMemoryBarrier*)DE_NULL, |
| 0u, (const vk::VkBufferMemoryBarrier*)DE_NULL, |
| 1u, &postCopyBarrier); |
| endCommandBuffer(vk, *cmdBuffer); |
| |
| { |
| const vk::Unique<vk::VkFence> fence (createFence(vk, device)); |
| VK_CHECK(queueSubmit(ctx, PROTECTION_DISABLED, queue, *cmdBuffer, *fence, ~0ull)); |
| } |
| } |
| |
| void copyToProtectedImage (ProtectedContext& ctx, vk::VkImage srcImage, vk::VkImage dstImage, vk::VkImageLayout dstImageLayout, deUint32 width, deUint32 height) |
| { |
| const vk::DeviceInterface& vk = ctx.getDeviceInterface(); |
| const vk::VkDevice device = ctx.getDevice(); |
| const vk::VkQueue queue = ctx.getQueue(); |
| const deUint32 queueFamilyIndex = ctx.getQueueFamilyIndex(); |
| |
| vk::Unique<vk::VkCommandPool> cmdPool (makeCommandPool(vk, device, PROTECTION_ENABLED, queueFamilyIndex)); |
| vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY)); |
| |
| const vk::VkImageSubresourceRange subresourceRange = |
| { |
| vk::VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask |
| 0u, // uint32_t baseMipLevel |
| 1u, // uint32_t levelCount |
| 0u, // uint32_t baseArrayLayer |
| 1u, // uint32_t layerCount |
| }; |
| |
| const vk::VkImageMemoryBarrier preImageBarriers[] = |
| { |
| // source image |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| vk::VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| vk::VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask; |
| vk::VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout oldLayout; |
| vk::VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout newLayout; |
| queueFamilyIndex, // deUint32 srcQueueFamilyIndex; |
| queueFamilyIndex, // deUint32 dstQueueFamilyIndex; |
| srcImage, // VkImage image; |
| subresourceRange // VkImageSubresourceRange subresourceRange; |
| }, |
| // destination image |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0, // VkAccessFlags srcAccessMask; |
| vk::VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask; |
| vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout; |
| vk::VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout newLayout; |
| queueFamilyIndex, // deUint32 srcQueueFamilyIndex; |
| queueFamilyIndex, // deUint32 dstQueueFamilyIndex; |
| dstImage, // VkImage image; |
| subresourceRange // VkImageSubresourceRange subresourceRange; |
| } |
| }; |
| |
| const vk::VkImageMemoryBarrier postImgBarrier = |
| { |
| vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| vk::VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| vk::VK_ACCESS_SHADER_READ_BIT, // VkAccessFlags dstAccessMask; |
| vk::VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout oldLayout; |
| dstImageLayout, // VkImageLayout newLayout; |
| queueFamilyIndex, // deUint32 srcQueueFamilyIndex; |
| queueFamilyIndex, // deUint32 dstQueueFamilyIndex; |
| dstImage, // VkImage image; |
| subresourceRange // VkImageSubresourceRange subresourceRange; |
| }; |
| |
| const vk::VkImageSubresourceLayers subresourceLayers = |
| { |
| vk::VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask; |
| 0u, // deUint32 mipLevel; |
| 0u, // deUint32 baseArrayLayer; |
| 1u // deUint32 layerCount; |
| }; |
| |
| const vk::VkImageCopy copyImageRegion = |
| { |
| subresourceLayers, // VkImageSubresourceCopy srcSubresource; |
| { 0, 0, 0 }, // VkOffset3D srcOffset; |
| subresourceLayers, // VkImageSubresourceCopy destSubresource; |
| { 0, 0, 0 }, // VkOffset3D destOffset; |
| { width, height, 1u }, // VkExtent3D extent; |
| }; |
| |
| beginCommandBuffer(vk, *cmdBuffer); |
| vk.cmdPipelineBarrier(*cmdBuffer, |
| vk::VK_PIPELINE_STAGE_TRANSFER_BIT, |
| vk::VK_PIPELINE_STAGE_TRANSFER_BIT, |
| (vk::VkDependencyFlags)0, |
| 0, (const vk::VkMemoryBarrier*)DE_NULL, |
| 0, (const vk::VkBufferMemoryBarrier*)DE_NULL, |
| DE_LENGTH_OF_ARRAY(preImageBarriers), preImageBarriers); |
| vk.cmdCopyImage(*cmdBuffer, srcImage, vk::VK_IMAGE_LAYOUT_GENERAL, dstImage, vk::VK_IMAGE_LAYOUT_GENERAL, 1u, ©ImageRegion); |
| vk.cmdPipelineBarrier(*cmdBuffer, |
| vk::VK_PIPELINE_STAGE_TRANSFER_BIT, |
| vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, |
| (vk::VkDependencyFlags)0, |
| 0, (const vk::VkMemoryBarrier*)DE_NULL, |
| 0, (const vk::VkBufferMemoryBarrier*)DE_NULL, |
| 1, &postImgBarrier); |
| endCommandBuffer(vk, *cmdBuffer); |
| |
| { |
| const vk::Unique<vk::VkFence> fence (createFence(vk, device)); |
| VK_CHECK(queueSubmit(ctx, PROTECTION_ENABLED, queue, *cmdBuffer, *fence, ~0ull)); |
| } |
| } |
| |
| void fillWithRandomColorTiles (const tcu::PixelBufferAccess& dst, const tcu::Vec4& minVal, const tcu::Vec4& maxVal, deUint32 seed) |
| { |
| const int numCols = dst.getWidth() >= 7 ? 7 : dst.getWidth(); |
| const int numRows = dst.getHeight() >= 5 ? 5 : dst.getHeight(); |
| de::Random rnd (seed); |
| |
| for (int slice = 0; slice < dst.getDepth(); slice++) |
| for (int row = 0; row < numRows; row++) |
| for (int col = 0; col < numCols; col++) |
| { |
| const int yBegin = (row + 0)*dst.getHeight() / numRows; |
| const int yEnd = (row + 1)*dst.getHeight() / numRows; |
| const int xBegin = (col + 0)*dst.getWidth() / numCols; |
| const int xEnd = (col + 1)*dst.getWidth() / numCols; |
| tcu::Vec4 color; |
| for (int i = 0; i < 4; i++) |
| color[i] = rnd.getFloat(minVal[i], maxVal[i]); |
| tcu::clear(tcu::getSubregion(dst, xBegin, yBegin, slice, xEnd - xBegin, yEnd - yBegin, 1), color); |
| } |
| } |
| |
| } // ProtectedMem |
| } // vkt |