--- /dev/null
- vk.cmdBeginRenderingKHR(commandBuffer, &renderingInfo);
+ /*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2021 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_KHR_fragment_shading_rate
+ *//*--------------------------------------------------------------------*/
+
+ #include "vktAttachmentRateTests.hpp"
+
+ #include "vkBufferWithMemory.hpp"
+ #include "vkImageWithMemory.hpp"
+ #include "vkQueryUtil.hpp"
+ #include "vkBarrierUtil.hpp"
+ #include "vkCmdUtil.hpp"
+ #include "vkTypeUtil.hpp"
+ #include "vkObjUtil.hpp"
+ #include "vkImageUtil.hpp"
+ #include "vkQueryUtil.hpp"
+ #include "vkPlatform.hpp"
+ #include "vkBuilderUtil.hpp"
+
+ #include "vktTestGroupUtil.hpp"
+ #include "vktTestCase.hpp"
+ #include "vktCustomInstancesDevices.hpp"
+
+ #include "deDefs.h"
+ #include "deMath.h"
+ #include "deSharedPtr.hpp"
+ #include "deString.h"
+ #include "deSTLUtil.hpp"
+
+ #include "tcuTestCase.hpp"
+ #include "tcuTestLog.hpp"
+ #include "tcuTextureUtil.hpp"
+ #include "tcuStringTemplate.hpp"
+
+ #include <string>
+ #include <vector>
+ #include <limits>
+ #include <map>
+
+ namespace vkt
+ {
+ namespace FragmentShadingRate
+ {
+ namespace
+ {
+
+ using namespace vk;
+
+ // flag used to test TM_SETUP_RATE_WITH_ATOMICS_IN_COMPUTE_SHADER;
+ // when it is 1 instead of using atomic operations to fill image
+ // plain store will be used as it is always supported
+ #define DEBUG_USE_STORE_INSTEAD_OF_ATOMICS 0
+
+ enum TestMode
+ {
+ TM_SETUP_RATE_WITH_ATOMICS_IN_COMPUTE_SHADER = 0,
+ TM_SETUP_RATE_WITH_FRAGMENT_SHADER,
+ TM_SETUP_RATE_WITH_COPYING_FROM_OTHER_IMAGE,
+ TM_SETUP_RATE_WITH_COPYING_FROM_EXCLUSIVE_IMAGE_USING_TRANSFER_QUEUE,
+ TM_SETUP_RATE_WITH_COPYING_FROM_CONCURENT_IMAGE_USING_TRANSFER_QUEUE,
+ TM_SETUP_RATE_WITH_LINEAR_TILED_IMAGE,
+
+ TM_TWO_SUBPASS
+ };
+
+ struct TestParams
+ {
+ TestMode mode;
+
+ VkFormat srFormat;
+ VkExtent2D srRate;
+
+ bool useDynamicRendering;
+ bool useImagelessFramebuffer;
+ };
+
+ deUint32 calculateRate(deUint32 rateWidth, deUint32 rateHeight)
+ {
+ return (deCtz32(rateWidth) << 2u) | deCtz32(rateHeight);
+ }
+
+ class AttachmentRateInstance : public TestInstance
+ {
+ public:
+ AttachmentRateInstance (Context& context, const de::SharedPtr<TestParams> params);
+ tcu::TestStatus iterate (void);
+
+ private:
+
+ // Helper structure used by buildFramebuffer method.
+ // It is used to build regular or imageless framebuffer.
+ struct FBAttachmentInfo
+ {
+ VkFormat format;
+ VkImageUsageFlags usage;
+ deUint32 width;
+ deUint32 height;
+ VkImageView view;
+ };
+
+ private:
+
+ de::MovePtr<ImageWithMemory> buildImageWithMemory (VkFormat format,
+ deUint32 width,
+ deUint32 height,
+ VkImageUsageFlags usage,
+ VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL,
+ std::vector<deUint32> queueFamilies = std::vector<deUint32>());
+ de::MovePtr<BufferWithMemory> buildBufferWithMemory (deUint32 size,
+ VkBufferUsageFlags usage);
+ Move<VkImageView> buildImageView (VkFormat format,
+ VkImage image);
+
+ void buildColorBufferObjects (deUint32 cbIindex,
+ VkImageUsageFlags cbUsage);
+ void buildShadingRateObjects (deUint32 srIndex,
+ deUint32 width,
+ deUint32 height,
+ VkImageUsageFlags srUsage,
+ VkImageTiling srTiling = VK_IMAGE_TILING_OPTIMAL);
+ void buildCounterBufferObjects ();
+
+ Move<VkRenderPass> buildRenderPass (VkFormat cbFormat,
+ deUint32 sr1TileWidth = 0,
+ deUint32 sr1TileHeight = 0,
+ deUint32 sr2TileWidth = 0,
+ deUint32 sr2TileHeight = 0) const;
+ Move<VkFramebuffer> buildFramebuffer (VkRenderPass renderPass,
+ const std::vector<FBAttachmentInfo>& attachmentInfo) const;
+ Move<VkPipelineLayout> buildPipelineLayout (const VkDescriptorSetLayout* setLayouts = DE_NULL) const;
+ Move<VkPipeline> buildGraphicsPipeline (deUint32 subpass,
+ VkRenderPass renderPass,
+ VkPipelineLayout layout,
+ VkShaderModule vertShader,
+ VkShaderModule fragShader,
+ bool useShadingRate = VK_TRUE) const;
+ Move<VkPipeline> buildComputePipeline (VkShaderModule compShader,
+ VkPipelineLayout pipelineLayout) const;
+ VkDescriptorSetAllocateInfo makeDescriptorSetAllocInfo (VkDescriptorPool descriptorPool,
+ const VkDescriptorSetLayout* pSetLayouts) const;
+
+ void startRendering (const VkCommandBuffer commandBuffer,
+ const VkRenderPass renderPass,
+ const VkFramebuffer framebuffer,
+ const VkRect2D& renderArea,
+ const std::vector<FBAttachmentInfo>& attachmentInfo) const;
+ void finishRendering (const VkCommandBuffer commandBuffer) const;
+
+ bool verifyUsingAtomicChecks (deUint32 tileWidth,
+ deUint32 tileHeight,
+ deUint32 rateWidth,
+ deUint32 rateHeight,
+ deUint32* outBufferPtr) const;
+
+ bool runComputeShaderMode (void);
+ bool runFragmentShaderMode (void);
+ bool runCopyMode (void);
+ bool runCopyModeOnTransferQueue (void);
+ bool runFillLinearTiledImage (void);
+ bool runTwoSubpassMode (void);
+
+ private:
+
+ // When custom device is used then m_device and m_allocator are used in all
+ // helper methods. Otherwise default device and allocator from context are used.
+ // Those objects are used only by tests from runCopyModeOnTransferQueue.
+ Move<VkDevice> m_device;
+ de::MovePtr<Allocator> m_allocator;
+
+ const de::SharedPtr<TestParams> m_params;
+ const deUint32 m_cbWidth;
+ const deUint32 m_cbHeight;
+ VkFormat m_cbFormat;
+ VkImageUsageFlags m_cbUsage;
+ VkImageUsageFlags m_srUsage;
+
+ // structures commonly used by most of tests
+ const VkImageSubresourceLayers m_defaultImageSubresourceLayers;
+ const VkImageSubresourceRange m_defaultImageSubresourceRange;
+ const VkBufferImageCopy m_defaultBufferImageCopy;
+
+ // objects commonly used by most of tests
+ de::MovePtr<ImageWithMemory> m_cbImage[2];
+ Move<VkImageView> m_cbImageView[2];
+ de::MovePtr<BufferWithMemory> m_cbReadBuffer[2];
+
+ de::MovePtr<ImageWithMemory> m_srImage[2];
+ Move<VkImageView> m_srImageView[2];
+
+ Move<VkDescriptorSetLayout> m_counterBufferDescriptorSetLayout;
+ Move<VkDescriptorPool> m_counterBufferDescriptorPool;
+ Move<VkDescriptorSet> m_counterBufferDescriptorSet;
+ de::MovePtr<BufferWithMemory> m_counterBuffer;
+
+ // properties commonly used by most of tests
+ VkExtent2D m_minTileSize;
+ VkExtent2D m_maxTileSize;
+ deUint32 m_maxAspectRatio;
+ };
+
+ AttachmentRateInstance::AttachmentRateInstance(Context& context, const de::SharedPtr<TestParams> params)
+ : vkt::TestInstance (context)
+ , m_params (params)
+ , m_cbWidth (60)
+ , m_cbHeight (60)
+ , m_cbFormat (VK_FORMAT_R32G32B32A32_UINT)
+ , m_cbUsage (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT)
+ , m_srUsage (VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR | VK_IMAGE_USAGE_TRANSFER_DST_BIT)
+ , m_defaultImageSubresourceLayers (makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u))
+ , m_defaultImageSubresourceRange (makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0, 1u))
+ , m_defaultBufferImageCopy (makeBufferImageCopy({ m_cbWidth, m_cbHeight, 1u }, m_defaultImageSubresourceLayers))
+ {
+ // prepare data needed to calculate tile sizes
+ const auto& srProperties = m_context.getFragmentShadingRateProperties();
+ m_minTileSize = srProperties.minFragmentShadingRateAttachmentTexelSize;
+ m_maxTileSize = srProperties.maxFragmentShadingRateAttachmentTexelSize;
+ m_maxAspectRatio = srProperties.maxFragmentShadingRateAttachmentTexelSizeAspectRatio;
+ }
+
+ de::MovePtr<ImageWithMemory> AttachmentRateInstance::buildImageWithMemory (VkFormat format,
+ deUint32 width,
+ deUint32 height,
+ VkImageUsageFlags usage,
+ VkImageTiling tiling,
+ std::vector<deUint32> queueFamilies)
+ {
+ VkImageCreateInfo imageCreateInfo
+ {
+ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkImageCreateFlags)0u, // VkImageCreateFlags flags;
+ VK_IMAGE_TYPE_2D, // VkImageType imageType;
+ format, // VkFormat format;
+ {
+ width, // deUint32 width;
+ height, // deUint32 height;
+ 1u // deUint32 depth;
+ }, // VkExtent3D extent;
+ 1u, // deUint32 mipLevels;
+ 1u, // deUint32 arrayLayers;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ tiling, // VkImageTiling tiling;
+ usage, // VkImageUsageFlags usage;
+ queueFamilies.empty() ?
+ VK_SHARING_MODE_EXCLUSIVE :
+ VK_SHARING_MODE_CONCURRENT, // VkSharingMode sharingMode;
+ (deUint32)queueFamilies.size(), // deUint32 queueFamilyIndexCount;
+ queueFamilies.data(), // const deUint32* pQueueFamilyIndices;
+ VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout initialLayout;
+ };
+
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ VkDevice device = m_device.get() ? *m_device : m_context.getDevice();
+ vk::Allocator& allocator = m_allocator.get() ? *m_allocator : m_context.getDefaultAllocator();
+ vk::MemoryRequirement memoryRequirement = (tiling == VK_IMAGE_TILING_LINEAR) ? MemoryRequirement::HostVisible : MemoryRequirement::Any;
+ return de::MovePtr<ImageWithMemory>(new ImageWithMemory(vk, device, allocator, imageCreateInfo, memoryRequirement));
+ }
+
+ de::MovePtr<BufferWithMemory> AttachmentRateInstance::buildBufferWithMemory(deUint32 size, VkBufferUsageFlags usage)
+ {
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ VkDevice device = m_device.get() ? *m_device : m_context.getDevice();
+ vk::Allocator& allocator = m_allocator.get() ? *m_allocator : m_context.getDefaultAllocator();
+ const VkBufferCreateInfo readBufferInfo = makeBufferCreateInfo(size, usage);
+
+ return de::MovePtr<BufferWithMemory>(new BufferWithMemory(vk, device, allocator, readBufferInfo, MemoryRequirement::HostVisible));
+ }
+
+ Move<VkImageView> AttachmentRateInstance::buildImageView (VkFormat format, VkImage image)
+ {
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ VkDevice device = m_device.get() ? *m_device : m_context.getDevice();
+ VkImageSubresourceRange subresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+
+ return makeImageView(vk, device, image, VK_IMAGE_VIEW_TYPE_2D, format, subresourceRange);
+ };
+
+ void AttachmentRateInstance::buildColorBufferObjects(deUint32 cbIndex, VkImageUsageFlags cbUsage)
+ {
+ DE_ASSERT(cbIndex < 2);
+
+ m_cbImage[cbIndex] = buildImageWithMemory(m_cbFormat, m_cbWidth, m_cbHeight, cbUsage);
+ m_cbImageView[cbIndex] = buildImageView(m_cbFormat, m_cbImage[cbIndex]->get());
+ m_cbReadBuffer[cbIndex] = buildBufferWithMemory(m_cbWidth * m_cbHeight * deUint32(sizeof(int)) * 4u, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+ }
+
+ void AttachmentRateInstance::buildShadingRateObjects(deUint32 srIndex, deUint32 width, deUint32 height, VkImageUsageFlags srUsage, VkImageTiling srTiling)
+ {
+ DE_ASSERT(srIndex < 2);
+
+ m_srImage[srIndex] = buildImageWithMemory(m_params->srFormat, width, height, srUsage, srTiling);
+ m_srImageView[srIndex] = buildImageView(m_params->srFormat, m_srImage[srIndex]->get());
+ }
+
+ void AttachmentRateInstance::buildCounterBufferObjects()
+ {
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ VkDevice device = m_device.get() ? *m_device : m_context.getDevice();
+
+ m_counterBufferDescriptorPool = DescriptorPoolBuilder()
+ .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1u)
+ .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
+ m_counterBufferDescriptorSetLayout = DescriptorSetLayoutBuilder()
+ .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT)
+ .build(vk, device);
+
+ const VkDescriptorSetAllocateInfo descriptorSetAllocInfo = makeDescriptorSetAllocInfo(*m_counterBufferDescriptorPool,
+ &(*m_counterBufferDescriptorSetLayout));
+ m_counterBufferDescriptorSet = allocateDescriptorSet(vk, device, &descriptorSetAllocInfo);
+
+ // create ssbo buffer for atomic counter
+ deUint32 ssboSize = deUint32(sizeof(deUint32));
+ m_counterBuffer = buildBufferWithMemory(ssboSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
+
+ const VkDescriptorBufferInfo descriptorInfo = makeDescriptorBufferInfo(**m_counterBuffer, 0, ssboSize);
+ DescriptorSetUpdateBuilder()
+ .writeSingle(*m_counterBufferDescriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorInfo)
+ .update(vk, device);
+
+ // reset counter
+ *((deUint32*)m_counterBuffer->getAllocation().getHostPtr()) = 0u;
+ flushAlloc(vk, device, m_counterBuffer->getAllocation());
+ }
+
+ Move<VkRenderPass> AttachmentRateInstance::buildRenderPass(VkFormat cbFormat,
+ deUint32 sr0TileWidth, deUint32 sr0TileHeight,
+ deUint32 sr1TileWidth, deUint32 sr1TileHeight) const
+ {
+ if (m_params->useDynamicRendering)
+ return Move<VkRenderPass>();
+
+ const bool useShadingRate0 = (sr0TileWidth * sr0TileHeight > 0);
+ const bool useShadingRate1 = (sr1TileWidth * sr1TileHeight > 0);
+
+ deUint32 attachmentCount = 1;
+ const deUint32 subpassCount = 1 + useShadingRate1;
+
+ std::vector<VkAttachmentReference2> colorAttachmentReferences(subpassCount, {
+ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0, // uint32_t attachment;
+ VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout layout;
+ 0, // VkImageAspectFlags aspectMask;
+ });
+
+ std::vector<VkAttachmentReference2> fragmentShadingRateAttachments(subpassCount, {
+ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 1, // uint32_t attachment;
+ VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR, // VkImageLayout layout;
+ 0, // VkImageAspectFlags aspectMask;
+ });
+
+ std::vector<VkFragmentShadingRateAttachmentInfoKHR> shadingRateAttachmentInfos(subpassCount, {
+ VK_STRUCTURE_TYPE_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ &fragmentShadingRateAttachments[0], // const VkAttachmentReference2* pFragmentShadingRateAttachment;
+ { sr0TileWidth, sr0TileHeight }, // VkExtent2D shadingRateAttachmentTexelSize;
+ });
+
+ std::vector<VkSubpassDescription2> subpassDescriptions(subpassCount, {
+ VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (vk::VkSubpassDescriptionFlags)0, // VkSubpassDescriptionFlags flags;
+ vk::VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
+ 0u, // uint32_t viewMask;
+ 0u, // uint32_t inputAttachmentCount;
+ DE_NULL, // const VkAttachmentReference2* pInputAttachments;
+ 1, // uint32_t colorAttachmentCount;
+ &colorAttachmentReferences[0], // const VkAttachmentReference2* pColorAttachments;
+ DE_NULL, // const VkAttachmentReference2* pResolveAttachments;
+ DE_NULL, // const VkAttachmentReference2* pDepthStencilAttachment;
+ 0u, // uint32_t preserveAttachmentCount;
+ DE_NULL, // const uint32_t* pPreserveAttachments;
+ });
+
+ std::vector<VkAttachmentDescription2> attachmentDescriptions(2 * subpassCount, {
+ VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkAttachmentDescriptionFlags)0u, // VkAttachmentDescriptionFlags flags;
+ cbFormat, // VkFormat format;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp;
+ VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp;
+ VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
+ VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
+ VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout initialLayout;
+ VK_IMAGE_LAYOUT_GENERAL // VkImageLayout finalLayout;
+ });
+
+ if (useShadingRate0)
+ {
+ attachmentCount = 2;
+ subpassDescriptions[0].pNext = &shadingRateAttachmentInfos[0];
+ attachmentDescriptions[1].format = m_params->srFormat;
+ attachmentDescriptions[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+ attachmentDescriptions[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attachmentDescriptions[1].initialLayout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
+ }
+
+ if (useShadingRate1)
+ {
+ attachmentCount = 4;
+ colorAttachmentReferences[1].attachment = 2;
+ fragmentShadingRateAttachments[1].attachment = 3;
+ shadingRateAttachmentInfos[1].pFragmentShadingRateAttachment = &fragmentShadingRateAttachments[1];
+ shadingRateAttachmentInfos[1].shadingRateAttachmentTexelSize = { sr1TileWidth, sr1TileHeight };
+ subpassDescriptions[1].pNext = &shadingRateAttachmentInfos[1];
+ subpassDescriptions[1].pColorAttachments = &colorAttachmentReferences[1];
+
+ attachmentDescriptions[3].format = m_params->srFormat;
+ attachmentDescriptions[3].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+ attachmentDescriptions[3].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attachmentDescriptions[3].initialLayout = VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
+ }
+
+ const VkRenderPassCreateInfo2 renderPassParams
+ {
+ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (vk::VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags;
+ attachmentCount, // uint32_t attachmentCount;
+ attachmentDescriptions.data(), // const VkAttachmentDescription2* pAttachments;
+ subpassCount, // uint32_t subpassCount;
+ subpassDescriptions.data(), // const VkSubpassDescription2* pSubpasses;
+ 0u, // uint32_t dependencyCount;
+ DE_NULL, // const VkSubpassDependency2* pDependencies;
+ 0u, // uint32_t correlatedViewMaskCount;
+ DE_NULL, // const uint32_t* pCorrelatedViewMasks;
+ };
+
+ VkDevice device = m_device.get() ? *m_device : m_context.getDevice();
+ return createRenderPass2(m_context.getDeviceInterface(), device, &renderPassParams);
+ }
+
+ Move<VkFramebuffer> AttachmentRateInstance::buildFramebuffer(VkRenderPass renderPass, const std::vector<FBAttachmentInfo>& attachmentInfo) const
+ {
+ if (m_params->useDynamicRendering)
+ return Move<VkFramebuffer>();
+
+ VkDevice device = m_device.get() ? *m_device : m_context.getDevice();
+
+ VkFramebufferCreateInfo framebufferParams
+ {
+ vk::VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (vk::VkFramebufferCreateFlags)0u, // VkFramebufferCreateFlags flags;
+ renderPass, // VkRenderPass renderPass;
+ (deUint32)attachmentInfo.size(), // uint32_t attachmentCount;
+ DE_NULL, // const VkImageView* pAttachments;
+ attachmentInfo[0].width, // uint32_t width;
+ attachmentInfo[0].height, // uint32_t height;
+ 1u, // uint32_t layers;
+ };
+
+ if (m_params->useImagelessFramebuffer)
+ {
+ std::vector<VkFramebufferAttachmentImageInfo> framebufferAttachmentImageInfo(attachmentInfo.size(),
+ {
+ VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENT_IMAGE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkImageCreateFlags)0u, // VkImageCreateFlags flags;
+ 0u, // VkImageUsageFlags usage;
+ 0u, // deUint32 width;
+ 0u, // deUint32 height;
+ 1u, // deUint32 layerCount;
+ 1u, // deUint32 viewFormatCount;
+ DE_NULL // const VkFormat* pViewFormats;
+ }
+ );
+
+ for (deUint32 i = 0; i < (deUint32)attachmentInfo.size(); ++i)
+ {
+ const auto& src = attachmentInfo[i];
+ auto& dst = framebufferAttachmentImageInfo[i];
+
+ dst.usage = src.usage;
+ dst.width = src.width;
+ dst.height = src.height;
+ dst.pViewFormats = &src.format;
+ }
+
+ VkFramebufferAttachmentsCreateInfo framebufferAttachmentsCreateInfo
+ {
+ VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (deUint32)framebufferAttachmentImageInfo.size(), // deUint32 attachmentImageInfoCount;
+ framebufferAttachmentImageInfo.data() // const VkFramebufferAttachmentImageInfo* pAttachmentImageInfos;
+ };
+
+ framebufferParams.pNext = &framebufferAttachmentsCreateInfo;
+ framebufferParams.flags = VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT;
+
+ return createFramebuffer(m_context.getDeviceInterface(), device, &framebufferParams);
+ }
+
+ // create array containing just attachment views
+ std::vector<VkImageView> attachments(attachmentInfo.size(), 0);
+ for (deUint32 i = 0; i < (deUint32)attachmentInfo.size(); ++i)
+ attachments[i] = attachmentInfo[i].view;
+
+ framebufferParams.pAttachments = attachments.data();
+
+ return createFramebuffer(m_context.getDeviceInterface(), device, &framebufferParams);
+ }
+
+ Move<VkPipelineLayout> AttachmentRateInstance::buildPipelineLayout(const VkDescriptorSetLayout* setLayouts) const
+ {
+ const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineLayoutCreateFlags)0, // VkPipelineLayoutCreateFlags flags;
+ (setLayouts != DE_NULL), // uint32_t setLayoutCount;
+ setLayouts, // const VkDescriptorSetLayout* pSetLayouts;
+ 0u, // uint32_t pushConstantRangeCount;
+ DE_NULL, // const VkPushConstantRange* pPushConstantRanges;
+ };
+
+ VkDevice device = m_device.get() ? *m_device : m_context.getDevice();
+ return createPipelineLayout(m_context.getDeviceInterface(), device, &pipelineLayoutCreateInfo, NULL);
+ }
+
+ Move<VkPipeline> AttachmentRateInstance::buildGraphicsPipeline(deUint32 subpass, VkRenderPass renderPass, VkPipelineLayout pipelineLayout,
+ VkShaderModule vertShader, VkShaderModule fragShader, bool useShadingRate) const
+ {
+ std::vector<VkPipelineShaderStageCreateInfo> pipelineShaderStageParams(2,
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType
+ DE_NULL, // const void* pNext
+ 0u, // VkPipelineShaderStageCreateFlags flags
+ VK_SHADER_STAGE_VERTEX_BIT, // VkShaderStageFlagBits stage
+ vertShader, // VkShaderModule module
+ "main", // const char* pName
+ DE_NULL // const VkSpecializationInfo* pSpecializationInfo
+ });
+
+ pipelineShaderStageParams[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+ pipelineShaderStageParams[1].module = fragShader;
+
+ const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType
+ DE_NULL, // const void* pNext
+ (VkPipelineVertexInputStateCreateFlags)0, // VkPipelineVertexInputStateCreateFlags flags
+ 0u, // deUint32 vertexBindingDescriptionCount
+ DE_NULL, // const VkVertexInputBindingDescription* pVertexBindingDescriptions
+ 0u, // deUint32 vertexAttributeDescriptionCount
+ DE_NULL // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions
+ };
+
+ const VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType
+ DE_NULL, // const void* pNext
+ 0u, // VkPipelineInputAssemblyStateCreateFlags flags
+ VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, // VkPrimitiveTopology topology
+ VK_FALSE // VkBool32 primitiveRestartEnable
+ };
+
+ tcu::UVec2 size (m_cbWidth, m_cbHeight);
+ VkViewport viewport = makeViewport (size);
+ VkRect2D scissor = makeRect2D (size);
+
+ const VkPipelineViewportStateCreateInfo viewportStateCreateInfo
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType sType
+ DE_NULL, // const void* pNext
+ (VkPipelineViewportStateCreateFlags)0, // VkPipelineViewportStateCreateFlags flags
+ 1u, // deUint32 viewportCount
+ &viewport, // const VkViewport* pViewports
+ 1u, // deUint32 scissorCount
+ &scissor // const VkRect2D* pScissors
+ };
+
+ const VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType
+ DE_NULL, // const void* pNext
+ 0u, // 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
+ };
+
+ const VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType
+ DE_NULL, // const void* pNext
+ 0u, // VkPipelineMultisampleStateCreateFlags flags
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits rasterizationSamples
+ VK_FALSE, // VkBool32 sampleShadingEnable
+ 1.0f, // float minSampleShading
+ DE_NULL, // const VkSampleMask* pSampleMask
+ VK_FALSE, // VkBool32 alphaToCoverageEnable
+ VK_FALSE // VkBool32 alphaToOneEnable
+ };
+
+ const VkStencilOpState stencilOpState
+ {
+ VK_STENCIL_OP_KEEP, // VkStencilOp failOp
+ VK_STENCIL_OP_KEEP, // VkStencilOp passOp
+ VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp
+ VK_COMPARE_OP_NEVER, // VkCompareOp compareOp
+ 0, // deUint32 compareMask
+ 0, // deUint32 writeMask
+ 0 // deUint32 reference
+ };
+
+ const VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType
+ DE_NULL, // const void* pNext
+ 0u, // VkPipelineDepthStencilStateCreateFlags flags
+ VK_FALSE, // VkBool32 depthTestEnable
+ VK_FALSE, // VkBool32 depthWriteEnable
+ VK_COMPARE_OP_LESS_OR_EQUAL, // VkCompareOp depthCompareOp
+ VK_FALSE, // VkBool32 depthBoundsTestEnable
+ VK_FALSE, // VkBool32 stencilTestEnable
+ stencilOpState, // VkStencilOpState front
+ stencilOpState, // VkStencilOpState back
+ 0.0f, // float minDepthBounds
+ 1.0f, // float maxDepthBounds
+ };
+
+ const VkPipelineColorBlendAttachmentState colorBlendAttachmentState
+ {
+ VK_FALSE, // VkBool32 blendEnable
+ VK_BLEND_FACTOR_ZERO, // VkBlendFactor srcColorBlendFactor
+ VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstColorBlendFactor
+ VK_BLEND_OP_ADD, // VkBlendOp colorBlendOp
+ VK_BLEND_FACTOR_ZERO, // VkBlendFactor srcAlphaBlendFactor
+ VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstAlphaBlendFactor
+ VK_BLEND_OP_ADD, // VkBlendOp alphaBlendOp
+ VK_COLOR_COMPONENT_R_BIT // VkColorComponentFlags colorWriteMask
+ | VK_COLOR_COMPONENT_G_BIT
+ | VK_COLOR_COMPONENT_B_BIT
+ | VK_COLOR_COMPONENT_A_BIT
+ };
+
+ const VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType
+ DE_NULL, // const void* pNext
+ 0u, // VkPipelineColorBlendStateCreateFlags flags
+ VK_FALSE, // VkBool32 logicOpEnable
+ VK_LOGIC_OP_CLEAR, // VkLogicOp logicOp
+ 1u, // deUint32 attachmentCount
+ &colorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments
+ { 0.0f, 0.0f, 0.0f, 0.0f } // float blendConstants[4]
+ };
+
+ const VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType
+ DE_NULL, // const void* pNext
+ 0u, // VkPipelineDynamicStateCreateFlags flags
+ 0u, // deUint32 dynamicStateCount
+ DE_NULL // const VkDynamicState* pDynamicStates
+ };
+
+ VkPipelineFragmentShadingRateStateCreateInfoKHR shadingRateStateCreateInfo
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ { 1, 1 }, // VkExtent2D fragmentSize;
+ { VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR,
+ VK_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_KHR } // VkFragmentShadingRateCombinerOpKHR combinerOps[2];
+ };
+
+ void* pNext = useShadingRate ? &shadingRateStateCreateInfo : DE_NULL;
+ VkPipelineRenderingCreateInfoKHR renderingCreateInfo
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR,
+ pNext,
+ 0u,
+ 1u,
+ &m_cbFormat,
+ VK_FORMAT_UNDEFINED,
+ VK_FORMAT_UNDEFINED
+ };
+
+ if (m_params->useDynamicRendering)
+ pNext = &renderingCreateInfo;
+
+ const VkGraphicsPipelineCreateInfo pipelineCreateInfo
+ {
+ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, // VkStructureType sType
+ pNext, // const void* pNext
+ 0u, // VkPipelineCreateFlags flags
+ (deUint32)pipelineShaderStageParams.size(), // deUint32 stageCount
+ &pipelineShaderStageParams[0], // const VkPipelineShaderStageCreateInfo* pStages
+ &vertexInputStateCreateInfo, // const VkPipelineVertexInputStateCreateInfo* pVertexInputState
+ &inputAssemblyStateCreateInfo, // const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState
+ DE_NULL, // const VkPipelineTessellationStateCreateInfo* pTessellationState
+ &viewportStateCreateInfo, // const VkPipelineViewportStateCreateInfo* pViewportState
+ &rasterizationStateCreateInfo, // const VkPipelineRasterizationStateCreateInfo* pRasterizationState
+ &multisampleStateCreateInfo, // const VkPipelineMultisampleStateCreateInfo* pMultisampleState
+ &depthStencilStateCreateInfo, // const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState
+ &colorBlendStateCreateInfo, // const VkPipelineColorBlendStateCreateInfo* pColorBlendState
+ &dynamicStateCreateInfo, // const VkPipelineDynamicStateCreateInfo* pDynamicState
+ pipelineLayout, // VkPipelineLayout layout
+ renderPass, // VkRenderPass renderPass
+ subpass, // deUint32 subpass
+ DE_NULL, // VkPipeline basePipelineHandle
+ 0 // deInt32 basePipelineIndex;
+ };
+
+ VkDevice device = m_device.get() ? *m_device : m_context.getDevice();
+ return createGraphicsPipeline(m_context.getDeviceInterface(), device, DE_NULL, &pipelineCreateInfo);
+ }
+
+ Move<VkPipeline> AttachmentRateInstance::buildComputePipeline(VkShaderModule compShader, VkPipelineLayout pipelineLayout) const
+ {
+ const VkPipelineShaderStageCreateInfo stageCreateInfo
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkPipelineShaderStageCreateFlags flags;
+ VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage;
+ compShader, // VkShaderModule module;
+ "main", // const char* pName;
+ DE_NULL // const VkSpecializationInfo* pSpecializationInfo;
+ };
+
+ const VkComputePipelineCreateInfo createInfo
+ {
+ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // VkPipelineCreateFlags flags;
+ stageCreateInfo, // VkPipelineShaderStageCreateInfo stage;
+ pipelineLayout, // VkPipelineLayout layout;
+ (VkPipeline)0, // VkPipeline basePipelineHandle;
+ 0u, // int32_t basePipelineIndex;
+ };
+
+ VkDevice device = m_device.get() ? *m_device : m_context.getDevice();
+ return createComputePipeline(m_context.getDeviceInterface(), device, (vk::VkPipelineCache)0u, &createInfo);
+ }
+
+ VkDescriptorSetAllocateInfo AttachmentRateInstance::makeDescriptorSetAllocInfo(VkDescriptorPool descriptorPool, const VkDescriptorSetLayout* pSetLayouts) const
+ {
+ return
+ {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ descriptorPool, // VkDescriptorPool descriptorPool;
+ 1u, // uint32_t setLayoutCount;
+ pSetLayouts, // const VkDescriptorSetLayout* pSetLayouts;
+ };
+ }
+
+ void AttachmentRateInstance::startRendering(const VkCommandBuffer commandBuffer,
+ const VkRenderPass renderPass,
+ const VkFramebuffer framebuffer,
+ const VkRect2D& renderArea,
+ const std::vector<FBAttachmentInfo>& attachmentInfo) const
+ {
+ const DeviceInterface& vk (m_context.getDeviceInterface());
+ std::vector<VkClearValue> clearColor (attachmentInfo.size(), makeClearValueColorU32(0, 0, 0, 0));
+
+ if (m_params->useDynamicRendering)
+ {
+ VkRenderingFragmentShadingRateAttachmentInfoKHR shadingRateAttachmentInfo
+ {
+ VK_STRUCTURE_TYPE_RENDERING_FRAGMENT_SHADING_RATE_ATTACHMENT_INFO_KHR, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ DE_NULL, // VkImageView imageView;
+ VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR, // VkImageLayout imageLayout;
+ { 0, 0 } // VkExtent2D shadingRateAttachmentTexelSize;
+ };
+
+ VkRenderingAttachmentInfoKHR colorAttachment
+ {
+ vk::VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ attachmentInfo[0].view, // VkImageView imageView;
+ VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout imageLayout;
+ VK_RESOLVE_MODE_NONE, // VkResolveModeFlagBits resolveMode;
+ DE_NULL, // VkImageView resolveImageView;
+ VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout resolveImageLayout;
+ VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp;
+ VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp;
+ clearColor[0] // VkClearValue clearValue;
+ };
+
+ VkRenderingInfoKHR renderingInfo
+ {
+ vk::VK_STRUCTURE_TYPE_RENDERING_INFO_KHR,
+ DE_NULL,
+ 0, // VkRenderingFlagsKHR flags;
+ renderArea, // VkRect2D renderArea;
+ 1u, // deUint32 layerCount;
+ 0u, // deUint32 viewMask;
+ 1u, // deUint32 colorAttachmentCount;
+ &colorAttachment, // const VkRenderingAttachmentInfoKHR* pColorAttachments;
+ DE_NULL, // const VkRenderingAttachmentInfoKHR* pDepthAttachment;
+ DE_NULL, // const VkRenderingAttachmentInfoKHR* pStencilAttachment;
+ };
+
+ // when shading rate is used it is defined as a second entry in attachmentInfo
+ if ((attachmentInfo.size() == 2) &&
+ (attachmentInfo[1].usage & VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR))
+ {
+ shadingRateAttachmentInfo.imageView = attachmentInfo[1].view;
+ shadingRateAttachmentInfo.shadingRateAttachmentTexelSize = { attachmentInfo[1].width, attachmentInfo[1].height };
+ renderingInfo.pNext = &shadingRateAttachmentInfo;
+ }
+
++ vk.cmdBeginRendering(commandBuffer, &renderingInfo);
+
+ return;
+ }
+
+ std::vector<VkImageView> attachments(attachmentInfo.size(), 0);
+ VkRenderPassAttachmentBeginInfo renderPassAttachmentBeginInfo;
+ void* pNext(DE_NULL);
+
+ if (m_params->useImagelessFramebuffer)
+ {
+ // create array containing attachment views
+ for (deUint32 i = 0; i < (deUint32)attachmentInfo.size(); ++i)
+ attachments[i] = attachmentInfo[i].view;
+
+ renderPassAttachmentBeginInfo =
+ {
+ VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (deUint32)attachments.size(), // deUint32 attachmentCount;
+ attachments.data() // const VkImageView* pAttachments;
+ };
+
+ pNext = &renderPassAttachmentBeginInfo;
+ }
+
+ beginRenderPass(vk, commandBuffer, renderPass, framebuffer, renderArea,
+ (deUint32)clearColor.size(), clearColor.data(), VK_SUBPASS_CONTENTS_INLINE, pNext);
+ }
+
+ void AttachmentRateInstance::finishRendering(const VkCommandBuffer commandBuffer) const
+ {
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+
+ if (m_params->useDynamicRendering)
+ endRendering(vk, commandBuffer);
+ else
+ endRenderPass(vk, commandBuffer);
+ }
+
+ tcu::TestStatus AttachmentRateInstance::iterate(void)
+ {
+ // instead of creating many classes that derive from large common class
+ // each test mode is defined in separate run* method, those methods
+ // then use same helper methods defined in this class
+
+ typedef bool (AttachmentRateInstance::*MethodPtr)();
+ const std::map<TestMode, MethodPtr> modeFuncMap
+ {
+ { TM_SETUP_RATE_WITH_ATOMICS_IN_COMPUTE_SHADER, &AttachmentRateInstance::runComputeShaderMode },
+ { TM_SETUP_RATE_WITH_FRAGMENT_SHADER, &AttachmentRateInstance::runFragmentShaderMode },
+ { TM_SETUP_RATE_WITH_COPYING_FROM_OTHER_IMAGE, &AttachmentRateInstance::runCopyMode },
+ { TM_SETUP_RATE_WITH_COPYING_FROM_EXCLUSIVE_IMAGE_USING_TRANSFER_QUEUE, &AttachmentRateInstance::runCopyModeOnTransferQueue },
+ { TM_SETUP_RATE_WITH_COPYING_FROM_CONCURENT_IMAGE_USING_TRANSFER_QUEUE, &AttachmentRateInstance::runCopyModeOnTransferQueue },
+ { TM_SETUP_RATE_WITH_LINEAR_TILED_IMAGE, &AttachmentRateInstance::runFillLinearTiledImage },
+ { TM_TWO_SUBPASS, &AttachmentRateInstance::runTwoSubpassMode },
+ };
+
+ if ((this->*modeFuncMap.at(m_params->mode))())
+ return tcu::TestStatus::pass("Pass");
+
+ return tcu::TestStatus::fail("Fail");
+ }
+
+ bool AttachmentRateInstance::verifyUsingAtomicChecks(deUint32 tileWidth, deUint32 tileHeight,
+ deUint32 rateWidth, deUint32 rateHeight,
+ deUint32* outBufferPtr) const
+ {
+ tcu::TestLog& log (m_context.getTestContext().getLog());
+ tcu::TextureLevel errorMaskStorage (tcu::TextureFormat(tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), m_cbWidth, m_cbHeight, 1u);
+ tcu::PixelBufferAccess errorMaskAccess (errorMaskStorage.getAccess());
+
+ deUint32 wrongFragments = 0;
+ const deUint32 fragmentsWithSameAtomicValueCount = rateWidth * rateHeight;
+
+ // map that uses atomic value as a kay and maps it to all fragments sharing same atomic
+ std::map<deUint32, std::vector<tcu::UVec2> > fragmentsWithSameAtomicValueMap;
+
+ // this method asumes that top and left edge of triangle are parallel to axes
+ // and we can store just single coordinate for those edges
+ deUint32 triangleLeftEdgeX = 0;
+ deUint32 triangleTopEdgeY = 0;
+
+ // this method assumes that greatest angle in the triangle points to the top-left corner of FB;
+ // this vector will then store fragments on the right edge of triangle; vector index represents y coordinate and value is x
+ std::vector<deUint32> fragmentsOnTheRightTriangleEdgeVect(m_cbHeight, 0);
+
+ tcu::clear(errorMaskAccess, tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0));
+
+ // loop over all fragments and validate the output
+ for (deUint32 cbFragmentY = 0; cbFragmentY < m_cbHeight; ++cbFragmentY)
+ for (deUint32 cbFragmentX = 0; cbFragmentX < m_cbWidth; ++cbFragmentX)
+ {
+ deUint32* fragmentColor = &outBufferPtr[4 * (cbFragmentY * m_cbWidth + cbFragmentX)];
+
+ // fragment not covered by primitive, skip it
+ if (fragmentColor[2] == 0)
+ continue;
+
+ // first fragment we hit will define top and left triangle edges
+ if ((triangleTopEdgeY + triangleLeftEdgeX) == 0)
+ {
+ triangleLeftEdgeX = cbFragmentX;
+ triangleTopEdgeY = cbFragmentY;
+ }
+
+ // constantly overwrite coordinate on right edge so that we are left with the farthest one
+ fragmentsOnTheRightTriangleEdgeVect[cbFragmentY] = cbFragmentX;
+
+ // make sure that fragment g and a components are 0
+ if ((fragmentColor[1] != 0) || (fragmentColor[3] != 0))
+ {
+ ++wrongFragments;
+ continue;
+ }
+
+ deUint32 rate = fragmentColor[0];
+ deUint32 fragmentRateX = 1 << ((rate / 4) & 3);
+ deUint32 fragmentRateY = 1 << (rate & 3);
+
+ // check if proper rate was used for fragment
+ if ((fragmentRateX != rateWidth) ||
+ (fragmentRateY != rateHeight))
+ {
+ ++wrongFragments;
+ errorMaskAccess.setPixel(tcu::Vec4(1.0f, 0.5f, 0.0f, 1.0f), cbFragmentX, cbFragmentY, 0u);
+ continue;
+ }
+
+ // mark correct fragments using few green shades so rates are visible
+ deUint32 atomicValue = fragmentColor[2];
+ errorMaskAccess.setPixel(tcu::Vec4(0.0f, 1.0f - float(atomicValue % 7) * 0.1f, 0.0f, 1.0f), cbFragmentX, cbFragmentY, 0u);
+
+ // find proper set in map and add value to it after doing verification with existing items
+ auto fragmentsSetMapIt = fragmentsWithSameAtomicValueMap.find(atomicValue);
+ if (fragmentsSetMapIt == fragmentsWithSameAtomicValueMap.end())
+ {
+ fragmentsWithSameAtomicValueMap[atomicValue] = { tcu::UVec2(cbFragmentX, cbFragmentY) };
+ fragmentsWithSameAtomicValueMap[atomicValue].reserve(fragmentsWithSameAtomicValueCount);
+ }
+ else
+ {
+ // make sure that fragments added to set are near the top-left fragment
+ auto& fragmentsSet = fragmentsSetMapIt->second;
+ if (((cbFragmentX - fragmentsSet[0].x()) > rateWidth) ||
+ ((cbFragmentY - fragmentsSet[0].y()) > rateHeight))
+ {
+ ++wrongFragments;
+ errorMaskAccess.setPixel(tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f), cbFragmentX, cbFragmentY, 0u);
+ }
+
+ fragmentsWithSameAtomicValueMap[atomicValue].emplace_back(cbFragmentX, cbFragmentY);
+ }
+ }
+
+ // check if there are no valid fragmenst at all
+ if ((triangleTopEdgeY + triangleLeftEdgeX) == 0)
+ {
+ log << tcu::TestLog::Message
+ << "No valid fragments."
+ << tcu::TestLog::EndMessage;
+ return false;
+ }
+
+ // if checks failed skip checking other tile sizes
+ if (wrongFragments)
+ {
+ log << tcu::TestLog::Message
+ << "Failed " << wrongFragments << " fragments for tileWidth: " << tileWidth << ", tileHeight: " << tileHeight
+ << tcu::TestLog::EndMessage
+ << tcu::TestLog::Image("ErrorMask", "Error mask", errorMaskAccess);
+ return false;
+ }
+
+ // do additional checks
+ tcu::Vec4 fragmentColor(0.0f, 1.0f, 0.0f, 1.0f);
+
+ tcu::clear(errorMaskAccess, tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0));
+
+ // make sure that there is same number of fragments that share same atomic value
+ for (auto& fragmentsSetMapIt : fragmentsWithSameAtomicValueMap)
+ {
+ // mark correct fragments using few green shades so rates are visible
+ fragmentColor = tcu::Vec4(0.0f, 1.0f - float(fragmentsSetMapIt.first % 7) * 0.1f, 0.0f, 1.0f);
+
+ const auto& fragmentSet = fragmentsSetMapIt.second;;
+ if (fragmentSet.size() != fragmentsWithSameAtomicValueCount)
+ {
+ const auto& topLeftFragment = fragmentSet[0];
+ deUint32 triangleRightEdgeX = fragmentsOnTheRightTriangleEdgeVect[topLeftFragment.y()];
+
+ // we can only count this as an error if set is fully inside of triangle, sets on
+ // edges may not have same number of fragments as sets fully located in the triangle
+ if ((topLeftFragment.y() > (triangleTopEdgeY)) &&
+ (topLeftFragment.x() > (triangleLeftEdgeX)) &&
+ (topLeftFragment.x() < (triangleRightEdgeX - rateWidth)))
+ {
+ wrongFragments += (deUint32)fragmentSet.size();
+ fragmentColor = tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f);
+ }
+ }
+
+ // mark all fragmens from set with proper color
+ for (auto& fragment : fragmentSet)
+ errorMaskAccess.setPixel(fragmentColor, fragment.x(), fragment.y(), 0u);
+ }
+
+ if (wrongFragments)
+ {
+ log << tcu::TestLog::Message
+ << "Wrong number of fragments with same atomic value (" << wrongFragments << ") for tileWidth: " << tileWidth << ", tileHeight: " << tileHeight
+ << tcu::TestLog::EndMessage
+ << tcu::TestLog::Image("ErrorMask", "Error mask", errorMaskAccess);
+ return false;
+ }
+
+ return true;
+ }
+
+ bool AttachmentRateInstance::runComputeShaderMode(void)
+ {
+ // clear the shading rate attachment, then using a compute shader, set the shading rate attachment
+ // values to the desired rate using various atomic operations, then use it to draw a basic triangle
+ // and do basic checks
+
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ VkDevice device = m_context.getDevice();
+ deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
+ VkMemoryBarrier memoryBarrier { VK_STRUCTURE_TYPE_MEMORY_BARRIER, DE_NULL, 0u, 0u };
+
+ Move<VkShaderModule> compShader = createShaderModule(vk, device, m_context.getBinaryCollection().get("comp"), 0);
+ Move<VkShaderModule> vertShader = createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0);
+ Move<VkShaderModule> fragShader = createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0);
+
+ Move<VkCommandPool> cmdPool = createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
+ Move<VkCommandBuffer> cmdBuffer = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+
+ // setup descriptor set with storage image for compute pipeline
+ Move<VkDescriptorSetLayout> computeDescriptorSetLayout = DescriptorSetLayoutBuilder()
+ .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_COMPUTE_BIT)
+ .build(vk, device);
+ Move<VkDescriptorPool> computeDescriptorPool = DescriptorPoolBuilder()
+ .addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1u)
+ .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
+ const VkDescriptorSetAllocateInfo computeDescriptorSetAllocInfo = makeDescriptorSetAllocInfo(*computeDescriptorPool, &(*computeDescriptorSetLayout));
+ Move<VkDescriptorSet> computeDescriptorSet = allocateDescriptorSet(vk, device, &computeDescriptorSetAllocInfo);
+
+ m_srUsage |= VK_IMAGE_USAGE_STORAGE_BIT;
+
+ buildCounterBufferObjects();
+ buildColorBufferObjects(0, m_cbUsage);
+
+ // iterate over all possible tile sizes
+ for (deUint32 tileWidth = m_minTileSize.width; tileWidth <= m_maxTileSize.width; tileWidth *= 2)
+ for (deUint32 tileHeight = m_minTileSize.height; tileHeight <= m_maxTileSize.height; tileHeight *= 2)
+ {
+ // skip tile sizes that have unsuported aspect ratio
+ deUint32 aspectRatio = (tileHeight > tileWidth) ? (tileHeight / tileWidth) : (tileWidth / tileHeight);
+ if (aspectRatio > m_maxAspectRatio)
+ continue;
+
+ // calculate size of shading rate attachment
+ deUint32 srWidth = (m_cbWidth + tileWidth - 1) / tileWidth;
+ deUint32 srHeight = (m_cbHeight + tileHeight - 1) / tileHeight;
+
+ buildShadingRateObjects(0, srWidth, srHeight, m_srUsage);
+
+ const VkDescriptorImageInfo computeDescriptorInfo = makeDescriptorImageInfo(DE_NULL, *m_srImageView[0], VK_IMAGE_LAYOUT_GENERAL);
+ DescriptorSetUpdateBuilder()
+ .writeSingle(*computeDescriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &computeDescriptorInfo)
+ .update(vk, device);
+
+ Move<VkPipelineLayout> computePipelineLayout = buildPipelineLayout(&(*computeDescriptorSetLayout));
+ Move<VkPipelineLayout> graphicsPipelineLayout = buildPipelineLayout(&(*m_counterBufferDescriptorSetLayout));
+ Move<VkPipeline> computePipeline = buildComputePipeline(*compShader, *computePipelineLayout);
+ Move<VkRenderPass> renderPass = buildRenderPass(m_cbFormat, tileWidth, tileHeight);
+ Move<VkPipeline> graphicsPipeline = buildGraphicsPipeline(0, *renderPass, *graphicsPipelineLayout, *vertShader, *fragShader);
+
+ std::vector<FBAttachmentInfo> attachmentInfo
+ {
+ { m_cbFormat, m_cbUsage, m_cbWidth, m_cbHeight, *m_cbImageView[0] },
+ { m_params->srFormat, m_srUsage, srWidth, srHeight, *m_srImageView[0] }
+ };
+ Move<VkFramebuffer> framebuffer = buildFramebuffer(*renderPass, attachmentInfo);
+
+ beginCommandBuffer(vk, *cmdBuffer, 0u);
+
+ // wait till sr image layout is changed
+ VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+ VkImageMemoryBarrier srImageBarrierGeneral =
+ makeImageMemoryBarrier(
+ VK_ACCESS_NONE_KHR,
+ VK_ACCESS_NONE_KHR,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL,
+ **m_srImage[0],
+ m_defaultImageSubresourceRange);
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &srImageBarrierGeneral);
+
+ // fill sr image using compute shader
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *computePipeline);
+ vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *computePipelineLayout, 0, 1, &(*computeDescriptorSet), 0, DE_NULL);
+ vk.cmdDispatch(*cmdBuffer, srWidth, srHeight, 1);
+
+ // wait till sr image is ready and change sr images layout
+ srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
+ memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
+ memoryBarrier.dstAccessMask = VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR;
+ VkImageMemoryBarrier srImageBarrierShadingRate =
+ makeImageMemoryBarrier(
+ VK_ACCESS_SHADER_WRITE_BIT,
+ VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR,
+ VK_IMAGE_LAYOUT_GENERAL,
+ VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR,
+ **m_srImage[0],
+ m_defaultImageSubresourceRange);
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 1, &memoryBarrier, 0, DE_NULL, 1, &srImageBarrierShadingRate);
+
+ // wait till cb image layout is changed
+ srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkImageMemoryBarrier cbImageBarrier =
+ makeImageMemoryBarrier(
+ VK_ACCESS_NONE_KHR,
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL,
+ **m_cbImage[0],
+ m_defaultImageSubresourceRange);
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &cbImageBarrier);
+
+ startRendering(*cmdBuffer, *renderPass, *framebuffer, makeRect2D(m_cbWidth, m_cbHeight), attachmentInfo);
+
+ // draw single triangle to cb
+ vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipelineLayout, 0, 1, &(*m_counterBufferDescriptorSet), 0, DE_NULL);
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
+ vk.cmdDraw(*cmdBuffer, 3u, 1, 0u, 0u);
+
+ finishRendering(*cmdBuffer);
+
+ // wait till color attachment is fully written
+ srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ memoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 1, &memoryBarrier, 0, DE_NULL, 0, DE_NULL);
+
+ // read back color buffer image
+ vk.cmdCopyImageToBuffer(*cmdBuffer, **m_cbImage[0], VK_IMAGE_LAYOUT_GENERAL, **m_cbReadBuffer[0], 1u, &m_defaultBufferImageCopy);
+
+ endCommandBuffer(vk, *cmdBuffer);
+
+ // submit commands and wait
+ const VkQueue queue = m_context.getUniversalQueue();
+ submitCommandsAndWait(vk, device, queue, cmdBuffer.get());
+
+ invalidateAlloc(vk, device, m_cbReadBuffer[0]->getAllocation());
+ if (!verifyUsingAtomicChecks(tileWidth, tileHeight,
+ m_params->srRate.width, m_params->srRate.height,
+ (deUint32*)m_cbReadBuffer[0]->getAllocation().getHostPtr()))
+ return false;
+
+ } // iterate over all possible tile sizes
+
+ return true;
+ }
+
+ bool AttachmentRateInstance::runFragmentShaderMode(void)
+ {
+ // Set up the image as a color attachment, and render rate to it,
+ // then use it to draw a basic triangle and do basic checks
+
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ VkDevice device = m_context.getDevice();
+ deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
+ VkMemoryBarrier memoryBarrier { VK_STRUCTURE_TYPE_MEMORY_BARRIER, DE_NULL, 0u, 0u };
+
+ Move<VkShaderModule> vertSetupShader = createShaderModule(vk, device, m_context.getBinaryCollection().get("vert_setup"), 0);
+ Move<VkShaderModule> fragSetupShader = createShaderModule(vk, device, m_context.getBinaryCollection().get("frag_setup"), 0);
+ Move<VkShaderModule> vertShader = createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0);
+ Move<VkShaderModule> fragShader = createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0);
+
+ Move<VkCommandPool> cmdPool = createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
+ Move<VkCommandBuffer> cmdBuffer = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+
+ m_srUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+
+ buildCounterBufferObjects();
+ buildColorBufferObjects(0, m_cbUsage);
+
+ // iterate over all possible tile sizes
+ for (deUint32 tileWidth = m_minTileSize.width; tileWidth <= m_maxTileSize.width; tileWidth *= 2)
+ for (deUint32 tileHeight = m_minTileSize.height; tileHeight <= m_maxTileSize.height; tileHeight *= 2)
+ {
+ // skip tile sizes that have unsuported aspect ratio
+ deUint32 aspectRatio = (tileHeight > tileWidth) ? (tileHeight / tileWidth) : (tileWidth / tileHeight);
+ if (aspectRatio > m_maxAspectRatio)
+ continue;
+
+ // calculate size of shading rate attachment
+ deUint32 srWidth = (m_cbWidth + tileWidth - 1) / tileWidth;
+ deUint32 srHeight = (m_cbHeight + tileHeight - 1) / tileHeight;
+
+ buildShadingRateObjects(0, srWidth, srHeight, m_srUsage);
+
+ Move<VkPipelineLayout> setupPipelineLayout = buildPipelineLayout();
+ Move<VkPipelineLayout> ratePipelineLayout = buildPipelineLayout(&(*m_counterBufferDescriptorSetLayout));
+ Move<VkRenderPass> setupRenderPass = buildRenderPass(m_params->srFormat);
+ Move<VkRenderPass> rateRenderPass = buildRenderPass(m_cbFormat, tileWidth, tileHeight);
+ Move<VkPipeline> setupPipeline = buildGraphicsPipeline(0, *setupRenderPass, *setupPipelineLayout, *vertSetupShader, *fragSetupShader, DE_FALSE);
+ Move<VkPipeline> ratePipeline = buildGraphicsPipeline(0, *rateRenderPass, *ratePipelineLayout, *vertShader, *fragShader);
+
+ std::vector<FBAttachmentInfo> setupAttachmentInfo
+ {
+ { m_params->srFormat, m_srUsage, srWidth, srHeight, *m_srImageView[0] }
+ };
+ std::vector<FBAttachmentInfo> rateAttachmentInfo
+ {
+ { m_cbFormat, m_cbUsage, m_cbWidth, m_cbHeight, *m_cbImageView[0] },
+ { m_params->srFormat, m_srUsage, srWidth, srHeight, *m_srImageView[0] }
+ };
+ Move<VkFramebuffer> setupFramebuffer = buildFramebuffer(*setupRenderPass, setupAttachmentInfo);
+ Move<VkFramebuffer> rateFramebuffer = buildFramebuffer(*rateRenderPass, rateAttachmentInfo);
+
+ beginCommandBuffer(vk, *cmdBuffer, 0u);
+
+ // wait till sr image layout is changed
+ VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkImageMemoryBarrier srImageBarrierGeneral =
+ makeImageMemoryBarrier(
+ VK_ACCESS_NONE_KHR,
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL,
+ **m_srImage[0],
+ m_defaultImageSubresourceRange);
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &srImageBarrierGeneral);
+
+ // render rate to sr image
+ startRendering(*cmdBuffer, *setupRenderPass, *setupFramebuffer, makeRect2D(srWidth, srHeight), setupAttachmentInfo);
+
+ // draw single triangle to cb
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *setupPipeline);
+ vk.cmdDraw(*cmdBuffer, 3u, 1, 0u, 0u);
+
+ finishRendering(*cmdBuffer);
+
+ // wait till sr image is ready and change sr images layout
+ srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
+ VkImageMemoryBarrier srImageBarrierShadingRate =
+ makeImageMemoryBarrier(
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR,
+ VK_IMAGE_LAYOUT_GENERAL,
+ VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR,
+ **m_srImage[0],
+ m_defaultImageSubresourceRange);
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &srImageBarrierShadingRate);
+
+ // wait till cb image layout is changed
+ srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkImageMemoryBarrier cbImageBarrier =
+ makeImageMemoryBarrier(
+ VK_ACCESS_NONE_KHR,
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL,
+ **m_cbImage[0],
+ m_defaultImageSubresourceRange);
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &cbImageBarrier);
+
+ startRendering(*cmdBuffer, *rateRenderPass, *rateFramebuffer, makeRect2D(m_cbWidth, m_cbHeight), rateAttachmentInfo);
+
+ // draw single triangle to cb
+ vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *ratePipelineLayout, 0, 1, &(*m_counterBufferDescriptorSet), 0, DE_NULL);
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *ratePipeline);
+ vk.cmdDraw(*cmdBuffer, 3u, 1, 0u, 0u);
+
+ finishRendering(*cmdBuffer);
+
+ // wait till color attachment is fully written
+ srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ memoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 1, &memoryBarrier, 0, DE_NULL, 0, DE_NULL);
+
+ // read back color buffer image
+ vk.cmdCopyImageToBuffer(*cmdBuffer, **m_cbImage[0], VK_IMAGE_LAYOUT_GENERAL, **m_cbReadBuffer[0], 1u, &m_defaultBufferImageCopy);
+
+ endCommandBuffer(vk, *cmdBuffer);
+
+ // submit commands and wait
+ const VkQueue queue = m_context.getUniversalQueue();
+ submitCommandsAndWait(vk, device, queue, cmdBuffer.get());
+
+ invalidateAlloc(vk, device, m_cbReadBuffer[0]->getAllocation());
+ if (!verifyUsingAtomicChecks(tileWidth, tileHeight,
+ m_params->srRate.width, m_params->srRate.height,
+ (deUint32*)m_cbReadBuffer[0]->getAllocation().getHostPtr()))
+ return false;
+
+ } // iterate over all possible tile sizes
+
+ return true;
+ }
+
+ bool AttachmentRateInstance::runCopyMode (void)
+ {
+ // Clear a separate image of the same format to that rate, copy it to
+ // the shading rate image, then use it to draw a basic triangle and do basic checks
+
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ VkDevice device = m_context.getDevice();
+ deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
+ VkMemoryBarrier memoryBarrier { VK_STRUCTURE_TYPE_MEMORY_BARRIER, DE_NULL, 0u, 0u };
+
+ Move<VkShaderModule> vertShader = createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0);
+ Move<VkShaderModule> fragShader = createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0);
+
+ Move<VkCommandPool> cmdPool = createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
+ Move<VkCommandBuffer> cmdBuffer = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+
+ buildCounterBufferObjects();
+ buildColorBufferObjects(0, m_cbUsage);
+
+ // iterate over all possible tile sizes
+ for (deUint32 tileWidth = m_minTileSize.width; tileWidth <= m_maxTileSize.width; tileWidth *= 2)
+ for (deUint32 tileHeight = m_minTileSize.height; tileHeight <= m_maxTileSize.height; tileHeight *= 2)
+ {
+ // skip tile sizes that have unsuported aspect ratio
+ deUint32 aspectRatio = (tileHeight > tileWidth) ? (tileHeight / tileWidth) : (tileWidth / tileHeight);
+ if (aspectRatio > m_maxAspectRatio)
+ continue;
+
+ // calculate size of shading rate attachment
+ deUint32 srWidth = (m_cbWidth + tileWidth - 1) / tileWidth;
+ deUint32 srHeight = (m_cbHeight + tileHeight - 1) / tileHeight;
+
+ buildShadingRateObjects(0, srWidth, srHeight, m_srUsage);
+
+ // create image that will be source for shading rate image
+ de::MovePtr<ImageWithMemory> srSrcImage = buildImageWithMemory(m_params->srFormat, srWidth, srHeight, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+
+ Move<VkPipelineLayout> graphicsPipelineLayout = buildPipelineLayout(&(*m_counterBufferDescriptorSetLayout));
+ Move<VkRenderPass> renderPass = buildRenderPass(m_cbFormat, tileWidth, tileHeight);
+ Move<VkPipeline> graphicsPipeline = buildGraphicsPipeline(0, *renderPass, *graphicsPipelineLayout, *vertShader, *fragShader);
+
+ std::vector<FBAttachmentInfo> attachmentInfo
+ {
+ { m_cbFormat, m_cbUsage, m_cbWidth, m_cbHeight, *m_cbImageView[0] },
+ { m_params->srFormat, m_srUsage, srWidth, srHeight, *m_srImageView[0] }
+ };
+ Move<VkFramebuffer> framebuffer = buildFramebuffer(*renderPass, attachmentInfo);
+
+ beginCommandBuffer(vk, *cmdBuffer, 0u);
+
+ // wait till sr images layout are changed
+ VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ std::vector<VkImageMemoryBarrier> srImageBarrierGeneral(2,
+ makeImageMemoryBarrier(
+ VK_ACCESS_NONE_KHR,
+ VK_ACCESS_NONE_KHR,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL,
+ **m_srImage[0],
+ m_defaultImageSubresourceRange));
+ srImageBarrierGeneral[1].image = **srSrcImage;
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 2, srImageBarrierGeneral.data());
+
+ // clear source sr image with proper rate
+ VkClearColorValue clearValue = { { 0, 0, 0, 0 } };
+ clearValue.uint32[0] = calculateRate(m_params->srRate.width, m_params->srRate.height);
+ vk.cmdClearColorImage(*cmdBuffer, **srSrcImage, VK_IMAGE_LAYOUT_GENERAL, &clearValue, 1, &m_defaultImageSubresourceRange);
+
+ // wait till sr source image is ready
+ srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 1, &memoryBarrier, 0, DE_NULL, 0, DE_NULL);
+
+ // copy sr source image to sr image used during rendering
+ VkImageCopy imageCopyRegion
+ {
+ m_defaultImageSubresourceLayers, // VkImageSubresourceLayers srcSubresource;
+ {0, 0, 0}, // VkOffset3D srcOffset;
+ m_defaultImageSubresourceLayers, // VkImageSubresourceLayers dstSubresource;
+ {0, 0, 0}, // VkOffset3D dstOffset;
+ { srWidth, srHeight, 1u } // VkExtent3D extent;
+ };
+ vk.cmdCopyImage(*cmdBuffer, **srSrcImage, VK_IMAGE_LAYOUT_GENERAL, **m_srImage[0], VK_IMAGE_LAYOUT_GENERAL, 1, &imageCopyRegion);
+
+ // wait till sr image is ready and change sr images layout
+ srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
+ memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ memoryBarrier.dstAccessMask = VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR;
+ VkImageMemoryBarrier srImageBarrierShadingRate =
+ makeImageMemoryBarrier(
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR,
+ VK_IMAGE_LAYOUT_GENERAL,
+ VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR,
+ **m_srImage[0],
+ m_defaultImageSubresourceRange);
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 1, &memoryBarrier, 0, DE_NULL, 1, &srImageBarrierShadingRate);
+
+ // wait till cb image layout is changed
+ srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkImageMemoryBarrier cbImageBarrier =
+ makeImageMemoryBarrier(
+ VK_ACCESS_NONE_KHR,
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL,
+ **m_cbImage[0],
+ m_defaultImageSubresourceRange);
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &cbImageBarrier);
+
+ startRendering(*cmdBuffer, *renderPass, *framebuffer, makeRect2D(m_cbWidth, m_cbHeight), attachmentInfo);
+
+ // draw single triangle to cb
+ vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipelineLayout, 0, 1, &(*m_counterBufferDescriptorSet), 0, DE_NULL);
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
+ vk.cmdDraw(*cmdBuffer, 3u, 1, 0u, 0u);
+
+ finishRendering(*cmdBuffer);
+
+ // wait till color attachment is fully written
+ srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ memoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 1, &memoryBarrier, 0, DE_NULL, 0, DE_NULL);
+
+ // read back color buffer image
+ vk.cmdCopyImageToBuffer(*cmdBuffer, **m_cbImage[0], VK_IMAGE_LAYOUT_GENERAL, **m_cbReadBuffer[0], 1u, &m_defaultBufferImageCopy);
+
+ endCommandBuffer(vk, *cmdBuffer);
+
+ // submit commands and wait
+ const VkQueue queue = m_context.getUniversalQueue();
+ submitCommandsAndWait(vk, device, queue, cmdBuffer.get());
+
+ invalidateAlloc(vk, device, m_cbReadBuffer[0]->getAllocation());
+ if (!verifyUsingAtomicChecks(tileWidth, tileHeight,
+ m_params->srRate.width, m_params->srRate.height,
+ (deUint32*)m_cbReadBuffer[0]->getAllocation().getHostPtr()))
+ return false;
+
+ } // iterate over all possible tile sizes
+
+ return true;
+ }
+
+ bool AttachmentRateInstance::runCopyModeOnTransferQueue(void)
+ {
+ // Clear a separate image of the same format to that rate, copy it to
+ // the shading rate image on separate transfer queue and then use copied
+ // image to draw a basic triangle and do basic checks
+
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ const PlatformInterface& vkp = m_context.getPlatformInterface();
+ const InstanceInterface& vki = m_context.getInstanceInterface();
+ VkPhysicalDevice pd = m_context.getPhysicalDevice();
+ deUint32 transferQueueFamilyIndex = std::numeric_limits<deUint32>::max();
+ deUint32 graphicsQueueFamilyIndex = std::numeric_limits<deUint32>::max();
+ VkMemoryBarrier memoryBarrier { VK_STRUCTURE_TYPE_MEMORY_BARRIER, DE_NULL, 0u, 0u };
+ std::vector<VkQueueFamilyProperties> queueFamilyProperties = getPhysicalDeviceQueueFamilyProperties(vki, pd);
+
+ // find graphics and transfer queue families
+ for (deUint32 queueNdx = 0; queueNdx < queueFamilyProperties.size(); queueNdx++)
+ {
+ VkQueueFlags queueFlags = queueFamilyProperties[queueNdx].queueFlags;
+ if ((graphicsQueueFamilyIndex == std::numeric_limits<deUint32>::max()) && (queueFlags & VK_QUEUE_GRAPHICS_BIT))
+ graphicsQueueFamilyIndex = queueNdx;
+ else if ((queueNdx != graphicsQueueFamilyIndex) && (queueFlags & VK_QUEUE_TRANSFER_BIT))
+ transferQueueFamilyIndex = queueNdx;
+ }
+ if (transferQueueFamilyIndex == std::numeric_limits<deUint32>::max())
+ TCU_THROW(NotSupportedError, "No separate transfer queue");
+
+ // using queueFamilies vector to determine if sr image uses exclusiv or concurrent sharing
+ std::vector<deUint32> queueFamilies;
+ if (m_params->mode == TM_SETUP_RATE_WITH_COPYING_FROM_CONCURENT_IMAGE_USING_TRANSFER_QUEUE)
+ queueFamilies = { graphicsQueueFamilyIndex, transferQueueFamilyIndex };
+
+ // create custom device
+ {
+ const float queuePriorities = 1.0f;
+ std::vector<VkDeviceQueueCreateInfo> queueInfo(2,
+ {
+ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkDeviceQueueCreateFlags)0u, // VkDeviceQueueCreateFlags flags;
+ transferQueueFamilyIndex, // uint32_t queueFamilyIndex;
+ 1u, // uint32_t queueCount;
+ &queuePriorities // const float* pQueuePriorities;
+ });
+ queueInfo[1].queueFamilyIndex = graphicsQueueFamilyIndex;
+
+ VkPhysicalDeviceFeatures deviceFeatures;
+ vki.getPhysicalDeviceFeatures(pd, &deviceFeatures);
+
+ VkPhysicalDeviceFragmentShadingRateFeaturesKHR fsrFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR, DE_NULL, DE_FALSE, DE_FALSE, DE_TRUE };
+ VkPhysicalDeviceImagelessFramebufferFeaturesKHR ifbFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES, DE_NULL, DE_TRUE };
+ VkPhysicalDeviceFeatures2 createPhysicalFeature { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, &fsrFeatures, deviceFeatures };
+
+ std::vector<const char*> enabledExtensions = { "VK_KHR_fragment_shading_rate" };
+ if (m_params->useImagelessFramebuffer)
+ {
+ enabledExtensions.push_back("VK_KHR_imageless_framebuffer");
+ fsrFeatures.pNext = &ifbFeatures;
+ }
+
+ std::vector<const char*> enabledLayers = getValidationLayers(vki, pd);
+ VkDeviceCreateInfo deviceInfo
+ {
+ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // VkStructureType sType;
+ &createPhysicalFeature, // const void* pNext;
+ (VkDeviceCreateFlags)0u, // VkDeviceCreateFlags flags;
+ 2u, // uint32_t queueCreateInfoCount;
+ queueInfo.data(), // const VkDeviceQueueCreateInfo* pQueueCreateInfos;
+ static_cast<deUint32>(enabledLayers.size()), // uint32_t enabledLayerCount;
+ de::dataOrNull(enabledLayers), // const char* const* ppEnabledLayerNames;
+ static_cast<deUint32>(enabledExtensions.size()), // uint32_t enabledExtensionCount;
+ enabledExtensions.data(), // const char* const* ppEnabledExtensionNames;
+ DE_NULL // const VkPhysicalDeviceFeatures* pEnabledFeatures;
+ };
+
+ m_device = createDevice(vkp, m_context.getInstance(), vki, pd, &deviceInfo);
+ m_allocator = de::MovePtr<Allocator>(new SimpleAllocator(m_context.getDeviceInterface(), *m_device, getPhysicalDeviceMemoryProperties(vki, pd)));
+ }
+
+ VkQueue transferQueue;
+ vk.getDeviceQueue(*m_device, transferQueueFamilyIndex, 0u, &transferQueue);
+ VkQueue graphicsQueue;
+ vk.getDeviceQueue(*m_device, graphicsQueueFamilyIndex, 0u, &graphicsQueue);
+
+ // create transfer and graphics command buffers
+ Move<VkCommandPool> transferCmdPool = createCommandPool(vk, *m_device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, transferQueueFamilyIndex);
+ Move<VkCommandBuffer> transferCmdBuffer = allocateCommandBuffer(vk, *m_device, *transferCmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+ Move<VkCommandPool> graphicsCmdPool = createCommandPool(vk, *m_device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, graphicsQueueFamilyIndex);
+ Move<VkCommandBuffer> graphicsCmdBuffer = allocateCommandBuffer(vk, *m_device, *graphicsCmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+
+ Move<VkShaderModule> vertShader = createShaderModule(vk, *m_device, m_context.getBinaryCollection().get("vert"), 0);
+ Move<VkShaderModule> fragShader = createShaderModule(vk, *m_device, m_context.getBinaryCollection().get("frag"), 0);
+
+ buildColorBufferObjects(0, m_cbUsage);
+ buildCounterBufferObjects();
+
+ // iterate over all possible tile sizes
+ for (deUint32 tileWidth = m_minTileSize.width; tileWidth <= m_maxTileSize.width; tileWidth *= 2)
+ for (deUint32 tileHeight = m_minTileSize.height; tileHeight <= m_maxTileSize.height; tileHeight *= 2)
+ {
+ // skip tile sizes that have unsuported aspect ratio
+ deUint32 aspectRatio = (tileHeight > tileWidth) ? (tileHeight / tileWidth) : (tileWidth / tileHeight);
+ if (aspectRatio > m_maxAspectRatio)
+ continue;
+
+ // calculate size of shading rate attachment
+ deUint32 srWidth = (m_cbWidth + tileWidth - 1) / tileWidth;
+ deUint32 srHeight = (m_cbHeight + tileHeight - 1) / tileHeight;
+
+ // create image that will be source for shading rate image
+ de::MovePtr<ImageWithMemory> srSrcImage = buildImageWithMemory(m_params->srFormat, srWidth, srHeight,
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
+
+ // create buffer that will contain shading rate source data
+ tcu::TextureFormat srTextureFormat = mapVkFormat(m_params->srFormat);
+ deUint32 srWriteBufferSize = srWidth * srHeight * getNumUsedChannels(srTextureFormat.order) * getChannelSize(srTextureFormat.type);
+ de::MovePtr<BufferWithMemory> srSrcBuffer = buildBufferWithMemory(srWriteBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
+
+ // fill buffer with tested shading rate
+ deUint8* srWriteBufferHostPtr = (deUint8*)srSrcBuffer->getAllocation().getHostPtr();
+ deUint8 value = (deUint8)calculateRate(m_params->srRate.width, m_params->srRate.height);
+ deMemset(srWriteBufferHostPtr, value, (size_t)srWriteBufferSize);
+ flushAlloc(vk, *m_device, srSrcBuffer->getAllocation());
+
+ // create shading rate iamge
+ m_srImage[0] = buildImageWithMemory(m_params->srFormat, srWidth, srHeight,
+ VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
+ VK_IMAGE_TILING_OPTIMAL, queueFamilies);
+ m_srImageView[0] = buildImageView(m_params->srFormat, m_srImage[0]->get());
+
+ Move<VkPipelineLayout> graphicsPipelineLayout = buildPipelineLayout(&(*m_counterBufferDescriptorSetLayout));
+ Move<VkRenderPass> renderPass = buildRenderPass(m_cbFormat, tileWidth, tileHeight);
+ Move<VkPipeline> graphicsPipeline = buildGraphicsPipeline(0, *renderPass, *graphicsPipelineLayout, *vertShader, *fragShader);
+
+ std::vector<FBAttachmentInfo> attachmentInfo
+ {
+ { m_cbFormat, m_cbUsage, m_cbWidth, m_cbHeight, *m_cbImageView[0] },
+ { m_params->srFormat, m_srUsage, srWidth, srHeight, *m_srImageView[0] }
+ };
+ Move<VkFramebuffer> framebuffer = buildFramebuffer(*renderPass, attachmentInfo);
+
+ beginCommandBuffer(vk, *transferCmdBuffer, 0u);
+
+ // wait till sr data is ready in buffer and change sr image layouts to general
+ VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ std::vector<VkImageMemoryBarrier> srImageBarrierGeneral(2,
+ makeImageMemoryBarrier(
+ VK_ACCESS_NONE_KHR,
+ VK_ACCESS_NONE_KHR,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL,
+ **m_srImage[0],
+ m_defaultImageSubresourceRange));
+ srImageBarrierGeneral[1].image = **srSrcImage;
+ vk.cmdPipelineBarrier(*transferCmdBuffer, srcStageMask, dstStageMask, 0, 1, &memoryBarrier, 0, DE_NULL, 2, srImageBarrierGeneral.data());
+
+ // copy sr data to images
+ const VkBufferImageCopy srCopyBuffer = makeBufferImageCopy({ srWidth, srHeight, 1u }, m_defaultImageSubresourceLayers);
+ vk.cmdCopyBufferToImage(*transferCmdBuffer, **srSrcBuffer, **srSrcImage, VK_IMAGE_LAYOUT_GENERAL, 1, &srCopyBuffer);
+
+ // wait till sr source image is ready
+ srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ vk.cmdPipelineBarrier(*transferCmdBuffer, srcStageMask, dstStageMask, 0, 1, &memoryBarrier, 0, DE_NULL, 0, DE_NULL);
+
+ // copy sr source image to sr image used during rendering
+ VkImageCopy imageCopyRegion
+ {
+ m_defaultImageSubresourceLayers, // VkImageSubresourceLayers srcSubresource;
+ {0, 0, 0}, // VkOffset3D srcOffset;
+ m_defaultImageSubresourceLayers, // VkImageSubresourceLayers dstSubresource;
+ {0, 0, 0}, // VkOffset3D dstOffset;
+ { srWidth, srHeight, 1u } // VkExtent3D extent;
+ };
+ vk.cmdCopyImage(*transferCmdBuffer, **srSrcImage, VK_IMAGE_LAYOUT_GENERAL, **m_srImage[0], VK_IMAGE_LAYOUT_GENERAL, 1, &imageCopyRegion);
+
+ // release exclusive ownership from the transfer queue family
+ srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ VkImageMemoryBarrier srImageBarrierOwnershipTransfer =
+ makeImageMemoryBarrier(
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_ACCESS_NONE_KHR,
+ VK_IMAGE_LAYOUT_GENERAL,
+ VK_IMAGE_LAYOUT_GENERAL,
+ **m_srImage[0], m_defaultImageSubresourceRange);
+ if (m_params->mode == TM_SETUP_RATE_WITH_COPYING_FROM_EXCLUSIVE_IMAGE_USING_TRANSFER_QUEUE)
+ {
+ srImageBarrierOwnershipTransfer.srcQueueFamilyIndex = transferQueueFamilyIndex;
+ srImageBarrierOwnershipTransfer.dstQueueFamilyIndex = graphicsQueueFamilyIndex;
+ }
+ vk.cmdPipelineBarrier(*transferCmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &srImageBarrierOwnershipTransfer);
+
+ endCommandBuffer(vk, *transferCmdBuffer);
+
+ beginCommandBuffer(vk, *graphicsCmdBuffer, 0u);
+
+ // acquire exclusive ownership for the graphics queue family - while changing sr images layout
+ vk.cmdPipelineBarrier(*graphicsCmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &srImageBarrierOwnershipTransfer);
+
+ // wait till sr image layout is changed
+ srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
+ VkImageMemoryBarrier srImageBarrierShadingRate =
+ makeImageMemoryBarrier(
+ VK_ACCESS_NONE_KHR,
+ VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR,
+ VK_IMAGE_LAYOUT_GENERAL,
+ VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR,
+ **m_srImage[0],
+ m_defaultImageSubresourceRange);
+ vk.cmdPipelineBarrier(*graphicsCmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &srImageBarrierShadingRate);
+
+ // wait till cb image layout is changed
+ srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkImageMemoryBarrier cbImageBarrier =
+ makeImageMemoryBarrier(
+ VK_ACCESS_NONE_KHR,
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL,
+ **m_cbImage[0],
+ m_defaultImageSubresourceRange);
+ vk.cmdPipelineBarrier(*graphicsCmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &cbImageBarrier);
+
+ startRendering(*graphicsCmdBuffer, *renderPass, *framebuffer, makeRect2D(m_cbWidth, m_cbHeight), attachmentInfo);
+
+ // draw single triangle to cb
+ vk.cmdBindDescriptorSets(*graphicsCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipelineLayout, 0, 1, &(*m_counterBufferDescriptorSet), 0, DE_NULL);
+ vk.cmdBindPipeline(*graphicsCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
+ vk.cmdDraw(*graphicsCmdBuffer, 3u, 1, 0u, 0u);
+
+ finishRendering(*graphicsCmdBuffer);
+
+ // wait till color attachment is fully written
+ srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ memoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ vk.cmdPipelineBarrier(*graphicsCmdBuffer, srcStageMask, dstStageMask, 0, 1, &memoryBarrier, 0, DE_NULL, 0, DE_NULL);
+
+ // read back color buffer image
+ vk.cmdCopyImageToBuffer(*graphicsCmdBuffer, **m_cbImage[0], VK_IMAGE_LAYOUT_GENERAL, **m_cbReadBuffer[0], 1u, &m_defaultBufferImageCopy);
+
+ endCommandBuffer(vk, *graphicsCmdBuffer);
+
+ // create synchronization objects
+ Move<VkSemaphore> semaphore = createSemaphore(vk, *m_device);
+ Move<VkFence> transferFence = createFence(vk, *m_device);
+ Move<VkFence> graphicsFence = createFence(vk, *m_device);
+
+ const VkSubmitInfo transferSubmitInfo
+ {
+ VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // deUint32 waitSemaphoreCount;
+ DE_NULL, // const VkSemaphore* pWaitSemaphores;
+ DE_NULL, // const VkPipelineStageFlags* pWaitDstStageMask;
+ 1u, // deUint32 commandBufferCount;
+ &*transferCmdBuffer, // const VkCommandBuffer* pCommandBuffers;
+ 1u, // deUint32 signalSemaphoreCount;
+ &*semaphore, // const VkSemaphore* pSignalSemaphores;
+ };
+ const VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+ const VkSubmitInfo graphicsSubmitInfo
+ {
+ VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 1u, // deUint32 waitSemaphoreCount;
+ &*semaphore, // const VkSemaphore* pWaitSemaphores;
+ &waitDstStageMask, // const VkPipelineStageFlags* pWaitDstStageMask;
+ 1u, // deUint32 commandBufferCount;
+ &*graphicsCmdBuffer, // const VkCommandBuffer* pCommandBuffers;
+ 0u, // deUint32 signalSemaphoreCount;
+ DE_NULL, // const VkSemaphore* pSignalSemaphores;
+ };
+
+ // submit commands to both queues
+ VK_CHECK(vk.queueSubmit(transferQueue, 1u, &transferSubmitInfo, *transferFence));
+ VK_CHECK(vk.queueSubmit(graphicsQueue, 1u, &graphicsSubmitInfo, *graphicsFence));
+
+ VkFence fences[] = { *graphicsFence, *transferFence };
+ VK_CHECK(vk.waitForFences(*m_device, 2u, fences, DE_TRUE, ~0ull));
+
+ invalidateAlloc(vk, *m_device, m_cbReadBuffer[0]->getAllocation());
+ if (!verifyUsingAtomicChecks(tileWidth, tileHeight,
+ m_params->srRate.width, m_params->srRate.height,
+ (deUint32*)m_cbReadBuffer[0]->getAllocation().getHostPtr()))
+ return false;
+
+ } // iterate over all possible tile sizes
+
+ return true;
+ }
+
+ bool AttachmentRateInstance::runFillLinearTiledImage(void)
+ {
+ // Create a linear tiled fragment shading rate attachment image and set
+ // its data on the host, then draw a basic triangle and do basic checks
+
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ VkDevice device = m_context.getDevice();
+ deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
+ VkImageSubresource imageSubresource = makeImageSubresource(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u);
+ VkMemoryBarrier memoryBarrier { VK_STRUCTURE_TYPE_MEMORY_BARRIER, DE_NULL, 0u, 0u };
+ VkSubresourceLayout srImageLayout;
+
+ Move<VkShaderModule> vertShader = createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0);
+ Move<VkShaderModule> fragShader = createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0);
+
+ Move<VkCommandPool> cmdPool = createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
+ Move<VkCommandBuffer> cmdBuffer = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+
+ buildCounterBufferObjects();
+ buildColorBufferObjects(0, m_cbUsage);
+
+ // iterate over all possible tile sizes
+ for (deUint32 tileWidth = m_minTileSize.width; tileWidth <= m_maxTileSize.width; tileWidth *= 2)
+ for (deUint32 tileHeight = m_minTileSize.height; tileHeight <= m_maxTileSize.height; tileHeight *= 2)
+ {
+ // skip tile sizes that have unsuported aspect ratio
+ deUint32 aspectRatio = (tileHeight > tileWidth) ? (tileHeight / tileWidth) : (tileWidth / tileHeight);
+ if (aspectRatio > m_maxAspectRatio)
+ continue;
+
+ // calculate size of shading rate attachment
+ deUint32 srWidth = (m_cbWidth + tileWidth - 1) / tileWidth;
+ deUint32 srHeight = (m_cbHeight + tileHeight - 1) / tileHeight;
+
+ buildShadingRateObjects(0, srWidth, srHeight, m_srUsage, VK_IMAGE_TILING_LINEAR);
+
+ deUint8* imagePtr = reinterpret_cast<deUint8*>(m_srImage[0]->getAllocation().getHostPtr());
+ deUint8 value = (deUint8)calculateRate(m_params->srRate.width, m_params->srRate.height);
+
+ // fill sr image on the host row by row
+ vk.getImageSubresourceLayout(device, **m_srImage[0], &imageSubresource, &srImageLayout);
+ for (deUint32 srTexelRow = 0; srTexelRow < srHeight; srTexelRow++)
+ {
+ deUint8* rowDst = imagePtr + srImageLayout.offset + srImageLayout.rowPitch * srTexelRow;
+ deMemset(rowDst, value, (size_t)srWidth);
+ }
+
+ Move<VkPipelineLayout> graphicsPipelineLayout = buildPipelineLayout(&(*m_counterBufferDescriptorSetLayout));
+ Move<VkRenderPass> renderPass = buildRenderPass(m_cbFormat, tileWidth, tileHeight);
+ Move<VkPipeline> graphicsPipeline = buildGraphicsPipeline(0, *renderPass, *graphicsPipelineLayout, *vertShader, *fragShader);
+
+ std::vector<FBAttachmentInfo> attachmentInfo
+ {
+ { m_cbFormat, m_cbUsage, m_cbWidth, m_cbHeight, *m_cbImageView[0] },
+ { m_params->srFormat, m_srUsage, srWidth, srHeight, *m_srImageView[0] }
+ };
+ Move<VkFramebuffer> framebuffer = buildFramebuffer(*renderPass, attachmentInfo);
+
+ beginCommandBuffer(vk, *cmdBuffer, 0u);
+
+ // wait till sr image layout is changed
+ VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
+ VkImageMemoryBarrier srImageBarrierAttachment =
+ makeImageMemoryBarrier(
+ VK_ACCESS_NONE_KHR,
+ VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR,
+ **m_srImage[0],
+ m_defaultImageSubresourceRange);
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &srImageBarrierAttachment);
+
+ // wait till cb image layout is changed
+ srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkImageMemoryBarrier cbImageBarrier =
+ makeImageMemoryBarrier(
+ VK_ACCESS_NONE_KHR,
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL,
+ **m_cbImage[0],
+ m_defaultImageSubresourceRange);
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 1, &cbImageBarrier);
+
+ startRendering(*cmdBuffer, *renderPass, *framebuffer, makeRect2D(m_cbWidth, m_cbHeight), attachmentInfo);
+
+ // draw single triangle to cb
+ vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipelineLayout, 0, 1, &(*m_counterBufferDescriptorSet), 0, DE_NULL);
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline);
+ vk.cmdDraw(*cmdBuffer, 3u, 1, 0u, 0u);
+
+ finishRendering(*cmdBuffer);
+
+ // wait till color attachment is fully written
+ srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ memoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 1, &memoryBarrier, 0, DE_NULL, 0, DE_NULL);
+
+ // read back color buffer image
+ vk.cmdCopyImageToBuffer(*cmdBuffer, **m_cbImage[0], VK_IMAGE_LAYOUT_GENERAL, **m_cbReadBuffer[0], 1u, &m_defaultBufferImageCopy);
+
+ endCommandBuffer(vk, *cmdBuffer);
+
+ // submit commands and wait
+ const VkQueue queue = m_context.getUniversalQueue();
+ submitCommandsAndWait(vk, device, queue, cmdBuffer.get());
+
+ invalidateAlloc(vk, device, m_cbReadBuffer[0]->getAllocation());
+ if (!verifyUsingAtomicChecks(tileWidth, tileHeight,
+ m_params->srRate.width, m_params->srRate.height,
+ (deUint32*)m_cbReadBuffer[0]->getAllocation().getHostPtr()))
+ return false;
+
+ } // iterate over all possible tile sizes
+
+ return true;
+ }
+
+ bool AttachmentRateInstance::runTwoSubpassMode(void)
+ {
+ // Set up a two-subpass render pass with different shading rate attachments used in each subpass.
+ // Then draw a basic triangle in each subpass and do basic checks.
+
+ const InstanceInterface& vki = m_context.getInstanceInterface();
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ VkPhysicalDevice pd = m_context.getPhysicalDevice();
+ VkDevice device = m_context.getDevice();
+ deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
+ VkMemoryBarrier memoryBarrier { VK_STRUCTURE_TYPE_MEMORY_BARRIER, DE_NULL, 0u, 0u };
+
+ Move<VkShaderModule> vertShader0 = createShaderModule(vk, device, m_context.getBinaryCollection().get("vert0"), 0);
+ Move<VkShaderModule> vertShader1 = createShaderModule(vk, device, m_context.getBinaryCollection().get("vert1"), 0);
+ Move<VkShaderModule> fragShader = createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0);
+
+ Move<VkCommandPool> cmdPool = createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
+ Move<VkCommandBuffer> cmdBuffer = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+
+ // fetch information about supported rates
+ deUint32 supportedFragmentShadingRateCount;
+ std::vector<VkPhysicalDeviceFragmentShadingRateKHR> supportedFragmentShadingRates;
+ vki.getPhysicalDeviceFragmentShadingRatesKHR(pd, &supportedFragmentShadingRateCount, DE_NULL);
+ supportedFragmentShadingRates.resize(supportedFragmentShadingRateCount, {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_KHR, // VkStructureType sType;
+ DE_NULL, // void* pNext;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlags sampleCounts;
+ { 0, 0 } // VkExtent2D fragmentSize;
+ });
+ vki.getPhysicalDeviceFragmentShadingRatesKHR(pd, &supportedFragmentShadingRateCount, &supportedFragmentShadingRates[0]);
+
+ // grab min and max tile sieze and biggest and smallest rate
+ deUint32 sr0Width = (m_cbWidth + m_minTileSize.width - 1) / m_minTileSize.width;
+ deUint32 sr0Height = (m_cbHeight + m_minTileSize.height - 1) / m_minTileSize.height;
+ deUint32 sr1Width = (m_cbWidth + m_maxTileSize.width - 1) / m_maxTileSize.width;
+ deUint32 sr1Height = (m_cbHeight + m_maxTileSize.height - 1) / m_maxTileSize.height;
+ deUint32 sr0RateWidth = supportedFragmentShadingRates[0].fragmentSize.width; // bigets supported rate
+ deUint32 sr0RateHeight = supportedFragmentShadingRates[0].fragmentSize.height;
+ deUint32 sr1RateWidth = supportedFragmentShadingRates[supportedFragmentShadingRateCount - 2].fragmentSize.width; // smallest supported rate excluding {1, 1}
+ deUint32 sr1RateHeight = supportedFragmentShadingRates[supportedFragmentShadingRateCount - 2].fragmentSize.height;
+
+ buildColorBufferObjects(0, m_cbUsage);
+ buildColorBufferObjects(1, m_cbUsage);
+ buildShadingRateObjects(0, sr0Width, sr0Height, m_srUsage);
+ buildShadingRateObjects(1, sr1Width, sr1Height, m_srUsage);
+ buildCounterBufferObjects();
+
+ Move<VkRenderPass> renderPass = buildRenderPass(m_cbFormat, m_minTileSize.width, m_minTileSize.height, m_maxTileSize.width, m_maxTileSize.height);
+ Move<VkPipelineLayout> pipelineLayout = buildPipelineLayout(&(*m_counterBufferDescriptorSetLayout));
+ Move<VkPipeline> graphicsPipeline0 = buildGraphicsPipeline(0, *renderPass, *pipelineLayout, *vertShader0, *fragShader);
+ Move<VkPipeline> graphicsPipeline1 = buildGraphicsPipeline(1, *renderPass, *pipelineLayout, *vertShader1, *fragShader);
+
+ std::vector<FBAttachmentInfo> attachmentInfo
+ {
+ { m_cbFormat, m_cbUsage, m_cbWidth, m_cbHeight, *m_cbImageView[0] },
+ { m_params->srFormat, m_srUsage, sr0Width, sr0Height, *m_srImageView[0] },
+ { m_cbFormat, m_cbUsage, m_cbWidth, m_cbHeight, *m_cbImageView[1] },
+ { m_params->srFormat, m_srUsage, sr1Width, sr1Height, *m_srImageView[1] }
+ };
+ Move<VkFramebuffer> framebuffer = buildFramebuffer(*renderPass, attachmentInfo);
+
+ beginCommandBuffer(vk, *cmdBuffer, 0u);
+
+ // change sr image layouts to general
+ VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ std::vector<VkImageMemoryBarrier> srImageBarrierGeneral(2,
+ makeImageMemoryBarrier(
+ VK_ACCESS_NONE_KHR,
+ VK_ACCESS_NONE_KHR,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL,
+ **m_srImage[0],
+ m_defaultImageSubresourceRange));
+ srImageBarrierGeneral[1].image = **m_srImage[1];
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 2, srImageBarrierGeneral.data());
+
+ VkClearColorValue clearValues[2] = { { { 0, 0, 0, 0 } }, { { 0, 0, 0, 0 } } };
+ clearValues[0].uint32[0] = calculateRate(sr0RateWidth, sr0RateHeight);
+ clearValues[1].uint32[0] = calculateRate(sr1RateWidth, sr1RateHeight);
+ vk.cmdClearColorImage(*cmdBuffer, **m_srImage[0], VK_IMAGE_LAYOUT_GENERAL, &clearValues[0], 1, &m_defaultImageSubresourceRange);
+ vk.cmdClearColorImage(*cmdBuffer, **m_srImage[1], VK_IMAGE_LAYOUT_GENERAL, &clearValues[1], 1, &m_defaultImageSubresourceRange);
+
+ // wait till sr data is ready and change sr images layout
+ srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
+ memoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ memoryBarrier.dstAccessMask = VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR;
+ std::vector<VkImageMemoryBarrier> srImageBarrierShadingRate(2,
+ makeImageMemoryBarrier(
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR,
+ VK_IMAGE_LAYOUT_GENERAL,
+ VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR,
+ **m_srImage[0],
+ m_defaultImageSubresourceRange));
+ srImageBarrierShadingRate[1].image = **m_srImage[1];
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 1, &memoryBarrier, 0, DE_NULL, 2, srImageBarrierShadingRate.data());
+
+ // wait till cb image layouts are changed
+ srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ std::vector<VkImageMemoryBarrier> cbImageBarrier(2,
+ makeImageMemoryBarrier(
+ VK_ACCESS_NONE_KHR,
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_IMAGE_LAYOUT_GENERAL,
+ **m_cbImage[0],
+ m_defaultImageSubresourceRange));
+ cbImageBarrier[1].image = **m_cbImage[1];
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 0, DE_NULL, 0, DE_NULL, 2, cbImageBarrier.data());
+
+ startRendering(*cmdBuffer, *renderPass, *framebuffer, makeRect2D(m_cbWidth, m_cbHeight), attachmentInfo);
+
+ // draw single triangle to first cb
+ vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0, 1, &(*m_counterBufferDescriptorSet), 0, DE_NULL);
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline0);
+ vk.cmdDraw(*cmdBuffer, 3u, 1, 0u, 0u);
+
+ vk.cmdNextSubpass(*cmdBuffer, VK_SUBPASS_CONTENTS_INLINE);
+
+ // draw single triangle to second cb
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *graphicsPipeline1);
+ vk.cmdDraw(*cmdBuffer, 3u, 1, 0u, 0u);
+
+ finishRendering(*cmdBuffer);
+
+ // wait till color attachments are fully written
+ srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ memoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ vk.cmdPipelineBarrier(*cmdBuffer, srcStageMask, dstStageMask, 0, 1, &memoryBarrier, 0, DE_NULL, 0, DE_NULL);
+
+ // read back color buffer images
+ vk.cmdCopyImageToBuffer(*cmdBuffer, **m_cbImage[0], VK_IMAGE_LAYOUT_GENERAL, **m_cbReadBuffer[0], 1u, &m_defaultBufferImageCopy);
+ vk.cmdCopyImageToBuffer(*cmdBuffer, **m_cbImage[1], VK_IMAGE_LAYOUT_GENERAL, **m_cbReadBuffer[1], 1u, &m_defaultBufferImageCopy);
+
+ endCommandBuffer(vk, *cmdBuffer);
+
+ // submit commands and wait
+ const VkQueue queue = m_context.getUniversalQueue();
+ submitCommandsAndWait(vk, device, queue, cmdBuffer.get());
+
+ // read back buffer with color attachment 1 data
+ Allocation& cb0BuffAlloc = m_cbReadBuffer[0]->getAllocation();
+ invalidateAlloc(vk, device, cb0BuffAlloc);
+
+ // read back buffer with color attachment 2 data
+ Allocation& cb1BuffAlloc = m_cbReadBuffer[1]->getAllocation();
+ invalidateAlloc(vk, device, cb1BuffAlloc);
+
+ // validate both attachemtns triangle
+ return (verifyUsingAtomicChecks(m_minTileSize.width, m_minTileSize.height,
+ sr0RateWidth, sr0RateHeight,
+ (deUint32*)m_cbReadBuffer[0]->getAllocation().getHostPtr()) &&
+ verifyUsingAtomicChecks(m_maxTileSize.width, m_maxTileSize.height,
+ sr1RateWidth, sr1RateHeight,
+ (deUint32*)m_cbReadBuffer[1]->getAllocation().getHostPtr()));
+ }
+
+ class AttachmentRateTestCase : public TestCase
+ {
+ public:
+ AttachmentRateTestCase (tcu::TestContext& context, const char* name, de::SharedPtr<TestParams> params);
+ ~AttachmentRateTestCase (void) = default;
+
+ void initPrograms (SourceCollections& programCollection) const override;
+ TestInstance* createInstance (Context& context) const override;
+ void checkSupport (Context& context) const override;
+
+ private:
+
+ const de::SharedPtr<TestParams> m_params;
+ };
+
+ AttachmentRateTestCase::AttachmentRateTestCase(tcu::TestContext& context, const char* name, de::SharedPtr<TestParams> params)
+ : vkt::TestCase (context, name, "")
+ , m_params (params)
+ {
+ }
+
+ void AttachmentRateTestCase::checkSupport(Context& context) const
+ {
+ context.requireDeviceFunctionality("VK_KHR_fragment_shading_rate");
+
+ if (m_params->useDynamicRendering)
+ context.requireDeviceFunctionality("VK_KHR_dynamic_rendering");
+ if (m_params->useImagelessFramebuffer)
+ context.requireDeviceFunctionality("VK_KHR_imageless_framebuffer");
+
+ if (!context.getFragmentShadingRateFeatures().attachmentFragmentShadingRate)
+ TCU_THROW(NotSupportedError, "pipelineFragmentShadingRate not supported");
+
+ const vk::InstanceInterface& vk = context.getInstanceInterface();
+ const vk::VkPhysicalDevice pd = context.getPhysicalDevice();
+
+ VkImageFormatProperties imageProperties;
+ VkImageUsageFlags srUsage = VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR |
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+
+ VkResult result = vk.getPhysicalDeviceImageFormatProperties(pd, m_params->srFormat, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, srUsage, 0, &imageProperties);
+ if (result == VK_ERROR_FORMAT_NOT_SUPPORTED)
+ TCU_THROW(NotSupportedError, "Format not supported");
+
+ if (m_params->mode != TM_TWO_SUBPASS)
+ {
+ deUint32 supportedFragmentShadingRateCount;
+ VkExtent2D testedRate = m_params->srRate;
+ std::vector<VkPhysicalDeviceFragmentShadingRateKHR> supportedFragmentShadingRates;
+
+ // fetch information about supported rates
+ vk.getPhysicalDeviceFragmentShadingRatesKHR(pd, &supportedFragmentShadingRateCount, DE_NULL);
+ supportedFragmentShadingRates.resize(supportedFragmentShadingRateCount, {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_KHR, // VkStructureType sType;
+ DE_NULL, // void* pNext;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlags sampleCounts;
+ { 0, 0 } // VkExtent2D fragmentSize;
+ });
+ vk.getPhysicalDeviceFragmentShadingRatesKHR(pd, &supportedFragmentShadingRateCount, &supportedFragmentShadingRates[0]);
+
+ // check if rate required by test is not supported
+ if (std::none_of(supportedFragmentShadingRates.begin(), supportedFragmentShadingRates.end(),
+ [&testedRate](const VkPhysicalDeviceFragmentShadingRateKHR& r)
+ { return (r.fragmentSize.width == testedRate.width && r.fragmentSize.height == testedRate.height); }))
+ {
+ TCU_THROW(NotSupportedError, "Rate not supported");
+ }
+ }
+
+ VkFormatFeatureFlags requiredFeatures = 0;
+ if (m_params->mode == TM_SETUP_RATE_WITH_ATOMICS_IN_COMPUTE_SHADER)
+ requiredFeatures = VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR | VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT;
+ else if ((m_params->mode == TM_SETUP_RATE_WITH_COPYING_FROM_OTHER_IMAGE) ||
+ (m_params->mode == TM_SETUP_RATE_WITH_COPYING_FROM_EXCLUSIVE_IMAGE_USING_TRANSFER_QUEUE) ||
+ (m_params->mode == TM_SETUP_RATE_WITH_COPYING_FROM_CONCURENT_IMAGE_USING_TRANSFER_QUEUE) ||
+ (m_params->mode == TM_SETUP_RATE_WITH_LINEAR_TILED_IMAGE))
+ requiredFeatures = VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
+ else if (m_params->mode == TM_SETUP_RATE_WITH_FRAGMENT_SHADER)
+ requiredFeatures = VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
+
+ #if DEBUG_USE_STORE_INSTEAD_OF_ATOMICS == 1
+ if (m_params->mode == TM_SETUP_RATE_WITH_ATOMICS_IN_COMPUTE_SHADER)
+ requiredFeatures = VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR | VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;
+ #endif
+
+ if (requiredFeatures)
+ {
+ const VkFormatProperties formatProperties = getPhysicalDeviceFormatProperties(vk, pd, m_params->srFormat);
+
+ if (m_params->mode == TM_SETUP_RATE_WITH_LINEAR_TILED_IMAGE)
+ {
+ if ((formatProperties.linearTilingFeatures & requiredFeatures) != requiredFeatures)
+ TCU_THROW(NotSupportedError, "Required format feature bits not supported");
+ }
+ else if ((formatProperties.optimalTilingFeatures & requiredFeatures) != requiredFeatures)
+ TCU_THROW(NotSupportedError, "Required format feature bits not supported");
+ }
+ }
+
+ void AttachmentRateTestCase::initPrograms(SourceCollections& programCollection) const
+ {
+ deUint32 rateValue = calculateRate(m_params->srRate.width, m_params->srRate.height);
+
+ if (m_params->mode == TM_SETUP_RATE_WITH_ATOMICS_IN_COMPUTE_SHADER)
+ {
+ std::stringstream compStream;
+ compStream <<
+ "#version 450\n"
+ "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
+ "layout(r32ui, binding = 0) coherent uniform highp uimage2D srImage;\n"
+ "void main (void)\n"
+ "{\n"
+ #if DEBUG_USE_STORE_INSTEAD_OF_ATOMICS == 1
+ " imageStore(srImage, ivec2(gl_GlobalInvocationID.xy), uvec4(" << rateValue << "));\n"
+ #else
+ " imageAtomicAdd(srImage, ivec2(gl_GlobalInvocationID.xy), " << rateValue << ");\n"
+ #endif
+ "}\n";
+
+ programCollection.glslSources.add("comp") << glu::ComputeSource(compStream.str());
+ }
+
+ tcu::StringTemplate vertTemplate(
+ "#version 450 core\n"
+ "out gl_PerVertex\n"
+ "{\n"
+ " vec4 gl_Position;\n"
+ "};\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(float(1.0 - 2.0 * int(gl_VertexIndex != 1)) * ${SCALE} + ${TRANSLATE},\n"
+ " float(1.0 - 2.0 * int(gl_VertexIndex > 0)) * ${SCALE} + ${TRANSLATE}, 0.0, 1.0);\n"
+ "}\n");
+
+ std::map<std::string, std::string> specializationMap
+ {
+ {"SCALE", "0.8" },
+ {"TRANSLATE", "0.0" },
+ };
+
+ if (m_params->mode == TM_TWO_SUBPASS)
+ {
+ specializationMap["SCALE"] = "0.4";
+ specializationMap["TRANSLATE"] = "-0.5";
+ programCollection.glslSources.add("vert0") << glu::VertexSource(vertTemplate.specialize(specializationMap));
+
+ specializationMap["SCALE"] = "0.4";
+ specializationMap["TRANSLATE"] = "0.5";
+ programCollection.glslSources.add("vert1") << glu::VertexSource(vertTemplate.specialize(specializationMap));
+ }
+ else
+ {
+ programCollection.glslSources.add("vert") << glu::VertexSource(vertTemplate.specialize(specializationMap));
+ }
+
+ if (m_params->mode == TM_SETUP_RATE_WITH_FRAGMENT_SHADER)
+ {
+ // use large triangle that will cover whole color buffer
+ specializationMap["SCALE"] = "9.0";
+ specializationMap["TRANSLATE"] = "0.0";
+ programCollection.glslSources.add("vert_setup") << glu::VertexSource(vertTemplate.specialize(specializationMap));
+
+ std::stringstream fragStream;
+ fragStream <<
+ "#version 450 core\n"
+ "layout(location = 0) out uint outColor;\n"
+ "void main()\n"
+ "{\n"
+ " outColor.x = " << rateValue << ";\n"
+ "}\n";
+ programCollection.glslSources.add("frag_setup") << glu::FragmentSource(fragStream.str());
+ }
+
+ std::string frag =
+ "#version 450 core\n"
+ "#extension GL_EXT_fragment_shading_rate : enable\n"
+ "layout(set = 0, binding = 0) buffer Block { uint counter; } buf;\n"
+ "layout(location = 0) out uvec4 outColor;\n"
+ "void main()\n"
+ "{\n"
+ " outColor.x = gl_ShadingRateEXT;\n"
+ " outColor.y = 0;\n"
+ " outColor.z = atomicAdd(buf.counter, 1);\n"
+ " outColor.w = 0;\n"
+ "}\n";
+ programCollection.glslSources.add("frag") << glu::FragmentSource(frag);
+ }
+
+ TestInstance* AttachmentRateTestCase::createInstance(Context& context) const
+ {
+ return new AttachmentRateInstance(context, m_params);
+ }
+
+ } // anonymous
+
+ void createAttachmentRateTests(tcu::TestContext& testCtx, tcu::TestCaseGroup* parentGroup, bool useDynamicRendering)
+ {
+ struct SRFormat
+ {
+ VkFormat format;
+ const char* name;
+ };
+
+ const std::vector<SRFormat> srFormats
+ {
+ { VK_FORMAT_R8_UINT, "r8_uint" },
+ { VK_FORMAT_R8G8_UINT, "r8g8_uint" },
+ { VK_FORMAT_R8G8B8_UINT, "r8g8b8_uint" },
+ { VK_FORMAT_R8G8B8A8_UINT, "r8g8b8a8_uint" },
+ { VK_FORMAT_R16_UINT, "r16_uint" },
+ { VK_FORMAT_R16G16_UINT, "r16g16_uint" },
+ { VK_FORMAT_R16G16B16_UINT, "r16g16b16_uint" },
+ { VK_FORMAT_R16G16B16A16_UINT, "r16g16b16a16_uint" },
+ { VK_FORMAT_R32_UINT, "r32_uint" },
+ { VK_FORMAT_R32G32_UINT, "r32g32_uint" },
+ { VK_FORMAT_R32G32B32_UINT, "r32g32b32_uint" },
+ { VK_FORMAT_R32G32B32A32_UINT, "r32g32b32a32_uint" },
+ { VK_FORMAT_R64_UINT, "r64_uint" },
+ { VK_FORMAT_R64G64_UINT, "r64g64_uint" },
+ { VK_FORMAT_R64G64B64_UINT, "r64g64b64_uint" },
+ { VK_FORMAT_R64G64B64A64_UINT, "r64g64b64a64_uint" },
+ };
+
+ struct SRRate
+ {
+ VkExtent2D count;
+ const char* name;
+ };
+
+ const std::vector<SRRate> srRates
+ {
+ { {1, 1}, "rate_1x1" },
+ { {1, 2}, "rate_1x2" },
+ { {1, 4}, "rate_1x4" },
+ { {2, 1}, "rate_2x1" },
+ { {2, 2}, "rate_2x2" },
+ { {2, 4}, "rate_2x4" },
+ { {4, 1}, "rate_4x1" },
+ { {4, 2}, "rate_4x2" },
+ { {4, 4}, "rate_4x4" },
+ };
+
+ struct TestModeParam
+ {
+ TestMode mode;
+ const char* name;
+ };
+
+ const std::vector<TestModeParam> testModeParams
+ {
+ { TM_SETUP_RATE_WITH_ATOMICS_IN_COMPUTE_SHADER, "setup_with_atomics" },
+ { TM_SETUP_RATE_WITH_FRAGMENT_SHADER, "setup_with_fragment" },
+ { TM_SETUP_RATE_WITH_COPYING_FROM_OTHER_IMAGE, "setup_with_copying" },
+ { TM_SETUP_RATE_WITH_COPYING_FROM_CONCURENT_IMAGE_USING_TRANSFER_QUEUE, "setup_with_copying_using_transfer_queue_concurent" },
+ { TM_SETUP_RATE_WITH_COPYING_FROM_EXCLUSIVE_IMAGE_USING_TRANSFER_QUEUE, "setup_with_copying_using_transfer_queue_exclusive" },
+ { TM_SETUP_RATE_WITH_LINEAR_TILED_IMAGE, "setup_with_linear_tiled_image" },
+ };
+
+ de::MovePtr<tcu::TestCaseGroup> mainGroup(new tcu::TestCaseGroup(testCtx, "attachment_rate", ""));
+
+ for (const auto& testModeParam : testModeParams)
+ {
+ de::MovePtr<tcu::TestCaseGroup> testModeGroup(new tcu::TestCaseGroup(testCtx, testModeParam.name, ""));
+
+ for (const auto& srFormat : srFormats)
+ {
+ de::MovePtr<tcu::TestCaseGroup> formatGroup(new tcu::TestCaseGroup(testCtx, srFormat.name, ""));
+ for (const auto& srRate : srRates)
+ {
+ formatGroup->addChild(new AttachmentRateTestCase(testCtx, srRate.name, de::SharedPtr<TestParams>(
+ new TestParams
+ {
+ testModeParam.mode, // TestMode mode;
+ srFormat.format, // VkFormat srFormat;
+ srRate.count, // VkExtent2D srRate;
+ useDynamicRendering, // bool useDynamicRendering;
+ false // bool useImagelessFramebuffer;
+ }
+ )));
+
+ // duplicate all tests for imageless framebuffer
+ if (!useDynamicRendering)
+ {
+ std::string imagelessName = std::string(srRate.name) + "_imageless";
+ formatGroup->addChild(new AttachmentRateTestCase(testCtx, imagelessName.c_str(), de::SharedPtr<TestParams>(
+ new TestParams
+ {
+ testModeParam.mode, // TestMode mode;
+ srFormat.format, // VkFormat srFormat;
+ srRate.count, // VkExtent2D srRate;
+ false, // bool useDynamicRendering;
+ true // bool useImagelessFramebuffer;
+ }
+ )));
+ }
+ }
+
+ testModeGroup->addChild(formatGroup.release());
+ }
+
+ mainGroup->addChild(testModeGroup.release());
+ }
+
+ if (!useDynamicRendering)
+ {
+ de::MovePtr<tcu::TestCaseGroup> miscGroup(new tcu::TestCaseGroup(testCtx, "misc", ""));
+ miscGroup->addChild(new AttachmentRateTestCase(testCtx, "two_subpass", de::SharedPtr<TestParams>(
+ new TestParams
+ {
+ TM_TWO_SUBPASS, // TestMode mode;
+ VK_FORMAT_R8_UINT, // VkFormat srFormat;
+ {0, 0}, // VkExtent2D srRate; // not used in TM_TWO_SUBPASS
+ false, // bool useDynamicRendering;
+ false // bool useImagelessFramebuffer;
+ }
+ )));
+ mainGroup->addChild(miscGroup.release());
+ }
+
+ parentGroup->addChild(mainGroup.release());
+ }
+
+ } // FragmentShadingRage
+ } // vkt