blob: b6ad82b0527c2a06b1605d5be239368781a587fd [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2020 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 Testing writing and reading for mismatched formats
*//*--------------------------------------------------------------------*/
#include "vktImageLoadStoreTests.hpp"
#include "vktTestCaseUtil.hpp"
#include "vktImageTestsUtil.hpp"
#include "vktImageLoadStoreUtil.hpp"
#include "vktImageTexture.hpp"
#include "vkDefs.hpp"
#include "vkRef.hpp"
#include "vkRefUtil.hpp"
#include "vkPlatform.hpp"
#include "vkPrograms.hpp"
#include "vkMemUtil.hpp"
#include "vkBarrierUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkImageUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkObjUtil.hpp"
#include "vkImageWithMemory.hpp"
#include "deMath.h"
#include "deUniquePtr.hpp"
#include "deSharedPtr.hpp"
#include "deStringUtil.hpp"
#include "tcuImageCompare.hpp"
#include "tcuTexture.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuFloat.hpp"
#include "tcuStringTemplate.hpp"
#include <string>
#include <vector>
#include <map>
using namespace vk;
namespace vkt
{
namespace image
{
namespace
{
struct FormatInfo
{
const char* GLSLFormat;
int VectorWidth;
int BytesPerPixel;
tcu::TextureChannelClass ChannelClass;
};
FormatInfo getFormatInfo (VkFormat format)
{
FormatInfo result;
const tcu::TextureFormat texFormat = mapVkFormat(format);
result.VectorWidth = getNumUsedChannels(texFormat.order);
result.BytesPerPixel = getPixelSize(texFormat);
result.ChannelClass = tcu::getTextureChannelClass(texFormat.type);
return result;
}
std::string ChannelClassToImageType (tcu::TextureChannelClass channelClass)
{
switch (channelClass)
{
case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: return "uimage2D";
case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: return "iimage2D";
default: return "image2D";
}
}
std::string ChannelClassToVecType (tcu::TextureChannelClass channelClass)
{
switch (channelClass)
{
case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: return "uvec4";
case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: return "ivec4";
default: return "vec4";
}
}
std::string ChannelClassToDefaultVecValue (tcu::TextureChannelClass channelClass)
{
switch (channelClass)
{
case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: return "uvec4(1, 10, 100, 1000)";
case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: return "ivec4(-1, 2, -1000, 2000)";
default: return "vec4(0.25, 0.5, 0.0, 1.0)";
}
}
const std::map<std::string, FormatInfo> SpirvFormats {
{ "Rgba32f", { nullptr, 4, 16, tcu::TEXTURECHANNELCLASS_FLOATING_POINT } },
{ "Rg32f", { nullptr, 2, 8, tcu::TEXTURECHANNELCLASS_FLOATING_POINT } },
{ "R32f", { nullptr, 1, 4, tcu::TEXTURECHANNELCLASS_FLOATING_POINT } },
{ "Rgba16f", { nullptr, 4, 8, tcu::TEXTURECHANNELCLASS_FLOATING_POINT } },
{ "Rg16f", { nullptr, 2, 4, tcu::TEXTURECHANNELCLASS_FLOATING_POINT } },
{ "R16f", { nullptr, 1, 2, tcu::TEXTURECHANNELCLASS_FLOATING_POINT } },
{ "Rgba16", { nullptr, 4, 8, tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT } },
{ "Rg16", { nullptr, 2, 4, tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT } },
{ "R16", { nullptr, 1, 2, tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT } },
{ "Rgba16Snorm", { "rgba16_snorm", 4, 8, tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT } },
{ "Rg16Snorm", { "rg16_snorm", 2, 4, tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT } },
{ "R16Snorm", { "r16_snorm", 1, 2, tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT } },
{ "Rgb10A2", { "rgb10_a2", 4, 4, tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT } },
{ "R11fG11fB10f", { "r11f_g11f_b10f", 3, 4, tcu::TEXTURECHANNELCLASS_FLOATING_POINT } },
{ "Rgba8", { nullptr, 4, 4, tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT } },
{ "Rg8", { nullptr, 2, 2, tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT } },
{ "R8", { nullptr, 1, 1, tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT } },
{ "Rgba8Snorm", { "rgba8_snorm", 4, 4, tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT } },
{ "Rg8Snorm", { "rg8_snorm", 2, 2, tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT } },
{ "R8Snorm", { "r8_snorm", 1, 1, tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT } },
{ "Rgba32i", { nullptr, 4, 16, tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER } },
{ "Rg32i", { nullptr, 2, 2, tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER } },
{ "R32i", { nullptr, 1, 1, tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER } },
{ "Rgba16i", { nullptr, 4, 8, tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER } },
{ "Rg16i", { nullptr, 2, 4, tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER } },
{ "R16i", { nullptr, 1, 2, tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER } },
{ "Rgba8i", { nullptr, 4, 4, tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER } },
{ "Rg8i", { nullptr, 2, 2, tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER } },
{ "R8i", { nullptr, 1, 1, tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER } },
{ "Rgba32ui", { nullptr, 4, 16, tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER } },
{ "Rg32ui", { nullptr, 2, 8, tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER } },
{ "R32ui", { nullptr, 1, 4, tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER } },
{ "Rgba16ui", { nullptr, 4, 8, tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER } },
{ "Rg16ui", { nullptr, 2, 4, tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER } },
{ "R16ui", { nullptr, 1, 2, tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER } },
{ "Rgb10a2ui", { "rgb10_a2ui", 4, 4, tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER } },
{ "Rgba8ui", { nullptr, 4, 4, tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER } },
{ "Rg8ui", { nullptr, 2, 2, tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER } },
{ "R8ui", { nullptr, 1, 1, tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER } }
};
FormatInfo getFormatInfo (const std::string& spirvFormat)
{
auto it = SpirvFormats.find(spirvFormat);
if (it != SpirvFormats.end()) return it->second;
else return {"", 0, 0, tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT};
}
bool matching (VkFormat format, const std::string& spirvFormat)
{
try
{
FormatInfo baseFormat = getFormatInfo(format);
FormatInfo shaderFormat = getFormatInfo(spirvFormat);
return (baseFormat.VectorWidth == shaderFormat.VectorWidth &&
baseFormat.BytesPerPixel == shaderFormat.BytesPerPixel &&
baseFormat.ChannelClass == shaderFormat.ChannelClass);
}
catch (const tcu::InternalError&)
{
return false;
}
}
enum class TestType
{
READ = 0,
SPARSE_READ,
WRITE
};
void fillImageCreateInfo (VkImageCreateInfo& imageCreateInfo, TestType testType, VkFormat format)
{
const VkImageCreateFlags imageFlags = ((testType == TestType::SPARSE_READ) ? (VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT) : 0u);
const VkImageCreateInfo createInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
imageFlags, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
format, // VkFormat format;
makeExtent3D(8, 8, 1), // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
0u, // deUint32 queueFamilyIndexCount;
nullptr, // const deUint32* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout;
};
imageCreateInfo = createInfo;
}
class MismatchedFormatTest : public TestCase
{
public:
MismatchedFormatTest (tcu::TestContext& testCtx,
const std::string& name,
const std::string& description,
const TestType type,
const VkFormat format,
const std::string& spirvFormat);
virtual void checkSupport (Context& context) const;
void initPrograms (SourceCollections& programCollection) const;
TestInstance* createInstance (Context& context) const;
private:
const TestType m_type;
const VkFormat m_format;
const std::string m_spirvFormat;
};
MismatchedFormatTest::MismatchedFormatTest (tcu::TestContext& testCtx,
const std::string& name,
const std::string& description,
const TestType type,
const VkFormat format,
const std::string& spirvFormat)
: TestCase (testCtx, name, description)
, m_type (type)
, m_format (format)
, m_spirvFormat (spirvFormat)
{
}
void MismatchedFormatTest::checkSupport (Context& context) const
{
const auto& vki = context.getInstanceInterface();
const auto physicalDevice = context.getPhysicalDevice();
#ifndef CTS_USES_VULKANSC
if (m_type == TestType::SPARSE_READ)
{
context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SPARSE_BINDING);
if (!getPhysicalDeviceFeatures(vki, physicalDevice).sparseResidencyBuffer)
TCU_THROW(NotSupportedError, "Sparse partially resident buffers not supported");
// Check sparse operations support before creating the image.
VkImageCreateInfo imageCreateInfo;
fillImageCreateInfo(imageCreateInfo, m_type, m_format);
if (!checkSparseImageFormatSupport(physicalDevice, vki, imageCreateInfo))
{
TCU_THROW(NotSupportedError, "The image format does not support sparse operations.");
}
if (!getPhysicalDeviceFeatures(context.getInstanceInterface(), context.getPhysicalDevice()).shaderResourceResidency)
{
TCU_THROW(NotSupportedError, "Shader resource residency not supported");
}
}
#endif // CTS_USES_VULKANSC
VkFormatProperties formatProperties = getPhysicalDeviceFormatProperties(vki, physicalDevice, m_format);
if ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) == 0)
{
TCU_THROW(NotSupportedError, "Creating storage image with this format is not supported");
}
}
void MismatchedFormatTest::initPrograms (SourceCollections& programCollection) const
{
std::string source;
if (m_type == TestType::READ)
{
source = R"(
#version 460 core
layout (${FORMAT}, binding=0) uniform ${IMAGE} inputImage;
void main()
{
${VECT} value = imageLoad(inputImage, ivec2(gl_GlobalInvocationID.xy));
}
)";
}
else if (m_type == TestType::WRITE)
{
source = R"(
#version 460 core
layout (${FORMAT}, binding=0) uniform ${IMAGE} inputImage;
void main()
{
imageStore(inputImage, ivec2(gl_GlobalInvocationID.xy), ${VALUE});
}
)";
}
else if (m_type == TestType::SPARSE_READ)
{
source = R"(
#version 460 core
#extension GL_ARB_sparse_texture2 : require
layout (${FORMAT}, binding=0) uniform ${IMAGE} inputImage;
void main()
{
${VECT} result;
int r = sparseImageLoadARB(inputImage, ivec2(gl_GlobalInvocationID.xy), result);
}
)";
}
const FormatInfo spirvFormatInfo = getFormatInfo(m_spirvFormat);
const std::string glslFormat = spirvFormatInfo.GLSLFormat ?
spirvFormatInfo.GLSLFormat : de::toLower(m_spirvFormat);
std::map<std::string, std::string> specializations;
specializations["FORMAT"] = glslFormat;
specializations["VECT"] = ChannelClassToVecType(spirvFormatInfo.ChannelClass);
specializations["IMAGE"] = ChannelClassToImageType(spirvFormatInfo.ChannelClass);
specializations["VALUE"] = ChannelClassToDefaultVecValue(spirvFormatInfo.ChannelClass);
programCollection.glslSources.add("comp") << glu::ComputeSource( tcu::StringTemplate{source}.specialize(specializations) );
}
class MismatchedFormatTestInstance : public TestInstance
{
public:
MismatchedFormatTestInstance (Context& context,
const TestType type,
const VkFormat format,
const std::string& spirvFormat);
tcu::TestStatus iterate (void);
protected:
const TestType m_type;
const VkFormat m_format;
const std::string m_spirvFormat;
};
MismatchedFormatTestInstance::MismatchedFormatTestInstance (Context& context, const TestType type, const VkFormat format, const std::string& spirvFormat)
: TestInstance (context)
, m_type (type)
, m_format (format)
, m_spirvFormat (spirvFormat)
{
}
tcu::TestStatus MismatchedFormatTestInstance::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();
auto& allocator = m_context.getDefaultAllocator();
#ifndef CTS_USES_VULKANSC
const auto physicalDevice = m_context.getPhysicalDevice();
const auto& instance = m_context.getInstanceInterface();
#endif // CTS_USES_VULKANSC
Move<VkShaderModule> shaderModule = createShaderModule(vk, device, m_context.getBinaryCollection().get("comp"), 0);
Move<VkDescriptorSetLayout> descriptorSetLayout = DescriptorSetLayoutBuilder()
.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT)
.build(vk, device);
Move<VkDescriptorPool> descriptorPool = DescriptorPoolBuilder()
.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
Move<VkDescriptorSet> descriptorSet = makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout);
Move<VkPipelineLayout> pipelineLayout = makePipelineLayout(vk, device, descriptorSetLayout.get());
Move<VkPipeline> pipeline = makeComputePipeline(vk, device, *pipelineLayout, *shaderModule);
VkImageCreateInfo imageCreateInfo;
fillImageCreateInfo(imageCreateInfo, m_type, m_format);
vk::Move<vk::VkImage> storageImage = createImage(vk, device, &imageCreateInfo);
const auto tcuFormat = mapVkFormat(m_format);
de::MovePtr<vk::Allocation> storageAllocation;
vk::Move<vk::VkSemaphore> bindSemaphore;
std::vector<de::SharedPtr<Allocation> > allocations;
if (m_type == TestType::SPARSE_READ)
{
bindSemaphore = createSemaphore(vk, device);
#ifndef CTS_USES_VULKANSC
allocateAndBindSparseImage( vk, device, physicalDevice, instance,
imageCreateInfo, *bindSemaphore, m_context.getSparseQueue(),
allocator, allocations, tcuFormat, *storageImage );
#endif // CTS_USES_VULKANSC
}
else
{
storageAllocation = allocator.allocate(getImageMemoryRequirements(vk, device, *storageImage), MemoryRequirement::Any);
VK_CHECK(vk.bindImageMemory(device, *storageImage, storageAllocation->getMemory(), storageAllocation->getOffset()));
}
const auto subresourceRange = makeImageSubresourceRange(getImageAspectFlags(tcuFormat), 0u, 1u, 0u, 1u);
Move<VkImageView> storageImageView = makeImageView(vk, device, *storageImage, VK_IMAGE_VIEW_TYPE_2D, m_format, subresourceRange);
VkDescriptorImageInfo storageImageInfo = makeDescriptorImageInfo(DE_NULL, *storageImageView, VK_IMAGE_LAYOUT_GENERAL);
DescriptorSetUpdateBuilder builder;
builder
.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &storageImageInfo)
.update(vk, device);
Move<VkCommandPool> cmdPool = createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, queueFamilyIndex);
Move<VkCommandBuffer> cmdBuffer = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
const auto layoutBarrier = makeImageMemoryBarrier(0u, (VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, *storageImage, subresourceRange);
beginCommandBuffer(vk, *cmdBuffer);
vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &layoutBarrier);
vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
vk.cmdDispatch(*cmdBuffer, 8, 8, 1);
endCommandBuffer(vk, *cmdBuffer);
submitCommandsAndWait(vk, device, queue, *cmdBuffer);
return tcu::TestStatus::pass("Passed");
}
TestInstance* MismatchedFormatTest::createInstance (Context& context) const
{
return new MismatchedFormatTestInstance(context, m_type, m_format, m_spirvFormat);
}
} // anonymous ns
tcu::TestCaseGroup* createImageMismatchedFormatsTests (tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "mismatched_formats", "Test image load/store operations on mismatched formats"));
de::MovePtr<tcu::TestCaseGroup> testGroupOpRead(new tcu::TestCaseGroup(testCtx, "image_read", "perform OpImageRead"));
de::MovePtr<tcu::TestCaseGroup> testGroupOpWrite(new tcu::TestCaseGroup(testCtx, "image_write", "perform OpImageWrite"));
#ifndef CTS_USES_VULKANSC
de::MovePtr<tcu::TestCaseGroup> testGroupOpSparseRead(new tcu::TestCaseGroup(testCtx, "sparse_image_read", "perform OpSparseImageRead"));
#endif // CTS_USES_VULKANSC
for (VkFormat format = VK_FORMAT_R4G4_UNORM_PACK8; format < VK_CORE_FORMAT_LAST; format = static_cast<VkFormat>(format+1))
{
for (auto& pair : SpirvFormats)
{
const std::string& spirvFormat = pair.first;
if (matching(format, spirvFormat))
{
const std::string enumName = getFormatName(format);
const std::string testName = de::toLower( enumName.substr(10) + "_with_" + spirvFormat );
testGroupOpRead->addChild(new MismatchedFormatTest( testCtx, testName, "",
TestType::READ,
format, spirvFormat) );
testGroupOpWrite->addChild(new MismatchedFormatTest(testCtx, testName, "",
TestType::WRITE,
format, spirvFormat) );
#ifndef CTS_USES_VULKANSC
testGroupOpSparseRead->addChild(new MismatchedFormatTest( testCtx, testName, "",
TestType::SPARSE_READ,
format, spirvFormat) );
#endif // CTS_USES_VULKANSC
}
}
}
testGroup->addChild(testGroupOpRead.release());
testGroup->addChild(testGroupOpWrite.release());
#ifndef CTS_USES_VULKANSC
testGroup->addChild(testGroupOpSparseRead.release());
#endif // CTS_USES_VULKANSC
return testGroup.release();
}
} // image
} // vkt