| /*------------------------------------------------------------------------ |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2019 Advanced Micro Devices, Inc. |
| * Copyright (c) 2019 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_AMD_mixed_attachment_samples |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vktPipelineMultisampleMixedAttachmentSamplesTests.hpp" |
| #include "vktPipelineSampleLocationsUtil.hpp" |
| #include "vktPipelineMakeUtil.hpp" |
| #include "vktTestCase.hpp" |
| #include "vktTestCaseUtil.hpp" |
| #include "vktTestGroupUtil.hpp" |
| |
| #include "vkCmdUtil.hpp" |
| #include "vkObjUtil.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 "deUniquePtr.hpp" |
| #include "deSharedPtr.hpp" |
| #include "deRandom.hpp" |
| #include "deMath.h" |
| |
| #include "tcuVector.hpp" |
| #include "tcuTestLog.hpp" |
| #include "tcuImageCompare.hpp" |
| #include "tcuTextureUtil.hpp" |
| #include "tcuRGBA.hpp" |
| |
| #include <string> |
| #include <vector> |
| |
| namespace vkt |
| { |
| namespace pipeline |
| { |
| namespace |
| { |
| using namespace vk; |
| using de::UniquePtr; |
| using de::MovePtr; |
| using de::SharedPtr; |
| using tcu::UVec2; |
| using tcu::Vec2; |
| using tcu::Vec4; |
| |
| 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); |
| } |
| |
| 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_ASSERT(false); |
| return 0u; |
| } |
| |
| struct CompareData |
| { |
| Vec4 color; |
| float depth; |
| deUint32 stencil; |
| |
| // Pad to 2*16 bytes, in the shader the base alignment of this structure is 16 due to vec4 |
| deUint32 padding[2]; |
| |
| CompareData() : color(Vec4(0.0f)), depth(0.0f), stencil(0u) |
| { |
| padding[0] = 0u; |
| padding[1] = 0u; |
| |
| static_assert(sizeof(CompareData) == (2 * 16), "Wrong structure size, expected 16 bytes"); |
| } |
| }; |
| |
| //! Make a dummy sampler. |
| Move<VkSampler> makeSampler (const DeviceInterface& vk, const VkDevice device) |
| { |
| const VkSamplerCreateInfo samplerParams = |
| { |
| VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (VkSamplerCreateFlags)0, // VkSamplerCreateFlags flags; |
| VK_FILTER_NEAREST, // VkFilter magFilter; |
| VK_FILTER_NEAREST, // VkFilter minFilter; |
| VK_SAMPLER_MIPMAP_MODE_NEAREST, // VkSamplerMipmapMode mipmapMode; |
| VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeU; |
| VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeV; |
| VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeW; |
| 0.0f, // float mipLodBias; |
| VK_FALSE, // VkBool32 anisotropyEnable; |
| 1.0f, // float maxAnisotropy; |
| VK_FALSE, // VkBool32 compareEnable; |
| VK_COMPARE_OP_ALWAYS, // VkCompareOp compareOp; |
| 0.0f, // float minLod; |
| 0.0f, // float maxLod; |
| VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, // VkBorderColor borderColor; |
| VK_FALSE, // VkBool32 unnormalizedCoordinates; |
| }; |
| return createSampler(vk, device, &samplerParams); |
| } |
| |
| Move<VkImage> makeImage (const DeviceInterface& vk, |
| const VkDevice device, |
| 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; |
| (VkImageCreateFlags)0, // 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 bool isDepthFormat (const VkFormat format) |
| { |
| return (getImageAspectFlags(format) & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; |
| } |
| |
| inline bool isStencilFormat (const VkFormat format) |
| { |
| return (getImageAspectFlags(format) & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; |
| } |
| |
| //! Create a test-specific MSAA pipeline |
| Move<VkPipeline> makeGraphicsPipeline (const DeviceInterface& vk, |
| const VkDevice device, |
| const VkPipelineLayout pipelineLayout, |
| const VkRenderPass renderPass, |
| const VkShaderModule vertexModule, |
| const VkShaderModule fragmentModule, |
| const bool useVertexInput, |
| const deUint32 subpassNdx, |
| const UVec2& renderSize, |
| const VkImageAspectFlags depthStencilAspect, //!< Used to determine which D/S tests to turn on |
| const VkSampleCountFlagBits numSamples, |
| const bool sampleShadingEnable, |
| const VkSampleLocationsInfoEXT* pSampleLocationsInfo = DE_NULL) |
| { |
| std::vector<VkVertexInputBindingDescription> vertexInputBindingDescriptions; |
| std::vector<VkVertexInputAttributeDescription> vertexInputAttributeDescriptions; |
| |
| // Vertex attributes: position and color |
| if (useVertexInput) |
| { |
| vertexInputBindingDescriptions.push_back (makeVertexInputBindingDescription (0u, 2 * sizeof(Vec4), 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, sizeof(Vec4))); |
| } |
| |
| 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 VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateInfo = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (VkPipelineInputAssemblyStateCreateFlags)0, // VkPipelineInputAssemblyStateCreateFlags flags; |
| VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // VkPrimitiveTopology topology; |
| VK_FALSE, // VkBool32 primitiveRestartEnable; |
| }; |
| |
| const VkViewport viewport = |
| { |
| 0.0f, 0.0f, // x, y |
| static_cast<float>(renderSize.x()), static_cast<float>(renderSize.y()), // widht, height |
| 0.0f, 1.0f // minDepth, maxDepth |
| }; |
| |
| const VkRect2D scissor = |
| { |
| makeOffset2D(0, 0), |
| makeExtent2D(renderSize.x(), renderSize.y()), |
| }; |
| |
| const VkPipelineViewportStateCreateInfo pipelineViewportStateInfo = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (VkPipelineViewportStateCreateFlags)0, // VkPipelineViewportStateCreateFlags flags; |
| 1u, // uint32_t viewportCount; |
| &viewport, // const VkViewport* pViewports; |
| 1u, // uint32_t scissorCount; |
| &scissor, // const VkRect2D* pScissors; |
| }; |
| |
| const VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateInfo = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (VkPipelineRasterizationStateCreateFlags)0, // VkPipelineRasterizationStateCreateFlags flags; |
| VK_FALSE, // VkBool32 depthClampEnable; |
| VK_FALSE, // VkBool32 rasterizerDiscardEnable; |
| VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode; |
| VK_CULL_MODE_NONE, // VkCullModeFlags cullMode; |
| VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace; |
| VK_FALSE, // VkBool32 depthBiasEnable; |
| 0.0f, // float depthBiasConstantFactor; |
| 0.0f, // float depthBiasClamp; |
| 0.0f, // float depthBiasSlopeFactor; |
| 1.0f, // float lineWidth; |
| }; |
| |
| VkPipelineSampleLocationsStateCreateInfoEXT pipelineSampleLocationsCreateInfo = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_TRUE, // VkBool32 sampleLocationsEnable; |
| VkSampleLocationsInfoEXT(), // VkSampleLocationsInfoEXT sampleLocationsInfo; |
| }; |
| |
| VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateInfo = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (VkPipelineMultisampleStateCreateFlags)0, // VkPipelineMultisampleStateCreateFlags flags; |
| numSamples, // VkSampleCountFlagBits rasterizationSamples; |
| sampleShadingEnable, // VkBool32 sampleShadingEnable; |
| 1.0f, // float minSampleShading; |
| DE_NULL, // const VkSampleMask* pSampleMask; |
| VK_FALSE, // VkBool32 alphaToCoverageEnable; |
| VK_FALSE // VkBool32 alphaToOneEnable; |
| }; |
| |
| if (pSampleLocationsInfo) |
| { |
| pipelineSampleLocationsCreateInfo.sampleLocationsInfo = *pSampleLocationsInfo; |
| pipelineMultisampleStateInfo.pNext = &pipelineSampleLocationsCreateInfo; |
| } |
| |
| // Simply increment the buffer |
| const VkStencilOpState stencilOpState = 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 |
| 0u); // reference |
| |
| // Always pass the depth test |
| VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateInfo = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (VkPipelineDepthStencilStateCreateFlags)0, // VkPipelineDepthStencilStateCreateFlags flags; |
| (depthStencilAspect & VK_IMAGE_ASPECT_DEPTH_BIT) != 0u, // VkBool32 depthTestEnable; |
| VK_TRUE, // VkBool32 depthWriteEnable; |
| VK_COMPARE_OP_ALWAYS, // VkCompareOp depthCompareOp; |
| VK_FALSE, // VkBool32 depthBoundsTestEnable; |
| (depthStencilAspect & VK_IMAGE_ASPECT_STENCIL_BIT) != 0u, // VkBool32 stencilTestEnable; |
| stencilOpState, // VkStencilOpState front; |
| stencilOpState, // VkStencilOpState back; |
| 0.0f, // float minDepthBounds; |
| 1.0f, // float maxDepthBounds; |
| }; |
| |
| const VkColorComponentFlags colorComponentsAll = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; |
| const VkPipelineColorBlendAttachmentState defaultBlendAttachmentState = |
| { |
| VK_FALSE, // VkBool32 blendEnable; |
| VK_BLEND_FACTOR_ONE, // VkBlendFactor srcColorBlendFactor; |
| VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstColorBlendFactor; |
| VK_BLEND_OP_ADD, // VkBlendOp colorBlendOp; |
| VK_BLEND_FACTOR_ONE, // VkBlendFactor srcAlphaBlendFactor; |
| VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstAlphaBlendFactor; |
| VK_BLEND_OP_ADD, // VkBlendOp alphaBlendOp; |
| colorComponentsAll, // VkColorComponentFlags colorWriteMask; |
| }; |
| |
| const VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateInfo = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (VkPipelineColorBlendStateCreateFlags)0, // VkPipelineColorBlendStateCreateFlags flags; |
| VK_FALSE, // VkBool32 logicOpEnable; |
| VK_LOGIC_OP_COPY, // VkLogicOp logicOp; |
| 1u, // deUint32 attachmentCount; |
| &defaultBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments; |
| { 0.0f, 0.0f, 0.0f, 0.0f }, // float blendConstants[4]; |
| }; |
| |
| const VkPipelineShaderStageCreateInfo pShaderStages[] = |
| { |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags; |
| VK_SHADER_STAGE_VERTEX_BIT, // VkShaderStageFlagBits stage; |
| vertexModule, // VkShaderModule module; |
| "main", // const char* pName; |
| DE_NULL, // const VkSpecializationInfo* pSpecializationInfo; |
| }, |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags; |
| VK_SHADER_STAGE_FRAGMENT_BIT, // VkShaderStageFlagBits stage; |
| fragmentModule, // VkShaderModule module; |
| "main", // const char* pName; |
| DE_NULL, // const VkSpecializationInfo* pSpecializationInfo; |
| } |
| }; |
| |
| const VkGraphicsPipelineCreateInfo graphicsPipelineInfo = |
| { |
| VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (VkPipelineCreateFlags)0, // VkPipelineCreateFlags flags; |
| DE_LENGTH_OF_ARRAY(pShaderStages), // deUint32 stageCount; |
| pShaderStages, // const VkPipelineShaderStageCreateInfo* pStages; |
| &vertexInputStateInfo, // const VkPipelineVertexInputStateCreateInfo* pVertexInputState; |
| &pipelineInputAssemblyStateInfo, // const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState; |
| DE_NULL, // const VkPipelineTessellationStateCreateInfo* pTessellationState; |
| &pipelineViewportStateInfo, // const VkPipelineViewportStateCreateInfo* pViewportState; |
| &pipelineRasterizationStateInfo, // const VkPipelineRasterizationStateCreateInfo* pRasterizationState; |
| &pipelineMultisampleStateInfo, // const VkPipelineMultisampleStateCreateInfo* pMultisampleState; |
| &pipelineDepthStencilStateInfo, // const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState; |
| &pipelineColorBlendStateInfo, // const VkPipelineColorBlendStateCreateInfo* pColorBlendState; |
| DE_NULL, // const VkPipelineDynamicStateCreateInfo* pDynamicState; |
| pipelineLayout, // VkPipelineLayout layout; |
| renderPass, // VkRenderPass renderPass; |
| subpassNdx, // deUint32 subpass; |
| DE_NULL, // VkPipeline basePipelineHandle; |
| -1, // deInt32 basePipelineIndex; |
| }; |
| |
| return createGraphicsPipeline(vk, device, DE_NULL, &graphicsPipelineInfo); |
| } |
| |
| //! Wrap float after an increment |
| inline float wrapIncFloat (float a, float min, float max) |
| { |
| return deFloatMax(min, deFloatMod(a, max)); |
| } |
| |
| //! Generate expected data for color, depth, and stencil samples of a given image. |
| //! Samples are ordered starting at pixel (0, 0) - see compute shader source for reference. |
| std::vector<CompareData> generateCompareData (const deUint32 seed, |
| const UVec2& imageSize, |
| const deUint32 numCoverageSamples, |
| const deUint32 numColorSamples, |
| const deUint32 numDepthStencilSamples) |
| { |
| std::vector<CompareData> allData; |
| de::Random rng (seed); |
| |
| for (deUint32 y = 0u; y < imageSize.y(); ++y) |
| for (deUint32 x = 0u; x < imageSize.x(); ++x) |
| for (deUint32 sample = 0u; sample < numCoverageSamples; ++sample) |
| { |
| CompareData cd; |
| |
| if (sample < numColorSamples) |
| { |
| for (int i = 0; i < 3; ++i) |
| cd.color[i] = 0.1f * static_cast<float>(rng.getInt(1, 10)); |
| |
| cd.color.w() = 1.0f; |
| } |
| |
| if (sample < numDepthStencilSamples) |
| { |
| const deUint32 globalSample = sample + numColorSamples * (x + imageSize.x() * y); |
| cd.depth = wrapIncFloat(0.05f * static_cast<float>(1 + globalSample), 0.05f, 1.0f); |
| cd.stencil = 1 + globalSample % numCoverageSamples; |
| } |
| |
| allData.push_back(cd); |
| } |
| |
| return allData; |
| } |
| |
| //! NDC transformation algorithm for sample locations |
| template<typename SampleAccessor> |
| std::vector<Vec2> ndcTransformEachSampleInPixel (const UVec2& framebufferSize, const deUint32 numSamplesPerPixel, const SampleAccessor& access) |
| { |
| 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; ++sampleNdx) |
| { |
| const Vec2& sp = access(x, y, sampleNdx); |
| const float globalX = sp.x() + static_cast<float>(x); |
| const float globalY = sp.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; |
| } |
| |
| class AccessStandardSampleLocationsArray |
| { |
| public: |
| AccessStandardSampleLocationsArray (const Vec2* ptr) : m_pData (ptr) {} |
| |
| const Vec2& operator ()(const deUint32 x, const deUint32 y, const deUint32 sampleNdx) const |
| { |
| DE_UNREF(x); |
| DE_UNREF(y); |
| return m_pData[sampleNdx]; |
| } |
| |
| private: |
| const Vec2* m_pData; |
| }; |
| |
| class AccessMultisamplePixelGrid |
| { |
| public: |
| AccessMultisamplePixelGrid (const MultisamplePixelGrid* ptr) : m_pGrid (ptr) {} |
| |
| Vec2 operator ()(const deUint32 x, const deUint32 y, const deUint32 sampleNdx) const |
| { |
| const VkSampleLocationEXT& sp = m_pGrid->getSample(x, y, sampleNdx); |
| return Vec2(sp.x, sp.y); |
| } |
| |
| private: |
| const MultisamplePixelGrid* m_pGrid; |
| }; |
| |
| //! Generate NDC space standard sample locations at each framebuffer pixel |
| //! Data is filled starting at pixel (0,0) and for each pixel there are numSamples samples |
| std::vector<Vec2> genFramebufferStandardSampleLocations (const VkSampleCountFlagBits numSamples, const UVec2& framebufferSize) |
| { |
| static const Vec2 s_location_samples_1[] = |
| { |
| Vec2(0.5f, 0.5f), |
| }; |
| static const Vec2 s_location_samples_2[] = |
| { |
| Vec2(0.75f, 0.75f), |
| Vec2(0.25f, 0.25f), |
| }; |
| static const Vec2 s_location_samples_4[] = |
| { |
| Vec2(0.375f, 0.125f), |
| Vec2(0.875f, 0.375f), |
| Vec2(0.125f, 0.625f), |
| Vec2(0.625f, 0.875f), |
| }; |
| static const Vec2 s_location_samples_8[] = |
| { |
| Vec2(0.5625f, 0.3125f), |
| Vec2(0.4375f, 0.6875f), |
| Vec2(0.8125f, 0.5625f), |
| Vec2(0.3125f, 0.1875f), |
| Vec2(0.1875f, 0.8125f), |
| Vec2(0.0625f, 0.4375f), |
| Vec2(0.6875f, 0.9375f), |
| Vec2(0.9375f, 0.0625f), |
| }; |
| static const Vec2 s_location_samples_16[] = |
| { |
| Vec2(0.5625f, 0.5625f), |
| Vec2(0.4375f, 0.3125f), |
| Vec2(0.3125f, 0.6250f), |
| Vec2(0.7500f, 0.4375f), |
| Vec2(0.1875f, 0.3750f), |
| Vec2(0.6250f, 0.8125f), |
| Vec2(0.8125f, 0.6875f), |
| Vec2(0.6875f, 0.1875f), |
| Vec2(0.3750f, 0.8750f), |
| Vec2(0.5000f, 0.0625f), |
| Vec2(0.2500f, 0.1250f), |
| Vec2(0.1250f, 0.7500f), |
| Vec2(0.0000f, 0.5000f), |
| Vec2(0.9375f, 0.2500f), |
| Vec2(0.8750f, 0.9375f), |
| Vec2(0.0625f, 0.0000f), |
| }; |
| |
| const Vec2* pSampleLocation = DE_NULL; |
| |
| switch (numSamples) |
| { |
| case VK_SAMPLE_COUNT_1_BIT: pSampleLocation = s_location_samples_1; break; |
| case VK_SAMPLE_COUNT_2_BIT: pSampleLocation = s_location_samples_2; break; |
| case VK_SAMPLE_COUNT_4_BIT: pSampleLocation = s_location_samples_4; break; |
| case VK_SAMPLE_COUNT_8_BIT: pSampleLocation = s_location_samples_8; break; |
| case VK_SAMPLE_COUNT_16_BIT: pSampleLocation = s_location_samples_16; break; |
| |
| default: |
| DE_ASSERT(0); |
| return std::vector<Vec2>(); |
| } |
| |
| return ndcTransformEachSampleInPixel(framebufferSize, static_cast<deUint32>(numSamples), AccessStandardSampleLocationsArray(pSampleLocation)); |
| } |
| |
| //! Generate NDC space custom sample locations at each framebuffer pixel, based on the given pixel grid |
| std::vector<Vec2> getSampleLocations (const MultisamplePixelGrid& pixelGrid, const UVec2& framebufferSize) |
| { |
| return ndcTransformEachSampleInPixel(framebufferSize, pixelGrid.samplesPerPixel(), AccessMultisamplePixelGrid(&pixelGrid)); |
| } |
| |
| struct PositionColor |
| { |
| tcu::Vec4 position; |
| tcu::Vec4 color; |
| |
| PositionColor (const tcu::Vec4& pos, const tcu::Vec4& col) : position(pos), color(col) {} |
| }; |
| |
| //! Generate subpixel triangles containing the sample position, based on compare data. |
| //! Stencil values are created by overlapping triangles, so the stencil pipeline state must be set up accordingly. |
| std::vector<PositionColor> generateSubpixelTriangles (const UVec2& renderSize, |
| const std::vector<CompareData>& compareData, |
| const std::vector<Vec2>& sampleLocations) |
| { |
| std::vector<PositionColor> 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) / renderSize.cast<float>(); |
| const Vec2 offset = pixelSize / 16.0f; // 4 bits precision |
| |
| // 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(); |
| |
| DE_ASSERT(compareData.size() == sampleLocations.size()); |
| |
| for (std::size_t globalSampleNdx = 0; globalSampleNdx < sampleLocations.size(); ++globalSampleNdx) |
| { |
| const Vec2& loc = sampleLocations[globalSampleNdx]; |
| const CompareData& cd = compareData [globalSampleNdx]; |
| |
| // Overdraw at the same position to get the desired stencil |
| // Draw at least once, if stencil is 0 |
| for (deUint32 i = 0; i < deMaxu32(1u, cd.stencil); ++i) |
| { |
| vertices.push_back(PositionColor(Vec4(loc.x(), loc.y() - y1, cd.depth, 1.0f), cd.color)); |
| vertices.push_back(PositionColor(Vec4(loc.x() - x1, loc.y() + y2, cd.depth, 1.0f), cd.color)); |
| vertices.push_back(PositionColor(Vec4(loc.x() + x1, loc.y() + y2, cd.depth, 1.0f), cd.color)); |
| } |
| } |
| |
| return vertices; |
| } |
| |
| void reportSampleError (tcu::TestLog& log, const std::string& sampleDesc, UVec2& renderSize, const deUint32 numCoverageSamples, const deUint32 globalSampleNdx) |
| { |
| const deUint32 pixelNdx = globalSampleNdx / numCoverageSamples; |
| const deUint32 x = pixelNdx % renderSize.x(); |
| const deUint32 y = pixelNdx / renderSize.x(); |
| const deUint32 sample = globalSampleNdx % numCoverageSamples; |
| |
| log << tcu::TestLog::Message << "Incorrect " << sampleDesc << " sample (" << sample << ") at pixel (" << x << ", " << y << ")" << tcu::TestLog::EndMessage; |
| } |
| |
| void checkSampleRequirements (Context& context, |
| const VkSampleCountFlagBits numColorSamples, |
| const VkSampleCountFlagBits numDepthStencilSamples, |
| const bool requireStandardSampleLocations) |
| { |
| const VkPhysicalDeviceLimits& limits = context.getDeviceProperties().limits; |
| |
| if ((limits.framebufferColorSampleCounts & numColorSamples) == 0u) |
| TCU_THROW(NotSupportedError, "framebufferColorSampleCounts: sample count not supported"); |
| |
| if ((limits.framebufferDepthSampleCounts & numDepthStencilSamples) == 0u) |
| TCU_THROW(NotSupportedError, "framebufferDepthSampleCounts: sample count not supported"); |
| |
| if ((limits.framebufferStencilSampleCounts & numDepthStencilSamples) == 0u) |
| TCU_THROW(NotSupportedError, "framebufferStencilSampleCounts: sample count not supported"); |
| |
| if ((limits.sampledImageColorSampleCounts & numColorSamples) == 0u) |
| TCU_THROW(NotSupportedError, "sampledImageColorSampleCounts: sample count not supported"); |
| |
| if ((limits.sampledImageDepthSampleCounts & numDepthStencilSamples) == 0u) |
| TCU_THROW(NotSupportedError, "sampledImageDepthSampleCounts: sample count not supported"); |
| |
| if ((limits.sampledImageStencilSampleCounts & numDepthStencilSamples) == 0u) |
| TCU_THROW(NotSupportedError, "sampledImageStencilSampleCounts: sample count not supported"); |
| |
| // This is required to output geometry that is covering a specific sample |
| if (requireStandardSampleLocations && !limits.standardSampleLocations) |
| TCU_THROW(NotSupportedError, "standardSampleLocations: not supported"); |
| } |
| |
| void checkImageRequirements (Context& context, |
| const VkFormat format, |
| const VkFormatFeatureFlags requiredFeatureFlags, |
| const VkImageUsageFlags requiredUsageFlags, |
| const VkSampleCountFlagBits requiredSampleCount = VK_SAMPLE_COUNT_1_BIT) |
| { |
| const InstanceInterface& vki = context.getInstanceInterface(); |
| const VkPhysicalDevice physicalDevice = context.getPhysicalDevice(); |
| VkImageFormatProperties imageProperties; |
| |
| const VkFormatProperties formatProperties = getPhysicalDeviceFormatProperties(vki, physicalDevice, format); |
| |
| if ((formatProperties.optimalTilingFeatures & requiredFeatureFlags) != requiredFeatureFlags) |
| TCU_THROW(NotSupportedError, (de::toString(format) + ": format features not supported").c_str()); |
| |
| const VkResult result = vki.getPhysicalDeviceImageFormatProperties(physicalDevice, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, requiredUsageFlags, (VkImageCreateFlags)0, &imageProperties); |
| |
| if (result == VK_ERROR_FORMAT_NOT_SUPPORTED) |
| TCU_THROW(NotSupportedError, (de::toString(format) + ": format not supported").c_str()); |
| |
| if ((imageProperties.sampleCounts & requiredSampleCount) != requiredSampleCount) |
| TCU_THROW(NotSupportedError, (de::toString(format) + ": sample count not supported").c_str()); |
| } |
| |
| //! Used after a render pass color output (draw or resolve) |
| void recordCopyOutputImageToBuffer (const DeviceInterface& vk, |
| const VkCommandBuffer cmdBuffer, |
| const UVec2& imageSize, |
| const VkImage srcImage, |
| const VkBuffer dstBuffer) |
| { |
| // Image read barrier after color output |
| { |
| const VkImageMemoryBarrier barrier = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| 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; |
| VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex; |
| VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex; |
| srcImage, // VkImage image; |
| makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u), // VkImageSubresourceRange subresourceRange; |
| }; |
| |
| vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0u, DE_NULL, 0u, DE_NULL, 1u, &barrier); |
| } |
| // 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, ®ion); |
| } |
| // 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); |
| } |
| } |
| |
| namespace VerifySamples |
| { |
| |
| //! The parameters that define a test case |
| struct TestParams |
| { |
| struct SampleCount |
| { |
| VkSampleCountFlagBits numCoverageSamples; //!< VkPipelineMultisampleStateCreateInfo::rasterizationSamples |
| VkSampleCountFlagBits numColorSamples; //!< VkAttachmentDescription::samples and VkImageCreateInfo::samples |
| VkSampleCountFlagBits numDepthStencilSamples; //!< VkAttachmentDescription::samples and VkImageCreateInfo::samples |
| }; |
| |
| VkFormat colorFormat; //!< Color attachment format |
| VkFormat depthStencilFormat; //!< D/S attachment format. Will test both aspects if it's a mixed format |
| bool useProgrammableSampleLocations; //!< Try to use VK_EXT_sample_locations if available |
| std::vector<SampleCount> perSubpassSamples; //!< Will use multiple subpasses if more than one element |
| |
| TestParams (void) |
| : colorFormat () |
| , depthStencilFormat () |
| , useProgrammableSampleLocations () |
| { |
| } |
| }; |
| |
| //! Common data used by the test |
| struct WorkingData |
| { |
| struct PerSubpass |
| { |
| deUint32 numVertices; //!< Number of vertices defined in the vertex buffer |
| Move<VkBuffer> vertexBuffer; |
| MovePtr<Allocation> vertexBufferAlloc; |
| Move<VkImage> colorImage; //!< Color image |
| Move<VkImageView> colorImageView; //!< Color attachment |
| MovePtr<Allocation> colorImageAlloc; |
| Move<VkImage> depthStencilImage; //!< Depth stencil image |
| Move<VkImageView> depthStencilImageView; //!< Depth stencil attachment |
| Move<VkImageView> depthOnlyImageView; //!< Depth aspect for shader read |
| Move<VkImageView> stencilOnlyImageView; //!< Stencil aspect for shader read |
| MovePtr<Allocation> depthStencilImageAlloc; |
| Move<VkBuffer> compareBuffer; //!< Buffer used to verify the images - comparison data |
| MovePtr<Allocation> compareBufferAlloc; |
| VkDeviceSize compareBufferSize; |
| Move<VkBuffer> resultBuffer; //!< Buffer used to verify the images - results |
| MovePtr<Allocation> resultBufferAlloc; |
| VkDeviceSize resultBufferSize; |
| deUint32 numResultElements; //!< Number of checksums in the result buffer |
| MovePtr<MultisamplePixelGrid> pixelGrid; //!< Programmable locations |
| |
| PerSubpass (void) |
| : numVertices () |
| , compareBufferSize () |
| , resultBufferSize () |
| , numResultElements () |
| { |
| } |
| }; |
| |
| UVec2 renderSize; //!< Size of the framebuffer |
| VkPhysicalDeviceSampleLocationsPropertiesEXT sampleLocationsProperties; //!< Used with VK_EXT_sample_locations |
| |
| std::vector<de::SharedPtr<PerSubpass> > perSubpass; //!< Test may use more than one set of data |
| |
| WorkingData (void) |
| : sampleLocationsProperties () |
| { |
| } |
| }; |
| |
| void addVerificationComputeShader (SourceCollections& programCollection, |
| const VkSampleCountFlagBits numCoverageSamples, |
| const VkSampleCountFlagBits numColorSamples, |
| const VkSampleCountFlagBits numDepthStencilSamples, |
| const VkFormat depthStencilFormat, |
| const std::string& nameSuffix) |
| { |
| const bool isColorMS = (numColorSamples != VK_SAMPLE_COUNT_1_BIT); |
| const bool isDepthStencilMS = (numDepthStencilSamples != VK_SAMPLE_COUNT_1_BIT); |
| const std::string colorBit = de::toString(static_cast<deUint32>(VK_IMAGE_ASPECT_COLOR_BIT)) + "u"; |
| const std::string depthBit = de::toString(static_cast<deUint32>(VK_IMAGE_ASPECT_DEPTH_BIT)) + "u"; |
| const std::string stencilBit = de::toString(static_cast<deUint32>(VK_IMAGE_ASPECT_STENCIL_BIT)) + "u"; |
| |
| std::ostringstream src; |
| src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" |
| << "\n" |
| << "struct CompareData {\n" |
| << " vec4 color;\n" |
| << " float depth;\n" |
| << " uint stencil;\n" |
| << "};\n" |
| << "\n" |
| << "layout(local_size_x = " << static_cast<deUint32>(numCoverageSamples) << ") in;\n" |
| // Always use this descriptor layout and ignore unused bindings |
| << "layout(set = 0, binding = 0, std430) writeonly buffer Output {\n" |
| << " uint values[];\n" |
| << "} sb_out;\n" |
| << "layout(set = 0, binding = 1, std430) readonly buffer InputCompare {\n" |
| << " CompareData data[];\n" |
| << "} sb_cmp;\n" |
| << "layout(set = 0, binding = 2) uniform sampler2D" << (isColorMS ? "MS" : "") << " colorImage;\n" |
| << "layout(set = 0, binding = 3) uniform sampler2D" << (isDepthStencilMS ? "MS" : "") <<" depthImage;\n" |
| << "layout(set = 0, binding = 4) uniform usampler2D" << (isDepthStencilMS ? "MS" : "") <<" stencilImage;\n" |
| << "\n" |
| << "void main (void)\n" |
| << "{\n" |
| |
| // Data for each sample in each pixel is laid out linearly (e.g 2 samples): |
| // [pixel(0, 0) sample(0)][pixel(0, 0) sample(1)][pixel(1, 0) sample(0)][pixel(1, 0) sample(1)]... |
| |
| << " uint globalIndex = gl_LocalInvocationID.x + gl_WorkGroupSize.x * (gl_WorkGroupID.x + gl_WorkGroupID.y * gl_NumWorkGroups.x);\n" |
| << " ivec2 position = ivec2(gl_WorkGroupID.x, gl_WorkGroupID.y);\n" |
| << " int sampleNdx = int(gl_LocalInvocationID.x);\n" |
| << " uint result = 0u;\n" |
| << "\n" |
| << " // Verify color samples\n" |
| << " if (sampleNdx < " << static_cast<deUint32>(numColorSamples) << ")\n" |
| << " {\n" |
| << " vec4 color = texelFetch(colorImage, position, sampleNdx);\n" // for non-MS (1 sample) case, sampleNdx = 0 and will instead be LOD = 0 |
| << " vec4 diff = abs(color - sb_cmp.data[globalIndex].color);\n" |
| << " vec4 threshold = vec4(0.02);\n" |
| << "\n" |
| << " if (all(lessThan(diff, threshold)))\n" |
| << " result |= " << colorBit << ";\n" |
| << " }\n" |
| << " else\n" |
| << " result |= " << colorBit << ";\n" // Pass, if sample doesn't exist |
| << "\n"; |
| |
| if (isDepthFormat(depthStencilFormat)) |
| { |
| src << " // Verify depth samples\n" |
| << " if (sampleNdx < " << static_cast<deUint32>(numDepthStencilSamples) << ")\n" |
| << " {\n" |
| << " float depth = texelFetch(depthImage, position, sampleNdx).r;\n" |
| << " float diff = abs(depth - sb_cmp.data[globalIndex].depth);\n" |
| << " float threshold = 0.002;\n" |
| << "\n" |
| << " if (diff < threshold)\n" |
| << " result |= " << depthBit << ";\n" |
| << " }\n" |
| << " else\n" |
| << " result |= " << depthBit << ";\n" |
| << "\n"; |
| } |
| |
| if (isStencilFormat(depthStencilFormat)) |
| { |
| src << " // Verify stencil samples\n" |
| << " if (sampleNdx < " << static_cast<deUint32>(numDepthStencilSamples) << ")\n" |
| << " {\n" |
| << " uint stencil = texelFetch(stencilImage, position, sampleNdx).r;\n" |
| << " uint diff = stencil - sb_cmp.data[globalIndex].stencil;\n" |
| << "\n" |
| << " if (diff == 0u)\n" |
| << " result |= " << stencilBit << ";\n" |
| << " }\n" |
| << " else\n" |
| << " result |= " << stencilBit << ";\n" |
| << "\n"; |
| } |
| |
| src << " sb_out.values[globalIndex] = result;\n" |
| << "}\n"; |
| programCollection.glslSources.add("comp" + nameSuffix) << glu::ComputeSource(src.str()); |
| } |
| |
| //! Get a compact sample count string in format X_Y_Z |
| std::string getSampleCountString (const TestParams::SampleCount& samples) |
| { |
| std::ostringstream str; |
| |
| str << static_cast<deUint32>(samples.numCoverageSamples) << "_" |
| << static_cast<deUint32>(samples.numColorSamples) << "_" |
| << static_cast<deUint32>(samples.numDepthStencilSamples); |
| |
| return str.str(); |
| } |
| |
| void initPrograms (SourceCollections& programCollection, const TestParams params) |
| { |
| // Vertex shader - position and color |
| { |
| 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"; |
| |
| programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); |
| } |
| |
| // Fragment shader - output color from VS |
| { |
| 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()); |
| } |
| |
| // Compute shader - image verification |
| for (deUint32 subpassNdx = 0; subpassNdx < static_cast<deUint32>(params.perSubpassSamples.size()); ++subpassNdx) |
| { |
| const TestParams::SampleCount& samples = params.perSubpassSamples[subpassNdx]; |
| addVerificationComputeShader(programCollection, |
| samples.numCoverageSamples, |
| samples.numColorSamples, |
| samples.numDepthStencilSamples, |
| params.depthStencilFormat, |
| "_" + getSampleCountString(samples)); |
| } |
| } |
| |
| //! A simple color, depth/stencil draw. Subpasses (if more than one) are independent |
| void draw (Context& context, const TestParams& params, WorkingData& wd) |
| { |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const VkDevice device = context.getDevice(); |
| const deUint32 numSubpasses = static_cast<deUint32>(wd.perSubpass.size()); |
| |
| Move<VkRenderPass> renderPass; |
| Move<VkFramebuffer> framebuffer; |
| std::vector<VkSampleLocationsInfoEXT> perSubpassSampleLocationsInfo; |
| std::vector<VkAttachmentSampleLocationsEXT> attachmentSampleLocations; |
| std::vector<VkSubpassSampleLocationsEXT> subpassSampleLocations; |
| |
| if (params.useProgrammableSampleLocations) |
| for (deUint32 subpassNdx = 0; subpassNdx < numSubpasses; ++subpassNdx) |
| { |
| perSubpassSampleLocationsInfo.push_back(makeSampleLocationsInfo(*wd.perSubpass[subpassNdx]->pixelGrid)); |
| } |
| |
| // Create a render pass and a framebuffer |
| { |
| std::vector<VkSubpassDescription> subpasses; |
| std::vector<VkImageView> attachments; |
| std::vector<VkAttachmentDescription> attachmentDescriptions; |
| std::vector<VkAttachmentReference> attachmentReferences; |
| |
| // Reserve capacity to avoid invalidating pointers to elements |
| attachmentReferences.reserve(numSubpasses * 2); |
| |
| for (deUint32 subpassNdx = 0; subpassNdx < numSubpasses; ++subpassNdx) |
| { |
| attachments.push_back(wd.perSubpass[subpassNdx]->colorImageView.get()); |
| attachments.push_back(wd.perSubpass[subpassNdx]->depthStencilImageView.get()); |
| |
| attachmentDescriptions.push_back(makeAttachmentDescription( |
| (VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags; |
| params.colorFormat, // VkFormat format; |
| params.perSubpassSamples[subpassNdx].numColorSamples, // VkSampleCountFlagBits samples; |
| VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp; |
| VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp; |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp; |
| VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL // VkImageLayout finalLayout; |
| )); |
| |
| attachmentDescriptions.push_back(makeAttachmentDescription( |
| (VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags; |
| params.depthStencilFormat, // VkFormat format; |
| params.perSubpassSamples[subpassNdx].numDepthStencilSamples, // VkSampleCountFlagBits samples; |
| 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_READ_ONLY_OPTIMAL // VkImageLayout finalLayout; |
| )); |
| |
| attachmentReferences.push_back(makeAttachmentReference(static_cast<deUint32>(attachmentReferences.size()), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)); |
| const VkAttachmentReference* colorRef = &attachmentReferences.back(); |
| |
| attachmentReferences.push_back(makeAttachmentReference(static_cast<deUint32>(attachmentReferences.size()), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)); |
| const VkAttachmentReference* depthStencilRef = &attachmentReferences.back(); |
| |
| if (params.useProgrammableSampleLocations) |
| { |
| const VkAttachmentSampleLocationsEXT newAttachmentSampleLocations = |
| { |
| attachmentReferences.back().attachment, // uint32_t attachmentIndex; |
| perSubpassSampleLocationsInfo[subpassNdx], // VkSampleLocationsInfoEXT sampleLocationsInfo; |
| }; |
| attachmentSampleLocations.push_back(newAttachmentSampleLocations); |
| |
| const VkSubpassSampleLocationsEXT newSubpassSampleLocations = |
| { |
| subpassNdx, // uint32_t subpassIndex; |
| perSubpassSampleLocationsInfo[subpassNdx], // VkSampleLocationsInfoEXT sampleLocationsInfo; |
| }; |
| subpassSampleLocations.push_back(newSubpassSampleLocations); |
| } |
| |
| const VkSubpassDescription subpassDescription = |
| { |
| (VkSubpassDescriptionFlags)0, // VkSubpassDescriptionFlags flags; |
| VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint; |
| 0u, // uint32_t inputAttachmentCount; |
| DE_NULL, // const VkAttachmentReference* pInputAttachments; |
| 1u, // uint32_t colorAttachmentCount; |
| colorRef, // const VkAttachmentReference* pColorAttachments; |
| DE_NULL, // const VkAttachmentReference* pResolveAttachments; |
| depthStencilRef, // const VkAttachmentReference* pDepthStencilAttachment; |
| 0u, // uint32_t preserveAttachmentCount; |
| DE_NULL, // const uint32_t* pPreserveAttachments; |
| }; |
| |
| subpasses.push_back(subpassDescription); |
| } |
| |
| // Assume there are no dependencies between subpasses |
| const VkRenderPassCreateInfo renderPassInfo = |
| { |
| VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags; |
| static_cast<deUint32>(attachmentDescriptions.size()), // deUint32 attachmentCount; |
| dataOrNullPtr(attachmentDescriptions), // const VkAttachmentDescription* pAttachments; |
| static_cast<deUint32>(subpasses.size()), // deUint32 subpassCount; |
| dataOrNullPtr(subpasses), // const VkSubpassDescription* pSubpasses; |
| 0u, // deUint32 dependencyCount; |
| DE_NULL, // const VkSubpassDependency* pDependencies; |
| }; |
| |
| renderPass = createRenderPass(vk, device, &renderPassInfo); |
| framebuffer = makeFramebuffer (vk, device, *renderPass, static_cast<deUint32>(attachments.size()), dataOrNullPtr(attachments), wd.renderSize.x(), wd.renderSize.y()); |
| } |
| |
| const Unique<VkShaderModule> vertexModule (createShaderModule(vk, device, context.getBinaryCollection().get("vert"), 0u)); |
| const Unique<VkShaderModule> fragmentModule (createShaderModule(vk, device, context.getBinaryCollection().get("frag"), 0u)); |
| const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device)); |
| |
| typedef SharedPtr<Unique<VkPipeline> > PipelineSp; |
| std::vector<PipelineSp> pipelines; |
| |
| for (deUint32 subpassNdx = 0; subpassNdx < numSubpasses; ++subpassNdx) |
| { |
| const VkSampleLocationsInfoEXT* pSampleLocationsInfo = (params.useProgrammableSampleLocations ? &perSubpassSampleLocationsInfo[subpassNdx] : DE_NULL); |
| |
| pipelines.push_back(PipelineSp(new Unique<VkPipeline>( |
| makeGraphicsPipeline(vk, device, *pipelineLayout, *renderPass, *vertexModule, *fragmentModule, /*use vertex input*/ true, subpassNdx, |
| wd.renderSize, getImageAspectFlags(params.depthStencilFormat), params.perSubpassSamples[subpassNdx].numCoverageSamples, |
| /*use sample shading*/ true, pSampleLocationsInfo)))); |
| } |
| |
| const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, context.getUniversalQueueFamilyIndex())); |
| const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer(vk, device, *cmdPool)); |
| |
| beginCommandBuffer(vk, *cmdBuffer); |
| |
| { |
| std::vector<VkClearValue> clearValues; |
| |
| for (deUint32 subpassNdx = 0; subpassNdx < numSubpasses; ++subpassNdx) |
| { |
| clearValues.push_back(makeClearValueColorF32(0.0f, 0.0f, 0.0f, 1.0f)); |
| clearValues.push_back(makeClearValueDepthStencil(1.0f, 0u)); |
| } |
| |
| const VkRect2D renderArea = |
| { |
| { 0u, 0u }, |
| { wd.renderSize.x(), wd.renderSize.y() } |
| }; |
| |
| VkRenderPassBeginInfo renderPassBeginInfo = |
| { |
| VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| *renderPass, // VkRenderPass renderPass; |
| *framebuffer, // VkFramebuffer framebuffer; |
| renderArea, // VkRect2D renderArea; |
| static_cast<deUint32>(clearValues.size()), // uint32_t clearValueCount; |
| dataOrNullPtr(clearValues), // const VkClearValue* pClearValues; |
| }; |
| |
| if (params.useProgrammableSampleLocations) |
| { |
| const VkRenderPassSampleLocationsBeginInfoEXT renderPassSampleLocationsBeginInfo = |
| { |
| VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| static_cast<deUint32>(attachmentSampleLocations.size()), // uint32_t attachmentInitialSampleLocationsCount; |
| dataOrNullPtr(attachmentSampleLocations), // const VkAttachmentSampleLocationsEXT* pAttachmentInitialSampleLocations; |
| static_cast<deUint32>(subpassSampleLocations.size()), // uint32_t postSubpassSampleLocationsCount; |
| dataOrNullPtr(subpassSampleLocations), // const VkSubpassSampleLocationsEXT* pPostSubpassSampleLocations; |
| }; |
| |
| renderPassBeginInfo.pNext = &renderPassSampleLocationsBeginInfo; |
| |
| vk.cmdBeginRenderPass(*cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); |
| } |
| else |
| vk.cmdBeginRenderPass(*cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); |
| } |
| |
| for (deUint32 subpassNdx = 0; subpassNdx < numSubpasses; ++subpassNdx) |
| { |
| if (subpassNdx != 0) |
| vk.cmdNextSubpass(*cmdBuffer, VK_SUBPASS_CONTENTS_INLINE); |
| |
| const VkDeviceSize vertexBufferOffset = 0ull; |
| vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &wd.perSubpass[subpassNdx]->vertexBuffer.get(), &vertexBufferOffset); |
| |
| vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, **pipelines[subpassNdx]); |
| |
| vk.cmdDraw(*cmdBuffer, wd.perSubpass[subpassNdx]->numVertices, 1u, 0u, 0u); |
| } |
| |
| vk.cmdEndRenderPass(*cmdBuffer); |
| |
| VK_CHECK(vk.endCommandBuffer(*cmdBuffer)); |
| submitCommandsAndWait(vk, device, context.getUniversalQueue(), *cmdBuffer); |
| } |
| |
| void dispatchImageCheck (Context& context, const TestParams& params, WorkingData& wd, const deUint32 subpassNdx) |
| { |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const VkDevice device = context.getDevice(); |
| WorkingData::PerSubpass& subpassData = *wd.perSubpass[subpassNdx]; |
| |
| const Unique<VkSampler> defaultSampler (makeSampler(vk, device)); |
| |
| // Create descriptor set |
| |
| const Unique<VkDescriptorSetLayout> descriptorSetLayout( |
| DescriptorSetLayoutBuilder() |
| .addSingleBinding (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT) |
| .addSingleBinding (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT) |
| .addSingleSamplerBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_COMPUTE_BIT, &defaultSampler.get()) |
| .addSingleSamplerBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_COMPUTE_BIT, &defaultSampler.get()) |
| .addSingleSamplerBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_COMPUTE_BIT, &defaultSampler.get()) |
| .build(vk, device)); |
| |
| const Unique<VkDescriptorPool> descriptorPool( |
| DescriptorPoolBuilder() |
| .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2u) |
| .addType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3u) |
| .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u)); |
| |
| const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout)); |
| |
| { |
| const VkDescriptorBufferInfo compareBufferInfo = makeDescriptorBufferInfo(*subpassData.compareBuffer, 0ull, subpassData.compareBufferSize); |
| const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(*subpassData.resultBuffer, 0ull, subpassData.resultBufferSize); |
| const VkDescriptorImageInfo colorImageInfo = makeDescriptorImageInfo(DE_NULL, *subpassData.colorImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); |
| const VkDescriptorImageInfo depthImageInfo = makeDescriptorImageInfo(DE_NULL, *subpassData.depthOnlyImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); |
| const VkDescriptorImageInfo stencilImageInfo = makeDescriptorImageInfo(DE_NULL, *subpassData.stencilOnlyImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); |
| |
| DescriptorSetUpdateBuilder builder; |
| |
| builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo); |
| builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &compareBufferInfo); |
| builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(2u), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &colorImageInfo); |
| |
| if (subpassData.depthOnlyImageView) |
| builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(3u), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &depthImageInfo); |
| |
| if (subpassData.stencilOnlyImageView) |
| builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(4u), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &stencilImageInfo); |
| |
| builder.update(vk, device); |
| } |
| |
| // Pipeline |
| |
| const std::string shaderName ("comp_" + getSampleCountString(params.perSubpassSamples[subpassNdx])); |
| const Unique<VkShaderModule> shaderModule (createShaderModule(vk, device, context.getBinaryCollection().get(shaderName), 0u)); |
| const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout)); |
| const Unique<VkPipeline> pipeline (makeComputePipeline(vk, device, *pipelineLayout, *shaderModule, DE_NULL)); |
| |
| const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, context.getUniversalQueueFamilyIndex())); |
| const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer(vk, device, *cmdPool)); |
| |
| beginCommandBuffer(vk, *cmdBuffer); |
| |
| vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); |
| vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL); |
| |
| vk.cmdDispatch(*cmdBuffer, wd.renderSize.x(), wd.renderSize.y(), 1u); |
| |
| { |
| const VkBufferMemoryBarrier barrier = |
| { |
| VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_SHADER_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; |
| *subpassData.resultBuffer, // VkBuffer buffer; |
| 0ull, // VkDeviceSize offset; |
| VK_WHOLE_SIZE, // VkDeviceSize size; |
| }; |
| |
| vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, |
| (const VkMemoryBarrier*)DE_NULL, 1u, &barrier, 0u, (const VkImageMemoryBarrier*)DE_NULL); |
| } |
| |
| VK_CHECK(vk.endCommandBuffer(*cmdBuffer)); |
| submitCommandsAndWait(vk, device, context.getUniversalQueue(), *cmdBuffer); |
| |
| invalidateMappedMemoryRange(vk, device, subpassData.resultBufferAlloc->getMemory(), subpassData.resultBufferAlloc->getOffset(), VK_WHOLE_SIZE); |
| } |
| |
| void createPerSubpassData (Context& context, const TestParams& params, WorkingData& wd, const deUint32 subpassNdx) |
| { |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const VkDevice device = context.getDevice(); |
| MovePtr<Allocator> allocator = MovePtr<Allocator>(new SimpleAllocator(vk, device, getPhysicalDeviceMemoryProperties(context.getInstanceInterface(), context.getPhysicalDevice()))); |
| const TestParams::SampleCount& samples = params.perSubpassSamples[subpassNdx]; |
| WorkingData::PerSubpass& subpassData = *wd.perSubpass[subpassNdx]; |
| |
| // Create images |
| { |
| |
| const VkImageUsageFlags colorImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; |
| const VkImageUsageFlags depthStencilImageUsageFlags = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; |
| |
| checkImageRequirements (context, |
| params.colorFormat, |
| VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT, |
| colorImageUsageFlags, |
| samples.numColorSamples); |
| |
| subpassData.colorImage = makeImage(vk, device, params.colorFormat, wd.renderSize, samples.numColorSamples, colorImageUsageFlags); |
| subpassData.colorImageAlloc = bindImage(vk, device, *allocator, *subpassData.colorImage, MemoryRequirement::Any); |
| subpassData.colorImageView = makeImageView(vk, device, *subpassData.colorImage, VK_IMAGE_VIEW_TYPE_2D, params.colorFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u)); |
| |
| checkImageRequirements (context, |
| params.depthStencilFormat, |
| VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT, |
| depthStencilImageUsageFlags, |
| samples.numDepthStencilSamples); |
| |
| subpassData.depthStencilImage = makeImage(vk, device, params.depthStencilFormat, wd.renderSize, samples.numDepthStencilSamples, depthStencilImageUsageFlags); |
| subpassData.depthStencilImageAlloc = bindImage(vk, device, *allocator, *subpassData.depthStencilImage, MemoryRequirement::Any); |
| subpassData.depthStencilImageView = makeImageView(vk, device, *subpassData.depthStencilImage, VK_IMAGE_VIEW_TYPE_2D, params.depthStencilFormat, makeImageSubresourceRange(getImageAspectFlags(params.depthStencilFormat), 0u, 1u, 0u, 1u)); |
| |
| if (isDepthFormat(params.depthStencilFormat)) |
| subpassData.depthOnlyImageView = makeImageView(vk, device, *subpassData.depthStencilImage, VK_IMAGE_VIEW_TYPE_2D, params.depthStencilFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u)); |
| |
| if (isStencilFormat(params.depthStencilFormat)) |
| subpassData.stencilOnlyImageView = makeImageView(vk, device, *subpassData.depthStencilImage, VK_IMAGE_VIEW_TYPE_2D, params.depthStencilFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_STENCIL_BIT, 0u, 1u, 0u, 1u)); |
| } |
| |
| // Create vertex and comparison buffers |
| { |
| const deUint32 seed = 123 + 19 * subpassNdx; |
| const std::vector<CompareData> compareData = generateCompareData(seed, wd.renderSize, samples.numCoverageSamples, samples.numColorSamples, samples.numDepthStencilSamples); |
| |
| subpassData.compareBufferSize = static_cast<VkDeviceSize>(sizeof(CompareData) * compareData.size()); |
| subpassData.compareBuffer = makeBuffer(vk, device, subpassData.compareBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| subpassData.compareBufferAlloc = bindBuffer(vk, device, *allocator, *subpassData.compareBuffer, MemoryRequirement::HostVisible); |
| |
| deMemcpy(subpassData.compareBufferAlloc->getHostPtr(), dataOrNullPtr(compareData), static_cast<std::size_t>(subpassData.compareBufferSize)); |
| flushMappedMemoryRange(vk, device, subpassData.compareBufferAlloc->getMemory(), subpassData.compareBufferAlloc->getOffset(), VK_WHOLE_SIZE); |
| |
| subpassData.numResultElements = static_cast<deUint32>(compareData.size()); |
| subpassData.resultBufferSize = static_cast<VkDeviceSize>(sizeof(deUint32) * compareData.size()); |
| subpassData.resultBuffer = makeBuffer(vk, device, subpassData.resultBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| subpassData.resultBufferAlloc = bindBuffer(vk, device, *allocator, *subpassData.resultBuffer, MemoryRequirement::HostVisible); |
| |
| deMemset(subpassData.resultBufferAlloc->getHostPtr(), 0, static_cast<std::size_t>(subpassData.resultBufferSize)); |
| flushMappedMemoryRange(vk, device, subpassData.resultBufferAlloc->getMemory(), subpassData.resultBufferAlloc->getOffset(), VK_WHOLE_SIZE); |
| |
| std::vector<PositionColor> vertices; |
| |
| if (params.useProgrammableSampleLocations) |
| { |
| subpassData.pixelGrid = MovePtr<MultisamplePixelGrid>(new MultisamplePixelGrid(UVec2(wd.sampleLocationsProperties.maxSampleLocationGridSize.width, |
| wd.sampleLocationsProperties.maxSampleLocationGridSize.height), |
| samples.numCoverageSamples)); |
| |
| const deUint32 locationsSeed = 211 + 4 * subpassNdx; |
| fillSampleLocationsRandom(*subpassData.pixelGrid, wd.sampleLocationsProperties.sampleLocationSubPixelBits, locationsSeed); |
| vertices = generateSubpixelTriangles(wd.renderSize, compareData, getSampleLocations(*subpassData.pixelGrid, wd.renderSize)); |
| } |
| else |
| { |
| const std::vector<Vec2> locations = genFramebufferStandardSampleLocations(samples.numCoverageSamples, wd.renderSize); |
| vertices = generateSubpixelTriangles(wd.renderSize, compareData, locations); |
| } |
| |
| const VkDeviceSize vertexBufferSize = static_cast<VkDeviceSize>(sizeof(vertices[0]) * vertices.size()); |
| subpassData.numVertices = static_cast<deUint32>(vertices.size()); |
| subpassData.vertexBuffer = makeBuffer(vk, device, vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); |
| subpassData.vertexBufferAlloc = bindBuffer(vk, device, *allocator, *subpassData.vertexBuffer, MemoryRequirement::HostVisible); |
| |
| deMemcpy(subpassData.vertexBufferAlloc->getHostPtr(), dataOrNullPtr(vertices), static_cast<std::size_t>(vertexBufferSize)); |
| flushMappedMemoryRange(vk, device, subpassData.vertexBufferAlloc->getMemory(), subpassData.vertexBufferAlloc->getOffset(), VK_WHOLE_SIZE); |
| } |
| } |
| |
| void checkRequirements (Context& context, TestParams params) |
| { |
| context.requireDeviceFunctionality("VK_AMD_mixed_attachment_samples"); |
| |
| if (params.useProgrammableSampleLocations) |
| context.requireDeviceFunctionality("VK_EXT_sample_locations"); |
| |
| for (deUint32 subpassNdx = 0; subpassNdx < static_cast<deUint32>(params.perSubpassSamples.size()); ++subpassNdx) |
| { |
| const TestParams::SampleCount& samples = params.perSubpassSamples[subpassNdx]; |
| checkSampleRequirements(context, samples.numColorSamples, samples.numDepthStencilSamples, !params.useProgrammableSampleLocations); |
| } |
| } |
| |
| //! Verify the values of all samples in all attachments. |
| tcu::TestStatus test (Context& context, const TestParams params) |
| { |
| WorkingData wd; |
| wd.renderSize = UVec2(2, 2); // Use a very small image, as we will verify all samples for all pixels |
| |
| // Query state related to programmable sample locations |
| if (params.useProgrammableSampleLocations) |
| { |
| const InstanceInterface& vki = context.getInstanceInterface(); |
| const VkPhysicalDevice physicalDevice = context.getPhysicalDevice(); |
| |
| wd.sampleLocationsProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT; |
| wd.sampleLocationsProperties.pNext = DE_NULL; |
| |
| VkPhysicalDeviceProperties2 properties = |
| { |
| VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, // VkStructureType sType; |
| &wd.sampleLocationsProperties, // void* pNext; |
| VkPhysicalDeviceProperties(), // VkPhysicalDeviceProperties properties; |
| }; |
| |
| vki.getPhysicalDeviceProperties2(physicalDevice, &properties); |
| |
| for (deUint32 subpassNdx = 0; subpassNdx < static_cast<deUint32>(params.perSubpassSamples.size()); ++subpassNdx) |
| { |
| if ((wd.sampleLocationsProperties.sampleLocationSampleCounts & params.perSubpassSamples[subpassNdx].numCoverageSamples) == 0u) |
| TCU_THROW(NotSupportedError, "VkSampleLocationsPropertiesAMD: sample count not supported"); |
| } |
| } |
| |
| // Create subpass data |
| for (deUint32 subpassNdx = 0; subpassNdx < static_cast<deUint32>(params.perSubpassSamples.size()); ++subpassNdx) |
| { |
| wd.perSubpass.push_back(SharedPtr<WorkingData::PerSubpass>(new WorkingData::PerSubpass())); |
| createPerSubpassData(context, params, wd, subpassNdx); |
| } |
| |
| // Draw test geometry |
| draw (context, params, wd); |
| |
| // Verify images with a compute shader |
| for (deUint32 subpassNdx = 0; subpassNdx < static_cast<deUint32>(params.perSubpassSamples.size()); ++subpassNdx) |
| dispatchImageCheck (context, params, wd, subpassNdx); |
| |
| // Test checksums |
| for (deUint32 subpassNdx = 0; subpassNdx < static_cast<deUint32>(params.perSubpassSamples.size()); ++subpassNdx) |
| { |
| const deUint32* const pSampleChecksumBase = static_cast<deUint32*>(wd.perSubpass[subpassNdx]->resultBufferAlloc->getHostPtr()); |
| const bool hasDepth = isDepthFormat(params.depthStencilFormat); |
| const bool hasStencil = isStencilFormat(params.depthStencilFormat); |
| bool allOk = true; |
| |
| context.getTestContext().getLog() << tcu::TestLog::Message << "Verify images in subpass " << subpassNdx << tcu::TestLog::EndMessage; |
| |
| for (deUint32 globalSampleNdx = 0; globalSampleNdx < wd.perSubpass[subpassNdx]->numResultElements; ++globalSampleNdx) |
| { |
| const TestParams::SampleCount& samples = params.perSubpassSamples[subpassNdx]; |
| const deUint32 checksum = pSampleChecksumBase[globalSampleNdx]; |
| |
| if ((checksum & VK_IMAGE_ASPECT_COLOR_BIT) == 0u) |
| { |
| reportSampleError(context.getTestContext().getLog(), "color", wd.renderSize, samples.numCoverageSamples, globalSampleNdx); |
| allOk = false; |
| } |
| |
| if (hasDepth && ((checksum & VK_IMAGE_ASPECT_DEPTH_BIT) == 0u)) |
| { |
| reportSampleError(context.getTestContext().getLog(), "depth", wd.renderSize, samples.numCoverageSamples, globalSampleNdx); |
| allOk = false; |
| } |
| |
| if (hasStencil && ((checksum & VK_IMAGE_ASPECT_STENCIL_BIT) == 0u)) |
| { |
| reportSampleError(context.getTestContext().getLog(), "stencil", wd.renderSize, samples.numCoverageSamples, globalSampleNdx); |
| allOk = false; |
| } |
| } |
| |
| if (!allOk) |
| return tcu::TestStatus::fail("Multisampled image has incorrect samples"); |
| } |
| |
| return tcu::TestStatus::pass("Pass"); |
| } |
| |
| } // VerifySamples |
| |
| namespace ShaderBuiltins |
| { |
| |
| struct TestParams |
| { |
| VkSampleCountFlagBits numCoverageSamples; //!< VkPipelineMultisampleStateCreateInfo::rasterizationSamples |
| VkSampleCountFlagBits numColorSamples; //!< VkAttachmentDescription::samples and VkImageCreateInfo::samples |
| VkSampleCountFlagBits numDepthStencilSamples; //!< VkAttachmentDescription::samples and VkImageCreateInfo::samples |
| VkFormat colorFormat; //!< Color attachment format |
| VkFormat depthStencilFormat; //!< D/S attachment format. Will test both aspects if it's a mixed format |
| }; |
| |
| struct WorkingData |
| { |
| UVec2 renderSize; //!< Size of the framebuffer |
| deUint32 numVertices; //!< Number of vertices defined in the vertex buffer |
| Move<VkBuffer> vertexBuffer; |
| MovePtr<Allocation> vertexBufferAlloc; |
| Move<VkImage> colorImage; //!< Color image |
| Move<VkImageView> colorImageView; //!< Color attachment |
| MovePtr<Allocation> colorImageAlloc; |
| Move<VkImage> depthStencilImage; //!< Depth stencil image |
| Move<VkImageView> depthStencilImageView; //!< Depth stencil attachment |
| Move<VkImageView> depthOnlyImageView; //!< Depth aspect for shader read |
| Move<VkImageView> stencilOnlyImageView; //!< Stencil aspect for shader read |
| MovePtr<Allocation> depthStencilImageAlloc; |
| Move<VkImage> resolveImage; //!< Resolve image |
| Move<VkImageView> resolveImageView; //!< Resolve attachment |
| MovePtr<Allocation> resolveImageAlloc; |
| Move<VkBuffer> colorBuffer; //!< Buffer used to copy resolve output |
| MovePtr<Allocation> colorBufferAlloc; |
| VkDeviceSize colorBufferSize; |
| |
| WorkingData (void) |
| : numVertices () |
| { |
| } |
| }; |
| |
| void initPrograms (SourceCollections& programCollection, const TestParams params) |
| { |
| // Vertex shader - no vertex data |
| { |
| std::ostringstream src; |
| src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" |
| << "\n" |
| << "out gl_PerVertex {\n" |
| << " vec4 gl_Position;\n" |
| << "};\n" |
| << "\n" |
| << "void main(void)\n" |
| << "{\n" |
| // Specify an oversized triangle covering the whole viewport. |
| << " switch (gl_VertexIndex)\n" |
| << " {\n" |
| << " case 0:\n" |
| << " gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n" |
| << " break;\n" |
| << " case 1:\n" |
| << " gl_Position = vec4(-1.0, 3.0, 0.0, 1.0);\n" |
| << " break;\n" |
| << " case 2:\n" |
| << " gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n" |
| << " break;\n" |
| << " }\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" |
| << "void main(void)\n" |
| << "{\n" |
| << " vec4 col = vec4(0.0, 0.0, 0.0, 1.0);\n" |
| << "\n"; |
| |
| if (params.numColorSamples == VK_SAMPLE_COUNT_1_BIT) |
| { |
| const deUint32 expectedMask = ((1u << static_cast<deUint32>(params.numCoverageSamples)) - 1u); |
| |
| // Expect all covered samples to be lit, the rest is zero |
| src << " if (gl_SampleMaskIn[0] == " << expectedMask << ")\n" |
| << " col.g = 1.0;\n" |
| << " else\n" |
| << " col.r = 1.0;\n"; |
| } |
| else |
| { |
| // Expect only a matching sample to be lit |
| src << " if (gl_SampleMaskIn[0] == (1 << gl_SampleID))\n" |
| << " col.g = 1.0;\n" |
| << " else\n" |
| << " col.r = 1.0;\n" |
| << "\n" |
| << " if (gl_SampleID >= " << static_cast<deUint32>(params.numColorSamples) << ") // number of color samples, should not happen\n" |
| << " col.b = 1.0;\n"; |
| } |
| |
| src << "\n" |
| << " o_color = col;\n" |
| << "}\n"; |
| |
| programCollection.glslSources.add("frag") << glu::FragmentSource(src.str()); |
| } |
| } |
| |
| //! A simple color, depth/stencil draw. Single subpass, no vertex input |
| void drawResolve (Context& context, const TestParams& params, WorkingData& wd) |
| { |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const VkDevice device = context.getDevice(); |
| const bool needResolve = (params.numColorSamples != VK_SAMPLE_COUNT_1_BIT); |
| |
| Move<VkRenderPass> renderPass; |
| Move<VkFramebuffer> framebuffer; |
| |
| // Create a render pass and a framebuffer |
| { |
| std::vector<VkImageView> attachments; |
| std::vector<VkAttachmentDescription> attachmentDescriptions; |
| |
| attachments.push_back(*wd.colorImageView); |
| attachments.push_back(*wd.depthStencilImageView); |
| |
| attachmentDescriptions.push_back(makeAttachmentDescription( |
| (VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags; |
| params.colorFormat, // VkFormat format; |
| params.numColorSamples, // VkSampleCountFlagBits samples; |
| VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp; |
| VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp; |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp; |
| VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL // VkImageLayout finalLayout; |
| )); |
| |
| attachmentDescriptions.push_back(makeAttachmentDescription( |
| (VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags; |
| params.depthStencilFormat, // VkFormat format; |
| params.numDepthStencilSamples, // VkSampleCountFlagBits samples; |
| 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; |
| )); |
| |
| if (needResolve) |
| { |
| attachments.push_back(*wd.resolveImageView); |
| |
| attachmentDescriptions.push_back(makeAttachmentDescription( |
| (VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags; |
| params.colorFormat, // VkFormat format; |
| VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; |
| 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; |
| )); |
| } |
| |
| const VkAttachmentReference colorRef = makeAttachmentReference(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); |
| const VkAttachmentReference depthStencilRef = makeAttachmentReference(1u, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); |
| const VkAttachmentReference resolveRef = makeAttachmentReference(2u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); |
| |
| const VkSubpassDescription subpassDescription = |
| { |
| (VkSubpassDescriptionFlags)0, // VkSubpassDescriptionFlags flags; |
| VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint; |
| 0u, // uint32_t inputAttachmentCount; |
| DE_NULL, // const VkAttachmentReference* pInputAttachments; |
| 1u, // uint32_t colorAttachmentCount; |
| &colorRef, // const VkAttachmentReference* pColorAttachments; |
| (needResolve ? &resolveRef : DE_NULL), // const VkAttachmentReference* pResolveAttachments; |
| &depthStencilRef, // const VkAttachmentReference* pDepthStencilAttachment; |
| 0u, // uint32_t preserveAttachmentCount; |
| DE_NULL, // const uint32_t* pPreserveAttachments; |
| }; |
| |
| // Assume there are no dependencies between subpasses |
| VkRenderPassCreateInfo renderPassInfo = |
| { |
| VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| (VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags; |
| static_cast<deUint32>(attachmentDescriptions.size()), // deUint32 attachmentCount; |
| dataOrNullPtr(attachmentDescriptions), // const VkAttachmentDescription* pAttachments; |
| 1u, // deUint32 subpassCount; |
| &subpassDescription, // const VkSubpassDescription* pSubpasses; |
| 0u, // deUint32 dependencyCount; |
| DE_NULL, // const VkSubpassDependency* pDependencies; |
| }; |
| |
| renderPass = createRenderPass(vk, device, &renderPassInfo); |
| framebuffer = makeFramebuffer (vk, device, *renderPass, static_cast<deUint32>(attachments.size()), dataOrNullPtr(attachments), wd.renderSize.x(), wd.renderSize.y()); |
| } |
| |
| const Unique<VkShaderModule> vertexModule (createShaderModule(vk, device, context.getBinaryCollection().get("vert"), 0u)); |
| const Unique<VkShaderModule> fragmentModule (createShaderModule(vk, device, context.getBinaryCollection().get("frag"), 0u)); |
| const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device)); |
| const bool useVertexInput = false; |
| const bool sampleShading = (params.numColorSamples != VK_SAMPLE_COUNT_1_BIT); |
| const deUint32 subpassNdx = 0u; |
| const Unique<VkPipeline> pipeline (makeGraphicsPipeline(vk, device, *pipelineLayout, *renderPass, *vertexModule, *fragmentModule, useVertexInput, subpassNdx, |
| wd.renderSize, getImageAspectFlags(params.depthStencilFormat), params.numCoverageSamples, sampleShading)); |
| |
| const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, context.getUniversalQueueFamilyIndex())); |
| const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer(vk, device, *cmdPool)); |
| |
| beginCommandBuffer(vk, *cmdBuffer); |
| |
| { |
| std::vector<VkClearValue> clearValues; |
| clearValues.push_back(makeClearValueColorF32(0.0f, 0.0f, 0.0f, 1.0f)); |
| clearValues.push_back(makeClearValueDepthStencil(1.0f, 0u)); |
| |
| const VkRect2D renderArea = |
| { |
| { 0u, 0u }, |
| { wd.renderSize.x(), wd.renderSize.y() } |
| }; |
| |
| const VkRenderPassBeginInfo renderPassBeginInfo = |
| { |
| VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| *renderPass, // VkRenderPass renderPass; |
| *framebuffer, // VkFramebuffer framebuffer; |
| renderArea, // VkRect2D renderArea; |
| static_cast<deUint32>(clearValues.size()), // uint32_t clearValueCount; |
| dataOrNullPtr(clearValues), // const VkClearValue* pClearValues; |
| }; |
| vk.cmdBeginRenderPass(*cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); |
| } |
| |
| vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); |
| vk.cmdDraw(*cmdBuffer, 3u, 1u, 0u, 0u); |
| |
| vk.cmdEndRenderPass(*cmdBuffer); |
| |
| if (needResolve) |
| recordCopyOutputImageToBuffer(vk, *cmdBuffer, wd.renderSize, *wd.resolveImage, *wd.colorBuffer); |
| else |
| recordCopyOutputImageToBuffer(vk, *cmdBuffer, wd.renderSize, *wd.colorImage, *wd.colorBuffer); |
| |
| VK_CHECK(vk.endCommandBuffer(*cmdBuffer)); |
| submitCommandsAndWait(vk, device, context.getUniversalQueue(), *cmdBuffer); |
| } |
| |
| void checkRequirements (Context& context, TestParams params) |
| { |
| context.requireDeviceFunctionality("VK_AMD_mixed_attachment_samples"); |
| |
| checkSampleRequirements(context, params.numColorSamples, params.numDepthStencilSamples, false /* require standard sample locations */); |
| } |
| |
| //! Verify the values of shader builtins |
| tcu::TestStatus test (Context& context, const TestParams params) |
| { |
| WorkingData wd; |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const VkDevice device = context.getDevice(); |
| MovePtr<Allocator> allocator = MovePtr<Allocator>(new SimpleAllocator(vk, device, getPhysicalDeviceMemoryProperties(context.getInstanceInterface(), context.getPhysicalDevice()))); |
| |
| wd.renderSize = UVec2(16, 16); |
| |
| // Create images and a color buffer |
| { |
| |
| const VkImageUsageFlags colorImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; |
| const VkImageUsageFlags depthStencilImageUsageFlags = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; |
| |
| checkImageRequirements (context, |
| params.colorFormat, |
| VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT, |
| colorImageUsageFlags, |
| params.numColorSamples); |
| |
| wd.colorImage = makeImage(vk, device, params.colorFormat, wd.renderSize, params.numColorSamples, colorImageUsageFlags); |
| wd.colorImageAlloc = bindImage(vk, device, *allocator, *wd.colorImage, MemoryRequirement::Any); |
| wd.colorImageView = makeImageView(vk, device, *wd.colorImage, VK_IMAGE_VIEW_TYPE_2D, params.colorFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u)); |
| |
| if (params.numColorSamples != VK_SAMPLE_COUNT_1_BIT) |
| { |
| wd.resolveImage = makeImage(vk, device, params.colorFormat, wd.renderSize, VK_SAMPLE_COUNT_1_BIT, colorImageUsageFlags); |
| wd.resolveImageAlloc = bindImage(vk, device, *allocator, *wd.resolveImage, MemoryRequirement::Any); |
| wd.resolveImageView = makeImageView(vk, device, *wd.resolveImage, VK_IMAGE_VIEW_TYPE_2D, params.colorFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u)); |
| } |
| |
| // Resolve result |
| wd.colorBufferSize = static_cast<VkDeviceSize>(tcu::getPixelSize(mapVkFormat(params.colorFormat)) * wd.renderSize.x() * wd.renderSize.y()); |
| wd.colorBuffer = makeBuffer(vk, device, wd.colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| wd.colorBufferAlloc = bindBuffer(vk, device, *allocator, *wd.colorBuffer, MemoryRequirement::HostVisible); |
| |
| deMemset(wd.colorBufferAlloc->getHostPtr(), 0, static_cast<std::size_t>(wd.colorBufferSize)); |
| flushMappedMemoryRange(vk, device, wd.colorBufferAlloc->getMemory(), wd.colorBufferAlloc->getOffset(), VK_WHOLE_SIZE); |
| |
| checkImageRequirements (context, |
| params.depthStencilFormat, |
| VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, |
| depthStencilImageUsageFlags, |
| params.numDepthStencilSamples); |
| |
| wd.depthStencilImage = makeImage(vk, device, params.depthStencilFormat, wd.renderSize, params.numDepthStencilSamples, depthStencilImageUsageFlags); |
| wd.depthStencilImageAlloc = bindImage(vk, device, *allocator, *wd.depthStencilImage, MemoryRequirement::Any); |
| wd.depthStencilImageView = makeImageView(vk, device, *wd.depthStencilImage, VK_IMAGE_VIEW_TYPE_2D, params.depthStencilFormat, makeImageSubresourceRange(getImageAspectFlags(params.depthStencilFormat), 0u, 1u, 0u, 1u)); |
| |
| if (isDepthFormat(params.depthStencilFormat)) |
| wd.depthOnlyImageView = makeImageView(vk, device, *wd.depthStencilImage, VK_IMAGE_VIEW_TYPE_2D, params.depthStencilFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u)); |
| |
| if (isStencilFormat(params.depthStencilFormat)) |
| wd.stencilOnlyImageView = makeImageView(vk, device, *wd.depthStencilImage, VK_IMAGE_VIEW_TYPE_2D, params.depthStencilFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_STENCIL_BIT, 0u, 1u, 0u, 1u)); |
| } |
| |
| // Draw, resolve, and copy to color buffer (see the fragment shader for details) |
| drawResolve(context, params, wd); |
| |
| // Verify resolved image |
| { |
| const tcu::ConstPixelBufferAccess image (tcu::ConstPixelBufferAccess(mapVkFormat(params.colorFormat), tcu::IVec3(wd.renderSize.x(), wd.renderSize.y(), 1),wd.colorBufferAlloc->getHostPtr())); |
| |
| if (compareGreenImage(context.getTestContext().getLog(), "resolve0", "Resolved test image", image)) |
| return tcu::TestStatus::pass("Pass"); |
| else |
| return tcu::TestStatus::fail("Some samples were incorrect"); |
| } |
| } |
| |
| } // ShaderBuiltins |
| |
| std::string getSampleCountGroupName(const VkSampleCountFlagBits coverageCount, |
| const VkSampleCountFlagBits colorCount, |
| const VkSampleCountFlagBits depthStencilCount) |
| { |
| std::ostringstream str; |
| str << "coverage_" << static_cast<deUint32>(coverageCount) |
| << "_color_" << static_cast<deUint32>(colorCount) |
| << "_depth_stencil_" << static_cast<deUint32>(depthStencilCount); |
| return str.str(); |
| } |
| |
| std::string getFormatShortString (const VkFormat format) |
| { |
| std::string s(de::toLower(getFormatName(format))); |
| return s.substr(10); |
| } |
| |
| std::string getFormatCaseName (const VkFormat colorFormat, |
| const VkFormat depthStencilFormat) |
| { |
| std::ostringstream str; |
| str << getFormatShortString(colorFormat) << "_" << getFormatShortString(depthStencilFormat); |
| return str.str(); |
| } |
| |
| void createMixedAttachmentSamplesTestsInGroup (tcu::TestCaseGroup* rootGroup) |
| { |
| const VkFormat colorFormatRange[] = |
| { |
| VK_FORMAT_R8G8B8A8_UNORM, |
| // If you add more, make sure it is handled in the test/shader |
| }; |
| |
| const VkFormat depthStencilFormatRange[] = |
| { |
| VK_FORMAT_D16_UNORM, |
| VK_FORMAT_X8_D24_UNORM_PACK32, |
| VK_FORMAT_D32_SFLOAT, |
| VK_FORMAT_S8_UINT, |
| VK_FORMAT_D16_UNORM_S8_UINT, |
| VK_FORMAT_D24_UNORM_S8_UINT, |
| VK_FORMAT_D32_SFLOAT_S8_UINT, |
| }; |
| |
| // Minimal set of formats to cover depth and stencil |
| const VkFormat depthStencilReducedFormatRange[] = |
| { |
| VK_FORMAT_D16_UNORM, //!< Must be supported |
| VK_FORMAT_D24_UNORM_S8_UINT, //!< Either this, or the next one must be supported |
| VK_FORMAT_D32_SFLOAT_S8_UINT, |
| }; |
| |
| struct SampleCase |
| { |
| VkSampleCountFlagBits colorSamples; |
| VkSampleCountFlagBits depthStencilSamples; |
| }; |
| |
| // Currently supported EQAA cases |
| static const SampleCase singlePassCases[] = |
| { |
| // Less color than depth/stencil |
| { VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_2_BIT }, |
| { VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_4_BIT }, |
| { VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_8_BIT }, |
| { VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_16_BIT }, |
| { VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT }, |
| { VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_8_BIT }, |
| { VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_16_BIT }, |
| { VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_8_BIT }, |
| { VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_16_BIT }, |
| { VK_SAMPLE_COUNT_8_BIT, VK_SAMPLE_COUNT_16_BIT }, |
| }; |
| |
| // Multi-subpass cases |
| |
| static const SampleCase caseSubpassIncreaseColor_1[] = |
| { |
| { VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_4_BIT }, |
| { VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT }, |
| }; |
| static const SampleCase caseSubpassIncreaseColor_2[] = |
| { |
| { VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_8_BIT }, |
| { VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_8_BIT }, |
| { VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_8_BIT }, |
| }; |
| static const SampleCase caseSubpassDecreaseColor_1[] = |
| { |
| { VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT }, |
| { VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_4_BIT }, |
| }; |
| static const SampleCase caseSubpassDecreaseColor_2[] = |
| { |
| { VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_8_BIT }, |
| { VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_8_BIT }, |
| { VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_8_BIT }, |
| }; |
| static const SampleCase caseSubpassIncreaseCoverage_1[] = |
| { |
| { VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_2_BIT }, |
| { VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT }, |
| }; |
| static const SampleCase caseSubpassIncreaseCoverage_2[] = |
| { |
| { VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_2_BIT }, |
| { VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT }, |
| { VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_8_BIT }, |
| }; |
| static const SampleCase caseSubpassDecreaseCoverage_1[] = |
| { |
| { VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT }, |
| { VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_2_BIT }, |
| }; |
| static const SampleCase caseSubpassDecreaseCoverage_2[] = |
| { |
| { VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_8_BIT }, |
| { VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT }, |
| { VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_2_BIT }, |
| }; |
| |
| static const struct |
| { |
| const char* const caseName; |
| const deUint32 numSampleCases; |
| const SampleCase* pSampleCase; |
| } subpassCases[] = |
| { |
| { "multi_subpass_decrease_color_4", DE_LENGTH_OF_ARRAY(caseSubpassDecreaseColor_1), caseSubpassDecreaseColor_1 }, |
| { "multi_subpass_decrease_color_8", DE_LENGTH_OF_ARRAY(caseSubpassDecreaseColor_2), caseSubpassDecreaseColor_2 }, |
| { "multi_subpass_decrease_coverage_4", DE_LENGTH_OF_ARRAY(caseSubpassDecreaseCoverage_1), caseSubpassDecreaseCoverage_1 }, |
| { "multi_subpass_decrease_coverage_8", DE_LENGTH_OF_ARRAY(caseSubpassDecreaseCoverage_2), caseSubpassDecreaseCoverage_2 }, |
| { "multi_subpass_increase_color_4", DE_LENGTH_OF_ARRAY(caseSubpassIncreaseColor_1), caseSubpassIncreaseColor_1 }, |
| { "multi_subpass_increase_color_8", DE_LENGTH_OF_ARRAY(caseSubpassIncreaseColor_2), caseSubpassIncreaseColor_2 }, |
| { "multi_subpass_increase_coverage_4", DE_LENGTH_OF_ARRAY(caseSubpassIncreaseCoverage_1), caseSubpassIncreaseCoverage_1 }, |
| { "multi_subpass_increase_coverage_8", DE_LENGTH_OF_ARRAY(caseSubpassIncreaseCoverage_2), caseSubpassIncreaseCoverage_2 }, |
| }; |
| |
| // Test 1: Per-sample expected value check |
| { |
| MovePtr<tcu::TestCaseGroup> standardLocationsGroup (new tcu::TestCaseGroup(rootGroup->getTestContext(), "verify_standard_locations", "")); |
| MovePtr<tcu::TestCaseGroup> programmableLocationsGroup (new tcu::TestCaseGroup(rootGroup->getTestContext(), "verify_programmable_locations", "")); |
| |
| tcu::TestCaseGroup* locationsGroups[2] = |
| { |
| standardLocationsGroup.get(), |
| programmableLocationsGroup.get() |
| }; |
| |
| for (deUint32 groupNdx = 0u; groupNdx < DE_LENGTH_OF_ARRAY(locationsGroups); ++groupNdx) |
| { |
| // Single subpass cases |
| for (deUint32 caseNdx = 0u; caseNdx < DE_LENGTH_OF_ARRAY(singlePassCases); ++caseNdx) |
| { |
| VerifySamples::TestParams::SampleCount samples; |
| samples.numColorSamples = singlePassCases[caseNdx].colorSamples; |
| samples.numDepthStencilSamples = singlePassCases[caseNdx].depthStencilSamples; |
| samples.numCoverageSamples = de::max(samples.numColorSamples, samples.numDepthStencilSamples); |
| |
| VerifySamples::TestParams params; |
| params.perSubpassSamples.push_back(samples); |
| params.useProgrammableSampleLocations = (locationsGroups[groupNdx] == programmableLocationsGroup.get()); |
| |
| MovePtr<tcu::TestCaseGroup> sampleCaseGroup(new tcu::TestCaseGroup( |
| rootGroup->getTestContext(), getSampleCountGroupName(samples.numCoverageSamples, samples.numColorSamples, samples.numDepthStencilSamples).c_str(), "")); |
| |
| for (const VkFormat *pDepthStencilFormat = depthStencilFormatRange; pDepthStencilFormat != DE_ARRAY_END(depthStencilFormatRange); ++pDepthStencilFormat) |
| for (const VkFormat *pColorFormat = colorFormatRange; pColorFormat != DE_ARRAY_END(colorFormatRange); ++pColorFormat) |
| { |
| params.colorFormat = *pColorFormat; |
| params.depthStencilFormat = *pDepthStencilFormat; |
| |
| addFunctionCaseWithPrograms( |
| sampleCaseGroup.get(), |
| getFormatCaseName(params.colorFormat, params.depthStencilFormat).c_str(), |
| "", |
| VerifySamples::checkRequirements, |
| VerifySamples::initPrograms, |
| VerifySamples::test, params); |
| } |
| |
| locationsGroups[groupNdx]->addChild(sampleCaseGroup.release()); |
| } |
| |
| // Multi subpass cases |
| for (deUint32 caseNdx = 0u; caseNdx < DE_LENGTH_OF_ARRAY(subpassCases); ++caseNdx) |
| { |
| VerifySamples::TestParams params; |
| params.useProgrammableSampleLocations = (locationsGroups[groupNdx] == programmableLocationsGroup.get()); |
| |
| for (deUint32 subpassNdx = 0; subpassNdx < subpassCases[caseNdx].numSampleCases; ++subpassNdx) |
| { |
| VerifySamples::TestParams::SampleCount samples; |
| samples.numColorSamples = subpassCases[caseNdx].pSampleCase[subpassNdx].colorSamples; |
| samples.numDepthStencilSamples = subpassCases[caseNdx].pSampleCase[subpassNdx].depthStencilSamples; |
| samples.numCoverageSamples = de::max(samples.numColorSamples, samples.numDepthStencilSamples); |
| params.perSubpassSamples.push_back(samples); |
| } |
| |
| MovePtr<tcu::TestCaseGroup> sampleCaseGroup(new tcu::TestCaseGroup(rootGroup->getTestContext(), subpassCases[caseNdx].caseName, "")); |
| |
| for (const VkFormat *pDepthStencilFormat = depthStencilReducedFormatRange; pDepthStencilFormat != DE_ARRAY_END(depthStencilReducedFormatRange); ++pDepthStencilFormat) |
| for (const VkFormat *pColorFormat = colorFormatRange; pColorFormat != DE_ARRAY_END(colorFormatRange); ++pColorFormat) |
| { |
| params.colorFormat = *pColorFormat; |
| params.depthStencilFormat = *pDepthStencilFormat; |
| |
| addFunctionCaseWithPrograms( |
| sampleCaseGroup.get(), |
| getFormatCaseName(params.colorFormat, params.depthStencilFormat).c_str(), |
| "", |
| VerifySamples::checkRequirements, |
| VerifySamples::initPrograms, |
| VerifySamples::test, params); |
| } |
| |
| locationsGroups[groupNdx]->addChild(sampleCaseGroup.release()); |
| } |
| } |
| |
| rootGroup->addChild(standardLocationsGroup.release()); |
| rootGroup->addChild(programmableLocationsGroup.release()); |
| } |
| |
| // Test 2: Shader built-ins check |
| { |
| MovePtr<tcu::TestCaseGroup> builtinsGroup (new tcu::TestCaseGroup(rootGroup->getTestContext(), "shader_builtins", "")); |
| |
| for (deUint32 caseNdx = 0u; caseNdx < DE_LENGTH_OF_ARRAY(singlePassCases); ++caseNdx) |
| { |
| ShaderBuiltins::TestParams params; |
| params.numColorSamples = singlePassCases[caseNdx].colorSamples; |
| params.numDepthStencilSamples = singlePassCases[caseNdx].depthStencilSamples; |
| params.numCoverageSamples = de::max(params.numColorSamples, params.numDepthStencilSamples); |
| |
| MovePtr<tcu::TestCaseGroup> sampleCaseGroup(new tcu::TestCaseGroup( |
| rootGroup->getTestContext(), getSampleCountGroupName(params.numCoverageSamples, params.numColorSamples, params.numDepthStencilSamples).c_str(), "")); |
| |
| for (const VkFormat *pDepthStencilFormat = depthStencilReducedFormatRange; pDepthStencilFormat != DE_ARRAY_END(depthStencilReducedFormatRange); ++pDepthStencilFormat) |
| for (const VkFormat *pColorFormat = colorFormatRange; pColorFormat != DE_ARRAY_END(colorFormatRange); ++pColorFormat) |
| { |
| params.colorFormat = *pColorFormat; |
| params.depthStencilFormat = *pDepthStencilFormat; |
| |
| addFunctionCaseWithPrograms( |
| sampleCaseGroup.get(), |
| getFormatCaseName(params.colorFormat, params.depthStencilFormat).c_str(), |
| "", |
| ShaderBuiltins::checkRequirements, |
| ShaderBuiltins::initPrograms, |
| ShaderBuiltins::test, |
| params); |
| } |
| |
| builtinsGroup->addChild(sampleCaseGroup.release()); |
| } |
| |
| rootGroup->addChild(builtinsGroup.release()); |
| } |
| } |
| |
| } // anonymous ns |
| |
| tcu::TestCaseGroup* createMultisampleMixedAttachmentSamplesTests (tcu::TestContext& testCtx) |
| { |
| return createTestGroup(testCtx, "mixed_attachment_samples", "Test a graphics pipeline with varying sample count per color and depth/stencil attachments", createMixedAttachmentSamplesTestsInGroup); |
| } |
| |
| } // pipeline |
| } // vkt |