/*------------------------------------------------------------------------ * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2021 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 OpImageWrite tests. *//*--------------------------------------------------------------------*/ #include "vktImageMismatchedWriteOpTests.hpp" #include "vktImageTexture.hpp" #include "vkImageUtil.hpp" #include "vkBuilderUtil.hpp" #include "vkObjUtil.hpp" #include "vktImageTestsUtil.hpp" #include "vkQueryUtil.hpp" #include "vkCmdUtil.hpp" #include "vkBarrierUtil.hpp" #include "tcuStringTemplate.hpp" #include "tcuTexture.hpp" #include "tcuTextureUtil.hpp" #include #define EPSILON_COMPARE(a,b,e) ((de::max((a),(b))-de::min((a),(b)))<=(e)) using namespace vk; namespace vkt { namespace image { namespace { using tcu::TextureFormat; using tcu::StringTemplate; using tcu::TextureChannelClass; using strings = std::map; class MismatchedWriteOpTest : public TestCase { public: struct Params { VkFormat vkFormat; int textureWidth; int textureHeight; VkFormat spirvFormat; }; typedef de::SharedPtr ParamsSp; MismatchedWriteOpTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const ParamsSp params) : TestCase (testCtx, name, description) , m_params (params) { } virtual void checkSupport (Context& context) const override; virtual TextureFormat getBufferFormat (void) const; void getProgramCodeAndVariables (StringTemplate& code, strings& variables) const; template void getParams(TestParams&); protected: const ParamsSp m_params; }; class MismatchedVectorSizesTest : public MismatchedWriteOpTest { public: MismatchedVectorSizesTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const ParamsSp params, const int sourceWidth) : MismatchedWriteOpTest (testCtx, name, description, params) , m_sourceWidth (sourceWidth) { DE_ASSERT(getNumUsedChannels(params->vkFormat) <= sourceWidth); } virtual void initPrograms (SourceCollections& programCollection) const override; virtual TestInstance* createInstance (Context& context) const override; private: const int m_sourceWidth; }; class MismatchedSignednessAndTypeTest : public MismatchedWriteOpTest { public: MismatchedSignednessAndTypeTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const ParamsSp params) : MismatchedWriteOpTest (testCtx, name, description, params) { } virtual void initPrograms (SourceCollections& programCollection) const override; virtual TestInstance* createInstance (Context& context) const override; }; class MismatchedWriteOpTestInstance : public TestInstance { public: using TestClass = MismatchedWriteOpTest; using ParamsSp = MismatchedWriteOpTest::ParamsSp; MismatchedWriteOpTestInstance (Context& context, const ParamsSp params, const TestClass* test) : TestInstance (context) , m_params (params) , m_test (test) { } virtual tcu::TestStatus iterate (void) override; virtual void clear (tcu::PixelBufferAccess& data) const; virtual void populate (tcu::PixelBufferAccess& data) const; virtual bool compare (tcu::PixelBufferAccess& result, tcu::PixelBufferAccess& reference) const = 0; protected: const ParamsSp m_params; const TestClass* m_test; }; class MismatchedVectorSizesTestInstance : public MismatchedWriteOpTestInstance { public: MismatchedVectorSizesTestInstance (Context& context, const ParamsSp params, const TestClass* test) : MismatchedWriteOpTestInstance (context, params, test) { } bool compare (tcu::PixelBufferAccess& result, tcu::PixelBufferAccess& reference) const override; }; class MismatchedSignednessAndTypeTestInstance : public MismatchedWriteOpTestInstance { public: MismatchedSignednessAndTypeTestInstance (Context& context, const ParamsSp params, const TestClass* test) : MismatchedWriteOpTestInstance (context, params, test) { } bool compare (tcu::PixelBufferAccess& result, tcu::PixelBufferAccess& reference) const override; }; namespace ut { class StorageBuffer2D { public: StorageBuffer2D (Context& context, const tcu::TextureFormat& format, deUint32 width, deUint32 height); VkBuffer getBuffer (void) const { return *m_buffer; } VkDeviceSize getSize (void) const { return m_bufferSize; } tcu::PixelBufferAccess& getPixelAccess (void) { return m_access[0]; } void flush (void) { flushAlloc(m_context.getDeviceInterface(), m_context.getDevice(), *m_bufferMemory); } void invalidate (void) { invalidateAlloc(m_context.getDeviceInterface(), m_context.getDevice(), *m_bufferMemory); } protected: friend class StorageImage2D; Allocation& getMemory (void) const { return *m_bufferMemory; } private: Context& m_context; const tcu::TextureFormat m_format; const deUint32 m_width; const deUint32 m_height; const VkDeviceSize m_bufferSize; Move m_buffer; de::MovePtr m_bufferMemory; std::vector m_access; }; class StorageImage2D { public: StorageImage2D (Context& context, VkFormat vkFormat, const int width, const int height, bool sparse = false); VkImageView getView (void) const { return *m_view; } tcu::PixelBufferAccess& getPixelAccess (void) { return m_buffer.getPixelAccess(); } void flush (void) { m_buffer.flush(); } void invalidate (void) { m_buffer.invalidate(); } void upload (const VkCommandBuffer cmdBuffer); void download (const VkCommandBuffer cmdBuffer); private: Context& m_context; const bool m_sparse; const int m_width; const int m_height; const vk::VkFormat m_vkFormat; const tcu::TextureFormat m_texFormat; StorageBuffer2D m_buffer; VkImageLayout m_layout; Move m_image; Move m_view; Move m_semaphore; std::vector> m_allocations; de::MovePtr m_imageMemory; }; StorageImage2D::StorageImage2D (Context& context, VkFormat vkFormat, const int width, const int height, bool sparse) : m_context (context) , m_sparse (sparse) , m_width (width) , m_height (height) , m_vkFormat (vkFormat) , m_texFormat (mapVkFormat(m_vkFormat)) , m_buffer (m_context, m_texFormat, m_width, m_height) { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkDevice dev = m_context.getDevice(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); Allocator& allocator = m_context.getDefaultAllocator(); // Create an image { VkImageCreateFlags imageCreateFlags = m_sparse? (VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT) : 0u; VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; const VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; imageCreateFlags, // VkImageCreateFlags flags; VK_IMAGE_TYPE_2D, // VkImageType imageType; m_vkFormat, // VkFormat format; { deUint32(m_width), deUint32(m_height), 1u }, // VkExtent3D extent; 1u, // deUint32 mipLevels; 1u, // deUint32 arrayLayers; VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; imageUsageFlags, // VkImageUsageFlags usage; VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 1u, // deUint32 queueFamilyIndexCount; &queueFamilyIndex, // const deUint32* pQueueFamilyIndices; (m_layout = VK_IMAGE_LAYOUT_UNDEFINED) // VkImageLayout initialLayout; }; m_image = createImage(vki, dev, &imageCreateInfo); if (m_sparse) { m_semaphore = createSemaphore(vki, dev); #ifndef CTS_USES_VULKANSC allocateAndBindSparseImage( vki, dev, m_context.getPhysicalDevice(), m_context.getInstanceInterface(), imageCreateInfo, *m_semaphore, m_context.getSparseQueue(), allocator, m_allocations, mapVkFormat(m_vkFormat), *m_image ); #endif // CTS_USES_VULKANSC } else { m_imageMemory = allocator.allocate(getImageMemoryRequirements(vki, dev, *m_image), MemoryRequirement::Any); VK_CHECK(vki.bindImageMemory(dev, *m_image, m_imageMemory->getMemory(), m_imageMemory->getOffset())); } VkImageSubresourceRange subresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u); m_view = makeImageView(vki, dev, *m_image, VK_IMAGE_VIEW_TYPE_2D, m_vkFormat, subresourceRange); } } void StorageImage2D::upload (const VkCommandBuffer cmdBuffer) { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkImageSubresourceRange fullImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u); const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(tcu::IVec3(m_width, m_height, 1)), 1u); { const VkBufferMemoryBarrier bufferBarrier = makeBufferMemoryBarrier( VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, m_buffer.getBuffer(), 0ull, m_buffer.getSize()); const VkImageMemoryBarrier beforeCopyBarrier = makeImageMemoryBarrier( (VkAccessFlagBits)0, VK_ACCESS_TRANSFER_WRITE_BIT, m_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, *m_image, fullImageSubresourceRange); vki.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &bufferBarrier, 1, &beforeCopyBarrier); } vki.cmdCopyBufferToImage(cmdBuffer, m_buffer.getBuffer(), *m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1u, ©Region); { const VkBufferMemoryBarrier bufferBarrier = makeBufferMemoryBarrier( VK_ACCESS_TRANSFER_READ_BIT, (VkAccessFlags)0, m_buffer.getBuffer(), 0ull, m_buffer.getSize()); m_layout = VK_IMAGE_LAYOUT_GENERAL; const VkImageMemoryBarrier afterCopyBarrier = makeImageMemoryBarrier( VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, m_layout, *m_image, fullImageSubresourceRange); vki.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &bufferBarrier, 1, &afterCopyBarrier); } } void StorageImage2D::download (const VkCommandBuffer cmdBuffer) { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkImageSubresourceRange fullImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u); const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(tcu::IVec3(m_width, m_height, 1)), 1u); { const VkBufferMemoryBarrier bufferBarrier = makeBufferMemoryBarrier( (VkAccessFlags)0, VK_ACCESS_TRANSFER_WRITE_BIT, m_buffer.getBuffer(), 0ull, m_buffer.getSize()); const VkImageMemoryBarrier beforeCopyBarrier = makeImageMemoryBarrier( VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, m_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *m_image, fullImageSubresourceRange); vki.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &bufferBarrier, 1, &beforeCopyBarrier); } vki.cmdCopyImageToBuffer(cmdBuffer, *m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer.getBuffer(), 1, ©Region); { const VkBufferMemoryBarrier bufferBarrier = makeBufferMemoryBarrier( VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, m_buffer.getBuffer(), 0ull, m_buffer.getSize()); const VkImageMemoryBarrier afterCopyBarrier = makeImageMemoryBarrier( VK_ACCESS_TRANSFER_READ_BIT, (VkAccessFlags)0, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_layout, *m_image, fullImageSubresourceRange); vki.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &bufferBarrier, 1, &afterCopyBarrier); } } StorageBuffer2D::StorageBuffer2D (Context& context, const tcu::TextureFormat& format, deUint32 width, deUint32 height) : m_context (context) , m_format (format) , m_width (width) , m_height (height) , m_bufferSize (m_width * m_height * m_format.getPixelSize()) { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkDevice dev = m_context.getDevice(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); Allocator& allocator = m_context.getDefaultAllocator(); const VkBufferUsageFlags bufferUsageFlags = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; const VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkBufferCreateFlags flags; m_bufferSize, // VkDeviceSize size; bufferUsageFlags, // VkBufferUsageFlags usage; VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 1u, // deUint32 queueFamilyIndexCount; &queueFamilyIndex // const deUint32* pQueueFamilyIndices; }; m_buffer = createBuffer(vki, dev, &bufferCreateInfo); m_bufferMemory = allocator.allocate(getBufferMemoryRequirements(vki, dev, *m_buffer), MemoryRequirement::HostVisible); VK_CHECK(vki.bindBufferMemory(dev, *m_buffer, m_bufferMemory->getMemory(), m_bufferMemory->getOffset())); m_access.emplace_back(m_format, tcu::IVec3(m_width, m_height, 1), m_bufferMemory->getHostPtr()); } tcu::Vec4 gluePixels (const tcu::Vec4& a, const tcu::Vec4& b, const int pivot) { tcu::Vec4 result; for (int i = 0; i < pivot; ++i) result[i] = a[i]; for (int i = pivot; i < 4; ++i) result[i] = b[i]; return result; } template bool comparePixels (const tcu::Vector& res, const tcu::Vector& ref, const int targetWidth, const T eps = {}) { bool ok = true; for (int i = 0; ok && i < targetWidth; ++i) { ok &= EPSILON_COMPARE(res[i], ref[i], eps); } return ok; } } // ut TestInstance* MismatchedVectorSizesTest::createInstance (Context& context) const { return (new MismatchedVectorSizesTestInstance(context, m_params, this)); } TestInstance* MismatchedSignednessAndTypeTest::createInstance (Context& context) const { return (new MismatchedSignednessAndTypeTestInstance(context, m_params, this)); } const VkFormat allFormats[] = { VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_R16G16_UNORM, VK_FORMAT_R16_UNORM, VK_FORMAT_R16G16B16A16_SNORM, VK_FORMAT_R16G16_SNORM, VK_FORMAT_R16_SNORM, VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_FORMAT_B10G11R11_UFLOAT_PACK32, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8_UNORM, VK_FORMAT_R8_UNORM, VK_FORMAT_R8G8B8A8_SNORM, VK_FORMAT_R8G8_SNORM, VK_FORMAT_R8_SNORM, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32G32_SINT, VK_FORMAT_R32_SINT, VK_FORMAT_R16G16B16A16_SINT, VK_FORMAT_R16G16_SINT, VK_FORMAT_R16_SINT, VK_FORMAT_R8G8B8A8_SINT, VK_FORMAT_R8G8_SINT, VK_FORMAT_R8_SINT, VK_FORMAT_R32G32B32A32_UINT, VK_FORMAT_R32G32_UINT, VK_FORMAT_R32_UINT, VK_FORMAT_R16G16B16A16_UINT, VK_FORMAT_R16G16_UINT, VK_FORMAT_R16_UINT, VK_FORMAT_A2B10G10R10_UINT_PACK32, VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R8G8_UINT, VK_FORMAT_R8_UINT, VK_FORMAT_R64_SINT, VK_FORMAT_R64_UINT, }; std::vector findFormatsByChannelClass(TextureChannelClass channelClass) { std::vector result; for (const VkFormat& f : allFormats) { if (getTextureChannelClass(mapVkFormat(f).type) == channelClass) result.emplace_back(f); } DE_ASSERT(!result.empty()); return result; } const char* getChannelStr (const TextureFormat::ChannelType& type) { switch (type) { case TextureFormat::FLOAT: return "float"; case TextureFormat::SIGNED_INT32: return "sint"; case TextureFormat::UNSIGNED_INT32: return "uint"; case TextureFormat::FLOAT64: return "double"; case TextureFormat::SIGNED_INT64: return "slong"; case TextureFormat::UNSIGNED_INT64: return "ulong"; default: DE_ASSERT(DE_FALSE); } return nullptr; } TextureFormat::ChannelType makeChannelType (tcu::TextureChannelClass channelClass, bool doubled) { auto channelType = TextureFormat::ChannelType::CHANNELTYPE_LAST; switch (channelClass) { case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: channelType = doubled ? TextureFormat::ChannelType::SIGNED_INT64 : TextureFormat::ChannelType::SIGNED_INT32; break; case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: channelType = doubled ? TextureFormat::ChannelType::UNSIGNED_INT64 : TextureFormat::ChannelType::UNSIGNED_INT32; break; default: channelType = doubled ? TextureFormat::ChannelType::FLOAT64 : TextureFormat::ChannelType::FLOAT; } return channelType; } TextureFormat makeBufferFormat (tcu::TextureChannelClass channelClass, bool doubled) { return TextureFormat(TextureFormat::ChannelOrder::RGBA, makeChannelType(channelClass, doubled)); } void MismatchedWriteOpTest::checkSupport (Context& context) const { // capabilities that may be used in the shader if (is64BitIntegerFormat(m_params->vkFormat)) { const VkPhysicalDeviceFeatures deviceFeatures = getPhysicalDeviceFeatures(context.getInstanceInterface(), context.getPhysicalDevice()); if(!deviceFeatures.shaderInt64) { TCU_THROW(NotSupportedError, "Device feature shaderInt64 is not supported"); } context.requireDeviceFunctionality("VK_EXT_shader_image_atomic_int64"); } // extensions used statically in the shader context.requireDeviceFunctionality("VK_KHR_variable_pointers"); context.requireDeviceFunctionality("VK_KHR_storage_buffer_storage_class"); VkFormatProperties formatProperties = getPhysicalDeviceFormatProperties(context.getInstanceInterface(), context.getPhysicalDevice(), m_params->vkFormat); if ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) == 0) { TCU_THROW(NotSupportedError, "Creating storage image with this format is not supported"); } } TextureFormat MismatchedWriteOpTest::getBufferFormat (void) const { const TextureFormat texFormat = mapVkFormat(m_params->vkFormat); return makeBufferFormat(getTextureChannelClass(texFormat.type), is64BitIntegerFormat(m_params->vkFormat)); } void MismatchedWriteOpTest::getProgramCodeAndVariables (StringTemplate& code, strings& variables) const { std::string shaderTemplate(R"( OpCapability Shader OpCapability StorageImageExtendedFormats ${CAPABILITY_INT64} OpExtension "SPV_KHR_variable_pointers" OpExtension "SPV_KHR_storage_buffer_storage_class" ${EXTENSIONS} %std450 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %main "main" %gid %image %buffer OpExecutionMode %main LocalSize 1 1 1 OpDecorate %gid BuiltIn GlobalInvocationId OpDecorate %image DescriptorSet 0 OpDecorate %image Binding 0 OpDecorate %rta ArrayStride ${ARRAY_STRIDE} OpMemberDecorate %struct 0 Offset 0 OpDecorate %struct Block OpDecorate %buffer DescriptorSet 0 OpDecorate %buffer Binding 1 %void = OpTypeVoid %fn_void = OpTypeFunction %void ${TYPES_INT64} %float = OpTypeFloat 32 %sint = OpTypeInt 32 1 %uint = OpTypeInt 32 0 %v4float = OpTypeVector %float 4 %v3float = OpTypeVector %float 3 %v2float = OpTypeVector %float 2 %v4sint = OpTypeVector %sint 4 %v3sint = OpTypeVector %sint 3 %v2sint = OpTypeVector %sint 2 %v4uint = OpTypeVector %uint 4 %v3uint = OpTypeVector %uint 3 %v2uint = OpTypeVector %uint 2 %v3uint_in_ptr = OpTypePointer Input %v3uint %gid = OpVariable %v3uint_in_ptr Input %image_type = OpTypeImage %${SAMPLED_TYPE} 2D 0 0 0 2 ${SPIRV_IMAGE_FORMAT} %image_ptr = OpTypePointer UniformConstant %image_type %image = OpVariable %image_ptr UniformConstant %image_width = OpConstant %sint ${IMAGE_WIDTH} %image_height = OpConstant %sint ${IMAGE_HEIGHT} %rta_offset = OpConstant %uint 0 %rta = OpTypeRuntimeArray %v4${SAMPLED_TYPE} %struct = OpTypeStruct %rta %ssbo_ptr = OpTypePointer StorageBuffer %struct %buffer = OpVariable %ssbo_ptr StorageBuffer %red_offset = OpConstant %uint 0 %green_offset = OpConstant %uint 1 %blue_offset = OpConstant %uint 2 %alpha_offset = OpConstant %uint 3 %${SAMPLED_TYPE}_PTR = OpTypePointer StorageBuffer %${SAMPLED_TYPE} %var_sint_ptr = OpTypePointer Function %sint ; Entry main procedure %main = OpFunction %void None %fn_void %entry = OpLabel %index = OpVariable %var_sint_ptr Function ; Transform gl_GlobalInvocationID.xyz to ivec2(gl_GlobalInvocationID.xy) %id = OpLoad %v3uint %gid %u_id_x = OpCompositeExtract %uint %id 0 %s_id_x = OpBitcast %sint %u_id_x %u_id_y = OpCompositeExtract %uint %id 1 %s_id_y = OpBitcast %sint %u_id_y %id_xy = OpCompositeConstruct %v2sint %s_id_x %s_id_y ; Calculate index in buffer %mul = OpIMul %sint %s_id_y %image_width %add = OpIAdd %sint %mul %s_id_x OpStore %index %add ; Final image variable used to read from or write to %img = OpLoad %image_type %image ; Accessors to buffer components %idx = OpLoad %sint %index %alpha_access = OpAccessChain %${SAMPLED_TYPE}_PTR %buffer %rta_offset %idx %alpha_offset %blue_access = OpAccessChain %${SAMPLED_TYPE}_PTR %buffer %rta_offset %idx %blue_offset %green_access = OpAccessChain %${SAMPLED_TYPE}_PTR %buffer %rta_offset %idx %green_offset %red_access = OpAccessChain %${SAMPLED_TYPE}_PTR %buffer %rta_offset %idx %red_offset %red = OpLoad %${SAMPLED_TYPE} %red_access %green = OpLoad %${SAMPLED_TYPE} %green_access %blue = OpLoad %${SAMPLED_TYPE} %blue_access %alpha = OpLoad %${SAMPLED_TYPE} %alpha_access ${WRITE_TO_IMAGE} OpReturn OpFunctionEnd )"); const std::string typesInt64(R"( %slong = OpTypeInt 64 1 %ulong = OpTypeInt 64 0 %v4slong = OpTypeVector %slong 4 %v3slong = OpTypeVector %slong 3 %v2slong = OpTypeVector %slong 2 %v4ulong = OpTypeVector %ulong 4 %v3ulong = OpTypeVector %ulong 3 %v2ulong = OpTypeVector %ulong 2 )"); const tcu::TextureFormat buffFormat = getBufferFormat(); variables["SPIRV_IMAGE_FORMAT"] = getSpirvFormat(m_params->vkFormat); variables["CAPABILITY_INT64"] = ""; variables["EXTENSIONS"] = ""; variables["TYPES_INT64"] = ""; if (is64BitIntegerFormat(m_params->vkFormat)) { variables["EXTENSIONS"] = "OpExtension \"SPV_EXT_shader_image_int64\""; variables["CAPABILITY_INT64"] = "OpCapability Int64ImageEXT\n" "OpCapability Int64"; variables["TYPES_INT64"] = typesInt64; } variables["SAMPLED_TYPE"] = getChannelStr(buffFormat.type); variables["IMAGE_WIDTH"] = std::to_string(m_params->textureWidth); variables["IMAGE_HEIGHT"] = std::to_string(m_params->textureHeight); variables["ARRAY_STRIDE"] = std::to_string(tcu::getChannelSize(buffFormat.type) * tcu::getNumUsedChannels(buffFormat.order)); code.setString(shaderTemplate); } void MismatchedVectorSizesTest::initPrograms (SourceCollections& programCollection) const { strings variables {}; StringTemplate shaderTemplate {}; const StringTemplate writeFromSingleComponent (R"( OpImageWrite %img %id_xy %red )"); const StringTemplate writeFromTwoComponents (R"( %rg = OpCompositeConstruct %v2${SAMPLED_TYPE} %red %green OpImageWrite %img %id_xy %rg )"); const StringTemplate writeFromThreeComponents (R"( %rgb = OpCompositeConstruct %v3${SAMPLED_TYPE} %red %green %blue OpImageWrite %img %id_xy %rgb )"); const StringTemplate writeFromFourComponents (R"( %rgba = OpCompositeConstruct %v4${SAMPLED_TYPE} %red %green %blue %alpha OpImageWrite %img %id_xy %rgba )"); getProgramCodeAndVariables(shaderTemplate, variables); variables["WRITE_TO_IMAGE"] = (m_sourceWidth == 1 ? writeFromSingleComponent : m_sourceWidth == 2 ? writeFromTwoComponents : m_sourceWidth == 3 ? writeFromThreeComponents : writeFromFourComponents).specialize(variables); programCollection.spirvAsmSources.add("comp") << shaderTemplate.specialize(variables) << vk::SpirVAsmBuildOptions(programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, true); } void MismatchedSignednessAndTypeTest::initPrograms (SourceCollections& programCollection) const { strings variables {}; StringTemplate shaderTemplate {}; const StringTemplate writeToImage (R"( %color = OpCompositeConstruct %v4${SAMPLED_TYPE} %red %green %blue %alpha OpImageWrite %img %id_xy %color )"); getProgramCodeAndVariables(shaderTemplate, variables); variables["WRITE_TO_IMAGE"] = writeToImage.specialize(variables); programCollection.spirvAsmSources.add("comp") << shaderTemplate.specialize(variables) << vk::SpirVAsmBuildOptions(programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, true); } void MismatchedWriteOpTestInstance::clear (tcu::PixelBufferAccess& pixels) const { const auto channelClass = tcu::getTextureChannelClass(mapVkFormat(m_params->vkFormat).type); switch (channelClass) { case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: tcu::clear(pixels, tcu::IVec4(-1, -2, -3, -4)); break; case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: tcu::clear(pixels, tcu::UVec4(1, 2, 3, 4)); break; default: tcu::clear(pixels, tcu::Vec4(0.2f, 0.3f, 0.4f, 0.5f)); } } void MismatchedWriteOpTestInstance::populate (tcu::PixelBufferAccess& pixels) const { const auto texFormat = mapVkFormat(m_params->vkFormat); const auto bitDepth = tcu::getTextureFormatBitDepth(texFormat); const auto channelClass = tcu::getTextureChannelClass(texFormat.type); const tcu::IVec4 signedMinValues (bitDepth[0] ? deIntMinValue32(deMin32(bitDepth[0], 32)) : (-1), bitDepth[1] ? deIntMinValue32(deMin32(bitDepth[1], 32)) : (-1), bitDepth[2] ? deIntMinValue32(deMin32(bitDepth[2], 32)) : (-1), bitDepth[3] ? deIntMinValue32(deMin32(bitDepth[3], 32)) : (-1)); const tcu::IVec4 signedMaxValues (bitDepth[0] ? deIntMaxValue32(deMin32(bitDepth[0], 32)) : 1, bitDepth[1] ? deIntMaxValue32(deMin32(bitDepth[1], 32)) : 1, bitDepth[2] ? deIntMaxValue32(deMin32(bitDepth[2], 32)) : 1, bitDepth[3] ? deIntMaxValue32(deMin32(bitDepth[3], 32)) : 1); const tcu::UVec4 unsignedMinValues (0u); const tcu::UVec4 unsignedMaxValues (bitDepth[0] ? deUintMaxValue32(deMin32(bitDepth[0], 32)) : 1u, bitDepth[1] ? deUintMaxValue32(deMin32(bitDepth[1], 32)) : 1u, bitDepth[2] ? deUintMaxValue32(deMin32(bitDepth[2], 32)) : 1u, bitDepth[3] ? deUintMaxValue32(deMin32(bitDepth[3], 32)) : 1u); auto nextSigned = [&](tcu::IVec4& color) { color[0] = (static_cast(color[0] + 2) < signedMaxValues[0]) ? (color[0] + 2) : signedMinValues[0]; color[1] = (static_cast(color[1] + 3) < signedMaxValues[1]) ? (color[1] + 3) : signedMinValues[1]; color[2] = (static_cast(color[2] + 5) < signedMaxValues[2]) ? (color[2] + 5) : signedMinValues[2]; color[3] = (static_cast(color[3] + 7) < signedMaxValues[3]) ? (color[3] + 7) : signedMinValues[3]; }; auto nextUnsigned = [&](tcu::UVec4& color) { color[0] = (static_cast(color[0] + 2) < unsignedMaxValues[0]) ? (color[0] + 2) : unsignedMinValues[0]; color[1] = (static_cast(color[1] + 3) < unsignedMaxValues[1]) ? (color[1] + 3) : unsignedMinValues[1]; color[2] = (static_cast(color[2] + 5) < unsignedMaxValues[2]) ? (color[2] + 5) : unsignedMinValues[2]; color[3] = (static_cast(color[3] + 7) < unsignedMaxValues[3]) ? (color[3] + 7) : unsignedMinValues[3]; }; deUint64 floatsData [4]; tcu::PixelBufferAccess floatsAccess (texFormat, 1, 1, 1, floatsData); tcu::Vec4 tmpFloats (0.0f); const float divider (static_cast(m_params->textureHeight)); const tcu::Vec4 ufloatStep (1.0f/(divider*1.0f), 1.0f/(divider*2.0f), 1.0f/(divider*3.0f), 1.0f/(divider*5.0f)); const tcu::Vec4 sfloatStep (2.0f/(divider*1.0f), 2.0f/(divider*2.0f), 2.0f/(divider*3.0f), 2.0f/(divider*5.0f)); tcu::IVec4 signedColor (0); tcu::UVec4 unsignedColor (0u); tcu::Vec4 ufloatColor (0.0f); tcu::Vec4 sfloatColor (-1.0f); for (int y = 0; y < m_params->textureHeight; ++y) { for (int x = 0; x < m_params->textureWidth; ++x) { switch (channelClass) { case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: pixels.setPixel(signedColor, x, y); break; case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: pixels.setPixel(unsignedColor, x, y); break; case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: floatsAccess.setPixel(sfloatColor, 0, 0); tmpFloats = ut::gluePixels(floatsAccess.getPixel(0, 0), sfloatColor, tcu::getNumUsedChannels(texFormat.order)); pixels.setPixel(tmpFloats, x, y); break; // TEXTURECHANNELCLASS_FLOATING_POINT or // TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT default: floatsAccess.setPixel(ufloatColor, 0, 0); tmpFloats = ut::gluePixels(floatsAccess.getPixel(0, 0), ufloatColor, tcu::getNumUsedChannels(texFormat.order)); pixels.setPixel(tmpFloats, x, y); break; } } nextSigned (signedColor); nextUnsigned (unsignedColor); sfloatColor += sfloatStep; ufloatColor += ufloatStep; } } tcu::TestStatus MismatchedWriteOpTestInstance::iterate (void) { const DeviceInterface& vki = m_context.getDeviceInterface(); const VkDevice dev = m_context.getDevice(); const VkQueue queue = m_context.getUniversalQueue(); const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); Move cmdPool = createCommandPool(vki, dev, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, queueFamilyIndex); Move cmdBuffer = allocateCommandBuffer(vki, dev, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY); Move shaderModule = createShaderModule(vki, dev, m_context.getBinaryCollection().get("comp"), 0); Move descriptorSetLayout = DescriptorSetLayoutBuilder() .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT) .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT) .build(vki, dev); Move descriptorPool = DescriptorPoolBuilder() .addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) .build(vki, dev, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u); Move descriptorSet = makeDescriptorSet(vki, dev, *descriptorPool, *descriptorSetLayout); Move pipelineLayout = makePipelineLayout(vki, dev, *descriptorSetLayout); Move pipeline = makeComputePipeline(vki, dev, *pipelineLayout, *shaderModule); ut::StorageImage2D image (m_context, m_params->vkFormat, m_params->textureWidth, m_params->textureHeight); ut::StorageBuffer2D buffer (m_context, m_test->getBufferFormat(), m_params->textureWidth, m_params->textureHeight); VkDescriptorImageInfo inputImageInfo = makeDescriptorImageInfo(DE_NULL, image.getView(), VK_IMAGE_LAYOUT_GENERAL); VkDescriptorBufferInfo outputBufferInfo = makeDescriptorBufferInfo(buffer.getBuffer(), 0u, buffer.getSize()); DescriptorSetUpdateBuilder builder; builder .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &inputImageInfo) .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &outputBufferInfo) .update(vki, dev); populate (buffer.getPixelAccess()); clear (image.getPixelAccess()); beginCommandBuffer(vki, *cmdBuffer); image.upload(*cmdBuffer); vki.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); vki.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL); vki.cmdDispatch(*cmdBuffer, m_params->textureWidth, m_params->textureHeight, 1); image.download(*cmdBuffer); endCommandBuffer(vki, *cmdBuffer); image.flush(); buffer.flush(); submitCommandsAndWait(vki, dev, queue, *cmdBuffer); image.invalidate(); buffer.invalidate(); return compare(image.getPixelAccess(), buffer.getPixelAccess()) ? tcu::TestStatus::pass("") : tcu::TestStatus::fail("Pixel comparison failed"); } bool MismatchedVectorSizesTestInstance::compare (tcu::PixelBufferAccess& result, tcu::PixelBufferAccess& reference) const { const tcu::TextureFormat texFormat = mapVkFormat(m_params->vkFormat); const tcu::TextureChannelClass channelClass = tcu::getTextureChannelClass(texFormat.type); const int targetWidth = getNumUsedChannels(texFormat.order); bool doContinue = true; for (int y = 0; doContinue && y < m_params->textureHeight; ++y) { for (int x = 0; doContinue && x < m_params->textureWidth; ++x) { switch (channelClass) { case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: doContinue = ut::comparePixels(result.getPixelInt(x,y), reference.getPixelInt(x,y), targetWidth ); break; case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: doContinue = ut::comparePixels(result.getPixelUint(x,y), reference.getPixelUint(x,y), targetWidth ); break; default: doContinue = ut::comparePixels(result.getPixel(x,y), reference.getPixel(x,y), targetWidth, 0.0005f); break; } } } return doContinue; } bool MismatchedSignednessAndTypeTestInstance::compare (tcu::PixelBufferAccess& result, tcu::PixelBufferAccess& reference) const { DE_UNREF(result); DE_UNREF(reference); return true; } } // anonymous tcu::TestCaseGroup* createImageWriteOpTests (tcu::TestContext& testCtx) { std::stringstream ss; auto genVectorSizesTestName = [&](const VkFormat& f, const int sourceWidth) -> std::string { ss.str(std::string()); ss << de::toLower(getSpirvFormat(f)) << "_from"; if (sourceWidth > 1) ss << "_vec" << sourceWidth; else ss << "_scalar"; return ss.str(); }; auto testGroup = new tcu::TestCaseGroup(testCtx, "mismatched_write_op", "Test image OpImageWrite operation in various aspects."); auto testGroupMismatchedVectorSizes = new tcu::TestCaseGroup(testCtx, "mismatched_vector_sizes", "Case OpImageWrite operation on mismatched vector sizes."); auto testGroupMismatchedSignedness = new tcu::TestCaseGroup(testCtx, "mismatched_signedness_and_type", "Case OpImageWrite operation on mismatched signedness and values."); for (const VkFormat& f : allFormats) { const auto switchClass = getTextureChannelClass(mapVkFormat(f).type); auto compatibleFormats = findFormatsByChannelClass(switchClass); auto end = compatibleFormats.cend(); auto begin = compatibleFormats.cbegin(); for (auto i = begin; i != end; ++i) { if (is64BitIntegerFormat(i[0]) || is64BitIntegerFormat(f)) continue; const std::string testName = de::toLower(getSpirvFormat(i[0])) + "_from_" + de::toLower(getSpirvFormat(f)); auto params = new MismatchedWriteOpTest::Params { f, 12, 8*static_cast(std::distance(begin,i)+1), i[0] }; testGroupMismatchedSignedness->addChild(new MismatchedSignednessAndTypeTest(testCtx, testName, {}, MismatchedVectorSizesTest::ParamsSp(params))); } for (int sourceWidth = 4; sourceWidth > 0; --sourceWidth) { if (sourceWidth >= getNumUsedChannels(f)) { auto params = new MismatchedWriteOpTest::Params { f, 12*sourceWidth, 8*(4-sourceWidth+1), f }; testGroupMismatchedVectorSizes->addChild( new MismatchedVectorSizesTest(testCtx, genVectorSizesTestName(f, sourceWidth), {}, MismatchedVectorSizesTest::ParamsSp(params), sourceWidth)); } } } testGroup->addChild(testGroupMismatchedVectorSizes); testGroup->addChild(testGroupMismatchedSignedness); return testGroup; } } // image } // vkt