/*------------------------------------------------------------------------- * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2017 Google 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 render pass multisample resolve *//*--------------------------------------------------------------------*/ #include "vktRenderPassMultisampleResolveTests.hpp" #include "vktRenderPassTestsUtil.hpp" #include "vktTestCaseUtil.hpp" #include "vktTestGroupUtil.hpp" #include "vkDefs.hpp" #include "vkDeviceUtil.hpp" #include "vkImageUtil.hpp" #include "vkMemUtil.hpp" #include "vkPlatform.hpp" #include "vkPrograms.hpp" #include "vkQueryUtil.hpp" #include "vkRef.hpp" #include "vkRefUtil.hpp" #include "vkTypeUtil.hpp" #include "vkCmdUtil.hpp" #include "vkObjUtil.hpp" #include "tcuFloat.hpp" #include "tcuImageCompare.hpp" #include "tcuFormatUtil.hpp" #include "tcuMaybe.hpp" #include "tcuResultCollector.hpp" #include "tcuTestLog.hpp" #include "tcuTextureUtil.hpp" #include "tcuVectorUtil.hpp" #include "tcuStringTemplate.hpp" #include "deUniquePtr.hpp" #include "deSharedPtr.hpp" using namespace vk; using tcu::BVec4; using tcu::IVec2; using tcu::IVec4; using tcu::UVec2; using tcu::UVec4; using tcu::Vec2; using tcu::Vec3; using tcu::Vec4; using tcu::ConstPixelBufferAccess; using tcu::PixelBufferAccess; using tcu::TestLog; using std::vector; typedef de::SharedPtr AllocationSp; typedef de::SharedPtr > VkImageSp; typedef de::SharedPtr > VkImageViewSp; typedef de::SharedPtr > VkBufferSp; typedef de::SharedPtr > VkSamplerSp; typedef de::SharedPtr > VkPipelineSp; typedef de::SharedPtr > VkDescriptorSetLayoutSp; typedef de::SharedPtr > VkDescriptorPoolSp; typedef de::SharedPtr > VkDescriptorSetSp; namespace vkt { namespace { using namespace renderpass; template de::SharedPtr safeSharedPtr (T* ptr) { try { return de::SharedPtr(ptr); } catch (...) { delete ptr; throw; } } enum TestType { RESOLVE = 0, MAX_ATTACHMENTS, COMPATIBILITY }; struct TestConfig { TestType testType; VkFormat format; deUint32 sampleCount; deUint32 layerCount; deUint32 attachmentCount; deUint32 width; deUint32 height; RenderingType renderingType; }; struct TestConfig2 : TestConfig { TestConfig2(const TestConfig& src, deUint32 level) : TestConfig (src) , resolveLevel (level) { } deUint32 resolveLevel; }; // Render pass traits that groups render pass related types together and by that help // to reduce number of template parrameters passed to number of functions in those tests struct RenderPass1Trait { typedef AttachmentDescription1 AttDesc; typedef AttachmentReference1 AttRef; typedef SubpassDescription1 SubpassDesc; typedef SubpassDependency1 SubpassDep; typedef RenderPassCreateInfo1 RenderPassCreateInfo; }; struct RenderPass2Trait { typedef AttachmentDescription2 AttDesc; typedef AttachmentReference2 AttRef; typedef SubpassDescription2 SubpassDesc; typedef SubpassDependency2 SubpassDep; typedef RenderPassCreateInfo2 RenderPassCreateInfo; }; class MultisampleRenderPassTestBase : public TestInstance { public: MultisampleRenderPassTestBase (Context& context, TestConfig config); ~MultisampleRenderPassTestBase (void); protected: Move createImage (VkSampleCountFlagBits sampleCountBit, VkImageUsageFlags usage) const; Move createImage (VkSampleCountFlagBits sampleCountBit, VkImageUsageFlags usage, deUint32 width, deUint32 height, deUint32 mipLevels) const; vector createImages (VkSampleCountFlagBits sampleCountBit, VkImageUsageFlags usage) const; vector createImages (VkSampleCountFlagBits sampleCountBit, VkImageUsageFlags usage, deUint32 width, deUint32 height, deUint32 mipLevels) const; vector createImageMemory (const vector& images) const; vector createImageViews (const vector& images, deUint32 mipLevel = 0) const; vector createBuffers () const; vector createBuffers (deUint32 width, deUint32 height, deUint32 mipLevels) const; vector createBufferMemory (const vector& buffers) const; Move createFramebuffer (const std::vector multisampleImageViews, const std::vector singlesampleImageViews, VkRenderPass renderPass) const; void clearAttachments (VkCommandBuffer commandBuffer) const; VkDeviceSize getPixelSize () const; tcu::Vec4 getFormatThreshold () const; VkSampleCountFlagBits sampleCountBitFromSampleCount (deUint32 count) const; void logImage (const std::string& name, const tcu::ConstPixelBufferAccess& image) const; protected: const bool m_testCompatibility; const RenderingType m_renderingType; const VkFormat m_format; const VkSampleCountFlagBits m_sampleCount; const deUint32 m_layerCount; const deUint32 m_attachmentsCount; const deUint32 m_width; const deUint32 m_height; }; MultisampleRenderPassTestBase::MultisampleRenderPassTestBase (Context& context, TestConfig config) : TestInstance (context) , m_testCompatibility (config.testType == COMPATIBILITY) , m_renderingType (config.renderingType) , m_format (config.format) , m_sampleCount (sampleCountBitFromSampleCount(config.sampleCount)) , m_layerCount (config.layerCount) , m_attachmentsCount (config.attachmentCount) , m_width (config.width) , m_height (config.height) { } MultisampleRenderPassTestBase::~MultisampleRenderPassTestBase () { } Move MultisampleRenderPassTestBase::createImage (VkSampleCountFlagBits sampleCountBit, VkImageUsageFlags usage) const { return createImage(sampleCountBit, usage, m_width, m_height, 1u); } Move MultisampleRenderPassTestBase::createImage (VkSampleCountFlagBits sampleCountBit, VkImageUsageFlags usage, deUint32 width, deUint32 height, deUint32 mipLevels) const { const InstanceInterface& vki = m_context.getInstanceInterface(); const DeviceInterface& vkd = m_context.getDeviceInterface(); VkDevice device = m_context.getDevice(); VkPhysicalDevice physicalDevice = m_context.getPhysicalDevice(); const tcu::TextureFormat format (mapVkFormat(m_format)); const VkImageType imageType (VK_IMAGE_TYPE_2D); const VkImageTiling imageTiling (VK_IMAGE_TILING_OPTIMAL); const VkFormatProperties formatProperties (getPhysicalDeviceFormatProperties(vki, physicalDevice, m_format)); const VkExtent3D imageExtent = { width, height, 1u }; try { const VkImageFormatProperties imageFormatProperties(getPhysicalDeviceImageFormatProperties(vki, physicalDevice, m_format, imageType, imageTiling, usage, 0u)); if ((tcu::hasDepthComponent(format.order) || tcu::hasStencilComponent(format.order)) && (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) == 0) TCU_THROW(NotSupportedError, "Format can't be used as depth stencil attachment"); if (!(tcu::hasDepthComponent(format.order) || tcu::hasStencilComponent(format.order)) && (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) == 0) TCU_THROW(NotSupportedError, "Format can't be used as color attachment"); if (imageFormatProperties.maxExtent.width < imageExtent.width || imageFormatProperties.maxExtent.height < imageExtent.height || ((imageFormatProperties.sampleCounts & m_sampleCount) == 0) || imageFormatProperties.maxArrayLayers < m_layerCount) { TCU_THROW(NotSupportedError, "Image type not supported"); } const VkImageCreateInfo pCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, DE_NULL, 0u, imageType, m_format, imageExtent, mipLevels, m_layerCount, sampleCountBit, imageTiling, usage, VK_SHARING_MODE_EXCLUSIVE, 0u, DE_NULL, VK_IMAGE_LAYOUT_UNDEFINED }; return ::createImage(vkd, device, &pCreateInfo); } catch (const vk::Error& error) { if (error.getError() == VK_ERROR_FORMAT_NOT_SUPPORTED) TCU_THROW(NotSupportedError, "Image format not supported"); throw; } } vector MultisampleRenderPassTestBase::createImages (VkSampleCountFlagBits sampleCountBit, VkImageUsageFlags usage) const { std::vector images (m_attachmentsCount); for (size_t imageNdx = 0; imageNdx < m_attachmentsCount; imageNdx++) images[imageNdx] = safeSharedPtr(new Unique(createImage(sampleCountBit, usage))); return images; } vector MultisampleRenderPassTestBase::createImages (VkSampleCountFlagBits sampleCountBit, VkImageUsageFlags usage, deUint32 width, deUint32 height, deUint32 mipLevels) const { std::vector images (m_attachmentsCount); for (size_t imageNdx = 0; imageNdx < m_attachmentsCount; imageNdx++) images[imageNdx] = safeSharedPtr(new Unique(createImage(sampleCountBit, usage, width, height, mipLevels))); return images; } vector MultisampleRenderPassTestBase::createImageMemory (const vector& images) const { const DeviceInterface& vkd = m_context.getDeviceInterface(); VkDevice device = m_context.getDevice(); Allocator& allocator = m_context.getDefaultAllocator(); std::vector memory (images.size()); for (size_t memoryNdx = 0; memoryNdx < memory.size(); memoryNdx++) { VkImage image = **images[memoryNdx]; VkMemoryRequirements requirements = getImageMemoryRequirements(vkd, device, image); de::MovePtr allocation (allocator.allocate(requirements, MemoryRequirement::Any)); VK_CHECK(vkd.bindImageMemory(device, image, allocation->getMemory(), allocation->getOffset())); memory[memoryNdx] = safeSharedPtr(allocation.release()); } return memory; } vector MultisampleRenderPassTestBase::createImageViews (const vector& images, deUint32 mipLevel) const { const DeviceInterface& vkd = m_context.getDeviceInterface(); VkDevice device = m_context.getDevice(); std::vector views (images.size()); const VkImageSubresourceRange range = { VK_IMAGE_ASPECT_COLOR_BIT, mipLevel, 1u, 0u, m_layerCount }; for (size_t imageNdx = 0; imageNdx < images.size(); imageNdx++) { const VkImageViewCreateInfo pCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, DE_NULL, 0u, **images[imageNdx], VK_IMAGE_VIEW_TYPE_2D_ARRAY, m_format, makeComponentMappingRGBA(), range, }; views[imageNdx] = safeSharedPtr(new Unique(createImageView(vkd, device, &pCreateInfo))); } return views; } vector MultisampleRenderPassTestBase::createBuffers () const { return createBuffers(m_width, m_height, 1u); } vector MultisampleRenderPassTestBase::createBuffers (deUint32 width, deUint32 height, deUint32 mipLevels) const { DE_ASSERT(mipLevels); VkDeviceSize size = 0; for (deUint32 level = 0; level < mipLevels; ++level) { DE_ASSERT(width && height); size += (width * height); height /= 2; width /=2; } const DeviceInterface& vkd = m_context.getDeviceInterface(); VkDevice device = m_context.getDevice(); std::vector buffers (m_attachmentsCount); const VkDeviceSize pixelSize (getPixelSize()); const VkBufferCreateInfo createInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, DE_NULL, 0u, size * m_layerCount * pixelSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_SHARING_MODE_EXCLUSIVE, 0u, DE_NULL }; for (size_t bufferNdx = 0; bufferNdx < buffers.size(); bufferNdx++) buffers[bufferNdx] = safeSharedPtr(new Unique(createBuffer(vkd, device, &createInfo))); return buffers; } vector MultisampleRenderPassTestBase::createBufferMemory (const vector& buffers) const { const DeviceInterface& vkd = m_context.getDeviceInterface(); VkDevice device = m_context.getDevice(); Allocator& allocator = m_context.getDefaultAllocator(); std::vector > memory (buffers.size()); for (size_t memoryNdx = 0; memoryNdx < memory.size(); memoryNdx++) { VkBuffer buffer = **buffers[memoryNdx]; VkMemoryRequirements requirements = getBufferMemoryRequirements(vkd, device, buffer); de::MovePtr allocation (allocator.allocate(requirements, MemoryRequirement::HostVisible)); VK_CHECK(vkd.bindBufferMemory(device, buffer, allocation->getMemory(), allocation->getOffset())); memory[memoryNdx] = safeSharedPtr(allocation.release()); } return memory; } Move MultisampleRenderPassTestBase::createFramebuffer (const std::vector multisampleImageViews, const std::vector singlesampleImageViews, VkRenderPass renderPass) const { // when RenderPass was not created then we are testing dynamic rendering // and we can't create framebuffer without valid RenderPass object if (!renderPass) return Move(); const DeviceInterface& vkd = m_context.getDeviceInterface(); VkDevice device = m_context.getDevice(); std::vector attachments; attachments.reserve(multisampleImageViews.size() + singlesampleImageViews.size()); DE_ASSERT(multisampleImageViews.size() == singlesampleImageViews.size()); for (size_t ndx = 0; ndx < multisampleImageViews.size(); ndx++) { attachments.push_back(**multisampleImageViews[ndx]); attachments.push_back(**singlesampleImageViews[ndx]); } const VkFramebufferCreateInfo createInfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, DE_NULL, 0u, renderPass, (deUint32)attachments.size(), &attachments[0], m_width, m_height, m_layerCount }; return ::createFramebuffer(vkd, device, &createInfo); } void MultisampleRenderPassTestBase::clearAttachments (VkCommandBuffer commandBuffer) const { const DeviceInterface& vkd = m_context.getDeviceInterface(); const tcu::TextureFormat format (mapVkFormat(m_format)); const tcu::TextureChannelClass channelClass (tcu::getTextureChannelClass(format.type)); VkClearValue value; // Clear everything to black switch (channelClass) { case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: value = makeClearValueColorF32(-1.0f, -1.0f, -1.0f, -1.0f); break; case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: value = makeClearValueColorF32(0.0f, 0.0f, 0.0f, 0.0f); break; case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: value = makeClearValueColorF32(-1.0f, -1.0f, -1.0f, -1.0f); break; case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: value = makeClearValueColorI32(-128, -128, -128, -128); break; case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: value = makeClearValueColorU32(0u, 0u, 0u, 0u); break; default: DE_FATAL("Unknown channel class"); } std::vector colors(m_attachmentsCount); for (deUint32 attachmentNdx = 0; attachmentNdx < m_attachmentsCount; attachmentNdx++) { colors[attachmentNdx].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; colors[attachmentNdx].colorAttachment = attachmentNdx; colors[attachmentNdx].clearValue = value; } const VkClearRect rect = { { { 0u, 0u }, { m_width, m_height } }, 0u, m_layerCount, }; vkd.cmdClearAttachments(commandBuffer, deUint32(colors.size()), &colors[0], 1u, &rect); } VkDeviceSize MultisampleRenderPassTestBase::getPixelSize () const { const tcu::TextureFormat format(mapVkFormat(m_format)); return format.getPixelSize(); } tcu::Vec4 MultisampleRenderPassTestBase::getFormatThreshold () const { const tcu::TextureFormat tcuFormat (mapVkFormat(m_format)); const deUint32 componentCount (tcu::getNumUsedChannels(tcuFormat.order)); if (isSnormFormat(m_format)) { return Vec4((componentCount >= 1) ? 1.5f * getRepresentableDiffSnorm(m_format, 0) : 0.0f, (componentCount >= 2) ? 1.5f * getRepresentableDiffSnorm(m_format, 1) : 0.0f, (componentCount >= 3) ? 1.5f * getRepresentableDiffSnorm(m_format, 2) : 0.0f, (componentCount == 4) ? 1.5f * getRepresentableDiffSnorm(m_format, 3) : 0.0f); } else if (isUnormFormat(m_format)) { return Vec4((componentCount >= 1) ? 1.5f * getRepresentableDiffUnorm(m_format, 0) : 0.0f, (componentCount >= 2) ? 1.5f * getRepresentableDiffUnorm(m_format, 1) : 0.0f, (componentCount >= 3) ? 1.5f * getRepresentableDiffUnorm(m_format, 2) : 0.0f, (componentCount == 4) ? 1.5f * getRepresentableDiffUnorm(m_format, 3) : 0.0f); } else if (isFloatFormat(m_format)) { return (tcuFormat.type == tcu::TextureFormat::HALF_FLOAT) ? tcu::Vec4(0.005f) : Vec4(0.00001f); } else return Vec4(0.001f); } VkSampleCountFlagBits MultisampleRenderPassTestBase::sampleCountBitFromSampleCount (deUint32 count) const { switch (count) { case 1: return VK_SAMPLE_COUNT_1_BIT; case 2: return VK_SAMPLE_COUNT_2_BIT; case 4: return VK_SAMPLE_COUNT_4_BIT; case 8: return VK_SAMPLE_COUNT_8_BIT; case 16: return VK_SAMPLE_COUNT_16_BIT; case 32: return VK_SAMPLE_COUNT_32_BIT; case 64: return VK_SAMPLE_COUNT_64_BIT; default: DE_FATAL("Invalid sample count"); return (VkSampleCountFlagBits)0x0; } } void MultisampleRenderPassTestBase::logImage (const std::string& name, const tcu::ConstPixelBufferAccess& image) const { m_context.getTestContext().getLog() << tcu::LogImage(name.c_str(), name.c_str(), image); for (deUint32 layerNdx = 0; layerNdx < m_layerCount; ++layerNdx) { const std::string layerName (name + " Layer:" + de::toString(layerNdx)); tcu::ConstPixelBufferAccess layerImage (image.getFormat(), m_width, m_height, 1, image.getPixelPtr(0, 0, layerNdx)); m_context.getTestContext().getLog() << tcu::LogImage(layerName.c_str(), layerName.c_str(), layerImage); } } class MultisampleRenderPassTestInstance : public MultisampleRenderPassTestBase { public: MultisampleRenderPassTestInstance (Context& context, TestConfig config); ~MultisampleRenderPassTestInstance (void); tcu::TestStatus iterate (void); private: template void submit (void); void submitSwitch (RenderingType renderingType); void verify (void); template Move createRenderPass (bool usedResolveAttachment); Move createRenderPassSwitch (bool usedResolveAttachment); Move createRenderPassCompatible (void); Move createRenderPipelineLayout (void); Move createRenderPipeline (void); private: const std::vector m_multisampleImages; const std::vector m_multisampleImageMemory; const std::vector m_multisampleImageViews; const std::vector m_singlesampleImages; const std::vector m_singlesampleImageMemory; const std::vector m_singlesampleImageViews; const Unique m_renderPass; const Unique m_renderPassCompatible; const Unique m_framebuffer; const Unique m_renderPipelineLayout; const Unique m_renderPipeline; const std::vector m_buffers; const std::vector m_bufferMemory; const Unique m_commandPool; tcu::TextureLevel m_sum; tcu::TextureLevel m_sumSrgb; deUint32 m_sampleMask; tcu::ResultCollector m_resultCollector; protected: MultisampleRenderPassTestInstance (Context& context, TestConfig config, deUint32 renderLevel); const deUint32 m_renderLevel; }; MultisampleRenderPassTestInstance::MultisampleRenderPassTestInstance (Context& context, TestConfig config) : MultisampleRenderPassTestInstance (context, config, /*defaulf render level*/0u) { } MultisampleRenderPassTestInstance::MultisampleRenderPassTestInstance (Context& context, TestConfig config, deUint32 renderLevel) : MultisampleRenderPassTestBase(context, config) , m_multisampleImages (createImages(m_sampleCount, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) , m_multisampleImageMemory (createImageMemory(m_multisampleImages)) , m_multisampleImageViews (createImageViews(m_multisampleImages)) , m_singlesampleImages (createImages(VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, (1u << renderLevel)*m_width, (1u << renderLevel)*m_height, renderLevel+1 )) , m_singlesampleImageMemory (createImageMemory(m_singlesampleImages)) , m_singlesampleImageViews (createImageViews(m_singlesampleImages, renderLevel)) // The "normal" render pass has an unused resolve attachment when testing compatibility. , m_renderPass (createRenderPassSwitch(!m_testCompatibility)) , m_renderPassCompatible (createRenderPassCompatible()) , m_framebuffer (createFramebuffer(m_multisampleImageViews, m_singlesampleImageViews, *m_renderPass)) , m_renderPipelineLayout (createRenderPipelineLayout()) , m_renderPipeline (createRenderPipeline()) , m_buffers (createBuffers((1u << renderLevel)*m_width, (1u << renderLevel)*m_height, renderLevel+1 )) , m_bufferMemory (createBufferMemory(m_buffers)) , m_commandPool (createCommandPool(context.getDeviceInterface(), context.getDevice(), VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, context.getUniversalQueueFamilyIndex())) , m_sum (tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT), m_width, m_height, m_layerCount) , m_sumSrgb (tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::FLOAT), m_width, m_height, m_layerCount) , m_sampleMask (0x0u) , m_renderLevel (renderLevel) { tcu::clear(m_sum.getAccess(), Vec4(0.0f, 0.0f, 0.0f, 0.0f)); tcu::clear(m_sumSrgb.getAccess(), Vec4(0.0f, 0.0f, 0.0f, 0.0f)); } MultisampleRenderPassTestInstance::~MultisampleRenderPassTestInstance (void) { } template void MultisampleRenderPassTestInstance::submit (void) { const DeviceInterface& vkd (m_context.getDeviceInterface()); const VkDevice device (m_context.getDevice()); const Unique commandBuffer (allocateCommandBuffer(vkd, device, *m_commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)); beginCommandBuffer(vkd, *commandBuffer); // Memory barriers between previous copies and rendering { std::vector barriers; for (size_t dstNdx = 0; dstNdx < m_singlesampleImages.size(); dstNdx++) { const VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, DE_NULL, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, **m_singlesampleImages[dstNdx], { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_layerCount } }; barriers.push_back(barrier); } vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, (deUint32)barriers.size(), &barriers[0]); } // Memory barriers to set multisample image layout to COLOR_ATTACHMENT_OPTIMAL if (m_renderingType == RENDERING_TYPE_DYNAMIC_RENDERING) { std::vector barriers; for (size_t dstNdx = 0; dstNdx < m_multisampleImages.size(); dstNdx++) { const VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, DE_NULL, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, **m_multisampleImages[dstNdx], { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_layerCount } }; barriers.push_back(barrier); } vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, (deUint32)barriers.size(), &barriers[0]); } VkRect2D renderArea = makeRect2D(m_width, m_height); if (m_renderingType == RENDERING_TYPE_DYNAMIC_RENDERING) { const VkClearValue clearValue = makeClearValueColor( { 0.0f, 0.0f, 0.0f, 1.0f } ); std::vector colorAttachments(m_attachmentsCount, { vk::VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR, // VkStructureType sType; DE_NULL, // const void* pNext; DE_NULL, // VkImageView imageView; vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout imageLayout; vk::VK_RESOLVE_MODE_AVERAGE_BIT, // VkResolveModeFlagBits resolveMode; DE_NULL, // VkImageView resolveImageView; vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout resolveImageLayout; vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp loadOp; vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp storeOp; clearValue // VkClearValue clearValue; }); for (deUint32 i = 0; i < m_attachmentsCount; ++i) { colorAttachments[i].imageView = **m_multisampleImageViews[i]; colorAttachments[i].resolveImageView = **m_singlesampleImageViews[i]; } vk::VkRenderingInfoKHR renderingInfo { vk::VK_STRUCTURE_TYPE_RENDERING_INFO_KHR, DE_NULL, 0, // VkRenderingFlagsKHR flags; renderArea, // VkRect2D renderArea; m_layerCount, // deUint32 layerCount; 0u, // deUint32 viewMask; m_attachmentsCount, // deUint32 colorAttachmentCount; colorAttachments.data(), // const VkRenderingAttachmentInfoKHR* pColorAttachments; DE_NULL, // const VkRenderingAttachmentInfoKHR* pDepthAttachment; DE_NULL, // const VkRenderingAttachmentInfoKHR* pStencilAttachment; }; vkd.cmdBeginRenderingKHR(*commandBuffer, &renderingInfo); } else { const typename RenderpassSubpass::SubpassBeginInfo subpassBeginInfo(DE_NULL, VK_SUBPASS_CONTENTS_INLINE); const VkRenderPassBeginInfo beginInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, DE_NULL, m_testCompatibility ? *m_renderPassCompatible : *m_renderPass, *m_framebuffer, renderArea, 0u, DE_NULL }; RenderpassSubpass::cmdBeginRenderPass(vkd, *commandBuffer, &beginInfo, &subpassBeginInfo); } // Clear everything to black clearAttachments(*commandBuffer); // Render black samples { vkd.cmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_renderPipeline); vkd.cmdPushConstants(*commandBuffer, *m_renderPipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0u, sizeof(m_sampleMask), &m_sampleMask); vkd.cmdDraw(*commandBuffer, 6u, 1u, 0u, 0u); } if (m_renderingType == RENDERING_TYPE_DYNAMIC_RENDERING) vkd.cmdEndRenderingKHR(*commandBuffer); else { const typename RenderpassSubpass::SubpassEndInfo subpassEndInfo(DE_NULL); RenderpassSubpass::cmdEndRenderPass(vkd, *commandBuffer, &subpassEndInfo); } // Memory barriers to set single-sample image layout to TRANSFER_SRC_OPTIMAL if (m_renderingType == RENDERING_TYPE_DYNAMIC_RENDERING) { std::vector barriers; for (size_t dstNdx = 0; dstNdx < m_singlesampleImages.size(); dstNdx++) { const VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, DE_NULL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, **m_singlesampleImages[dstNdx], { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_layerCount } }; barriers.push_back(barrier); } vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, (deUint32)barriers.size(), &barriers[0]); } for (size_t dstNdx = 0; dstNdx < m_singlesampleImages.size(); dstNdx++) { // assume that buffer(s) have enough memory to store desired amount of mipmaps copyImageToBuffer(vkd, *commandBuffer, **m_singlesampleImages[dstNdx], **m_buffers[dstNdx], m_format, tcu::IVec2((1u << m_renderLevel)*m_width, (1u << m_renderLevel)*m_height), m_renderLevel, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_layerCount); } endCommandBuffer(vkd, *commandBuffer); submitCommandsAndWait(vkd, device, m_context.getUniversalQueue(), *commandBuffer); for (size_t memoryBufferNdx = 0; memoryBufferNdx < m_bufferMemory.size(); memoryBufferNdx++) invalidateMappedMemoryRange(vkd, device, m_bufferMemory[memoryBufferNdx]->getMemory(), 0u, VK_WHOLE_SIZE); } void MultisampleRenderPassTestInstance::submitSwitch (RenderingType renderingType) { switch (renderingType) { case RENDERING_TYPE_RENDERPASS_LEGACY: submit(); break; case RENDERING_TYPE_RENDERPASS2: case RENDERING_TYPE_DYNAMIC_RENDERING: submit(); break; default: TCU_THROW(InternalError, "Impossible"); } } void MultisampleRenderPassTestInstance::verify (void) { const Vec4 errorColor (1.0f, 0.0f, 0.0f, 1.0f); const Vec4 okColor (0.0f, 1.0f, 0.0f, 1.0f); const tcu::TextureFormat format (mapVkFormat(m_format)); const tcu::TextureChannelClass channelClass (tcu::getTextureChannelClass(format.type)); deUint32 offset (0u); deUint32 width ((1u << m_renderLevel) * m_width); deUint32 height ((1u << m_renderLevel) * m_height); deUint32 pixelSize (static_cast(getPixelSize())); for (deUint32 level = 0; level < m_renderLevel; ++level) { offset += (width * height * pixelSize); height /= 2; width /= 2; } std::vector accesses; for (deUint32 attachmentIdx = 0; attachmentIdx < m_attachmentsCount; ++attachmentIdx) { void* const ptr = static_cast(m_bufferMemory[attachmentIdx]->getHostPtr()) + offset; accesses.push_back(tcu::ConstPixelBufferAccess(format, m_width, m_height, m_layerCount, ptr)); } tcu::TextureLevel errorMask (tcu::TextureFormat(tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), m_width, m_height, m_layerCount); tcu::TestLog& log (m_context.getTestContext().getLog()); switch (channelClass) { case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: { const int componentCount (tcu::getNumUsedChannels(format.order)); bool isOk = true; float clearValue; float renderValue; switch (channelClass) { case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: clearValue = -1.0f; renderValue = 1.0f; break; case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: clearValue = 0.0f; renderValue = 1.0f; break; default: clearValue = 0.0f; renderValue = 0.0f; DE_FATAL("Unknown channel class"); } for (deUint32 z = 0; z < m_layerCount; z++) for (deUint32 y = 0; y < m_height; y++) for (deUint32 x = 0; x < m_width; x++) { // Color has to be black if no samples were covered, white if all samples were covered or same in every attachment const Vec4 firstColor (accesses[0].getPixel(x, y, z)); const Vec4 refColor (m_sampleMask == 0x0u ? Vec4(clearValue, componentCount > 1 ? clearValue : 0.0f, componentCount > 2 ? clearValue : 0.0f, componentCount > 3 ? clearValue : 1.0f) : m_sampleMask == ((0x1u << m_sampleCount) - 1u) ? Vec4(renderValue, componentCount > 1 ? renderValue : 0.0f, componentCount > 2 ? renderValue : 0.0f, componentCount > 3 ? renderValue : 1.0f) : firstColor); errorMask.getAccess().setPixel(okColor, x, y, z); for (size_t attachmentNdx = 0; attachmentNdx < m_attachmentsCount; attachmentNdx++) { const Vec4 color (accesses[attachmentNdx].getPixel(x, y, z)); if (refColor != color) { isOk = false; errorMask.getAccess().setPixel(errorColor, x, y, z); break; } } { const Vec4 old = m_sum.getAccess().getPixel(x, y, z); m_sum.getAccess().setPixel(old + (tcu::isSRGB(format) ? tcu::sRGBToLinear(firstColor) : firstColor), x, y, z); const Vec4 oldSrgb = m_sumSrgb.getAccess().getPixel(x, y, z); m_sumSrgb.getAccess().setPixel(oldSrgb + firstColor, x, y, z); } } if (!isOk) { const std::string sectionName ("ResolveVerifyWithMask" + de::toString(m_sampleMask)); const tcu::ScopedLogSection section (log, sectionName, sectionName); for (size_t attachmentNdx = 0; attachmentNdx < m_attachmentsCount; attachmentNdx++) logImage(std::string("Attachment") + de::toString(attachmentNdx), accesses[attachmentNdx]); logImage("ErrorMask", errorMask.getAccess()); if (m_sampleMask == 0x0u) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Empty sample mask didn't produce all " << clearValue << " pixels" << tcu::TestLog::EndMessage; m_resultCollector.fail("Empty sample mask didn't produce correct pixel values"); } else if (m_sampleMask == ((0x1u << m_sampleCount) - 1u)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Full sample mask didn't produce all " << renderValue << " pixels" << tcu::TestLog::EndMessage; m_resultCollector.fail("Full sample mask didn't produce correct pixel values"); } else { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Resolve is inconsistent between attachments" << tcu::TestLog::EndMessage; m_resultCollector.fail("Resolve is inconsistent between attachments"); } } break; } case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: { const int componentCount (tcu::getNumUsedChannels(format.order)); const UVec4 bitDepth (tcu::getTextureFormatBitDepth(format).cast()); const UVec4 renderValue (tcu::select((UVec4(1u) << tcu::min(UVec4(8u), bitDepth)) - UVec4(1u), UVec4(0u, 0u, 0u, 1u), tcu::lessThan(IVec4(0, 1, 2, 3), IVec4(componentCount)))); const UVec4 clearValue (tcu::select(UVec4(0u), UVec4(0u, 0u, 0u, 1u), tcu::lessThan(IVec4(0, 1, 2, 3), IVec4(componentCount)))); bool unexpectedValues = false; bool inconsistentComponents = false; bool inconsistentAttachments = false; for (deUint32 z = 0; z < m_layerCount; z++) for (deUint32 y = 0; y < m_height; y++) for (deUint32 x = 0; x < m_width; x++) { // Color has to be all zeros if no samples were covered, all 255 if all samples were covered or consistent across all attachments const UVec4 refColor (m_sampleMask == 0x0u ? clearValue : m_sampleMask == ((0x1u << m_sampleCount) - 1u) ? renderValue : accesses[0].getPixelUint(x, y, z)); bool isOk = true; // If reference value was taken from first attachment, check that it is valid value i.e. clear or render value if (m_sampleMask != 0x0u && m_sampleMask != ((0x1u << m_sampleCount) - 1u)) { // Each component must be resolved same way const BVec4 isRenderValue (refColor == renderValue); const BVec4 isClearValue (refColor == clearValue); const bool unexpectedValue (tcu::anyNotEqual(tcu::logicalOr(isRenderValue, isClearValue), BVec4(true))); const bool inconsistentComponent (!(tcu::allEqual(isRenderValue, BVec4(true)) || tcu::allEqual(isClearValue, BVec4(true)))); unexpectedValues |= unexpectedValue; inconsistentComponents |= inconsistentComponent; if (unexpectedValue || inconsistentComponent) isOk = false; } for (size_t attachmentNdx = 0; attachmentNdx < m_attachmentsCount; attachmentNdx++) { const UVec4 color (accesses[attachmentNdx].getPixelUint(x, y, z)); if (refColor != color) { isOk = false; inconsistentAttachments = true; break; } } errorMask.getAccess().setPixel((isOk ? okColor : errorColor), x, y, z); } if (unexpectedValues || inconsistentComponents || inconsistentAttachments) { const std::string sectionName ("ResolveVerifyWithMask" + de::toString(m_sampleMask)); const tcu::ScopedLogSection section (log, sectionName, sectionName); for (size_t attachmentNdx = 0; attachmentNdx < m_attachmentsCount; attachmentNdx++) logImage(std::string("Attachment") + de::toString(attachmentNdx), accesses[attachmentNdx]); logImage("ErrorMask", errorMask.getAccess()); if (m_sampleMask == 0x0u) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Empty sample mask didn't produce all " << clearValue << " pixels" << tcu::TestLog::EndMessage; m_resultCollector.fail("Empty sample mask didn't produce correct pixels"); } else if (m_sampleMask == ((0x1u << m_sampleCount) - 1u)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Full sample mask didn't produce all " << renderValue << " pixels" << tcu::TestLog::EndMessage; m_resultCollector.fail("Full sample mask didn't produce correct pixels"); } else { if (unexpectedValues) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Resolve produced unexpected values i.e. not " << clearValue << " or " << renderValue << tcu::TestLog::EndMessage; m_resultCollector.fail("Resolve produced unexpected values"); } if (inconsistentComponents) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Different components of attachment were resolved to different values." << tcu::TestLog::EndMessage; m_resultCollector.fail("Different components of attachment were resolved to different values."); } if (inconsistentAttachments) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Different attachments were resolved to different values." << tcu::TestLog::EndMessage; m_resultCollector.fail("Different attachments were resolved to different values."); } } } break; } case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: { const int componentCount (tcu::getNumUsedChannels(format.order)); const IVec4 bitDepth (tcu::getTextureFormatBitDepth(format)); const IVec4 renderValue (tcu::select((IVec4(1) << (tcu::min(IVec4(8), bitDepth) - IVec4(1))) - IVec4(1), IVec4(0, 0, 0, 1), tcu::lessThan(IVec4(0, 1, 2, 3), IVec4(componentCount)))); const IVec4 clearValue (tcu::select(-(IVec4(1) << (tcu::min(IVec4(8), bitDepth) - IVec4(1))), IVec4(0, 0, 0, 1), tcu::lessThan(IVec4(0, 1, 2, 3), IVec4(componentCount)))); bool unexpectedValues = false; bool inconsistentComponents = false; bool inconsistentAttachments = false; for (deUint32 z = 0; z < m_layerCount; z++) for (deUint32 y = 0; y < m_height; y++) for (deUint32 x = 0; x < m_width; x++) { // Color has to be all zeros if no samples were covered, all 255 if all samples were covered or consistent across all attachments const IVec4 refColor (m_sampleMask == 0x0u ? clearValue : m_sampleMask == ((0x1u << m_sampleCount) - 1u) ? renderValue : accesses[0].getPixelInt(x, y, z)); bool isOk = true; // If reference value was taken from first attachment, check that it is valid value i.e. clear or render value if (m_sampleMask != 0x0u && m_sampleMask != ((0x1u << m_sampleCount) - 1u)) { // Each component must be resolved same way const BVec4 isRenderValue (refColor == renderValue); const BVec4 isClearValue (refColor == clearValue); const bool unexpectedValue (tcu::anyNotEqual(tcu::logicalOr(isRenderValue, isClearValue), BVec4(true))); const bool inconsistentComponent (!(tcu::allEqual(isRenderValue, BVec4(true)) || tcu::allEqual(isClearValue, BVec4(true)))); unexpectedValues |= unexpectedValue; inconsistentComponents |= inconsistentComponent; if (unexpectedValue || inconsistentComponent) isOk = false; } for (size_t attachmentNdx = 0; attachmentNdx < m_attachmentsCount; attachmentNdx++) { const IVec4 color (accesses[attachmentNdx].getPixelInt(x, y, z)); if (refColor != color) { isOk = false; inconsistentAttachments = true; break; } } errorMask.getAccess().setPixel((isOk ? okColor : errorColor), x, y, z); } if (unexpectedValues || inconsistentComponents || inconsistentAttachments) { const std::string sectionName ("ResolveVerifyWithMask" + de::toString(m_sampleMask)); const tcu::ScopedLogSection section (log, sectionName, sectionName); for (size_t attachmentNdx = 0; attachmentNdx < m_attachmentsCount; attachmentNdx++) logImage(std::string("Attachment") + de::toString(attachmentNdx), accesses[attachmentNdx]); logImage("ErrorMask", errorMask.getAccess()); if (m_sampleMask == 0x0u) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Empty sample mask didn't produce all " << clearValue << " pixels" << tcu::TestLog::EndMessage; m_resultCollector.fail("Empty sample mask didn't produce correct pixels"); } else if (m_sampleMask == ((0x1u << m_sampleCount) - 1u)) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Full sample mask didn't produce all " << renderValue << " pixels" << tcu::TestLog::EndMessage; m_resultCollector.fail("Full sample mask didn't produce correct pixels"); } else { if (unexpectedValues) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Resolve produced unexpected values i.e. not " << clearValue << " or " << renderValue << tcu::TestLog::EndMessage; m_resultCollector.fail("Resolve produced unexpected values"); } if (inconsistentComponents) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Different components of attachment were resolved to different values." << tcu::TestLog::EndMessage; m_resultCollector.fail("Different components of attachment were resolved to different values."); } if (inconsistentAttachments) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Different attachments were resolved to different values." << tcu::TestLog::EndMessage; m_resultCollector.fail("Different attachments were resolved to different values."); } } } break; } default: DE_FATAL("Unknown channel class"); } } tcu::TestStatus MultisampleRenderPassTestInstance::iterate (void) { if (m_sampleMask == 0u) { const tcu::TextureFormat format (mapVkFormat(m_format)); const tcu::TextureChannelClass channelClass (tcu::getTextureChannelClass(format.type)); tcu::TestLog& log (m_context.getTestContext().getLog()); switch (channelClass) { case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: log << TestLog::Message << "Clearing target to zero and rendering 255 pixels with every possible sample mask" << TestLog::EndMessage; break; case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: log << TestLog::Message << "Clearing target to -128 and rendering 127 pixels with every possible sample mask" << TestLog::EndMessage; break; case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: log << TestLog::Message << "Clearing target to black and rendering white pixels with every possible sample mask" << TestLog::EndMessage; break; default: DE_FATAL("Unknown channel class"); } } submitSwitch(m_renderingType); verify(); if (m_sampleMask == ((0x1u << m_sampleCount) - 1u)) { const tcu::TextureFormat format (mapVkFormat(m_format)); const tcu::TextureChannelClass channelClass (tcu::getTextureChannelClass(format.type)); const Vec4 threshold (getFormatThreshold()); tcu::TestLog& log (m_context.getTestContext().getLog()); if (channelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT || channelClass == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || channelClass == tcu::TEXTURECHANNELCLASS_FLOATING_POINT) { const int componentCount (tcu::getNumUsedChannels(format.order)); const Vec4 errorColor (1.0f, 0.0f, 0.0f, 1.0f); const Vec4 okColor (0.0f, 1.0f, 0.0f, 1.0f); tcu::TextureLevel errorMask (tcu::TextureFormat(tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), m_width, m_height, m_layerCount); bool isOk = true; Vec4 maxDiff (0.0f); Vec4 expectedAverage; switch (channelClass) { case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: { expectedAverage = Vec4(0.5f, componentCount > 1 ? 0.5f : 0.0f, componentCount > 2 ? 0.5f : 0.0f, componentCount > 3 ? 0.5f : 1.0f); break; } case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: { expectedAverage = Vec4(0.0f, 0.0f, 0.0f, componentCount > 3 ? 0.0f : 1.0f); break; } default: DE_FATAL("Unknown channel class"); } for (deUint32 z = 0; z < m_layerCount; z++) for (deUint32 y = 0; y < m_height; y++) for (deUint32 x = 0; x < m_width; x++) { const Vec4 sum (m_sum.getAccess().getPixel(x, y, z)); const Vec4 average (sum / Vec4((float)(0x1u << m_sampleCount))); const Vec4 diff (tcu::abs(average - expectedAverage)); m_sum.getAccess().setPixel(average, x, y, z); errorMask.getAccess().setPixel(okColor, x, y, z); bool failThreshold; if (!tcu::isSRGB(format)) { failThreshold = (diff[0] > threshold.x() || diff[1] > threshold.y() || diff[2] > threshold.z() || diff[3] > threshold.w()); } else { const Vec4 sumSrgb(m_sumSrgb.getAccess().getPixel(x, y, z)); const Vec4 averageSrgb(sumSrgb / Vec4((float)(0x1u << m_sampleCount))); const Vec4 diffSrgb(tcu::abs(averageSrgb - expectedAverage)); m_sumSrgb.getAccess().setPixel(averageSrgb, x, y, z); // Spec doesn't restrict implementation to downsample in linear color space. So, comparing both non linear and // linear diff's in case of srgb formats. failThreshold = ((diff[0] > threshold.x() || diff[1] > threshold.y() || diff[2] > threshold.z() || diff[3] > threshold.w()) && (diffSrgb[0] > threshold.x() || diffSrgb[1] > threshold.y() || diffSrgb[2] > threshold.z() || diffSrgb[3] > threshold.w())); } if (failThreshold) { isOk = false; maxDiff = tcu::max(maxDiff, diff); errorMask.getAccess().setPixel(errorColor, x, y, z); } } log << TestLog::Image("Average resolved values in attachment 0", "Average resolved values in attachment 0", m_sum); if (!isOk) { std::stringstream message; m_context.getTestContext().getLog() << tcu::LogImage("ErrorMask", "ErrorMask", errorMask.getAccess()); message << "Average resolved values differ from expected average values by more than "; switch (componentCount) { case 1: message << threshold.x(); break; case 2: message << "vec2" << Vec2(threshold.x(), threshold.y()); break; case 3: message << "vec3" << Vec3(threshold.x(), threshold.y(), threshold.z()); break; default: message << "vec4" << threshold; } message << ". Max diff " << maxDiff; log << TestLog::Message << message.str() << TestLog::EndMessage; m_resultCollector.fail("Average resolved values differ from expected average values"); } } return tcu::TestStatus(m_resultCollector.getResult(), m_resultCollector.getMessage()); } else { m_sampleMask++; return tcu::TestStatus::incomplete(); } } template Move MultisampleRenderPassTestInstance::createRenderPass (bool usedResolveAttachment) { // make name for RenderPass1Trait or RenderPass2Trait shorter typedef RenderPassTrait RPT; typedef typename RPT::AttDesc AttDesc; typedef typename RPT::AttRef AttRef; typedef typename RPT::SubpassDesc SubpassDesc; typedef typename RPT::RenderPassCreateInfo RenderPassCreateInfo; const DeviceInterface& vkd = m_context.getDeviceInterface(); VkDevice device = m_context.getDevice(); std::vector attachments; std::vector colorAttachmentRefs; std::vector resolveAttachmentRefs; for (size_t attachmentNdx = 0; attachmentNdx < m_attachmentsCount; attachmentNdx++) { { const AttDesc multisampleAttachment ( // sType DE_NULL, // pNext 0u, // flags m_format, // format m_sampleCount, // samples VK_ATTACHMENT_LOAD_OP_DONT_CARE, // loadOp VK_ATTACHMENT_STORE_OP_DONT_CARE, // storeOp VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencilLoadOp VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilStoreOp VK_IMAGE_LAYOUT_UNDEFINED, // initialLayout VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // finalLayout ); const AttRef attachmentRef ( // sType DE_NULL, // pNext (deUint32)attachments.size(), // attachment VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // layout 0u // aspectMask ); colorAttachmentRefs.push_back(attachmentRef); attachments.push_back(multisampleAttachment); } { const AttDesc singlesampleAttachment ( // sType DE_NULL, // pNext 0u, // flags m_format, // format VK_SAMPLE_COUNT_1_BIT, // samples VK_ATTACHMENT_LOAD_OP_DONT_CARE, // loadOp VK_ATTACHMENT_STORE_OP_STORE, // storeOp VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencilLoadOp VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilStoreOp VK_IMAGE_LAYOUT_UNDEFINED, // initialLayout VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL // finalLayout ); const auto attachmentId = (usedResolveAttachment ? static_cast(attachments.size()) : VK_ATTACHMENT_UNUSED); const AttRef attachmentRef ( // sType DE_NULL, // pNext attachmentId, // attachment VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // layout 0u // aspectMask ); resolveAttachmentRefs.push_back(attachmentRef); attachments.push_back(singlesampleAttachment); } } DE_ASSERT(colorAttachmentRefs.size() == resolveAttachmentRefs.size()); DE_ASSERT(attachments.size() == colorAttachmentRefs.size() + resolveAttachmentRefs.size()); const SubpassDesc subpass ( // sType DE_NULL, // pNext (VkSubpassDescriptionFlags)0, // flags VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint 0u, // viewMask 0u, // inputAttachmentCount DE_NULL, // pInputAttachments (deUint32)colorAttachmentRefs.size(), // colorAttachmentCount &colorAttachmentRefs[0], // pColorAttachments &resolveAttachmentRefs[0], // pResolveAttachments DE_NULL, // pDepthStencilAttachment 0u, // preserveAttachmentCount DE_NULL // pPreserveAttachments ); const RenderPassCreateInfo renderPassCreator ( // sType DE_NULL, // pNext (VkRenderPassCreateFlags)0u, // flags (deUint32)attachments.size(), // attachmentCount &attachments[0], // pAttachments 1u, // subpassCount &subpass, // pSubpasses 0u, // dependencyCount DE_NULL, // pDependencies 0u, // correlatedViewMaskCount DE_NULL // pCorrelatedViewMasks ); return renderPassCreator.createRenderPass(vkd, device); } Move MultisampleRenderPassTestInstance::createRenderPassSwitch (bool usedResolveAttachment) { switch (m_renderingType) { case RENDERING_TYPE_RENDERPASS_LEGACY: return createRenderPass(usedResolveAttachment); case RENDERING_TYPE_RENDERPASS2: return createRenderPass(usedResolveAttachment); case RENDERING_TYPE_DYNAMIC_RENDERING: return Move(); default: TCU_THROW(InternalError, "Impossible"); } } Move MultisampleRenderPassTestInstance::createRenderPassCompatible (void) { if (m_testCompatibility) { // The compatible render pass is always created with a used resolve attachment. return createRenderPassSwitch(true); } else { return {}; } } Move MultisampleRenderPassTestInstance::createRenderPipelineLayout (void) { const DeviceInterface& vkd = m_context.getDeviceInterface(); VkDevice device = m_context.getDevice(); const VkPushConstantRange pushConstant = { VK_SHADER_STAGE_FRAGMENT_BIT, 0u, 4u }; const VkPipelineLayoutCreateInfo createInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, DE_NULL, (vk::VkPipelineLayoutCreateFlags)0, 0u, DE_NULL, 1u, &pushConstant }; return createPipelineLayout(vkd, device, &createInfo); } Move MultisampleRenderPassTestInstance::createRenderPipeline (void) { const DeviceInterface& vkd = m_context.getDeviceInterface(); VkDevice device = m_context.getDevice(); const vk::BinaryCollection& binaryCollection = m_context.getBinaryCollection(); const Unique vertexShaderModule (createShaderModule(vkd, device, binaryCollection.get("quad-vert"), 0u)); const Unique fragmentShaderModule (createShaderModule(vkd, device, binaryCollection.get("quad-frag"), 0u)); const Move geometryShaderModule (m_layerCount == 1 ? Move() : createShaderModule(vkd, device, binaryCollection.get("geom"), 0u)); // Disable blending const VkPipelineColorBlendAttachmentState attachmentBlendState = { VK_FALSE, VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, VK_BLEND_OP_ADD, VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ONE, VK_BLEND_OP_ADD, VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT }; std::vector attachmentBlendStates(m_attachmentsCount, attachmentBlendState); const VkPipelineVertexInputStateCreateInfo vertexInputState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, DE_NULL, (VkPipelineVertexInputStateCreateFlags)0u, 0u, DE_NULL, 0u, DE_NULL }; const tcu::UVec2 renderArea (m_width, m_height); const std::vector viewports (1, makeViewport(renderArea)); const std::vector scissors (1, makeRect2D(renderArea)); const VkPipelineMultisampleStateCreateInfo multisampleState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, DE_NULL, (VkPipelineMultisampleStateCreateFlags)0u, sampleCountBitFromSampleCount(m_sampleCount), VK_FALSE, 0.0f, DE_NULL, VK_FALSE, VK_FALSE, }; const VkPipelineDepthStencilStateCreateInfo depthStencilState = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, DE_NULL, (VkPipelineDepthStencilStateCreateFlags)0u, VK_FALSE, VK_TRUE, VK_COMPARE_OP_ALWAYS, VK_FALSE, VK_TRUE, { VK_STENCIL_OP_KEEP, VK_STENCIL_OP_INCREMENT_AND_WRAP, VK_STENCIL_OP_KEEP, VK_COMPARE_OP_ALWAYS, ~0u, ~0u, 0xFFu / (m_sampleCount + 1) }, { VK_STENCIL_OP_KEEP, VK_STENCIL_OP_INCREMENT_AND_WRAP, VK_STENCIL_OP_KEEP, VK_COMPARE_OP_ALWAYS, ~0u, ~0u, 0xFFu / (m_sampleCount + 1) }, 0.0f, 1.0f }; const VkPipelineColorBlendStateCreateInfo blendState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, DE_NULL, (VkPipelineColorBlendStateCreateFlags)0u, VK_FALSE, VK_LOGIC_OP_COPY, deUint32(attachmentBlendStates.size()), &attachmentBlendStates[0], { 0.0f, 0.0f, 0.0f, 0.0f } }; VkPipelineRenderingCreateInfoKHR* pNext = DE_NULL; std::vector attachmentFormats(m_attachmentsCount, m_format); VkPipelineRenderingCreateInfoKHR renderingCreateInfo { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR, DE_NULL, 0u, m_attachmentsCount, attachmentFormats.data(), VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED }; if (m_renderingType == RENDERING_TYPE_DYNAMIC_RENDERING) pNext = &renderingCreateInfo; return makeGraphicsPipeline(vkd, // const DeviceInterface& vk device, // const VkDevice device *m_renderPipelineLayout, // const VkPipelineLayout pipelineLayout *vertexShaderModule, // const VkShaderModule vertexShaderModule DE_NULL, // const VkShaderModule tessellationControlShaderModule DE_NULL, // const VkShaderModule tessellationEvalShaderModule m_layerCount != 1 ? *geometryShaderModule : DE_NULL,// const VkShaderModule geometryShaderModule *fragmentShaderModule, // const VkShaderModule fragmentShaderModule *m_renderPass, // const VkRenderPass renderPass viewports, // const std::vector& viewports scissors, // const std::vector& scissors VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // const VkPrimitiveTopology topology 0u, // const deUint32 subpass 0u, // const deUint32 patchControlPoints &vertexInputState, // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo DE_NULL, // const VkPipelineRasterizationStateCreateInfo* rasterizationStateCreateInfo &multisampleState, // const VkPipelineMultisampleStateCreateInfo* multisampleStateCreateInfo &depthStencilState, // const VkPipelineDepthStencilStateCreateInfo* depthStencilStateCreateInfo &blendState, // const VkPipelineColorBlendStateCreateInfo* colorBlendStateCreateInfo DE_NULL, // const VkPipelineDynamicStateCreateInfo* dynamicStateCreateInfo pNext); // const void* pNext } class MaxAttachmenstsRenderPassTestInstance : public MultisampleRenderPassTestBase { public: MaxAttachmenstsRenderPassTestInstance (Context& context, TestConfig config); ~MaxAttachmenstsRenderPassTestInstance (void); tcu::TestStatus iterate (void); private: template void submit (void); void submitSwitch (RenderingType renderingType); void verify (void); Move createDescriptorSetLayout (void); Move createDescriptorPool (void); Move createDescriptorSet (void); template Move createRenderPass (void); Move createRenderPassSwitch (const RenderingType renderingType); Move createRenderPipelineLayout (bool secondSubpass); Move createRenderPipeline (bool secondSubpass); private: const std::vector m_multisampleImages; const std::vector m_multisampleImageMemory; const std::vector m_multisampleImageViews; const std::vector m_singlesampleImages; const std::vector m_singlesampleImageMemory; const std::vector m_singlesampleImageViews; const Unique m_descriptorSetLayout; const Unique m_descriptorPool; const Unique m_descriptorSet; const Unique m_renderPass; const Unique m_framebuffer; const Unique m_pipelineLayoutPass0; const Unique m_pipelinePass0; const Unique m_pipelineLayoutPass1; const Unique m_pipelinePass1; const std::vector m_buffers; const std::vector m_bufferMemory; const Unique m_commandPool; tcu::ResultCollector m_resultCollector; }; MaxAttachmenstsRenderPassTestInstance::MaxAttachmenstsRenderPassTestInstance (Context& context, TestConfig config) : MultisampleRenderPassTestBase(context, config) , m_multisampleImages (createImages(m_sampleCount, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) , m_multisampleImageMemory (createImageMemory(m_multisampleImages)) , m_multisampleImageViews (createImageViews(m_multisampleImages)) , m_singlesampleImages (createImages(VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT)) , m_singlesampleImageMemory (createImageMemory(m_singlesampleImages)) , m_singlesampleImageViews (createImageViews(m_singlesampleImages)) , m_descriptorSetLayout (createDescriptorSetLayout()) , m_descriptorPool (createDescriptorPool()) , m_descriptorSet (createDescriptorSet()) , m_renderPass (createRenderPassSwitch(config.renderingType)) , m_framebuffer (createFramebuffer(m_multisampleImageViews, m_singlesampleImageViews, *m_renderPass)) , m_pipelineLayoutPass0 (createRenderPipelineLayout(0)) , m_pipelinePass0 (createRenderPipeline(0)) , m_pipelineLayoutPass1 (createRenderPipelineLayout(1)) , m_pipelinePass1 (createRenderPipeline(1)) , m_buffers (createBuffers()) , m_bufferMemory (createBufferMemory(m_buffers)) , m_commandPool (createCommandPool(context.getDeviceInterface(), context.getDevice(), VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, context.getUniversalQueueFamilyIndex())) { } MaxAttachmenstsRenderPassTestInstance::~MaxAttachmenstsRenderPassTestInstance (void) { } template void MaxAttachmenstsRenderPassTestInstance::submit (void) { const DeviceInterface& vkd (m_context.getDeviceInterface()); const VkDevice device (m_context.getDevice()); const Unique commandBuffer (allocateCommandBuffer(vkd, device, *m_commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)); const typename RenderpassSubpass::SubpassBeginInfo subpassBeginInfo (DE_NULL, VK_SUBPASS_CONTENTS_INLINE); const typename RenderpassSubpass::SubpassEndInfo subpassEndInfo (DE_NULL); beginCommandBuffer(vkd, *commandBuffer); // Memory barriers between previous copies and rendering { std::vector barriers; for (size_t dstNdx = 0; dstNdx < m_singlesampleImages.size(); dstNdx++) { const VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, DE_NULL, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, **m_singlesampleImages[dstNdx], { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_layerCount } }; barriers.push_back(barrier); } vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, (deUint32)barriers.size(), &barriers[0]); } { const VkRenderPassBeginInfo beginInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, DE_NULL, *m_renderPass, *m_framebuffer, { { 0u, 0u }, { m_width, m_height } }, 0u, DE_NULL }; RenderpassSubpass::cmdBeginRenderPass(vkd, *commandBuffer, &beginInfo, &subpassBeginInfo); } // Clear everything to black clearAttachments(*commandBuffer); // First subpass - render black samples { vkd.cmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelinePass0); vkd.cmdDraw(*commandBuffer, 6u, 1u, 0u, 0u); } // Second subpasss - merge attachments { RenderpassSubpass::cmdNextSubpass(vkd, *commandBuffer, &subpassBeginInfo, &subpassEndInfo); vkd.cmdBindPipeline(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelinePass1); vkd.cmdBindDescriptorSets(*commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelineLayoutPass1, 0, 1u, &*m_descriptorSet, 0, NULL); vkd.cmdDraw(*commandBuffer, 6u, 1u, 0u, 0u); } RenderpassSubpass::cmdEndRenderPass(vkd, *commandBuffer, &subpassEndInfo); // Memory barriers between rendering and copies { std::vector barriers; for (size_t dstNdx = 0; dstNdx < m_singlesampleImages.size(); dstNdx++) { const VkImageMemoryBarrier barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, DE_NULL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, **m_singlesampleImages[dstNdx], { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_layerCount } }; barriers.push_back(barrier); } vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, (deUint32)barriers.size(), &barriers[0]); } // Copy image memory to buffers for (size_t dstNdx = 0; dstNdx < m_singlesampleImages.size(); dstNdx++) { const VkBufferImageCopy region = { 0u, 0u, 0u, { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, m_layerCount, }, { 0u, 0u, 0u }, { m_width, m_height, 1u } }; vkd.cmdCopyImageToBuffer(*commandBuffer, **m_singlesampleImages[dstNdx], VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, **m_buffers[dstNdx], 1u, ®ion); } // Memory barriers between copies and host access { std::vector barriers; for (size_t dstNdx = 0; dstNdx < m_buffers.size(); dstNdx++) { const VkBufferMemoryBarrier barrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, DE_NULL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, **m_buffers[dstNdx], 0u, VK_WHOLE_SIZE }; barriers.push_back(barrier); } vkd.cmdPipelineBarrier(*commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 0u, DE_NULL, (deUint32)barriers.size(), &barriers[0], 0u, DE_NULL); } endCommandBuffer(vkd, *commandBuffer); submitCommandsAndWait(vkd, device, m_context.getUniversalQueue(), *commandBuffer); for (size_t memoryBufferNdx = 0; memoryBufferNdx < m_bufferMemory.size(); memoryBufferNdx++) invalidateMappedMemoryRange(vkd, device, m_bufferMemory[memoryBufferNdx]->getMemory(), 0u, VK_WHOLE_SIZE); } void MaxAttachmenstsRenderPassTestInstance::submitSwitch (RenderingType renderingType) { switch (renderingType) { case RENDERING_TYPE_RENDERPASS_LEGACY: submit(); break; case RENDERING_TYPE_RENDERPASS2: submit(); break; default: TCU_THROW(InternalError, "Impossible"); } } template bool isValueAboveThreshold1 (const VecType& vale, const VecType& threshold) { return (vale[0] > threshold[0]); } template bool isValueAboveThreshold2 (const VecType& vale, const VecType& threshold) { return (vale[0] > threshold[0]) || (vale[1] > threshold[1]); } template bool isValueAboveThreshold3 (const VecType& vale, const VecType& threshold) { return (vale[0] > threshold[0]) || (vale[1] > threshold[1]) || (vale[2] > threshold[2]); } template bool isValueAboveThreshold4 (const VecType& vale, const VecType& threshold) { return (vale[0] > threshold[0]) || (vale[1] > threshold[1]) || (vale[2] > threshold[2]) || (vale[3] > threshold[3]); } void MaxAttachmenstsRenderPassTestInstance::verify (void) { const Vec4 errorColor (1.0f, 0.0f, 0.0f, 1.0f); const Vec4 okColor (0.0f, 1.0f, 0.0f, 1.0f); const tcu::TextureFormat format (mapVkFormat(m_format)); const tcu::TextureChannelClass channelClass (tcu::getTextureChannelClass(format.type)); const int componentCount (tcu::getNumUsedChannels(format.order)); const int outputsCount = m_attachmentsCount / 2; DE_ASSERT((componentCount >= 0) && (componentCount < 5)); std::vector accesses; for (int outputNdx = 0; outputNdx < outputsCount; ++outputNdx) { void* const ptr = m_bufferMemory[outputNdx]->getHostPtr(); accesses.push_back(tcu::ConstPixelBufferAccess(format, m_width, m_height, 1, ptr)); } tcu::TextureLevel errorMask (tcu::TextureFormat(tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), m_width, m_height, outputsCount); tcu::TestLog& log (m_context.getTestContext().getLog()); bool isOk = true; switch (channelClass) { case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: { const Vec4 refColor(0.0f, 0.3f, 0.6f, 0.75f); const Vec4 threshold(getFormatThreshold()); typedef bool(*ValueAboveThresholdFn)(const Vec4&, const Vec4&); ValueAboveThresholdFn componentToFnMap[4] = { isValueAboveThreshold1, isValueAboveThreshold2, isValueAboveThreshold3, isValueAboveThreshold4 }; ValueAboveThresholdFn isValueAboveThreshold = componentToFnMap[componentCount - 1]; bool isSRGBFormat = tcu::isSRGB(format); for (int outputNdx = 0; outputNdx < outputsCount; outputNdx++) for (int y = 0; y < (int)m_height; y++) for (int x = 0; x < (int)m_width; x++) { Vec4 color = accesses[outputNdx].getPixel(x, y); if (isSRGBFormat) color = tcu::sRGBToLinear(color); const Vec4 diff(tcu::abs(color - refColor)); if (isValueAboveThreshold(diff, threshold)) { isOk = false; errorMask.getAccess().setPixel(errorColor, x, y, outputNdx); break; } else errorMask.getAccess().setPixel(okColor, x, y, outputNdx); } break; } case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: { const UVec4 refColor(0, 48, 144, 189); UVec4 threshold(1, 1, 1, 1); if (m_format == VK_FORMAT_A2B10G10R10_UINT_PACK32) threshold[3] = 200; typedef bool(*ValueAboveThresholdFn)(const UVec4&, const UVec4&); ValueAboveThresholdFn componentToFnMap[4] = { isValueAboveThreshold1, isValueAboveThreshold2, isValueAboveThreshold3, isValueAboveThreshold4 }; ValueAboveThresholdFn isValueAboveThreshold = componentToFnMap[componentCount - 1]; for (int outputNdx = 0; outputNdx < outputsCount; outputNdx++) for (int y = 0; y < (int)m_height; y++) for (int x = 0; x < (int)m_width; x++) { const UVec4 color (accesses[outputNdx].getPixelUint(x, y)); const UVec4 diff (std::abs(int(color.x()) - int(refColor.x())), std::abs(int(color.y()) - int(refColor.y())), std::abs(int(color.z()) - int(refColor.z())), std::abs(int(color.w()) - int(refColor.w()))); if (isValueAboveThreshold(diff, threshold)) { isOk = false; errorMask.getAccess().setPixel(errorColor, x, y, outputNdx); break; } else errorMask.getAccess().setPixel(okColor, x, y, outputNdx); } break; } case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: { const IVec4 refColor (0, 24, 75, 93); const IVec4 threshold (1, 1, 1, 1); typedef bool(*ValueAboveThresholdFn)(const IVec4&, const IVec4&); ValueAboveThresholdFn componentToFnMap[4] = { isValueAboveThreshold1, isValueAboveThreshold2, isValueAboveThreshold3, isValueAboveThreshold4 }; ValueAboveThresholdFn isValueAboveThreshold = componentToFnMap[componentCount - 1]; for (int outputNdx = 0; outputNdx < outputsCount; outputNdx++) for (int y = 0; y < (int)m_height; y++) for (int x = 0; x < (int)m_width; x++) { const IVec4 color (accesses[outputNdx].getPixelInt(x, y)); const IVec4 diff (std::abs(color.x() - refColor.x()), std::abs(color.y() - refColor.y()), std::abs(color.z() - refColor.z()), std::abs(color.w() - refColor.w())); if (isValueAboveThreshold(diff, threshold)) { isOk = false; errorMask.getAccess().setPixel(errorColor, x, y, outputNdx); break; } else errorMask.getAccess().setPixel(okColor, x, y, outputNdx); } break; } default: DE_FATAL("Unknown channel class"); } if (!isOk) { const std::string sectionName ("MaxAttachmentsVerify"); const tcu::ScopedLogSection section (log, sectionName, sectionName); logImage("ErrorMask", errorMask.getAccess()); m_resultCollector.fail("Fail"); } } tcu::TestStatus MaxAttachmenstsRenderPassTestInstance::iterate(void) { submitSwitch(m_renderingType); verify(); return tcu::TestStatus(m_resultCollector.getResult(), m_resultCollector.getMessage()); } Move MaxAttachmenstsRenderPassTestInstance::createDescriptorSetLayout() { const VkDescriptorSetLayoutBinding bindingTemplate = { 0, // binding VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, // descriptorType 1u, // descriptorCount VK_SHADER_STAGE_FRAGMENT_BIT, // stageFlags DE_NULL // pImmutableSamplers }; std::vector bindings(m_attachmentsCount, bindingTemplate); for (deUint32 idx = 0; idx < m_attachmentsCount; ++idx) bindings[idx].binding = idx; const VkDescriptorSetLayoutCreateInfo createInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, // sType DE_NULL, // pNext 0u, // flags m_attachmentsCount, // bindingCount &bindings[0] // pBindings }; return ::createDescriptorSetLayout(m_context.getDeviceInterface(), m_context.getDevice(), &createInfo); } Move MaxAttachmenstsRenderPassTestInstance::createDescriptorPool() { const VkDescriptorPoolSize size = { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, // type m_attachmentsCount // descriptorCount }; const VkDescriptorPoolCreateInfo createInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, // sType DE_NULL, // pNext VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, // flags 1u, // maxSets 1u, // poolSizeCount &size // pPoolSizes }; return ::createDescriptorPool(m_context.getDeviceInterface(), m_context.getDevice(), &createInfo); } Move MaxAttachmenstsRenderPassTestInstance::createDescriptorSet() { const VkDescriptorSetAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // sType DE_NULL, // pNext *m_descriptorPool, // descriptorPool 1u, // descriptorSetCount &*m_descriptorSetLayout // pSetLayouts }; const vk::DeviceInterface& vkd = m_context.getDeviceInterface(); vk::VkDevice device = m_context.getDevice(); Move descriptorSet = allocateDescriptorSet(vkd, device, &allocateInfo); vector descriptorImageInfo (m_attachmentsCount); vector descriptorWrites (m_attachmentsCount); for (deUint32 idx = 0; idx < m_attachmentsCount; ++idx) { const VkDescriptorImageInfo imageInfo = { DE_NULL, // VkSampler sampler **m_singlesampleImageViews[idx], // VkImageView imageView VK_IMAGE_LAYOUT_GENERAL // VkImageLayout imageLayout }; descriptorImageInfo[idx] = imageInfo; const VkWriteDescriptorSet write = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // VkStructureType sType DE_NULL, // const void* pNext *descriptorSet, // VkDescriptorSet dstSet (deUint32)idx, // uint32_t dstBinding 0u, // uint32_t dstArrayElement 1u, // uint32_t descriptorCount VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, // VkDescriptorType descriptorType &descriptorImageInfo[idx], // const VkDescriptorImageInfo* pImageInfo DE_NULL, // const VkDescriptorBufferInfo* pBufferInfo DE_NULL // const VkBufferView* pTexelBufferView }; descriptorWrites[idx] = write; } vkd.updateDescriptorSets(device, (deUint32)descriptorWrites.size(), &descriptorWrites[0], 0u, DE_NULL); return descriptorSet; } template Move MaxAttachmenstsRenderPassTestInstance::createRenderPass(void) { // make name for RenderPass1Trait or RenderPass2Trait shorter typedef RenderPassTrait RPT; typedef RenderPassTrait RPT; typedef typename RPT::AttDesc AttDesc; typedef typename RPT::AttRef AttRef; typedef typename RPT::SubpassDep SubpassDep; typedef typename RPT::SubpassDesc SubpassDesc; typedef typename RPT::RenderPassCreateInfo RenderPassCreateInfo; const DeviceInterface& vkd = m_context.getDeviceInterface(); VkDevice device = m_context.getDevice(); std::vector attachments; std::vector sp0colorAttachmentRefs; std::vector sp0resolveAttachmentRefs; std::vector sp1inAttachmentRefs; std::vector sp1colorAttachmentRefs; for (size_t attachmentNdx = 0; attachmentNdx < m_attachmentsCount; attachmentNdx++) { // define first subpass outputs { const AttDesc multisampleAttachment ( DE_NULL, // pNext 0u, // flags m_format, // format m_sampleCount, // samples VK_ATTACHMENT_LOAD_OP_DONT_CARE, // loadOp VK_ATTACHMENT_STORE_OP_STORE, // storeOp VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencilLoadOp VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilStoreOp VK_IMAGE_LAYOUT_UNDEFINED, // initialLayout VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // finalLayout ); const AttRef attachmentRef ( DE_NULL, (deUint32)attachments.size(), // attachment VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // layout 0u // aspectMask ); sp0colorAttachmentRefs.push_back(attachmentRef); attachments.push_back(multisampleAttachment); } // define first subpass resolve attachments { const AttDesc singlesampleAttachment ( DE_NULL, // pNext 0u, // flags m_format, // format VK_SAMPLE_COUNT_1_BIT, // samples VK_ATTACHMENT_LOAD_OP_DONT_CARE, // loadOp VK_ATTACHMENT_STORE_OP_STORE, // storeOp VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencilLoadOp VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilStoreOp VK_IMAGE_LAYOUT_UNDEFINED, // initialLayout VK_IMAGE_LAYOUT_GENERAL // finalLayout ); const AttRef attachmentRef ( DE_NULL, // pNext (deUint32)attachments.size(), // attachment VK_IMAGE_LAYOUT_GENERAL, // layout 0u // aspectMask ); sp0resolveAttachmentRefs.push_back(attachmentRef); attachments.push_back(singlesampleAttachment); } // define second subpass inputs { const AttRef attachmentRef ( DE_NULL, // pNext (deUint32)attachments.size() - 1, // attachment VK_IMAGE_LAYOUT_GENERAL, // layout VK_IMAGE_ASPECT_COLOR_BIT // aspectMask ); sp1inAttachmentRefs.push_back(attachmentRef); } // define second subpass outputs - it merges pairs of // results that were produced by the first subpass if (attachmentNdx < (m_attachmentsCount / 2)) { const AttRef colorAttachmentRef ( DE_NULL, // pNext (deUint32)attachments.size() - 1, // attachment VK_IMAGE_LAYOUT_GENERAL, // layout 0u // aspectMask ); sp1colorAttachmentRefs.push_back(colorAttachmentRef); } } DE_ASSERT(sp0colorAttachmentRefs.size() == sp0resolveAttachmentRefs.size()); DE_ASSERT(attachments.size() == sp0colorAttachmentRefs.size() + sp0resolveAttachmentRefs.size()); { const SubpassDesc subpass0 ( // sType DE_NULL, // pNext (VkSubpassDescriptionFlags)0, // flags VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint 0u, // viewMask 0u, // inputAttachmentCount DE_NULL, // pInputAttachments (deUint32)sp0colorAttachmentRefs.size(), // colorAttachmentCount &sp0colorAttachmentRefs[0], // pColorAttachments &sp0resolveAttachmentRefs[0], // pResolveAttachments DE_NULL, // pDepthStencilAttachment 0u, // preserveAttachmentCount DE_NULL // pPreserveAttachments ); const SubpassDesc subpass1 ( // sType DE_NULL, // pNext (VkSubpassDescriptionFlags)0, // flags VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint 0u, // viewMask (deUint32)sp1inAttachmentRefs.size(), // inputAttachmentCount &sp1inAttachmentRefs[0], // pInputAttachments (deUint32)sp1colorAttachmentRefs.size(), // colorAttachmentCount &sp1colorAttachmentRefs[0], // pColorAttachments DE_NULL, // pResolveAttachments DE_NULL, // pDepthStencilAttachment 0u, // preserveAttachmentCount DE_NULL // pPreserveAttachments ); SubpassDesc subpasses[] = { subpass0, subpass1 }; const SubpassDep subpassDependency ( DE_NULL, // pNext 0u, // srcSubpass 1u, // dstSubpass VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // srcStageMask VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, // dstStageMask VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // srcAccessMask VK_ACCESS_INPUT_ATTACHMENT_READ_BIT, // dstAccessMask 0u, // dependencyFlags 0u // viewOffset ); const RenderPassCreateInfo renderPassCreator ( // sType DE_NULL, // pNext (VkRenderPassCreateFlags)0u, // flags (deUint32)attachments.size(), // attachmentCount &attachments[0], // pAttachments 2u, // subpassCount subpasses, // pSubpasses 1u, // dependencyCount &subpassDependency, // pDependencies 0u, // correlatedViewMaskCount DE_NULL // pCorrelatedViewMasks ); return renderPassCreator.createRenderPass(vkd, device); } } Move MaxAttachmenstsRenderPassTestInstance::createRenderPassSwitch(const RenderingType renderingType) { switch (renderingType) { case RENDERING_TYPE_RENDERPASS_LEGACY: return createRenderPass(); case RENDERING_TYPE_RENDERPASS2: return createRenderPass(); default: TCU_THROW(InternalError, "Impossible"); } } Move MaxAttachmenstsRenderPassTestInstance::createRenderPipelineLayout(bool secondSubpass) { const DeviceInterface& vkd = m_context.getDeviceInterface(); VkDevice device = m_context.getDevice(); const VkPipelineLayoutCreateInfo createInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, DE_NULL, (vk::VkPipelineLayoutCreateFlags)0, secondSubpass ? 1u : 0u, secondSubpass ? &*m_descriptorSetLayout : DE_NULL, 0u, DE_NULL }; return createPipelineLayout(vkd, device, &createInfo); } Move MaxAttachmenstsRenderPassTestInstance::createRenderPipeline(bool secondSubpass) { const DeviceInterface& vkd = m_context.getDeviceInterface(); VkDevice device = m_context.getDevice(); const vk::BinaryCollection& binaryCollection = m_context.getBinaryCollection(); VkSampleCountFlagBits sampleCount = sampleCountBitFromSampleCount(m_sampleCount); deUint32 blendStatesCount = m_attachmentsCount; std::string fragShaderNameBase = "quad-frag-sp0-"; if (secondSubpass) { sampleCount = VK_SAMPLE_COUNT_1_BIT; blendStatesCount /= 2; fragShaderNameBase = "quad-frag-sp1-"; } std::string fragShaderName = fragShaderNameBase + de::toString(m_attachmentsCount); const Unique vertexShaderModule (createShaderModule(vkd, device, binaryCollection.get("quad-vert"), 0u)); const Unique fragmentShaderModule (createShaderModule(vkd, device, binaryCollection.get(fragShaderName), 0u)); const Move geometryShaderModule (m_layerCount == 1 ? Move() : createShaderModule(vkd, device, binaryCollection.get("geom"), 0u)); // Disable blending const VkPipelineColorBlendAttachmentState attachmentBlendState = { VK_FALSE, VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, VK_BLEND_OP_ADD, VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ONE, VK_BLEND_OP_ADD, VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT }; std::vector attachmentBlendStates(blendStatesCount, attachmentBlendState); const VkPipelineVertexInputStateCreateInfo vertexInputState = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, DE_NULL, (VkPipelineVertexInputStateCreateFlags)0u, 0u, DE_NULL, 0u, DE_NULL }; const tcu::UVec2 renderArea (m_width, m_height); const std::vector viewports (1, makeViewport(renderArea)); const std::vector scissors (1, makeRect2D(renderArea)); const VkPipelineMultisampleStateCreateInfo multisampleState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, DE_NULL, (VkPipelineMultisampleStateCreateFlags)0u, sampleCount, VK_FALSE, 0.0f, DE_NULL, VK_FALSE, VK_FALSE, }; const VkPipelineDepthStencilStateCreateInfo depthStencilState = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, DE_NULL, (VkPipelineDepthStencilStateCreateFlags)0u, VK_FALSE, VK_TRUE, VK_COMPARE_OP_ALWAYS, VK_FALSE, VK_TRUE, { VK_STENCIL_OP_KEEP, VK_STENCIL_OP_INCREMENT_AND_WRAP, VK_STENCIL_OP_KEEP, VK_COMPARE_OP_ALWAYS, ~0u, ~0u, 0xFFu / (m_sampleCount + 1) }, { VK_STENCIL_OP_KEEP, VK_STENCIL_OP_INCREMENT_AND_WRAP, VK_STENCIL_OP_KEEP, VK_COMPARE_OP_ALWAYS, ~0u, ~0u, 0xFFu / (m_sampleCount + 1) }, 0.0f, 1.0f }; const VkPipelineColorBlendStateCreateInfo blendState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, DE_NULL, (VkPipelineColorBlendStateCreateFlags)0u, VK_FALSE, VK_LOGIC_OP_COPY, deUint32(attachmentBlendStates.size()), &attachmentBlendStates[0], { 0.0f, 0.0f, 0.0f, 0.0f } }; return makeGraphicsPipeline(vkd, // vk device, // device secondSubpass ? *m_pipelineLayoutPass1 : *m_pipelineLayoutPass0, // pipelineLayout *vertexShaderModule, // vertexShaderModule DE_NULL, // tessellationControlShaderModule DE_NULL, // tessellationEvalShaderModule m_layerCount != 1 ? *geometryShaderModule : DE_NULL, // geometryShaderModule *fragmentShaderModule, // fragmentShaderModule *m_renderPass, // renderPass viewports, // viewports scissors, // scissors VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // topology secondSubpass, // subpass 0u, // patchControlPoints &vertexInputState, // vertexInputStateCreateInfo DE_NULL, // rasterizationStateCreateInfo &multisampleState, // multisampleStateCreateInfo &depthStencilState, // depthStencilStateCreateInfo &blendState); // colorBlendStateCreateInfo } class MultisampleRenderPassResolveLevelTestInstance : public MultisampleRenderPassTestInstance { public: MultisampleRenderPassResolveLevelTestInstance (Context& context, TestConfig2 config); ~MultisampleRenderPassResolveLevelTestInstance (void) = default; }; MultisampleRenderPassResolveLevelTestInstance::MultisampleRenderPassResolveLevelTestInstance (Context& context, TestConfig2 config) : MultisampleRenderPassTestInstance(context, config, config.resolveLevel) { } struct Programs { void init(vk::SourceCollections& dst, TestConfig config) const { const tcu::TextureFormat format (mapVkFormat(config.format)); const tcu::TextureChannelClass channelClass (tcu::getTextureChannelClass(format.type)); dst.glslSources.add("quad-vert") << glu::VertexSource( "#version 450\n" "out gl_PerVertex {\n" "\tvec4 gl_Position;\n" "};\n" "highp float;\n" "void main (void) {\n" "\tgl_Position = vec4(((gl_VertexIndex + 2) / 3) % 2 == 0 ? -1.0 : 1.0,\n" "\t ((gl_VertexIndex + 1) / 3) % 2 == 0 ? -1.0 : 1.0, 0.0, 1.0);\n" "}\n"); if (config.layerCount > 1) { std::ostringstream src; src << "#version 450\n" << "highp float;\n" << "\n" << "layout(triangles) in;\n" << "layout(triangle_strip, max_vertices = " << 3 * 2 * config.layerCount << ") out;\n" << "\n" << "in gl_PerVertex {\n" << " vec4 gl_Position;\n" << "} gl_in[];\n" << "\n" << "out gl_PerVertex {\n" << " vec4 gl_Position;\n" << "};\n" << "\n" << "void main (void) {\n" << " for (int layerNdx = 0; layerNdx < " << config.layerCount << "; ++layerNdx) {\n" << " for(int vertexNdx = 0; vertexNdx < gl_in.length(); vertexNdx++) {\n" << " gl_Position = gl_in[vertexNdx].gl_Position;\n" << " gl_Layer = layerNdx;\n" << " EmitVertex();\n" << " };\n" << " EndPrimitive();\n" << " };\n" << "}\n"; dst.glslSources.add("geom") << glu::GeometrySource(src.str()); } const tcu::StringTemplate genericLayoutTemplate("layout(location = ${INDEX}) out ${TYPE_PREFIX}vec4 o_color${INDEX};\n"); const tcu::StringTemplate genericBodyTemplate("\to_color${INDEX} = ${TYPE_PREFIX}vec4(${COLOR_VAL});\n"); if (config.testType == RESOLVE || config.testType == COMPATIBILITY) { const tcu::StringTemplate fragTemplate("#version 450\n" "layout(push_constant) uniform PushConstant {\n" "\thighp uint sampleMask;\n" "} pushConstants;\n" "${LAYOUT}" "void main (void)\n" "{\n" "${BODY}" "}\n"); std::map parameters; switch (channelClass) { case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: parameters["TYPE_PREFIX"] = "u"; parameters["COLOR_VAL"] = "255"; break; case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: parameters["TYPE_PREFIX"] = "i"; parameters["COLOR_VAL"] = "127"; break; case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: parameters["TYPE_PREFIX"] = ""; parameters["COLOR_VAL"] = "1.0"; break; default: DE_FATAL("Unknown channel class"); } std::string layoutDefinitions = ""; std::string shaderBody = "\tgl_SampleMask[0] = int(pushConstants.sampleMask);\n"; for (deUint32 attIdx = 0; attIdx < config.attachmentCount; ++attIdx) { parameters["INDEX"] = de::toString(attIdx); layoutDefinitions += genericLayoutTemplate.specialize(parameters); shaderBody += genericBodyTemplate.specialize(parameters); } parameters["LAYOUT"] = layoutDefinitions; parameters["BODY"] = shaderBody; dst.glslSources.add("quad-frag") << glu::FragmentSource(fragTemplate.specialize(parameters)); } else // MAX_ATTACMENTS { const tcu::StringTemplate fragTemplate("#version 450\n" "${LAYOUT}" "void main (void)\n" "{\n" "${BODY}" "}\n"); std::map parameters; switch (channelClass) { case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER: parameters["TYPE_PREFIX"] = "u"; parameters["COLOR_VAL"] = "0, 64, 192, 252"; break; case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER: parameters["TYPE_PREFIX"] = "i"; parameters["COLOR_VAL"] = "0, 32, 100, 124"; break; case tcu::TEXTURECHANNELCLASS_FLOATING_POINT: case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT: case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT: parameters["TYPE_PREFIX"] = ""; parameters["COLOR_VAL"] = "0.0, 0.4, 0.8, 1.0"; break; default: DE_FATAL("Unknown channel class"); } // parts of fragment shader for second subpass - Vulkan introduced a new uniform type and syntax to glsl for input attachments const tcu::StringTemplate subpassLayoutTemplate("layout (input_attachment_index = ${INDEX}, set = 0, binding = ${INDEX}) uniform ${TYPE_PREFIX}subpassInput i_color${INDEX};\n"); const tcu::StringTemplate subpassFBodyTemplate("\to_color${INDEX} = subpassLoad(i_color${INDEX})*0.5 + subpassLoad(i_color${MIX_INDEX})*0.25;\n"); const tcu::StringTemplate subpassIBodyTemplate("\to_color${INDEX} = subpassLoad(i_color${INDEX}) / 2 + subpassLoad(i_color${MIX_INDEX}) / 4;\n"); bool selectIBody = isIntFormat(config.format) || isUintFormat(config.format); const tcu::StringTemplate& subpassBodyTemplate = selectIBody ? subpassIBodyTemplate : subpassFBodyTemplate; std::string sp0layoutDefinitions = ""; std::string sp0shaderBody = ""; std::string sp1inLayoutDefinitions = ""; std::string sp1outLayoutDefinitions = ""; std::string sp1shaderBody = ""; deUint32 halfAttachments = config.attachmentCount / 2; for (deUint32 attIdx = 0; attIdx < config.attachmentCount; ++attIdx) { parameters["INDEX"] = de::toString(attIdx); sp0layoutDefinitions += genericLayoutTemplate.specialize(parameters); sp0shaderBody += genericBodyTemplate.specialize(parameters); sp1inLayoutDefinitions += subpassLayoutTemplate.specialize(parameters); if (attIdx < halfAttachments) { // we are combining pairs of input attachments to produce half the number of outputs parameters["MIX_INDEX"] = de::toString(halfAttachments + attIdx); sp1outLayoutDefinitions += genericLayoutTemplate.specialize(parameters); sp1shaderBody += subpassBodyTemplate.specialize(parameters); } } // construct fragment shaders for subpass1 and subpass2; note that there // is different shader definition depending on number of attachments std::string nameBase = "quad-frag-sp"; std::string namePostfix = de::toString(config.attachmentCount); parameters["LAYOUT"] = sp0layoutDefinitions; parameters["BODY"] = sp0shaderBody; dst.glslSources.add(nameBase + "0-" + namePostfix) << glu::FragmentSource(fragTemplate.specialize(parameters)); parameters["LAYOUT"] = sp1inLayoutDefinitions + sp1outLayoutDefinitions; parameters["BODY"] = sp1shaderBody; dst.glslSources.add(nameBase + "1-" + namePostfix) << glu::FragmentSource(fragTemplate.specialize(parameters)); } } }; template void checkSupport(Context& context, TestConfigType config) { if (config.layerCount > 1) context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER); if (config.renderingType == RENDERING_TYPE_RENDERPASS2) context.requireDeviceFunctionality("VK_KHR_create_renderpass2"); if (config.renderingType == RENDERING_TYPE_DYNAMIC_RENDERING) context.requireDeviceFunctionality("VK_KHR_dynamic_rendering"); if (context.isDeviceFunctionalitySupported("VK_KHR_portability_subset") && !context.getPortabilitySubsetFeatures().multisampleArrayImage && (config.sampleCount != VK_SAMPLE_COUNT_1_BIT) && (config.layerCount != 1)) { TCU_THROW(NotSupportedError, "VK_KHR_portability_subset: Implementation does not support image array with multiple samples per texel"); } const InstanceInterface& vki = context.getInstanceInterface(); vk::VkPhysicalDevice physicalDevice = context.getPhysicalDevice(); const vk::VkPhysicalDeviceProperties properties = vk::getPhysicalDeviceProperties(vki, physicalDevice); if (config.attachmentCount > properties.limits.maxColorAttachments) TCU_THROW(NotSupportedError, "Required number of color attachments not supported."); } std::string formatToName (VkFormat format) { const std::string formatStr = de::toString(format); const std::string prefix = "VK_FORMAT_"; DE_ASSERT(formatStr.substr(0, prefix.length()) == prefix); return de::toLower(formatStr.substr(prefix.length())); } void initTests (tcu::TestCaseGroup* group, RenderingType renderingType) { static const VkFormat formats[] = { VK_FORMAT_R5G6B5_UNORM_PACK16, VK_FORMAT_R8_UNORM, VK_FORMAT_R8_SNORM, VK_FORMAT_R8_UINT, VK_FORMAT_R8_SINT, VK_FORMAT_R8G8_UNORM, VK_FORMAT_R8G8_SNORM, VK_FORMAT_R8G8_UINT, VK_FORMAT_R8G8_SINT, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SNORM, VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R8G8B8A8_SINT, VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_A8B8G8R8_UNORM_PACK32, VK_FORMAT_A8B8G8R8_SNORM_PACK32, VK_FORMAT_A8B8G8R8_UINT_PACK32, VK_FORMAT_A8B8G8R8_SINT_PACK32, VK_FORMAT_A8B8G8R8_SRGB_PACK32, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_A2R10G10B10_UNORM_PACK32, VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_FORMAT_A2B10G10R10_UINT_PACK32, VK_FORMAT_R16_UNORM, VK_FORMAT_R16_SNORM, VK_FORMAT_R16_UINT, VK_FORMAT_R16_SINT, VK_FORMAT_R16_SFLOAT, VK_FORMAT_R16G16_UNORM, VK_FORMAT_R16G16_SNORM, VK_FORMAT_R16G16_UINT, VK_FORMAT_R16G16_SINT, VK_FORMAT_R16G16_SFLOAT, VK_FORMAT_R16G16B16A16_UNORM, VK_FORMAT_R16G16B16A16_SNORM, VK_FORMAT_R16G16B16A16_UINT, VK_FORMAT_R16G16B16A16_SINT, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_R32_UINT, VK_FORMAT_R32_SINT, VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32G32_UINT, VK_FORMAT_R32G32_SINT, VK_FORMAT_R32G32_SFLOAT, VK_FORMAT_R32G32B32A32_UINT, VK_FORMAT_R32G32B32A32_SINT, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, }; const deUint32 sampleCounts[] = { 2u, 4u, 8u }; const deUint32 layerCounts[] = { 1u, 3u, 6u }; const deUint32 resolveLevels[] = { 2u, 3u, 4u }; tcu::TestContext& testCtx (group->getTestContext()); for (size_t layerCountNdx = 0; layerCountNdx < DE_LENGTH_OF_ARRAY(layerCounts); layerCountNdx++) { const deUint32 layerCount (layerCounts[layerCountNdx]); const std::string layerGroupName ("layers_" + de::toString(layerCount)); de::MovePtr layerGroup (new tcu::TestCaseGroup(testCtx, layerGroupName.c_str(), layerGroupName.c_str())); for (size_t formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(formats); formatNdx++) { const VkFormat format (formats[formatNdx]); const std::string formatName (formatToName(format)); de::MovePtr formatGroup (new tcu::TestCaseGroup(testCtx, formatName.c_str(), formatName.c_str())); for (size_t sampleCountNdx = 0; sampleCountNdx < DE_LENGTH_OF_ARRAY(sampleCounts); sampleCountNdx++) { const deUint32 sampleCount(sampleCounts[sampleCountNdx]); // Skip this test as it is rather slow if (layerCount == 6 && sampleCount == 8) continue; std::string testName ("samples_" + de::toString(sampleCount)); const TestConfig testConfig = { RESOLVE, format, sampleCount, layerCount, 4u, 32u, 32u, renderingType }; formatGroup->addChild(new InstanceFactory1WithSupport, Programs>(testCtx, tcu::NODETYPE_SELF_VALIDATE, testName.c_str(), testName.c_str(), testConfig, typename FunctionSupport1::Args(checkSupport, testConfig))); for (deUint32 resolveLevel : resolveLevels) { const TestConfig2 testConfig2(testConfig, resolveLevel); std::string resolveLevelTestNameStr(testName + "_resolve_level_" + de::toString(resolveLevel)); const char* resolveLevelTestName = resolveLevelTestNameStr.c_str(); formatGroup->addChild(new InstanceFactory1WithSupport, Programs>(testCtx, tcu::NODETYPE_SELF_VALIDATE, resolveLevelTestName, resolveLevelTestName, testConfig2, typename FunctionSupport1::Args(checkSupport, testConfig2))); } // MaxAttachmenstsRenderPassTest is ment to test extreme cases where applications might consume all available on-chip // memory. This is achieved by using maxColorAttachments attachments and two subpasses, but during test creation we // dont know what is the maximal number of attachments (spirv tools are not available on all platforms) so we cant // construct shaders during test execution. To be able to test this we need to execute tests for all available // numbers of attachments despite the fact that we are only interested in the maximal number; test construction code // assumes that the number of attachments is power of two if ((renderingType != RENDERING_TYPE_DYNAMIC_RENDERING) && (layerCount == 1)) { for (deUint32 power = 2; power < 5; ++power) { deUint32 attachmentCount = 1 << power; std::string maxAttName = "max_attachments_" + de::toString(attachmentCount) + "_" + testName; TestConfig maxAttachmentsTestConfig = testConfig; maxAttachmentsTestConfig.testType = MAX_ATTACHMENTS; maxAttachmentsTestConfig.attachmentCount = attachmentCount; formatGroup->addChild(new InstanceFactory1WithSupport, Programs>(testCtx, tcu::NODETYPE_SELF_VALIDATE, maxAttName.c_str(), maxAttName.c_str(), maxAttachmentsTestConfig, typename FunctionSupport1::Args(checkSupport, maxAttachmentsTestConfig))); } { std::string compatibilityTestName = "compatibility_" + testName; TestConfig compatibilityTestConfig = testConfig; compatibilityTestConfig.testType = COMPATIBILITY; compatibilityTestConfig.attachmentCount = 1; formatGroup->addChild(new InstanceFactory1WithSupport, Programs>(testCtx, tcu::NODETYPE_SELF_VALIDATE, compatibilityTestName.c_str(), compatibilityTestName.c_str(), compatibilityTestConfig, typename FunctionSupport1::Args(checkSupport, compatibilityTestConfig))); } } } if (layerCount == 1) group->addChild(formatGroup.release()); else layerGroup->addChild(formatGroup.release()); } if (layerCount != 1) group->addChild(layerGroup.release()); } } } // anonymous tcu::TestCaseGroup* createRenderPassMultisampleResolveTests (tcu::TestContext& testCtx) { return createTestGroup(testCtx, "multisample_resolve", "Multisample render pass resolve tests", initTests, RENDERING_TYPE_RENDERPASS_LEGACY); } tcu::TestCaseGroup* createRenderPass2MultisampleResolveTests (tcu::TestContext& testCtx) { return createTestGroup(testCtx, "multisample_resolve", "Multisample render pass resolve tests", initTests, RENDERING_TYPE_RENDERPASS2); } tcu::TestCaseGroup* createDynamicRenderingMultisampleResolveTests (tcu::TestContext& testCtx) { return createTestGroup(testCtx, "multisample_resolve", "Multisample dynamic rendering resolve tests", initTests, RENDERING_TYPE_DYNAMIC_RENDERING); } } // vkt