/*------------------------------------------------------------------------ * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2016 The Khronos Group Inc. * Copyright (c) 2016 The Android Open Source Project * * 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 Image load/store Tests *//*--------------------------------------------------------------------*/ #include "vktImageLoadStoreTests.hpp" #include "vktTestCaseUtil.hpp" #include "vktImageTestsUtil.hpp" #include "vktImageTexture.hpp" #include "vkDefs.hpp" #include "vkRef.hpp" #include "vkRefUtil.hpp" #include "vkPlatform.hpp" #include "vkPrograms.hpp" #include "vkMemUtil.hpp" #include "vkBuilderUtil.hpp" #include "vkQueryUtil.hpp" #include "vkImageUtil.hpp" #include "deUniquePtr.hpp" #include "deSharedPtr.hpp" #include "deStringUtil.hpp" #include "tcuImageCompare.hpp" #include "tcuTexture.hpp" #include "tcuTextureUtil.hpp" #include "tcuFloat.hpp" #include #include using namespace vk; namespace vkt { namespace image { namespace { typedef de::SharedPtr > SharedVkDescriptorSet; typedef de::SharedPtr > SharedVkImageView; template inline de::SharedPtr > makeVkSharedPtr (Move vkMove) { return de::SharedPtr >(new Unique(vkMove)); } inline VkImageCreateInfo makeImageCreateInfo (const Texture& texture, const VkFormat format, const VkImageUsageFlags usage, const VkImageCreateFlags flags) { const VkImageCreateInfo imageParams = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; (isCube(texture) ? (VkImageCreateFlags)VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0u) | flags, // VkImageCreateFlags flags; mapImageType(texture.type()), // VkImageType imageType; format, // VkFormat format; makeExtent3D(texture.layerSize()), // VkExtent3D extent; 1u, // deUint32 mipLevels; (deUint32)texture.numLayers(), // deUint32 arrayLayers; VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; usage, // VkImageUsageFlags usage; VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 0u, // deUint32 queueFamilyIndexCount; DE_NULL, // const deUint32* pQueueFamilyIndices; VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; }; return imageParams; } inline VkBufferImageCopy makeBufferImageCopy (const Texture& texture) { return image::makeBufferImageCopy(makeExtent3D(texture.layerSize()), texture.numLayers()); } ImageType getImageTypeForSingleLayer (const ImageType imageType) { switch (imageType) { case IMAGE_TYPE_1D: case IMAGE_TYPE_1D_ARRAY: return IMAGE_TYPE_1D; case IMAGE_TYPE_2D: case IMAGE_TYPE_2D_ARRAY: case IMAGE_TYPE_CUBE: case IMAGE_TYPE_CUBE_ARRAY: // A single layer for cube is a 2d face return IMAGE_TYPE_2D; case IMAGE_TYPE_3D: return IMAGE_TYPE_3D; case IMAGE_TYPE_BUFFER: return IMAGE_TYPE_BUFFER; default: DE_FATAL("Internal test error"); return IMAGE_TYPE_LAST; } } float computeStoreColorScale (const VkFormat format, const tcu::IVec3 imageSize) { const int maxImageDimension = de::max(imageSize.x(), de::max(imageSize.y(), imageSize.z())); const float div = static_cast(maxImageDimension - 1); if (isUnormFormat(format)) return 1.0f / div; else if (isSnormFormat(format)) return 2.0f / div; else return 1.0f; } inline float computeStoreColorBias (const VkFormat format) { return isSnormFormat(format) ? -1.0f : 0.0f; } inline bool isIntegerFormat (const VkFormat format) { return isIntFormat(format) || isUintFormat(format); } tcu::ConstPixelBufferAccess getLayerOrSlice (const Texture& texture, const tcu::ConstPixelBufferAccess access, const int layer) { switch (texture.type()) { case IMAGE_TYPE_1D: case IMAGE_TYPE_2D: case IMAGE_TYPE_BUFFER: // Not layered DE_ASSERT(layer == 0); return access; case IMAGE_TYPE_1D_ARRAY: return tcu::getSubregion(access, 0, layer, access.getWidth(), 1); case IMAGE_TYPE_2D_ARRAY: case IMAGE_TYPE_CUBE: case IMAGE_TYPE_CUBE_ARRAY: case IMAGE_TYPE_3D: // 3d texture is treated as if depth was the layers return tcu::getSubregion(access, 0, 0, layer, access.getWidth(), access.getHeight(), 1); default: DE_FATAL("Internal test error"); return tcu::ConstPixelBufferAccess(); } } std::string getFormatCaseName (const VkFormat format) { const std::string fullName = getFormatName(format); DE_ASSERT(de::beginsWith(fullName, "VK_FORMAT_")); return de::toLower(fullName.substr(10)); } //! \return true if all layers match in both pixel buffers bool comparePixelBuffers (tcu::TestLog& log, const Texture& texture, const VkFormat format, const tcu::ConstPixelBufferAccess reference, const tcu::ConstPixelBufferAccess result) { DE_ASSERT(reference.getFormat() == result.getFormat()); DE_ASSERT(reference.getSize() == result.getSize()); const bool intFormat = isIntegerFormat(format); const bool is3d = (texture.type() == IMAGE_TYPE_3D); const int numLayersOrSlices = (is3d ? texture.size().z() : texture.numLayers()); const int numCubeFaces = 6; int passedLayers = 0; for (int layerNdx = 0; layerNdx < numLayersOrSlices; ++layerNdx) { const std::string comparisonName = "Comparison" + de::toString(layerNdx); const std::string comparisonDesc = "Image Comparison, " + (isCube(texture) ? "face " + de::toString(layerNdx % numCubeFaces) + ", cube " + de::toString(layerNdx / numCubeFaces) : is3d ? "slice " + de::toString(layerNdx) : "layer " + de::toString(layerNdx)); const tcu::ConstPixelBufferAccess refLayer = getLayerOrSlice(texture, reference, layerNdx); const tcu::ConstPixelBufferAccess resultLayer = getLayerOrSlice(texture, result, layerNdx); bool ok = false; if (intFormat) ok = tcu::intThresholdCompare(log, comparisonName.c_str(), comparisonDesc.c_str(), refLayer, resultLayer, tcu::UVec4(0), tcu::COMPARE_LOG_RESULT); else ok = tcu::floatThresholdCompare(log, comparisonName.c_str(), comparisonDesc.c_str(), refLayer, resultLayer, tcu::Vec4(0.01f), tcu::COMPARE_LOG_RESULT); if (ok) ++passedLayers; } return passedLayers == numLayersOrSlices; } //!< Zero out invalid pixels in the image (denormalized, infinite, NaN values) void replaceBadFloatReinterpretValues (const tcu::PixelBufferAccess access) { DE_ASSERT(tcu::getTextureChannelClass(access.getFormat().type) == tcu::TEXTURECHANNELCLASS_FLOATING_POINT); for (int z = 0; z < access.getDepth(); ++z) for (int y = 0; y < access.getHeight(); ++y) for (int x = 0; x < access.getWidth(); ++x) { const tcu::Vec4 color(access.getPixel(x, y, z)); tcu::Vec4 newColor = color; for (int i = 0; i < 4; ++i) { if (access.getFormat().type == tcu::TextureFormat::HALF_FLOAT) { const tcu::Float16 f(color[i]); if (f.isDenorm() || f.isInf() || f.isNaN()) newColor[i] = 0.0f; } else { const tcu::Float32 f(color[i]); if (f.isDenorm() || f.isInf() || f.isNaN()) newColor[i] = 0.0f; } } if (newColor != color) access.setPixel(newColor, x, y, z); } } //!< replace invalid pixels in the image (-128) void replaceSnormReinterpretValues (const tcu::PixelBufferAccess access) { DE_ASSERT(tcu::getTextureChannelClass(access.getFormat().type) == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT); for (int z = 0; z < access.getDepth(); ++z) for (int y = 0; y < access.getHeight(); ++y) for (int x = 0; x < access.getWidth(); ++x) { const tcu::IVec4 color(access.getPixelInt(x, y, z)); tcu::IVec4 newColor = color; for (int i = 0; i < 4; ++i) { const deInt32 oldColor(color[i]); if (oldColor == -128) newColor[i] = -127; } if (newColor != color) access.setPixel(newColor, x, y, z); } } tcu::TextureLevel generateReferenceImage (const tcu::IVec3& imageSize, const VkFormat imageFormat, const VkFormat readFormat) { // Generate a reference image data using the storage format tcu::TextureLevel reference(mapVkFormat(imageFormat), imageSize.x(), imageSize.y(), imageSize.z()); const tcu::PixelBufferAccess access = reference.getAccess(); const float storeColorScale = computeStoreColorScale(imageFormat, imageSize); const float storeColorBias = computeStoreColorBias(imageFormat); const bool intFormat = isIntegerFormat(imageFormat); const int xMax = imageSize.x() - 1; const int yMax = imageSize.y() - 1; for (int z = 0; z < imageSize.z(); ++z) for (int y = 0; y < imageSize.y(); ++y) for (int x = 0; x < imageSize.x(); ++x) { const tcu::IVec4 color(x^y^z, (xMax - x)^y^z, x^(yMax - y)^z, (xMax - x)^(yMax - y)^z); if (intFormat) access.setPixel(color, x, y, z); else access.setPixel(color.asFloat()*storeColorScale + storeColorBias, x, y, z); } // If the image is to be accessed as a float texture, get rid of invalid values if (isFloatFormat(readFormat) && imageFormat != readFormat) replaceBadFloatReinterpretValues(tcu::PixelBufferAccess(mapVkFormat(readFormat), imageSize, access.getDataPtr())); if (isSnormFormat(readFormat) && imageFormat != readFormat) replaceSnormReinterpretValues(tcu::PixelBufferAccess(mapVkFormat(readFormat), imageSize, access.getDataPtr())); return reference; } inline tcu::TextureLevel generateReferenceImage (const tcu::IVec3& imageSize, const VkFormat imageFormat) { return generateReferenceImage(imageSize, imageFormat, imageFormat); } void flipHorizontally (const tcu::PixelBufferAccess access) { const int xMax = access.getWidth() - 1; const int halfWidth = access.getWidth() / 2; if (isIntegerFormat(mapTextureFormat(access.getFormat()))) for (int z = 0; z < access.getDepth(); z++) for (int y = 0; y < access.getHeight(); y++) for (int x = 0; x < halfWidth; x++) { const tcu::UVec4 temp = access.getPixelUint(xMax - x, y, z); access.setPixel(access.getPixelUint(x, y, z), xMax - x, y, z); access.setPixel(temp, x, y, z); } else for (int z = 0; z < access.getDepth(); z++) for (int y = 0; y < access.getHeight(); y++) for (int x = 0; x < halfWidth; x++) { const tcu::Vec4 temp = access.getPixel(xMax - x, y, z); access.setPixel(access.getPixel(x, y, z), xMax - x, y, z); access.setPixel(temp, x, y, z); } } #if defined(DE_DEBUG) inline bool colorScaleAndBiasAreValid (const VkFormat format, const float colorScale, const float colorBias) { // Only normalized (fixed-point) formats may have scale/bias const bool integerOrFloatFormat = isIntFormat(format) || isUintFormat(format) || isFloatFormat(format); return !integerOrFloatFormat || (colorScale == 1.0f && colorBias == 0.0f); } #endif inline bool formatsAreCompatible (const VkFormat format0, const VkFormat format1) { return format0 == format1 || mapVkFormat(format0).getPixelSize() == mapVkFormat(format1).getPixelSize(); } void commandImageWriteBarrierBetweenShaderInvocations (Context& context, const VkCommandBuffer cmdBuffer, const VkImage image, const Texture& texture) { const DeviceInterface& vk = context.getDeviceInterface(); const VkImageSubresourceRange fullImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, texture.numLayers()); const VkImageMemoryBarrier shaderWriteBarrier = makeImageMemoryBarrier( VK_ACCESS_SHADER_WRITE_BIT, 0u, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, image, fullImageSubresourceRange); vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &shaderWriteBarrier); } void commandBufferWriteBarrierBeforeHostRead (Context& context, const VkCommandBuffer cmdBuffer, const VkBuffer buffer, const VkDeviceSize bufferSizeBytes) { const DeviceInterface& vk = context.getDeviceInterface(); const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier( VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, buffer, 0ull, bufferSizeBytes); vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &shaderWriteBarrier, 0, (const VkImageMemoryBarrier*)DE_NULL); } //! Copy all layers of an image to a buffer. void commandCopyImageToBuffer (Context& context, const VkCommandBuffer cmdBuffer, const VkImage image, const VkBuffer buffer, const VkDeviceSize bufferSizeBytes, const Texture& texture) { const DeviceInterface& vk = context.getDeviceInterface(); const VkImageSubresourceRange fullImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, texture.numLayers()); const VkImageMemoryBarrier prepareForTransferBarrier = makeImageMemoryBarrier( VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, fullImageSubresourceRange); const VkBufferImageCopy copyRegion = makeBufferImageCopy(texture); const VkBufferMemoryBarrier copyBarrier = makeBufferMemoryBarrier( VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, buffer, 0ull, bufferSizeBytes); vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &prepareForTransferBarrier); vk.cmdCopyImageToBuffer(cmdBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, 1u, ©Region); vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, ©Barrier, 0, (const VkImageMemoryBarrier*)DE_NULL); } //! Minimum chunk size is determined by the offset alignment requirements. VkDeviceSize getOptimalUniformBufferChunkSize (Context& context, VkDeviceSize minimumRequiredChunkSizeBytes) { const VkPhysicalDeviceProperties properties = getPhysicalDeviceProperties(context.getInstanceInterface(), context.getPhysicalDevice()); const VkDeviceSize alignment = properties.limits.minUniformBufferOffsetAlignment; if (minimumRequiredChunkSizeBytes > alignment) return alignment + (minimumRequiredChunkSizeBytes / alignment) * alignment; else return alignment; } class StoreTest : public TestCase { public: enum TestFlags { FLAG_SINGLE_LAYER_BIND = 0x1, //!< Run the shader multiple times, each time binding a different layer. }; StoreTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const Texture& texture, const VkFormat format, const TestFlags flags = static_cast(0)); void initPrograms (SourceCollections& programCollection) const; TestInstance* createInstance (Context& context) const; private: const Texture m_texture; const VkFormat m_format; const bool m_singleLayerBind; }; StoreTest::StoreTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const Texture& texture, const VkFormat format, const TestFlags flags) : TestCase (testCtx, name, description) , m_texture (texture) , m_format (format) , m_singleLayerBind ((flags & FLAG_SINGLE_LAYER_BIND) != 0) { if (m_singleLayerBind) DE_ASSERT(m_texture.numLayers() > 1); } void StoreTest::initPrograms (SourceCollections& programCollection) const { const float storeColorScale = computeStoreColorScale(m_format, m_texture.size()); const float storeColorBias = computeStoreColorBias(m_format); DE_ASSERT(colorScaleAndBiasAreValid(m_format, storeColorScale, storeColorBias)); const std::string xMax = de::toString(m_texture.size().x() - 1); const std::string yMax = de::toString(m_texture.size().y() - 1); const std::string signednessPrefix = isUintFormat(m_format) ? "u" : isIntFormat(m_format) ? "i" : ""; const std::string colorBaseExpr = signednessPrefix + "vec4(" + "gx^gy^gz, " + "(" + xMax + "-gx)^gy^gz, " + "gx^(" + yMax + "-gy)^gz, " + "(" + xMax + "-gx)^(" + yMax + "-gy)^gz)"; const std::string colorExpr = colorBaseExpr + (storeColorScale == 1.0f ? "" : "*" + de::toString(storeColorScale)) + (storeColorBias == 0.0f ? "" : " + float(" + de::toString(storeColorBias) + ")"); const int dimension = (m_singleLayerBind ? m_texture.layerDimension() : m_texture.dimension()); const std::string texelCoordStr = (dimension == 1 ? "gx" : dimension == 2 ? "ivec2(gx, gy)" : dimension == 3 ? "ivec3(gx, gy, gz)" : ""); const ImageType usedImageType = (m_singleLayerBind ? getImageTypeForSingleLayer(m_texture.type()) : m_texture.type()); const std::string formatQualifierStr = getShaderImageFormatQualifier(mapVkFormat(m_format)); const std::string imageTypeStr = getShaderImageType(mapVkFormat(m_format), usedImageType); std::ostringstream src; src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_440) << "\n" << "\n" << "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n" << "layout (binding = 0, " << formatQualifierStr << ") writeonly uniform highp " << imageTypeStr << " u_image;\n"; if (m_singleLayerBind) src << "layout (binding = 1) readonly uniform Constants {\n" << " int u_layerNdx;\n" << "};\n"; src << "\n" << "void main (void)\n" << "{\n" << " int gx = int(gl_GlobalInvocationID.x);\n" << " int gy = int(gl_GlobalInvocationID.y);\n" << " int gz = " << (m_singleLayerBind ? "u_layerNdx" : "int(gl_GlobalInvocationID.z)") << ";\n" << " imageStore(u_image, " << texelCoordStr << ", " << colorExpr << ");\n" << "}\n"; programCollection.glslSources.add("comp") << glu::ComputeSource(src.str()); } //! Generic test iteration algorithm for image tests class BaseTestInstance : public TestInstance { public: BaseTestInstance (Context& context, const Texture& texture, const VkFormat format, const bool singleLayerBind); tcu::TestStatus iterate (void); virtual ~BaseTestInstance (void) {} protected: virtual VkDescriptorSetLayout prepareDescriptors (void) = 0; virtual tcu::TestStatus verifyResult (void) = 0; virtual void commandBeforeCompute (const VkCommandBuffer cmdBuffer) = 0; virtual void commandBetweenShaderInvocations (const VkCommandBuffer cmdBuffer) = 0; virtual void commandAfterCompute (const VkCommandBuffer cmdBuffer) = 0; virtual void commandBindDescriptorsForLayer (const VkCommandBuffer cmdBuffer, const VkPipelineLayout pipelineLayout, const int layerNdx) = 0; const Texture m_texture; const VkFormat m_format; const bool m_singleLayerBind; }; BaseTestInstance::BaseTestInstance (Context& context, const Texture& texture, const VkFormat format, const bool singleLayerBind) : TestInstance (context) , m_texture (texture) , m_format (format) , m_singleLayerBind (singleLayerBind) { } tcu::TestStatus BaseTestInstance::iterate (void) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); const VkQueue queue = m_context.getUniversalQueue(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); const Unique shaderModule(createShaderModule(vk, device, m_context.getBinaryCollection().get("comp"), 0)); const VkDescriptorSetLayout descriptorSetLayout = prepareDescriptors(); const Unique pipelineLayout(makePipelineLayout(vk, device, descriptorSetLayout)); const Unique pipeline(makeComputePipeline(vk, device, *pipelineLayout, *shaderModule)); const Unique cmdPool(makeCommandPool(vk, device, queueFamilyIndex)); const Unique cmdBuffer(makeCommandBuffer(vk, device, *cmdPool)); beginCommandBuffer(vk, *cmdBuffer); vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); commandBeforeCompute(*cmdBuffer); const tcu::IVec3 workSize = (m_singleLayerBind ? m_texture.layerSize() : m_texture.size()); const int loopNumLayers = (m_singleLayerBind ? m_texture.numLayers() : 1); for (int layerNdx = 0; layerNdx < loopNumLayers; ++layerNdx) { commandBindDescriptorsForLayer(*cmdBuffer, *pipelineLayout, layerNdx); if (layerNdx > 0) commandBetweenShaderInvocations(*cmdBuffer); vk.cmdDispatch(*cmdBuffer, workSize.x(), workSize.y(), workSize.z()); } commandAfterCompute(*cmdBuffer); endCommandBuffer(vk, *cmdBuffer); submitCommandsAndWait(vk, device, queue, *cmdBuffer); return verifyResult(); } //! Base store test implementation class StoreTestInstance : public BaseTestInstance { public: StoreTestInstance (Context& context, const Texture& texture, const VkFormat format, const bool singleLayerBind); protected: tcu::TestStatus verifyResult (void); // Add empty implementations for functions that might be not needed void commandBeforeCompute (const VkCommandBuffer) {} void commandBetweenShaderInvocations (const VkCommandBuffer) {} void commandAfterCompute (const VkCommandBuffer) {} de::MovePtr m_imageBuffer; const VkDeviceSize m_imageSizeBytes; }; StoreTestInstance::StoreTestInstance (Context& context, const Texture& texture, const VkFormat format, const bool singleLayerBind) : BaseTestInstance (context, texture, format, singleLayerBind) , m_imageSizeBytes (getImageSizeBytes(texture.size(), format)) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); Allocator& allocator = m_context.getDefaultAllocator(); // A helper buffer with enough space to hold the whole image. Usage flags accommodate all derived test instances. m_imageBuffer = de::MovePtr(new Buffer( vk, device, allocator, makeBufferCreateInfo(m_imageSizeBytes, VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible)); } tcu::TestStatus StoreTestInstance::verifyResult (void) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); const tcu::IVec3 imageSize = m_texture.size(); const tcu::TextureLevel reference = generateReferenceImage(imageSize, m_format); const Allocation& alloc = m_imageBuffer->getAllocation(); invalidateMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), m_imageSizeBytes); const tcu::ConstPixelBufferAccess result(mapVkFormat(m_format), imageSize, alloc.getHostPtr()); if (comparePixelBuffers(m_context.getTestContext().getLog(), m_texture, m_format, reference.getAccess(), result)) return tcu::TestStatus::pass("Passed"); else return tcu::TestStatus::fail("Image comparison failed"); } //! Store test for images class ImageStoreTestInstance : public StoreTestInstance { public: ImageStoreTestInstance (Context& context, const Texture& texture, const VkFormat format, const bool singleLayerBind); protected: VkDescriptorSetLayout prepareDescriptors (void); void commandBeforeCompute (const VkCommandBuffer cmdBuffer); void commandBetweenShaderInvocations (const VkCommandBuffer cmdBuffer); void commandAfterCompute (const VkCommandBuffer cmdBuffer); void commandBindDescriptorsForLayer (const VkCommandBuffer cmdBuffer, const VkPipelineLayout pipelineLayout, const int layerNdx); de::MovePtr m_image; de::MovePtr m_constantsBuffer; const VkDeviceSize m_constantsBufferChunkSizeBytes; Move m_descriptorSetLayout; Move m_descriptorPool; std::vector m_allDescriptorSets; std::vector m_allImageViews; }; ImageStoreTestInstance::ImageStoreTestInstance (Context& context, const Texture& texture, const VkFormat format, const bool singleLayerBind) : StoreTestInstance (context, texture, format, singleLayerBind) , m_constantsBufferChunkSizeBytes (getOptimalUniformBufferChunkSize(context, sizeof(deUint32))) , m_allDescriptorSets (texture.numLayers()) , m_allImageViews (texture.numLayers()) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); Allocator& allocator = m_context.getDefaultAllocator(); m_image = de::MovePtr(new Image( vk, device, allocator, makeImageCreateInfo(m_texture, m_format, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 0u), MemoryRequirement::Any)); // This buffer will be used to pass constants to the shader const int numLayers = m_texture.numLayers(); const VkDeviceSize constantsBufferSizeBytes = numLayers * m_constantsBufferChunkSizeBytes; m_constantsBuffer = de::MovePtr(new Buffer( vk, device, allocator, makeBufferCreateInfo(constantsBufferSizeBytes, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT), MemoryRequirement::HostVisible)); { const Allocation& alloc = m_constantsBuffer->getAllocation(); deUint8* const basePtr = static_cast(alloc.getHostPtr()); deMemset(alloc.getHostPtr(), 0, static_cast(constantsBufferSizeBytes)); for (int layerNdx = 0; layerNdx < numLayers; ++layerNdx) { deUint32* valuePtr = reinterpret_cast(basePtr + layerNdx * m_constantsBufferChunkSizeBytes); *valuePtr = static_cast(layerNdx); } flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), constantsBufferSizeBytes); } } VkDescriptorSetLayout ImageStoreTestInstance::prepareDescriptors (void) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); const int numLayers = m_texture.numLayers(); m_descriptorSetLayout = DescriptorSetLayoutBuilder() .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT) .addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT) .build(vk, device); m_descriptorPool = DescriptorPoolBuilder() .addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, numLayers) .addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, numLayers) .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, numLayers); if (m_singleLayerBind) { for (int layerNdx = 0; layerNdx < numLayers; ++layerNdx) { m_allDescriptorSets[layerNdx] = makeVkSharedPtr(makeDescriptorSet(vk, device, *m_descriptorPool, *m_descriptorSetLayout)); m_allImageViews[layerNdx] = makeVkSharedPtr(makeImageView( vk, device, m_image->get(), mapImageViewType(getImageTypeForSingleLayer(m_texture.type())), m_format, makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, layerNdx, 1u))); } } else // bind all layers at once { m_allDescriptorSets[0] = makeVkSharedPtr(makeDescriptorSet(vk, device, *m_descriptorPool, *m_descriptorSetLayout)); m_allImageViews[0] = makeVkSharedPtr(makeImageView( vk, device, m_image->get(), mapImageViewType(m_texture.type()), m_format, makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, numLayers))); } return *m_descriptorSetLayout; // not passing the ownership } void ImageStoreTestInstance::commandBindDescriptorsForLayer (const VkCommandBuffer cmdBuffer, const VkPipelineLayout pipelineLayout, const int layerNdx) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); const VkDescriptorSet descriptorSet = **m_allDescriptorSets[layerNdx]; const VkImageView imageView = **m_allImageViews[layerNdx]; const VkDescriptorImageInfo descriptorImageInfo = makeDescriptorImageInfo(DE_NULL, imageView, VK_IMAGE_LAYOUT_GENERAL); // Set the next chunk of the constants buffer. Each chunk begins with layer index that we've set before. const VkDescriptorBufferInfo descriptorConstantsBufferInfo = makeDescriptorBufferInfo( m_constantsBuffer->get(), layerNdx*m_constantsBufferChunkSizeBytes, m_constantsBufferChunkSizeBytes); DescriptorSetUpdateBuilder() .writeSingle(descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descriptorImageInfo) .writeSingle(descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &descriptorConstantsBufferInfo) .update(vk, device); vk.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0u, 1u, &descriptorSet, 0u, DE_NULL); } void ImageStoreTestInstance::commandBeforeCompute (const VkCommandBuffer cmdBuffer) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkImageSubresourceRange fullImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_texture.numLayers()); const VkImageMemoryBarrier setImageLayoutBarrier = makeImageMemoryBarrier( 0u, 0u, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, m_image->get(), fullImageSubresourceRange); const VkDeviceSize constantsBufferSize = m_texture.numLayers() * m_constantsBufferChunkSizeBytes; const VkBufferMemoryBarrier writeConstantsBarrier = makeBufferMemoryBarrier( VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, m_constantsBuffer->get(), 0ull, constantsBufferSize); vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &writeConstantsBarrier, 1, &setImageLayoutBarrier); } void ImageStoreTestInstance::commandBetweenShaderInvocations (const VkCommandBuffer cmdBuffer) { commandImageWriteBarrierBetweenShaderInvocations(m_context, cmdBuffer, m_image->get(), m_texture); } void ImageStoreTestInstance::commandAfterCompute (const VkCommandBuffer cmdBuffer) { commandCopyImageToBuffer(m_context, cmdBuffer, m_image->get(), m_imageBuffer->get(), m_imageSizeBytes, m_texture); } //! Store test for buffers class BufferStoreTestInstance : public StoreTestInstance { public: BufferStoreTestInstance (Context& context, const Texture& texture, const VkFormat format); protected: VkDescriptorSetLayout prepareDescriptors (void); void commandAfterCompute (const VkCommandBuffer cmdBuffer); void commandBindDescriptorsForLayer (const VkCommandBuffer cmdBuffer, const VkPipelineLayout pipelineLayout, const int layerNdx); Move m_descriptorSetLayout; Move m_descriptorPool; Move m_descriptorSet; Move m_bufferView; }; BufferStoreTestInstance::BufferStoreTestInstance (Context& context, const Texture& texture, const VkFormat format) : StoreTestInstance(context, texture, format, false) { } VkDescriptorSetLayout BufferStoreTestInstance::prepareDescriptors (void) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); m_descriptorSetLayout = DescriptorSetLayoutBuilder() .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT) .build(vk, device); m_descriptorPool = DescriptorPoolBuilder() .addType(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u); m_descriptorSet = makeDescriptorSet(vk, device, *m_descriptorPool, *m_descriptorSetLayout); m_bufferView = makeBufferView(vk, device, m_imageBuffer->get(), m_format, 0ull, m_imageSizeBytes); return *m_descriptorSetLayout; // not passing the ownership } void BufferStoreTestInstance::commandBindDescriptorsForLayer (const VkCommandBuffer cmdBuffer, const VkPipelineLayout pipelineLayout, const int layerNdx) { DE_ASSERT(layerNdx == 0); DE_UNREF(layerNdx); const VkDevice device = m_context.getDevice(); const DeviceInterface& vk = m_context.getDeviceInterface(); DescriptorSetUpdateBuilder() .writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, &m_bufferView.get()) .update(vk, device); vk.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0u, 1u, &m_descriptorSet.get(), 0u, DE_NULL); } void BufferStoreTestInstance::commandAfterCompute (const VkCommandBuffer cmdBuffer) { commandBufferWriteBarrierBeforeHostRead(m_context, cmdBuffer, m_imageBuffer->get(), m_imageSizeBytes); } class LoadStoreTest : public TestCase { public: enum TestFlags { FLAG_SINGLE_LAYER_BIND = 1 << 0, //!< Run the shader multiple times, each time binding a different layer. FLAG_RESTRICT_IMAGES = 1 << 1, //!< If given, images in the shader will be qualified with "restrict". }; LoadStoreTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const Texture& texture, const VkFormat format, const VkFormat imageFormat, const TestFlags flags = static_cast(0)); void initPrograms (SourceCollections& programCollection) const; TestInstance* createInstance (Context& context) const; private: const Texture m_texture; const VkFormat m_format; //!< Format as accessed in the shader const VkFormat m_imageFormat; //!< Storage format const bool m_singleLayerBind; const bool m_restrictImages; }; LoadStoreTest::LoadStoreTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const Texture& texture, const VkFormat format, const VkFormat imageFormat, const TestFlags flags) : TestCase (testCtx, name, description) , m_texture (texture) , m_format (format) , m_imageFormat (imageFormat) , m_singleLayerBind ((flags & FLAG_SINGLE_LAYER_BIND) != 0) , m_restrictImages ((flags & FLAG_RESTRICT_IMAGES) != 0) { if (m_singleLayerBind) DE_ASSERT(m_texture.numLayers() > 1); DE_ASSERT(formatsAreCompatible(m_format, m_imageFormat)); } void LoadStoreTest::initPrograms (SourceCollections& programCollection) const { const int dimension = (m_singleLayerBind ? m_texture.layerDimension() : m_texture.dimension()); const ImageType usedImageType = (m_singleLayerBind ? getImageTypeForSingleLayer(m_texture.type()) : m_texture.type()); const std::string formatQualifierStr = getShaderImageFormatQualifier(mapVkFormat(m_format)); const std::string imageTypeStr = getShaderImageType(mapVkFormat(m_format), usedImageType); const std::string maybeRestrictStr = (m_restrictImages ? "restrict " : ""); const std::string xMax = de::toString(m_texture.size().x() - 1); std::ostringstream src; src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_440) << "\n" << "\n" << "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n" << "layout (binding = 0, " << formatQualifierStr << ") " << maybeRestrictStr << "readonly uniform highp " << imageTypeStr << " u_image0;\n" << "layout (binding = 1, " << formatQualifierStr << ") " << maybeRestrictStr << "writeonly uniform highp " << imageTypeStr << " u_image1;\n" << "\n" << "void main (void)\n" << "{\n" << (dimension == 1 ? " int pos = int(gl_GlobalInvocationID.x);\n" " imageStore(u_image1, pos, imageLoad(u_image0, " + xMax + "-pos));\n" : dimension == 2 ? " ivec2 pos = ivec2(gl_GlobalInvocationID.xy);\n" " imageStore(u_image1, pos, imageLoad(u_image0, ivec2(" + xMax + "-pos.x, pos.y)));\n" : dimension == 3 ? " ivec3 pos = ivec3(gl_GlobalInvocationID);\n" " imageStore(u_image1, pos, imageLoad(u_image0, ivec3(" + xMax + "-pos.x, pos.y, pos.z)));\n" : "") << "}\n"; programCollection.glslSources.add("comp") << glu::ComputeSource(src.str()); } //! Load/store test base implementation class LoadStoreTestInstance : public BaseTestInstance { public: LoadStoreTestInstance (Context& context, const Texture& texture, const VkFormat format, const VkFormat imageFormat, const bool singleLayerBind); protected: virtual Buffer* getResultBuffer (void) const = 0; //!< Get the buffer that contains the result image tcu::TestStatus verifyResult (void); // Add empty implementations for functions that might be not needed void commandBeforeCompute (const VkCommandBuffer) {} void commandBetweenShaderInvocations (const VkCommandBuffer) {} void commandAfterCompute (const VkCommandBuffer) {} de::MovePtr m_imageBuffer; //!< Source data and helper buffer const VkDeviceSize m_imageSizeBytes; const VkFormat m_imageFormat; //!< Image format (for storage, may be different than texture format) tcu::TextureLevel m_referenceImage; //!< Used as input data and later to verify result image }; LoadStoreTestInstance::LoadStoreTestInstance (Context& context, const Texture& texture, const VkFormat format, const VkFormat imageFormat, const bool singleLayerBind) : BaseTestInstance (context, texture, format, singleLayerBind) , m_imageSizeBytes (getImageSizeBytes(texture.size(), format)) , m_imageFormat (imageFormat) , m_referenceImage (generateReferenceImage(texture.size(), imageFormat, format)) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); Allocator& allocator = m_context.getDefaultAllocator(); // A helper buffer with enough space to hold the whole image. m_imageBuffer = de::MovePtr(new Buffer( vk, device, allocator, makeBufferCreateInfo(m_imageSizeBytes, VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT), MemoryRequirement::HostVisible)); // Copy reference data to buffer for subsequent upload to image. const Allocation& alloc = m_imageBuffer->getAllocation(); deMemcpy(alloc.getHostPtr(), m_referenceImage.getAccess().getDataPtr(), static_cast(m_imageSizeBytes)); flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), m_imageSizeBytes); } tcu::TestStatus LoadStoreTestInstance::verifyResult (void) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); // Apply the same transformation as done in the shader const tcu::PixelBufferAccess reference = m_referenceImage.getAccess(); flipHorizontally(reference); const Allocation& alloc = getResultBuffer()->getAllocation(); invalidateMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), m_imageSizeBytes); const tcu::ConstPixelBufferAccess result(mapVkFormat(m_imageFormat), m_texture.size(), alloc.getHostPtr()); if (comparePixelBuffers(m_context.getTestContext().getLog(), m_texture, m_imageFormat, reference, result)) return tcu::TestStatus::pass("Passed"); else return tcu::TestStatus::fail("Image comparison failed"); } //! Load/store test for images class ImageLoadStoreTestInstance : public LoadStoreTestInstance { public: ImageLoadStoreTestInstance (Context& context, const Texture& texture, const VkFormat format, const VkFormat imageFormat, const bool singleLayerBind); protected: VkDescriptorSetLayout prepareDescriptors (void); void commandBeforeCompute (const VkCommandBuffer cmdBuffer); void commandBetweenShaderInvocations (const VkCommandBuffer cmdBuffer); void commandAfterCompute (const VkCommandBuffer cmdBuffer); void commandBindDescriptorsForLayer (const VkCommandBuffer cmdBuffer, const VkPipelineLayout pipelineLayout, const int layerNdx); Buffer* getResultBuffer (void) const { return m_imageBuffer.get(); } de::MovePtr m_imageSrc; de::MovePtr m_imageDst; Move m_descriptorSetLayout; Move m_descriptorPool; std::vector m_allDescriptorSets; std::vector m_allSrcImageViews; std::vector m_allDstImageViews; }; ImageLoadStoreTestInstance::ImageLoadStoreTestInstance (Context& context, const Texture& texture, const VkFormat format, const VkFormat imageFormat, const bool singleLayerBind) : LoadStoreTestInstance (context, texture, format, imageFormat, singleLayerBind) , m_allDescriptorSets (texture.numLayers()) , m_allSrcImageViews (texture.numLayers()) , m_allDstImageViews (texture.numLayers()) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); Allocator& allocator = m_context.getDefaultAllocator(); const VkImageCreateFlags imageFlags = (m_format == m_imageFormat ? 0u : (VkImageCreateFlags)VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT); m_imageSrc = de::MovePtr(new Image( vk, device, allocator, makeImageCreateInfo(m_texture, m_imageFormat, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, imageFlags), MemoryRequirement::Any)); m_imageDst = de::MovePtr(new Image( vk, device, allocator, makeImageCreateInfo(m_texture, m_imageFormat, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, imageFlags), MemoryRequirement::Any)); } VkDescriptorSetLayout ImageLoadStoreTestInstance::prepareDescriptors (void) { const VkDevice device = m_context.getDevice(); const DeviceInterface& vk = m_context.getDeviceInterface(); const int numLayers = m_texture.numLayers(); m_descriptorSetLayout = DescriptorSetLayoutBuilder() .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT) .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT) .build(vk, device); m_descriptorPool = DescriptorPoolBuilder() .addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, numLayers) .addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, numLayers) .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, numLayers); if (m_singleLayerBind) { for (int layerNdx = 0; layerNdx < numLayers; ++layerNdx) { const VkImageViewType viewType = mapImageViewType(getImageTypeForSingleLayer(m_texture.type())); const VkImageSubresourceRange subresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, layerNdx, 1u); m_allDescriptorSets[layerNdx] = makeVkSharedPtr(makeDescriptorSet(vk, device, *m_descriptorPool, *m_descriptorSetLayout)); m_allSrcImageViews[layerNdx] = makeVkSharedPtr(makeImageView(vk, device, m_imageSrc->get(), viewType, m_format, subresourceRange)); m_allDstImageViews[layerNdx] = makeVkSharedPtr(makeImageView(vk, device, m_imageDst->get(), viewType, m_format, subresourceRange)); } } else // bind all layers at once { const VkImageViewType viewType = mapImageViewType(m_texture.type()); const VkImageSubresourceRange subresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, numLayers); m_allDescriptorSets[0] = makeVkSharedPtr(makeDescriptorSet(vk, device, *m_descriptorPool, *m_descriptorSetLayout)); m_allSrcImageViews[0] = makeVkSharedPtr(makeImageView(vk, device, m_imageSrc->get(), viewType, m_format, subresourceRange)); m_allDstImageViews[0] = makeVkSharedPtr(makeImageView(vk, device, m_imageDst->get(), viewType, m_format, subresourceRange)); } return *m_descriptorSetLayout; // not passing the ownership } void ImageLoadStoreTestInstance::commandBindDescriptorsForLayer (const VkCommandBuffer cmdBuffer, const VkPipelineLayout pipelineLayout, const int layerNdx) { const VkDevice device = m_context.getDevice(); const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDescriptorSet descriptorSet = **m_allDescriptorSets[layerNdx]; const VkImageView srcImageView = **m_allSrcImageViews[layerNdx]; const VkImageView dstImageView = **m_allDstImageViews[layerNdx]; const VkDescriptorImageInfo descriptorSrcImageInfo = makeDescriptorImageInfo(DE_NULL, srcImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); const VkDescriptorImageInfo descriptorDstImageInfo = makeDescriptorImageInfo(DE_NULL, dstImageView, VK_IMAGE_LAYOUT_GENERAL); DescriptorSetUpdateBuilder() .writeSingle(descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descriptorSrcImageInfo) .writeSingle(descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &descriptorDstImageInfo) .update(vk, device); vk.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0u, 1u, &descriptorSet, 0u, DE_NULL); } void ImageLoadStoreTestInstance::commandBeforeCompute (const VkCommandBuffer cmdBuffer) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkImageSubresourceRange fullImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_texture.numLayers()); { const VkImageMemoryBarrier preCopyImageBarriers[] = { makeImageMemoryBarrier( 0u, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, m_imageSrc->get(), fullImageSubresourceRange), makeImageMemoryBarrier( 0u, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, m_imageDst->get(), fullImageSubresourceRange) }; const VkBufferMemoryBarrier barrierFlushHostWriteBeforeCopy = makeBufferMemoryBarrier( VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, m_imageBuffer->get(), 0ull, m_imageSizeBytes); vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &barrierFlushHostWriteBeforeCopy, DE_LENGTH_OF_ARRAY(preCopyImageBarriers), preCopyImageBarriers); } { const VkImageMemoryBarrier barrierAfterCopy = makeImageMemoryBarrier( VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, m_imageSrc->get(), fullImageSubresourceRange); const VkBufferImageCopy copyRegion = makeBufferImageCopy(m_texture); vk.cmdCopyBufferToImage(cmdBuffer, m_imageBuffer->get(), m_imageSrc->get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1u, ©Region); vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &barrierAfterCopy); } } void ImageLoadStoreTestInstance::commandBetweenShaderInvocations (const VkCommandBuffer cmdBuffer) { commandImageWriteBarrierBetweenShaderInvocations(m_context, cmdBuffer, m_imageDst->get(), m_texture); } void ImageLoadStoreTestInstance::commandAfterCompute (const VkCommandBuffer cmdBuffer) { commandCopyImageToBuffer(m_context, cmdBuffer, m_imageDst->get(), m_imageBuffer->get(), m_imageSizeBytes, m_texture); } //! Load/store test for buffers class BufferLoadStoreTestInstance : public LoadStoreTestInstance { public: BufferLoadStoreTestInstance (Context& context, const Texture& texture, const VkFormat format, const VkFormat imageFormat); protected: VkDescriptorSetLayout prepareDescriptors (void); void commandAfterCompute (const VkCommandBuffer cmdBuffer); void commandBindDescriptorsForLayer (const VkCommandBuffer cmdBuffer, const VkPipelineLayout pipelineLayout, const int layerNdx); Buffer* getResultBuffer (void) const { return m_imageBufferDst.get(); } de::MovePtr m_imageBufferDst; Move m_descriptorSetLayout; Move m_descriptorPool; Move m_descriptorSet; Move m_bufferViewSrc; Move m_bufferViewDst; }; BufferLoadStoreTestInstance::BufferLoadStoreTestInstance (Context& context, const Texture& texture, const VkFormat format, const VkFormat imageFormat) : LoadStoreTestInstance(context, texture, format, imageFormat, false) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); Allocator& allocator = m_context.getDefaultAllocator(); // Create a destination buffer. m_imageBufferDst = de::MovePtr(new Buffer( vk, device, allocator, makeBufferCreateInfo(m_imageSizeBytes, VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT), MemoryRequirement::HostVisible)); } VkDescriptorSetLayout BufferLoadStoreTestInstance::prepareDescriptors (void) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice device = m_context.getDevice(); m_descriptorSetLayout = DescriptorSetLayoutBuilder() .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT) .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT) .build(vk, device); m_descriptorPool = DescriptorPoolBuilder() .addType(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) .addType(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u); m_descriptorSet = makeDescriptorSet(vk, device, *m_descriptorPool, *m_descriptorSetLayout); m_bufferViewSrc = makeBufferView(vk, device, m_imageBuffer->get(), m_format, 0ull, m_imageSizeBytes); m_bufferViewDst = makeBufferView(vk, device, m_imageBufferDst->get(), m_format, 0ull, m_imageSizeBytes); return *m_descriptorSetLayout; // not passing the ownership } void BufferLoadStoreTestInstance::commandBindDescriptorsForLayer (const VkCommandBuffer cmdBuffer, const VkPipelineLayout pipelineLayout, const int layerNdx) { DE_ASSERT(layerNdx == 0); DE_UNREF(layerNdx); const VkDevice device = m_context.getDevice(); const DeviceInterface& vk = m_context.getDeviceInterface(); DescriptorSetUpdateBuilder() .writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, &m_bufferViewSrc.get()) .writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, &m_bufferViewDst.get()) .update(vk, device); vk.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0u, 1u, &m_descriptorSet.get(), 0u, DE_NULL); } void BufferLoadStoreTestInstance::commandAfterCompute (const VkCommandBuffer cmdBuffer) { commandBufferWriteBarrierBeforeHostRead(m_context, cmdBuffer, m_imageBufferDst->get(), m_imageSizeBytes); } TestInstance* StoreTest::createInstance (Context& context) const { if (m_texture.type() == IMAGE_TYPE_BUFFER) return new BufferStoreTestInstance(context, m_texture, m_format); else return new ImageStoreTestInstance(context, m_texture, m_format, m_singleLayerBind); } TestInstance* LoadStoreTest::createInstance (Context& context) const { if (m_texture.type() == IMAGE_TYPE_BUFFER) return new BufferLoadStoreTestInstance(context, m_texture, m_format, m_imageFormat); else return new ImageLoadStoreTestInstance(context, m_texture, m_format, m_imageFormat, m_singleLayerBind); } // TODO Which image/format combinations should be supported? Spec says it should be queried with vkGetPhysicalDeviceImageFormatProperties. // What about buffer/format? (texel storage buffer) (use vkGetPhysicalDeviceFormatProperties ?) static const Texture s_textures[] = { Texture(IMAGE_TYPE_1D, tcu::IVec3(64, 1, 1), 1), Texture(IMAGE_TYPE_1D_ARRAY, tcu::IVec3(64, 1, 1), 8), Texture(IMAGE_TYPE_2D, tcu::IVec3(64, 64, 1), 1), Texture(IMAGE_TYPE_2D_ARRAY, tcu::IVec3(64, 64, 1), 8), Texture(IMAGE_TYPE_3D, tcu::IVec3(64, 64, 8), 1), Texture(IMAGE_TYPE_CUBE, tcu::IVec3(64, 64, 1), 6), Texture(IMAGE_TYPE_CUBE_ARRAY, tcu::IVec3(64, 64, 1), 2*6), Texture(IMAGE_TYPE_BUFFER, tcu::IVec3(64, 1, 1), 1), }; const Texture& getTestTexture (const ImageType imageType) { for (int textureNdx = 0; textureNdx < DE_LENGTH_OF_ARRAY(s_textures); ++textureNdx) if (s_textures[textureNdx].type() == imageType) return s_textures[textureNdx]; DE_FATAL("Internal error"); return s_textures[0]; } static const VkFormat s_formats[] = { VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32G32B32A32_UINT, VK_FORMAT_R16G16B16A16_UINT, VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R32_UINT, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R16G16B16A16_SINT, VK_FORMAT_R8G8B8A8_SINT, VK_FORMAT_R32_SINT, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SNORM, }; } // anonymous ns tcu::TestCaseGroup* createImageStoreTests (tcu::TestContext& testCtx) { de::MovePtr testGroup(new tcu::TestCaseGroup(testCtx, "store", "Plain imageStore() cases")); for (int textureNdx = 0; textureNdx < DE_LENGTH_OF_ARRAY(s_textures); ++textureNdx) { const Texture& texture = s_textures[textureNdx]; de::MovePtr groupByImageViewType (new tcu::TestCaseGroup(testCtx, getImageTypeName(texture.type()).c_str(), "")); const bool isLayered = (texture.numLayers() > 1); for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(s_formats); ++formatNdx) { groupByImageViewType->addChild(new StoreTest(testCtx, getFormatCaseName(s_formats[formatNdx]), "", texture, s_formats[formatNdx])); if (isLayered) groupByImageViewType->addChild(new StoreTest(testCtx, getFormatCaseName(s_formats[formatNdx]) + "_single_layer", "", texture, s_formats[formatNdx], StoreTest::FLAG_SINGLE_LAYER_BIND)); } testGroup->addChild(groupByImageViewType.release()); } return testGroup.release(); } tcu::TestCaseGroup* createImageLoadStoreTests (tcu::TestContext& testCtx) { de::MovePtr testGroup(new tcu::TestCaseGroup(testCtx, "load_store", "Cases with imageLoad() followed by imageStore()")); for (int textureNdx = 0; textureNdx < DE_LENGTH_OF_ARRAY(s_textures); ++textureNdx) { const Texture& texture = s_textures[textureNdx]; de::MovePtr groupByImageViewType (new tcu::TestCaseGroup(testCtx, getImageTypeName(texture.type()).c_str(), "")); const bool isLayered = (texture.numLayers() > 1); for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(s_formats); ++formatNdx) { groupByImageViewType->addChild(new LoadStoreTest(testCtx, getFormatCaseName(s_formats[formatNdx]), "", texture, s_formats[formatNdx], s_formats[formatNdx])); if (isLayered) groupByImageViewType->addChild(new LoadStoreTest(testCtx, getFormatCaseName(s_formats[formatNdx]) + "_single_layer", "", texture, s_formats[formatNdx], s_formats[formatNdx], LoadStoreTest::FLAG_SINGLE_LAYER_BIND)); } testGroup->addChild(groupByImageViewType.release()); } return testGroup.release(); } tcu::TestCaseGroup* createImageFormatReinterpretTests (tcu::TestContext& testCtx) { de::MovePtr testGroup(new tcu::TestCaseGroup(testCtx, "format_reinterpret", "Cases with differing texture and image formats")); for (int textureNdx = 0; textureNdx < DE_LENGTH_OF_ARRAY(s_textures); ++textureNdx) { const Texture& texture = s_textures[textureNdx]; de::MovePtr groupByImageViewType (new tcu::TestCaseGroup(testCtx, getImageTypeName(texture.type()).c_str(), "")); for (int imageFormatNdx = 0; imageFormatNdx < DE_LENGTH_OF_ARRAY(s_formats); ++imageFormatNdx) for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(s_formats); ++formatNdx) { //TODO Are all conversions valid or do we have to limit (or expand) somehow? Is it stated anywhere in the spec? const std::string caseName = getFormatCaseName(s_formats[imageFormatNdx]) + "_" + getFormatCaseName(s_formats[formatNdx]); if (imageFormatNdx != formatNdx && formatsAreCompatible(s_formats[imageFormatNdx], s_formats[formatNdx])) groupByImageViewType->addChild(new LoadStoreTest(testCtx, caseName, "", texture, s_formats[formatNdx], s_formats[imageFormatNdx])); } testGroup->addChild(groupByImageViewType.release()); } return testGroup.release(); } de::MovePtr createImageQualifierRestrictCase (tcu::TestContext& testCtx, const ImageType imageType, const std::string& name) { const VkFormat format = VK_FORMAT_R32G32B32A32_UINT; const Texture& texture = getTestTexture(imageType); return de::MovePtr(new LoadStoreTest(testCtx, name, "", texture, format, format, LoadStoreTest::FLAG_RESTRICT_IMAGES)); } } // image } // vkt