blob: 6593c74f00806b7bf03b8684ec8ec868b5ebcfb2 [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2017 The Khronos Group Inc.
*
* 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 Tests for mutable images
*//*--------------------------------------------------------------------*/
#include "vktImageMutableTests.hpp"
#include "vktImageLoadStoreUtil.hpp"
#include "vktTestCaseUtil.hpp"
#include "vktImageTexture.hpp"
#include "vktCustomInstancesDevices.hpp"
#include "vkBuilderUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkImageUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkObjUtil.hpp"
#include "vkRef.hpp"
#include "vkDefs.hpp"
#include "vkPlatform.hpp"
#include "vkWsiUtil.hpp"
#include "vkDeviceUtil.hpp"
#include "deUniquePtr.hpp"
#include "deSharedPtr.hpp"
#include "tcuImageCompare.hpp"
#include "tcuTestLog.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuPlatform.hpp"
#include "tcuCommandLine.hpp"
#include <string>
#include <vector>
using namespace vk;
using namespace tcu;
using namespace vk::wsi;
using de::UniquePtr;
using de::MovePtr;
using de::SharedPtr;
using std::vector;
using std::string;
namespace vkt
{
namespace image
{
typedef SharedPtr<Unique<VkPipeline> > SharedPtrVkPipeline;
typedef SharedPtr<Unique<VkImageView> > SharedPtrVkImageView;
template<typename T>
inline SharedPtr<Unique<T> > makeSharedPtr (Move<T> move)
{
return SharedPtr<Unique<T> >(new Unique<T>(move));
}
enum Upload {
UPLOAD_CLEAR = 0,
UPLOAD_COPY,
UPLOAD_STORE,
UPLOAD_DRAW,
UPLOAD_LAST
};
enum Download {
DOWNLOAD_COPY = 0,
DOWNLOAD_LOAD,
DOWNLOAD_TEXTURE,
DOWNLOAD_LAST
};
std::string getUploadString (const int upload)
{
const char* strs[] = { "clear", "copy", "store", "draw" };
return strs[upload];
}
std::string getDownloadString (const int download)
{
const char* strs[] = { "copy", "load", "texture" };
return strs[download];
}
struct CaseDef
{
ImageType imageType;
IVec3 size;
deUint32 numLayers;
VkFormat imageFormat;
VkFormat viewFormat;
enum Upload upload;
enum Download download;
bool isFormatListTest;
bool isSwapchainImageTest;
Type wsiType;
};
static const deUint32 COLOR_TABLE_SIZE = 4;
// Reference color values for float color rendering. Values have been chosen
// so that when the bit patterns are reinterpreted as a 16-bit float, we do not
// run into NaN / inf / denorm values.
static const Vec4 COLOR_TABLE_FLOAT[COLOR_TABLE_SIZE] =
{
Vec4(0.00f, 0.40f, 0.80f, 0.10f),
Vec4(0.50f, 0.10f, 0.90f, 0.20f),
Vec4(0.20f, 0.60f, 1.00f, 0.30f),
Vec4(0.30f, 0.70f, 0.00f, 0.40f),
};
// Reference color values for integer color rendering. We avoid negative
// values (even for SINT formats) to avoid the situation where sign extension
// leads to NaN / inf values when they are reinterpreted with a float
// format.
static const IVec4 COLOR_TABLE_INT[COLOR_TABLE_SIZE] =
{
IVec4(0x70707070, 0x3C3C3C3C, 0x65656565, 0x29292929),
IVec4(0x3C3C3C3C, 0x65656565, 0x29292929, 0x70707070),
IVec4(0x29292929, 0x70707070, 0x3C3C3C3C, 0x65656565),
IVec4(0x65656565, 0x29292929, 0x70707070, 0x3C3C3C3C),
};
// Reference clear colors created from the color table values
static const VkClearValue REFERENCE_CLEAR_COLOR_FLOAT[COLOR_TABLE_SIZE] =
{
makeClearValueColorF32(COLOR_TABLE_FLOAT[0].x(), COLOR_TABLE_FLOAT[0].y(), COLOR_TABLE_FLOAT[0].z(), COLOR_TABLE_FLOAT[0].w()),
makeClearValueColorF32(COLOR_TABLE_FLOAT[1].x(), COLOR_TABLE_FLOAT[1].y(), COLOR_TABLE_FLOAT[1].z(), COLOR_TABLE_FLOAT[1].w()),
makeClearValueColorF32(COLOR_TABLE_FLOAT[2].x(), COLOR_TABLE_FLOAT[2].y(), COLOR_TABLE_FLOAT[2].z(), COLOR_TABLE_FLOAT[2].w()),
makeClearValueColorF32(COLOR_TABLE_FLOAT[3].x(), COLOR_TABLE_FLOAT[3].y(), COLOR_TABLE_FLOAT[3].z(), COLOR_TABLE_FLOAT[3].w()),
};
static const Texture s_textures[] =
{
Texture(IMAGE_TYPE_2D, tcu::IVec3(32, 32, 1), 1),
Texture(IMAGE_TYPE_2D_ARRAY, tcu::IVec3(32, 32, 1), 4),
};
static VkClearValue getClearValueInt(const CaseDef& caseDef, deUint32 colorTableIndex)
{
VkClearValue clearValue;
deUint32 channelMask = 0;
if (caseDef.upload == UPLOAD_DRAW)
{
// We use this mask to get small color values in the vertex buffer and
// avoid possible round off errors from int-to-float conversions.
channelMask = 0xFFu;
}
else
{
VkFormat format;
tcu::TextureFormat tcuFormat;
// Select a mask such that no integer-based color values end up
// reinterpreted as NaN/Inf/denorm values.
if (caseDef.upload == UPLOAD_CLEAR || caseDef.upload == UPLOAD_COPY)
format = caseDef.imageFormat;
else
format = caseDef.viewFormat;
tcuFormat = mapVkFormat(format);
switch (getChannelSize(tcuFormat.type))
{
case 1: // 8-bit
channelMask = 0xFFu;
break;
case 2: // 16-bit
channelMask = 0xFFFFu;
break;
case 4: // 32-bit
channelMask = 0xFFFFFFFFu;
break;
default:
DE_ASSERT(0);
}
}
clearValue.color.int32[0] = COLOR_TABLE_INT[colorTableIndex].x() & channelMask;
clearValue.color.int32[1] = COLOR_TABLE_INT[colorTableIndex].y() & channelMask;
clearValue.color.int32[2] = COLOR_TABLE_INT[colorTableIndex].z() & channelMask;
clearValue.color.int32[3] = COLOR_TABLE_INT[colorTableIndex].w() & channelMask;
return clearValue;
}
VkImageType getImageType (const ImageType textureImageType)
{
switch (textureImageType)
{
case IMAGE_TYPE_2D:
case IMAGE_TYPE_2D_ARRAY:
return VK_IMAGE_TYPE_2D;
default:
DE_ASSERT(0);
return VK_IMAGE_TYPE_LAST;
}
}
VkImageViewType getImageViewType (const ImageType textureImageType)
{
switch (textureImageType)
{
case IMAGE_TYPE_2D:
return VK_IMAGE_VIEW_TYPE_2D;
case IMAGE_TYPE_2D_ARRAY:
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
default:
DE_ASSERT(0);
return VK_IMAGE_VIEW_TYPE_LAST;
}
}
static const VkFormat s_formats[] =
{
VK_FORMAT_R32G32B32A32_SFLOAT,
VK_FORMAT_R16G16B16A16_SFLOAT,
VK_FORMAT_R32G32_SFLOAT,
VK_FORMAT_R16G16_SFLOAT,
VK_FORMAT_R32_SFLOAT,
VK_FORMAT_R32G32B32A32_UINT,
VK_FORMAT_R16G16B16A16_UINT,
VK_FORMAT_R8G8B8A8_UINT,
VK_FORMAT_R32G32_UINT,
VK_FORMAT_R16G16_UINT,
VK_FORMAT_R32_UINT,
VK_FORMAT_R32G32B32A32_SINT,
VK_FORMAT_R16G16B16A16_SINT,
VK_FORMAT_R8G8B8A8_SINT,
VK_FORMAT_R32G32_SINT,
VK_FORMAT_R16G16_SINT,
VK_FORMAT_R32_SINT,
VK_FORMAT_R8G8B8A8_UNORM,
VK_FORMAT_R8G8B8A8_SNORM,
VK_FORMAT_R8G8B8A8_SRGB,
VK_FORMAT_B8G8R8A8_UNORM,
VK_FORMAT_B8G8R8A8_SNORM,
VK_FORMAT_B8G8R8A8_SRGB,
};
static const VkFormat s_swapchainFormats[] =
{
VK_FORMAT_R8G8B8A8_UNORM,
VK_FORMAT_R8G8B8A8_SNORM,
VK_FORMAT_R8G8B8A8_SRGB,
VK_FORMAT_B8G8R8A8_UNORM,
VK_FORMAT_B8G8R8A8_SNORM,
VK_FORMAT_B8G8R8A8_SRGB,
};
bool isSRGBConversionRequired(const CaseDef& caseDef)
{
bool required = false;
if (isSRGB(mapVkFormat(caseDef.imageFormat)))
{
if (caseDef.upload == UPLOAD_CLEAR)
{
required = true;
}
}
if (isSRGB(mapVkFormat(caseDef.viewFormat)))
{
if (caseDef.upload == UPLOAD_DRAW || caseDef.upload == UPLOAD_STORE)
{
required = true;
}
}
return required;
}
inline bool formatsAreCompatible (const VkFormat format0, const VkFormat format1)
{
return format0 == format1 || mapVkFormat(format0).getPixelSize() == mapVkFormat(format1).getPixelSize();
}
std::string getColorFormatStr (const int numComponents, const bool isUint, const bool isSint)
{
std::ostringstream str;
if (numComponents == 1)
str << (isUint ? "uint" : isSint ? "int" : "float");
else
str << (isUint ? "u" : isSint ? "i" : "") << "vec" << numComponents;
return str.str();
}
std::string getShaderSamplerType (const tcu::TextureFormat& format, VkImageViewType type)
{
std::ostringstream samplerType;
if (tcu::getTextureChannelClass(format.type) == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER)
samplerType << "u";
else if (tcu::getTextureChannelClass(format.type) == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER)
samplerType << "i";
switch (type)
{
case VK_IMAGE_VIEW_TYPE_2D:
samplerType << "sampler2D";
break;
case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
samplerType << "sampler2DArray";
break;
default:
DE_FATAL("Ivalid image view type");
break;
}
return samplerType.str();
}
void initPrograms (SourceCollections& programCollection, const CaseDef caseDef)
{
if (caseDef.upload == UPLOAD_DRAW)
{
{
std::ostringstream src;
src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
<< "\n"
<< "layout(location = 0) in vec4 in_position;\n"
<< "layout(location = 1) in vec4 in_color;\n"
<< "layout(location = 0) out vec4 out_color;\n"
<< "\n"
<< "out gl_PerVertex {\n"
<< " vec4 gl_Position;\n"
<< "};\n"
<< "\n"
<< "void main(void)\n"
<< "{\n"
<< " gl_Position = in_position;\n"
<< " out_color = in_color;\n"
<< "}\n";
programCollection.glslSources.add("uploadDrawVert") << glu::VertexSource(src.str());
}
{
const int numComponents = getNumUsedChannels(mapVkFormat(caseDef.viewFormat).order);
const bool isUint = isUintFormat(caseDef.viewFormat);
const bool isSint = isIntFormat(caseDef.viewFormat);
const std::string colorFormat = getColorFormatStr(numComponents, isUint, isSint);
std::ostringstream src;
src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
<< "\n"
<< "layout(location = 0) in vec4 in_color;\n"
<< "layout(location = 0) out " << colorFormat << " out_color;\n"
<< "\n"
<< "void main(void)\n"
<< "{\n"
<< " out_color = " << colorFormat << "("
<< (numComponents == 1 ? "in_color.r" :
numComponents == 2 ? "in_color.rg" :
numComponents == 3 ? "in_color.rgb" : "in_color")
<< ");\n"
<< "}\n";
programCollection.glslSources.add("uploadDrawFrag") << glu::FragmentSource(src.str());
}
}
if (caseDef.upload == UPLOAD_STORE)
{
const TextureFormat tcuFormat = mapVkFormat(caseDef.viewFormat);
const std::string imageFormatStr = getShaderImageFormatQualifier(tcuFormat);
const std::string imageTypeStr = getShaderImageType(tcuFormat, caseDef.imageType);
const std::string colorTypeStr = isUintFormat(caseDef.viewFormat) ? "uvec4" : isIntFormat(caseDef.viewFormat) ? "ivec4" : "vec4";
const bool isIntegerFormat = isUintFormat(caseDef.viewFormat) || isIntFormat(caseDef.viewFormat);
std::ostringstream src;
src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
<< "\n"
<< "layout (local_size_x = 1) in;\n"
<< "\n"
<< "layout(binding=0, " << imageFormatStr << ") writeonly uniform " << imageTypeStr << " u_image;\n"
<< "\n"
<< "const " << colorTypeStr << " colorTable[] = " << colorTypeStr << "[](\n";
for (deUint32 idx = 0; idx < COLOR_TABLE_SIZE; idx++)
{
if (isIntegerFormat)
{
const VkClearValue clearValue = getClearValueInt(caseDef, idx);
src << " " << colorTypeStr << "(" << clearValue.color.int32[0] << ", " << clearValue.color.int32[1] << ", " << clearValue.color.int32[2] << ", " << clearValue.color.int32[3] << ")";
}
else
src << " " << colorTypeStr << "(" << COLOR_TABLE_FLOAT[idx].x() << ", " << COLOR_TABLE_FLOAT[idx].y() << ", " << COLOR_TABLE_FLOAT[idx].z() << ", " << COLOR_TABLE_FLOAT[idx].w() << ")";
if (idx < COLOR_TABLE_SIZE - 1)
src << ",";
src << "\n";
}
src << ");\n"
<< "\n"
<< "void main(void)\n"
<< "{\n";
if (caseDef.imageType == IMAGE_TYPE_2D)
{
src << " ivec2 pos = ivec2(gl_GlobalInvocationID.xy);\n";
}
else
{
DE_ASSERT(caseDef.imageType == IMAGE_TYPE_2D_ARRAY);
src << " ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);\n";
}
src << " " << colorTypeStr << " color = colorTable[gl_GlobalInvocationID.z];\n"
<< " imageStore(u_image, pos, color);\n"
<< "}\n";
programCollection.glslSources.add("uploadStoreComp") << glu::ComputeSource(src.str());
}
if (caseDef.download == DOWNLOAD_LOAD)
{
const TextureFormat tcuFormat = mapVkFormat(caseDef.viewFormat);
const std::string imageFormatStr = getShaderImageFormatQualifier(tcuFormat);
const std::string imageTypeStr = getShaderImageType(tcuFormat, caseDef.imageType);
const std::string colorTypeStr = isUintFormat(caseDef.viewFormat) ? "uvec4" : isIntFormat(caseDef.viewFormat) ? "ivec4" : "vec4";
std::ostringstream src;
src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
<< "\n"
<< "layout (local_size_x = 1) in;\n"
<< "\n"
<< "layout(binding=0, " << imageFormatStr << ") readonly uniform " << imageTypeStr << " in_image;\n"
<< "layout(binding=1, " << imageFormatStr << ") writeonly uniform " << imageTypeStr << " out_image;\n"
<< "\n"
<< "void main(void)\n"
<< "{\n";
if (caseDef.imageType == IMAGE_TYPE_2D)
{
src << " ivec2 pos = ivec2(gl_GlobalInvocationID.xy);\n";
}
else
{
DE_ASSERT(caseDef.imageType == IMAGE_TYPE_2D_ARRAY);
src << " ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);\n";
}
src << " imageStore(out_image, pos, imageLoad(in_image, pos));\n"
<< "}\n";
programCollection.glslSources.add("downloadLoadComp") << glu::ComputeSource(src.str());
}
if (caseDef.download == DOWNLOAD_TEXTURE)
{
const TextureFormat tcuFormat = mapVkFormat(caseDef.viewFormat);
const VkImageViewType viewType = getImageViewType(caseDef.imageType);
const std::string samplerTypeStr = getShaderSamplerType(tcuFormat, viewType);
const std::string imageFormatStr = getShaderImageFormatQualifier(tcuFormat);
const std::string imageTypeStr = getShaderImageType(tcuFormat, caseDef.imageType);
const std::string colorTypeStr = isUintFormat(caseDef.viewFormat) ? "uvec4" : isIntFormat(caseDef.viewFormat) ? "ivec4" : "vec4";
std::ostringstream src;
src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
<< "\n"
<< "layout (local_size_x = 1) in;\n"
<< "\n"
<< "layout(binding=0) uniform " << samplerTypeStr << " u_tex;\n"
<< "layout(binding=1, " << imageFormatStr << ") writeonly uniform " << imageTypeStr << " out_image;\n"
<< "\n"
<< "void main(void)\n"
<< "{\n";
if (caseDef.imageType == IMAGE_TYPE_2D)
{
src << " ivec2 pos = ivec2(gl_GlobalInvocationID.xy);\n";
}
else
{
DE_ASSERT(caseDef.imageType == IMAGE_TYPE_2D_ARRAY);
src << " ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);\n";
}
src << " imageStore(out_image, pos, texelFetch(u_tex, pos, 0));\n"
<< "}\n";
programCollection.glslSources.add("downloadTextureComp") << glu::ComputeSource(src.str());
}
}
Move<VkImage> makeImage (const DeviceInterface& vk,
const VkDevice device,
VkImageCreateFlags flags,
VkImageType imageType,
const VkFormat format,
const VkFormat viewFormat,
const bool useImageFormatList,
const IVec3& size,
const deUint32 numMipLevels,
const deUint32 numLayers,
const VkImageUsageFlags usage)
{
const VkFormat formatList[2] =
{
format,
viewFormat
};
const VkImageFormatListCreateInfo formatListInfo =
{
VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, // VkStructureType sType;
DE_NULL, // const void* pNext;
2u, // deUint32 viewFormatCount
formatList // const VkFormat* pViewFormats
};
const VkImageCreateInfo imageParams =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
useImageFormatList ? &formatListInfo : DE_NULL, // const void* pNext;
flags, // VkImageCreateFlags flags;
imageType, // VkImageType imageType;
format, // VkFormat format;
makeExtent3D(size), // VkExtent3D extent;
numMipLevels, // deUint32 mipLevels;
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 createImage(vk, device, &imageParams);
}
inline VkImageSubresourceRange makeColorSubresourceRange (const int baseArrayLayer, const int layerCount)
{
return makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, static_cast<deUint32>(baseArrayLayer), static_cast<deUint32>(layerCount));
}
Move<VkSampler> makeSampler (const DeviceInterface& vk, const VkDevice device)
{
const VkSamplerCreateInfo samplerParams =
{
VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkSamplerCreateFlags)0, // VkSamplerCreateFlags flags;
VK_FILTER_NEAREST, // VkFilter magFilter;
VK_FILTER_NEAREST, // VkFilter minFilter;
VK_SAMPLER_MIPMAP_MODE_NEAREST, // VkSamplerMipmapMode mipmapMode;
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeU;
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeV;
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeW;
0.0f, // float mipLodBias;
VK_FALSE, // VkBool32 anisotropyEnable;
1.0f, // float maxAnisotropy;
VK_FALSE, // VkBool32 compareEnable;
VK_COMPARE_OP_ALWAYS, // VkCompareOp compareOp;
0.0f, // float minLod;
0.0f, // float maxLod;
VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, // VkBorderColor borderColor;
VK_FALSE, // VkBool32 unnormalizedCoordinates;
};
return createSampler(vk, device, &samplerParams);
}
Move<VkPipeline> makeGraphicsPipeline (const DeviceInterface& vk,
const VkDevice device,
const VkPipelineLayout pipelineLayout,
const VkRenderPass renderPass,
const VkShaderModule vertexModule,
const VkShaderModule fragmentModule,
const IVec2& renderSize,
const VkPrimitiveTopology topology,
const deUint32 subpass)
{
const std::vector<VkViewport> viewports (1, makeViewport(renderSize));
const std::vector<VkRect2D> scissors (1, makeRect2D(renderSize));
const VkVertexInputBindingDescription vertexInputBindingDescription =
{
0u, // deUint32 binding;
(deUint32)(2 * sizeof(Vec4)), // deUint32 stride;
VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
};
const VkVertexInputAttributeDescription vertexInputAttributeDescriptions[] =
{
{
0u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
0u, // deUint32 offset;
},
{
1u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
(deUint32)sizeof(Vec4), // deUint32 offset;
}
};
const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkPipelineVertexInputStateCreateFlags)0, // VkPipelineVertexInputStateCreateFlags flags;
1u, // deUint32 vertexBindingDescriptionCount;
&vertexInputBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
2u, // deUint32 vertexAttributeDescriptionCount;
vertexInputAttributeDescriptions // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
};
return vk::makeGraphicsPipeline(vk, // const DeviceInterface& vk
device, // const VkDevice device
pipelineLayout, // const VkPipelineLayout pipelineLayout
vertexModule, // const VkShaderModule vertexShaderModule
DE_NULL, // const VkShaderModule tessellationControlModule
DE_NULL, // const VkShaderModule tessellationEvalModule
DE_NULL, // const VkShaderModule geometryShaderModule
fragmentModule, // const VkShaderModule fragmentShaderModule
renderPass, // const VkRenderPass renderPass
viewports, // const std::vector<VkViewport>& viewports
scissors, // const std::vector<VkRect2D>& scissors
topology, // const VkPrimitiveTopology topology
subpass, // const deUint32 subpass
0u, // const deUint32 patchControlPoints
&vertexInputStateCreateInfo); // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo
}
Move<VkPipeline> makeComputePipeline (const DeviceInterface& vk,
const VkDevice device,
const VkPipelineLayout pipelineLayout,
const VkShaderModule shaderModule,
const VkSpecializationInfo* specInfo)
{
const VkPipelineShaderStageCreateInfo shaderStageInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage;
shaderModule, // VkShaderModule module;
"main", // const char* pName;
specInfo, // const VkSpecializationInfo* pSpecializationInfo;
};
const VkComputePipelineCreateInfo pipelineInfo =
{
VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkPipelineCreateFlags)0, // VkPipelineCreateFlags flags;
shaderStageInfo, // VkPipelineShaderStageCreateInfo stage;
pipelineLayout, // VkPipelineLayout layout;
DE_NULL, // VkPipeline basePipelineHandle;
0, // deInt32 basePipelineIndex;
};
return createComputePipeline(vk, device, DE_NULL , &pipelineInfo);
}
Move<VkRenderPass> makeRenderPass (const DeviceInterface& vk,
const VkDevice device,
const VkFormat colorFormat,
const deUint32 numLayers)
{
const VkAttachmentDescription colorAttachmentDescription =
{
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags;
colorFormat, // VkFormat format;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp;
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp;
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout finalLayout;
};
vector<VkAttachmentDescription> attachmentDescriptions(numLayers, colorAttachmentDescription);
// Create a subpass for each attachment (each attachement is a layer of an arrayed image).
vector<VkAttachmentReference> colorAttachmentReferences (numLayers);
vector<VkSubpassDescription> subpasses;
// Ordering here must match the framebuffer attachments
for (deUint32 i = 0; i < numLayers; ++i)
{
const VkAttachmentReference attachmentRef =
{
i, // deUint32 attachment;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout layout;
};
colorAttachmentReferences[i] = attachmentRef;
const VkSubpassDescription subpassDescription =
{
(VkSubpassDescriptionFlags)0, // VkSubpassDescriptionFlags flags;
VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
0u, // deUint32 inputAttachmentCount;
DE_NULL, // const VkAttachmentReference* pInputAttachments;
1u, // deUint32 colorAttachmentCount;
&colorAttachmentReferences[i], // const VkAttachmentReference* pColorAttachments;
DE_NULL, // const VkAttachmentReference* pResolveAttachments;
DE_NULL, // const VkAttachmentReference* pDepthStencilAttachment;
0u, // deUint32 preserveAttachmentCount;
DE_NULL // const deUint32* pPreserveAttachments;
};
subpasses.push_back(subpassDescription);
}
const VkRenderPassCreateInfo renderPassInfo =
{
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags;
static_cast<deUint32>(attachmentDescriptions.size()), // deUint32 attachmentCount;
&attachmentDescriptions[0], // const VkAttachmentDescription* pAttachments;
static_cast<deUint32>(subpasses.size()), // deUint32 subpassCount;
&subpasses[0], // const VkSubpassDescription* pSubpasses;
0u, // deUint32 dependencyCount;
DE_NULL // const VkSubpassDependency* pDependencies;
};
return createRenderPass(vk, device, &renderPassInfo);
}
Move<VkCommandBuffer> makeCommandBuffer (const DeviceInterface& vk, const VkDevice device, const VkCommandPool commandPool)
{
return allocateCommandBuffer(vk, device, commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
}
vector<Vec4> genVertexData (const CaseDef& caseDef)
{
vector<Vec4> vectorData;
const bool isIntegerFormat = isUintFormat(caseDef.viewFormat) || isIntFormat(caseDef.viewFormat);
for (deUint32 z = 0; z < caseDef.numLayers; z++)
{
const deUint32 colorIdx = z % COLOR_TABLE_SIZE;
Vec4 color;
if (isIntegerFormat)
{
const VkClearValue clearValue = getClearValueInt(caseDef, colorIdx);
const IVec4 colorInt (clearValue.color.int32[0], clearValue.color.int32[1], clearValue.color.int32[2], clearValue.color.int32[3]);
color = colorInt.cast<float>();
}
else
{
color = COLOR_TABLE_FLOAT[colorIdx];
}
vectorData.push_back(Vec4(-1.0f, -1.0f, 0.0f, 1.0f));
vectorData.push_back(color);
vectorData.push_back(Vec4(-1.0f, 1.0f, 0.0f, 1.0f));
vectorData.push_back(color);
vectorData.push_back(Vec4( 1.0f, -1.0f, 0.0f, 1.0f));
vectorData.push_back(color);
vectorData.push_back(Vec4( 1.0f, 1.0f, 0.0f, 1.0f));
vectorData.push_back(color);
}
return vectorData;
}
void generateExpectedImage(const tcu::PixelBufferAccess& image, const CaseDef& caseDef)
{
const tcu::TextureChannelClass channelClass = tcu::getTextureChannelClass(image.getFormat().type);
const bool isIntegerFormat = channelClass == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER || channelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
const IVec2 size = caseDef.size.swizzle(0, 1);
for (int z = 0; z < static_cast<int>(caseDef.numLayers); z++)
{
const deUint32 colorIdx = z % COLOR_TABLE_SIZE;
for (int y = 0; y < size.y(); y++)
for (int x = 0; x < size.x(); x++)
{
if (isIntegerFormat)
{
const VkClearValue clearValue = getClearValueInt(caseDef, colorIdx);
const IVec4 colorInt (clearValue.color.int32[0], clearValue.color.int32[1], clearValue.color.int32[2], clearValue.color.int32[3]);
image.setPixel(colorInt, x, y, z);
}
else
if(isSRGBConversionRequired(caseDef))
image.setPixel(tcu::linearToSRGB(COLOR_TABLE_FLOAT[colorIdx]), x, y, z);
else
image.setPixel(COLOR_TABLE_FLOAT[colorIdx], x, y, z);
}
}
}
VkImageUsageFlags getImageUsageForTestCase (const CaseDef& caseDef)
{
VkImageUsageFlags flags = 0u;
switch (caseDef.upload)
{
case UPLOAD_CLEAR:
flags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
break;
case UPLOAD_DRAW:
flags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
break;
case UPLOAD_STORE:
flags |= VK_IMAGE_USAGE_STORAGE_BIT;
break;
case UPLOAD_COPY:
flags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
break;
default:
DE_FATAL("Invalid upload method");
break;
}
switch (caseDef.download)
{
case DOWNLOAD_TEXTURE:
flags |= VK_IMAGE_USAGE_SAMPLED_BIT;
break;
case DOWNLOAD_LOAD:
flags |= VK_IMAGE_USAGE_STORAGE_BIT;
break;
case DOWNLOAD_COPY:
flags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
break;
default:
DE_FATAL("Invalid download method");
break;
}
// We can only create a view for the image if it is going to be used for any of these usages,
// so let's make sure that we have at least one of them.
VkImageUsageFlags viewRequiredFlags = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
if (!(flags & viewRequiredFlags))
flags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
return flags;
}
// Executes a combination of upload/download methods
class UploadDownloadExecutor
{
public:
UploadDownloadExecutor(Context& context, VkDevice device, VkQueue queue, deUint32 queueFamilyIndex, const CaseDef& caseSpec) :
m_caseDef(caseSpec),
m_haveMaintenance2(context.isDeviceFunctionalitySupported("VK_KHR_maintenance2")),
m_vk(context.getDeviceInterface()),
m_device(device),
m_queue(queue),
m_queueFamilyIndex(queueFamilyIndex),
m_allocator(context.getDeviceInterface(), device,
getPhysicalDeviceMemoryProperties(context.getInstanceInterface(),
context.getPhysicalDevice()))
{
}
void runSwapchain(Context& context, VkBuffer buffer, VkImage image);
void run(Context& context, VkBuffer buffer);
private:
void uploadClear(Context& context);
void uploadStore(Context& context);
void uploadCopy(Context& context);
void uploadDraw(Context& context);
void downloadCopy(Context& context, VkBuffer buffer);
void downloadTexture(Context& context, VkBuffer buffer);
void downloadLoad(Context& context, VkBuffer buffer);
void copyImageToBuffer(VkImage image,
VkBuffer buffer,
const IVec3 size,
const VkAccessFlags srcAccessMask,
const VkImageLayout oldLayout,
const deUint32 numLayers);
const CaseDef& m_caseDef;
bool m_haveMaintenance2;
const DeviceInterface& m_vk;
const VkDevice m_device;
const VkQueue m_queue;
const deUint32 m_queueFamilyIndex;
SimpleAllocator m_allocator;
Move<VkCommandPool> m_cmdPool;
Move<VkCommandBuffer> m_cmdBuffer;
bool m_imageIsIntegerFormat;
bool m_viewIsIntegerFormat;
// Target image for upload paths
VkImage m_image;
Move<VkImage> m_imageHolder;
MovePtr<Allocation> m_imageAlloc;
// Upload copy
struct
{
Move<VkBuffer> colorBuffer;
VkDeviceSize colorBufferSize;
MovePtr<Allocation> colorBufferAlloc;
} m_uCopy;
// Upload draw
struct
{
Move<VkBuffer> vertexBuffer;
MovePtr<Allocation> vertexBufferAlloc;
Move<VkPipelineLayout> pipelineLayout;
Move<VkRenderPass> renderPass;
Move<VkShaderModule> vertexModule;
Move<VkShaderModule> fragmentModule;
vector<SharedPtrVkImageView> attachments;
vector<VkImageView> attachmentHandles;
vector<SharedPtrVkPipeline> pipelines;
Move<VkFramebuffer> framebuffer;
} m_uDraw;
// Upload store
struct
{
Move<VkDescriptorPool> descriptorPool;
Move<VkPipelineLayout> pipelineLayout;
Move<VkDescriptorSetLayout> descriptorSetLayout;
Move<VkDescriptorSet> descriptorSet;
VkDescriptorImageInfo imageDescriptorInfo;
Move<VkShaderModule> computeModule;
Move<VkPipeline> computePipeline;
Move<VkImageView> imageView;
} m_uStore;
// Download load
struct
{
Move<VkDescriptorPool> descriptorPool;
Move<VkPipelineLayout> pipelineLayout;
Move<VkDescriptorSetLayout> descriptorSetLayout;
Move<VkDescriptorSet> descriptorSet;
Move<VkShaderModule> computeModule;
Move<VkPipeline> computePipeline;
Move<VkImageView> inImageView;
VkDescriptorImageInfo inImageDescriptorInfo;
Move<VkImage> outImage;
Move<VkImageView> outImageView;
MovePtr<Allocation> outImageAlloc;
VkDescriptorImageInfo outImageDescriptorInfo;
} m_dLoad;
// Download texture
struct
{
Move<VkDescriptorPool> descriptorPool;
Move<VkPipelineLayout> pipelineLayout;
Move<VkDescriptorSetLayout> descriptorSetLayout;
Move<VkDescriptorSet> descriptorSet;
Move<VkShaderModule> computeModule;
Move<VkPipeline> computePipeline;
Move<VkImageView> inImageView;
VkDescriptorImageInfo inImageDescriptorInfo;
Move<VkSampler> sampler;
Move<VkImage> outImage;
Move<VkImageView> outImageView;
MovePtr<Allocation> outImageAlloc;
VkDescriptorImageInfo outImageDescriptorInfo;
} m_dTex;
VkImageLayout m_imageLayoutAfterUpload;
VkAccessFlagBits m_imageUploadAccessMask;
};
void UploadDownloadExecutor::runSwapchain(Context& context, VkBuffer buffer, VkImage image)
{
m_imageIsIntegerFormat = isUintFormat(m_caseDef.imageFormat) || isIntFormat(m_caseDef.imageFormat);
m_viewIsIntegerFormat = isUintFormat(m_caseDef.viewFormat) || isIntFormat(m_caseDef.viewFormat);
m_cmdPool = createCommandPool(m_vk, m_device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, m_queueFamilyIndex);
m_cmdBuffer = makeCommandBuffer(m_vk, m_device, *m_cmdPool);
beginCommandBuffer(m_vk, *m_cmdBuffer);
m_image = image;
switch (m_caseDef.upload)
{
case UPLOAD_DRAW:
uploadDraw(context);
break;
case UPLOAD_STORE:
uploadStore(context);
break;
case UPLOAD_CLEAR:
uploadClear(context);
break;
case UPLOAD_COPY:
uploadCopy(context);
break;
default:
DE_FATAL("Unsupported upload method");
}
switch (m_caseDef.download)
{
case DOWNLOAD_COPY:
downloadCopy(context, buffer);
break;
case DOWNLOAD_LOAD:
downloadLoad(context, buffer);
break;
case DOWNLOAD_TEXTURE:
downloadTexture(context, buffer);
break;
default:
DE_FATAL("Unsupported download method");
}
endCommandBuffer(m_vk, *m_cmdBuffer);
submitCommandsAndWait(m_vk, m_device, m_queue, *m_cmdBuffer);
}
void UploadDownloadExecutor::run(Context& context, VkBuffer buffer)
{
m_imageIsIntegerFormat = isUintFormat(m_caseDef.imageFormat) || isIntFormat(m_caseDef.imageFormat);
m_viewIsIntegerFormat = isUintFormat(m_caseDef.viewFormat) || isIntFormat(m_caseDef.viewFormat);
m_cmdPool = createCommandPool(m_vk, m_device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, m_queueFamilyIndex);
m_cmdBuffer = makeCommandBuffer(m_vk, m_device, *m_cmdPool);
beginCommandBuffer(m_vk, *m_cmdBuffer);
const VkImageUsageFlags imageUsage = getImageUsageForTestCase(m_caseDef);
const VkImageCreateFlags imageFlags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | (m_haveMaintenance2 ? VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR : 0);
m_imageHolder = makeImage(m_vk, m_device, imageFlags, getImageType(m_caseDef.imageType), m_caseDef.imageFormat, m_caseDef.viewFormat,
m_caseDef.isFormatListTest, m_caseDef.size, 1u, m_caseDef.numLayers, imageUsage);
m_image = *m_imageHolder;
m_imageAlloc = bindImage(m_vk, m_device, m_allocator, m_image, MemoryRequirement::Any);
switch (m_caseDef.upload)
{
case UPLOAD_DRAW:
uploadDraw(context);
break;
case UPLOAD_STORE:
uploadStore(context);
break;
case UPLOAD_CLEAR:
uploadClear(context);
break;
case UPLOAD_COPY:
uploadCopy(context);
break;
default:
DE_FATAL("Unsupported upload method");
}
switch (m_caseDef.download)
{
case DOWNLOAD_COPY:
downloadCopy(context, buffer);
break;
case DOWNLOAD_LOAD:
downloadLoad(context, buffer);
break;
case DOWNLOAD_TEXTURE:
downloadTexture(context, buffer);
break;
default:
DE_FATAL("Unsupported download method");
}
endCommandBuffer(m_vk, *m_cmdBuffer);
submitCommandsAndWait(m_vk, m_device, m_queue, *m_cmdBuffer);
}
void UploadDownloadExecutor::uploadClear(Context& context)
{
(void) context;
VkImageLayout requiredImageLayout = VK_IMAGE_LAYOUT_GENERAL;
const VkImageSubresourceRange subresourceRange = makeColorSubresourceRange(0, m_caseDef.numLayers);
const VkImageMemoryBarrier imageInitBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkAccessFlags srcAccessMask;
VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAcessMask;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
requiredImageLayout, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 destQueueFamilyIndex;
m_image, // VkImage image;
subresourceRange // VkImageSubresourceRange subresourceRange;
};
m_vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
0u, DE_NULL, 0u, DE_NULL, 1u, &imageInitBarrier);
for (deUint32 layer = 0; layer < m_caseDef.numLayers; layer++)
{
const VkImageSubresourceRange layerSubresourceRange = makeColorSubresourceRange(layer, 1u);
const deUint32 colorIdx = layer % COLOR_TABLE_SIZE;
const VkClearColorValue clearColor = m_imageIsIntegerFormat ? getClearValueInt(m_caseDef, colorIdx).color : REFERENCE_CLEAR_COLOR_FLOAT[colorIdx].color;
m_vk.cmdClearColorImage(*m_cmdBuffer, m_image, requiredImageLayout, &clearColor, 1u, &layerSubresourceRange);
}
m_imageLayoutAfterUpload = requiredImageLayout;
m_imageUploadAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
}
void UploadDownloadExecutor::uploadStore(Context& context)
{
const vk::VkImageViewUsageCreateInfo viewUsageCreateInfo =
{
VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR, // VkStructureType sType
DE_NULL, // const void* pNext
VK_IMAGE_USAGE_STORAGE_BIT, // VkImageUsageFlags usage;
};
m_uStore.imageView = makeImageView(m_vk, m_device, m_image, getImageViewType(m_caseDef.imageType), m_caseDef.viewFormat,
makeColorSubresourceRange(0, m_caseDef.numLayers), m_haveMaintenance2 ? &viewUsageCreateInfo : DE_NULL);
// Setup compute pipeline
m_uStore.descriptorPool = DescriptorPoolBuilder()
.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
.build(m_vk, m_device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
m_uStore.descriptorSetLayout = DescriptorSetLayoutBuilder()
.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT)
.build(m_vk, m_device);
m_uStore.pipelineLayout = makePipelineLayout(m_vk, m_device, *m_uStore.descriptorSetLayout);
m_uStore.descriptorSet = makeDescriptorSet(m_vk, m_device, *m_uStore.descriptorPool, *m_uStore.descriptorSetLayout);
m_uStore.imageDescriptorInfo = makeDescriptorImageInfo(DE_NULL, *m_uStore.imageView, VK_IMAGE_LAYOUT_GENERAL);
m_uStore.computeModule = createShaderModule(m_vk, m_device, context.getBinaryCollection().get("uploadStoreComp"), 0);
m_uStore.computePipeline = makeComputePipeline(m_vk, m_device, *m_uStore.pipelineLayout, *m_uStore.computeModule, DE_NULL);
DescriptorSetUpdateBuilder()
.writeSingle(*m_uStore.descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &m_uStore.imageDescriptorInfo)
.update(m_vk, m_device);
// Transition storage image for shader access (imageStore)
VkImageLayout requiredImageLayout = VK_IMAGE_LAYOUT_GENERAL;
const VkImageMemoryBarrier imageBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkAccessFlags)0, // VkAccessFlags srcAccessMask;
(VkAccessFlags)VK_ACCESS_SHADER_WRITE_BIT, // VkAccessFlags dstAccessMask;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
requiredImageLayout, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 destQueueFamilyIndex;
m_image, // VkImage image;
makeColorSubresourceRange(0, m_caseDef.numLayers), // VkImageSubresourceRange subresourceRange;
};
m_vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u,
0u, DE_NULL, 0u, DE_NULL, 1u, &imageBarrier);
// Dispatch
m_vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *m_uStore.computePipeline);
m_vk.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *m_uStore.pipelineLayout, 0u, 1u, &m_uStore.descriptorSet.get(), 0u, DE_NULL);
m_vk.cmdDispatch(*m_cmdBuffer, m_caseDef.size.x(), m_caseDef.size.y(), m_caseDef.numLayers);
m_imageLayoutAfterUpload = requiredImageLayout;
m_imageUploadAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
}
void UploadDownloadExecutor::uploadCopy(Context& context)
{
(void) context;
// Create a host-mappable buffer with the color data to upload
const VkDeviceSize pixelSize = tcu::getPixelSize(mapVkFormat(m_caseDef.imageFormat));
const VkDeviceSize layerSize = m_caseDef.size.x() * m_caseDef.size.y() * m_caseDef.size.z() * pixelSize;
m_uCopy.colorBufferSize = layerSize * m_caseDef.numLayers;
m_uCopy.colorBuffer = makeBuffer(m_vk, m_device, m_uCopy.colorBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
m_uCopy.colorBufferAlloc = bindBuffer(m_vk, m_device, m_allocator, *m_uCopy.colorBuffer, MemoryRequirement::HostVisible);
// Fill color buffer
const tcu::TextureFormat tcuFormat = mapVkFormat(m_caseDef.imageFormat);
VkDeviceSize layerOffset = 0ull;
for (deUint32 layer = 0; layer < m_caseDef.numLayers; layer++)
{
tcu::PixelBufferAccess imageAccess = tcu::PixelBufferAccess(tcuFormat, m_caseDef.size.x(), m_caseDef.size.y(), 1u, (deUint8*) m_uCopy.colorBufferAlloc->getHostPtr() + layerOffset);
const deUint32 colorIdx = layer % COLOR_TABLE_SIZE;
if (m_imageIsIntegerFormat)
{
const VkClearValue clearValue = getClearValueInt(m_caseDef, colorIdx);
const IVec4 colorInt (clearValue.color.int32[0], clearValue.color.int32[1], clearValue.color.int32[2], clearValue.color.int32[3]);
tcu::clear(imageAccess, colorInt);
}
else
tcu::clear(imageAccess, COLOR_TABLE_FLOAT[colorIdx]);
layerOffset += layerSize;
}
flushAlloc(m_vk, m_device, *(m_uCopy.colorBufferAlloc));
// Prepare buffer and image for copy
const VkBufferMemoryBarrier bufferInitBarrier =
{
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
VK_ACCESS_HOST_WRITE_BIT, // VkAccessFlags srcAccessMask;
VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex;
*m_uCopy.colorBuffer, // VkBuffer buffer;
0ull, // VkDeviceSize offset;
VK_WHOLE_SIZE, // VkDeviceSize size;
};
const VkImageMemoryBarrier imageInitBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkAccessFlags srcAccessMask;
VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 destQueueFamilyIndex;
m_image, // VkImage image;
makeColorSubresourceRange(0, m_caseDef.numLayers) // VkImageSubresourceRange subresourceRange;
};
m_vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
0u, DE_NULL, 1u, &bufferInitBarrier, 1u, &imageInitBarrier);
// Copy buffer to image
const VkImageSubresourceLayers subresource =
{
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
0u, // deUint32 mipLevel;
0u, // deUint32 baseArrayLayer;
m_caseDef.numLayers, // deUint32 layerCount;
};
const VkBufferImageCopy region =
{
0ull, // VkDeviceSize bufferOffset;
0u, // deUint32 bufferRowLength;
0u, // deUint32 bufferImageHeight;
subresource, // VkImageSubresourceLayers imageSubresource;
makeOffset3D(0, 0, 0), // VkOffset3D imageOffset;
makeExtent3D(m_caseDef.size), // VkExtent3D imageExtent;
};
m_vk.cmdCopyBufferToImage(*m_cmdBuffer, *m_uCopy.colorBuffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1u, &region);
const VkImageMemoryBarrier imagePostInitBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask;
VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask;
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 destQueueFamilyIndex;
m_image, // VkImage image;
makeColorSubresourceRange(0, m_caseDef.numLayers) // VkImageSubresourceRange subresourceRange;
};
m_vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
0u, DE_NULL, 0u, DE_NULL, 1u, &imagePostInitBarrier);
m_imageLayoutAfterUpload = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
m_imageUploadAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
}
void UploadDownloadExecutor::uploadDraw(Context& context)
{
// Create vertex buffer
{
const vector<Vec4> vertices = genVertexData(m_caseDef);
const VkDeviceSize vertexBufferSize = vertices.size() * sizeof(Vec4);
m_uDraw.vertexBuffer = makeBuffer(m_vk, m_device, vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
m_uDraw.vertexBufferAlloc = bindBuffer(m_vk, m_device, m_allocator, *m_uDraw.vertexBuffer, MemoryRequirement::HostVisible);
deMemcpy(m_uDraw.vertexBufferAlloc->getHostPtr(), &vertices[0], static_cast<std::size_t>(vertexBufferSize));
flushAlloc(m_vk, m_device, *(m_uDraw.vertexBufferAlloc));
}
// Create attachments and pipelines for each image layer
m_uDraw.pipelineLayout = makePipelineLayout(m_vk, m_device);
m_uDraw.renderPass = makeRenderPass(m_vk, m_device, m_caseDef.viewFormat, m_caseDef.numLayers);
m_uDraw.vertexModule = createShaderModule(m_vk, m_device, context.getBinaryCollection().get("uploadDrawVert"), 0u);
m_uDraw.fragmentModule = createShaderModule(m_vk, m_device, context.getBinaryCollection().get("uploadDrawFrag"), 0u);
for (deUint32 subpassNdx = 0; subpassNdx < m_caseDef.numLayers; ++subpassNdx)
{
const vk::VkImageViewUsageCreateInfo viewUsageCreateInfo =
{
VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR, // VkStructureType sType
DE_NULL, // const void* pNext
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, // VkImageUsageFlags usage;
};
Move<VkImageView> imageView = makeImageView(m_vk, m_device, m_image, getImageViewType(m_caseDef.imageType), m_caseDef.viewFormat,
makeColorSubresourceRange(subpassNdx, 1), m_haveMaintenance2 ? &viewUsageCreateInfo : DE_NULL);
m_uDraw.attachmentHandles.push_back(*imageView);
m_uDraw.attachments.push_back(makeSharedPtr(imageView));
m_uDraw.pipelines.push_back(makeSharedPtr(makeGraphicsPipeline(m_vk, m_device, *m_uDraw.pipelineLayout, *m_uDraw.renderPass, *m_uDraw.vertexModule, *m_uDraw.fragmentModule,
m_caseDef.size.swizzle(0, 1), VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, subpassNdx)));
}
// Create framebuffer
{
const IVec2 size = m_caseDef.size.swizzle(0, 1);
m_uDraw.framebuffer = makeFramebuffer(m_vk, m_device, *m_uDraw.renderPass, static_cast<deUint32>(m_uDraw.attachmentHandles.size()), &m_uDraw.attachmentHandles[0], static_cast<deUint32>(size.x()), static_cast<deUint32>(size.y()));
}
// Create command buffer
{
{
vector<VkClearValue> clearValues (m_caseDef.numLayers, m_viewIsIntegerFormat ? getClearValueInt(m_caseDef, 0) : REFERENCE_CLEAR_COLOR_FLOAT[0]);
beginRenderPass(m_vk, *m_cmdBuffer, *m_uDraw.renderPass, *m_uDraw.framebuffer, makeRect2D(0, 0, m_caseDef.size.x(), m_caseDef.size.y()), (deUint32)clearValues.size(), &clearValues[0]);
}
// Render
const VkDeviceSize vertexDataPerDraw = 4 * 2 * sizeof(Vec4);
VkDeviceSize vertexBufferOffset = 0ull;
for (deUint32 subpassNdx = 0; subpassNdx < m_caseDef.numLayers; ++subpassNdx)
{
if (subpassNdx != 0)
m_vk.cmdNextSubpass(*m_cmdBuffer, VK_SUBPASS_CONTENTS_INLINE);
m_vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, **m_uDraw.pipelines[subpassNdx]);
m_vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_uDraw.vertexBuffer.get(), &vertexBufferOffset);
m_vk.cmdDraw(*m_cmdBuffer, 4u, 1u, 0u, 0u);
vertexBufferOffset += vertexDataPerDraw;
}
endRenderPass(m_vk, *m_cmdBuffer);
}
m_imageLayoutAfterUpload = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
m_imageUploadAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
}
void UploadDownloadExecutor::downloadCopy(Context& context, VkBuffer buffer)
{
(void) context;
copyImageToBuffer(m_image, buffer, m_caseDef.size, m_imageUploadAccessMask, m_imageLayoutAfterUpload, m_caseDef.numLayers);
}
void UploadDownloadExecutor::downloadTexture(Context& context, VkBuffer buffer)
{
// Create output image with download result
const VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
m_dTex.outImage = makeImage(m_vk, m_device, 0u, VK_IMAGE_TYPE_2D, m_caseDef.viewFormat, m_caseDef.viewFormat, false, m_caseDef.size, 1u, m_caseDef.numLayers, usageFlags);
m_dTex.outImageAlloc = bindImage(m_vk, m_device, m_allocator, *m_dTex.outImage, MemoryRequirement::Any);
m_dTex.outImageView = makeImageView(m_vk, m_device, *m_dTex.outImage, getImageViewType(m_caseDef.imageType), m_caseDef.viewFormat, makeColorSubresourceRange(0, m_caseDef.numLayers));
const vk::VkImageViewUsageCreateInfo viewUsageCreateInfo =
{
VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR, // VkStructureType sType
DE_NULL, // const void* pNext
VK_IMAGE_USAGE_SAMPLED_BIT, // VkImageUsageFlags usage;
};
m_dTex.inImageView = makeImageView(m_vk, m_device, m_image, getImageViewType(m_caseDef.imageType), m_caseDef.viewFormat,
makeColorSubresourceRange(0, m_caseDef.numLayers), m_haveMaintenance2 ? &viewUsageCreateInfo : DE_NULL);
m_dTex.sampler = makeSampler(m_vk, m_device);
// Setup compute pipeline
m_dTex.descriptorPool = DescriptorPoolBuilder()
.addType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
.build(m_vk, m_device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
m_dTex.descriptorSetLayout = DescriptorSetLayoutBuilder()
.addSingleSamplerBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_COMPUTE_BIT, &m_dTex.sampler.get())
.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT)
.build(m_vk, m_device);
m_dTex.pipelineLayout = makePipelineLayout(m_vk, m_device, *m_dTex.descriptorSetLayout);
m_dTex.descriptorSet = makeDescriptorSet(m_vk, m_device, *m_dTex.descriptorPool, *m_dTex.descriptorSetLayout);
m_dTex.inImageDescriptorInfo = makeDescriptorImageInfo(DE_NULL, *m_dTex.inImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
m_dTex.outImageDescriptorInfo = makeDescriptorImageInfo(DE_NULL, *m_dTex.outImageView, VK_IMAGE_LAYOUT_GENERAL);
m_dTex.computeModule = createShaderModule(m_vk, m_device, context.getBinaryCollection().get("downloadTextureComp"), 0);
m_dTex.computePipeline = makeComputePipeline(m_vk, m_device, *m_dTex.pipelineLayout, *m_dTex.computeModule, DE_NULL);
DescriptorSetUpdateBuilder()
.writeSingle(*m_dTex.descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &m_dTex.inImageDescriptorInfo)
.writeSingle(*m_dTex.descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &m_dTex.outImageDescriptorInfo)
.update(m_vk, m_device);
// Transition images for shader access (texture / imageStore)
const VkImageMemoryBarrier imageBarriers[] =
{
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkAccessFlags)m_imageUploadAccessMask, // VkAccessFlags srcAccessMask;
(VkAccessFlags)VK_ACCESS_SHADER_READ_BIT, // VkAccessFlags dstAccessMask;
m_imageLayoutAfterUpload, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 destQueueFamilyIndex;
m_image, // VkImage image;
makeColorSubresourceRange(0, m_caseDef.numLayers), // VkImageSubresourceRange subresourceRange;
},
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkAccessFlags)0, // VkAccessFlags srcAccessMask;
(VkAccessFlags)VK_ACCESS_SHADER_WRITE_BIT, // VkAccessFlags dstAccessMask;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 destQueueFamilyIndex;
*m_dTex.outImage, // VkImage image;
makeColorSubresourceRange(0, m_caseDef.numLayers), // VkImageSubresourceRange subresourceRange;
}
};
m_vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u,
0u, DE_NULL, 0u, DE_NULL, 2u, imageBarriers);
// Dispatch
m_vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *m_dTex.computePipeline);
m_vk.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *m_dTex.pipelineLayout, 0u, 1u, &m_dTex.descriptorSet.get(), 0u, DE_NULL);
m_vk.cmdDispatch(*m_cmdBuffer, m_caseDef.size.x(), m_caseDef.size.y(), m_caseDef.numLayers);
// Copy output image to color buffer
copyImageToBuffer(*m_dTex.outImage, buffer, m_caseDef.size, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL, m_caseDef.numLayers);
}
void UploadDownloadExecutor::downloadLoad(Context& context, VkBuffer buffer)
{
// Create output image with download result
const VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
m_dLoad.outImage = makeImage(m_vk, m_device, 0u, VK_IMAGE_TYPE_2D, m_caseDef.viewFormat, m_caseDef.viewFormat, false, m_caseDef.size, 1u, m_caseDef.numLayers, usageFlags);
m_dLoad.outImageAlloc = bindImage(m_vk, m_device, m_allocator, *m_dLoad.outImage, MemoryRequirement::Any);
m_dLoad.outImageView = makeImageView(m_vk, m_device, *m_dLoad.outImage, getImageViewType(m_caseDef.imageType), m_caseDef.viewFormat, makeColorSubresourceRange(0, m_caseDef.numLayers));
const vk::VkImageViewUsageCreateInfo viewUsageCreateInfo =
{
VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR, // VkStructureType sType
DE_NULL, // const void* pNext
VK_IMAGE_USAGE_STORAGE_BIT, // VkImageUsageFlags usage;
};
m_dLoad.inImageView = makeImageView(m_vk, m_device, m_image, getImageViewType(m_caseDef.imageType), m_caseDef.viewFormat,
makeColorSubresourceRange(0, m_caseDef.numLayers), m_haveMaintenance2 ? &viewUsageCreateInfo : DE_NULL);
// Setup compute pipeline
m_dLoad.descriptorPool = DescriptorPoolBuilder()
.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2u)
.build(m_vk, m_device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
m_dLoad.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(m_vk, m_device);
m_dLoad.pipelineLayout = makePipelineLayout(m_vk, m_device, *m_dLoad.descriptorSetLayout);
m_dLoad.descriptorSet = makeDescriptorSet(m_vk, m_device, *m_dLoad.descriptorPool, *m_dLoad.descriptorSetLayout);
m_dLoad.inImageDescriptorInfo = makeDescriptorImageInfo(DE_NULL, *m_dLoad.inImageView, VK_IMAGE_LAYOUT_GENERAL);
m_dLoad.outImageDescriptorInfo = makeDescriptorImageInfo(DE_NULL, *m_dLoad.outImageView, VK_IMAGE_LAYOUT_GENERAL);
m_dLoad.computeModule = createShaderModule(m_vk, m_device, context.getBinaryCollection().get("downloadLoadComp"), 0);
m_dLoad.computePipeline = makeComputePipeline(m_vk, m_device, *m_dLoad.pipelineLayout, *m_dLoad.computeModule, DE_NULL);
DescriptorSetUpdateBuilder()
.writeSingle(*m_dLoad.descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &m_dLoad.inImageDescriptorInfo)
.writeSingle(*m_dLoad.descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &m_dLoad.outImageDescriptorInfo)
.update(m_vk, m_device);
// Transition storage images for shader access (imageLoad/Store)
VkImageLayout requiredImageLayout = VK_IMAGE_LAYOUT_GENERAL;
const VkImageMemoryBarrier imageBarriers[] =
{
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkAccessFlags)m_imageUploadAccessMask, // VkAccessFlags srcAccessMask;
(VkAccessFlags)VK_ACCESS_SHADER_READ_BIT, // VkAccessFlags dstAccessMask;
m_imageLayoutAfterUpload, // VkImageLayout oldLayout;
requiredImageLayout, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 destQueueFamilyIndex;
m_image, // VkImage image;
makeColorSubresourceRange(0, m_caseDef.numLayers), // VkImageSubresourceRange subresourceRange;
},
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkAccessFlags)0, // VkAccessFlags srcAccessMask;
(VkAccessFlags)VK_ACCESS_SHADER_WRITE_BIT, // VkAccessFlags dstAccessMask;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
requiredImageLayout, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 destQueueFamilyIndex;
*m_dLoad.outImage, // VkImage image;
makeColorSubresourceRange(0, m_caseDef.numLayers), // VkImageSubresourceRange subresourceRange;
}
};
m_vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0u,
0u, DE_NULL, 0u, DE_NULL, 2u, imageBarriers);
// Dispatch
m_vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *m_dLoad.computePipeline);
m_vk.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *m_dLoad.pipelineLayout, 0u, 1u, &m_dLoad.descriptorSet.get(), 0u, DE_NULL);
m_vk.cmdDispatch(*m_cmdBuffer, m_caseDef.size.x(), m_caseDef.size.y(), m_caseDef.numLayers);
// Copy output image to color buffer
copyImageToBuffer(*m_dLoad.outImage, buffer, m_caseDef.size, VK_ACCESS_SHADER_WRITE_BIT, requiredImageLayout, m_caseDef.numLayers);
}
void UploadDownloadExecutor::copyImageToBuffer(VkImage sourceImage,
VkBuffer buffer,
const IVec3 size,
const VkAccessFlags srcAccessMask,
const VkImageLayout oldLayout,
const deUint32 numLayers)
{
// Copy result to host visible buffer for inspection
const VkImageMemoryBarrier imageBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
srcAccessMask, // VkAccessFlags srcAccessMask;
VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask;
oldLayout, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 destQueueFamilyIndex;
sourceImage, // VkImage image;
makeColorSubresourceRange(0, numLayers) // VkImageSubresourceRange subresourceRange;
};
m_vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
0u, DE_NULL, 0u, DE_NULL, 1u, &imageBarrier);
const VkImageSubresourceLayers subresource =
{
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
0u, // deUint32 mipLevel;
0u, // deUint32 baseArrayLayer;
numLayers, // deUint32 layerCount;
};
const VkBufferImageCopy region =
{
0ull, // VkDeviceSize bufferOffset;
0u, // deUint32 bufferRowLength;
0u, // deUint32 bufferImageHeight;
subresource, // VkImageSubresourceLayers imageSubresource;
makeOffset3D(0, 0, 0), // VkOffset3D imageOffset;
makeExtent3D(size), // VkExtent3D imageExtent;
};
m_vk.cmdCopyImageToBuffer(*m_cmdBuffer, sourceImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, 1u, &region);
const VkBufferMemoryBarrier bufferBarrier =
{
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask;
VK_ACCESS_HOST_READ_BIT, // VkAccessFlags dstAccessMask;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex;
buffer, // VkBuffer buffer;
0ull, // VkDeviceSize offset;
VK_WHOLE_SIZE, // VkDeviceSize size;
};
m_vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
0u, DE_NULL, 1u, &bufferBarrier, 0u, DE_NULL);
}
tcu::TestStatus testMutable (Context& context, const CaseDef caseDef)
{
const DeviceInterface& vk = context.getDeviceInterface();
const VkDevice device = context.getDevice();
Allocator& allocator = context.getDefaultAllocator();
// Create a color buffer for host-inspection of results
// For the Copy download method, this is the target of the download, for other
// download methods, pixel data will be copied to this buffer from the download
// target
const VkDeviceSize colorBufferSize = caseDef.size.x() * caseDef.size.y() * caseDef.size.z() * caseDef.numLayers * tcu::getPixelSize(mapVkFormat(caseDef.imageFormat));
const Unique<VkBuffer> colorBuffer (makeBuffer(vk, device, colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT));
const UniquePtr<Allocation> colorBufferAlloc (bindBuffer(vk, device, allocator, *colorBuffer, MemoryRequirement::HostVisible));
deMemset(colorBufferAlloc->getHostPtr(), 0, static_cast<std::size_t>(colorBufferSize));
flushAlloc(vk, device, *colorBufferAlloc);
// Execute the test
UploadDownloadExecutor executor(context, device, context.getUniversalQueue(), context.getUniversalQueueFamilyIndex(), caseDef);
executor.run(context, *colorBuffer);
// Verify results
{
invalidateAlloc(vk, device, *colorBufferAlloc);
// For verification purposes, we use the format of the upload to generate the expected image
const VkFormat format = caseDef.upload == UPLOAD_CLEAR || caseDef.upload == UPLOAD_COPY ? caseDef.imageFormat : caseDef.viewFormat;
const tcu::TextureFormat tcuFormat = mapVkFormat(format);
const bool isIntegerFormat = isUintFormat(format) || isIntFormat(format);
const tcu::ConstPixelBufferAccess resultImage (tcuFormat, caseDef.size.x(), caseDef.size.y(), caseDef.numLayers, colorBufferAlloc->getHostPtr());
tcu::TextureLevel textureLevel (tcuFormat, caseDef.size.x(), caseDef.size.y(), caseDef.numLayers);
const tcu::PixelBufferAccess expectedImage = textureLevel.getAccess();
generateExpectedImage(expectedImage, caseDef);
bool ok;
if (isIntegerFormat)
ok = tcu::intThresholdCompare(context.getTestContext().getLog(), "Image comparison", "", expectedImage, resultImage, tcu::UVec4(1), tcu::COMPARE_LOG_RESULT);
else
ok = tcu::floatThresholdCompare(context.getTestContext().getLog(), "Image comparison", "", expectedImage, resultImage, tcu::Vec4(0.01f), tcu::COMPARE_LOG_RESULT);
return ok ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Fail");
}
}
void checkSupport (Context& context, const CaseDef caseDef)
{
const InstanceInterface& vki = context.getInstanceInterface();
const VkPhysicalDevice physDevice = context.getPhysicalDevice();
// If this is a VK_KHR_image_format_list test, check that the extension is supported
if (caseDef.isFormatListTest)
context.requireDeviceFunctionality("VK_KHR_image_format_list");
// Check required features on the format for the required upload/download methods
VkFormatProperties imageFormatProps, viewFormatProps;
vki.getPhysicalDeviceFormatProperties(physDevice, caseDef.imageFormat, &imageFormatProps);
vki.getPhysicalDeviceFormatProperties(physDevice, caseDef.viewFormat, &viewFormatProps);
VkFormatFeatureFlags viewFormatFeatureFlags = 0u;
switch (caseDef.upload)
{
case UPLOAD_DRAW:
viewFormatFeatureFlags |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
break;
case UPLOAD_STORE:
viewFormatFeatureFlags |= VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;
break;
case UPLOAD_CLEAR:
viewFormatFeatureFlags |= VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
break;
case UPLOAD_COPY:
viewFormatFeatureFlags |= VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
break;
default:
DE_FATAL("Invalid upload method");
break;
}
switch (caseDef.download)
{
case DOWNLOAD_TEXTURE:
viewFormatFeatureFlags |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
// For the texture case we write the samples read to a separate output image with the same view format
// so we need to check that we can also use the view format for storage
viewFormatFeatureFlags |= VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;
break;
case DOWNLOAD_LOAD:
viewFormatFeatureFlags |= VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;
break;
case DOWNLOAD_COPY:
viewFormatFeatureFlags |= VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
break;
default:
DE_FATAL("Invalid download method");
break;
}
if ((viewFormatProps.optimalTilingFeatures & viewFormatFeatureFlags) != viewFormatFeatureFlags)
TCU_THROW(NotSupportedError, "View format doesn't support upload/download method");
const bool haveMaintenance2 = context.isDeviceFunctionalitySupported("VK_KHR_maintenance2");
// We don't use the base image for anything other than transfer
// operations so there are no features to check. However, The Vulkan
// 1.0 spec does not allow us to create an image view with usage that
// is not supported by the main format. With VK_KHR_maintenance2, we
// can do this via VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR.
if ((imageFormatProps.optimalTilingFeatures & viewFormatFeatureFlags) != viewFormatFeatureFlags &&
!haveMaintenance2)
{
TCU_THROW(NotSupportedError, "Image format doesn't support upload/download method");
}
// If no format feature flags are supported, the format itself is not supported,
// and images of that format cannot be created.
if (imageFormatProps.optimalTilingFeatures == 0)
{
TCU_THROW(NotSupportedError, "Base image format is not supported");
}
}
tcu::TestCaseGroup* createImageMutableTests (TestContext& testCtx)
{
de::MovePtr<TestCaseGroup> testGroup (new TestCaseGroup(testCtx, "mutable", "Cases with mutable images"));
for (int textureNdx = 0; textureNdx < DE_LENGTH_OF_ARRAY(s_textures); ++textureNdx)
{
const Texture& texture = s_textures[textureNdx];
de::MovePtr<tcu::TestCaseGroup> groupByImageViewType (new tcu::TestCaseGroup(testCtx, getImageTypeName(texture.type()).c_str(), ""));
for (int imageFormatNdx = 0; imageFormatNdx < DE_LENGTH_OF_ARRAY(s_formats); ++imageFormatNdx)
for (int viewFormatNdx = 0; viewFormatNdx < DE_LENGTH_OF_ARRAY(s_formats); ++viewFormatNdx)
{
if (imageFormatNdx != viewFormatNdx && formatsAreCompatible(s_formats[imageFormatNdx], s_formats[viewFormatNdx]))
{
for (int upload = 0; upload < UPLOAD_LAST; upload++)
{
if (upload == UPLOAD_STORE && !isFormatImageLoadStoreCapable(s_formats[viewFormatNdx]))
continue;
for (int download = 0; download < DOWNLOAD_LAST; download++)
{
if ((download == DOWNLOAD_LOAD || download == DOWNLOAD_TEXTURE) &&
!isFormatImageLoadStoreCapable(s_formats[viewFormatNdx]))
continue;
CaseDef caseDef =
{
texture.type(),
texture.layerSize(),
static_cast<deUint32>(texture.numLayers()),
s_formats[imageFormatNdx],
s_formats[viewFormatNdx],
static_cast<enum Upload>(upload),
static_cast<enum Download>(download),
false, // isFormatListTest;
false, // isSwapchainImageTest
vk::wsi::TYPE_LAST // wsiType
};
std::string caseName = getFormatShortString(s_formats[imageFormatNdx]) + "_" + getFormatShortString(s_formats[viewFormatNdx]) +
"_" + getUploadString(upload) + "_" + getDownloadString(download);
addFunctionCaseWithPrograms(groupByImageViewType.get(), caseName, "", checkSupport, initPrograms, testMutable, caseDef);
caseDef.isFormatListTest = true;
caseName += "_format_list";
addFunctionCaseWithPrograms(groupByImageViewType.get(), caseName, "", checkSupport, initPrograms, testMutable, caseDef);
}
}
}
}
testGroup->addChild(groupByImageViewType.release());
}
return testGroup.release();
}
typedef vector<VkExtensionProperties> Extensions;
void checkAllSupported(const Extensions& supportedExtensions, const vector<string>& requiredExtensions)
{
for (vector<string>::const_iterator requiredExtName = requiredExtensions.begin();
requiredExtName != requiredExtensions.end();
++requiredExtName)
{
if (!isExtensionSupported(supportedExtensions, RequiredExtension(*requiredExtName)))
TCU_THROW(NotSupportedError, (*requiredExtName + " is not supported").c_str());
}
}
CustomInstance createInstanceWithWsi(Context& context,
const Extensions& supportedExtensions,
Type wsiType,
const VkAllocationCallbacks* pAllocator = DE_NULL)
{
vector<string> extensions;
extensions.push_back("VK_KHR_surface");
extensions.push_back(getExtensionName(wsiType));
// VK_EXT_swapchain_colorspace adds new surface formats. Driver can enumerate
// the formats regardless of whether VK_EXT_swapchain_colorspace was enabled,
// but using them without enabling the extension is not allowed. Thus we have
// two options:
//
// 1) Filter out non-core formats to stay within valid usage.
//
// 2) Enable VK_EXT_swapchain colorspace if advertised by the driver.
//
// We opt for (2) as it provides basic coverage for the extension as a bonus.
if (isExtensionSupported(supportedExtensions, RequiredExtension("VK_EXT_swapchain_colorspace")))
extensions.push_back("VK_EXT_swapchain_colorspace");
checkAllSupported(supportedExtensions, extensions);
return createCustomInstanceWithExtensions(context, extensions, pAllocator);
}
Move<VkDevice> createDeviceWithWsi(const PlatformInterface& vkp,
VkInstance instance,
const InstanceInterface& vki,
VkPhysicalDevice physicalDevice,
const Extensions& supportedExtensions,
const deUint32 queueFamilyIndex,
const VkAllocationCallbacks* pAllocator,
bool enableValidation)
{
const float queuePriorities[] = { 1.0f };
const VkDeviceQueueCreateInfo queueInfos[] =
{
{
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
DE_NULL,
(VkDeviceQueueCreateFlags)0,
queueFamilyIndex,
DE_LENGTH_OF_ARRAY(queuePriorities),
&queuePriorities[0]
}
};
VkPhysicalDeviceFeatures features;
deMemset(&features, 0x0, sizeof(features));
const char* const extensions[] = { "VK_KHR_swapchain", "VK_KHR_swapchain_mutable_format" };
const VkDeviceCreateInfo deviceParams =
{
VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
DE_NULL,
(VkDeviceCreateFlags)0,
DE_LENGTH_OF_ARRAY(queueInfos),
&queueInfos[0],
0u, // enabledLayerCount
DE_NULL, // ppEnabledLayerNames
DE_LENGTH_OF_ARRAY(extensions), // enabledExtensionCount
DE_ARRAY_BEGIN(extensions), // ppEnabledExtensionNames
&features
};
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(extensions); ++ndx)
{
if (!isExtensionSupported(supportedExtensions, RequiredExtension(extensions[ndx])))
TCU_THROW(NotSupportedError, (string(extensions[ndx]) + " is not supported").c_str());
}
return createCustomDevice(enableValidation, vkp, instance, vki, physicalDevice, &deviceParams, pAllocator);
}
struct InstanceHelper
{
const vector<VkExtensionProperties> supportedExtensions;
const CustomInstance instance;
const InstanceDriver& vki;
InstanceHelper(Context& context, Type wsiType, const VkAllocationCallbacks* pAllocator = DE_NULL)
: supportedExtensions(enumerateInstanceExtensionProperties(context.getPlatformInterface(),
DE_NULL))
, instance(createInstanceWithWsi(context,
supportedExtensions,
wsiType,
pAllocator))
, vki(instance.getDriver())
{}
};
struct DeviceHelper
{
const VkPhysicalDevice physicalDevice;
const deUint32 queueFamilyIndex;
const Unique<VkDevice> device;
const DeviceDriver vkd;
const VkQueue queue;
DeviceHelper(Context& context,
const InstanceInterface& vki,
VkInstance instance,
VkSurfaceKHR surface,
const VkAllocationCallbacks* pAllocator = DE_NULL)
: physicalDevice(chooseDevice(vki, instance, context.getTestContext().getCommandLine()))
, queueFamilyIndex(chooseQueueFamilyIndex(vki, physicalDevice, surface))
, device(createDeviceWithWsi(context.getPlatformInterface(),
context.getInstance(),
vki,
physicalDevice,
enumerateDeviceExtensionProperties(vki, physicalDevice, DE_NULL),
queueFamilyIndex,
pAllocator,
context.getTestContext().getCommandLine().isValidationEnabled()))
, vkd(context.getPlatformInterface(), context.getInstance(), *device)
, queue(getDeviceQueue(vkd, *device, queueFamilyIndex, 0))
{
}
};
MovePtr<Display> createDisplay(const vk::Platform& platform,
const Extensions& supportedExtensions,
Type wsiType)
{
try
{
return MovePtr<Display>(platform.createWsiDisplay(wsiType));
}
catch (const tcu::NotSupportedError& e)
{
if (isExtensionSupported(supportedExtensions, RequiredExtension(getExtensionName(wsiType))) &&
platform.hasDisplay(wsiType))
{
// If VK_KHR_{platform}_surface was supported, vk::Platform implementation
// must support creating native display & window for that WSI type.
throw tcu::TestError(e.getMessage());
}
else
throw;
}
}
MovePtr<Window> createWindow(const Display& display, const Maybe<UVec2>& initialSize)
{
try
{
return MovePtr<Window>(display.createWindow(initialSize));
}
catch (const tcu::NotSupportedError& e)
{
// See createDisplay - assuming that wsi::Display was supported platform port
// should also support creating a window.
throw tcu::TestError(e.getMessage());
}
}
struct NativeObjects
{
const UniquePtr<Display> display;
const UniquePtr<Window> window;
NativeObjects(Context& context,
const Extensions& supportedExtensions,
Type wsiType,
const Maybe<UVec2>& initialWindowSize = tcu::nothing<UVec2>())
: display(createDisplay(context.getTestContext().getPlatform().getVulkanPlatform(), supportedExtensions, wsiType))
, window(createWindow(*display, initialWindowSize))
{}
};
Move<VkSwapchainKHR> makeSwapchain(const DeviceInterface& vk,
const VkDevice device,
const vk::wsi::Type wsiType,
const VkSurfaceKHR surface,
const VkSurfaceCapabilitiesKHR capabilities,
const VkSurfaceFormatKHR surfaceFormat,
const VkFormat viewFormat,
const deUint32 numLayers,
const VkImageUsageFlags usage,
const tcu::UVec2& desiredSize,
deUint32 desiredImageCount
)
{
const VkFormat formatList[2] =
{
surfaceFormat.format,
viewFormat
};
const VkImageFormatListCreateInfo formatListInfo =
{
VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, // VkStructureType sType;
DE_NULL, // const void* pNext;
2u, // deUint32 viewFormatCount
formatList // const VkFormat* pViewFormats
};
const VkSurfaceTransformFlagBitsKHR transform = (capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : capabilities.currentTransform;
const PlatformProperties& platformProperties = getPlatformProperties(wsiType);
const VkSwapchainCreateInfoKHR swapchainInfo =
{
VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, // VkStructureType sType;
&formatListInfo, // const void* pNext;
VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR, // VkSwapchainCreateFlagsKHR flags;
surface, // VkSurfaceKHR surface;
de::clamp(desiredImageCount, capabilities.minImageCount, capabilities.maxImageCount > 0 ? capabilities.maxImageCount : capabilities.minImageCount + desiredImageCount), // deUint32 minImageCount;
surfaceFormat.format, // VkFormat imageFormat;
surfaceFormat.colorSpace, // VkColorSpaceKHR imageColorSpace;
(platformProperties.swapchainExtent == PlatformProperties::SWAPCHAIN_EXTENT_MUST_MATCH_WINDOW_SIZE
? capabilities.currentExtent : vk::makeExtent2D(desiredSize.x(), desiredSize.y())), // VkExtent2D imageExtent;
numLayers, // deUint32 imageArrayLayers;
usage, // VkImageUsageFlags imageUsage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode imageSharingMode;
0u, // deUint32 queueFamilyIndexCount;
(const deUint32*)DE_NULL, // const deUint32* pQueueFamilyIndices;
transform, // VkSurfaceTransformFlagBitsKHR preTransform;
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, // VkCompositeAlphaFlagBitsKHR compositeAlpha;
VK_PRESENT_MODE_FIFO_KHR, // VkPresentModeKHR presentMode;
VK_FALSE, // VkBool32 clipped;
(VkSwapchainKHR)0 // VkSwapchainKHR oldSwapchain;
};
return createSwapchainKHR(vk, device, &swapchainInfo);
}
tcu::TestStatus testSwapchainMutable(Context& context, CaseDef caseDef)
{
const Type wsiType(caseDef.wsiType);
const tcu::UVec2 desiredSize(256, 256);
const InstanceHelper instHelper(context, wsiType);
const NativeObjects native(context, instHelper.supportedExtensions, wsiType, tcu::just(desiredSize));
const Unique<VkSurfaceKHR> surface(createSurface(instHelper.vki, instHelper.instance, wsiType, *native.display, *native.window));
const DeviceHelper devHelper(context, instHelper.vki, instHelper.instance, *surface);
const DeviceInterface& vk = devHelper.vkd;
const InstanceDriver& vki = instHelper.vki;
const VkDevice device = *devHelper.device;
const VkPhysicalDevice physDevice = devHelper.physicalDevice;
SimpleAllocator allocator(vk, device, getPhysicalDeviceMemoryProperties(vki, context.getPhysicalDevice()));
const VkImageUsageFlags imageUsage = getImageUsageForTestCase(caseDef);
{
VkImageFormatProperties properties;
VkResult result;
result = vki.getPhysicalDeviceImageFormatProperties(physDevice, caseDef.imageFormat, getImageType(caseDef.imageType), VK_IMAGE_TILING_OPTIMAL, imageUsage, 0, &properties);
if (result == VK_ERROR_FORMAT_NOT_SUPPORTED)
{
TCU_THROW(NotSupportedError, "Image format is not supported for required usage");
}
result = vki.getPhysicalDeviceImageFormatProperties(physDevice, caseDef.viewFormat, getImageType(caseDef.imageType), VK_IMAGE_TILING_OPTIMAL, imageUsage, 0, &properties);
if (result == VK_ERROR_FORMAT_NOT_SUPPORTED)
{
TCU_THROW(NotSupportedError, "Image view format is not supported for required usage");
}
}
const VkSurfaceCapabilitiesKHR capabilities = getPhysicalDeviceSurfaceCapabilities(vki,
physDevice,
*surface);
if (caseDef.numLayers > capabilities.maxImageArrayLayers)
caseDef.numLayers = capabilities.maxImageArrayLayers;
// Check support for requested formats by swapchain surface
const vector<VkSurfaceFormatKHR>surfaceFormats = getPhysicalDeviceSurfaceFormats(vki,
physDevice,
*surface);
const VkSurfaceFormatKHR* surfaceFormat = DE_NULL;
const VkFormat* viewFormat = DE_NULL;
for (vector<VkSurfaceFormatKHR>::size_type i = 0; i < surfaceFormats.size(); i++)
{
if (surfaceFormats[i].format == caseDef.imageFormat)
surfaceFormat = &surfaceFormats[i];
if (surfaceFormats[i].format == caseDef.viewFormat)
viewFormat = &surfaceFormats[i].format;
}
if (surfaceFormat == DE_NULL)
TCU_THROW(NotSupportedError, "Image format is not supported by swapchain.");
if (viewFormat == DE_NULL)
TCU_THROW(NotSupportedError, "Image view format is not supported by swapchain.");
if ((capabilities.supportedUsageFlags & imageUsage) != imageUsage)
TCU_THROW(NotSupportedError, "Image usage request not supported by swapchain.");
const Unique<VkSwapchainKHR> swapchain(
makeSwapchain(
vk,
device,
caseDef.wsiType,
*surface,
capabilities,
*surfaceFormat,
caseDef.viewFormat,
caseDef.numLayers,
imageUsage,
desiredSize,
2)
);
const vector<VkImage> swapchainImages = getSwapchainImages(vk, device, *swapchain);
// Create a color buffer for host-inspection of results
// For the Copy download method, this is the target of the download, for other
// download methods, pixel data will be copied to this buffer from the download
// target
const VkDeviceSize colorBufferSize = caseDef.size.x() * caseDef.size.y() * caseDef.size.z() * caseDef.numLayers * tcu::getPixelSize(mapVkFormat(caseDef.imageFormat));
const Unique<VkBuffer> colorBuffer(makeBuffer(vk, device, colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT));
const UniquePtr<Allocation> colorBufferAlloc(bindBuffer(vk, device, allocator, *colorBuffer, MemoryRequirement::HostVisible));
deMemset(colorBufferAlloc->getHostPtr(), 0, static_cast<std::size_t>(colorBufferSize));
flushAlloc(vk, device, *colorBufferAlloc);
// Execute the test
UploadDownloadExecutor executor(context, device, devHelper.queue, devHelper.queueFamilyIndex, caseDef);
executor.runSwapchain(context, *colorBuffer, swapchainImages[0]);
// Verify results
{
invalidateAlloc(vk, device, *colorBufferAlloc);
// For verification purposes, we use the format of the upload to generate the expected image
const VkFormat format = caseDef.upload == UPLOAD_CLEAR || caseDef.upload == UPLOAD_COPY ? caseDef.imageFormat : caseDef.viewFormat;
const tcu::TextureFormat tcuFormat = mapVkFormat(format);
const bool isIntegerFormat = isUintFormat(format) || isIntFormat(format);
const tcu::ConstPixelBufferAccess resultImage(tcuFormat, caseDef.size.x(), caseDef.size.y(), caseDef.numLayers, colorBufferAlloc->getHostPtr());
tcu::TextureLevel textureLevel(tcuFormat, caseDef.size.x(), caseDef.size.y(), caseDef.numLayers);
const tcu::PixelBufferAccess expectedImage = textureLevel.getAccess();
generateExpectedImage(expectedImage, caseDef);
bool ok;
if (isIntegerFormat)
ok = tcu::intThresholdCompare(context.getTestContext().getLog(), "Image comparison", "", expectedImage, resultImage, tcu::UVec4(1), tcu::COMPARE_LOG_RESULT);
else
ok = tcu::floatThresholdCompare(context.getTestContext().getLog(), "Image comparison", "", expectedImage, resultImage, tcu::Vec4(0.01f), tcu::COMPARE_LOG_RESULT);
return ok ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Fail");
}
}
tcu::TestCaseGroup* createSwapchainImageMutableTests(TestContext& testCtx)
{
de::MovePtr<TestCaseGroup> testGroup(new TestCaseGroup(testCtx, "swapchain_mutable", "Cases with swapchain mutable images"));
for (int typeNdx = 0; typeNdx < vk::wsi::TYPE_LAST; ++typeNdx)
{
const vk::wsi::Type wsiType = (vk::wsi::Type)typeNdx;
de::MovePtr<TestCaseGroup> testGroupWsi(new TestCaseGroup(testCtx, getName(wsiType), ""));
for (int textureNdx = 0; textureNdx < DE_LENGTH_OF_ARRAY(s_textures); ++textureNdx)
{
const Texture& texture = s_textures[textureNdx];
de::MovePtr<tcu::TestCaseGroup> groupByImageViewType(new tcu::TestCaseGroup(testCtx, getImageTypeName(texture.type()).c_str(), ""));
for (int imageFormatNdx = 0; imageFormatNdx < DE_LENGTH_OF_ARRAY(s_swapchainFormats); ++imageFormatNdx)
for (int viewFormatNdx = 0; viewFormatNdx < DE_LENGTH_OF_ARRAY(s_swapchainFormats); ++viewFormatNdx)
{
if (imageFormatNdx != viewFormatNdx && formatsAreCompatible(s_swapchainFormats[imageFormatNdx], s_swapchainFormats[viewFormatNdx]))
{
for (int upload = 0; upload < UPLOAD_LAST; upload++)
{
if (upload == UPLOAD_STORE && !isFormatImageLoadStoreCapable(s_swapchainFormats[viewFormatNdx]))
continue;
for (int download = 0; download < DOWNLOAD_LAST; download++)
{
if ((download == DOWNLOAD_LOAD || download == DOWNLOAD_TEXTURE) &&
!isFormatImageLoadStoreCapable(s_swapchainFormats[viewFormatNdx]))
continue;
CaseDef caseDef =
{
texture.type(),
texture.layerSize(),
static_cast<deUint32>(texture.numLayers()),
s_swapchainFormats[imageFormatNdx],
s_swapchainFormats[viewFormatNdx],
static_cast<enum Upload>(upload),
static_cast<enum Download>(download),
true, // isFormatListTest;
true, // isSwapchainImageTest
wsiType
};
std::string caseName = getFormatShortString(s_swapchainFormats[imageFormatNdx]) + "_" + getFormatShortString(s_swapchainFormats[viewFormatNdx]) +
"_" + getUploadString(upload) + "_" + getDownloadString(download) + "_format_list";
addFunctionCaseWithPrograms(groupByImageViewType.get(), caseName, "", checkSupport, initPrograms, testSwapchainMutable, caseDef);
}
}
}
}
testGroupWsi->addChild(groupByImageViewType.release());
}
testGroup->addChild(testGroupWsi.release());
}
return testGroup.release();
}
} // image
} // vkt