| /*------------------------------------------------------------------------ |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2020 The Khronos Group Inc. |
| * Copyright (c) 2020 Valve Corporation. |
| * |
| * 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 vktImageSubresourceLayoutTests.cpp |
| * \brief Tests for vkGetImageSubresourceLayout |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vktTestCase.hpp" |
| |
| #include "vkDefs.hpp" |
| #include "vkImageUtil.hpp" |
| #include "vkQueryUtil.hpp" |
| #include "vkObjUtil.hpp" |
| #include "vkBarrierUtil.hpp" |
| #include "vkTypeUtil.hpp" |
| #include "vkCmdUtil.hpp" |
| #include "vkStrUtil.hpp" |
| #include "vkBufferWithMemory.hpp" |
| #include "vkImageWithMemory.hpp" |
| |
| #include "tcuTestLog.hpp" |
| #include "tcuTextureUtil.hpp" |
| #include "tcuFloat.hpp" |
| |
| #include "deRandom.hpp" |
| |
| #include <vector> |
| #include <sstream> |
| #include <limits> |
| #include <string> |
| |
| using namespace vk; |
| |
| namespace vkt |
| { |
| namespace image |
| { |
| namespace |
| { |
| |
| // Helper class to calculate buffer sizes and offsets for image mipmap levels. |
| class BufferLevels |
| { |
| public: |
| struct Level |
| { |
| VkDeviceSize offset; // In bytes. |
| VkDeviceSize size; // In bytes. |
| VkExtent3D dimensions; // .depth will be the number of layers for 2D images and the depth for 3D images. |
| }; |
| |
| BufferLevels (VkImageType type, VkFormat format, VkExtent3D levelZero, deUint32 maxLevels, VkImageAspectFlags aspects = 0u); |
| VkDeviceSize totalSize () const; |
| VkDeviceSize pixelSize () const; |
| deUint32 numLevels () const; |
| const Level& getLevel (deUint32 level) const; |
| |
| private: |
| VkDeviceSize m_pixelSize; // In bytes. |
| std::vector<Level> m_levels; |
| }; |
| |
| BufferLevels::BufferLevels (VkImageType type, VkFormat format, VkExtent3D levelZero, deUint32 maxLevels, VkImageAspectFlags aspects) |
| { |
| DE_ASSERT(type == VK_IMAGE_TYPE_2D || type == VK_IMAGE_TYPE_3D); |
| DE_ASSERT(maxLevels >= 1u); |
| |
| const auto tcuFormat = vk::mapVkFormat(format); |
| const auto maxLevelsSz = static_cast<size_t>(maxLevels); |
| |
| VkDeviceSize currentOffset = 0ull; |
| VkExtent3D nextExtent = levelZero; |
| deUint32 levelCount = 0; |
| |
| if (!aspects || (aspects & VK_IMAGE_ASPECT_COLOR_BIT)) |
| { |
| m_pixelSize = static_cast<VkDeviceSize>(tcu::getPixelSize(tcuFormat)); |
| } |
| else if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT) |
| { |
| const auto copyFormat = getDepthCopyFormat(format); |
| m_pixelSize = static_cast<VkDeviceSize>(tcu::getPixelSize(copyFormat)); |
| } |
| else if (aspects & VK_IMAGE_ASPECT_STENCIL_BIT) |
| { |
| const auto copyFormat = getStencilCopyFormat(format); |
| m_pixelSize = static_cast<VkDeviceSize>(tcu::getPixelSize(copyFormat)); |
| } |
| else |
| DE_ASSERT(false); |
| |
| while (m_levels.size() < maxLevelsSz) |
| { |
| Level level; |
| |
| level.offset = currentOffset; |
| level.size = m_pixelSize * nextExtent.width * nextExtent.height * nextExtent.depth; |
| level.dimensions = nextExtent; |
| |
| m_levels.push_back(level); |
| |
| // This was the last available level. |
| if (nextExtent.width == 1u && nextExtent.height == 1u && (type == VK_IMAGE_TYPE_2D || nextExtent.depth == 1u)) |
| break; |
| |
| nextExtent.width = std::max(1u, (nextExtent.width / 2u)); |
| nextExtent.height = std::max(1u, (nextExtent.height / 2u)); |
| |
| // 2D arrays all have the same array size. |
| if (type == VK_IMAGE_TYPE_3D) |
| nextExtent.depth = std::max(1u, (nextExtent.depth / 2u)); |
| |
| currentOffset += level.size; |
| ++levelCount; |
| } |
| } |
| |
| VkDeviceSize BufferLevels::totalSize () const |
| { |
| VkDeviceSize total = 0ull; |
| std::for_each(begin(m_levels), end(m_levels), [&total] (const Level& l) { total += l.size; }); |
| return total; |
| } |
| |
| VkDeviceSize BufferLevels::pixelSize () const |
| { |
| return m_pixelSize; |
| } |
| |
| deUint32 BufferLevels::numLevels () const |
| { |
| return static_cast<deUint32>(m_levels.size()); |
| } |
| |
| const BufferLevels::Level& BufferLevels::getLevel (deUint32 level) const |
| { |
| return m_levels.at(level); |
| } |
| |
| // Default image dimensions. For 2D images, .depth indicates the number of layers. |
| VkExtent3D getDefaultDimensions (VkImageType type, bool array) |
| { |
| DE_ASSERT(type == VK_IMAGE_TYPE_2D || type == VK_IMAGE_TYPE_3D); |
| DE_ASSERT(!array || type == VK_IMAGE_TYPE_2D); |
| |
| constexpr VkExtent3D kDefault3D = { 32u, 48u, 56u }; |
| constexpr VkExtent3D kDefault2DArray = kDefault3D; |
| constexpr VkExtent3D kDefault2D = { 240u, 320u, 1u }; |
| |
| if (type == VK_IMAGE_TYPE_3D) |
| return kDefault3D; |
| if (array) |
| return kDefault2DArray; |
| return kDefault2D; |
| } |
| |
| class ImageSubresourceLayoutCase : public vkt::TestCase |
| { |
| public: |
| struct TestParams |
| { |
| VkImageType imageType; |
| VkFormat imageFormat; |
| VkExtent3D dimensions; // .depth will be the number of layers for 2D images and the depth for 3D images. |
| deUint32 mipLevels; |
| }; |
| |
| ImageSubresourceLayoutCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params); |
| virtual ~ImageSubresourceLayoutCase (void) {} |
| |
| virtual void initPrograms (vk::SourceCollections&) const {} |
| virtual TestInstance* createInstance (Context& context) const; |
| virtual void checkSupport (Context& context) const; |
| |
| static constexpr VkFormatFeatureFlags kRequiredFeatures = (VK_FORMAT_FEATURE_TRANSFER_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT); |
| static constexpr VkImageUsageFlags kImageUsageFlags = (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT); |
| static constexpr VkImageTiling kImageTiling = VK_IMAGE_TILING_LINEAR; |
| private: |
| TestParams m_params; |
| }; |
| |
| class ImageSubresourceLayoutInstance : public vkt::TestInstance |
| { |
| public: |
| ImageSubresourceLayoutInstance (Context& context, const ImageSubresourceLayoutCase::TestParams& params); |
| virtual ~ImageSubresourceLayoutInstance (void) {} |
| |
| virtual tcu::TestStatus iterate (void); |
| tcu::TestStatus iterateAspect (VkImageAspectFlagBits aspect); |
| private: |
| ImageSubresourceLayoutCase::TestParams m_params; |
| }; |
| |
| ImageSubresourceLayoutCase::ImageSubresourceLayoutCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params) |
| : vkt::TestCase (testCtx, name, description) |
| , m_params (params) |
| { |
| } |
| |
| TestInstance* ImageSubresourceLayoutCase::createInstance (Context& context) const |
| { |
| return new ImageSubresourceLayoutInstance (context, m_params); |
| } |
| |
| void ImageSubresourceLayoutCase::checkSupport (Context& context) const |
| { |
| const auto& vki = context.getInstanceInterface(); |
| const auto physicalDevice = context.getPhysicalDevice(); |
| |
| const auto formatProperties = getPhysicalDeviceFormatProperties(vki, physicalDevice, m_params.imageFormat); |
| if ((formatProperties.linearTilingFeatures & kRequiredFeatures) != kRequiredFeatures) |
| TCU_THROW(NotSupportedError, "Required format features not supported"); |
| |
| VkImageFormatProperties imgFormatProperties; |
| const auto result = vki.getPhysicalDeviceImageFormatProperties(physicalDevice, m_params.imageFormat, m_params.imageType, kImageTiling, kImageUsageFlags, 0u, &imgFormatProperties); |
| if (result == VK_ERROR_FORMAT_NOT_SUPPORTED) |
| TCU_THROW(NotSupportedError, "Linear tiling not supported for format"); |
| VK_CHECK(result); |
| |
| { |
| BufferLevels levels (m_params.imageType, m_params.imageFormat, m_params.dimensions, m_params.mipLevels); |
| if (imgFormatProperties.maxMipLevels < levels.numLevels()) |
| TCU_THROW(NotSupportedError, "Required number of mip levels not supported for format"); |
| } |
| |
| if (m_params.imageType == VK_IMAGE_TYPE_2D && imgFormatProperties.maxArrayLayers < m_params.dimensions.depth) |
| TCU_THROW(NotSupportedError, "Required number of layers not supported for format"); |
| } |
| |
| ImageSubresourceLayoutInstance::ImageSubresourceLayoutInstance (Context& context, const ImageSubresourceLayoutCase::TestParams& params) |
| : vkt::TestInstance (context) |
| , m_params (params) |
| { |
| } |
| |
| // Fills length bytes starting at location with pseudorandom data. |
| void fillWithRandomData (de::Random& rnd, void* location, VkDeviceSize length) |
| { |
| auto bytePtr = reinterpret_cast<unsigned char*>(location); |
| const auto endPtr = bytePtr + length; |
| |
| while (bytePtr != endPtr) |
| { |
| const auto remaining = (endPtr - bytePtr); |
| |
| if (remaining >= 8) { const auto data = rnd.getUint64(); deMemcpy(bytePtr, &data, sizeof(data)); bytePtr += sizeof(data); } |
| else if (remaining >= 4) { const auto data = rnd.getUint32(); deMemcpy(bytePtr, &data, sizeof(data)); bytePtr += sizeof(data); } |
| else if (remaining >= 2) { const auto data = rnd.getUint16(); deMemcpy(bytePtr, &data, sizeof(data)); bytePtr += sizeof(data); } |
| else { const auto data = rnd.getUint8(); deMemcpy(bytePtr, &data, sizeof(data)); bytePtr += sizeof(data); } |
| } |
| } |
| |
| // Fills data in blocks of 32 bits, discarding the higher 8 bits of each block. |
| void fillWithRandomData24In32 (de::Random& rnd, void* location, VkDeviceSize length) |
| { |
| static const auto blockSize = sizeof(deUint32); |
| DE_ASSERT(length % blockSize == 0); |
| |
| auto dataPtr = reinterpret_cast<unsigned char*>(location); |
| const auto numBlocks = length / blockSize; |
| |
| for (VkDeviceSize i = 0; i < numBlocks; ++i) |
| { |
| auto data = rnd.getUint32(); |
| data &= 0xFFFFFFu; // Remove the higher 8 bits. |
| deMemcpy(dataPtr, &data, blockSize); |
| dataPtr += blockSize; |
| } |
| } |
| |
| // Helpers to make fillWithRandomFloatingPoint a template. Returns normal numbers in the range [0, 1). |
| template <class T> |
| T getNormalFPValue (de::Random& rnd); |
| |
| template<> |
| float getNormalFPValue<float> (de::Random& rnd) |
| { |
| float value; |
| do { |
| value = rnd.getFloat(); |
| } while (tcu::Float32(value).isDenorm()); |
| return value; |
| } |
| |
| template<> |
| double getNormalFPValue<double> (de::Random& rnd) |
| { |
| double value; |
| do { |
| value = rnd.getDouble(); |
| } while (tcu::Float64(value).isDenorm()); |
| return value; |
| } |
| |
| template<> |
| tcu::Float16 getNormalFPValue<tcu::Float16> (de::Random& rnd) |
| { |
| tcu::Float16 value; |
| do { |
| value = tcu::Float16(rnd.getFloat()); |
| } while (value.isDenorm()); |
| return value; |
| } |
| |
| template <class T> |
| void fillWithRandomFloatingPoint (de::Random& rnd, void* location, VkDeviceSize length) |
| { |
| static const auto typeSize = sizeof(T); |
| |
| DE_ASSERT(length % typeSize == 0); |
| |
| const auto numElements = length / typeSize; |
| auto elemPtr = reinterpret_cast<unsigned char*>(location); |
| T elem; |
| |
| for (VkDeviceSize i = 0; i < numElements; ++i) |
| { |
| elem = getNormalFPValue<T>(rnd); |
| deMemcpy(elemPtr, &elem, typeSize); |
| elemPtr += typeSize; |
| } |
| } |
| |
| tcu::TestStatus ImageSubresourceLayoutInstance::iterate (void) |
| { |
| // Test every aspect supported by the image format. |
| const auto tcuFormat = mapVkFormat(m_params.imageFormat); |
| const auto aspectFlags = getImageAspectFlags(tcuFormat); |
| |
| static const VkImageAspectFlagBits aspectBits[] = |
| { |
| VK_IMAGE_ASPECT_COLOR_BIT, |
| VK_IMAGE_ASPECT_DEPTH_BIT, |
| VK_IMAGE_ASPECT_STENCIL_BIT, |
| }; |
| |
| for (int i = 0; i < DE_LENGTH_OF_ARRAY(aspectBits); ++i) |
| { |
| const auto bit = aspectBits[i]; |
| if (aspectFlags & bit) |
| { |
| const auto aspectResult = iterateAspect(bit); |
| if (aspectResult.getCode() != QP_TEST_RESULT_PASS) |
| return aspectResult; // Early return for failures. |
| } |
| } |
| |
| return tcu::TestStatus::pass("Pass"); |
| } |
| |
| tcu::TestStatus ImageSubresourceLayoutInstance::iterateAspect (VkImageAspectFlagBits imageAspect) |
| { |
| // * Create linear image with several mipmaps |
| // * Fill its levels with unique appropriate data (avoiding invalid sfloat values, for example). |
| // * Ask for the subresource layout parameters. |
| // * Verify they make sense. |
| // * Check accessing data with the given parameters gives back the original data. |
| |
| const auto& vkd = m_context.getDeviceInterface(); |
| const auto device = m_context.getDevice(); |
| auto& alloc = m_context.getDefaultAllocator(); |
| const auto queue = m_context.getUniversalQueue(); |
| const auto queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); |
| auto& log = m_context.getTestContext().getLog(); |
| |
| log << tcu::TestLog::Message << "Testing aspect " << imageAspect << tcu::TestLog::EndMessage; |
| |
| // Get an idea of the buffer size and parameters to prepare image data. |
| const BufferLevels bufferLevels (m_params.imageType, m_params.imageFormat, m_params.dimensions, m_params.mipLevels, imageAspect); |
| const auto pixelSize = bufferLevels.pixelSize(); |
| const auto pixelSizeSz = static_cast<size_t>(pixelSize); |
| const auto numLevels = bufferLevels.numLevels(); |
| |
| // Create source buffer. |
| const auto bufferSize = bufferLevels.totalSize(); |
| const auto bufferInfo = makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); |
| BufferWithMemory buffer (vkd, device, alloc, bufferInfo, MemoryRequirement::HostVisible); |
| auto& bufferAlloc = buffer.getAllocation(); |
| auto* bufferPtr = reinterpret_cast<unsigned char*>(bufferAlloc.getHostPtr()); |
| |
| // Fill buffer with random appropriate data. |
| const deUint32 randomSeed = 1594055758u + static_cast<deUint32>(m_params.imageFormat) + static_cast<deUint32>(imageAspect); |
| de::Random rnd (randomSeed); |
| const auto tcuFormat = mapVkFormat(m_params.imageFormat); |
| // For some formats, the copy block is 32 bits wide but the 8 MSB need to be ignored, so we zero them out. |
| const bool use24LSB = ((m_params.imageFormat == VK_FORMAT_X8_D24_UNORM_PACK32 || m_params.imageFormat == VK_FORMAT_D24_UNORM_S8_UINT) && imageAspect == VK_IMAGE_ASPECT_DEPTH_BIT); |
| |
| if (tcuFormat.type == tcu::TextureFormat::FLOAT || (m_params.imageFormat == VK_FORMAT_D32_SFLOAT_S8_UINT && imageAspect == VK_IMAGE_ASPECT_DEPTH_BIT)) |
| fillWithRandomFloatingPoint<float>(rnd, bufferPtr, bufferSize); |
| else if (tcuFormat.type == tcu::TextureFormat::FLOAT64) |
| fillWithRandomFloatingPoint<double>(rnd, bufferPtr, bufferSize); |
| else if (tcuFormat.type == tcu::TextureFormat::HALF_FLOAT) |
| fillWithRandomFloatingPoint<tcu::Float16>(rnd, bufferPtr, bufferSize); |
| else if (use24LSB) |
| fillWithRandomData24In32(rnd, bufferPtr, bufferSize); |
| else |
| fillWithRandomData(rnd, bufferPtr, bufferSize); |
| |
| flushAlloc(vkd, device, bufferAlloc); |
| |
| // Reinterpret the depth dimension parameter as the number of layers if needed. |
| const auto numLayers = ((m_params.imageType == VK_IMAGE_TYPE_3D) ? 1u : m_params.dimensions.depth); |
| VkExtent3D imageExtent = m_params.dimensions; |
| if (m_params.imageType == VK_IMAGE_TYPE_2D) |
| imageExtent.depth = 1u; |
| |
| // Create image. |
| const VkImageCreateInfo imageInfo = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; |
| nullptr, // const void* pNext; |
| 0u, // VkImageCreateFlags flags; |
| m_params.imageType, // VkImageType imageType; |
| m_params.imageFormat, // VkFormat format; |
| imageExtent, // VkExtent3D extent; |
| numLevels, // deUint32 mipLevels; |
| numLayers, // deUint32 arrayLayers; |
| VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; |
| ImageSubresourceLayoutCase::kImageTiling, // VkImageTiling tiling; |
| ImageSubresourceLayoutCase::kImageUsageFlags, // VkImageUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 0u, // deUint32 queueFamilyIndexCount; |
| nullptr, // const deUint32* pQueueFamilyIndices; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| }; |
| ImageWithMemory image (vkd, device, alloc, imageInfo, MemoryRequirement::HostVisible); |
| auto& imageAlloc = image.getAllocation(); |
| auto* imagePtr = reinterpret_cast<unsigned char*>(imageAlloc.getHostPtr()); |
| |
| // Copy regions. |
| std::vector<VkBufferImageCopy> copyRegions; |
| copyRegions.reserve(numLevels); |
| |
| for (deUint32 levelNdx = 0u; levelNdx < numLevels; ++levelNdx) |
| { |
| const auto& level = bufferLevels.getLevel(levelNdx); |
| auto levelExtent = level.dimensions; |
| |
| if (m_params.imageType == VK_IMAGE_TYPE_2D) |
| levelExtent.depth = 1u; // For 2D images, .depth indicates the number of layers. |
| |
| VkBufferImageCopy region; |
| region.bufferOffset = level.offset; |
| region.bufferRowLength = 0u; // Tightly packed data. |
| region.bufferImageHeight = 0u; // Ditto. |
| region.imageSubresource.aspectMask = imageAspect; |
| region.imageSubresource.baseArrayLayer = 0u; |
| region.imageSubresource.layerCount = numLayers; |
| region.imageSubresource.mipLevel = levelNdx; |
| region.imageOffset = { 0, 0, 0 }; |
| region.imageExtent = levelExtent; |
| |
| copyRegions.push_back(region); |
| } |
| |
| // Image layout transitions. |
| const auto imageSubresourceRange = makeImageSubresourceRange(imageAspect, 0u, numLevels, 0u, numLayers); |
| const auto initialLayoutBarrier = makeImageMemoryBarrier(0u, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, image.get(), imageSubresourceRange); |
| const auto finalLayoutBarrier = makeImageMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, image.get(), imageSubresourceRange); |
| |
| // Command buffer. |
| const auto cmdPool = makeCommandPool(vkd, device, queueFamilyIndex); |
| const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY); |
| const auto cmdBuffer = cmdBufferPtr.get(); |
| |
| // Transition layout, copy, transition layout. |
| beginCommandBuffer(vkd, cmdBuffer); |
| vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &initialLayoutBarrier); |
| vkd.cmdCopyBufferToImage(cmdBuffer, buffer.get(), image.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<deUint32>(copyRegions.size()), copyRegions.data()); |
| vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &finalLayoutBarrier); |
| endCommandBuffer(vkd, cmdBuffer); |
| submitCommandsAndWait(vkd, device, queue, cmdBuffer); |
| |
| // Sync image memory for host access. |
| invalidateAlloc(vkd, device, imageAlloc); |
| |
| VkSubresourceLayout levelSubresourceLayout; |
| VkSubresourceLayout subresourceLayout; |
| for (deUint32 levelNdx = 0u; levelNdx < numLevels; ++levelNdx) |
| { |
| // Get base level subresource. |
| const auto levelSubresource = makeImageSubresource(imageAspect, levelNdx, 0u); |
| vkd.getImageSubresourceLayout(device, image.get(), &levelSubresource, &levelSubresourceLayout); |
| |
| const auto& level = bufferLevels.getLevel(levelNdx); |
| for (deUint32 layerNdx = 0; layerNdx < numLayers; ++layerNdx) |
| { |
| const auto imageSubresource = makeImageSubresource(imageAspect, levelNdx, layerNdx); |
| vkd.getImageSubresourceLayout(device, image.get(), &imageSubresource, &subresourceLayout); |
| |
| // Verify returned values. |
| const auto subresourceWidth = level.dimensions.width; |
| const auto subresourceHeight = level.dimensions.height; |
| const auto subresourceDepth = ((m_params.imageType == VK_IMAGE_TYPE_2D) ? 1u : level.dimensions.depth); |
| const auto numPixels = subresourceWidth * subresourceHeight * subresourceDepth; |
| |
| if (numLayers > 1u && levelSubresourceLayout.arrayPitch != subresourceLayout.arrayPitch) |
| { |
| // Inconsistent arrayPitch. |
| std::ostringstream msg; |
| msg << "Image level " << levelNdx |
| << " layer " << layerNdx |
| << " reports array pitch of " << subresourceLayout.arrayPitch << " bytes in size" |
| << " with base layer reporting array pitch of " << levelSubresourceLayout.arrayPitch << " bytes in size"; |
| return tcu::TestStatus::fail(msg.str()); |
| } |
| |
| if ((subresourceLayout.offset - levelSubresourceLayout.offset) != (layerNdx * subresourceLayout.arrayPitch)) |
| { |
| // Inconsistent offset. |
| std::ostringstream msg; |
| msg << "Image level " << levelNdx |
| << " layer " << layerNdx |
| << " has offset inconsistent with array pitch: base offset " << levelSubresourceLayout.offset |
| << ", layer offset " << subresourceLayout.offset |
| << ", array pitch " << subresourceLayout.arrayPitch; |
| return tcu::TestStatus::fail(msg.str()); |
| } |
| |
| if (subresourceLayout.size < pixelSize * numPixels) |
| { |
| // Subresource size too small. |
| std::ostringstream msg; |
| msg << "Image level " << levelNdx |
| << " layer " << layerNdx |
| << " reports " << subresourceLayout.size << " bytes in size" |
| << " with pixel size " << pixelSize |
| << " and dimensions " << subresourceWidth << "x" << subresourceHeight << "x" << subresourceDepth; |
| return tcu::TestStatus::fail(msg.str()); |
| } |
| |
| // Note: if subresourceHeight is <= 1u, rowPitch can be zero. |
| if (subresourceHeight > 1u && subresourceLayout.rowPitch < pixelSize * subresourceWidth) |
| { |
| // Row pitch too small. |
| std::ostringstream msg; |
| msg << "Image level " << levelNdx |
| << " layer " << layerNdx |
| << " reports row pitch of " << subresourceLayout.rowPitch |
| << " bytes with " << pixelSize |
| << " bytes in pixel size and width " << subresourceWidth; |
| return tcu::TestStatus::fail(msg.str()); |
| } |
| |
| if (numLayers > 1u && subresourceLayout.arrayPitch < pixelSize * numPixels) |
| { |
| // Array pitch too small. |
| std::ostringstream msg; |
| msg << "Image level " << levelNdx |
| << " layer " << layerNdx |
| << " reports array pitch of " << subresourceLayout.arrayPitch |
| << " bytes with " << pixelSize |
| << " bytes in pixel size and layer dimensions " << subresourceWidth << "x" << subresourceHeight; |
| return tcu::TestStatus::fail(msg.str()); |
| } |
| |
| // If subresourceDepth is <= 1u, depthPitch can be zero. |
| if (subresourceDepth > 1u && m_params.imageType == VK_IMAGE_TYPE_3D && subresourceLayout.depthPitch < pixelSize * subresourceWidth * subresourceHeight) |
| { |
| // Depth pitch too small. |
| std::ostringstream msg; |
| msg << "Image level " << levelNdx |
| << " layer " << layerNdx |
| << " reports depth pitch of " << subresourceLayout.depthPitch << " bytes" |
| << " with pixel size " << pixelSize |
| << " and dimensions " << subresourceWidth << "x" << subresourceHeight << "x" << subresourceDepth; |
| return tcu::TestStatus::fail(msg.str()); |
| } |
| |
| // Verify image data. |
| const auto layerBufferOffset = level.offset + layerNdx * numPixels * pixelSize; |
| const auto layerImageOffset = subresourceLayout.offset; |
| const auto layerBufferPtr = bufferPtr + layerBufferOffset; |
| const auto layerImagePtr = imagePtr + layerImageOffset; |
| bool pixelMatch; |
| |
| // We could do this row by row to be faster, but in the use24LSB case we need to manipulate pixels independently. |
| for (deUint32 x = 0u; x < subresourceWidth; ++x) |
| for (deUint32 y = 0u; y < subresourceHeight; ++y) |
| for (deUint32 z = 0u; z < subresourceDepth; ++z) |
| { |
| const auto bufferPixelOffset = (z * subresourceWidth * subresourceHeight + y * subresourceWidth + x) * pixelSize; |
| const auto imagePixelOffset = z * subresourceLayout.depthPitch + y * subresourceLayout.rowPitch + x * pixelSize; |
| const auto bufferPixelPtr = layerBufferPtr + bufferPixelOffset; |
| const auto imagePixelPtr = layerImagePtr + imagePixelOffset; |
| |
| if (use24LSB) |
| { |
| DE_ASSERT(pixelSize == sizeof(deUint32)); |
| deUint32 pixelValue; |
| deMemcpy(&pixelValue, imagePixelPtr, pixelSizeSz); |
| pixelValue &= 0xFFFFFFu; // Discard the 8 MSB. |
| pixelMatch = (deMemCmp(bufferPixelPtr, &pixelValue, pixelSizeSz) == 0); |
| } |
| else |
| pixelMatch = (deMemCmp(bufferPixelPtr, imagePixelPtr, pixelSizeSz) == 0); |
| |
| if (!pixelMatch) |
| { |
| std::ostringstream msg; |
| msg << "Found difference from image pixel to buffer pixel at coordinates" |
| << " level=" << levelNdx |
| << " layer=" << layerNdx |
| << " x=" << x |
| << " y=" << y |
| << " z=" << z |
| ; |
| return tcu::TestStatus::fail(msg.str()); |
| } |
| } |
| } |
| } |
| |
| return tcu::TestStatus::pass("Pass"); |
| } |
| |
| } // anonymous namespace |
| |
| tcu::TestCaseGroup* createImageSubresourceLayoutTests (tcu::TestContext& testCtx) |
| { |
| de::MovePtr<tcu::TestCaseGroup> layoutTestGroup (new tcu::TestCaseGroup(testCtx, "subresource_layout", "Tests for vkGetImageSubresourceLayout")); |
| |
| struct |
| { |
| VkImageType type; |
| bool array; |
| const char* name; |
| const char* desc; |
| } imageClass[] = |
| { |
| { VK_IMAGE_TYPE_2D, false, "2d", "2D images" }, |
| { VK_IMAGE_TYPE_2D, true, "2d_array", "2D images with multiple layers" }, |
| { VK_IMAGE_TYPE_3D, false, "3d", "3D images" }, |
| }; |
| |
| struct |
| { |
| deUint32 maxLevels; |
| const char* name; |
| const char* desc; |
| } mipLevels[] = |
| { |
| { 1u, "1_level", "Single mip level" }, |
| { 2u, "2_levels", "Two mip levels" }, |
| { 4u, "4_levels", "Four mip levels" }, |
| { std::numeric_limits<deUint32>::max(), "all_levels", "All possible levels" }, |
| }; |
| |
| VkFormat testFormats[] = |
| { |
| VK_FORMAT_R4G4_UNORM_PACK8, |
| VK_FORMAT_R4G4B4A4_UNORM_PACK16, |
| VK_FORMAT_B4G4R4A4_UNORM_PACK16, |
| VK_FORMAT_R5G6B5_UNORM_PACK16, |
| VK_FORMAT_B5G6R5_UNORM_PACK16, |
| VK_FORMAT_R5G5B5A1_UNORM_PACK16, |
| VK_FORMAT_B5G5R5A1_UNORM_PACK16, |
| VK_FORMAT_A1R5G5B5_UNORM_PACK16, |
| VK_FORMAT_R8_UNORM, |
| VK_FORMAT_R8_SNORM, |
| VK_FORMAT_R8_USCALED, |
| VK_FORMAT_R8_SSCALED, |
| VK_FORMAT_R8_UINT, |
| VK_FORMAT_R8_SINT, |
| VK_FORMAT_R8_SRGB, |
| VK_FORMAT_R8G8_UNORM, |
| VK_FORMAT_R8G8_SNORM, |
| VK_FORMAT_R8G8_USCALED, |
| VK_FORMAT_R8G8_SSCALED, |
| VK_FORMAT_R8G8_UINT, |
| VK_FORMAT_R8G8_SINT, |
| VK_FORMAT_R8G8_SRGB, |
| VK_FORMAT_R8G8B8_UNORM, |
| VK_FORMAT_R8G8B8_SNORM, |
| VK_FORMAT_R8G8B8_USCALED, |
| VK_FORMAT_R8G8B8_SSCALED, |
| VK_FORMAT_R8G8B8_UINT, |
| VK_FORMAT_R8G8B8_SINT, |
| VK_FORMAT_R8G8B8_SRGB, |
| VK_FORMAT_B8G8R8_UNORM, |
| VK_FORMAT_B8G8R8_SNORM, |
| VK_FORMAT_B8G8R8_USCALED, |
| VK_FORMAT_B8G8R8_SSCALED, |
| VK_FORMAT_B8G8R8_UINT, |
| VK_FORMAT_B8G8R8_SINT, |
| VK_FORMAT_B8G8R8_SRGB, |
| VK_FORMAT_R8G8B8A8_UNORM, |
| VK_FORMAT_R8G8B8A8_SNORM, |
| VK_FORMAT_R8G8B8A8_USCALED, |
| VK_FORMAT_R8G8B8A8_SSCALED, |
| VK_FORMAT_R8G8B8A8_UINT, |
| VK_FORMAT_R8G8B8A8_SINT, |
| VK_FORMAT_R8G8B8A8_SRGB, |
| VK_FORMAT_B8G8R8A8_UNORM, |
| VK_FORMAT_B8G8R8A8_SNORM, |
| VK_FORMAT_B8G8R8A8_USCALED, |
| VK_FORMAT_B8G8R8A8_SSCALED, |
| VK_FORMAT_B8G8R8A8_UINT, |
| VK_FORMAT_B8G8R8A8_SINT, |
| VK_FORMAT_B8G8R8A8_SRGB, |
| VK_FORMAT_A8B8G8R8_UNORM_PACK32, |
| VK_FORMAT_A8B8G8R8_SNORM_PACK32, |
| VK_FORMAT_A8B8G8R8_USCALED_PACK32, |
| VK_FORMAT_A8B8G8R8_SSCALED_PACK32, |
| VK_FORMAT_A8B8G8R8_UINT_PACK32, |
| VK_FORMAT_A8B8G8R8_SINT_PACK32, |
| VK_FORMAT_A8B8G8R8_SRGB_PACK32, |
| VK_FORMAT_A2R10G10B10_UNORM_PACK32, |
| VK_FORMAT_A2R10G10B10_SNORM_PACK32, |
| VK_FORMAT_A2R10G10B10_USCALED_PACK32, |
| VK_FORMAT_A2R10G10B10_SSCALED_PACK32, |
| VK_FORMAT_A2R10G10B10_UINT_PACK32, |
| VK_FORMAT_A2R10G10B10_SINT_PACK32, |
| VK_FORMAT_A2B10G10R10_UNORM_PACK32, |
| VK_FORMAT_A2B10G10R10_SNORM_PACK32, |
| VK_FORMAT_A2B10G10R10_USCALED_PACK32, |
| VK_FORMAT_A2B10G10R10_SSCALED_PACK32, |
| VK_FORMAT_A2B10G10R10_UINT_PACK32, |
| VK_FORMAT_A2B10G10R10_SINT_PACK32, |
| VK_FORMAT_R16_UNORM, |
| VK_FORMAT_R16_SNORM, |
| VK_FORMAT_R16_USCALED, |
| VK_FORMAT_R16_SSCALED, |
| VK_FORMAT_R16_UINT, |
| VK_FORMAT_R16_SINT, |
| VK_FORMAT_R16_SFLOAT, |
| VK_FORMAT_R16G16_UNORM, |
| VK_FORMAT_R16G16_SNORM, |
| VK_FORMAT_R16G16_USCALED, |
| VK_FORMAT_R16G16_SSCALED, |
| VK_FORMAT_R16G16_UINT, |
| VK_FORMAT_R16G16_SINT, |
| VK_FORMAT_R16G16_SFLOAT, |
| VK_FORMAT_R16G16B16_UNORM, |
| VK_FORMAT_R16G16B16_SNORM, |
| VK_FORMAT_R16G16B16_USCALED, |
| VK_FORMAT_R16G16B16_SSCALED, |
| VK_FORMAT_R16G16B16_UINT, |
| VK_FORMAT_R16G16B16_SINT, |
| VK_FORMAT_R16G16B16_SFLOAT, |
| VK_FORMAT_R16G16B16A16_UNORM, |
| VK_FORMAT_R16G16B16A16_SNORM, |
| VK_FORMAT_R16G16B16A16_USCALED, |
| VK_FORMAT_R16G16B16A16_SSCALED, |
| VK_FORMAT_R16G16B16A16_UINT, |
| VK_FORMAT_R16G16B16A16_SINT, |
| VK_FORMAT_R16G16B16A16_SFLOAT, |
| VK_FORMAT_R32_UINT, |
| VK_FORMAT_R32_SINT, |
| VK_FORMAT_R32_SFLOAT, |
| VK_FORMAT_R32G32_UINT, |
| VK_FORMAT_R32G32_SINT, |
| VK_FORMAT_R32G32_SFLOAT, |
| VK_FORMAT_R32G32B32_UINT, |
| VK_FORMAT_R32G32B32_SINT, |
| VK_FORMAT_R32G32B32_SFLOAT, |
| VK_FORMAT_R32G32B32A32_UINT, |
| VK_FORMAT_R32G32B32A32_SINT, |
| VK_FORMAT_R32G32B32A32_SFLOAT, |
| VK_FORMAT_R64_UINT, |
| VK_FORMAT_R64_SINT, |
| VK_FORMAT_R64_SFLOAT, |
| VK_FORMAT_R64G64_UINT, |
| VK_FORMAT_R64G64_SINT, |
| VK_FORMAT_R64G64_SFLOAT, |
| VK_FORMAT_R64G64B64_UINT, |
| VK_FORMAT_R64G64B64_SINT, |
| VK_FORMAT_R64G64B64_SFLOAT, |
| VK_FORMAT_R64G64B64A64_UINT, |
| VK_FORMAT_R64G64B64A64_SINT, |
| VK_FORMAT_R64G64B64A64_SFLOAT, |
| // Leaving out depth/stencil formats due to this part of the spec: |
| // |
| // "Depth/stencil formats are considered opaque and need not be stored in the exact number of bits per texel or component |
| // ordering indicated by the format enum. However, implementations must not substitute a different depth or stencil |
| // precision than that described in the format (e.g. D16 must not be implemented as D24 or D32)." |
| // |
| // Which means the size of the texel is not known for depth/stencil formats and we cannot iterate over them to check their |
| // values. |
| #if 0 |
| VK_FORMAT_D16_UNORM, |
| VK_FORMAT_X8_D24_UNORM_PACK32, |
| VK_FORMAT_D32_SFLOAT, |
| VK_FORMAT_S8_UINT, |
| VK_FORMAT_D16_UNORM_S8_UINT, |
| VK_FORMAT_D24_UNORM_S8_UINT, |
| VK_FORMAT_D32_SFLOAT_S8_UINT, |
| #endif |
| }; |
| |
| for (int classIdx = 0; classIdx < DE_LENGTH_OF_ARRAY(imageClass); ++classIdx) |
| { |
| const auto& imgClass = imageClass[classIdx]; |
| de::MovePtr<tcu::TestCaseGroup> classGroup (new tcu::TestCaseGroup(testCtx, imgClass.name, imgClass.desc)); |
| |
| for (int mipIdx = 0; mipIdx < DE_LENGTH_OF_ARRAY(mipLevels); ++mipIdx) |
| { |
| const auto &mipLevel = mipLevels[mipIdx]; |
| de::MovePtr<tcu::TestCaseGroup> mipGroup (new tcu::TestCaseGroup(testCtx, mipLevel.name, mipLevel.desc)); |
| |
| for (int formatIdx = 0; formatIdx < DE_LENGTH_OF_ARRAY(testFormats); ++formatIdx) |
| { |
| static const auto prefixLen = std::string("VK_FORMAT_").size(); |
| const auto format = testFormats[formatIdx]; |
| const auto fmtName = std::string(getFormatName(format)); |
| const auto name = de::toLower(fmtName.substr(prefixLen)); // Remove VK_FORMAT_ prefix. |
| const auto desc = "Using format " + fmtName; |
| |
| ImageSubresourceLayoutCase::TestParams params; |
| params.imageFormat = format; |
| params.imageType = imgClass.type; |
| params.mipLevels = mipLevel.maxLevels; |
| params.dimensions = getDefaultDimensions(imgClass.type, imgClass.array); |
| |
| mipGroup->addChild(new ImageSubresourceLayoutCase(testCtx, name, desc, params)); |
| } |
| |
| classGroup->addChild(mipGroup.release()); |
| } |
| |
| layoutTestGroup->addChild(classGroup.release()); |
| } |
| |
| return layoutTestGroup.release(); |
| } |
| |
| } // namespace image |
| } // namespace vkt |