blob: 7f24747b136d599c7621abed05a4f32e8c5e8530 [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2017 Advanced Micro Devices, Inc.
* 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 VK_EXT_sample_locations
*//*--------------------------------------------------------------------*/
#include "vktPipelineMultisampleSampleLocationsExtTests.hpp"
#include "vktPipelineMakeUtil.hpp"
#include "vktTestCase.hpp"
#include "vktTestGroupUtil.hpp"
#include "vktTestCaseUtil.hpp"
#include "vkPlatform.hpp"
#include "vkMemUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkRefUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkPrograms.hpp"
#include "vkImageUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkObjUtil.hpp"
#include "deUniquePtr.hpp"
#include "deRandom.hpp"
#include "deMath.h"
#include "tcuTestLog.hpp"
#include "tcuImageCompare.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuRGBA.hpp"
#include "tcuVectorUtil.hpp"
#include <string>
#include <vector>
#include <set>
#include <algorithm>
namespace vkt
{
namespace pipeline
{
namespace
{
using namespace vk;
using de::UniquePtr;
using de::MovePtr;
using tcu::Vec4;
using tcu::Vec2;
using tcu::UVec2;
using tcu::UVec4;
using tcu::RGBA;
static const deUint32 STENCIL_REFERENCE = 1u;
static const float DEPTH_CLEAR = 1.0f;
static const float DEPTH_REFERENCE = 0.5f;
static const Vec4 CLEAR_COLOR_0 = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
static const Vec4 CLEAR_COLOR_1 = Vec4(0.5f, 0.25f, 0.75f, 1.0f);
static const VkDeviceSize ZERO = 0u;
template<typename T>
inline const T* dataOrNullPtr (const std::vector<T>& v)
{
return (v.empty() ? DE_NULL : &v[0]);
}
template<typename T>
inline T* dataOrNullPtr (std::vector<T>& v)
{
return (v.empty() ? DE_NULL : &v[0]);
}
template<typename T>
inline void append (std::vector<T>& first, const std::vector<T>& second)
{
first.insert(first.end(), second.begin(), second.end());
}
//! Order a Vector by X, Y, Z, and W
template<typename VectorT>
struct LessThan
{
bool operator()(const VectorT& v1, const VectorT& v2) const
{
for (int i = 0; i < VectorT::SIZE; ++i)
{
if (v1[i] == v2[i])
continue;
else
return v1[i] < v2[i];
}
return false;
}
};
//! Similar to the class in vktTestCaseUtil.hpp, but uses Arg0 directly rather than through a InstanceFunction1
template<typename Arg0>
class FunctionProgramsSimple1
{
public:
typedef void (*Function) (vk::SourceCollections& dst, Arg0 arg0);
FunctionProgramsSimple1 (Function func) : m_func(func) {}
void init (vk::SourceCollections& dst, const Arg0& arg0) const { m_func(dst, arg0); }
private:
const Function m_func;
};
//! Convenience function to create a TestCase based on a freestanding initPrograms and a TestInstance implementation
template<typename Instance, typename Arg0>
void addInstanceTestCaseWithPrograms (tcu::TestCaseGroup* group,
const std::string& name,
const std::string& desc,
typename FunctionProgramsSimple1<Arg0>::Function initPrograms,
Arg0 arg0)
{
group->addChild(new InstanceFactory1<Instance, Arg0, FunctionProgramsSimple1<Arg0> >(
group->getTestContext(), tcu::NODETYPE_SELF_VALIDATE, name, desc, FunctionProgramsSimple1<Arg0>(initPrograms), arg0));
}
std::string getString (const VkSampleCountFlagBits sampleCount)
{
std::ostringstream str;
str << "samples_" << static_cast<deUint32>(sampleCount);
return str.str();
}
bool isSupportedDepthStencilFormat (const InstanceInterface& vki, const VkPhysicalDevice physDevice, const VkFormat format)
{
VkFormatProperties formatProps;
vki.getPhysicalDeviceFormatProperties(physDevice, format, &formatProps);
return (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
}
VkFormat findSupportedDepthStencilFormat (Context& context, const bool useDepth, const bool useStencil)
{
const InstanceInterface& vki = context.getInstanceInterface();
const VkPhysicalDevice physDevice = context.getPhysicalDevice();
if (useDepth && !useStencil)
return VK_FORMAT_D16_UNORM; // must be supported
// One of these formats must be supported.
if (isSupportedDepthStencilFormat(vki, physDevice, VK_FORMAT_D24_UNORM_S8_UINT))
return VK_FORMAT_D24_UNORM_S8_UINT;
if (isSupportedDepthStencilFormat(vki, physDevice, VK_FORMAT_D32_SFLOAT_S8_UINT))
return VK_FORMAT_D32_SFLOAT_S8_UINT;
return VK_FORMAT_UNDEFINED;
}
VkImageAspectFlags getImageAspectFlags (const VkFormat format)
{
const tcu::TextureFormat tcuFormat = mapVkFormat(format);
if (tcuFormat.order == tcu::TextureFormat::DS) return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
else if (tcuFormat.order == tcu::TextureFormat::D) return VK_IMAGE_ASPECT_DEPTH_BIT;
else if (tcuFormat.order == tcu::TextureFormat::S) return VK_IMAGE_ASPECT_STENCIL_BIT;
DE_FATAL("Format not handled");
return 0u;
}
//! Return NotSupported if required extensions are missing
void requireExtensions (Context& context)
{
const InstanceInterface& vki = context.getInstanceInterface();
const VkPhysicalDevice physicalDevice = context.getPhysicalDevice();
const std::vector<VkExtensionProperties> supportedExtensions = enumerateDeviceExtensionProperties(vki, physicalDevice, DE_NULL);
if (!isExtensionSupported(supportedExtensions, RequiredExtension("VK_EXT_sample_locations")))
TCU_THROW(NotSupportedError, "Missing extension: VK_EXT_sample_locations");
}
VkPhysicalDeviceSampleLocationsPropertiesEXT getSampleLocationsPropertiesEXT (Context& context)
{
const InstanceInterface& vki = context.getInstanceInterface();
const VkPhysicalDevice physicalDevice = context.getPhysicalDevice();
VkPhysicalDeviceSampleLocationsPropertiesEXT sampleLocationsProperties;
deMemset(&sampleLocationsProperties, 0, sizeof(sampleLocationsProperties));
sampleLocationsProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT;
sampleLocationsProperties.pNext = DE_NULL;
VkPhysicalDeviceProperties2 properties =
{
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, // VkStructureType sType;
&sampleLocationsProperties, // void* pNext;
VkPhysicalDeviceProperties(), // VkPhysicalDeviceProperties properties;
};
vki.getPhysicalDeviceProperties2(physicalDevice, &properties);
return sampleLocationsProperties;
}
//! Specify sample locations in a pixel grid
class MultisamplePixelGrid
{
public:
MultisamplePixelGrid (const tcu::UVec2& gridSize, const VkSampleCountFlagBits numSamples)
: m_gridSize (gridSize)
, m_numSamples (numSamples)
, m_sampleLocations (gridSize.x() * gridSize.y() * numSamples)
{
DE_ASSERT(gridSize.x() > 0 && gridSize.y() > 0);
DE_ASSERT(numSamples > 1);
}
//! If grid x,y is larger than gridSize, then each coordinate is wrapped, x' = x % size_x
const VkSampleLocationEXT& getSample (deUint32 gridX, deUint32 gridY, const deUint32 sampleNdx) const
{
return m_sampleLocations[getSampleIndex(gridX, gridY, sampleNdx)];
}
void setSample (const deUint32 gridX, const deUint32 gridY, const deUint32 sampleNdx, const VkSampleLocationEXT& location)
{
DE_ASSERT(gridX < m_gridSize.x());
DE_ASSERT(gridY < m_gridSize.y());
m_sampleLocations[getSampleIndex(gridX, gridY, sampleNdx)] = location;
}
const tcu::UVec2& size (void) const { return m_gridSize; }
VkSampleCountFlagBits samplesPerPixel (void) const { return m_numSamples; }
const VkSampleLocationEXT* sampleLocations (void) const { return dataOrNullPtr(m_sampleLocations); }
VkSampleLocationEXT* sampleLocations (void) { return dataOrNullPtr(m_sampleLocations); }
deUint32 sampleLocationCount (void) const { return static_cast<deUint32>(m_sampleLocations.size()); }
private:
deUint32 getSampleIndex (deUint32 gridX, deUint32 gridY, const deUint32 sampleNdx) const
{
gridX %= m_gridSize.x();
gridY %= m_gridSize.y();
return (gridY * m_gridSize.x() + gridX) * static_cast<deUint32>(m_numSamples) + sampleNdx;
}
tcu::UVec2 m_gridSize;
VkSampleCountFlagBits m_numSamples;
std::vector<VkSampleLocationEXT> m_sampleLocations;
};
inline deUint32 numSamplesPerPixel (const MultisamplePixelGrid& pixelGrid)
{
return static_cast<deUint32>(pixelGrid.samplesPerPixel());
}
//! References the data inside MultisamplePixelGrid
inline VkSampleLocationsInfoEXT makeSampleLocationsInfo (const MultisamplePixelGrid& pixelGrid)
{
const VkSampleLocationsInfoEXT info =
{
VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT, // VkStructureType sType;
DE_NULL, // const void* pNext;
pixelGrid.samplesPerPixel(), // VkSampleCountFlagBits sampleLocationsPerPixel;
makeExtent2D(pixelGrid.size().x(), pixelGrid.size().y()), // VkExtent2D sampleLocationGridSize;
pixelGrid.sampleLocationCount(), // uint32_t sampleLocationsCount;
pixelGrid.sampleLocations(), // const VkSampleLocationEXT* pSampleLocations;
};
return info;
}
inline VkSampleLocationsInfoEXT makeEmptySampleLocationsInfo ()
{
const VkSampleLocationsInfoEXT info =
{
VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkSampleCountFlagBits)0, // VkSampleCountFlagBits sampleLocationsPerPixel;
makeExtent2D(0,0), // VkExtent2D sampleLocationGridSize;
0, // uint32_t sampleLocationsCount;
DE_NULL, // const VkSampleLocationEXT* pSampleLocations;
};
return info;
}
void logPixelGrid (tcu::TestLog& log, const VkPhysicalDeviceSampleLocationsPropertiesEXT& sampleLocationsProperties, const MultisamplePixelGrid& pixelGrid)
{
log << tcu::TestLog::Section("pixelGrid", "Multisample pixel grid configuration:")
<< tcu::TestLog::Message << sampleLocationsProperties << tcu::TestLog::EndMessage
<< tcu::TestLog::Message << "Specified grid size = " << pixelGrid.size() << tcu::TestLog::EndMessage;
for (deUint32 gridY = 0; gridY < pixelGrid.size().y(); ++gridY)
for (deUint32 gridX = 0; gridX < pixelGrid.size().x(); ++gridX)
{
log << tcu::TestLog::Message << "Pixel(" << gridX << ", " << gridY <<")" << tcu::TestLog::EndMessage;
for (deUint32 sampleNdx = 0; sampleNdx < numSamplesPerPixel(pixelGrid); ++sampleNdx)
{
const VkSampleLocationEXT& loc = pixelGrid.getSample(gridX, gridY, sampleNdx);
log << tcu::TestLog::Message << "* Sample(" << sampleNdx <<") = " << Vec2(loc.x, loc.y) << tcu::TestLog::EndMessage;
}
}
log << tcu::TestLog::Message << "Sample locations visualization" << tcu::TestLog::EndMessage;
{
const deUint32 height = deMinu32(1u << sampleLocationsProperties.sampleLocationSubPixelBits, 16u); // increase if you want more precision
const deUint32 width = 2 * height; // works well with a fixed-size font
std::vector<char> buffer (width * height);
for (deUint32 gridY = 0; gridY < pixelGrid.size().y(); ++gridY)
for (deUint32 gridX = 0; gridX < pixelGrid.size().x(); ++gridX)
{
std::fill(buffer.begin(), buffer.end(), '.');
for (deUint32 sampleNdx = 0; sampleNdx < numSamplesPerPixel(pixelGrid); ++sampleNdx)
{
const VkSampleLocationEXT& loc = pixelGrid.getSample(gridX, gridY, sampleNdx);
const deUint32 ndx = deMinu32(width - 1, static_cast<deUint32>(static_cast<float>(width) * loc.x)) +
deMinu32(height - 1, static_cast<deUint32>(static_cast<float>(height) * loc.y)) * width;
const deUint32 evenNdx = ndx - ndx % 2;
buffer[evenNdx ] = '[';
buffer[evenNdx + 1] = ']';
}
std::ostringstream str;
str << "Pixel(" << gridX << ", " << gridY <<")\n";
for (deUint32 lineNdx = 0; lineNdx < height; ++lineNdx)
{
str.write(&buffer[width * lineNdx], width);
str << "\n";
}
log << tcu::TestLog::Message << str.str() << tcu::TestLog::EndMessage;
}
}
log << tcu::TestLog::EndSection;
}
//! Fill each grid pixel with a distinct samples pattern, rounding locations based on subPixelBits
void fillSampleLocationsRandom (MultisamplePixelGrid& grid, const deUint32 subPixelBits, const deUint32 seed = 142u)
{
const deUint32 numLocations = 1u << subPixelBits;
de::Random rng (seed);
for (deUint32 gridY = 0; gridY < grid.size().y(); ++gridY)
for (deUint32 gridX = 0; gridX < grid.size().x(); ++gridX)
{
std::set<UVec2, LessThan<UVec2> > takenLocationIndices;
for (deUint32 sampleNdx = 0; sampleNdx < numSamplesPerPixel(grid); /* no increment */)
{
const UVec2 locationNdx (rng.getUint32() % numLocations,
rng.getUint32() % numLocations);
if (takenLocationIndices.find(locationNdx) == takenLocationIndices.end())
{
const VkSampleLocationEXT location =
{
static_cast<float>(locationNdx.x()) / static_cast<float>(numLocations), // float x;
static_cast<float>(locationNdx.y()) / static_cast<float>(numLocations), // float y;
};
grid.setSample(gridX, gridY, sampleNdx, location);
takenLocationIndices.insert(locationNdx);
++sampleNdx; // next sample
}
}
}
}
//! Place samples very close to each other
void fillSampleLocationsPacked (MultisamplePixelGrid& grid, const deUint32 subPixelBits)
{
const deUint32 numLocations = 1u << subPixelBits;
const int offset[3] = { -1, 0, 1 };
de::Random rng (214);
for (deUint32 gridY = 0; gridY < grid.size().y(); ++gridY)
for (deUint32 gridX = 0; gridX < grid.size().x(); ++gridX)
{
// Will start placing from this location
const UVec2 baseLocationNdx (rng.getUint32() % numLocations,
rng.getUint32() % numLocations);
UVec2 locationNdx = baseLocationNdx;
std::set<UVec2, LessThan<UVec2> > takenLocationIndices;
for (deUint32 sampleNdx = 0; sampleNdx < numSamplesPerPixel(grid); /* no increment */)
{
if (takenLocationIndices.find(locationNdx) == takenLocationIndices.end())
{
const VkSampleLocationEXT location =
{
static_cast<float>(locationNdx.x()) / static_cast<float>(numLocations), // float x;
static_cast<float>(locationNdx.y()) / static_cast<float>(numLocations), // float y;
};
grid.setSample(gridX, gridY, sampleNdx, location);
takenLocationIndices.insert(locationNdx);
++sampleNdx; // next sample
}
// Find next location by applying a small offset. Just keep iterating if a redundant location is chosen
locationNdx.x() = static_cast<deUint32>(deClamp32(locationNdx.x() + offset[rng.getUint32() % DE_LENGTH_OF_ARRAY(offset)], 0u, numLocations - 1));
locationNdx.y() = static_cast<deUint32>(deClamp32(locationNdx.y() + offset[rng.getUint32() % DE_LENGTH_OF_ARRAY(offset)], 0u, numLocations - 1));
}
}
}
//! Unorm/int compare, very low threshold as we are expecting near-exact values
bool compareGreenImage (tcu::TestLog& log, const char* name, const char* description, const tcu::ConstPixelBufferAccess& image)
{
tcu::TextureLevel greenImage(image.getFormat(), image.getWidth(), image.getHeight());
tcu::clear(greenImage.getAccess(), tcu::RGBA::green().toIVec());
return tcu::intThresholdCompare(log, name, description, greenImage.getAccess(), image, tcu::UVec4(2u), tcu::COMPARE_LOG_RESULT);
}
//! Silent compare - no logging
bool intThresholdCompare (const tcu::ConstPixelBufferAccess& reference, const tcu::ConstPixelBufferAccess& result, const UVec4& threshold)
{
using namespace tcu;
int width = reference.getWidth();
int height = reference.getHeight();
int depth = reference.getDepth();
UVec4 maxDiff (0, 0, 0, 0);
TCU_CHECK_INTERNAL(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth);
for (int z = 0; z < depth; z++)
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
IVec4 refPix = reference.getPixelInt(x, y, z);
IVec4 cmpPix = result.getPixelInt(x, y, z);
UVec4 diff = abs(refPix - cmpPix).cast<deUint32>();
maxDiff = max(maxDiff, diff);
}
}
}
return boolAll(lessThanEqual(maxDiff, threshold));
}
int countUniqueColors (const tcu::ConstPixelBufferAccess& image)
{
std::set<Vec4, LessThan<Vec4> > colors;
for (int y = 0; y < image.getHeight(); ++y)
for (int x = 0; x < image.getWidth(); ++x)
{
colors.insert(image.getPixel(x, y));
}
return static_cast<int>(colors.size());
}
Move<VkImage> makeImage (const DeviceInterface& vk,
const VkDevice device,
const VkImageCreateFlags flags,
const VkFormat format,
const UVec2& size,
const VkSampleCountFlagBits samples,
const VkImageUsageFlags usage)
{
const VkImageCreateInfo imageParams =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
flags, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
format, // VkFormat format;
makeExtent3D(size.x(), size.y(), 1), // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arrayLayers;
samples, // 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 Move<VkBuffer> makeBuffer (const DeviceInterface& vk, const VkDevice device, const VkDeviceSize bufferSize, const VkBufferUsageFlags usage)
{
const VkBufferCreateInfo bufferCreateInfo = makeBufferCreateInfo(bufferSize, usage);
return createBuffer(vk, device, &bufferCreateInfo);
}
Move<VkEvent> makeEvent (const DeviceInterface& vk, const VkDevice device)
{
const VkEventCreateInfo createInfo =
{
VK_STRUCTURE_TYPE_EVENT_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkEventCreateFlags)0, // VkEventCreateFlags flags;
};
return createEvent(vk, device, &createInfo);
}
//! Generate NDC space sample locations at each framebuffer pixel
//! Data is filled starting at pixel (0,0) and for each pixel there are numSamples locations
std::vector<Vec2> genFramebufferSampleLocations (const MultisamplePixelGrid& pixelGrid, const UVec2& gridSize, const UVec2& framebufferSize)
{
std::vector<Vec2> locations;
for (deUint32 y = 0; y < framebufferSize.y(); ++y)
for (deUint32 x = 0; x < framebufferSize.x(); ++x)
for (deUint32 sampleNdx = 0; sampleNdx < numSamplesPerPixel(pixelGrid); ++sampleNdx)
{
const VkSampleLocationEXT& location = pixelGrid.getSample(x % gridSize.x(), y % gridSize.y(), sampleNdx);
const float globalX = location.x + static_cast<float>(x);
const float globalY = location.y + static_cast<float>(y);
// Transform to [-1, 1] space
locations.push_back(Vec2(-1.0f + 2.0f * (globalX / static_cast<float>(framebufferSize.x())),
-1.0f + 2.0f * (globalY / static_cast<float>(framebufferSize.y()))));
}
return locations;
}
struct PositionColor
{
tcu::Vec4 position;
tcu::Vec4 color;
PositionColor (const tcu::Vec4& pos, const tcu::Vec4& col) : position(pos), color(col) {}
};
std::vector<PositionColor> genVerticesFullQuad (const Vec4& color = Vec4(1.0f), const float z = 0.0f)
{
const PositionColor vertices[] =
{
PositionColor(Vec4( 1.0f, -1.0f, z, 1.0f), color),
PositionColor(Vec4(-1.0f, -1.0f, z, 1.0f), color),
PositionColor(Vec4(-1.0f, 1.0f, z, 1.0f), color),
PositionColor(Vec4(-1.0f, 1.0f, z, 1.0f), color),
PositionColor(Vec4( 1.0f, 1.0f, z, 1.0f), color),
PositionColor(Vec4( 1.0f, -1.0f, z, 1.0f), color),
};
return std::vector<PositionColor>(vertices, vertices + DE_LENGTH_OF_ARRAY(vertices));
}
//! Some abstract geometry with angled edges, to make multisampling visible.
std::vector<PositionColor> genVerticesShapes (const Vec4& color = Vec4(1.0f), const float z = 0.0f)
{
std::vector<PositionColor> vertices;
const float numSteps = 16.0f;
const float angleStep = (2.0f * DE_PI) / numSteps;
for (float a = 0.0f; a <= 2.0f * DE_PI; a += angleStep)
{
vertices.push_back(PositionColor(Vec4(1.0f * deFloatCos(a), 1.0f * deFloatSin(a), z, 1.0f), color));
vertices.push_back(PositionColor(Vec4(0.1f * deFloatCos(a - angleStep), 0.1f * deFloatSin(a - angleStep), z, 1.0f), color));
vertices.push_back(PositionColor(Vec4(0.1f * deFloatCos(a + angleStep), 0.1f * deFloatSin(a + angleStep), z, 1.0f), color));
}
return vertices;
}
//! Stencil op that only allows drawing over the cleared area of an attachment.
inline VkStencilOpState stencilOpStateDrawOnce (void)
{
return makeStencilOpState(
VK_STENCIL_OP_KEEP, // stencil fail
VK_STENCIL_OP_ZERO, // depth & stencil pass
VK_STENCIL_OP_KEEP, // depth only fail
VK_COMPARE_OP_EQUAL, // compare op
~0u, // compare mask
~0u, // write mask
STENCIL_REFERENCE); // reference
}
//! Stencil op that simply increments the buffer with each passing test.
inline VkStencilOpState stencilOpStateIncrement(void)
{
return makeStencilOpState(
VK_STENCIL_OP_KEEP, // stencil fail
VK_STENCIL_OP_INCREMENT_AND_CLAMP, // depth & stencil pass
VK_STENCIL_OP_KEEP, // depth only fail
VK_COMPARE_OP_ALWAYS, // compare op
~0u, // compare mask
~0u, // write mask
STENCIL_REFERENCE); // reference
}
//! A few preconfigured vertex attribute configurations
enum VertexInputConfig
{
VERTEX_INPUT_NONE = 0u,
VERTEX_INPUT_VEC4,
VERTEX_INPUT_VEC4_VEC4,
};
//! Create a MSAA pipeline, with max per-sample shading
Move<VkPipeline> makeGraphicsPipeline (const DeviceInterface& vk,
const VkDevice device,
const std::vector<VkDynamicState>& dynamicState,
const VkPipelineLayout pipelineLayout,
const VkRenderPass renderPass,
const VkShaderModule vertexModule,
const VkShaderModule fragmentModule,
const deUint32 subpassIndex,
const VkViewport& viewport,
const VkRect2D scissor,
const VkSampleCountFlagBits numSamples,
const bool useSampleLocations,
const VkSampleLocationsInfoEXT& sampleLocationsInfo,
const bool useDepth,
const bool useStencil,
const VertexInputConfig vertexInputConfig,
const VkPrimitiveTopology topology,
const VkStencilOpState& stencilOpState)
{
std::vector<VkVertexInputBindingDescription> vertexInputBindingDescriptions;
std::vector<VkVertexInputAttributeDescription> vertexInputAttributeDescriptions;
const deUint32 sizeofVec4 = static_cast<deUint32>(sizeof(Vec4));
switch (vertexInputConfig)
{
case VERTEX_INPUT_NONE:
break;
case VERTEX_INPUT_VEC4:
vertexInputBindingDescriptions.push_back (makeVertexInputBindingDescription (0u, sizeofVec4, VK_VERTEX_INPUT_RATE_VERTEX));
vertexInputAttributeDescriptions.push_back(makeVertexInputAttributeDescription(0u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, 0u));
break;
case VERTEX_INPUT_VEC4_VEC4:
vertexInputBindingDescriptions.push_back (makeVertexInputBindingDescription (0u, 2u * sizeofVec4, VK_VERTEX_INPUT_RATE_VERTEX));
vertexInputAttributeDescriptions.push_back(makeVertexInputAttributeDescription(0u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, 0u));
vertexInputAttributeDescriptions.push_back(makeVertexInputAttributeDescription(1u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, sizeofVec4));
break;
default:
DE_FATAL("Vertex input config not supported");
break;
}
const VkPipelineVertexInputStateCreateInfo vertexInputStateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkPipelineVertexInputStateCreateFlags)0, // VkPipelineVertexInputStateCreateFlags flags;
static_cast<deUint32>(vertexInputBindingDescriptions.size()), // uint32_t vertexBindingDescriptionCount;
dataOrNullPtr(vertexInputBindingDescriptions), // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
static_cast<deUint32>(vertexInputAttributeDescriptions.size()), // uint32_t vertexAttributeDescriptionCount;
dataOrNullPtr(vertexInputAttributeDescriptions), // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
};
const std::vector<VkViewport> viewports (1, viewport);
const std::vector<VkRect2D> scissors (1, scissor);
const VkPipelineSampleLocationsStateCreateInfoEXT pipelineSampleLocationsCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT, // VkStructureType sType;
DE_NULL, // const void* pNext;
useSampleLocations, // VkBool32 sampleLocationsEnable;
sampleLocationsInfo, // VkSampleLocationsInfoEXT sampleLocationsInfo;
};
const VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
&pipelineSampleLocationsCreateInfo, // const void* pNext;
(VkPipelineMultisampleStateCreateFlags)0, // VkPipelineMultisampleStateCreateFlags flags;
numSamples, // VkSampleCountFlagBits rasterizationSamples;
VK_TRUE, // VkBool32 sampleShadingEnable;
1.0f, // float minSampleShading;
DE_NULL, // const VkSampleMask* pSampleMask;
VK_FALSE, // VkBool32 alphaToCoverageEnable;
VK_FALSE // VkBool32 alphaToOneEnable;
};
VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkPipelineDepthStencilStateCreateFlags)0, // VkPipelineDepthStencilStateCreateFlags flags;
useDepth, // VkBool32 depthTestEnable;
true, // VkBool32 depthWriteEnable;
VK_COMPARE_OP_LESS, // VkCompareOp depthCompareOp;
VK_FALSE, // VkBool32 depthBoundsTestEnable;
useStencil, // VkBool32 stencilTestEnable;
stencilOpState, // VkStencilOpState front;
stencilOpState, // VkStencilOpState back;
0.0f, // float minDepthBounds;
1.0f, // float maxDepthBounds;
};
const VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo =
{
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkPipelineDynamicStateCreateFlags)0, // VkPipelineDynamicStateCreateFlags flags;
static_cast<deUint32>(dynamicState.size()), // uint32_t dynamicStateCount;
dataOrNullPtr(dynamicState), // const VkDynamicState* pDynamicStates;
};
return makeGraphicsPipeline(vk, // const DeviceInterface& vk
device, // const VkDevice device
pipelineLayout, // const VkPipelineLayout pipelineLayout
vertexModule, // const VkShaderModule vertexShaderModule
DE_NULL, // const VkShaderModule tessellationControlShaderModule
DE_NULL, // const VkShaderModule tessellationEvalShaderModule
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
subpassIndex, // const deUint32 subpass
0u, // const deUint32 patchControlPoints
&vertexInputStateInfo, // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo
DE_NULL, // const VkPipelineRasterizationStateCreateInfo* rasterizationStateCreateInfo
&pipelineMultisampleStateInfo, // const VkPipelineMultisampleStateCreateInfo* multisampleStateCreateInfo
&pipelineDepthStencilStateInfo, // const VkPipelineDepthStencilStateCreateInfo* depthStencilStateCreateInfo
DE_NULL, // const VkPipelineColorBlendStateCreateInfo* colorBlendStateCreateInfo
&dynamicStateCreateInfo); // const VkPipelineDynamicStateCreateInfo* dynamicStateCreateInfo
}
inline Move<VkPipeline> makeGraphicsPipelineSinglePassColor (const DeviceInterface& vk,
const VkDevice device,
const std::vector<VkDynamicState>& dynamicState,
const VkPipelineLayout pipelineLayout,
const VkRenderPass renderPass,
const VkShaderModule vertexModule,
const VkShaderModule fragmentModule,
const VkViewport& viewport,
const VkRect2D scissor,
const VkSampleCountFlagBits numSamples,
const bool useSampleLocations,
const VkSampleLocationsInfoEXT& sampleLocationsInfo,
const VertexInputConfig vertexInputConfig,
const VkPrimitiveTopology topology)
{
return makeGraphicsPipeline(vk, device, dynamicState, pipelineLayout, renderPass, vertexModule, fragmentModule,
/*subpass*/ 0u, viewport, scissor, numSamples, useSampleLocations, sampleLocationsInfo,
/*depth test*/ false, /*stencil test*/ false, vertexInputConfig, topology, stencilOpStateIncrement());
}
//! Utility to build and maintain render pass, framebuffer and related resources.
//! Use bake() before using the render pass.
class RenderTarget
{
public:
RenderTarget (void)
{
nextSubpass();
}
//! Returns an attachment index that is used to reference this attachment later
deUint32 addAttachment (const VkImageView imageView,
const VkAttachmentDescriptionFlags flags,
const VkFormat format,
const VkSampleCountFlagBits numSamples,
const VkAttachmentLoadOp loadOp,
const VkAttachmentStoreOp storeOp,
const VkAttachmentLoadOp stencilLoadOp,
const VkAttachmentStoreOp stencilStoreOp,
const VkImageLayout initialLayout,
const VkImageLayout finalLayout,
const VkClearValue clearValue,
const VkSampleLocationsInfoEXT* pInitialSampleLocations = DE_NULL)
{
const deUint32 index = static_cast<deUint32>(m_attachments.size());
m_attachments.push_back(imageView);
m_attachmentDescriptions.push_back(makeAttachmentDescription(
flags, // VkAttachmentDescriptionFlags flags;
format, // VkFormat format;
numSamples, // VkSampleCountFlagBits samples;
loadOp, // VkAttachmentLoadOp loadOp;
storeOp, // VkAttachmentStoreOp storeOp;
stencilLoadOp, // VkAttachmentLoadOp stencilLoadOp;
stencilStoreOp, // VkAttachmentStoreOp stencilStoreOp;
initialLayout, // VkImageLayout initialLayout;
finalLayout // VkImageLayout finalLayout;
));
m_clearValues.push_back(clearValue); // always add, even if unused
if (pInitialSampleLocations)
{
const VkAttachmentSampleLocationsEXT attachmentSampleLocations =
{
index, // uint32_t attachmentIndex;
*pInitialSampleLocations, // VkSampleLocationsInfoEXT sampleLocationsInfo;
};
m_attachmentSampleLocations.push_back(attachmentSampleLocations);
}
return index;
}
void addSubpassColorAttachment (const deUint32 attachmentIndex, const VkImageLayout subpassLayout)
{
m_subpasses.back().colorAttachmentReferences.push_back(
makeAttachmentReference(attachmentIndex, subpassLayout));
m_subpasses.back().resolveAttachmentReferences.push_back(
makeAttachmentReference(VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_UNDEFINED));
}
void addSubpassColorAttachmentWithResolve (const deUint32 colorAttachmentIndex, const VkImageLayout colorSubpassLayout, const deUint32 resolveAttachmentIndex, const VkImageLayout resolveSubpassLayout)
{
m_subpasses.back().colorAttachmentReferences.push_back(
makeAttachmentReference(colorAttachmentIndex, colorSubpassLayout));
m_subpasses.back().resolveAttachmentReferences.push_back(
makeAttachmentReference(resolveAttachmentIndex, resolveSubpassLayout));
}
void addSubpassDepthStencilAttachment (const deUint32 attachmentIndex, const VkImageLayout subpassLayout, const VkSampleLocationsInfoEXT* pSampleLocations = DE_NULL)
{
m_subpasses.back().depthStencilAttachmentReferences.push_back(
makeAttachmentReference(attachmentIndex, subpassLayout));
if (pSampleLocations)
{
const VkSubpassSampleLocationsEXT subpassSampleLocations =
{
static_cast<deUint32>(m_subpasses.size() - 1), // uint32_t subpassIndex;
*pSampleLocations, // VkSampleLocationsInfoEXT sampleLocationsInfo;
};
m_subpassSampleLocations.push_back(subpassSampleLocations);
}
}
void addSubpassInputAttachment (const deUint32 attachmentIndex, const VkImageLayout subpassLayout)
{
m_subpasses.back().inputAttachmentReferences.push_back(
makeAttachmentReference(attachmentIndex, subpassLayout));
}
void addSubpassPreserveAttachment (const deUint32 attachmentIndex)
{
m_subpasses.back().preserveAttachmentReferences.push_back(attachmentIndex);
}
void nextSubpass (void)
{
m_subpasses.push_back(SubpassDescription());
}
//! Create a RenderPass and Framebuffer based on provided attachments
void bake (const DeviceInterface& vk,
const VkDevice device,
const UVec2& framebufferSize)
{
DE_ASSERT(!m_renderPass);
const deUint32 numSubpasses = static_cast<deUint32>(m_subpasses.size());
std::vector<VkSubpassDescription> subpassDescriptions;
std::vector<VkSubpassDependency> subpassDependencies;
for (deUint32 subpassNdx = 0; subpassNdx < numSubpasses; ++subpassNdx)
{
const SubpassDescription& sd = m_subpasses[subpassNdx];
const VkSubpassDescription description =
{
(VkSubpassDescriptionFlags)0, // VkSubpassDescriptionFlags flags;
VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
static_cast<deUint32>(sd.inputAttachmentReferences.size()), // deUint32 inputAttachmentCount;
dataOrNullPtr(sd.inputAttachmentReferences), // const VkAttachmentReference* pInputAttachments;
static_cast<deUint32>(sd.colorAttachmentReferences.size()), // deUint32 colorAttachmentCount;
dataOrNullPtr(sd.colorAttachmentReferences), // const VkAttachmentReference* pColorAttachments;
dataOrNullPtr(sd.resolveAttachmentReferences), // const VkAttachmentReference* pResolveAttachments;
dataOrNullPtr(sd.depthStencilAttachmentReferences), // const VkAttachmentReference* pDepthStencilAttachment;
static_cast<deUint32>(sd.preserveAttachmentReferences.size()), // deUint32 preserveAttachmentCount;
dataOrNullPtr(sd.preserveAttachmentReferences) // const deUint32* pPreserveAttachments;
};
subpassDescriptions.push_back(description);
// Add a very coarse dependency enforcing sequential ordering of subpasses
if (subpassNdx > 0)
{
static const VkAccessFlags accessAny = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
| VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
const VkSubpassDependency dependency =
{
subpassNdx - 1, // uint32_t srcSubpass;
subpassNdx, // uint32_t dstSubpass;
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, // VkPipelineStageFlags srcStageMask;
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, // VkPipelineStageFlags dstStageMask;
accessAny, // VkAccessFlags srcAccessMask;
accessAny, // VkAccessFlags dstAccessMask;
(VkDependencyFlags)0, // VkDependencyFlags dependencyFlags;
};
subpassDependencies.push_back(dependency);
}
}
const VkRenderPassCreateInfo renderPassInfo =
{
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags;
static_cast<deUint32>(m_attachmentDescriptions.size()), // deUint32 attachmentCount;
dataOrNullPtr(m_attachmentDescriptions), // const VkAttachmentDescription* pAttachments;
static_cast<deUint32>(subpassDescriptions.size()), // deUint32 subpassCount;
dataOrNullPtr(subpassDescriptions), // const VkSubpassDescription* pSubpasses;
static_cast<deUint32>(subpassDependencies.size()), // deUint32 dependencyCount;
dataOrNullPtr(subpassDependencies) // const VkSubpassDependency* pDependencies;
};
m_renderPass = createRenderPass(vk, device, &renderPassInfo);
m_framebuffer = makeFramebuffer (vk, device, *m_renderPass, static_cast<deUint32>(m_attachments.size()), dataOrNullPtr(m_attachments), framebufferSize.x(), framebufferSize.y());
}
VkRenderPass getRenderPass (void) const
{
DE_ASSERT(m_renderPass);
return *m_renderPass;
}
VkFramebuffer getFramebuffer (void) const
{
DE_ASSERT(m_framebuffer);
return *m_framebuffer;
}
void recordBeginRenderPass (const DeviceInterface& vk,
const VkCommandBuffer cmdBuffer,
const VkRect2D& renderArea,
const VkSubpassContents subpassContents) const
{
DE_ASSERT(m_renderPass);
DE_ASSERT(m_framebuffer);
const VkRenderPassSampleLocationsBeginInfoEXT renderPassSampleLocationsBeginInfo =
{
VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT, // VkStructureType sType;
DE_NULL, // const void* pNext;
static_cast<deUint32>(m_attachmentSampleLocations.size()), // uint32_t attachmentInitialSampleLocationsCount;
dataOrNullPtr(m_attachmentSampleLocations), // const VkAttachmentSampleLocationsEXT* pAttachmentInitialSampleLocations;
static_cast<deUint32>(m_subpassSampleLocations.size()), // uint32_t postSubpassSampleLocationsCount;
dataOrNullPtr(m_subpassSampleLocations), // const VkSubpassSampleLocationsEXT* pPostSubpassSampleLocations;
};
const VkRenderPassBeginInfo renderPassBeginInfo =
{
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, // VkStructureType sType;
&renderPassSampleLocationsBeginInfo, // const void* pNext;
*m_renderPass, // VkRenderPass renderPass;
*m_framebuffer, // VkFramebuffer framebuffer;
renderArea, // VkRect2D renderArea;
static_cast<deUint32>(m_clearValues.size()), // uint32_t clearValueCount;
dataOrNullPtr(m_clearValues), // const VkClearValue* pClearValues;
};
vk.cmdBeginRenderPass(cmdBuffer, &renderPassBeginInfo, subpassContents);
}
private:
struct SubpassDescription
{
std::vector<VkAttachmentReference> inputAttachmentReferences;
std::vector<VkAttachmentReference> colorAttachmentReferences;
std::vector<VkAttachmentReference> resolveAttachmentReferences;
std::vector<VkAttachmentReference> depthStencilAttachmentReferences;
std::vector<deUint32> preserveAttachmentReferences;
};
std::vector<SubpassDescription> m_subpasses;
std::vector<VkImageView> m_attachments;
std::vector<VkAttachmentDescription> m_attachmentDescriptions;
std::vector<VkClearValue> m_clearValues;
std::vector<VkAttachmentSampleLocationsEXT> m_attachmentSampleLocations;
std::vector<VkSubpassSampleLocationsEXT> m_subpassSampleLocations;
Move<VkRenderPass> m_renderPass;
Move<VkFramebuffer> m_framebuffer;
// No copying allowed
RenderTarget (const RenderTarget&);
RenderTarget& operator=(const RenderTarget&);
};
void recordImageBarrier (const DeviceInterface& vk,
const VkCommandBuffer cmdBuffer,
const VkImage image,
const VkImageAspectFlags aspect,
const VkPipelineStageFlags srcStageMask,
const VkPipelineStageFlags dstStageMask,
const VkAccessFlags srcAccessMask,
const VkAccessFlags dstAccessMask,
const VkImageLayout oldLayout,
const VkImageLayout newLayout,
const VkSampleLocationsInfoEXT* pSampleLocationsInfo = DE_NULL)
{
const VkImageMemoryBarrier barrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
pSampleLocationsInfo, // const void* pNext;
srcAccessMask, // VkAccessFlags srcAccessMask;
dstAccessMask, // VkAccessFlags dstAccessMask;
oldLayout, // VkImageLayout oldLayout;
newLayout, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex;
image, // VkImage image;
makeImageSubresourceRange(aspect, 0u, 1u, 0u, 1u), // VkImageSubresourceRange subresourceRange;
};
vk.cmdPipelineBarrier(cmdBuffer, srcStageMask, dstStageMask, (VkDependencyFlags)0, 0u, DE_NULL, 0u, DE_NULL, 1u, &barrier);
}
void recordWaitEventWithImage (const DeviceInterface& vk,
const VkCommandBuffer cmdBuffer,
const VkEvent event,
const VkImage image,
const VkImageAspectFlags aspect,
const VkPipelineStageFlags srcStageMask,
const VkPipelineStageFlags dstStageMask,
const VkAccessFlags srcAccessMask,
const VkAccessFlags dstAccessMask,
const VkImageLayout oldLayout,
const VkImageLayout newLayout,
const VkSampleLocationsInfoEXT* pSampleLocationsInfo = DE_NULL)
{
const VkImageMemoryBarrier barrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
pSampleLocationsInfo, // const void* pNext;
srcAccessMask, // VkAccessFlags srcAccessMask;
dstAccessMask, // VkAccessFlags dstAccessMask;
oldLayout, // VkImageLayout oldLayout;
newLayout, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex;
image, // VkImage image;
makeImageSubresourceRange(aspect, 0u, 1u, 0u, 1u), // VkImageSubresourceRange subresourceRange;
};
vk.cmdWaitEvents(
cmdBuffer, // VkCommandBuffer commandBuffer,
1u, // uint32_t eventCount,
&event, // const VkEvent* pEvents,
srcStageMask, // VkPipelineStageFlags srcStageMask,
dstStageMask, // VkPipelineStageFlags dstStageMask,
0u, // uint32_t memoryBarrierCount,
DE_NULL, // const VkMemoryBarrier* pMemoryBarriers,
0u, // uint32_t bufferMemoryBarrierCount,
DE_NULL, // const VkBufferMemoryBarrier* pBufferMemoryBarriers,
1u, // uint32_t imageMemoryBarrierCount,
&barrier); // const VkImageMemoryBarrier* pImageMemoryBarriers);
}
void recordCopyImageToBuffer (const DeviceInterface& vk,
const VkCommandBuffer cmdBuffer,
const UVec2& imageSize,
const VkImage srcImage,
const VkBuffer dstBuffer)
{
// Resolve image -> host buffer
{
const VkBufferImageCopy region =
{
0ull, // VkDeviceSize bufferOffset;
0u, // uint32_t bufferRowLength;
0u, // uint32_t bufferImageHeight;
makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u), // VkImageSubresourceLayers imageSubresource;
makeOffset3D(0, 0, 0), // VkOffset3D imageOffset;
makeExtent3D(imageSize.x(), imageSize.y(), 1u), // VkExtent3D imageExtent;
};
vk.cmdCopyImageToBuffer(cmdBuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstBuffer, 1u, &region);
}
// Buffer write barrier
{
const VkBufferMemoryBarrier barrier =
{
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, // uint32_t srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex;
dstBuffer, // VkBuffer buffer;
0ull, // VkDeviceSize offset;
VK_WHOLE_SIZE, // VkDeviceSize size;
};
vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0,
0u, DE_NULL, 1u, &barrier, DE_NULL, 0u);
}
}
void recordClearAttachments (const DeviceInterface& vk,
const VkCommandBuffer cmdBuffer,
const deUint32 colorAttachment,
const VkClearValue& colorClearValue,
const VkImageAspectFlags depthStencilAspect,
const VkClearValue& depthStencilClearValue,
const VkRect2D& clearRect)
{
std::vector<VkClearAttachment> attachments;
const VkClearRect rect =
{
clearRect, // VkRect2D rect;
0u, // uint32_t baseArrayLayer;
1u, // uint32_t layerCount;
};
// Clear color
{
const VkClearAttachment attachment =
{
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
colorAttachment, // uint32_t colorAttachment;
colorClearValue, // VkClearValue clearValue;
};
attachments.push_back(attachment);
}
if ((depthStencilAspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0u)
{
const VkClearAttachment attachment =
{
depthStencilAspect, // VkImageAspectFlags aspectMask;
VK_ATTACHMENT_UNUSED, // uint32_t colorAttachment;
depthStencilClearValue, // VkClearValue clearValue;
};
attachments.push_back(attachment);
}
vk.cmdClearAttachments(cmdBuffer, static_cast<deUint32>(attachments.size()), dataOrNullPtr(attachments), 1u, &rect);
}
//! Suitable for executing in a render pass, no queries
void beginSecondaryCommandBuffer (const DeviceInterface& vk,
const VkCommandBuffer commandBuffer,
const VkRenderPass renderPass,
const deUint32 subpass,
const VkFramebuffer framebuffer)
{
const VkCommandBufferInheritanceInfo inheritanceInfo =
{
VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
renderPass, // VkRenderPass renderPass;
subpass, // uint32_t subpass;
framebuffer, // VkFramebuffer framebuffer;
VK_FALSE, // VkBool32 occlusionQueryEnable;
(VkQueryControlFlags)0, // VkQueryControlFlags queryFlags;
(VkQueryPipelineStatisticFlags)0, // VkQueryPipelineStatisticFlags pipelineStatistics;
};
const VkCommandBufferBeginInfo beginInfo =
{
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
|VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT), // VkCommandBufferUsageFlags flags;
&inheritanceInfo, // const VkCommandBufferInheritanceInfo* pInheritanceInfo;
};
VK_CHECK(vk.beginCommandBuffer(commandBuffer, &beginInfo));
}
//! Verify results of a VkPhysicalDeviceSampleLocationsPropertiesEXT query with VkPhysicalDeviceProperties2KHR
tcu::TestStatus testQuerySampleLocationProperties (Context& context)
{
requireExtensions(context);
const VkPhysicalDeviceSampleLocationsPropertiesEXT sampleLocationsProperties = getSampleLocationsPropertiesEXT(context);
context.getTestContext().getLog()
<< tcu::TestLog::Section("VkPhysicalDeviceSampleLocationsPropertiesEXT", "Query results")
<< tcu::TestLog::Message << sampleLocationsProperties << tcu::TestLog::EndMessage
<< tcu::TestLog::EndSection;
const VkSampleCountFlags allowedSampleCounts = (VK_SAMPLE_COUNT_2_BIT |
VK_SAMPLE_COUNT_4_BIT |
VK_SAMPLE_COUNT_8_BIT |
VK_SAMPLE_COUNT_16_BIT |
VK_SAMPLE_COUNT_32_BIT |
VK_SAMPLE_COUNT_64_BIT);
if ((sampleLocationsProperties.sampleLocationSampleCounts & allowedSampleCounts) == 0)
{
return tcu::TestStatus::fail("VkPhysicalDeviceSampleLocationsPropertiesEXT: sampleLocationSampleCounts should specify at least one MSAA sample count");
}
if (sampleLocationsProperties.maxSampleLocationGridSize.width == 0u ||
sampleLocationsProperties.maxSampleLocationGridSize.height == 0u ||
sampleLocationsProperties.maxSampleLocationGridSize.width > 16384u || // max not specified, but try to catch nonsense values like -1
sampleLocationsProperties.maxSampleLocationGridSize.height > 16384u)
{
return tcu::TestStatus::fail("VkPhysicalDeviceSampleLocationsPropertiesEXT: maxSampleLocationGridSize must be at least (1,1) size");
}
for (int i = 0; i < 2; ++i)
{
if (sampleLocationsProperties.sampleLocationCoordinateRange[i] < 0.0f ||
sampleLocationsProperties.sampleLocationCoordinateRange[i] > 1.0f)
{
return tcu::TestStatus::fail("VkPhysicalDeviceSampleLocationsPropertiesEXT: sampleLocationCoordinateRange[] values must be in [0, 1] range");
}
}
if (sampleLocationsProperties.sampleLocationSubPixelBits == 0u ||
sampleLocationsProperties.sampleLocationSubPixelBits > 64u) // max not specified, but try to catch nonsense values
{
return tcu::TestStatus::fail("VkPhysicalDeviceSampleLocationsPropertiesEXT: sampleLocationSubPixelBits should be greater than 0");
}
return tcu::TestStatus::pass("Pass");
}
//! Verify results of vkGetPhysicalDeviceMultisamplePropertiesEXT queries
tcu::TestStatus testQueryMultisampleProperties (Context& context)
{
requireExtensions(context);
const InstanceInterface& vki = context.getInstanceInterface();
const VkPhysicalDevice physicalDevice = context.getPhysicalDevice();
tcu::TestLog& log = context.getTestContext().getLog();
const VkPhysicalDeviceSampleLocationsPropertiesEXT sampleLocationsProperties = getSampleLocationsPropertiesEXT(context);
const VkSampleCountFlagBits sampleCountRange[] =
{
VK_SAMPLE_COUNT_1_BIT,
VK_SAMPLE_COUNT_2_BIT,
VK_SAMPLE_COUNT_4_BIT,
VK_SAMPLE_COUNT_8_BIT,
VK_SAMPLE_COUNT_16_BIT,
VK_SAMPLE_COUNT_32_BIT,
VK_SAMPLE_COUNT_64_BIT,
};
bool allOk = true;
for (const VkSampleCountFlagBits* pLoopNumSamples = sampleCountRange; pLoopNumSamples < DE_ARRAY_END(sampleCountRange); ++pLoopNumSamples)
{
VkMultisamplePropertiesEXT multisampleProperties =
{
VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT, // VkStructureType sType;
DE_NULL, // void* pNext;
VkExtent2D(), // VkExtent2D maxSampleLocationGridSize;
};
vki.getPhysicalDeviceMultisamplePropertiesEXT(physicalDevice, *pLoopNumSamples, &multisampleProperties);
log << tcu::TestLog::Section("getPhysicalDeviceMultisamplePropertiesEXT", "Query results")
<< tcu::TestLog::Message << "Sample count: " << *pLoopNumSamples << tcu::TestLog::EndMessage
<< tcu::TestLog::Message << multisampleProperties << tcu::TestLog::EndMessage;
const bool isSupportedSampleCount = (*pLoopNumSamples & sampleLocationsProperties.sampleLocationSampleCounts) != 0;
if (isSupportedSampleCount)
{
if (!(multisampleProperties.maxSampleLocationGridSize.width >= sampleLocationsProperties.maxSampleLocationGridSize.width &&
multisampleProperties.maxSampleLocationGridSize.height >= sampleLocationsProperties.maxSampleLocationGridSize.height))
{
allOk = false;
log << tcu::TestLog::Message
<< "FAIL: Grid size should be the same or larger than VkPhysicalDeviceSampleLocationsPropertiesEXT::maxSampleLocationGridSize"
<< tcu::TestLog::EndMessage;
}
}
else
{
if (!(multisampleProperties.maxSampleLocationGridSize.width == 0u &&
multisampleProperties.maxSampleLocationGridSize.height == 0u))
{
allOk = false;
log << tcu::TestLog::Message << "FAIL: Expected (0, 0) grid size" << tcu::TestLog::EndMessage;
}
}
log << tcu::TestLog::EndSection;
}
return allOk ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Some values were incorrect");
}
// These tests only use a color attachment and focus on per-sample data
namespace VerifySamples
{
//! Data layout used in verify sample locations and interpolation cases
namespace SampleDataSSBO
{
static VkDeviceSize STATIC_SIZE = 6 * sizeof(deUint32);
static UVec2& renderSize (void* const basePtr) { return *reinterpret_cast<UVec2*> (static_cast<deUint8*>(basePtr) + 0 * sizeof(deUint32)); }
static UVec2& gridSize (void* const basePtr) { return *reinterpret_cast<UVec2*> (static_cast<deUint8*>(basePtr) + 2 * sizeof(deUint32)); }
static deUint32& samplesPerPixel (void* const basePtr) { return *reinterpret_cast<deUint32*> (static_cast<deUint8*>(basePtr) + 4 * sizeof(deUint32)); }
template<typename T>
static T* sampleData (void* const basePtr) { DE_STATIC_ASSERT(sizeof(T) == sizeof(Vec2));
return reinterpret_cast<T*> (static_cast<deUint8*>(basePtr) + STATIC_SIZE); }
} // SampleDataSSBO
enum TestOptionFlagBits
{
TEST_OPTION_DYNAMIC_STATE_BIT = 0x1, //!< Use dynamic pipeline state to pass in sample locations
TEST_OPTION_CLOSELY_PACKED_BIT = 0x2, //!< Place samples as close as possible to each other
};
typedef deUint32 TestOptionFlags;
struct TestParams
{
VkSampleCountFlagBits numSamples;
TestOptionFlags options;
};
std::string declareSampleDataSSBO (void)
{
std::ostringstream str;
str << "layout(set = 0, binding = 0, std430) readonly buffer SampleData {\n" // make sure this matches SampleDataSSBO definition
<< " uvec2 renderSize;\n"
<< " uvec2 gridSize;\n"
<< " uint samplesPerPixel;\n"
<< " // padding 1-uint size;\n"
<< " vec2 data[];\n"
<< "} sb_data;\n";
return str.str();
};
void addProgramsVerifyLocationGeometry (SourceCollections& programCollection, const TestParams)
{
// Vertex shader
{
std::ostringstream src;
src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
<< "\n"
<< "layout(location = 0) in vec4 in_position;\n"
<< "\n"
<< "out gl_PerVertex {\n"
<< " vec4 gl_Position;\n"
<< "};\n"
<< "\n"
<< "void main(void)\n"
<< "{\n"
<< " gl_Position = in_position;\n"
<< "}\n";
programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
}
// Fragment shader
{
std::ostringstream src;
src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
<< "\n"
<< "layout(location = 0) out vec4 o_color;\n"
<< "\n"
<< declareSampleDataSSBO()
<< "\n"
<< "void main(void)\n"
<< "{\n"
<< " uvec2 fragCoord = uvec2(gl_FragCoord.xy);\n"
<< " uint index = (fragCoord.y * sb_data.renderSize.x + fragCoord.x) * sb_data.samplesPerPixel + gl_SampleID;\n"
<< "\n"
<< " if (gl_PrimitiveID == index)\n"
<< " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
<< " else\n"
<< " o_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
<< "}\n";
programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
}
}
void addProgramsVerifyInterpolation (SourceCollections& programCollection, const TestParams)
{
// Vertex shader
{
std::ostringstream src;
src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
<< "\n"
<< "layout(location = 0) in vec4 in_position;\n"
<< "layout(location = 0) out vec2 o_position;\n"
<< "\n"
<< "out gl_PerVertex {\n"
<< " vec4 gl_Position;\n"
<< "};\n"
<< "\n"
<< "void main(void)\n"
<< "{\n"
<< " gl_Position = in_position;\n"
<< " o_position = in_position.xy;\n" // user-data that will be interpolated
<< "}\n";
programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
}
// Fragment shader
{
std::ostringstream src;
src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
<< "\n"
<< "layout(location = 0) sample in vec2 in_value;\n"
<< "layout(location = 0) out vec4 o_color;\n"
<< "\n"
<< declareSampleDataSSBO()
<< "\n"
<< "void main(void)\n"
<< "{\n"
<< " uvec2 fragCoord = uvec2(gl_FragCoord.xy);\n"
<< " uint index = (fragCoord.y * sb_data.renderSize.x + fragCoord.x) * sb_data.samplesPerPixel + gl_SampleID;\n"
<< " vec2 diff = abs(sb_data.data[index] - in_value);\n"
<< " vec2 threshold = vec2(0.002);\n"
<< "\n"
<< " if (all(lessThan(diff, threshold)))\n"
<< " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
<< " else\n"
<< " o_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
<< "}\n";
programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
}
}
class TestBase : public TestInstance
{
public:
TestBase (Context& context, const TestParams params)
: TestInstance (context)
, m_params (params)
, m_sampleLocationsProperties (getSampleLocationsPropertiesEXT(context))
, m_colorFormat (VK_FORMAT_R8G8B8A8_UNORM)
, m_numVertices (0)
, m_currentGridNdx (0)
{
requireExtensions(context);
VkMultisamplePropertiesEXT multisampleProperties =
{
VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT, // VkStructureType sType;
DE_NULL, // void* pNext;
VkExtent2D(), // VkExtent2D maxSampleLocationGridSize;
};
m_context.getInstanceInterface().getPhysicalDeviceMultisamplePropertiesEXT(m_context.getPhysicalDevice(), m_params.numSamples, &multisampleProperties);
// Generate grid size combinations
for (deUint32 y = multisampleProperties.maxSampleLocationGridSize.height; y >= 1u; y >>= 1)
for (deUint32 x = multisampleProperties.maxSampleLocationGridSize.width; x >= 1u; x >>= 1)
{
DE_ASSERT(multisampleProperties.maxSampleLocationGridSize.width % x == 0u);
DE_ASSERT(multisampleProperties.maxSampleLocationGridSize.height % y == 0u);
m_gridSizes.push_back(UVec2(x, y));
}
}
tcu::TestStatus iterate (void)
{
// Will be executed several times, for all possible pixel grid sizes
const VkPhysicalDeviceLimits& limits = m_context.getDeviceProperties().limits;
if (!m_context.getDeviceFeatures().sampleRateShading)
TCU_THROW(NotSupportedError, "Missing feature: sampleRateShading");
if ((limits.framebufferColorSampleCounts & m_params.numSamples) == 0u)
TCU_THROW(NotSupportedError, "framebufferColorSampleCounts: sample count not supported");
if ((m_sampleLocationsProperties.sampleLocationSampleCounts & m_params.numSamples) == 0u)
TCU_THROW(NotSupportedError, "VkPhysicalDeviceSampleLocationsPropertiesEXT: sample count not supported");
if (!(currentGridSize().x() >= 1 && currentGridSize().y() >= 1))
return tcu::TestStatus::fail("maxSampleLocationGridSize is invalid");
// Prepare the pixel grid
{
const deUint32 pixelGridRepetitions = 2; // just to make sure the pattern is consistently applied across the framebuffer
m_renderSize = UVec2(pixelGridRepetitions * currentGridSize().x(),
pixelGridRepetitions * currentGridSize().y());
m_pixelGrid = MovePtr<MultisamplePixelGrid>(new MultisamplePixelGrid(currentGridSize(), m_params.numSamples));
if ((m_params.options & TEST_OPTION_CLOSELY_PACKED_BIT) != 0u)
fillSampleLocationsPacked(*m_pixelGrid, m_sampleLocationsProperties.sampleLocationSubPixelBits);
else
fillSampleLocationsRandom(*m_pixelGrid, m_sampleLocationsProperties.sampleLocationSubPixelBits);
logPixelGrid (m_context.getTestContext().getLog(), m_sampleLocationsProperties, *m_pixelGrid);
}
// Create images
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
// Images and staging buffers
m_colorImage = makeImage(vk, device, (VkImageCreateFlags)0, m_colorFormat, m_renderSize, m_params.numSamples, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
m_colorImageAlloc = bindImage(vk, device, allocator, *m_colorImage, MemoryRequirement::Any);
m_colorImageView = makeImageView(vk, device, *m_colorImage, VK_IMAGE_VIEW_TYPE_2D, m_colorFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u));
m_resolveImage = makeImage(vk, device, (VkImageCreateFlags)0, m_colorFormat, m_renderSize, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
m_resolveImageAlloc = bindImage(vk, device, allocator, *m_resolveImage, MemoryRequirement::Any);
m_resolveImageView = makeImageView(vk, device, *m_resolveImage, VK_IMAGE_VIEW_TYPE_2D, m_colorFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u));
const VkDeviceSize colorBufferSize = m_renderSize.x() * m_renderSize.y() * tcu::getPixelSize(mapVkFormat(m_colorFormat));
m_colorBuffer = makeBuffer(vk, device, colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
m_colorBufferAlloc = bindBuffer(vk, device, allocator, *m_colorBuffer, MemoryRequirement::HostVisible);
}
if (!testPixelGrid())
return tcu::TestStatus::fail("Fail");
if (shrinkCurrentGrid())
return tcu::TestStatus::incomplete();
else
return tcu::TestStatus::pass("Pass");
}
protected:
//! Return true if the test passed the current grid size
virtual bool testPixelGrid (void) = 0;
const UVec2& currentGridSize (void)
{
return m_gridSizes[m_currentGridNdx];
}
//! Return false if the grid is already at (1, 1) size
bool shrinkCurrentGrid (void)
{
if (m_gridSizes.size() <= m_currentGridNdx + 1)
return false;
++m_currentGridNdx;
return true;
}
void drawSinglePass (const VertexInputConfig vertexInputConfig)
{
DE_ASSERT(m_descriptorSetLayout);
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const VkViewport viewport = makeViewport(m_renderSize);
const VkRect2D renderArea = makeRect2D(m_renderSize);
const VkRect2D scissor = makeRect2D(m_renderSize);
const Unique<VkShaderModule> vertexModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0u));
const Unique<VkShaderModule> fragmentModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0u));
const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *m_descriptorSetLayout));
const bool useDynamicStateSampleLocations = ((m_params.options & TEST_OPTION_DYNAMIC_STATE_BIT) != 0u);
const VkSampleLocationsInfoEXT sampleLocationsInfo = makeSampleLocationsInfo(*m_pixelGrid);
RenderTarget rt;
rt.addAttachment(
*m_colorImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_colorFormat, // VkFormat format,
m_params.numSamples, // VkSampleCountFlagBits numSamples,
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,
makeClearValueColor(CLEAR_COLOR_0)); // VkClearValue clearValue,
rt.addAttachment(
*m_resolveImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_colorFormat, // VkFormat format,
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits numSamples,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // 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_TRANSFER_SRC_OPTIMAL, // VkImageLayout finalLayout,
VkClearValue()); // VkClearValue clearValue,
rt.addSubpassColorAttachmentWithResolve(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
rt.bake(vk, device, m_renderSize);
Move<VkPipeline> pipeline;
if (useDynamicStateSampleLocations)
{
std::vector<VkDynamicState> dynamicState;
dynamicState.push_back(VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT);
pipeline = makeGraphicsPipelineSinglePassColor(
vk, device, dynamicState, *pipelineLayout, rt.getRenderPass(), *vertexModule, *fragmentModule, viewport, scissor,
m_params.numSamples, /*use sample locations*/ true, makeEmptySampleLocationsInfo(), vertexInputConfig, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
}
else
{
pipeline = makeGraphicsPipelineSinglePassColor(
vk, device, std::vector<VkDynamicState>(), *pipelineLayout, rt.getRenderPass(), *vertexModule, *fragmentModule, viewport, scissor,
m_params.numSamples, /*use sample locations*/ true, sampleLocationsInfo, vertexInputConfig, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
}
const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, m_context.getUniversalQueueFamilyIndex()));
const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer(vk, device, *cmdPool));
beginCommandBuffer(vk, *cmdBuffer);
rt.recordBeginRenderPass(vk, *cmdBuffer, renderArea, VK_SUBPASS_CONTENTS_INLINE);
vk.cmdBindVertexBuffers(*cmdBuffer, /*first binding*/ 0u, /*num bindings*/ 1u, &m_vertexBuffer.get(), /*offsets*/ &ZERO);
vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
if (useDynamicStateSampleLocations)
vk.cmdSetSampleLocationsEXT(*cmdBuffer, &sampleLocationsInfo);
if (m_descriptorSet)
vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &m_descriptorSet.get(), 0u, DE_NULL);
vk.cmdDraw(*cmdBuffer, m_numVertices, 1u, 0u, 0u);
endRenderPass(vk, *cmdBuffer);
// Resolve image -> host buffer
recordImageBarrier(vk, *cmdBuffer, *m_resolveImage,
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspect,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask,
VK_PIPELINE_STAGE_TRANSFER_BIT, // VkPipelineStageFlags dstStageMask,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask,
VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout oldLayout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); // VkImageLayout newLayout)
recordCopyImageToBuffer(vk, *cmdBuffer, m_renderSize, *m_resolveImage, *m_colorBuffer);
endCommandBuffer(vk, *cmdBuffer);
submitCommandsAndWait(vk, device, m_context.getUniversalQueue(), *cmdBuffer);
invalidateAlloc(vk, device, *m_colorBufferAlloc);
}
void createSampleDataBufferAndDescriptors (const VkDeviceSize bufferSize)
{
// Make sure the old descriptor set is destroyed before we destroy its pool
m_descriptorSet = Move<VkDescriptorSet>();
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
m_sampleDataBuffer = makeBuffer(vk, device, bufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
m_sampleDataBufferAlloc = bindBuffer(vk, device, allocator, *m_sampleDataBuffer, MemoryRequirement::HostVisible);
m_descriptorSetLayout = DescriptorSetLayoutBuilder()
.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT)
.build(vk, device);
m_descriptorPool = DescriptorPoolBuilder()
.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
m_descriptorSet = makeDescriptorSet(vk, device, *m_descriptorPool, *m_descriptorSetLayout);
const VkDescriptorBufferInfo bufferDescriptorInfo = makeDescriptorBufferInfo(*m_sampleDataBuffer, 0ull, bufferSize);
DescriptorSetUpdateBuilder()
.writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &bufferDescriptorInfo)
.update(vk, device);
SampleDataSSBO::renderSize (m_sampleDataBufferAlloc->getHostPtr()) = m_renderSize;
SampleDataSSBO::gridSize (m_sampleDataBufferAlloc->getHostPtr()) = m_pixelGrid->size();
SampleDataSSBO::samplesPerPixel (m_sampleDataBufferAlloc->getHostPtr()) = m_pixelGrid->samplesPerPixel();
flushAlloc(vk, device, *m_sampleDataBufferAlloc);
}
template<typename Vertex>
void createVertexBuffer (const std::vector<Vertex>& vertices)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
const VkDeviceSize vertexBufferSize = static_cast<VkDeviceSize>(vertices.size() * sizeof(vertices[0]));
m_numVertices = static_cast<deUint32>(vertices.size());
m_vertexBuffer = makeBuffer(vk, device, vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
m_vertexBufferAlloc = bindBuffer(vk, device, allocator, *m_vertexBuffer, MemoryRequirement::HostVisible);
deMemcpy(m_vertexBufferAlloc->getHostPtr(), dataOrNullPtr(vertices), static_cast<std::size_t>(vertexBufferSize));
flushAlloc(vk, device, *m_vertexBufferAlloc);
}
const TestParams m_params;
const VkPhysicalDeviceSampleLocationsPropertiesEXT m_sampleLocationsProperties;
const VkFormat m_colorFormat;
UVec2 m_renderSize;
MovePtr<MultisamplePixelGrid> m_pixelGrid;
deUint32 m_numVertices;
Move<VkBuffer> m_vertexBuffer;
MovePtr<Allocation> m_vertexBufferAlloc;
Move<VkImage> m_colorImage;
Move<VkImageView> m_colorImageView;
MovePtr<Allocation> m_colorImageAlloc;
Move<VkImage> m_resolveImage;
Move<VkImageView> m_resolveImageView;
MovePtr<Allocation> m_resolveImageAlloc;
Move<VkBuffer> m_colorBuffer;
MovePtr<Allocation> m_colorBufferAlloc;
Move<VkBuffer> m_sampleDataBuffer;
MovePtr<Allocation> m_sampleDataBufferAlloc;
Move<VkDescriptorSetLayout> m_descriptorSetLayout;
Move<VkDescriptorPool> m_descriptorPool;
Move<VkDescriptorSet> m_descriptorSet;
private:
deUint32 m_currentGridNdx;
std::vector<UVec2> m_gridSizes;
};
//! Check that each custom sample has the expected position
class VerifyLocationTest : public TestBase
{
public:
VerifyLocationTest (Context& context, const TestParams params) : TestBase(context, params) {}
bool testPixelGrid (void)
{
// Create vertices
{
// For each sample location (in the whole framebuffer), create a sub-pixel triangle that contains it.
// NDC viewport size is 2.0 in X and Y and NDC pixel width/height depends on the framebuffer resolution.
const Vec2 pixelSize = Vec2(2.0f) / m_renderSize.cast<float>();
const Vec2 offset = pixelSize / UVec2(1u << m_sampleLocationsProperties.sampleLocationSubPixelBits).cast<float>();
std::vector<Vec4> vertices;
// Surround with a roughly centered triangle
const float y1 = 0.5f * offset.y();
const float y2 = 0.35f * offset.y();
const float x1 = 0.5f * offset.x();
const std::vector<Vec2> locations = genFramebufferSampleLocations(*m_pixelGrid, m_pixelGrid->size(), m_renderSize);
for (std::vector<Vec2>::const_iterator iter = locations.begin(); iter != locations.end(); ++iter)
{
vertices.push_back(Vec4(iter->x(), iter->y() - y1, 0.0f, 1.0f));
vertices.push_back(Vec4(iter->x() - x1, iter->y() + y2, 0.0f, 1.0f));
vertices.push_back(Vec4(iter->x() + x1, iter->y() + y2, 0.0f, 1.0f));
}
createVertexBuffer(vertices);
}
createSampleDataBufferAndDescriptors(SampleDataSSBO::STATIC_SIZE); // no per-sample data used
drawSinglePass(VERTEX_INPUT_VEC4); // sample locations are taken from the pixel grid
// Verify
const tcu::ConstPixelBufferAccess image (tcu::ConstPixelBufferAccess(mapVkFormat(m_colorFormat), tcu::IVec3(m_renderSize.x(), m_renderSize.y(), 1), m_colorBufferAlloc->getHostPtr()));
return compareGreenImage(m_context.getTestContext().getLog(), "resolve0", "Resolved test image", image);
}
};
//! Verify that vertex attributes are correctly interpolated at each custom sample location
class VerifyInterpolationTest : public TestBase
{
public:
VerifyInterpolationTest (Context& context, const TestParams params) : TestBase(context, params) {}
bool testPixelGrid (void)
{
createVertexBuffer(genVerticesFullQuad());
// Create sample data SSBO
{
const deUint32 numSamples = m_pixelGrid->samplesPerPixel();
const deUint32 numDataEntries = numSamples * m_renderSize.x() * m_renderSize.y();
const VkDeviceSize bufferSize = SampleDataSSBO::STATIC_SIZE + sizeof(Vec2) * numDataEntries;
createSampleDataBufferAndDescriptors(bufferSize);
Vec2* const pSampleData = SampleDataSSBO::sampleData<Vec2>(m_sampleDataBufferAlloc->getHostPtr());
const std::vector<Vec2> locations = genFramebufferSampleLocations(*m_pixelGrid, m_pixelGrid->size(), m_renderSize);
// Fill SSBO with interpolated values (here: from -1.0 to 1.0 across the render area in both x and y)
DE_ASSERT(locations.size() == numDataEntries);
std::copy(locations.begin(), locations.end(), pSampleData);
flushAlloc(m_context.getDeviceInterface(), m_context.getDevice(), *m_sampleDataBufferAlloc);
}
drawSinglePass(VERTEX_INPUT_VEC4_VEC4); // sample locations are taken from the pixel grid
// Verify
const tcu::ConstPixelBufferAccess image (tcu::ConstPixelBufferAccess(mapVkFormat(m_colorFormat), tcu::IVec3(m_renderSize.x(), m_renderSize.y(), 1), m_colorBufferAlloc->getHostPtr()));
return compareGreenImage(m_context.getTestContext().getLog(), "resolve0", "Resolved test image", image);
}
};
template<typename Test, typename ProgramsFunc>
void addCases (tcu::TestCaseGroup* group, const VkSampleCountFlagBits numSamples, const ProgramsFunc initPrograms)
{
TestParams params;
deMemset(&params, 0, sizeof(params));
params.numSamples = numSamples;
params.options = (TestOptionFlags)0;
addInstanceTestCaseWithPrograms<Test>(group, getString(numSamples).c_str(), "", initPrograms, params);
params.options = (TestOptionFlags)TEST_OPTION_DYNAMIC_STATE_BIT;
addInstanceTestCaseWithPrograms<Test>(group, (getString(numSamples) + "_dynamic").c_str(), "", initPrograms, params);
params.options = (TestOptionFlags)TEST_OPTION_CLOSELY_PACKED_BIT;
addInstanceTestCaseWithPrograms<Test>(group, (getString(numSamples) + "_packed").c_str(), "", initPrograms, params);
}
} // VerifySamples
// Draw tests with at least two "passes" where sample locations may change.
// Test case is based on a combination of parameters defined below. Not all combinations are compatible.
namespace Draw
{
//! Options common to all test cases
enum TestOptionFlagBits
{
TEST_OPTION_SAME_PATTERN_BIT = 1u << 0, //!< Use the same sample pattern for all operations
TEST_OPTION_DYNAMIC_STATE_BIT = 1u << 1, //!< Use dynamic pipeline state to pass in sample locations
TEST_OPTION_SECONDARY_COMMAND_BUFFER_BIT = 1u << 2, //!< Put drawing commands in a secondary buffer, including sample locations change (if dynamic)
TEST_OPTION_GENERAL_LAYOUT_BIT = 1u << 3, //!< Transition the image to general layout at some point in rendering
TEST_OPTION_WAIT_EVENTS_BIT = 1u << 4, //!< Use image memory barriers with vkCmdWaitEvents rather than vkCmdPipelineBarrier
};
typedef deUint32 TestOptionFlags;
//! Determines where draws/clears with custom samples occur in the test
enum TestDrawIn
{
TEST_DRAW_IN_RENDER_PASSES = 0u, //!< Each operation in a separate render pass
TEST_DRAW_IN_SUBPASSES, //!< Each operation in a separate subpass of the same render pass
TEST_DRAW_IN_SAME_SUBPASS, //!< Each operation in the same subpass
};
//! How a clear before the second pass will be done
enum TestClears
{
TEST_CLEARS_NO_CLEAR = 0u, //!< Don't clear
TEST_CLEARS_LOAD_OP_CLEAR, //!< Render pass attachment load clear
TEST_CLEARS_CMD_CLEAR_ATTACHMENTS, //!< vkCmdClearAttachments within a subpass
TEST_CLEARS_CMD_CLEAR_IMAGE, //!< vkCmdClear{Color|DepthStencil}Image outside a render pass
};
//! What type of image will be verified with custom samples
enum TestImageAspect
{
TEST_IMAGE_ASPECT_COLOR = 0u, //!< Color image
TEST_IMAGE_ASPECT_DEPTH, //!< Depth aspect of an image (can be mixed format)
TEST_IMAGE_ASPECT_STENCIL, //!< Stencil aspect of an image (can be mixed format)
};
struct TestParams
{
VkSampleCountFlagBits numSamples;
TestOptionFlags options;
TestDrawIn drawIn;
TestClears clears;
TestImageAspect imageAspect;
};
const char* getString (const TestImageAspect aspect)
{
switch (aspect)
{
case TEST_IMAGE_ASPECT_COLOR: return "color";
case TEST_IMAGE_ASPECT_DEPTH: return "depth";
case TEST_IMAGE_ASPECT_STENCIL: return "stencil";
}
DE_ASSERT(0);
return DE_NULL;
}
const char* getString (const TestDrawIn drawIn)
{
switch (drawIn)
{
case TEST_DRAW_IN_RENDER_PASSES: return "separate_renderpass";
case TEST_DRAW_IN_SUBPASSES: return "separate_subpass";
case TEST_DRAW_IN_SAME_SUBPASS: return "same_subpass";
}
DE_ASSERT(0);
return DE_NULL;
}
const char* getString (const TestClears clears)
{
switch (clears)
{
case TEST_CLEARS_NO_CLEAR: return "no_clear";
case TEST_CLEARS_LOAD_OP_CLEAR: return "load_op_clear";
case TEST_CLEARS_CMD_CLEAR_ATTACHMENTS: return "clear_attachments";
case TEST_CLEARS_CMD_CLEAR_IMAGE: return "clear_image";
}
DE_ASSERT(0);
return DE_NULL;
}
std::string getTestOptionFlagsString (const deUint32 flags)
{
std::ostringstream str;
if ((flags & TEST_OPTION_SAME_PATTERN_BIT) != 0) str << (str.tellp() > 0 ? "_" : "") << "same_pattern";
if ((flags & TEST_OPTION_DYNAMIC_STATE_BIT) != 0) str << (str.tellp() > 0 ? "_" : "") << "dynamic";
if ((flags & TEST_OPTION_SECONDARY_COMMAND_BUFFER_BIT) != 0) str << (str.tellp() > 0 ? "_" : "") << "secondary_cmd_buf";
if ((flags & TEST_OPTION_GENERAL_LAYOUT_BIT) != 0) str << (str.tellp() > 0 ? "_" : "") << "general_layout";
if ((flags & TEST_OPTION_WAIT_EVENTS_BIT) != 0) str << (str.tellp() > 0 ? "_" : "") << "event";
return str.str();
}
void initPrograms (SourceCollections& programCollection, const TestParams)
{
// Vertex shader
{
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 o_color;\n"
<< "\n"
<< "out gl_PerVertex {\n"
<< " vec4 gl_Position;\n"
<< "};\n"
<< "\n"
<< "void main(void)\n"
<< "{\n"
<< " gl_Position = in_position;\n"
<< " o_color = in_color;\n"
<< "\n"
// We use instance index to draw the left shape (index = 0) or the right shape (index = 1).
// Vertices are squished and moved to either half of the viewport.
<< " if (gl_InstanceIndex == 0)\n"
<< " gl_Position.x = 0.5 * (gl_Position.x - 1.0);\n"
<< " else if (gl_InstanceIndex == 1)\n"
<< " gl_Position.x = 0.5 * (gl_Position.x + 1.0);\n"
<< "}\n";
programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
}
// Fragment shader
{
std::ostringstream src;
src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
<< "\n"
<< "layout(location = 0) in vec4 in_color;\n"
<< "layout(location = 0) out vec4 o_color;\n"
<< "\n"
<< "void main(void)\n"
<< "{\n"
<< " o_color = in_color;\n"
<< "}\n";
programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
}
}
//! Draw shapes using changing sample patterns. Add clears and other operations as necessary
class DrawTest : public TestInstance
{
static const deUint32 NUM_PASSES = 2u;
public:
DrawTest (Context& context, const TestParams params)
: TestInstance (context)
, m_params (params)
, m_sampleLocationsProperties (getSampleLocationsPropertiesEXT(context))
, m_renderSize (64, 32)
, m_numVertices (0)
, m_colorFormat (VK_FORMAT_R8G8B8A8_UNORM)
, m_depthStencilFormat (VK_FORMAT_UNDEFINED)
, m_depthStencilAspect (0)
{
requireExtensions(context);
VkMultisamplePropertiesEXT multisampleProperties =
{
VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT, // VkStructureType sType;
DE_NULL, // void* pNext;
VkExtent2D(), // VkExtent2D maxSampleLocationGridSize;
};
// For this test always use the full pixel grid
m_context.getInstanceInterface().getPhysicalDeviceMultisamplePropertiesEXT(m_context.getPhysicalDevice(), m_params.numSamples, &multisampleProperties);
m_gridSize.x() = multisampleProperties.maxSampleLocationGridSize.width;
m_gridSize.y() = multisampleProperties.maxSampleLocationGridSize.height;
}
tcu::TestStatus iterate (void)
{
// Requirements
{
const VkPhysicalDeviceLimits& limits = m_context.getDeviceProperties().limits;
if ((limits.framebufferColorSampleCounts & m_params.numSamples) == 0u)
TCU_THROW(NotSupportedError, "framebufferColorSampleCounts: sample count not supported");
if ((m_sampleLocationsProperties.sampleLocationSampleCounts & m_params.numSamples) == 0u)
TCU_THROW(NotSupportedError, "VkPhysicalDeviceSampleLocationsPropertiesEXT: sample count not supported");
if (!(m_gridSize.x() >= 1 && m_gridSize.y() >= 1))
return tcu::TestStatus::fail("maxSampleLocationGridSize is invalid");
// Are we allowed to modify the sample pattern within the same subpass?
if (m_params.drawIn == TEST_DRAW_IN_SAME_SUBPASS && !useSameSamplePattern() && !m_sampleLocationsProperties.variableSampleLocations)
TCU_THROW(NotSupportedError, "VkPhysicalDeviceSampleLocationsPropertiesEXT: variableSampleLocations not supported");
}
// Images
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
const VkImageUsageFlags colorImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
m_colorImage = makeImage(vk, device, (VkImageCreateFlags)0, m_colorFormat, m_renderSize, m_params.numSamples, colorImageUsageFlags);
m_colorImageAlloc = bindImage(vk, device, allocator, *m_colorImage, MemoryRequirement::Any);
m_colorImageView = makeImageView(vk, device, *m_colorImage, VK_IMAGE_VIEW_TYPE_2D, m_colorFormat,
makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u));
m_resolveImage = makeImage(vk, device, (VkImageCreateFlags)0, m_colorFormat, m_renderSize, VK_SAMPLE_COUNT_1_BIT, colorImageUsageFlags);
m_resolveImageAlloc = bindImage(vk, device, allocator, *m_resolveImage, MemoryRequirement::Any);
m_resolveImageView = makeImageView(vk, device, *m_resolveImage, VK_IMAGE_VIEW_TYPE_2D, m_colorFormat,
makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u));
const VkDeviceSize colorBufferSize = m_renderSize.x() * m_renderSize.y() * tcu::getPixelSize(mapVkFormat(m_colorFormat));
m_colorBuffer = makeBuffer(vk, device, colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
m_colorBufferAlloc = bindBuffer(vk, device, allocator, *m_colorBuffer, MemoryRequirement::HostVisible);
if (m_params.imageAspect != TEST_IMAGE_ASPECT_COLOR)
{
const VkImageUsageFlags depthStencilImageUsageFlags = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
m_depthStencilFormat = findSupportedDepthStencilFormat(m_context, useDepth(), useStencil());
m_depthStencilAspect = (useDepth() ? VK_IMAGE_ASPECT_DEPTH_BIT : (VkImageAspectFlagBits)0) |
(useStencil() ? VK_IMAGE_ASPECT_STENCIL_BIT : (VkImageAspectFlagBits)0);
m_depthStencilImage = makeImage(vk, device, VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT,
m_depthStencilFormat, m_renderSize, m_params.numSamples, depthStencilImageUsageFlags);
m_depthStencilImageAlloc = bindImage(vk, device, allocator, *m_depthStencilImage, MemoryRequirement::Any);
m_depthStencilImageView = makeImageView(vk, device, *m_depthStencilImage, VK_IMAGE_VIEW_TYPE_2D, m_depthStencilFormat,
makeImageSubresourceRange(m_depthStencilAspect, 0u, 1u, 0u, 1u));
}
}
// Vertices
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
Allocator& allocator = m_context.getDefaultAllocator();
std::vector<PositionColor> vertices;
if (useDepth())
{
append(vertices, genVerticesShapes (RGBA::black().toVec(), DEPTH_REFERENCE / 2.0f)); // mask above (z = 0.0 is nearest)
append(vertices, genVerticesFullQuad(RGBA::white().toVec(), DEPTH_REFERENCE)); // fill below the mask, using the depth test
}
else if (useStencil())
{
append(vertices, genVerticesShapes (RGBA::black().toVec(), DEPTH_REFERENCE)); // first mask
append(vertices, genVerticesFullQuad(RGBA::white().toVec(), DEPTH_REFERENCE / 2.0f)); // then fill the whole area, using the stencil test
}
else
vertices = genVerticesShapes();
const VkDeviceSize vertexBufferSize = static_cast<VkDeviceSize>(vertices.size() * sizeof(vertices[0]));
m_numVertices = static_cast<deUint32>(vertices.size());
m_vertexBuffer = makeBuffer(vk, device, vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
m_vertexBufferAlloc = bindBuffer(vk, device, allocator, *m_vertexBuffer, MemoryRequirement::HostVisible);
deMemcpy(m_vertexBufferAlloc->getHostPtr(), dataOrNullPtr(vertices), static_cast<std::size_t>(vertexBufferSize));
flushAlloc(vk, device, *m_vertexBufferAlloc);
}
// Multisample pixel grids - set up two sample patterns for two draw passes
{
const deUint32 numGrids = (useSameSamplePattern() ? 1u : NUM_PASSES);
m_pixelGrids.reserve(numGrids);
for (deUint32 passNdx = 0u; passNdx < numGrids; ++passNdx)
{
const deUint32 seed = 142u + 75u * passNdx;
m_pixelGrids.push_back(MultisamplePixelGrid(m_gridSize, m_params.numSamples));
fillSampleLocationsRandom(m_pixelGrids.back(), m_sampleLocationsProperties.sampleLocationSubPixelBits, seed);
logPixelGrid (m_context.getTestContext().getLog(), m_sampleLocationsProperties, m_pixelGrids.back());
}
}
// Some test cases will not clear the left hand image, so we can use it directly
const bool isClearCase = (m_params.clears != TEST_CLEARS_NO_CLEAR);
const bool hasLeftSideImage = (!isClearCase ||
(m_params.drawIn != TEST_DRAW_IN_RENDER_PASSES && m_params.clears != TEST_CLEARS_CMD_CLEAR_ATTACHMENTS));
// Render second pass reference image with the first pattern
tcu::TextureLevel refImagePattern0;
if (!useSameSamplePattern() && !hasLeftSideImage)
{
const tcu::TextureFormat colorFormat = mapVkFormat(m_colorFormat);
drawPatternChangeReference();
refImagePattern0.setStorage(colorFormat, m_renderSize.x(), m_renderSize.y());
tcu::copy(refImagePattern0.getAccess(), tcu::ConstPixelBufferAccess(colorFormat, tcu::IVec3(m_renderSize.x(), m_renderSize.y(), 1), m_colorBufferAlloc->getHostPtr()));
}
// Two-pass rendering
switch (m_params.drawIn)
{
case TEST_DRAW_IN_RENDER_PASSES: drawRenderPasses(); break;
case TEST_DRAW_IN_SUBPASSES: drawSubpasses(); break;
case TEST_DRAW_IN_SAME_SUBPASS: drawSameSubpass(); break;
default:
DE_ASSERT(0);
break;
}
// Log the result
const tcu::ConstPixelBufferAccess image (tcu::ConstPixelBufferAccess(mapVkFormat(m_colorFormat), tcu::IVec3(m_renderSize.x(), m_renderSize.y(), 1), m_colorBufferAlloc->getHostPtr()));
m_context.getTestContext().getLog()
<< tcu::TestLog::ImageSet("Result", "Final result")
<< tcu::TestLog::Image("resolve0", "resolve0", image)
<< tcu::TestLog::EndImageSet;
// Verify result
{
DE_ASSERT((m_renderSize.x() % 2) == 0);
DE_ASSERT((m_renderSize.y() % 2) == 0);
// Count colors in each image half separately, each half may have its own background color
const int numBackgroundColors = 1;
const int numExpectedColorsRight = numBackgroundColors + static_cast<int>(m_params.numSamples);
const int numExpectedColorsLeft = (isClearCase ? numBackgroundColors : numExpectedColorsRight);
const int numActualColorsLeft = countUniqueColors(tcu::getSubregion(image, 0, 0, m_renderSize.x()/2, m_renderSize.y()));
const int numActualColorsRight = countUniqueColors(tcu::getSubregion(image, m_renderSize.x()/2, 0, m_renderSize.x()/2, m_renderSize.y()));
if (numActualColorsLeft != numExpectedColorsLeft || numActualColorsRight != numExpectedColorsRight)
{
std::ostringstream msg;
msg << "Expected " << numExpectedColorsLeft << " unique colors, but got " << numActualColorsLeft;
if (numActualColorsLeft != numActualColorsRight)
msg << " and " << numActualColorsRight;
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << msg.str() << tcu::TestLog::EndMessage;
return tcu::TestStatus::fail("Resolved image has incorrect pixels");
}
if (hasLeftSideImage)
{
// Compare the left and the right half
const bool match = intThresholdCompare(tcu::getSubregion(image, 0, 0, m_renderSize.x()/2, m_renderSize.y()),
tcu::getSubregion(image, m_renderSize.x()/2, 0, m_renderSize.x()/2, m_renderSize.y()),
UVec4(2u));
if (useSameSamplePattern() && !match)
return tcu::TestStatus::fail("Multisample pattern should be identical in both image halves");
else if (!useSameSamplePattern() && match)
return tcu::TestStatus::fail("Multisample pattern doesn't seem to change between left and right image halves");
}
else if (!useSameSamplePattern())
{
// Compare the right half with the previously rendered reference image -- patterns should be different
bool match = intThresholdCompare(tcu::getSubregion(refImagePattern0.getAccess(), m_renderSize.x()/2, 0, m_renderSize.x()/2, m_renderSize.y()),
tcu::getSubregion(image, m_renderSize.x()/2, 0, m_renderSize.x()/2, m_renderSize.y()),
UVec4(2u));
if (match)
return tcu::TestStatus::fail("Multisample pattern doesn't seem to change between passes");
}
}
return tcu::TestStatus::pass("Pass");
}
protected:
bool useDepth (void) const { return m_params.imageAspect == TEST_IMAGE_ASPECT_DEPTH; }
bool useStencil (void) const { return m_params.imageAspect == TEST_IMAGE_ASPECT_STENCIL; }
bool useSameSamplePattern (void) const { return (m_params.options & TEST_OPTION_SAME_PATTERN_BIT) != 0u; }
bool useDynamicState (void) const { return (m_params.options & TEST_OPTION_DYNAMIC_STATE_BIT) != 0u; }
bool useSecondaryCmdBuffer (void) const { return (m_params.options & TEST_OPTION_SECONDARY_COMMAND_BUFFER_BIT) != 0u; }
bool useGeneralLayout (void) const { return (m_params.options & TEST_OPTION_GENERAL_LAYOUT_BIT) != 0u; }
bool useWaitEvents (void) const { return (m_params.options & TEST_OPTION_WAIT_EVENTS_BIT) != 0u; }
//! Draw the second pass image, but with sample pattern from the first pass -- used to verify that the pattern is different
void drawPatternChangeReference (void)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const VkViewport viewport = makeViewport(m_renderSize);
const VkRect2D renderArea = makeRect2D(m_renderSize);
const VkRect2D scissor = makeRect2D(m_renderSize);
const Unique<VkShaderModule> vertexModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0u));
const Unique<VkShaderModule> fragmentModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0u));
const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device));
const VkSampleLocationsInfoEXT sampleLocationsInfo = makeSampleLocationsInfo(m_pixelGrids[0]);
const VkClearValue clearColor0 = (m_params.clears == TEST_CLEARS_NO_CLEAR ? makeClearValueColor(CLEAR_COLOR_0) : makeClearValueColor(CLEAR_COLOR_1));
RenderTarget rt;
rt.addAttachment(
*m_colorImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_colorFormat, // VkFormat format,
m_params.numSamples, // VkSampleCountFlagBits numSamples,
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,
clearColor0); // VkClearValue clearValue,
rt.addAttachment(
*m_resolveImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_colorFormat, // VkFormat format,
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits numSamples,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // 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_TRANSFER_SRC_OPTIMAL, // VkImageLayout finalLayout,
VkClearValue()); // VkClearValue clearValue,
rt.addSubpassColorAttachmentWithResolve(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
if (useDepth() || useStencil())
{
rt.addAttachment(
*m_depthStencilImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_depthStencilFormat, // VkFormat format,
m_params.numSamples, // VkSampleCountFlagBits numSamples,
VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp,
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp,
VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp stencilLoadOp,
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp stencilStoreOp,
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // VkImageLayout finalLayout,
makeClearValueDepthStencil(DEPTH_CLEAR, STENCIL_REFERENCE), // VkClearValue clearValue,
&sampleLocationsInfo); // VkSampleLocationsInfoEXT* pInitialSampleLocations
rt.addSubpassDepthStencilAttachment(2u, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, &sampleLocationsInfo);
}
rt.bake(vk, device, m_renderSize);
const Unique<VkPipeline> pipeline(makeGraphicsPipeline(
vk, device, std::vector<VkDynamicState>(), *pipelineLayout, rt.getRenderPass(), *vertexModule, *fragmentModule,
/*subpass index*/ 0u, viewport, scissor, m_params.numSamples, /*use sample locations*/ true, sampleLocationsInfo,
useDepth(), useStencil(), VERTEX_INPUT_VEC4_VEC4, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, stencilOpStateDrawOnce()));
const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, m_context.getUniversalQueueFamilyIndex()));
const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer(vk, device, *cmdPool));
Move<VkCommandBuffer> secondaryCmdBuffer;
VkCommandBuffer currentCmdBuffer = *cmdBuffer;
beginCommandBuffer(vk, currentCmdBuffer);
rt.recordBeginRenderPass(vk, currentCmdBuffer, renderArea, (useSecondaryCmdBuffer() ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS : VK_SUBPASS_CONTENTS_INLINE));
// For maximum consistency also use a secondary command buffer, if the two-pass path uses it
if (useSecondaryCmdBuffer())
{
secondaryCmdBuffer = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
currentCmdBuffer = *secondaryCmdBuffer;
beginSecondaryCommandBuffer(vk, currentCmdBuffer, rt.getRenderPass(), /*subpass*/ 0u, rt.getFramebuffer());
}
vk.cmdBindVertexBuffers(currentCmdBuffer, /*first binding*/ 0u, /*num bindings*/ 1u, &m_vertexBuffer.get(), /*offsets*/ &ZERO);
vk.cmdBindPipeline(currentCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
// Draw the right shape only
vk.cmdDraw(currentCmdBuffer, m_numVertices, /*instance count*/ 1u, /*first vertex*/ 0u, /*first instance*/ 1u);
if (useSecondaryCmdBuffer())
{
endCommandBuffer(vk, currentCmdBuffer);
currentCmdBuffer = *cmdBuffer;
vk.cmdExecuteCommands(currentCmdBuffer, 1u, &secondaryCmdBuffer.get());
}
endRenderPass(vk, *cmdBuffer);
// Resolve image -> host buffer
recordImageBarrier(vk, *cmdBuffer, *m_resolveImage,
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspect,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask,
VK_PIPELINE_STAGE_TRANSFER_BIT, // VkPipelineStageFlags dstStageMask,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask,
VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout oldLayout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); // VkImageLayout newLayout)
recordCopyImageToBuffer(vk, *cmdBuffer, m_renderSize, *m_resolveImage, *m_colorBuffer);
endCommandBuffer(vk, *cmdBuffer);
submitCommandsAndWait(vk, device, m_context.getUniversalQueue(), *cmdBuffer);
invalidateAlloc(vk, device, *m_colorBufferAlloc);
}
//! Draw two shapes with distinct sample patterns, each in its own render pass
void drawRenderPasses (void)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const VkViewport viewport = makeViewport(m_renderSize);
const VkRect2D renderArea = makeRect2D(m_renderSize);
const VkRect2D scissor = makeRect2D(m_renderSize);
const Unique<VkShaderModule> vertexModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0u));
const Unique<VkShaderModule> fragmentModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0u));
const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device));
const VkClearValue clearColor0 = makeClearValueColor(CLEAR_COLOR_0);
const VkClearValue clearColor1 = makeClearValueColor(CLEAR_COLOR_1);
const VkClearValue clearDepthStencil0 = makeClearValueDepthStencil(DEPTH_CLEAR, STENCIL_REFERENCE);
const VkSampleLocationsInfoEXT sampleLocationsInfo [NUM_PASSES] =
{
makeSampleLocationsInfo(m_pixelGrids[0]),
makeSampleLocationsInfo(m_pixelGrids[useSameSamplePattern() ? 0 : 1]),
};
const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, m_context.getUniversalQueueFamilyIndex()));
Move<VkCommandBuffer> cmdBuffer [NUM_PASSES] =
{
makeCommandBuffer(vk, device, *cmdPool),
makeCommandBuffer(vk, device, *cmdPool),
};
Move<VkCommandBuffer> secondaryCmdBuffer [NUM_PASSES];
RenderTarget rt [NUM_PASSES];
Move<VkPipeline> pipeline [NUM_PASSES];
Move<VkEvent> event [2]; /*color and depth/stencil*/
// Layouts expected by the second render pass
const VkImageLayout colorLayout1 = useGeneralLayout() && !(useDepth() || useStencil()) ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
const VkImageLayout depthStencilLayout1 = useGeneralLayout() && (useDepth() || useStencil()) ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
// First render pass - no resolves
{
rt[0].addAttachment(
*m_colorImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_colorFormat, // VkFormat format,
m_params.numSamples, // VkSampleCountFlagBits numSamples,
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,
clearColor0); // VkClearValue clearValue,
rt[0].addSubpassColorAttachment(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
if (useDepth() || useStencil())
{
rt[0].addAttachment(
*m_depthStencilImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_depthStencilFormat, // VkFormat format,
m_params.numSamples, // VkSampleCountFlagBits numSamples,
VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp,
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp,
VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp stencilLoadOp,
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp stencilStoreOp,
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // VkImageLayout finalLayout,
clearDepthStencil0, // VkClearValue clearValue,
&sampleLocationsInfo[0]); // VkSampleLocationsInfoEXT* pInitialSampleLocations
rt[0].addSubpassDepthStencilAttachment(1u, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, &sampleLocationsInfo[0]);
}
rt[0].bake(vk, device, m_renderSize);
}
// Second render pass
{
const VkAttachmentLoadOp loadOp = (m_params.clears == TEST_CLEARS_LOAD_OP_CLEAR ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD);
rt[1].addAttachment(
*m_colorImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_colorFormat, // VkFormat format,
m_params.numSamples, // VkSampleCountFlagBits numSamples,
loadOp, // 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,
colorLayout1, // VkImageLayout initialLayout,
colorLayout1, // VkImageLayout finalLayout,
clearColor1); // VkClearValue clearValue,
rt[1].addAttachment(
*m_resolveImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_colorFormat, // VkFormat format,
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits numSamples,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // 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_TRANSFER_SRC_OPTIMAL, // VkImageLayout finalLayout,
VkClearValue()); // VkClearValue clearValue,
rt[1].addSubpassColorAttachmentWithResolve(0u, colorLayout1,
1u, colorLayout1);
if (useDepth() || useStencil())
{
rt[1].addAttachment(
*m_depthStencilImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_depthStencilFormat, // VkFormat format,
m_params.numSamples, // VkSampleCountFlagBits numSamples,
loadOp, // VkAttachmentLoadOp loadOp,
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp,
loadOp, // VkAttachmentLoadOp stencilLoadOp,
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp stencilStoreOp,
depthStencilLayout1, // VkImageLayout initialLayout,
depthStencilLayout1, // VkImageLayout finalLayout,
clearDepthStencil0, // VkClearValue clearValue,
&sampleLocationsInfo[1]); // VkSampleLocationsInfoEXT* pInitialSampleLocations
rt[1].addSubpassDepthStencilAttachment(2u, depthStencilLayout1, &sampleLocationsInfo[1]);
}
rt[1].bake(vk, device, m_renderSize);
}
// Pipelines
if (useDynamicState())
{
std::vector<VkDynamicState> dynamicState;
dynamicState.push_back(VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT);
for (deUint32 passNdx = 0; passNdx < NUM_PASSES; ++passNdx)
{
pipeline[passNdx] = makeGraphicsPipeline(
vk, device, dynamicState, *pipelineLayout, rt[passNdx].getRenderPass(), *vertexModule, *fragmentModule,
/*subpass index*/ 0u, viewport, scissor, m_params.numSamples, /*use sample locations*/ true, makeEmptySampleLocationsInfo(),
useDepth(), useStencil(), VERTEX_INPUT_VEC4_VEC4, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, stencilOpStateDrawOnce());
}
}
else for (deUint32 passNdx = 0; passNdx < NUM_PASSES; ++passNdx)
{
pipeline[passNdx] = makeGraphicsPipeline(
vk, device, std::vector<VkDynamicState>(), *pipelineLayout, rt[passNdx].getRenderPass(), *vertexModule, *fragmentModule,
/*subpass index*/ 0u, viewport, scissor, m_params.numSamples, /*use sample locations*/ true, sampleLocationsInfo[passNdx],
useDepth(), useStencil(), VERTEX_INPUT_VEC4_VEC4, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, stencilOpStateDrawOnce());
}
// Record secondary command buffers
if (useSecondaryCmdBuffer())
{
secondaryCmdBuffer[0] = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
secondaryCmdBuffer[1] = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
// First render pass contents
beginSecondaryCommandBuffer(vk, *secondaryCmdBuffer[0], rt[0].getRenderPass(), /*subpass*/ 0u, rt[0].getFramebuffer());
recordFirstPassContents(*secondaryCmdBuffer[0], *pipeline[0], sampleLocationsInfo[0]);
endCommandBuffer(vk, *secondaryCmdBuffer[0]);
// Second render pass contents
beginSecondaryCommandBuffer(vk, *secondaryCmdBuffer[1], rt[1].getRenderPass(), /*subpass*/ 0u, rt[1].getFramebuffer());
recordSecondPassContents(*secondaryCmdBuffer[1], *pipeline[1], sampleLocationsInfo[1], clearColor1, clearDepthStencil0, scissor);
endCommandBuffer(vk, *secondaryCmdBuffer[1]);
}
// Record primary command buffers
VkCommandBuffer currentCmdBuffer = *cmdBuffer[0];
beginCommandBuffer(vk, currentCmdBuffer);
// First render pass
if (useSecondaryCmdBuffer())
{
rt[0].recordBeginRenderPass(vk, currentCmdBuffer, renderArea, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
vk.cmdExecuteCommands(currentCmdBuffer, 1u, &secondaryCmdBuffer[0].get());
endRenderPass(vk, currentCmdBuffer);
}
else
{
rt[0].recordBeginRenderPass(vk, currentCmdBuffer, renderArea, VK_SUBPASS_CONTENTS_INLINE);
recordFirstPassContents(currentCmdBuffer, *pipeline[0], sampleLocationsInfo[0]);
endRenderPass(vk, currentCmdBuffer);
}
endCommandBuffer(vk, currentCmdBuffer);
// Record the second primary command buffer
currentCmdBuffer = *cmdBuffer[1];
beginCommandBuffer(vk, currentCmdBuffer);
if (m_params.clears == TEST_CLEARS_CMD_CLEAR_IMAGE)
{
{
const VkImageLayout finalLayout = (useWaitEvents() ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : colorLayout1);
recordImageBarrier(vk, currentCmdBuffer, *m_colorImage,
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspect,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask,
VK_PIPELINE_STAGE_TRANSFER_BIT, // VkPipelineStageFlags dstStageMask,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask,
VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout oldLayout,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // VkImageLayout newLayout)
const VkImageSubresourceRange subresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
vk.cmdClearColorImage(currentCmdBuffer, *m_colorImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColor1.color, 1u, &subresourceRange);
recordImageBarrier(vk, currentCmdBuffer, *m_colorImage,
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspect,
VK_PIPELINE_STAGE_TRANSFER_BIT, // VkPipelineStageFlags srcStageMask,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags dstStageMask,
VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask,
(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT), // VkAccessFlags dstAccessMask,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout,
finalLayout); // VkImageLayout newLayout)
}
if (useDepth() || useStencil())
{
const VkImageLayout finalLayout = (useWaitEvents() ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : depthStencilLayout1);
recordImageBarrier(vk, currentCmdBuffer, *m_depthStencilImage,
getImageAspectFlags(m_depthStencilFormat), // VkImageAspectFlags aspect,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, // VkPipelineStageFlags srcStageMask,
VK_PIPELINE_STAGE_TRANSFER_BIT, // VkPipelineStageFlags dstStageMask,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask,
VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // VkImageLayout oldLayout,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout)
&sampleLocationsInfo[0]); // VkSampleLocationsInfoEXT
const VkImageSubresourceRange subresourceRange = makeImageSubresourceRange(m_depthStencilAspect, 0u, 1u, 0u, 1u);
vk.cmdClearDepthStencilImage(currentCmdBuffer, *m_depthStencilImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearDepthStencil0.depthStencil, 1u, &subresourceRange);
recordImageBarrier(vk, currentCmdBuffer, *m_depthStencilImage,
getImageAspectFlags(m_depthStencilFormat), // VkImageAspectFlags aspect,
VK_PIPELINE_STAGE_TRANSFER_BIT, // VkPipelineStageFlags srcStageMask,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, // VkPipelineStageFlags dstStageMask,
VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask,
(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT), // VkAccessFlags dstAccessMask,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout,
finalLayout, // VkImageLayout newLayout)
&sampleLocationsInfo[0]); // VkSampleLocationsInfoEXT
}
}
else if (!useWaitEvents())
{
// Barrier between the render passes
recordImageBarrier(vk, currentCmdBuffer, *m_colorImage,
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspect,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags dstStageMask,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask,
(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT), // VkAccessFlags dstAccessMask,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout oldLayout,
colorLayout1); // VkImageLayout newLayout)
if (useDepth() || useStencil())
{
recordImageBarrier(vk, currentCmdBuffer, *m_depthStencilImage,
getImageAspectFlags(m_depthStencilFormat), // VkImageAspectFlags aspect,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, // VkPipelineStageFlags srcStageMask,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, // VkPipelineStageFlags dstStageMask,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask,
(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT), // VkAccessFlags dstAccessMask,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // VkImageLayout oldLayout,
depthStencilLayout1); // VkImageLayout newLayout)
}
}
if (useWaitEvents())
{
// Use events to sync both render passes
event[0] = makeEvent(vk, device);
vk.cmdSetEvent(currentCmdBuffer, *event[0], VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
recordWaitEventWithImage(vk, currentCmdBuffer, *event[0], *m_colorImage,
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspect,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags dstStageMask,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask,
(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT), // VkAccessFlags dstAccessMask,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout oldLayout,
colorLayout1); // VkImageLayout newLayout,
if (useDepth() || useStencil())
{
event[1] = makeEvent(vk, device);
vk.cmdSetEvent(currentCmdBuffer, *event[1], VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT);
recordWaitEventWithImage(vk, currentCmdBuffer, *event[1], *m_depthStencilImage,
getImageAspectFlags(m_depthStencilFormat), // VkImageAspectFlags aspect,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, // VkPipelineStageFlags srcStageMask,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, // VkPipelineStageFlags dstStageMask,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask,
(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT), // VkAccessFlags dstAccessMask,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // VkImageLayout oldLayout,
depthStencilLayout1); // VkImageLayout newLayout,
}
}
// Second render pass
if (useSecondaryCmdBuffer())
{
rt[1].recordBeginRenderPass(vk, currentCmdBuffer, renderArea, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
vk.cmdExecuteCommands(currentCmdBuffer, 1u, &secondaryCmdBuffer[1].get());
endRenderPass(vk, currentCmdBuffer);
}
else
{
rt[1].recordBeginRenderPass(vk, currentCmdBuffer, renderArea, VK_SUBPASS_CONTENTS_INLINE);
recordSecondPassContents(currentCmdBuffer, *pipeline[1], sampleLocationsInfo[1], clearColor1, clearDepthStencil0, scissor);
endRenderPass(vk, currentCmdBuffer);
}
// Resolve image -> host buffer
recordImageBarrier(vk, currentCmdBuffer, *m_resolveImage,
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspect,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask,
VK_PIPELINE_STAGE_TRANSFER_BIT, // VkPipelineStageFlags dstStageMask,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask,
VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout oldLayout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); // VkImageLayout newLayout)
recordCopyImageToBuffer(vk, currentCmdBuffer, m_renderSize, *m_resolveImage, *m_colorBuffer);
endCommandBuffer(vk, currentCmdBuffer);
// Submit work
{
const Unique<VkFence> fence (createFence(vk, device));
const VkCommandBuffer buffers [NUM_PASSES] =
{
*cmdBuffer[0],
*cmdBuffer[1],
};
const VkSubmitInfo submitInfo =
{
VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // uint32_t waitSemaphoreCount;
DE_NULL, // const VkSemaphore* pWaitSemaphores;
DE_NULL, // const VkPipelineStageFlags* pWaitDstStageMask;
DE_LENGTH_OF_ARRAY(buffers), // uint32_t commandBufferCount;
buffers, // const VkCommandBuffer* pCommandBuffers;
0u, // uint32_t signalSemaphoreCount;
DE_NULL, // const VkSemaphore* pSignalSemaphores;
};
VK_CHECK(vk.queueSubmit(m_context.getUniversalQueue(), 1u, &submitInfo, *fence));
VK_CHECK(vk.waitForFences(device, 1u, &fence.get(), DE_TRUE, ~0ull));
}
invalidateAlloc(vk, device, *m_colorBufferAlloc);
}
void recordFirstPassContents (const VkCommandBuffer cmdBuffer,
const VkPipeline pipeline,
const VkSampleLocationsInfoEXT& sampleLocationsInfo)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
vk.cmdBindVertexBuffers(cmdBuffer, /*first binding*/ 0u, /*num bindings*/ 1u, &m_vertexBuffer.get(), /*offsets*/ &ZERO);
vk.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
if (useDynamicState())
vk.cmdSetSampleLocationsEXT(cmdBuffer, &sampleLocationsInfo);
if (m_params.clears == TEST_CLEARS_NO_CLEAR)
vk.cmdDraw(cmdBuffer, m_numVertices, /*instance count*/ 1u, /*first vertex*/ 0u, /*first instance*/ 0u); // left shape only
else
vk.cmdDraw(cmdBuffer, m_numVertices, /*instance count*/ NUM_PASSES, /*first vertex*/ 0u, /*first instance*/ 0u); // both shapes
}
void recordSecondPassContents (const VkCommandBuffer cmdBuffer,
const VkPipeline pipeline,
const VkSampleLocationsInfoEXT& sampleLocationsInfo,
const VkClearValue& clearColor,
const VkClearValue& clearDepthStencil,
const VkRect2D& clearRect)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
vk.cmdBindVertexBuffers(cmdBuffer, /*first binding*/ 0u, /*num bindings*/ 1u, &m_vertexBuffer.get(), /*offsets*/ &ZERO);
vk.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
if (m_params.clears == TEST_CLEARS_CMD_CLEAR_ATTACHMENTS)
recordClearAttachments(vk, cmdBuffer, 0u, clearColor, m_depthStencilAspect, clearDepthStencil, clearRect);
if (useDynamicState())
vk.cmdSetSampleLocationsEXT(cmdBuffer, &sampleLocationsInfo);
// Draw the right shape only
vk.cmdDraw(cmdBuffer, m_numVertices, /*instance count*/ 1u, /*first vertex*/ 0u, /*first instance*/ 1u);
}
//! Draw two shapes in two subpasses of the same render pass
void drawSubpasses (void)
{
DE_ASSERT(m_params.clears != TEST_CLEARS_CMD_CLEAR_IMAGE); // not possible in a render pass
DE_ASSERT(m_params.clears != TEST_CLEARS_LOAD_OP_CLEAR); // can't specify a load op for a subpass
DE_ASSERT((m_params.options & TEST_OPTION_WAIT_EVENTS_BIT) == 0); // can't change layouts inside a subpass
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const VkViewport viewport = makeViewport(m_renderSize);
const VkRect2D renderArea = makeRect2D(m_renderSize);
const VkRect2D scissor = makeRect2D(m_renderSize);
const Unique<VkShaderModule> vertexModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0u));
const Unique<VkShaderModule> fragmentModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0u));
const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device));
const VkClearValue clearColor0 = makeClearValueColor(CLEAR_COLOR_0);
const VkClearValue clearColor1 = makeClearValueColor(CLEAR_COLOR_1);
const VkClearValue clearDepthStencil0 = makeClearValueDepthStencil(DEPTH_CLEAR, STENCIL_REFERENCE);
const VkSampleLocationsInfoEXT sampleLocationsInfo [NUM_PASSES] =
{
makeSampleLocationsInfo(m_pixelGrids[0]),
makeSampleLocationsInfo(m_pixelGrids[useSameSamplePattern() ? 0 : 1]),
};
const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, m_context.getUniversalQueueFamilyIndex()));
const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer(vk, device, *cmdPool));
Move<VkCommandBuffer> secondaryCmdBuffer [NUM_PASSES];
RenderTarget rt;
Move<VkPipeline> pipeline [NUM_PASSES];
Move<VkEvent> event;
// Layouts used in the second subpass
const VkImageLayout colorLayout1 = useGeneralLayout() && !(useDepth() || useStencil()) ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
const VkImageLayout depthStencilLayout1 = useGeneralLayout() && (useDepth() || useStencil()) ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
// Prepare the render pass
{
rt.addAttachment(
*m_colorImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_colorFormat, // VkFormat format,
m_params.numSamples, // VkSampleCountFlagBits numSamples,
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,
clearColor0); // VkClearValue clearValue,
rt.addAttachment(
*m_resolveImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_colorFormat, // VkFormat format,
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits numSamples,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // 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_TRANSFER_SRC_OPTIMAL, // VkImageLayout finalLayout,
VkClearValue()); // VkClearValue clearValue,
// First subpass
rt.addSubpassColorAttachment(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
if (useDepth() || useStencil())
{
rt.addAttachment(
*m_depthStencilImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_depthStencilFormat, // VkFormat format,
m_params.numSamples, // VkSampleCountFlagBits numSamples,
VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp,
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp,
VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp stencilLoadOp,
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp stencilStoreOp,
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // VkImageLayout finalLayout,
clearDepthStencil0, // VkClearValue clearValue,
&sampleLocationsInfo[0]); // VkSampleLocationsInfoEXT* pInitialSampleLocations
rt.addSubpassDepthStencilAttachment(2u, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, &sampleLocationsInfo[0]);
}
// Second subpass
rt.nextSubpass();
rt.addSubpassColorAttachmentWithResolve(0u, colorLayout1,
1u, colorLayout1);
if (useDepth() || useStencil())
rt.addSubpassDepthStencilAttachment(2u, depthStencilLayout1, &sampleLocationsInfo[1]);
rt.bake(vk, device, m_renderSize);
}
// Pipelines
if (useDynamicState())
{
std::vector<VkDynamicState> dynamicState;
dynamicState.push_back(VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT);
for (deUint32 passNdx = 0; passNdx < NUM_PASSES; ++passNdx)
{
pipeline[passNdx] = makeGraphicsPipeline(
vk, device, dynamicState, *pipelineLayout, rt.getRenderPass(), *vertexModule, *fragmentModule,
/*subpass*/ passNdx, viewport, scissor, m_params.numSamples, /*use sample locations*/ true, makeEmptySampleLocationsInfo(),
useDepth(), useStencil(), VERTEX_INPUT_VEC4_VEC4, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, stencilOpStateDrawOnce());
}
}
else for (deUint32 passNdx = 0; passNdx < NUM_PASSES; ++passNdx)
{
pipeline[passNdx] = makeGraphicsPipeline(
vk, device, std::vector<VkDynamicState>(), *pipelineLayout, rt.getRenderPass(), *vertexModule, *fragmentModule,
/*subpass*/ passNdx, viewport, scissor, m_params.numSamples, /*use sample locations*/ true, sampleLocationsInfo[passNdx],
useDepth(), useStencil(), VERTEX_INPUT_VEC4_VEC4, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, stencilOpStateDrawOnce());
}
// Record secondary command buffers
if (useSecondaryCmdBuffer())
{
secondaryCmdBuffer[0] = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
secondaryCmdBuffer[1] = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
// First subpass contents
beginSecondaryCommandBuffer(vk, *secondaryCmdBuffer[0], rt.getRenderPass(), /*subpass*/ 0u, rt.getFramebuffer());
recordFirstPassContents(*secondaryCmdBuffer[0], *pipeline[0], sampleLocationsInfo[0]);
endCommandBuffer(vk, *secondaryCmdBuffer[0]);
// Second subpass contents
beginSecondaryCommandBuffer(vk, *secondaryCmdBuffer[1], rt.getRenderPass(), /*subpass*/ 1u, rt.getFramebuffer());
recordSecondPassContents(*secondaryCmdBuffer[1], *pipeline[1], sampleLocationsInfo[1], clearColor1, clearDepthStencil0, scissor);
endCommandBuffer(vk, *secondaryCmdBuffer[1]);
}
// Record primary command buffer
beginCommandBuffer(vk, *cmdBuffer);
if (useSecondaryCmdBuffer())
{
rt.recordBeginRenderPass(vk, *cmdBuffer, renderArea, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
vk.cmdExecuteCommands(*cmdBuffer, 1u, &secondaryCmdBuffer[0].get());
vk.cmdNextSubpass(*cmdBuffer, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
vk.cmdExecuteCommands(*cmdBuffer, 1u, &secondaryCmdBuffer[1].get());
}
else
{
rt.recordBeginRenderPass(vk, *cmdBuffer, renderArea, VK_SUBPASS_CONTENTS_INLINE);
recordFirstPassContents(*cmdBuffer, *pipeline[0], sampleLocationsInfo[0]);
vk.cmdNextSubpass(*cmdBuffer, VK_SUBPASS_CONTENTS_INLINE);
recordSecondPassContents(*cmdBuffer, *pipeline[1], sampleLocationsInfo[1], clearColor1, clearDepthStencil0, scissor);
}
endRenderPass(vk, *cmdBuffer);
// Resolve image -> host buffer
recordImageBarrier(vk, *cmdBuffer, *m_resolveImage,
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspect,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask,
VK_PIPELINE_STAGE_TRANSFER_BIT, // VkPipelineStageFlags dstStageMask,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask,
VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout oldLayout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); // VkImageLayout newLayout)
recordCopyImageToBuffer(vk, *cmdBuffer, m_renderSize, *m_resolveImage, *m_colorBuffer);
endCommandBuffer(vk, *cmdBuffer);
submitCommandsAndWait(vk, device, m_context.getUniversalQueue(), *cmdBuffer);
invalidateAlloc(vk, device, *m_colorBufferAlloc);
}
//! Draw two shapes within the same subpass of a renderpass
void drawSameSubpass (void)
{
DE_ASSERT(m_params.clears != TEST_CLEARS_CMD_CLEAR_IMAGE); // not possible in a render pass
DE_ASSERT(m_params.clears != TEST_CLEARS_LOAD_OP_CLEAR); // can't specify a load op for a subpass
DE_ASSERT((m_params.options & TEST_OPTION_WAIT_EVENTS_BIT) == 0); // can't change layouts inside a subpass
DE_ASSERT((m_params.options & TEST_OPTION_GENERAL_LAYOUT_BIT) == 0); // can't change layouts inside a subpass
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice device = m_context.getDevice();
const VkViewport viewport = makeViewport(m_renderSize);
const VkRect2D renderArea = makeRect2D(m_renderSize);
const VkRect2D scissor = makeRect2D(m_renderSize);
const Unique<VkShaderModule> vertexModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0u));
const Unique<VkShaderModule> fragmentModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0u));
const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device));
const VkClearValue clearColor0 = makeClearValueColor(CLEAR_COLOR_0);
const VkClearValue clearColor1 = makeClearValueColor(CLEAR_COLOR_1);
const VkClearValue clearDepthStencil0 = makeClearValueDepthStencil(DEPTH_CLEAR, STENCIL_REFERENCE);
const VkSampleLocationsInfoEXT sampleLocationsInfo [NUM_PASSES] =
{
makeSampleLocationsInfo(m_pixelGrids[0]),
makeSampleLocationsInfo(m_pixelGrids[useSameSamplePattern() ? 0 : 1]),
};
const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, m_context.getUniversalQueueFamilyIndex()));
const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer(vk, device, *cmdPool));
Move<VkCommandBuffer> secondaryCmdBuffer;
RenderTarget rt;
Move<VkPipeline> pipeline [NUM_PASSES];
Move<VkEvent> event;
// Prepare the render pass
{
rt.addAttachment(
*m_colorImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_colorFormat, // VkFormat format,
m_params.numSamples, // VkSampleCountFlagBits numSamples,
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,
clearColor0); // VkClearValue clearValue,
rt.addAttachment(
*m_resolveImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_colorFormat, // VkFormat format,
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits numSamples,
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // 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_TRANSFER_SRC_OPTIMAL, // VkImageLayout finalLayout,
VkClearValue()); // VkClearValue clearValue,
rt.addSubpassColorAttachmentWithResolve(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
1u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
if (useDepth() || useStencil())
{
rt.addAttachment(
*m_depthStencilImageView, // VkImageView imageView,
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags,
m_depthStencilFormat, // VkFormat format,
m_params.numSamples, // VkSampleCountFlagBits numSamples,
VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp,
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp,
VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp stencilLoadOp,
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp stencilStoreOp,
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // VkImageLayout finalLayout,
clearDepthStencil0, // VkClearValue clearValue,
&sampleLocationsInfo[0]); // VkSampleLocationsInfoEXT* pInitialSampleLocations
rt.addSubpassDepthStencilAttachment(2u, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, &sampleLocationsInfo[0]);
}
rt.bake(vk, device, m_renderSize);
}
// Pipelines
if (useDynamicState())
{
std::vector<VkDynamicState> dynamicState;
dynamicState.push_back(VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT);
for (deUint32 passNdx = 0; passNdx < NUM_PASSES; ++passNdx)
{
pipeline[passNdx] = makeGraphicsPipeline(
vk, device, dynamicState, *pipelineLayout, rt.getRenderPass(), *vertexModule, *fragmentModule,
/*subpass*/ 0u, viewport, scissor, m_params.numSamples, /*use sample locations*/ true, makeEmptySampleLocationsInfo(),
useDepth(), useStencil(), VERTEX_INPUT_VEC4_VEC4, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, stencilOpStateDrawOnce());
}
}
else for (deUint32 passNdx = 0; passNdx < NUM_PASSES; ++passNdx)
{
pipeline[passNdx] = makeGraphicsPipeline(
vk, device, std::vector<VkDynamicState>(), *pipelineLayout, rt.getRenderPass(), *vertexModule, *fragmentModule,
/*subpass*/ 0u, viewport, scissor, m_params.numSamples, /*use sample locations*/ true, sampleLocationsInfo[passNdx],
useDepth(), useStencil(), VERTEX_INPUT_VEC4_VEC4, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, stencilOpStateDrawOnce());
}
// Record secondary command buffers
if (useSecondaryCmdBuffer())
{
secondaryCmdBuffer = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
beginSecondaryCommandBuffer(vk, *secondaryCmdBuffer, rt.getRenderPass(), /*subpass*/ 0u, rt.getFramebuffer());
recordFirstPassContents(*secondaryCmdBuffer, *pipeline[0], sampleLocationsInfo[0]);
recordSecondPassContents(*secondaryCmdBuffer, *pipeline[1], sampleLocationsInfo[1], clearColor1, clearDepthStencil0, scissor);
endCommandBuffer(vk, *secondaryCmdBuffer);
}
// Record primary command buffer
beginCommandBuffer(vk, *cmdBuffer);
if (useSecondaryCmdBuffer())
{
rt.recordBeginRenderPass(vk, *cmdBuffer, renderArea, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
vk.cmdExecuteCommands(*cmdBuffer, 1u, &secondaryCmdBuffer.get());
}
else
{
rt.recordBeginRenderPass(vk, *cmdBuffer, renderArea, VK_SUBPASS_CONTENTS_INLINE);
recordFirstPassContents(*cmdBuffer, *pipeline[0], sampleLocationsInfo[0]);
recordSecondPassContents(*cmdBuffer, *pipeline[1], sampleLocationsInfo[1], clearColor1, clearDepthStencil0, scissor);
}
endRenderPass(vk, *cmdBuffer);
// Resolve image -> host buffer
recordImageBarrier(vk, *cmdBuffer, *m_resolveImage,
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspect,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // VkPipelineStageFlags srcStageMask,
VK_PIPELINE_STAGE_TRANSFER_BIT, // VkPipelineStageFlags dstStageMask,
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags srcAccessMask,
VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout oldLayout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); // VkImageLayout newLayout)
recordCopyImageToBuffer(vk, *cmdBuffer, m_renderSize, *m_resolveImage, *m_colorBuffer);
endCommandBuffer(vk, *cmdBuffer);
submitCommandsAndWait(vk, device, m_context.getUniversalQueue(), *cmdBuffer);
invalidateAlloc(vk, device, *m_colorBufferAlloc);
}
const TestParams m_params;
const VkPhysicalDeviceSampleLocationsPropertiesEXT m_sampleLocationsProperties;
const UVec2 m_renderSize;
UVec2 m_gridSize;
std::vector<MultisamplePixelGrid> m_pixelGrids;
deUint32 m_numVertices;
Move<VkBuffer> m_vertexBuffer;
MovePtr<Allocation> m_vertexBufferAlloc;
const VkFormat m_colorFormat;
Move<VkImage> m_colorImage;
Move<VkImageView> m_colorImageView;
MovePtr<Allocation> m_colorImageAlloc;
VkFormat m_depthStencilFormat;
VkImageAspectFlags m_depthStencilAspect;
Move<VkImage> m_depthStencilImage;
Move<VkImageView> m_depthStencilImageView;
MovePtr<Allocation> m_depthStencilImageAlloc;
Move<VkImage> m_resolveImage;
Move<VkImageView> m_resolveImageView;
MovePtr<Allocation> m_resolveImageAlloc;
Move<VkBuffer> m_colorBuffer;
MovePtr<Allocation> m_colorBufferAlloc;
};
} // Draw
void createTestsInGroup (tcu::TestCaseGroup* rootGroup)
{
// Queries
{
MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(rootGroup->getTestContext(), "query", ""));
addFunctionCase(group.get(), "sample_locations_properties", "", testQuerySampleLocationProperties);
addFunctionCase(group.get(), "multisample_properties", "", testQueryMultisampleProperties);
rootGroup->addChild(group.release());
}
const VkSampleCountFlagBits sampleCountRange[] =
{
VK_SAMPLE_COUNT_2_BIT,
VK_SAMPLE_COUNT_4_BIT,
VK_SAMPLE_COUNT_8_BIT,
VK_SAMPLE_COUNT_16_BIT,
// There are no implementations that support 32 or 64 programmable samples currently
};
// Verify custom sample locations and interpolation
{
using namespace VerifySamples;
MovePtr<tcu::TestCaseGroup> groupLocation (new tcu::TestCaseGroup(rootGroup->getTestContext(), "verify_location", ""));
MovePtr<tcu::TestCaseGroup> groupInterpolation (new tcu::TestCaseGroup(rootGroup->getTestContext(), "verify_interpolation", ""));
for (const VkSampleCountFlagBits* pLoopNumSamples = sampleCountRange; pLoopNumSamples < DE_ARRAY_END(sampleCountRange); ++pLoopNumSamples)
{
addCases<VerifyLocationTest> (groupLocation.get(), *pLoopNumSamples, addProgramsVerifyLocationGeometry);
addCases<VerifyInterpolationTest>(groupInterpolation.get(), *pLoopNumSamples, addProgramsVerifyInterpolation);
}
rootGroup->addChild(groupLocation.release());
rootGroup->addChild(groupInterpolation.release());
}
// Draw with custom samples and various options
{
using namespace Draw;
const deUint32 optionSets[] =
{
TEST_OPTION_SAME_PATTERN_BIT,
0u,
TEST_OPTION_DYNAMIC_STATE_BIT,
TEST_OPTION_SECONDARY_COMMAND_BUFFER_BIT,
TEST_OPTION_DYNAMIC_STATE_BIT | TEST_OPTION_SECONDARY_COMMAND_BUFFER_BIT,
TEST_OPTION_GENERAL_LAYOUT_BIT,
TEST_OPTION_GENERAL_LAYOUT_BIT | TEST_OPTION_DYNAMIC_STATE_BIT,
TEST_OPTION_GENERAL_LAYOUT_BIT | TEST_OPTION_SECONDARY_COMMAND_BUFFER_BIT,
TEST_OPTION_GENERAL_LAYOUT_BIT | TEST_OPTION_DYNAMIC_STATE_BIT | TEST_OPTION_SECONDARY_COMMAND_BUFFER_BIT,
TEST_OPTION_WAIT_EVENTS_BIT,
TEST_OPTION_WAIT_EVENTS_BIT | TEST_OPTION_GENERAL_LAYOUT_BIT,
TEST_OPTION_WAIT_EVENTS_BIT | TEST_OPTION_GENERAL_LAYOUT_BIT | TEST_OPTION_SECONDARY_COMMAND_BUFFER_BIT,
};
const struct
{
TestDrawIn drawIn;
TestClears clears;
} drawClearSets[] =
{
{ TEST_DRAW_IN_RENDER_PASSES, TEST_CLEARS_NO_CLEAR },
{ TEST_DRAW_IN_RENDER_PASSES, TEST_CLEARS_LOAD_OP_CLEAR },
{ TEST_DRAW_IN_RENDER_PASSES, TEST_CLEARS_CMD_CLEAR_ATTACHMENTS },
{ TEST_DRAW_IN_RENDER_PASSES, TEST_CLEARS_CMD_CLEAR_IMAGE },
{ TEST_DRAW_IN_SUBPASSES, TEST_CLEARS_NO_CLEAR },
{ TEST_DRAW_IN_SUBPASSES, TEST_CLEARS_CMD_CLEAR_ATTACHMENTS },
{ TEST_DRAW_IN_SAME_SUBPASS, TEST_CLEARS_NO_CLEAR },
{ TEST_DRAW_IN_SAME_SUBPASS, TEST_CLEARS_CMD_CLEAR_ATTACHMENTS },
};
const TestImageAspect aspectRange[] =
{
TEST_IMAGE_ASPECT_COLOR,
TEST_IMAGE_ASPECT_DEPTH,
TEST_IMAGE_ASPECT_STENCIL,
};
MovePtr<tcu::TestCaseGroup> drawGroup (new tcu::TestCaseGroup(rootGroup->getTestContext(), "draw", ""));
for (const TestImageAspect* pLoopImageAspect = aspectRange; pLoopImageAspect != DE_ARRAY_END(aspectRange); ++pLoopImageAspect)
{
MovePtr<tcu::TestCaseGroup> aspectGroup (new tcu::TestCaseGroup(drawGroup->getTestContext(), getString(*pLoopImageAspect), ""));
for (const VkSampleCountFlagBits* pLoopNumSamples = sampleCountRange; pLoopNumSamples < DE_ARRAY_END(sampleCountRange); ++pLoopNumSamples)
{
MovePtr<tcu::TestCaseGroup> samplesGroup (new tcu::TestCaseGroup(aspectGroup->getTestContext(), getString(*pLoopNumSamples).c_str(), ""));
for (deUint32 loopDrawSetNdx = 0u; loopDrawSetNdx < DE_LENGTH_OF_ARRAY(drawClearSets); ++loopDrawSetNdx)
for (const deUint32* pLoopOptions = optionSets; pLoopOptions != DE_ARRAY_END(optionSets); ++pLoopOptions)
{
const TestParams params =
{
*pLoopNumSamples, // VkSampleCountFlagBits numSamples;
*pLoopOptions, // TestOptionFlags options;
drawClearSets[loopDrawSetNdx].drawIn, // TestDrawIn drawIn;
drawClearSets[loopDrawSetNdx].clears, // TestClears clears;
*pLoopImageAspect, // TestImageAspect imageAspect;
};
// Filter out incompatible parameter combinations
if (params.imageAspect != TEST_IMAGE_ASPECT_COLOR)
{
// If the sample pattern is changed, the D/S image must be cleared or the result is undefined
if (((params.options & TEST_OPTION_SAME_PATTERN_BIT) == 0u) && (params.clears == TEST_CLEARS_NO_CLEAR))
continue;
}
// We are using events to change image layout and this is only allowed outside a render pass
if (((params.options & TEST_OPTION_WAIT_EVENTS_BIT) != 0u) && (params.drawIn != TEST_DRAW_IN_RENDER_PASSES))
continue;
// Can't change image layout inside a subpass
if (((params.options & TEST_OPTION_GENERAL_LAYOUT_BIT) != 0u) && (params.drawIn == TEST_DRAW_IN_SAME_SUBPASS))
continue;
std::ostringstream caseName;
caseName << getString(params.drawIn) << "_"
<< getString(params.clears) << (params.options != 0 ? "_" : "")
<< getTestOptionFlagsString(params.options);
addInstanceTestCaseWithPrograms<DrawTest>(samplesGroup.get(), caseName.str().c_str(), "", initPrograms, params);
}
aspectGroup->addChild(samplesGroup.release());
}
drawGroup->addChild(aspectGroup.release());
}
rootGroup->addChild(drawGroup.release());
}
}
} // anonymous ns
tcu::TestCaseGroup* createMultisampleSampleLocationsExtTests (tcu::TestContext& testCtx)
{
return createTestGroup(testCtx, "sample_locations_ext", "Test a graphics pipeline with user-defined sample locations", createTestsInGroup);
}
} // pipeline
} // vkt