| /*------------------------------------------------------------------------ |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2015-2016 The Khronos Group Inc. |
| * Copyright (c) 2015-2016 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 Vulkan Copies And Blitting Tests |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vktApiCopiesAndBlittingTests.hpp" |
| |
| #include "deStringUtil.hpp" |
| #include "deUniquePtr.hpp" |
| |
| #include "tcuImageCompare.hpp" |
| #include "tcuTexture.hpp" |
| #include "tcuTextureUtil.hpp" |
| #include "tcuVectorType.hpp" |
| #include "tcuVectorUtil.hpp" |
| #include "tcuTestLog.hpp" |
| #include "tcuTexLookupVerifier.hpp" |
| |
| #include "vkImageUtil.hpp" |
| #include "vkMemUtil.hpp" |
| #include "vkPrograms.hpp" |
| #include "vkQueryUtil.hpp" |
| #include "vkRefUtil.hpp" |
| #include "vktTestCase.hpp" |
| #include "vktTestCaseUtil.hpp" |
| #include "vktTestGroupUtil.hpp" |
| #include "vkTypeUtil.hpp" |
| #include "vkCmdUtil.hpp" |
| #include "vkObjUtil.hpp" |
| |
| #include <set> |
| |
| namespace vkt |
| { |
| |
| namespace api |
| { |
| |
| namespace |
| { |
| enum MirrorMode |
| { |
| MIRROR_MODE_NONE = 0, |
| MIRROR_MODE_X = (1<<0), |
| MIRROR_MODE_Y = (1<<1), |
| MIRROR_MODE_XY = MIRROR_MODE_X | MIRROR_MODE_Y, |
| |
| MIRROR_MODE_LAST |
| }; |
| |
| enum AllocationKind |
| { |
| ALLOCATION_KIND_SUBALLOCATED, |
| ALLOCATION_KIND_DEDICATED, |
| }; |
| |
| template <typename Type> |
| class BinaryCompare |
| { |
| public: |
| bool operator() (const Type& a, const Type& b) const |
| { |
| return deMemCmp(&a, &b, sizeof(Type)) < 0; |
| } |
| }; |
| |
| typedef std::set<vk::VkFormat, BinaryCompare<vk::VkFormat> > FormatSet; |
| |
| FormatSet dedicatedAllocationImageToImageFormatsToTestSet; |
| FormatSet dedicatedAllocationBlittingFormatsToTestSet; |
| |
| using namespace vk; |
| |
| VkImageAspectFlags getAspectFlags (tcu::TextureFormat format) |
| { |
| VkImageAspectFlags aspectFlag = 0; |
| aspectFlag |= (tcu::hasDepthComponent(format.order)? VK_IMAGE_ASPECT_DEPTH_BIT : 0); |
| aspectFlag |= (tcu::hasStencilComponent(format.order)? VK_IMAGE_ASPECT_STENCIL_BIT : 0); |
| |
| if (!aspectFlag) |
| aspectFlag = VK_IMAGE_ASPECT_COLOR_BIT; |
| |
| return aspectFlag; |
| } |
| |
| VkImageAspectFlags getAspectFlags (VkFormat format) |
| { |
| if (isCompressedFormat(format)) |
| return VK_IMAGE_ASPECT_COLOR_BIT; |
| else |
| return getAspectFlags(mapVkFormat(format)); |
| } |
| |
| tcu::TextureFormat getSizeCompatibleTcuTextureFormat (VkFormat format) |
| { |
| if (isCompressedFormat(format)) |
| return (getBlockSizeInBytes(format) == 8) ? mapVkFormat(VK_FORMAT_R16G16B16A16_UINT) : mapVkFormat(VK_FORMAT_R32G32B32A32_UINT); |
| else |
| return mapVkFormat(format); |
| } |
| |
| // This is effectively same as vk::isFloatFormat(mapTextureFormat(format)) |
| // except that it supports some formats that are not mappable to VkFormat. |
| // When we are checking combined depth and stencil formats, each aspect is |
| // checked separately, and in some cases we construct PBA with a format that |
| // is not mappable to VkFormat. |
| bool isFloatFormat (tcu::TextureFormat format) |
| { |
| return tcu::getTextureChannelClass(format.type) == tcu::TEXTURECHANNELCLASS_FLOATING_POINT; |
| } |
| |
| union CopyRegion |
| { |
| VkBufferCopy bufferCopy; |
| VkImageCopy imageCopy; |
| VkBufferImageCopy bufferImageCopy; |
| VkImageBlit imageBlit; |
| VkImageResolve imageResolve; |
| }; |
| |
| struct ImageParms |
| { |
| VkImageType imageType; |
| VkFormat format; |
| VkExtent3D extent; |
| VkImageTiling tiling; |
| VkImageLayout operationLayout; |
| }; |
| |
| struct TestParams |
| { |
| union Data |
| { |
| struct Buffer |
| { |
| VkDeviceSize size; |
| } buffer; |
| |
| ImageParms image; |
| } src, dst; |
| |
| std::vector<CopyRegion> regions; |
| |
| union |
| { |
| VkFilter filter; |
| VkSampleCountFlagBits samples; |
| }; |
| |
| AllocationKind allocationKind; |
| deUint32 mipLevels; |
| deBool singleCommand; |
| deUint32 barrierCount; |
| deBool separateDepthStencilLayouts; |
| |
| TestParams (void) |
| { |
| mipLevels = 1u; |
| singleCommand = DE_TRUE; |
| barrierCount = 1u; |
| separateDepthStencilLayouts = DE_FALSE; |
| } |
| }; |
| |
| de::MovePtr<Allocation> allocateBuffer (const InstanceInterface& vki, |
| const DeviceInterface& vkd, |
| const VkPhysicalDevice& physDevice, |
| const VkDevice device, |
| const VkBuffer& buffer, |
| const MemoryRequirement requirement, |
| Allocator& allocator, |
| AllocationKind allocationKind) |
| { |
| switch (allocationKind) |
| { |
| case ALLOCATION_KIND_SUBALLOCATED: |
| { |
| const VkMemoryRequirements memoryRequirements = getBufferMemoryRequirements(vkd, device, buffer); |
| |
| return allocator.allocate(memoryRequirements, requirement); |
| } |
| |
| case ALLOCATION_KIND_DEDICATED: |
| { |
| return allocateDedicated(vki, vkd, physDevice, device, buffer, requirement); |
| } |
| |
| default: |
| { |
| TCU_THROW(InternalError, "Invalid allocation kind"); |
| } |
| } |
| } |
| |
| de::MovePtr<Allocation> allocateImage (const InstanceInterface& vki, |
| const DeviceInterface& vkd, |
| const VkPhysicalDevice& physDevice, |
| const VkDevice device, |
| const VkImage& image, |
| const MemoryRequirement requirement, |
| Allocator& allocator, |
| AllocationKind allocationKind) |
| { |
| switch (allocationKind) |
| { |
| case ALLOCATION_KIND_SUBALLOCATED: |
| { |
| const VkMemoryRequirements memoryRequirements = getImageMemoryRequirements(vkd, device, image); |
| |
| return allocator.allocate(memoryRequirements, requirement); |
| } |
| |
| case ALLOCATION_KIND_DEDICATED: |
| { |
| return allocateDedicated(vki, vkd, physDevice, device, image, requirement); |
| } |
| |
| default: |
| { |
| TCU_THROW(InternalError, "Invalid allocation kind"); |
| } |
| } |
| } |
| |
| |
| inline deUint32 getArraySize(const ImageParms& parms) |
| { |
| return (parms.imageType != VK_IMAGE_TYPE_3D) ? parms.extent.depth : 1u; |
| } |
| |
| inline VkImageCreateFlags getCreateFlags(const ImageParms& parms) |
| { |
| return parms.imageType == VK_IMAGE_TYPE_2D && parms.extent.depth % 6 == 0 ? |
| VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0; |
| } |
| |
| inline VkExtent3D getExtent3D(const ImageParms& parms, deUint32 mipLevel = 0u) |
| { |
| const bool isCompressed = isCompressedFormat(parms.format); |
| const deUint32 blockWidth = (isCompressed) ? getBlockWidth(parms.format) : 1u; |
| const deUint32 blockHeight = (isCompressed) ? getBlockHeight(parms.format) : 1u; |
| |
| if (isCompressed && mipLevel != 0u) |
| DE_FATAL("Not implemented"); |
| |
| const VkExtent3D extent = |
| { |
| (parms.extent.width >> mipLevel) * blockWidth, |
| (parms.imageType != VK_IMAGE_TYPE_1D) ? ((parms.extent.height >> mipLevel) * blockHeight) : 1u, |
| (parms.imageType == VK_IMAGE_TYPE_3D) ? parms.extent.depth : 1u, |
| }; |
| return extent; |
| } |
| |
| const tcu::TextureFormat mapCombinedToDepthTransferFormat (const tcu::TextureFormat& combinedFormat) |
| { |
| tcu::TextureFormat format; |
| switch (combinedFormat.type) |
| { |
| case tcu::TextureFormat::UNORM_INT16: |
| case tcu::TextureFormat::UNSIGNED_INT_16_8_8: |
| format = tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT16); |
| break; |
| case tcu::TextureFormat::UNSIGNED_INT_24_8_REV: |
| format = tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNSIGNED_INT_24_8_REV); |
| break; |
| case tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV: |
| case tcu::TextureFormat::FLOAT: |
| format = tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT); |
| break; |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| return format; |
| } |
| |
| class CopiesAndBlittingTestInstance : public vkt::TestInstance |
| { |
| public: |
| CopiesAndBlittingTestInstance (Context& context, |
| TestParams testParams); |
| virtual tcu::TestStatus iterate (void) = 0; |
| |
| enum FillMode |
| { |
| FILL_MODE_GRADIENT = 0, |
| FILL_MODE_WHITE, |
| FILL_MODE_RED, |
| FILL_MODE_MULTISAMPLE, |
| |
| FILL_MODE_LAST |
| }; |
| |
| protected: |
| const TestParams m_params; |
| |
| Move<VkCommandPool> m_cmdPool; |
| Move<VkCommandBuffer> m_cmdBuffer; |
| Move<VkFence> m_fence; |
| de::MovePtr<tcu::TextureLevel> m_sourceTextureLevel; |
| de::MovePtr<tcu::TextureLevel> m_destinationTextureLevel; |
| de::MovePtr<tcu::TextureLevel> m_expectedTextureLevel[16]; |
| |
| VkCommandBufferBeginInfo m_cmdBufferBeginInfo; |
| |
| void generateBuffer (tcu::PixelBufferAccess buffer, int width, int height, int depth = 1, FillMode = FILL_MODE_GRADIENT); |
| virtual void generateExpectedResult (void); |
| void uploadBuffer (tcu::ConstPixelBufferAccess bufferAccess, const Allocation& bufferAlloc); |
| void uploadImage (const tcu::ConstPixelBufferAccess& src, VkImage dst, const ImageParms& parms, const deUint32 mipLevels = 1u); |
| virtual tcu::TestStatus checkTestResult (tcu::ConstPixelBufferAccess result); |
| virtual void copyRegionToTextureLevel (tcu::ConstPixelBufferAccess src, tcu::PixelBufferAccess dst, CopyRegion region, deUint32 mipLevel = 0u) = 0; |
| deUint32 calculateSize (tcu::ConstPixelBufferAccess src) const |
| { |
| return src.getWidth() * src.getHeight() * src.getDepth() * tcu::getPixelSize(src.getFormat()); |
| } |
| |
| de::MovePtr<tcu::TextureLevel> readImage (vk::VkImage image, |
| const ImageParms& imageParms, |
| const deUint32 mipLevel = 0u); |
| |
| private: |
| void uploadImageAspect (const tcu::ConstPixelBufferAccess& src, |
| const VkImage& dst, |
| const ImageParms& parms, |
| const deUint32 mipLevels = 1u); |
| void readImageAspect (vk::VkImage src, |
| const tcu::PixelBufferAccess& dst, |
| const ImageParms& parms, |
| const deUint32 mipLevel = 0u); |
| }; |
| |
| CopiesAndBlittingTestInstance::CopiesAndBlittingTestInstance (Context& context, TestParams testParams) |
| : vkt::TestInstance (context) |
| , m_params (testParams) |
| { |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const VkDevice vkDevice = context.getDevice(); |
| const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); |
| |
| // Create command pool |
| m_cmdPool = createCommandPool(vk, vkDevice, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex); |
| |
| // Create command buffer |
| m_cmdBuffer = allocateCommandBuffer(vk, vkDevice, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY); |
| |
| // Create fence |
| m_fence = createFence(vk, vkDevice); |
| } |
| |
| void CopiesAndBlittingTestInstance::generateBuffer (tcu::PixelBufferAccess buffer, int width, int height, int depth, FillMode mode) |
| { |
| const tcu::TextureChannelClass channelClass = tcu::getTextureChannelClass(buffer.getFormat().type); |
| tcu::Vec4 maxValue (1.0f); |
| |
| if (buffer.getFormat().order == tcu::TextureFormat::S) |
| { |
| // Stencil-only is stored in the first component. Stencil is always 8 bits. |
| maxValue.x() = 1 << 8; |
| } |
| else if (buffer.getFormat().order == tcu::TextureFormat::DS) |
| { |
| // In a combined format, fillWithComponentGradients expects stencil in the fourth component. |
| maxValue.w() = 1 << 8; |
| } |
| else if (channelClass == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER || channelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER) |
| { |
| // The tcu::Vectors we use as pixels are 32-bit, so clamp to that. |
| const tcu::IVec4 bits = tcu::min(tcu::getTextureFormatBitDepth(buffer.getFormat()), tcu::IVec4(32)); |
| const int signBit = (channelClass == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER ? 1 : 0); |
| |
| for (int i = 0; i < 4; ++i) |
| { |
| if (bits[i] != 0) |
| maxValue[i] = static_cast<float>((deUint64(1) << (bits[i] - signBit)) - 1); |
| } |
| } |
| |
| if (mode == FILL_MODE_GRADIENT) |
| { |
| tcu::fillWithComponentGradients(buffer, tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), maxValue); |
| return; |
| } |
| |
| const tcu::Vec4 redColor (maxValue.x(), 0.0, 0.0, maxValue.w()); |
| const tcu::Vec4 greenColor (0.0, maxValue.y(), 0.0, maxValue.w()); |
| const tcu::Vec4 blueColor (0.0, 0.0, maxValue.z(), maxValue.w()); |
| const tcu::Vec4 whiteColor (maxValue.x(), maxValue.y(), maxValue.z(), maxValue.w()); |
| |
| for (int z = 0; z < depth; ++z) |
| for (int y = 0; y < height; ++y) |
| for (int x = 0; x < width; ++x) |
| { |
| switch (mode) |
| { |
| case FILL_MODE_WHITE: |
| if (tcu::isCombinedDepthStencilType(buffer.getFormat().type)) |
| { |
| buffer.setPixDepth(1.0f, x, y, z); |
| if (tcu::hasStencilComponent(buffer.getFormat().order)) |
| buffer.setPixStencil(255, x, y, z); |
| } |
| else |
| buffer.setPixel(whiteColor, x, y, z); |
| break; |
| |
| case FILL_MODE_RED: |
| if (tcu::isCombinedDepthStencilType(buffer.getFormat().type)) |
| { |
| buffer.setPixDepth(redColor[0], x, y, z); |
| if (tcu::hasStencilComponent(buffer.getFormat().order)) |
| buffer.setPixStencil((int)redColor[3], x, y, z); |
| } |
| else |
| buffer.setPixel(redColor, x, y, z); |
| break; |
| |
| case FILL_MODE_MULTISAMPLE: |
| { |
| float xScaled = static_cast<float>(x) / static_cast<float>(width); |
| float yScaled = static_cast<float>(y) / static_cast<float>(height); |
| buffer.setPixel((xScaled == yScaled) ? tcu::Vec4(0.0, 0.5, 0.5, 1.0) : ((xScaled > yScaled) ? greenColor : blueColor), x, y, z); |
| break; |
| } |
| |
| default: |
| break; |
| } |
| } |
| } |
| |
| void CopiesAndBlittingTestInstance::uploadBuffer (tcu::ConstPixelBufferAccess bufferAccess, const Allocation& bufferAlloc) |
| { |
| const DeviceInterface& vk = m_context.getDeviceInterface(); |
| const VkDevice vkDevice = m_context.getDevice(); |
| const deUint32 bufferSize = calculateSize(bufferAccess); |
| |
| // Write buffer data |
| deMemcpy(bufferAlloc.getHostPtr(), bufferAccess.getDataPtr(), bufferSize); |
| flushAlloc(vk, vkDevice, bufferAlloc); |
| } |
| |
| void CopiesAndBlittingTestInstance::uploadImageAspect (const tcu::ConstPixelBufferAccess& imageAccess, const VkImage& image, const ImageParms& parms, const deUint32 mipLevels) |
| { |
| const InstanceInterface& vki = m_context.getInstanceInterface(); |
| const DeviceInterface& vk = m_context.getDeviceInterface(); |
| const VkPhysicalDevice vkPhysDevice = m_context.getPhysicalDevice(); |
| const VkDevice vkDevice = m_context.getDevice(); |
| const VkQueue queue = m_context.getUniversalQueue(); |
| const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); |
| Allocator& memAlloc = m_context.getDefaultAllocator(); |
| Move<VkBuffer> buffer; |
| const deUint32 bufferSize = calculateSize(imageAccess); |
| de::MovePtr<Allocation> bufferAlloc; |
| const deUint32 arraySize = getArraySize(parms); |
| const VkExtent3D imageExtent = getExtent3D(parms); |
| std::vector <VkBufferImageCopy> copyRegions; |
| |
| // Create source buffer |
| { |
| const VkBufferCreateInfo bufferParams = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferCreateFlags flags; |
| bufferSize, // VkDeviceSize size; |
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT, // VkBufferUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyIndexCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| }; |
| |
| buffer = createBuffer(vk, vkDevice, &bufferParams); |
| bufferAlloc = allocateBuffer(vki, vk, vkPhysDevice, vkDevice, *buffer, MemoryRequirement::HostVisible, memAlloc, m_params.allocationKind); |
| VK_CHECK(vk.bindBufferMemory(vkDevice, *buffer, bufferAlloc->getMemory(), bufferAlloc->getOffset())); |
| } |
| |
| // Barriers for copying buffer to image |
| const VkBufferMemoryBarrier preBufferBarrier = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_HOST_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| *buffer, // VkBuffer buffer; |
| 0u, // VkDeviceSize offset; |
| bufferSize // VkDeviceSize size; |
| }; |
| |
| const VkImageAspectFlags formatAspect = (m_params.separateDepthStencilLayouts) ? getAspectFlags(imageAccess.getFormat()) : getAspectFlags(parms.format); |
| const bool skipPreImageBarrier = (m_params.separateDepthStencilLayouts) ? false : ((formatAspect == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) && |
| getAspectFlags(imageAccess.getFormat()) == VK_IMAGE_ASPECT_STENCIL_BIT)); |
| |
| const VkImageMemoryBarrier preImageBarrier = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout; |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| image, // VkImage image; |
| { // VkImageSubresourceRange subresourceRange; |
| formatAspect, // VkImageAspectFlags aspect; |
| 0u, // deUint32 baseMipLevel; |
| mipLevels, // deUint32 mipLevels; |
| 0u, // deUint32 baseArraySlice; |
| arraySize, // deUint32 arraySize; |
| } |
| }; |
| |
| const VkImageMemoryBarrier postImageBarrier = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask; |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout; |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| image, // VkImage image; |
| { // VkImageSubresourceRange subresourceRange; |
| formatAspect, // VkImageAspectFlags aspect; |
| 0u, // deUint32 baseMipLevel; |
| mipLevels, // deUint32 mipLevels; |
| 0u, // deUint32 baseArraySlice; |
| arraySize, // deUint32 arraySize; |
| } |
| }; |
| |
| for (deUint32 mipLevelNdx = 0; mipLevelNdx < mipLevels; mipLevelNdx++) |
| { |
| const VkExtent3D copyExtent = |
| { |
| imageExtent.width >> mipLevelNdx, |
| imageExtent.height >> mipLevelNdx, |
| imageExtent.depth |
| }; |
| |
| const VkBufferImageCopy copyRegion = |
| { |
| 0u, // VkDeviceSize bufferOffset; |
| copyExtent.width, // deUint32 bufferRowLength; |
| copyExtent.height, // deUint32 bufferImageHeight; |
| { |
| getAspectFlags(imageAccess.getFormat()), // VkImageAspectFlags aspect; |
| mipLevelNdx, // deUint32 mipLevel; |
| 0u, // deUint32 baseArrayLayer; |
| arraySize, // deUint32 layerCount; |
| }, // VkImageSubresourceLayers imageSubresource; |
| { 0, 0, 0 }, // VkOffset3D imageOffset; |
| copyExtent // VkExtent3D imageExtent; |
| }; |
| |
| copyRegions.push_back(copyRegion); |
| } |
| |
| // Write buffer data |
| deMemcpy(bufferAlloc->getHostPtr(), imageAccess.getDataPtr(), bufferSize); |
| flushAlloc(vk, vkDevice, *bufferAlloc); |
| |
| // Copy buffer to image |
| beginCommandBuffer(vk, *m_cmdBuffer); |
| vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, |
| 1, &preBufferBarrier, (skipPreImageBarrier ? 0 : 1), (skipPreImageBarrier ? DE_NULL : &preImageBarrier)); |
| vk.cmdCopyBufferToImage(*m_cmdBuffer, *buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (deUint32)copyRegions.size(), ©Regions[0]); |
| vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &postImageBarrier); |
| endCommandBuffer(vk, *m_cmdBuffer); |
| |
| submitCommandsAndWait(vk, vkDevice, queue, *m_cmdBuffer); |
| } |
| |
| void CopiesAndBlittingTestInstance::uploadImage (const tcu::ConstPixelBufferAccess& src, VkImage dst, const ImageParms& parms, const deUint32 mipLevels) |
| { |
| if (tcu::isCombinedDepthStencilType(src.getFormat().type)) |
| { |
| if (tcu::hasDepthComponent(src.getFormat().order)) |
| { |
| tcu::TextureLevel depthTexture (mapCombinedToDepthTransferFormat(src.getFormat()), src.getWidth(), src.getHeight(), src.getDepth()); |
| tcu::copy(depthTexture.getAccess(), tcu::getEffectiveDepthStencilAccess(src, tcu::Sampler::MODE_DEPTH)); |
| uploadImageAspect(depthTexture.getAccess(), dst, parms); |
| } |
| |
| if (tcu::hasStencilComponent(src.getFormat().order)) |
| { |
| tcu::TextureLevel stencilTexture (tcu::getEffectiveDepthStencilTextureFormat(src.getFormat(), tcu::Sampler::MODE_STENCIL), src.getWidth(), src.getHeight(), src.getDepth()); |
| tcu::copy(stencilTexture.getAccess(), tcu::getEffectiveDepthStencilAccess(src, tcu::Sampler::MODE_STENCIL)); |
| uploadImageAspect(stencilTexture.getAccess(), dst, parms); |
| } |
| } |
| else |
| uploadImageAspect(src, dst, parms, mipLevels); |
| } |
| |
| tcu::TestStatus CopiesAndBlittingTestInstance::checkTestResult (tcu::ConstPixelBufferAccess result) |
| { |
| const tcu::ConstPixelBufferAccess expected = m_expectedTextureLevel[0]->getAccess(); |
| |
| if (isFloatFormat(result.getFormat())) |
| { |
| const tcu::Vec4 threshold (0.0f); |
| if (!tcu::floatThresholdCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", expected, result, threshold, tcu::COMPARE_LOG_RESULT)) |
| return tcu::TestStatus::fail("CopiesAndBlitting test"); |
| } |
| else |
| { |
| const tcu::UVec4 threshold (0u); |
| if (tcu::hasDepthComponent(result.getFormat().order) || tcu::hasStencilComponent(result.getFormat().order)) |
| { |
| if (!tcu::dsThresholdCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", expected, result, 0.1f, tcu::COMPARE_LOG_RESULT)) |
| return tcu::TestStatus::fail("CopiesAndBlitting test"); |
| } |
| else |
| { |
| if (!tcu::intThresholdCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", expected, result, threshold, tcu::COMPARE_LOG_RESULT)) |
| return tcu::TestStatus::fail("CopiesAndBlitting test"); |
| } |
| } |
| |
| return tcu::TestStatus::pass("CopiesAndBlitting test"); |
| } |
| |
| void CopiesAndBlittingTestInstance::generateExpectedResult (void) |
| { |
| const tcu::ConstPixelBufferAccess src = m_sourceTextureLevel->getAccess(); |
| const tcu::ConstPixelBufferAccess dst = m_destinationTextureLevel->getAccess(); |
| |
| m_expectedTextureLevel[0] = de::MovePtr<tcu::TextureLevel>(new tcu::TextureLevel(dst.getFormat(), dst.getWidth(), dst.getHeight(), dst.getDepth())); |
| tcu::copy(m_expectedTextureLevel[0]->getAccess(), dst); |
| |
| for (deUint32 i = 0; i < m_params.regions.size(); i++) |
| copyRegionToTextureLevel(src, m_expectedTextureLevel[0]->getAccess(), m_params.regions[i]); |
| } |
| |
| class CopiesAndBlittingTestCase : public vkt::TestCase |
| { |
| public: |
| CopiesAndBlittingTestCase (tcu::TestContext& testCtx, |
| const std::string& name, |
| const std::string& description) |
| : vkt::TestCase (testCtx, name, description) |
| {} |
| |
| virtual TestInstance* createInstance (Context& context) const = 0; |
| }; |
| |
| void CopiesAndBlittingTestInstance::readImageAspect (vk::VkImage image, |
| const tcu::PixelBufferAccess& dst, |
| const ImageParms& imageParms, |
| const deUint32 mipLevel) |
| { |
| const InstanceInterface& vki = m_context.getInstanceInterface(); |
| const DeviceInterface& vk = m_context.getDeviceInterface(); |
| const VkPhysicalDevice physDevice = m_context.getPhysicalDevice(); |
| const VkDevice device = m_context.getDevice(); |
| const VkQueue queue = m_context.getUniversalQueue(); |
| Allocator& allocator = m_context.getDefaultAllocator(); |
| |
| Move<VkBuffer> buffer; |
| de::MovePtr<Allocation> bufferAlloc; |
| const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); |
| const VkDeviceSize pixelDataSize = calculateSize(dst); |
| const VkExtent3D imageExtent = getExtent3D(imageParms, mipLevel); |
| |
| // Create destination buffer |
| { |
| const VkBufferCreateInfo bufferParams = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferCreateFlags flags; |
| pixelDataSize, // VkDeviceSize size; |
| VK_BUFFER_USAGE_TRANSFER_DST_BIT, // VkBufferUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyIndexCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| }; |
| |
| buffer = createBuffer(vk, device, &bufferParams); |
| bufferAlloc = allocateBuffer(vki, vk, physDevice, device, *buffer, MemoryRequirement::HostVisible, allocator, m_params.allocationKind); |
| VK_CHECK(vk.bindBufferMemory(device, *buffer, bufferAlloc->getMemory(), bufferAlloc->getOffset())); |
| |
| deMemset(bufferAlloc->getHostPtr(), 0, static_cast<size_t>(pixelDataSize)); |
| flushAlloc(vk, device, *bufferAlloc); |
| } |
| |
| // Barriers for copying image to buffer |
| const VkImageAspectFlags formatAspect = getAspectFlags(imageParms.format); |
| const VkImageMemoryBarrier imageBarrier = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask; |
| imageParms.operationLayout, // VkImageLayout oldLayout; |
| VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout newLayout; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| image, // VkImage image; |
| { // VkImageSubresourceRange subresourceRange; |
| formatAspect, // VkImageAspectFlags aspectMask; |
| mipLevel, // deUint32 baseMipLevel; |
| 1u, // deUint32 mipLevels; |
| 0u, // deUint32 baseArraySlice; |
| getArraySize(imageParms)// deUint32 arraySize; |
| } |
| }; |
| |
| const VkBufferMemoryBarrier bufferBarrier = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_HOST_READ_BIT, // VkAccessFlags dstAccessMask; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| *buffer, // VkBuffer buffer; |
| 0u, // VkDeviceSize offset; |
| pixelDataSize // VkDeviceSize size; |
| }; |
| |
| const VkImageMemoryBarrier postImageBarrier = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask; |
| VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout oldLayout; |
| imageParms.operationLayout, // VkImageLayout newLayout; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| image, // VkImage image; |
| { |
| formatAspect, // VkImageAspectFlags aspectMask; |
| mipLevel, // deUint32 baseMipLevel; |
| 1u, // deUint32 mipLevels; |
| 0u, // deUint32 baseArraySlice; |
| getArraySize(imageParms) // deUint32 arraySize; |
| } // VkImageSubresourceRange subresourceRange; |
| }; |
| |
| // Copy image to buffer |
| const VkImageAspectFlags aspect = getAspectFlags(dst.getFormat()); |
| const VkBufferImageCopy copyRegion = |
| { |
| 0u, // VkDeviceSize bufferOffset; |
| imageExtent.width, // deUint32 bufferRowLength; |
| imageExtent.height, // deUint32 bufferImageHeight; |
| { |
| aspect, // VkImageAspectFlags aspect; |
| mipLevel, // deUint32 mipLevel; |
| 0u, // deUint32 baseArrayLayer; |
| getArraySize(imageParms), // deUint32 layerCount; |
| }, // VkImageSubresourceLayers imageSubresource; |
| { 0, 0, 0 }, // VkOffset3D imageOffset; |
| imageExtent // VkExtent3D imageExtent; |
| }; |
| |
| beginCommandBuffer(vk, *m_cmdBuffer); |
| vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &imageBarrier); |
| vk.cmdCopyImageToBuffer(*m_cmdBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *buffer, 1u, ©Region); |
| vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT|VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &bufferBarrier, 1, &postImageBarrier); |
| endCommandBuffer(vk, *m_cmdBuffer); |
| |
| submitCommandsAndWait(vk, device, queue, *m_cmdBuffer); |
| |
| // Read buffer data |
| invalidateAlloc(vk, device, *bufferAlloc); |
| tcu::copy(dst, tcu::ConstPixelBufferAccess(dst.getFormat(), dst.getSize(), bufferAlloc->getHostPtr())); |
| } |
| |
| de::MovePtr<tcu::TextureLevel> CopiesAndBlittingTestInstance::readImage (vk::VkImage image, |
| const ImageParms& parms, |
| const deUint32 mipLevel) |
| { |
| const tcu::TextureFormat imageFormat = getSizeCompatibleTcuTextureFormat(parms.format); |
| de::MovePtr<tcu::TextureLevel> resultLevel (new tcu::TextureLevel(imageFormat, parms.extent.width >> mipLevel, parms.extent.height >> mipLevel, parms.extent.depth)); |
| |
| if (tcu::isCombinedDepthStencilType(imageFormat.type)) |
| { |
| if (tcu::hasDepthComponent(imageFormat.order)) |
| { |
| tcu::TextureLevel depthTexture (mapCombinedToDepthTransferFormat(imageFormat), parms.extent.width, parms.extent.height, parms.extent.depth); |
| readImageAspect(image, depthTexture.getAccess(), parms); |
| tcu::copy(tcu::getEffectiveDepthStencilAccess(resultLevel->getAccess(), tcu::Sampler::MODE_DEPTH), depthTexture.getAccess()); |
| } |
| |
| if (tcu::hasStencilComponent(imageFormat.order)) |
| { |
| tcu::TextureLevel stencilTexture (tcu::getEffectiveDepthStencilTextureFormat(imageFormat, tcu::Sampler::MODE_STENCIL), parms.extent.width, parms.extent.height, parms.extent.depth); |
| readImageAspect(image, stencilTexture.getAccess(), parms); |
| tcu::copy(tcu::getEffectiveDepthStencilAccess(resultLevel->getAccess(), tcu::Sampler::MODE_STENCIL), stencilTexture.getAccess()); |
| } |
| } |
| else |
| readImageAspect(image, resultLevel->getAccess(), parms, mipLevel); |
| |
| return resultLevel; |
| } |
| |
| // Copy from image to image. |
| |
| class CopyImageToImage : public CopiesAndBlittingTestInstance |
| { |
| public: |
| CopyImageToImage (Context& context, |
| TestParams params); |
| virtual tcu::TestStatus iterate (void); |
| |
| protected: |
| virtual tcu::TestStatus checkTestResult (tcu::ConstPixelBufferAccess result = tcu::ConstPixelBufferAccess()); |
| |
| private: |
| Move<VkImage> m_source; |
| de::MovePtr<Allocation> m_sourceImageAlloc; |
| Move<VkImage> m_destination; |
| de::MovePtr<Allocation> m_destinationImageAlloc; |
| |
| virtual void copyRegionToTextureLevel (tcu::ConstPixelBufferAccess src, tcu::PixelBufferAccess dst, CopyRegion region, deUint32 mipLevel = 0u); |
| }; |
| |
| CopyImageToImage::CopyImageToImage (Context& context, TestParams params) |
| : CopiesAndBlittingTestInstance(context, params) |
| { |
| const InstanceInterface& vki = context.getInstanceInterface(); |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const VkPhysicalDevice vkPhysDevice = context.getPhysicalDevice(); |
| const VkDevice vkDevice = context.getDevice(); |
| const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); |
| Allocator& memAlloc = context.getDefaultAllocator(); |
| |
| // Create source image |
| { |
| const VkImageCreateInfo sourceImageParams = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| getCreateFlags(m_params.src.image), // VkImageCreateFlags flags; |
| m_params.src.image.imageType, // VkImageType imageType; |
| m_params.src.image.format, // VkFormat format; |
| getExtent3D(m_params.src.image), // VkExtent3D extent; |
| 1u, // deUint32 mipLevels; |
| getArraySize(m_params.src.image), // deUint32 arraySize; |
| VK_SAMPLE_COUNT_1_BIT, // deUint32 samples; |
| VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; |
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT | |
| VK_IMAGE_USAGE_TRANSFER_DST_BIT, // VkImageUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| }; |
| |
| m_source = createImage(vk, vkDevice, &sourceImageParams); |
| m_sourceImageAlloc = allocateImage(vki, vk, vkPhysDevice, vkDevice, *m_source, MemoryRequirement::Any, memAlloc, m_params.allocationKind); |
| VK_CHECK(vk.bindImageMemory(vkDevice, *m_source, m_sourceImageAlloc->getMemory(), m_sourceImageAlloc->getOffset())); |
| } |
| |
| // Create destination image |
| { |
| const VkImageCreateInfo destinationImageParams = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| getCreateFlags(m_params.dst.image), // VkImageCreateFlags flags; |
| m_params.dst.image.imageType, // VkImageType imageType; |
| m_params.dst.image.format, // VkFormat format; |
| getExtent3D(m_params.dst.image), // VkExtent3D extent; |
| 1u, // deUint32 mipLevels; |
| getArraySize(m_params.dst.image), // deUint32 arraySize; |
| VK_SAMPLE_COUNT_1_BIT, // deUint32 samples; |
| VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; |
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT | |
| VK_IMAGE_USAGE_TRANSFER_DST_BIT, // VkImageUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| }; |
| |
| m_destination = createImage(vk, vkDevice, &destinationImageParams); |
| m_destinationImageAlloc = allocateImage(vki, vk, vkPhysDevice, vkDevice, *m_destination, MemoryRequirement::Any, memAlloc, m_params.allocationKind); |
| VK_CHECK(vk.bindImageMemory(vkDevice, *m_destination, m_destinationImageAlloc->getMemory(), m_destinationImageAlloc->getOffset())); |
| } |
| } |
| |
| tcu::TestStatus CopyImageToImage::iterate (void) |
| { |
| const bool srcCompressed = isCompressedFormat(m_params.src.image.format); |
| const bool dstCompressed = isCompressedFormat(m_params.dst.image.format); |
| |
| const tcu::TextureFormat srcTcuFormat = getSizeCompatibleTcuTextureFormat(m_params.src.image.format); |
| const tcu::TextureFormat dstTcuFormat = getSizeCompatibleTcuTextureFormat(m_params.dst.image.format); |
| |
| m_sourceTextureLevel = de::MovePtr<tcu::TextureLevel>(new tcu::TextureLevel(srcTcuFormat, |
| (int)m_params.src.image.extent.width, |
| (int)m_params.src.image.extent.height, |
| (int)m_params.src.image.extent.depth)); |
| generateBuffer(m_sourceTextureLevel->getAccess(), m_params.src.image.extent.width, m_params.src.image.extent.height, m_params.src.image.extent.depth, FILL_MODE_GRADIENT); |
| m_destinationTextureLevel = de::MovePtr<tcu::TextureLevel>(new tcu::TextureLevel(dstTcuFormat, |
| (int)m_params.dst.image.extent.width, |
| (int)m_params.dst.image.extent.height, |
| (int)m_params.dst.image.extent.depth)); |
| generateBuffer(m_destinationTextureLevel->getAccess(), m_params.dst.image.extent.width, m_params.dst.image.extent.height, m_params.dst.image.extent.depth, FILL_MODE_GRADIENT); |
| generateExpectedResult(); |
| |
| uploadImage(m_sourceTextureLevel->getAccess(), m_source.get(), m_params.src.image); |
| uploadImage(m_destinationTextureLevel->getAccess(), m_destination.get(), m_params.dst.image); |
| |
| const DeviceInterface& vk = m_context.getDeviceInterface(); |
| const VkDevice vkDevice = m_context.getDevice(); |
| const VkQueue queue = m_context.getUniversalQueue(); |
| |
| std::vector<VkImageCopy> imageCopies; |
| for (deUint32 i = 0; i < m_params.regions.size(); i++) |
| { |
| VkImageCopy imageCopy = m_params.regions[i].imageCopy; |
| |
| // When copying between compressed and uncompressed formats the extent |
| // members represent the texel dimensions of the source image. |
| if (srcCompressed) |
| { |
| const deUint32 blockWidth = getBlockWidth(m_params.src.image.format); |
| const deUint32 blockHeight = getBlockHeight(m_params.src.image.format); |
| |
| imageCopy.srcOffset.x *= blockWidth; |
| imageCopy.srcOffset.y *= blockHeight; |
| imageCopy.extent.width *= blockWidth; |
| imageCopy.extent.height *= blockHeight; |
| } |
| |
| if (dstCompressed) |
| { |
| const deUint32 blockWidth = getBlockWidth(m_params.dst.image.format); |
| const deUint32 blockHeight = getBlockHeight(m_params.dst.image.format); |
| |
| imageCopy.dstOffset.x *= blockWidth; |
| imageCopy.dstOffset.y *= blockHeight; |
| } |
| |
| imageCopies.push_back(imageCopy); |
| } |
| |
| const VkImageMemoryBarrier imageBarriers[] = |
| { |
| // source image |
| { |
| VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask; |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout; |
| m_params.src.image.operationLayout, // VkImageLayout newLayout; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| m_source.get(), // VkImage image; |
| { // VkImageSubresourceRange subresourceRange; |
| getAspectFlags(srcTcuFormat), // VkImageAspectFlags aspectMask; |
| 0u, // deUint32 baseMipLevel; |
| 1u, // deUint32 mipLevels; |
| 0u, // deUint32 baseArraySlice; |
| getArraySize(m_params.src.image)// deUint32 arraySize; |
| } |
| }, |
| // destination image |
| { |
| VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask; |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout; |
| m_params.dst.image.operationLayout, // VkImageLayout newLayout; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| m_destination.get(), // VkImage image; |
| { // VkImageSubresourceRange subresourceRange; |
| getAspectFlags(dstTcuFormat), // VkImageAspectFlags aspectMask; |
| 0u, // deUint32 baseMipLevel; |
| 1u, // deUint32 mipLevels; |
| 0u, // deUint32 baseArraySlice; |
| getArraySize(m_params.dst.image)// deUint32 arraySize; |
| } |
| }, |
| }; |
| |
| beginCommandBuffer(vk, *m_cmdBuffer); |
| vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, DE_LENGTH_OF_ARRAY(imageBarriers), imageBarriers); |
| vk.cmdCopyImage(*m_cmdBuffer, m_source.get(), m_params.src.image.operationLayout, m_destination.get(), m_params.dst.image.operationLayout, (deUint32)imageCopies.size(), imageCopies.data()); |
| endCommandBuffer(vk, *m_cmdBuffer); |
| |
| submitCommandsAndWait (vk, vkDevice, queue, *m_cmdBuffer); |
| |
| de::MovePtr<tcu::TextureLevel> resultTextureLevel = readImage(*m_destination, m_params.dst.image); |
| |
| return checkTestResult(resultTextureLevel->getAccess()); |
| } |
| |
| tcu::TestStatus CopyImageToImage::checkTestResult (tcu::ConstPixelBufferAccess result) |
| { |
| const tcu::Vec4 fThreshold (0.0f); |
| const tcu::UVec4 uThreshold (0u); |
| |
| if (tcu::isCombinedDepthStencilType(result.getFormat().type)) |
| { |
| if (tcu::hasDepthComponent(result.getFormat().order)) |
| { |
| const tcu::Sampler::DepthStencilMode mode = tcu::Sampler::MODE_DEPTH; |
| const tcu::ConstPixelBufferAccess depthResult = tcu::getEffectiveDepthStencilAccess(result, mode); |
| const tcu::ConstPixelBufferAccess expectedResult = tcu::getEffectiveDepthStencilAccess(m_expectedTextureLevel[0]->getAccess(), mode); |
| |
| if (isFloatFormat(result.getFormat())) |
| { |
| if (!tcu::floatThresholdCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", expectedResult, depthResult, fThreshold, tcu::COMPARE_LOG_RESULT)) |
| return tcu::TestStatus::fail("CopiesAndBlitting test"); |
| } |
| else |
| { |
| if (!tcu::intThresholdCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", expectedResult, depthResult, uThreshold, tcu::COMPARE_LOG_RESULT)) |
| return tcu::TestStatus::fail("CopiesAndBlitting test"); |
| } |
| } |
| |
| if (tcu::hasStencilComponent(result.getFormat().order)) |
| { |
| const tcu::Sampler::DepthStencilMode mode = tcu::Sampler::MODE_STENCIL; |
| const tcu::ConstPixelBufferAccess stencilResult = tcu::getEffectiveDepthStencilAccess(result, mode); |
| const tcu::ConstPixelBufferAccess expectedResult = tcu::getEffectiveDepthStencilAccess(m_expectedTextureLevel[0]->getAccess(), mode); |
| |
| if (isFloatFormat(result.getFormat())) |
| { |
| if (!tcu::floatThresholdCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", expectedResult, stencilResult, fThreshold, tcu::COMPARE_LOG_RESULT)) |
| return tcu::TestStatus::fail("CopiesAndBlitting test"); |
| } |
| else |
| { |
| if (!tcu::intThresholdCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", expectedResult, stencilResult, uThreshold, tcu::COMPARE_LOG_RESULT)) |
| return tcu::TestStatus::fail("CopiesAndBlitting test"); |
| } |
| } |
| } |
| else |
| { |
| if (isFloatFormat(result.getFormat())) |
| { |
| if (!tcu::floatThresholdCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", m_expectedTextureLevel[0]->getAccess(), result, fThreshold, tcu::COMPARE_LOG_RESULT)) |
| return tcu::TestStatus::fail("CopiesAndBlitting test"); |
| } |
| else if (isSnormFormat(mapTextureFormat(result.getFormat()))) |
| { |
| // There may be an ambiguity between two possible binary representations of 1.0. |
| // Get rid of that by expanding the data to floats and re-normalizing again. |
| |
| tcu::TextureLevel resultSnorm (result.getFormat(), result.getWidth(), result.getHeight(), result.getDepth()); |
| { |
| tcu::TextureLevel resultFloat (tcu::TextureFormat(resultSnorm.getFormat().order, tcu::TextureFormat::FLOAT), resultSnorm.getWidth(), resultSnorm.getHeight(), resultSnorm.getDepth()); |
| |
| tcu::copy(resultFloat.getAccess(), result); |
| tcu::copy(resultSnorm, resultFloat.getAccess()); |
| } |
| |
| tcu::TextureLevel expectedSnorm (m_expectedTextureLevel[0]->getFormat(), m_expectedTextureLevel[0]->getWidth(), m_expectedTextureLevel[0]->getHeight(), m_expectedTextureLevel[0]->getDepth()); |
| |
| { |
| tcu::TextureLevel expectedFloat (tcu::TextureFormat(expectedSnorm.getFormat().order, tcu::TextureFormat::FLOAT), expectedSnorm.getWidth(), expectedSnorm.getHeight(), expectedSnorm.getDepth()); |
| |
| tcu::copy(expectedFloat.getAccess(), m_expectedTextureLevel[0]->getAccess()); |
| tcu::copy(expectedSnorm, expectedFloat.getAccess()); |
| } |
| |
| if (!tcu::intThresholdCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", expectedSnorm.getAccess(), resultSnorm.getAccess(), uThreshold, tcu::COMPARE_LOG_RESULT)) |
| return tcu::TestStatus::fail("CopiesAndBlitting test"); |
| } |
| else |
| { |
| if (!tcu::intThresholdCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", m_expectedTextureLevel[0]->getAccess(), result, uThreshold, tcu::COMPARE_LOG_RESULT)) |
| return tcu::TestStatus::fail("CopiesAndBlitting test"); |
| } |
| } |
| |
| return tcu::TestStatus::pass("CopiesAndBlitting test"); |
| } |
| |
| void CopyImageToImage::copyRegionToTextureLevel (tcu::ConstPixelBufferAccess src, tcu::PixelBufferAccess dst, CopyRegion region, deUint32 mipLevel) |
| { |
| DE_UNREF(mipLevel); |
| |
| VkOffset3D srcOffset = region.imageCopy.srcOffset; |
| VkOffset3D dstOffset = region.imageCopy.dstOffset; |
| VkExtent3D extent = region.imageCopy.extent; |
| |
| if (m_params.src.image.imageType == VK_IMAGE_TYPE_3D && m_params.dst.image.imageType == VK_IMAGE_TYPE_2D) |
| { |
| dstOffset.z = srcOffset.z; |
| extent.depth = std::max(region.imageCopy.extent.depth, region.imageCopy.dstSubresource.layerCount); |
| } |
| if (m_params.src.image.imageType == VK_IMAGE_TYPE_2D && m_params.dst.image.imageType == VK_IMAGE_TYPE_3D) |
| { |
| srcOffset.z = dstOffset.z; |
| extent.depth = std::max(region.imageCopy.extent.depth, region.imageCopy.srcSubresource.layerCount); |
| } |
| |
| |
| if (tcu::isCombinedDepthStencilType(src.getFormat().type)) |
| { |
| DE_ASSERT(src.getFormat() == dst.getFormat()); |
| |
| // Copy depth. |
| if (tcu::hasDepthComponent(src.getFormat().order)) |
| { |
| const tcu::ConstPixelBufferAccess srcSubRegion = getEffectiveDepthStencilAccess(tcu::getSubregion(src, srcOffset.x, srcOffset.y, srcOffset.z, extent.width, extent.height, extent.depth), tcu::Sampler::MODE_DEPTH); |
| const tcu::PixelBufferAccess dstSubRegion = getEffectiveDepthStencilAccess(tcu::getSubregion(dst, dstOffset.x, dstOffset.y, dstOffset.z, extent.width, extent.height, extent.depth), tcu::Sampler::MODE_DEPTH); |
| tcu::copy(dstSubRegion, srcSubRegion); |
| } |
| |
| // Copy stencil. |
| if (tcu::hasStencilComponent(src.getFormat().order)) |
| { |
| const tcu::ConstPixelBufferAccess srcSubRegion = getEffectiveDepthStencilAccess(tcu::getSubregion(src, srcOffset.x, srcOffset.y, srcOffset.z, extent.width, extent.height, extent.depth), tcu::Sampler::MODE_STENCIL); |
| const tcu::PixelBufferAccess dstSubRegion = getEffectiveDepthStencilAccess(tcu::getSubregion(dst, dstOffset.x, dstOffset.y, dstOffset.z, extent.width, extent.height, extent.depth), tcu::Sampler::MODE_STENCIL); |
| tcu::copy(dstSubRegion, srcSubRegion); |
| } |
| } |
| else |
| { |
| const tcu::ConstPixelBufferAccess srcSubRegion = tcu::getSubregion(src, srcOffset.x, srcOffset.y, srcOffset.z, extent.width, extent.height, extent.depth); |
| // CopyImage acts like a memcpy. Replace the destination format with the srcformat to use a memcpy. |
| const tcu::PixelBufferAccess dstWithSrcFormat (srcSubRegion.getFormat(), dst.getSize(), dst.getDataPtr()); |
| const tcu::PixelBufferAccess dstSubRegion = tcu::getSubregion(dstWithSrcFormat, dstOffset.x, dstOffset.y, dstOffset.z, extent.width, extent.height, extent.depth); |
| |
| tcu::copy(dstSubRegion, srcSubRegion); |
| } |
| } |
| |
| class CopyImageToImageTestCase : public vkt::TestCase |
| { |
| public: |
| CopyImageToImageTestCase (tcu::TestContext& testCtx, |
| const std::string& name, |
| const std::string& description, |
| const TestParams params) |
| : vkt::TestCase (testCtx, name, description) |
| , m_params (params) |
| {} |
| |
| virtual TestInstance* createInstance (Context& context) const |
| { |
| return new CopyImageToImage(context, m_params); |
| } |
| |
| virtual void checkSupport (Context& context) const |
| { |
| if (m_params.allocationKind == ALLOCATION_KIND_DEDICATED) |
| { |
| if (!context.isDeviceFunctionalitySupported("VK_KHR_dedicated_allocation")) |
| TCU_THROW(NotSupportedError, "VK_KHR_dedicated_allocation is not supported"); |
| } |
| |
| if (m_params.separateDepthStencilLayouts) |
| if (!context.isDeviceFunctionalitySupported("VK_KHR_separate_depth_stencil_layouts")) |
| TCU_THROW(NotSupportedError, "VK_KHR_separate_depth_stencil_layouts is not supported"); |
| |
| if ((m_params.dst.image.imageType == VK_IMAGE_TYPE_3D && m_params.src.image.imageType == VK_IMAGE_TYPE_2D) || |
| (m_params.dst.image.imageType == VK_IMAGE_TYPE_2D && m_params.src.image.imageType == VK_IMAGE_TYPE_3D)) |
| { |
| if (!context.isDeviceFunctionalitySupported("VK_KHR_maintenance1")) |
| TCU_THROW(NotSupportedError, "Extension VK_KHR_maintenance1 not supported"); |
| } |
| |
| const VkPhysicalDeviceLimits limits = context.getDeviceProperties().limits; |
| VkImageFormatProperties properties; |
| |
| if ((context.getInstanceInterface().getPhysicalDeviceImageFormatProperties (context.getPhysicalDevice(), |
| m_params.src.image.format, |
| m_params.src.image.imageType, |
| VK_IMAGE_TILING_OPTIMAL, |
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT, |
| 0, |
| &properties) == VK_ERROR_FORMAT_NOT_SUPPORTED) || |
| (context.getInstanceInterface().getPhysicalDeviceImageFormatProperties (context.getPhysicalDevice(), |
| m_params.dst.image.format, |
| m_params.dst.image.imageType, |
| VK_IMAGE_TILING_OPTIMAL, |
| VK_IMAGE_USAGE_TRANSFER_DST_BIT, |
| 0, |
| &properties) == VK_ERROR_FORMAT_NOT_SUPPORTED)) |
| { |
| TCU_THROW(NotSupportedError, "Format not supported"); |
| } |
| |
| // Check maxImageDimension2D |
| { |
| if (m_params.src.image.imageType == VK_IMAGE_TYPE_2D && (m_params.src.image.extent.width > limits.maxImageDimension2D |
| || m_params.src.image.extent.height > limits.maxImageDimension2D)) |
| { |
| TCU_THROW(NotSupportedError, "Requested 2D src image dimensions not supported"); |
| } |
| |
| if (m_params.dst.image.imageType == VK_IMAGE_TYPE_2D && (m_params.dst.image.extent.width > limits.maxImageDimension2D |
| || m_params.dst.image.extent.height > limits.maxImageDimension2D)) |
| { |
| TCU_THROW(NotSupportedError, "Requested 2D dst image dimensions not supported"); |
| } |
| } |
| |
| // Check maxImageDimension3D |
| { |
| if (m_params.src.image.imageType == VK_IMAGE_TYPE_3D && (m_params.src.image.extent.width > limits.maxImageDimension3D |
| || m_params.src.image.extent.height > limits.maxImageDimension3D |
| || m_params.src.image.extent.depth > limits.maxImageDimension3D)) |
| { |
| TCU_THROW(NotSupportedError, "Requested 3D src image dimensions not supported"); |
| } |
| |
| if (m_params.dst.image.imageType == VK_IMAGE_TYPE_3D && (m_params.dst.image.extent.width > limits.maxImageDimension3D |
| || m_params.dst.image.extent.height > limits.maxImageDimension3D |
| || m_params.src.image.extent.depth > limits.maxImageDimension3D)) |
| { |
| TCU_THROW(NotSupportedError, "Requested 3D dst image dimensions not supported"); |
| } |
| } |
| } |
| |
| private: |
| TestParams m_params; |
| }; |
| |
| // Copy from buffer to buffer. |
| |
| class CopyBufferToBuffer : public CopiesAndBlittingTestInstance |
| { |
| public: |
| CopyBufferToBuffer (Context& context, TestParams params); |
| virtual tcu::TestStatus iterate (void); |
| private: |
| virtual void copyRegionToTextureLevel (tcu::ConstPixelBufferAccess, tcu::PixelBufferAccess, CopyRegion, deUint32 mipLevel = 0u); |
| Move<VkBuffer> m_source; |
| de::MovePtr<Allocation> m_sourceBufferAlloc; |
| Move<VkBuffer> m_destination; |
| de::MovePtr<Allocation> m_destinationBufferAlloc; |
| }; |
| |
| CopyBufferToBuffer::CopyBufferToBuffer (Context& context, TestParams params) |
| : CopiesAndBlittingTestInstance (context, params) |
| { |
| const InstanceInterface& vki = context.getInstanceInterface(); |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const VkPhysicalDevice vkPhysDevice = context.getPhysicalDevice(); |
| const VkDevice vkDevice = context.getDevice(); |
| const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); |
| Allocator& memAlloc = context.getDefaultAllocator(); |
| |
| // Create source buffer |
| { |
| const VkBufferCreateInfo sourceBufferParams = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferCreateFlags flags; |
| m_params.src.buffer.size, // VkDeviceSize size; |
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT, // VkBufferUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyIndexCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| }; |
| |
| m_source = createBuffer(vk, vkDevice, &sourceBufferParams); |
| m_sourceBufferAlloc = allocateBuffer(vki, vk, vkPhysDevice, vkDevice, *m_source, MemoryRequirement::HostVisible, memAlloc, m_params.allocationKind); |
| VK_CHECK(vk.bindBufferMemory(vkDevice, *m_source, m_sourceBufferAlloc->getMemory(), m_sourceBufferAlloc->getOffset())); |
| } |
| |
| // Create destination buffer |
| { |
| const VkBufferCreateInfo destinationBufferParams = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferCreateFlags flags; |
| m_params.dst.buffer.size, // VkDeviceSize size; |
| VK_BUFFER_USAGE_TRANSFER_DST_BIT, // VkBufferUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyIndexCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| }; |
| |
| m_destination = createBuffer(vk, vkDevice, &destinationBufferParams); |
| m_destinationBufferAlloc = allocateBuffer(vki, vk, vkPhysDevice, vkDevice, *m_destination, MemoryRequirement::HostVisible, memAlloc, m_params.allocationKind); |
| VK_CHECK(vk.bindBufferMemory(vkDevice, *m_destination, m_destinationBufferAlloc->getMemory(), m_destinationBufferAlloc->getOffset())); |
| } |
| } |
| |
| tcu::TestStatus CopyBufferToBuffer::iterate (void) |
| { |
| const int srcLevelWidth = (int)(m_params.src.buffer.size/4); // Here the format is VK_FORMAT_R32_UINT, we need to divide the buffer size by 4 |
| m_sourceTextureLevel = de::MovePtr<tcu::TextureLevel>(new tcu::TextureLevel(mapVkFormat(VK_FORMAT_R32_UINT), srcLevelWidth, 1)); |
| generateBuffer(m_sourceTextureLevel->getAccess(), srcLevelWidth, 1, 1, FILL_MODE_RED); |
| |
| const int dstLevelWidth = (int)(m_params.dst.buffer.size/4); |
| m_destinationTextureLevel = de::MovePtr<tcu::TextureLevel>(new tcu::TextureLevel(mapVkFormat(VK_FORMAT_R32_UINT), dstLevelWidth, 1)); |
| generateBuffer(m_destinationTextureLevel->getAccess(), dstLevelWidth, 1, 1, FILL_MODE_WHITE); |
| |
| generateExpectedResult(); |
| |
| uploadBuffer(m_sourceTextureLevel->getAccess(), *m_sourceBufferAlloc); |
| uploadBuffer(m_destinationTextureLevel->getAccess(), *m_destinationBufferAlloc); |
| |
| const DeviceInterface& vk = m_context.getDeviceInterface(); |
| const VkDevice vkDevice = m_context.getDevice(); |
| const VkQueue queue = m_context.getUniversalQueue(); |
| |
| const VkBufferMemoryBarrier srcBufferBarrier = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_HOST_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| *m_source, // VkBuffer buffer; |
| 0u, // VkDeviceSize offset; |
| m_params.src.buffer.size // VkDeviceSize size; |
| }; |
| |
| const VkBufferMemoryBarrier dstBufferBarrier = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_HOST_READ_BIT, // VkAccessFlags dstAccessMask; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| *m_destination, // VkBuffer buffer; |
| 0u, // VkDeviceSize offset; |
| m_params.dst.buffer.size // VkDeviceSize size; |
| }; |
| |
| std::vector<VkBufferCopy> bufferCopies; |
| for (deUint32 i = 0; i < m_params.regions.size(); i++) |
| bufferCopies.push_back(m_params.regions[i].bufferCopy); |
| |
| beginCommandBuffer(vk, *m_cmdBuffer); |
| vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &srcBufferBarrier, 0, (const VkImageMemoryBarrier*)DE_NULL); |
| vk.cmdCopyBuffer(*m_cmdBuffer, m_source.get(), m_destination.get(), (deUint32)m_params.regions.size(), &bufferCopies[0]); |
| vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &dstBufferBarrier, 0, (const VkImageMemoryBarrier*)DE_NULL); |
| endCommandBuffer(vk, *m_cmdBuffer); |
| submitCommandsAndWait(vk, vkDevice, queue, *m_cmdBuffer); |
| |
| // Read buffer data |
| de::MovePtr<tcu::TextureLevel> resultLevel (new tcu::TextureLevel(mapVkFormat(VK_FORMAT_R32_UINT), dstLevelWidth, 1)); |
| invalidateAlloc(vk, vkDevice, *m_destinationBufferAlloc); |
| tcu::copy(*resultLevel, tcu::ConstPixelBufferAccess(resultLevel->getFormat(), resultLevel->getSize(), m_destinationBufferAlloc->getHostPtr())); |
| |
| return checkTestResult(resultLevel->getAccess()); |
| } |
| |
| void CopyBufferToBuffer::copyRegionToTextureLevel (tcu::ConstPixelBufferAccess src, tcu::PixelBufferAccess dst, CopyRegion region, deUint32 mipLevel) |
| { |
| DE_UNREF(mipLevel); |
| |
| deMemcpy((deUint8*) dst.getDataPtr() + region.bufferCopy.dstOffset, |
| (deUint8*) src.getDataPtr() + region.bufferCopy.srcOffset, |
| (size_t)region.bufferCopy.size); |
| } |
| |
| class BufferToBufferTestCase : public vkt::TestCase |
| { |
| public: |
| BufferToBufferTestCase (tcu::TestContext& testCtx, |
| const std::string& name, |
| const std::string& description, |
| const TestParams params) |
| : vkt::TestCase (testCtx, name, description) |
| , m_params (params) |
| {} |
| |
| virtual TestInstance* createInstance (Context& context) const |
| { |
| return new CopyBufferToBuffer(context, m_params); |
| } |
| private: |
| TestParams m_params; |
| }; |
| |
| // Copy from image to buffer. |
| |
| class CopyImageToBuffer : public CopiesAndBlittingTestInstance |
| { |
| public: |
| CopyImageToBuffer (Context& context, |
| TestParams testParams); |
| virtual tcu::TestStatus iterate (void); |
| private: |
| virtual void copyRegionToTextureLevel (tcu::ConstPixelBufferAccess src, tcu::PixelBufferAccess dst, CopyRegion region, deUint32 mipLevel = 0u); |
| |
| tcu::TextureFormat m_textureFormat; |
| VkDeviceSize m_bufferSize; |
| |
| Move<VkImage> m_source; |
| de::MovePtr<Allocation> m_sourceImageAlloc; |
| Move<VkBuffer> m_destination; |
| de::MovePtr<Allocation> m_destinationBufferAlloc; |
| }; |
| |
| CopyImageToBuffer::CopyImageToBuffer (Context& context, TestParams testParams) |
| : CopiesAndBlittingTestInstance(context, testParams) |
| , m_textureFormat(mapVkFormat(testParams.src.image.format)) |
| , m_bufferSize(m_params.dst.buffer.size * tcu::getPixelSize(m_textureFormat)) |
| { |
| const InstanceInterface& vki = context.getInstanceInterface(); |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const VkPhysicalDevice vkPhysDevice = context.getPhysicalDevice(); |
| const VkDevice vkDevice = context.getDevice(); |
| const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); |
| Allocator& memAlloc = context.getDefaultAllocator(); |
| |
| // Create source image |
| { |
| const VkImageCreateInfo sourceImageParams = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| getCreateFlags(m_params.src.image), // VkImageCreateFlags flags; |
| m_params.src.image.imageType, // VkImageType imageType; |
| m_params.src.image.format, // VkFormat format; |
| getExtent3D(m_params.src.image), // VkExtent3D extent; |
| 1u, // deUint32 mipLevels; |
| getArraySize(m_params.src.image), // deUint32 arraySize; |
| VK_SAMPLE_COUNT_1_BIT, // deUint32 samples; |
| VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; |
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT | |
| VK_IMAGE_USAGE_TRANSFER_DST_BIT, // VkImageUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| }; |
| |
| m_source = createImage(vk, vkDevice, &sourceImageParams); |
| m_sourceImageAlloc = allocateImage(vki, vk, vkPhysDevice, vkDevice, *m_source, MemoryRequirement::Any, memAlloc, m_params.allocationKind); |
| VK_CHECK(vk.bindImageMemory(vkDevice, *m_source, m_sourceImageAlloc->getMemory(), m_sourceImageAlloc->getOffset())); |
| } |
| |
| // Create destination buffer |
| { |
| const VkBufferCreateInfo destinationBufferParams = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferCreateFlags flags; |
| m_bufferSize, // VkDeviceSize size; |
| VK_BUFFER_USAGE_TRANSFER_DST_BIT, // VkBufferUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyIndexCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| }; |
| |
| m_destination = createBuffer(vk, vkDevice, &destinationBufferParams); |
| m_destinationBufferAlloc = allocateBuffer(vki, vk, vkPhysDevice, vkDevice, *m_destination, MemoryRequirement::HostVisible, memAlloc, m_params.allocationKind); |
| VK_CHECK(vk.bindBufferMemory(vkDevice, *m_destination, m_destinationBufferAlloc->getMemory(), m_destinationBufferAlloc->getOffset())); |
| } |
| } |
| |
| tcu::TestStatus CopyImageToBuffer::iterate (void) |
| { |
| m_sourceTextureLevel = de::MovePtr<tcu::TextureLevel>(new tcu::TextureLevel(m_textureFormat, |
| m_params.src.image.extent.width, |
| m_params.src.image.extent.height, |
| m_params.src.image.extent.depth)); |
| generateBuffer(m_sourceTextureLevel->getAccess(), m_params.src.image.extent.width, m_params.src.image.extent.height, m_params.src.image.extent.depth); |
| m_destinationTextureLevel = de::MovePtr<tcu::TextureLevel>(new tcu::TextureLevel(m_textureFormat, (int)m_params.dst.buffer.size, 1)); |
| generateBuffer(m_destinationTextureLevel->getAccess(), (int)m_params.dst.buffer.size, 1, 1); |
| |
| generateExpectedResult(); |
| |
| uploadImage(m_sourceTextureLevel->getAccess(), *m_source, m_params.src.image); |
| uploadBuffer(m_destinationTextureLevel->getAccess(), *m_destinationBufferAlloc); |
| |
| const DeviceInterface& vk = m_context.getDeviceInterface(); |
| const VkDevice vkDevice = m_context.getDevice(); |
| const VkQueue queue = m_context.getUniversalQueue(); |
| |
| // Barriers for copying image to buffer |
| const VkImageMemoryBarrier imageBarrier = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask; |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout; |
| VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout newLayout; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| *m_source, // VkImage image; |
| { // VkImageSubresourceRange subresourceRange; |
| getAspectFlags(m_textureFormat), // VkImageAspectFlags aspectMask; |
| 0u, // deUint32 baseMipLevel; |
| 1u, // deUint32 mipLevels; |
| 0u, // deUint32 baseArraySlice; |
| 1u // deUint32 arraySize; |
| } |
| }; |
| |
| const VkBufferMemoryBarrier bufferBarrier = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_HOST_READ_BIT, // VkAccessFlags dstAccessMask; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| *m_destination, // VkBuffer buffer; |
| 0u, // VkDeviceSize offset; |
| m_bufferSize // VkDeviceSize size; |
| }; |
| |
| // Copy from image to buffer |
| std::vector<VkBufferImageCopy> bufferImageCopies; |
| for (deUint32 i = 0; i < m_params.regions.size(); i++) |
| bufferImageCopies.push_back(m_params.regions[i].bufferImageCopy); |
| |
| beginCommandBuffer(vk, *m_cmdBuffer); |
| vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &imageBarrier); |
| vk.cmdCopyImageToBuffer(*m_cmdBuffer, m_source.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_destination.get(), (deUint32)m_params.regions.size(), &bufferImageCopies[0]); |
| vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &bufferBarrier, 0, (const VkImageMemoryBarrier*)DE_NULL); |
| endCommandBuffer(vk, *m_cmdBuffer); |
| |
| submitCommandsAndWait (vk, vkDevice, queue, *m_cmdBuffer); |
| |
| // Read buffer data |
| de::MovePtr<tcu::TextureLevel> resultLevel (new tcu::TextureLevel(m_textureFormat, (int)m_params.dst.buffer.size, 1)); |
| invalidateAlloc(vk, vkDevice, *m_destinationBufferAlloc); |
| tcu::copy(*resultLevel, tcu::ConstPixelBufferAccess(resultLevel->getFormat(), resultLevel->getSize(), m_destinationBufferAlloc->getHostPtr())); |
| |
| return checkTestResult(resultLevel->getAccess()); |
| } |
| |
| class CopyImageToBufferTestCase : public vkt::TestCase |
| { |
| public: |
| CopyImageToBufferTestCase (tcu::TestContext& testCtx, |
| const std::string& name, |
| const std::string& description, |
| const TestParams params) |
| : vkt::TestCase (testCtx, name, description) |
| , m_params (params) |
| {} |
| |
| virtual TestInstance* createInstance (Context& context) const |
| { |
| return new CopyImageToBuffer(context, m_params); |
| } |
| private: |
| TestParams m_params; |
| }; |
| |
| void CopyImageToBuffer::copyRegionToTextureLevel (tcu::ConstPixelBufferAccess src, tcu::PixelBufferAccess dst, CopyRegion region, deUint32 mipLevel) |
| { |
| DE_UNREF(mipLevel); |
| |
| deUint32 rowLength = region.bufferImageCopy.bufferRowLength; |
| if (!rowLength) |
| rowLength = region.bufferImageCopy.imageExtent.width; |
| |
| deUint32 imageHeight = region.bufferImageCopy.bufferImageHeight; |
| if (!imageHeight) |
| imageHeight = region.bufferImageCopy.imageExtent.height; |
| |
| const int texelSize = src.getFormat().getPixelSize(); |
| const VkExtent3D extent = region.bufferImageCopy.imageExtent; |
| const VkOffset3D srcOffset = region.bufferImageCopy.imageOffset; |
| const int texelOffset = (int) region.bufferImageCopy.bufferOffset / texelSize; |
| |
| for (deUint32 z = 0; z < extent.depth; z++) |
| { |
| for (deUint32 y = 0; y < extent.height; y++) |
| { |
| int texelIndex = texelOffset + (z * imageHeight + y) * rowLength; |
| const tcu::ConstPixelBufferAccess srcSubRegion = tcu::getSubregion(src, srcOffset.x, srcOffset.y + y, srcOffset.z + z, |
| region.bufferImageCopy.imageExtent.width, 1, 1); |
| const tcu::PixelBufferAccess dstSubRegion = tcu::getSubregion(dst, texelIndex, 0, region.bufferImageCopy.imageExtent.width, 1); |
| tcu::copy(dstSubRegion, srcSubRegion); |
| } |
| } |
| } |
| |
| // Copy from buffer to image. |
| |
| class CopyBufferToImage : public CopiesAndBlittingTestInstance |
| { |
| public: |
| CopyBufferToImage (Context& context, |
| TestParams testParams); |
| virtual tcu::TestStatus iterate (void); |
| |
| private: |
| virtual void copyRegionToTextureLevel (tcu::ConstPixelBufferAccess src, tcu::PixelBufferAccess dst, CopyRegion region, deUint32 mipLevel = 0u); |
| |
| tcu::TextureFormat m_textureFormat; |
| VkDeviceSize m_bufferSize; |
| |
| Move<VkBuffer> m_source; |
| de::MovePtr<Allocation> m_sourceBufferAlloc; |
| Move<VkImage> m_destination; |
| de::MovePtr<Allocation> m_destinationImageAlloc; |
| }; |
| |
| CopyBufferToImage::CopyBufferToImage (Context& context, TestParams testParams) |
| : CopiesAndBlittingTestInstance(context, testParams) |
| , m_textureFormat(mapVkFormat(testParams.dst.image.format)) |
| , m_bufferSize(m_params.src.buffer.size * tcu::getPixelSize(m_textureFormat)) |
| { |
| const InstanceInterface& vki = context.getInstanceInterface(); |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const VkPhysicalDevice vkPhysDevice = context.getPhysicalDevice(); |
| const VkDevice vkDevice = context.getDevice(); |
| const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); |
| Allocator& memAlloc = context.getDefaultAllocator(); |
| |
| // Create source buffer |
| { |
| const VkBufferCreateInfo sourceBufferParams = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferCreateFlags flags; |
| m_bufferSize, // VkDeviceSize size; |
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT, // VkBufferUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyIndexCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| }; |
| |
| m_source = createBuffer(vk, vkDevice, &sourceBufferParams); |
| m_sourceBufferAlloc = allocateBuffer(vki, vk, vkPhysDevice, vkDevice, *m_source, MemoryRequirement::HostVisible, memAlloc, m_params.allocationKind); |
| VK_CHECK(vk.bindBufferMemory(vkDevice, *m_source, m_sourceBufferAlloc->getMemory(), m_sourceBufferAlloc->getOffset())); |
| } |
| |
| // Create destination image |
| { |
| const VkImageCreateInfo destinationImageParams = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| getCreateFlags(m_params.dst.image), // VkImageCreateFlags flags; |
| m_params.dst.image.imageType, // VkImageType imageType; |
| m_params.dst.image.format, // VkFormat format; |
| getExtent3D(m_params.dst.image), // VkExtent3D extent; |
| 1u, // deUint32 mipLevels; |
| getArraySize(m_params.dst.image), // deUint32 arraySize; |
| VK_SAMPLE_COUNT_1_BIT, // deUint32 samples; |
| VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; |
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT | |
| VK_IMAGE_USAGE_TRANSFER_DST_BIT, // VkImageUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| }; |
| |
| m_destination = createImage(vk, vkDevice, &destinationImageParams); |
| m_destinationImageAlloc = allocateImage(vki, vk, vkPhysDevice, vkDevice, *m_destination, MemoryRequirement::Any, memAlloc, m_params.allocationKind); |
| VK_CHECK(vk.bindImageMemory(vkDevice, *m_destination, m_destinationImageAlloc->getMemory(), m_destinationImageAlloc->getOffset())); |
| } |
| } |
| |
| tcu::TestStatus CopyBufferToImage::iterate (void) |
| { |
| m_sourceTextureLevel = de::MovePtr<tcu::TextureLevel>(new tcu::TextureLevel(m_textureFormat, (int)m_params.src.buffer.size, 1)); |
| generateBuffer(m_sourceTextureLevel->getAccess(), (int)m_params.src.buffer.size, 1, 1); |
| m_destinationTextureLevel = de::MovePtr<tcu::TextureLevel>(new tcu::TextureLevel(m_textureFormat, |
| m_params.dst.image.extent.width, |
| m_params.dst.image.extent.height, |
| m_params.dst.image.extent.depth)); |
| |
| generateBuffer(m_destinationTextureLevel->getAccess(), m_params.dst.image.extent.width, m_params.dst.image.extent.height, m_params.dst.image.extent.depth); |
| |
| generateExpectedResult(); |
| |
| uploadBuffer(m_sourceTextureLevel->getAccess(), *m_sourceBufferAlloc); |
| uploadImage(m_destinationTextureLevel->getAccess(), *m_destination, m_params.dst.image); |
| |
| const DeviceInterface& vk = m_context.getDeviceInterface(); |
| const VkDevice vkDevice = m_context.getDevice(); |
| const VkQueue queue = m_context.getUniversalQueue(); |
| |
| const VkImageMemoryBarrier imageBarrier = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask; |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout; |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| *m_destination, // VkImage image; |
| { // VkImageSubresourceRange subresourceRange; |
| getAspectFlags(m_textureFormat), // VkImageAspectFlags aspectMask; |
| 0u, // deUint32 baseMipLevel; |
| 1u, // deUint32 mipLevels; |
| 0u, // deUint32 baseArraySlice; |
| 1u // deUint32 arraySize; |
| } |
| }; |
| |
| // Copy from buffer to image |
| std::vector<VkBufferImageCopy> bufferImageCopies; |
| for (deUint32 i = 0; i < m_params.regions.size(); i++) |
| bufferImageCopies.push_back(m_params.regions[i].bufferImageCopy); |
| |
| beginCommandBuffer(vk, *m_cmdBuffer); |
| vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &imageBarrier); |
| vk.cmdCopyBufferToImage(*m_cmdBuffer, m_source.get(), m_destination.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (deUint32)m_params.regions.size(), bufferImageCopies.data()); |
| endCommandBuffer(vk, *m_cmdBuffer); |
| |
| submitCommandsAndWait (vk, vkDevice, queue, *m_cmdBuffer); |
| |
| de::MovePtr<tcu::TextureLevel> resultLevel = readImage(*m_destination, m_params.dst.image); |
| |
| return checkTestResult(resultLevel->getAccess()); |
| } |
| |
| class CopyBufferToImageTestCase : public vkt::TestCase |
| { |
| public: |
| CopyBufferToImageTestCase (tcu::TestContext& testCtx, |
| const std::string& name, |
| const std::string& description, |
| const TestParams params) |
| : vkt::TestCase (testCtx, name, description) |
| , m_params (params) |
| {} |
| |
| virtual ~CopyBufferToImageTestCase (void) {} |
| |
| virtual TestInstance* createInstance (Context& context) const |
| { |
| return new CopyBufferToImage(context, m_params); |
| } |
| private: |
| TestParams m_params; |
| }; |
| |
| void CopyBufferToImage::copyRegionToTextureLevel (tcu::ConstPixelBufferAccess src, tcu::PixelBufferAccess dst, CopyRegion region, deUint32 mipLevel) |
| { |
| DE_UNREF(mipLevel); |
| |
| deUint32 rowLength = region.bufferImageCopy.bufferRowLength; |
| if (!rowLength) |
| rowLength = region.bufferImageCopy.imageExtent.width; |
| |
| deUint32 imageHeight = region.bufferImageCopy.bufferImageHeight; |
| if (!imageHeight) |
| imageHeight = region.bufferImageCopy.imageExtent.height; |
| |
| const int texelSize = dst.getFormat().getPixelSize(); |
| const VkExtent3D extent = region.bufferImageCopy.imageExtent; |
| const VkOffset3D dstOffset = region.bufferImageCopy.imageOffset; |
| const int texelOffset = (int) region.bufferImageCopy.bufferOffset / texelSize; |
| |
| for (deUint32 z = 0; z < extent.depth; z++) |
| { |
| for (deUint32 y = 0; y < extent.height; y++) |
| { |
| int texelIndex = texelOffset + (z * imageHeight + y) * rowLength; |
| const tcu::ConstPixelBufferAccess srcSubRegion = tcu::getSubregion(src, texelIndex, 0, region.bufferImageCopy.imageExtent.width, 1); |
| const tcu::PixelBufferAccess dstSubRegion = tcu::getSubregion(dst, dstOffset.x, dstOffset.y + y, dstOffset.z + z, |
| region.bufferImageCopy.imageExtent.width, 1, 1); |
| tcu::copy(dstSubRegion, srcSubRegion); |
| } |
| } |
| } |
| |
| class CopyBufferToDepthStencil : public CopiesAndBlittingTestInstance |
| { |
| public: |
| CopyBufferToDepthStencil (Context& context, |
| TestParams testParams); |
| virtual tcu::TestStatus iterate (void); |
| private: |
| virtual void copyRegionToTextureLevel (tcu::ConstPixelBufferAccess src, tcu::PixelBufferAccess dst, CopyRegion region, deUint32 mipLevel = 0u); |
| |
| tcu::TextureFormat m_textureFormat; |
| VkDeviceSize m_bufferSize; |
| |
| Move<VkBuffer> m_source; |
| de::MovePtr<Allocation> m_sourceBufferAlloc; |
| Move<VkImage> m_destination; |
| de::MovePtr<Allocation> m_destinationImageAlloc; |
| }; |
| |
| void CopyBufferToDepthStencil::copyRegionToTextureLevel(tcu::ConstPixelBufferAccess src, tcu::PixelBufferAccess dst, CopyRegion region, deUint32 mipLevel) |
| { |
| DE_UNREF(mipLevel); |
| |
| deUint32 rowLength = region.bufferImageCopy.bufferRowLength; |
| if (!rowLength) |
| rowLength = region.bufferImageCopy.imageExtent.width; |
| |
| deUint32 imageHeight = region.bufferImageCopy.bufferImageHeight; |
| if (!imageHeight) |
| imageHeight = region.bufferImageCopy.imageExtent.height; |
| |
| const int texelSize = dst.getFormat().getPixelSize(); |
| const VkExtent3D extent = region.bufferImageCopy.imageExtent; |
| const VkOffset3D dstOffset = region.bufferImageCopy.imageOffset; |
| const int texelOffset = (int)region.bufferImageCopy.bufferOffset / texelSize; |
| |
| for (deUint32 z = 0; z < extent.depth; z++) |
| { |
| for (deUint32 y = 0; y < extent.height; y++) |
| { |
| int texelIndex = texelOffset + (z * imageHeight + y) * rowLength; |
| const tcu::ConstPixelBufferAccess srcSubRegion = tcu::getSubregion(src, texelIndex, 0, region.bufferImageCopy.imageExtent.width, 1); |
| const tcu::PixelBufferAccess dstSubRegion = tcu::getSubregion(dst, dstOffset.x, dstOffset.y + y, dstOffset.z + z, |
| region.bufferImageCopy.imageExtent.width, 1, 1); |
| |
| if (region.bufferImageCopy.imageSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) |
| { |
| tcu::copy(dstSubRegion, tcu::getEffectiveDepthStencilAccess(srcSubRegion, tcu::Sampler::MODE_DEPTH), DE_FALSE); |
| } |
| else |
| { |
| tcu::copy(dstSubRegion, tcu::getEffectiveDepthStencilAccess(srcSubRegion, tcu::Sampler::MODE_STENCIL), DE_FALSE); |
| } |
| } |
| } |
| } |
| |
| bool isSupportedDepthStencilFormat(const InstanceInterface& vki, const VkPhysicalDevice physDevice, const VkFormat format) |
| { |
| VkFormatProperties formatProps; |
| vki.getPhysicalDeviceFormatProperties(physDevice, format, &formatProps); |
| return (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0; |
| } |
| |
| CopyBufferToDepthStencil::CopyBufferToDepthStencil(Context& context, TestParams testParams) |
| : CopiesAndBlittingTestInstance(context, testParams) |
| , m_textureFormat(mapVkFormat(testParams.dst.image.format)) |
| , m_bufferSize(0) |
| { |
| const InstanceInterface& vki = context.getInstanceInterface(); |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const VkPhysicalDevice vkPhysDevice = context.getPhysicalDevice(); |
| const VkDevice vkDevice = context.getDevice(); |
| const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); |
| Allocator& memAlloc = context.getDefaultAllocator(); |
| const bool hasDepth = tcu::hasDepthComponent(mapVkFormat(m_params.dst.image.format).order); |
| const bool hasStencil = tcu::hasStencilComponent(mapVkFormat(m_params.dst.image.format).order); |
| |
| if (!isSupportedDepthStencilFormat(vki, vkPhysDevice, testParams.dst.image.format)) |
| { |
| TCU_THROW(NotSupportedError, "Image format not supported."); |
| } |
| |
| if (hasDepth) |
| { |
| glw::GLuint texelSize = m_textureFormat.getPixelSize(); |
| if (texelSize > sizeof(float)) |
| { |
| // We must have D32F_S8 format, depth must be packed so we only need |
| // to allocate space for the D32F part. Stencil will be separate |
| texelSize = sizeof(float); |
| } |
| m_bufferSize += static_cast<VkDeviceSize>(m_params.dst.image.extent.width) * static_cast<VkDeviceSize>(m_params.dst.image.extent.height) * static_cast<VkDeviceSize>(texelSize); |
| } |
| if (hasStencil) |
| { |
| // Stencil is always 8bits and packed. |
| m_bufferSize += static_cast<VkDeviceSize>(m_params.dst.image.extent.width) * static_cast<VkDeviceSize>(m_params.dst.image.extent.height); |
| } |
| |
| // Create source buffer, this is where the depth & stencil data will go that's used by test's regions. |
| { |
| const VkBufferCreateInfo sourceBufferParams = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 0u, // VkBufferCreateFlags flags; |
| m_bufferSize, // VkDeviceSize size; |
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT, // VkBufferUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyIndexCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| }; |
| |
| m_source = createBuffer(vk, vkDevice, &sourceBufferParams); |
| m_sourceBufferAlloc = allocateBuffer(vki, vk, vkPhysDevice, vkDevice, *m_source, MemoryRequirement::HostVisible, memAlloc, m_params.allocationKind); |
| VK_CHECK(vk.bindBufferMemory(vkDevice, *m_source, m_sourceBufferAlloc->getMemory(), m_sourceBufferAlloc->getOffset())); |
| } |
| |
| // Create destination image |
| { |
| const VkImageCreateInfo destinationImageParams = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| getCreateFlags(m_params.dst.image), // VkImageCreateFlags flags; |
| m_params.dst.image.imageType, // VkImageType imageType; |
| m_params.dst.image.format, // VkFormat format; |
| getExtent3D(m_params.dst.image), // VkExtent3D extent; |
| 1u, // deUint32 mipLevels; |
| getArraySize(m_params.dst.image), // deUint32 arraySize; |
| VK_SAMPLE_COUNT_1_BIT, // deUint32 samples; |
| VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; |
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT | |
| VK_IMAGE_USAGE_TRANSFER_DST_BIT, // VkImageUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| }; |
| |
| m_destination = createImage(vk, vkDevice, &destinationImageParams); |
| m_destinationImageAlloc = allocateImage(vki, vk, vkPhysDevice, vkDevice, *m_destination, MemoryRequirement::Any, memAlloc, m_params.allocationKind); |
| VK_CHECK(vk.bindImageMemory(vkDevice, *m_destination, m_destinationImageAlloc->getMemory(), m_destinationImageAlloc->getOffset())); |
| } |
| } |
| |
| tcu::TestStatus CopyBufferToDepthStencil::iterate(void) |
| { |
| // Create source depth/stencil content. Treat as 1D texture to get different pattern |
| m_sourceTextureLevel = de::MovePtr<tcu::TextureLevel>(new tcu::TextureLevel(m_textureFormat, (int)m_params.src.buffer.size, 1)); |
| // Fill buffer with linear gradiant |
| generateBuffer(m_sourceTextureLevel->getAccess(), (int)m_params.src.buffer.size, 1, 1); |
| |
| // Create image layer for depth/stencil |
| m_destinationTextureLevel = de::MovePtr<tcu::TextureLevel>(new tcu::TextureLevel(m_textureFormat, |
| m_params.dst.image.extent.width, |
| m_params.dst.image.extent.height, |
| m_params.dst.image.extent.depth)); |
| |
| // Fill image layer with 2D gradiant |
| generateBuffer(m_destinationTextureLevel->getAccess(), m_params.dst.image.extent.width, m_params.dst.image.extent.height, m_params.dst.image.extent.depth); |
| |
| // Fill m_extendedTextureLevel with copy of m_destinationTextureLevel |
| // Then iterate over each of the regions given in m_params.regions and copy m_sourceTextureLevel content to m_extendedTextureLevel |
| // This emulates what the HW will be doing. |
| generateExpectedResult(); |
| |
| // Upload our source depth/stencil content to the source buffer |
| // This is the buffer that will be used by region commands |
| std::vector<VkBufferImageCopy> bufferImageCopies; |
| VkDeviceSize bufferOffset = 0; |
| const VkDevice vkDevice = m_context.getDevice(); |
| const DeviceInterface& vk = m_context.getDeviceInterface(); |
| const VkQueue queue = m_context.getUniversalQueue(); |
| char* dstPtr = reinterpret_cast<char*>(m_sourceBufferAlloc->getHostPtr()); |
| bool depthLoaded = DE_FALSE; |
| bool stencilLoaded = DE_FALSE; |
| VkDeviceSize depthOffset = 0; |
| VkDeviceSize stencilOffset = 0; |
| |
| // To be able to test ordering depth & stencil differently |
| // we take the given copy regions and use that as the desired order |
| // and copy the appropriate data into place and compute the appropriate |
| // data offsets to be used in the copy command. |
| for (deUint32 i = 0; i < m_params.regions.size(); i++) |
| { |
| tcu::ConstPixelBufferAccess bufferAccess = m_sourceTextureLevel->getAccess(); |
| deUint32 bufferSize = bufferAccess.getWidth() * bufferAccess.getHeight() * bufferAccess.getDepth(); |
| VkBufferImageCopy copyData = m_params.regions[i].bufferImageCopy; |
| char* srcPtr; |
| |
| if (copyData.imageSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT && !depthLoaded) |
| { |
| // Create level that is same component as depth buffer (e.g. D16, D24, D32F) |
| tcu::TextureLevel depthTexture(mapCombinedToDepthTransferFormat(bufferAccess.getFormat()), bufferAccess.getWidth(), bufferAccess.getHeight(), bufferAccess.getDepth()); |
| bufferSize *= tcu::getPixelSize(depthTexture.getFormat()); |
| // Copy depth component only from source data. This gives us packed depth-only data. |
| tcu::copy(depthTexture.getAccess(), tcu::getEffectiveDepthStencilAccess(bufferAccess, tcu::Sampler::MODE_DEPTH)); |
| srcPtr = (char*)depthTexture.getAccess().getDataPtr(); |
| // Copy packed depth-only data to output buffer |
| deMemcpy(dstPtr, srcPtr, bufferSize); |
| depthLoaded = DE_TRUE; |
| depthOffset = bufferOffset; |
| dstPtr += bufferSize; |
| bufferOffset += bufferSize; |
| copyData.bufferOffset += depthOffset; |
| } |
| else if (!stencilLoaded) |
| { |
| // Create level that is same component as stencil buffer (always 8-bits) |
| tcu::TextureLevel stencilTexture(tcu::getEffectiveDepthStencilTextureFormat(bufferAccess.getFormat(), tcu::Sampler::MODE_STENCIL), bufferAccess.getWidth(), bufferAccess.getHeight(), bufferAccess.getDepth()); |
| // Copy stencil component only from source data. This gives us packed stencil-only data. |
| tcu::copy(stencilTexture.getAccess(), tcu::getEffectiveDepthStencilAccess(bufferAccess, tcu::Sampler::MODE_STENCIL)); |
| srcPtr = (char*)stencilTexture.getAccess().getDataPtr(); |
| // Copy packed stencil-only data to output buffer |
| deMemcpy(dstPtr, srcPtr, bufferSize); |
| stencilLoaded = DE_TRUE; |
| stencilOffset = bufferOffset; |
| dstPtr += bufferSize; |
| bufferOffset += bufferSize; |
| |
| // Reference image generation uses pixel offsets based on buffer offset. |
| // We need to adjust the offset now that the stencil data is not interleaved. |
| copyData.bufferOffset /= tcu::getPixelSize(m_textureFormat); |
| |
| copyData.bufferOffset += stencilOffset; |
| } |
| |
| bufferImageCopies.push_back(copyData); |
| } |
| |
| flushAlloc(vk, vkDevice, *m_sourceBufferAlloc); |
| |
| // Upload the depth/stencil data from m_destinationTextureLevel to initialize |
| // depth and stencil to known values. |
| // Uses uploadImageAspect so makes its own buffers for depth and stencil |
| // aspects (as needed) and copies them with independent vkCmdCopyBufferToImage commands. |
| uploadImage(m_destinationTextureLevel->getAccess(), *m_destination, m_params.dst.image); |
| |
| const VkImageMemoryBarrier imageBarrier = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask; |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout; |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| *m_destination, // VkImage image; |
| { // VkImageSubresourceRange subresourceRange; |
| getAspectFlags(m_textureFormat), // VkImageAspectFlags aspectMask; |
| 0u, // deUint32 baseMipLevel; |
| 1u, // deUint32 mipLevels; |
| 0u, // deUint32 baseArraySlice; |
| 1u // deUint32 arraySize; |
| } |
| }; |
| |
| // Copy from buffer to depth/stencil image |
| |
| beginCommandBuffer(vk, *m_cmdBuffer); |
| vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &imageBarrier); |
| |
| if (m_params.singleCommand) |
| { |
| // Issue a single copy command with regions defined by the test. |
| vk.cmdCopyBufferToImage(*m_cmdBuffer, m_source.get(), m_destination.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (deUint32)m_params.regions.size(), bufferImageCopies.data()); |
| } |
| else |
| { |
| // Issue a a copy command per region defined by the test. |
| for (deUint32 i = 0; i < bufferImageCopies.size(); i++) |
| { |
| vk.cmdCopyBufferToImage(*m_cmdBuffer, m_source.get(), m_destination.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferImageCopies[i]); |
| } |
| } |
| endCommandBuffer(vk, *m_cmdBuffer); |
| |
| submitCommandsAndWait(vk, vkDevice, queue, *m_cmdBuffer); |
| |
| de::MovePtr<tcu::TextureLevel> resultLevel = readImage(*m_destination, m_params.dst.image); |
| |
| // For combined depth/stencil formats both aspects are checked even when the test only |
| // copies one. Clear such aspects here for both the result and the reference. |
| if (tcu::hasDepthComponent(m_textureFormat.order) && !depthLoaded) |
| { |
| tcu::clearDepth(m_expectedTextureLevel[0]->getAccess(), 0.0f); |
| tcu::clearDepth(resultLevel->getAccess(), 0.0f); |
| } |
| if (tcu::hasStencilComponent(m_textureFormat.order) && !stencilLoaded) |
| { |
| tcu::clearStencil(m_expectedTextureLevel[0]->getAccess(), 0); |
| tcu::clearStencil(resultLevel->getAccess(), 0); |
| } |
| |
| return checkTestResult(resultLevel->getAccess()); |
| } |
| |
| class CopyBufferToDepthStencilTestCase : public vkt::TestCase |
| { |
| public: |
| CopyBufferToDepthStencilTestCase (tcu::TestContext& testCtx, |
| const std::string& name, |
| const std::string& description, |
| const TestParams params) |
| : vkt::TestCase(testCtx, name, description) |
| , m_params(params) |
| {} |
| |
| virtual ~CopyBufferToDepthStencilTestCase (void) {} |
| |
| virtual TestInstance* createInstance (Context& context) const |
| { |
| return new CopyBufferToDepthStencil(context, m_params); |
| } |
| private: |
| TestParams m_params; |
| }; |
| |
| // Copy from image to image with scaling. |
| |
| class BlittingImages : public CopiesAndBlittingTestInstance |
| { |
| public: |
| BlittingImages (Context& context, |
| TestParams params); |
| virtual tcu::TestStatus iterate (void); |
| protected: |
| virtual tcu::TestStatus checkTestResult (tcu::ConstPixelBufferAccess result); |
| virtual void copyRegionToTextureLevel (tcu::ConstPixelBufferAccess src, tcu::PixelBufferAccess dst, CopyRegion region, deUint32 mipLevel = 0u); |
| virtual void generateExpectedResult (void); |
| private: |
| bool checkNonNearestFilteredResult (const tcu::ConstPixelBufferAccess& result, |
| const tcu::ConstPixelBufferAccess& clampedReference, |
| const tcu::ConstPixelBufferAccess& unclampedReference, |
| const tcu::TextureFormat& sourceFormat); |
| bool checkNearestFilteredResult (const tcu::ConstPixelBufferAccess& result, |
| const tcu::ConstPixelBufferAccess& source); |
| |
| Move<VkImage> m_source; |
| de::MovePtr<Allocation> m_sourceImageAlloc; |
| Move<VkImage> m_destination; |
| de::MovePtr<Allocation> m_destinationImageAlloc; |
| |
| de::MovePtr<tcu::TextureLevel> m_unclampedExpectedTextureLevel; |
| }; |
| |
| BlittingImages::BlittingImages (Context& context, TestParams params) |
| : CopiesAndBlittingTestInstance(context, params) |
| { |
| const InstanceInterface& vki = context.getInstanceInterface(); |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const VkPhysicalDevice vkPhysDevice = context.getPhysicalDevice(); |
| const VkDevice vkDevice = context.getDevice(); |
| const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); |
| Allocator& memAlloc = context.getDefaultAllocator(); |
| |
| // Create source image |
| { |
| const VkImageCreateInfo sourceImageParams = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| getCreateFlags(m_params.src.image), // VkImageCreateFlags flags; |
| m_params.src.image.imageType, // VkImageType imageType; |
| m_params.src.image.format, // VkFormat format; |
| getExtent3D(m_params.src.image), // VkExtent3D extent; |
| 1u, // deUint32 mipLevels; |
| getArraySize(m_params.src.image), // deUint32 arraySize; |
| VK_SAMPLE_COUNT_1_BIT, // deUint32 samples; |
| m_params.src.image.tiling, // VkImageTiling tiling; |
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT | |
| VK_IMAGE_USAGE_TRANSFER_DST_BIT, // VkImageUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| }; |
| |
| m_source = createImage(vk, vkDevice, &sourceImageParams); |
| m_sourceImageAlloc = allocateImage(vki, vk, vkPhysDevice, vkDevice, *m_source, MemoryRequirement::Any, memAlloc, m_params.allocationKind); |
| VK_CHECK(vk.bindImageMemory(vkDevice, *m_source, m_sourceImageAlloc->getMemory(), m_sourceImageAlloc->getOffset())); |
| } |
| |
| // Create destination image |
| { |
| const VkImageCreateInfo destinationImageParams = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| getCreateFlags(m_params.dst.image), // VkImageCreateFlags flags; |
| m_params.dst.image.imageType, // VkImageType imageType; |
| m_params.dst.image.format, // VkFormat format; |
| getExtent3D(m_params.dst.image), // VkExtent3D extent; |
| 1u, // deUint32 mipLevels; |
| getArraySize(m_params.dst.image), // deUint32 arraySize; |
| VK_SAMPLE_COUNT_1_BIT, // deUint32 samples; |
| m_params.dst.image.tiling, // VkImageTiling tiling; |
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT | |
| VK_IMAGE_USAGE_TRANSFER_DST_BIT, // VkImageUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 1u, // deUint32 queueFamilyCount; |
| &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| }; |
| |
| m_destination = createImage(vk, vkDevice, &destinationImageParams); |
| m_destinationImageAlloc = allocateImage(vki, vk, vkPhysDevice, vkDevice, *m_destination, MemoryRequirement::Any, memAlloc, m_params.allocationKind); |
| VK_CHECK(vk.bindImageMemory(vkDevice, *m_destination, m_destinationImageAlloc->getMemory(), m_destinationImageAlloc->getOffset())); |
| } |
| } |
| |
| tcu::TestStatus BlittingImages::iterate (void) |
| { |
| const tcu::TextureFormat srcTcuFormat = mapVkFormat(m_params.src.image.format); |
| const tcu::TextureFormat dstTcuFormat = mapVkFormat(m_params.dst.image.format); |
| m_sourceTextureLevel = de::MovePtr<tcu::TextureLevel>(new tcu::TextureLevel(srcTcuFormat, |
| m_params.src.image.extent.width, |
| m_params.src.image.extent.height, |
| m_params.src.image.extent.depth)); |
| generateBuffer(m_sourceTextureLevel->getAccess(), m_params.src.image.extent.width, m_params.src.image.extent.height, m_params.src.image.extent.depth, FILL_MODE_GRADIENT); |
| m_destinationTextureLevel = de::MovePtr<tcu::TextureLevel>(new tcu::TextureLevel(dstTcuFormat, |
| (int)m_params.dst.image.extent.width, |
| (int)m_params.dst.image.extent.height, |
| (int)m_params.dst.image.extent.depth)); |
| generateBuffer(m_destinationTextureLevel->getAccess(), m_params.dst.image.extent.width, m_params.dst.image.extent.height, m_params.dst.image.extent.depth, FILL_MODE_WHITE); |
| generateExpectedResult(); |
| |
| uploadImage(m_sourceTextureLevel->getAccess(), m_source.get(), m_params.src.image); |
| uploadImage(m_destinationTextureLevel->getAccess(), m_destination.get(), m_params.dst.image); |
| |
| const DeviceInterface& vk = m_context.getDeviceInterface(); |
| const VkDevice vkDevice = m_context.getDevice(); |
| const VkQueue queue = m_context.getUniversalQueue(); |
| |
| std::vector<VkImageBlit> regions; |
| for (deUint32 i = 0; i < m_params.regions.size(); i++) |
| regions.push_back(m_params.regions[i].imageBlit); |
| |
| // Barriers for copying image to buffer |
| const VkImageMemoryBarrier srcImageBarrier = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask; |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout; |
| m_params.src.image.operationLayout, // VkImageLayout newLayout; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| m_source.get(), // VkImage image; |
| { // VkImageSubresourceRange subresourceRange; |
| getAspectFlags(srcTcuFormat), // VkImageAspectFlags aspectMask; |
| 0u, // deUint32 baseMipLevel; |
| 1u, // deUint32 mipLevels; |
| 0u, // deUint32 baseArraySlice; |
| 1u // deUint32 arraySize; |
| } |
| }; |
| |
| const VkImageMemoryBarrier dstImageBarrier = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask; |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout; |
| m_params.dst.image.operationLayout, // VkImageLayout newLayout; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex; |
| m_destination.get(), // VkImage image; |
| { // VkImageSubresourceRange subresourceRange; |
| getAspectFlags(dstTcuFormat), // VkImageAspectFlags aspectMask; |
| 0u, // deUint32 baseMipLevel; |
| 1u, // deUint32 mipLevels; |
| 0u, // deUint32 baseArraySlice; |
| 1u // deUint32 arraySize; |
| } |
| }; |
| |
| beginCommandBuffer(vk, *m_cmdBuffer); |
| vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &srcImageBarrier); |
| vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &dstImageBarrier); |
| vk.cmdBlitImage(*m_cmdBuffer, m_source.get(), m_params.src.image.operationLayout, m_destination.get(), m_params.dst.image.operationLayout, (deUint32)m_params.regions.size(), ®ions[0], m_params.filter); |
| endCommandBuffer(vk, *m_cmdBuffer); |
| submitCommandsAndWait(vk, vkDevice, queue, *m_cmdBuffer); |
| |
| de::MovePtr<tcu::TextureLevel> resultTextureLevel = readImage(*m_destination, m_params.dst.image); |
| |
| return checkTestResult(resultTextureLevel->getAccess()); |
| } |
| |
| static float calculateFloatConversionError (int srcBits) |
| { |
| if (srcBits > 0) |
| { |
| const int clampedBits = de::clamp<int>(srcBits, 0, 32); |
| const float srcMaxValue = de::max((float)(1ULL<<clampedBits) - 1.0f, 1.0f); |
| const float error = 1.0f / srcMaxValue; |
| |
| return de::clamp<float>(error, 0.0f, 1.0f); |
| } |
| else |
| return 1.0f; |
| } |
| |
| tcu::Vec4 getFormatThreshold (const tcu::TextureFormat& format) |
| { |
| tcu::Vec4 threshold(0.01f); |
| |
| switch (format.type) |
| { |
| case tcu::TextureFormat::HALF_FLOAT: |
| threshold = tcu::Vec4(0.005f); |
| break; |
| |
| case tcu::TextureFormat::FLOAT: |
| case tcu::TextureFormat::FLOAT64: |
| threshold = tcu::Vec4(0.001f); |
| break; |
| |
| case tcu::TextureFormat::UNSIGNED_INT_11F_11F_10F_REV: |
| threshold = tcu::Vec4(0.02f, 0.02f, 0.0625f, 1.0f); |
| break; |
| |
| case tcu::TextureFormat::UNSIGNED_INT_999_E5_REV: |
| threshold = tcu::Vec4(0.05f, 0.05f, 0.05f, 1.0f); |
| break; |
| |
| case tcu::TextureFormat::UNORM_INT_1010102_REV: |
| threshold = tcu::Vec4(0.002f, 0.002f, 0.002f, 0.3f); |
| break; |
| |
| case tcu:: TextureFormat::UNORM_INT8: |
| threshold = tcu::Vec4(0.008f, 0.008f, 0.008f, 0.008f); |
| break; |
| |
| default: |
| const tcu::IVec4 bits = tcu::getTextureFormatMantissaBitDepth(format); |
| threshold = tcu::Vec4(calculateFloatConversionError(bits.x()), |
| calculateFloatConversionError(bits.y()), |
| calculateFloatConversionError(bits.z()), |
| calculateFloatConversionError(bits.w())); |
| } |
| |
| // Return value matching the channel order specified by the format |
| if (format.order == tcu::TextureFormat::BGR || format.order == tcu::TextureFormat::BGRA) |
| return threshold.swizzle(2, 1, 0, 3); |
| else |
| return threshold; |
| } |
| |
| bool BlittingImages::checkNonNearestFilteredResult (const tcu::ConstPixelBufferAccess& result, |
| const tcu::ConstPixelBufferAccess& clampedExpected, |
| const tcu::ConstPixelBufferAccess& unclampedExpected, |
| const tcu::TextureFormat& srcFormat) |
| { |
| tcu::TestLog& log (m_context.getTestContext().getLog()); |
| const tcu::TextureFormat dstFormat = result.getFormat(); |
| const tcu::TextureChannelClass dstChannelClass = tcu::getTextureChannelClass(dstFormat.type); |
| const tcu::TextureChannelClass srcChannelClass = tcu::getTextureChannelClass(srcFormat.type); |
| bool isOk = false; |
| |
| log << tcu::TestLog::Section("ClampedSourceImage", "Region with clamped edges on source image."); |
| |
| // if either of srcImage or dstImage stores values as a signed/unsigned integer, |
| // the other must also store values a signed/unsigned integer |
| // e.g. blit unorm to uscaled is not allowed as uscaled formats store data as integers |
| // despite the fact that both formats are sampled as floats |
| bool dstImageIsIntClass = dstChannelClass == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER || |
| dstChannelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER; |
| bool srcImageIsIntClass = srcChannelClass == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER || |
| srcChannelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER; |
| if (dstImageIsIntClass != srcImageIsIntClass) |
| { |
| log << tcu::TestLog::EndSection; |
| return false; |
| } |
| |
| if (isFloatFormat(dstFormat)) |
| { |
| const bool srcIsSRGB = tcu::isSRGB(srcFormat); |
| const tcu::Vec4 srcMaxDiff = getFormatThreshold(srcFormat) * tcu::Vec4(srcIsSRGB ? 2.0f : 1.0f); |
| const tcu::Vec4 dstMaxDiff = getFormatThreshold(dstFormat); |
| const tcu::Vec4 threshold = ( srcMaxDiff + dstMaxDiff ) * ((m_params.filter == VK_FILTER_CUBIC_EXT) ? 1.5f : 1.0f); |
| |
| isOk = tcu::floatThresholdCompare(log, "Compare", "Result comparsion", clampedExpected, result, threshold, tcu::COMPARE_LOG_RESULT); |
| log << tcu::TestLog::EndSection; |
| |
| if (!isOk) |
| { |
| log << tcu::TestLog::Section("NonClampedSourceImage", "Region with non-clamped edges on source image."); |
| isOk = tcu::floatThresholdCompare(log, "Compare", "Result comparsion", unclampedExpected, result, threshold, tcu::COMPARE_LOG_RESULT); |
| log << tcu::TestLog::EndSection; |
| } |
| } |
| else |
| { |
| tcu::UVec4 threshold; |
| // Calculate threshold depending on channel width of destination format. |
| const tcu::IVec4 dstBitDepth = tcu::getTextureFormatBitDepth(dstFormat); |
| const tcu::IVec4 srcBitDepth = tcu::getTextureFormatBitDepth(srcFormat); |
| for (deUint32 i = 0; i < 4; ++i) |
| threshold[i] = 1 + de::max( ( ( 1 << dstBitDepth[i] ) - 1 ) / de::clamp((1 << srcBitDepth[i]) - 1, 1, 256), 1); |
| |
| isOk = tcu::intThresholdCompare(log, "Compare", "Result comparsion", clampedExpected, result, threshold, tcu::COMPARE_LOG_RESULT); |
| log << tcu::TestLog::EndSection; |
| |
| if (!isOk) |
| { |
| log << tcu::TestLog::Section("NonClampedSourceImage", "Region with non-clamped edges on source image."); |
| isOk = tcu::intThresholdCompare(log, "Compare", "Result comparsion", unclampedExpected, result, threshold, tcu::COMPARE_LOG_RESULT); |
| log << tcu::TestLog::EndSection; |
| } |
| } |
| |
| return isOk; |
| } |
| |
| //! Utility to encapsulate coordinate computation and loops. |
| struct CompareEachPixelInEachRegion |
| { |
| virtual ~CompareEachPixelInEachRegion (void) {} |
| virtual bool compare (const void* pUserData, const int x, const int y, const int z, const tcu::Vec3& srcNormCoord) const = 0; |
| |
| bool forEach (const void* pUserData, |
| const std::vector<CopyRegion>& regions, |
| const int sourceWidth, |
| const int sourceHeight, |
| const int sourceDepth, |
| const tcu::PixelBufferAccess& errorMask) const |
| { |
| bool compareOk = true; |
| |
| for (std::vector<CopyRegion>::const_iterator regionIter = regions.begin(); regionIter != regions.end(); ++regionIter) |
| { |
| const VkImageBlit& blit = regionIter->imageBlit; |
| |
| const int xStart = deMin32(blit.dstOffsets[0].x, blit.dstOffsets[1].x); |
| const int yStart = deMin32(blit.dstOffsets[0].y, blit.dstOffsets[1].y); |
| const int zStart = deMin32(blit.dstOffsets[0].z, blit.dstOffsets[1].z); |
| const int xEnd = deMax32(blit.dstOffsets[0].x, blit.dstOffsets[1].x); |
| const int yEnd = deMax32(blit.dstOffsets[0].y, blit.dstOffsets[1].y); |
| const int zEnd = deMax32(blit.dstOffsets[0].z, blit.dstOffsets[1].z); |
| const float xScale = static_cast<float>(blit.srcOffsets[1].x - blit.srcOffsets[0].x) / static_cast<float>(blit.dstOffsets[1].x - blit.dstOffsets[0].x); |
| const float yScale = static_cast<float>(blit.srcOffsets[1].y - blit.srcOffsets[0].y) / static_cast<float>(blit.dstOffsets[1].y - blit.dstOffsets[0].y); |
| const float zScale = static_cast<float>(blit.srcOffsets[1].z - blit.srcOffsets[0].z) / static_cast<float>(blit.dstOffsets[1].z - blit.dstOffsets[0].z); |
| const float srcInvW = 1.0f / static_cast<float>(sourceWidth); |
| const float srcInvH = 1.0f / static_cast<float>(sourceHeight); |
| const float srcInvD = 1.0f / static_cast<float>(sourceDepth); |
| |
| for (int z = zStart; z < zEnd; z++) |
| for (int y = yStart; y < yEnd; y++) |
| for (int x = xStart; x < xEnd; x++) |
| { |
| const tcu::Vec3 srcNormCoord |
| ( |
| (xScale * (static_cast<float>(x - blit.dstOffsets[0].x) + 0.5f) + static_cast<float>(blit.srcOffsets[0].x)) * srcInvW, |
| (yScale * (static_cast<float>(y - blit.dstOffsets[0].y) + 0.5f) + static_cast<float>(blit.srcOffsets[0].y)) * srcInvH, |
| (zScale * (static_cast<float>(z - blit.dstOffsets[0].z) + 0.5f) + static_cast<float>(blit.srcOffsets[0].z)) * srcInvD |
| ); |
| |
| if (!compare(pUserData, x, y, z, srcNormCoord)) |
| { |
| errorMask.setPixel(tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y, z); |
| compareOk = false; |
| } |
| } |
| } |
| return compareOk; |
| } |
| }; |
| |
| tcu::Vec4 getFloatOrFixedPointFormatThreshold (const tcu::TextureFormat& format) |
| { |
| const tcu::TextureChannelClass channelClass = tcu::getTextureChannelClass(format.type); |
| const tcu::IVec4 bitDepth = tcu::getTextureFormatBitDepth(format); |
| |
| if (channelClass == tcu::TEXTURECHANNELCLASS_FLOATING_POINT) |
| { |
| return getFormatThreshold(format); |
| } |
| else if (channelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT || |
| channelClass == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT) |
| { |
| const bool isSigned = (channelClass == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT); |
| const float range = isSigned ? 1.0f - (-1.0f) |
| : 1.0f - 0.0f; |
| |
| tcu::Vec4 v; |
| for (int i = 0; i < 4; ++i) |
| { |
| if (bitDepth[i] == 0) |
| v[i] = 1.0f; |
| else |
| v[i] = range / static_cast<float>((1 << bitDepth[i]) - 1); |
| } |
| return v; |
| } |
| else |
| { |
| DE_ASSERT(0); |
| return tcu::Vec4(); |
| } |
| } |
| |
| bool floatNearestBlitCompare (const tcu::ConstPixelBufferAccess& source, |
| const tcu::ConstPixelBufferAccess& result, |
| const tcu::PixelBufferAccess& errorMask, |
| const std::vector<CopyRegion>& regions) |
| { |
| const tcu::Sampler sampler (tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::NEAREST, tcu::Sampler::NEAREST); |
| tcu::LookupPrecision precision; |
| |
| { |
| const tcu::IVec4 dstBitDepth = tcu::getTextureFormatBitDepth(result.getFormat()); |
| const tcu::Vec4 srcMaxDiff = getFloatOrFixedPointFormatThreshold(source.getFormat()); |
| const tcu::Vec4 dstMaxDiff = getFloatOrFixedPointFormatThreshold(result.getFormat()); |
| |
| precision.colorMask = tcu::notEqual(dstBitDepth, tcu::IVec4(0)); |
| precision.colorThreshold = tcu::max(srcMaxDiff, dstMaxDiff); |
| } |
| |
| const struct Capture |
| { |
| const tcu::ConstPixelBufferAccess& source; |
| const tcu::ConstPixelBufferAccess& result; |
| const tcu::Sampler& sampler; |
| const tcu::LookupPrecision& precision; |
| const bool isSRGB; |
| } capture = |
| { |
| source, result, sampler, precision, tcu::isSRGB(result.getFormat()) |
| }; |
| |
| const struct Loop : CompareEachPixelInEachRegion |
| { |
| Loop (void) {} |
| |
| bool compare (const void* pUserData, const int x, const int y, const int z, const tcu::Vec3& srcNormCoord) const |
| { |
| const Capture& c = *static_cast<const Capture*>(pUserData); |
| const tcu::TexLookupScaleMode lookupScaleDontCare = tcu::TEX_LOOKUP_SCALE_MINIFY; |
| tcu::Vec4 dstColor = c.result.getPixel(x, y, z); |
| |
| // TexLookupVerifier performs a conversion to linear space, so we have to as well |
| if (c.isSRGB) |
| dstColor = tcu::sRGBToLinear(dstColor); |
| |
| return tcu::isLevel3DLookupResultValid(c.source, c.sampler, lookupScaleDontCare, c.precision, srcNormCoord, dstColor); |
| } |
| } loop; |
| |
| return loop.forEach(&capture, regions, source.getWidth(), source.getHeight(), source.getDepth(), errorMask); |
| } |
| |
| bool intNearestBlitCompare (const tcu::ConstPixelBufferAccess& source, |
| const tcu::ConstPixelBufferAccess& result, |
| const tcu::PixelBufferAccess& errorMask, |
| const std::vector<CopyRegion>& regions) |
| { |
| const tcu::Sampler sampler (tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::NEAREST, tcu::Sampler::NEAREST); |
| tcu::IntLookupPrecision precision; |
| |
| { |
| const tcu::IVec4 srcBitDepth = tcu::getTextureFormatBitDepth(source.getFormat()); |
| const tcu::IVec4 dstBitDepth = tcu::getTextureFormatBitDepth(result. |