1/*------------------------------------------------------------------------ 2 * Vulkan Conformance Tests 3 * ------------------------ 4 * 5 * Copyright (c) 2023 The Khronos Group Inc. 6 * Copyright (c) 2023 Valve Corporation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 *//*! 21 * \file 22 * \brief Extended dynamic state misc tests 23 *//*--------------------------------------------------------------------*/ 24 25#include "vktPipelineExtendedDynamicStateMiscTests.hpp" 26#include "vktTestCase.hpp" 27#include "vktTestCaseUtil.hpp" 28#include "vkBuilderUtil.hpp" 29#include "vkCmdUtil.hpp" 30#include "vkImageUtil.hpp" 31#include "vkObjUtil.hpp" 32 33#include "tcuImageCompare.hpp" 34 35#include "deUniquePtr.hpp" 36 37#include <sstream> 38#include <vector> 39#include <memory> 40#include <utility> 41 42namespace vkt 43{ 44namespace pipeline 45{ 46 47namespace 48{ 49 50using namespace vk; 51 52constexpr uint32_t kVertexCount = 4u; 53 54void checkDynamicRasterizationSamplesSupport (Context& context) 55{ 56#ifndef CTS_USES_VULKANSC 57 if (!context.getExtendedDynamicState3FeaturesEXT().extendedDynamicState3RasterizationSamples) 58 TCU_THROW(NotSupportedError, "extendedDynamicState3RasterizationSamples not supported"); 59#else 60 DE_UNREF(context); 61 TCU_THROW(NotSupportedError, "extendedDynamicState3RasterizationSamples not supported"); 62#endif // CTS_USES_VULKANSC 63} 64 65void sampleShadingWithDynamicSampleCountSupport (Context& context, PipelineConstructionType pipelineConstructionType) 66{ 67 checkPipelineConstructionRequirements(context.getInstanceInterface(), context.getPhysicalDevice(), pipelineConstructionType); 68 checkDynamicRasterizationSamplesSupport(context); 69 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_FRAGMENT_STORES_AND_ATOMICS); 70 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SAMPLE_RATE_SHADING); 71} 72 73void initFullScreenQuadVertexProgram (vk::SourceCollections& programCollection, const char* name) 74{ 75 std::ostringstream vert; 76 vert 77 << "#version 460\n" 78 << "vec2 positions[" << kVertexCount << "] = vec2[](\n" 79 << " vec2(-1.0, -1.0),\n" 80 << " vec2(-1.0, 1.0),\n" 81 << " vec2( 1.0, -1.0),\n" 82 << " vec2( 1.0, 1.0)\n" 83 << ");\n" 84 << "void main (void) {\n" 85 << " gl_Position = vec4(positions[gl_VertexIndex % " << kVertexCount << "], 0.0, 1.0);\n" 86 << "}\n" 87 ; 88 programCollection.glslSources.add(name) << glu::VertexSource(vert.str()); 89} 90 91void initBlueAndAtomicCounterFragmentProgram (vk::SourceCollections& programCollection, const char* name) 92{ 93 std::ostringstream frag; 94 frag 95 << "#version 460\n" 96 << "layout (location=0) out vec4 outColor;\n" 97 << "layout (set=0, binding=0) buffer InvocationCounterBlock { uint invocations; } counterBuffer;\n" 98 << "void main (void) {\n" 99 << " uint sampleId = gl_SampleID;\n" // Enable sample shading for shader objects by reading gl_SampleID 100 << " atomicAdd(counterBuffer.invocations, 1u);\n" 101 << " outColor = vec4(0.0, 0.0, 1.0, 1.0);\n" 102 << "}\n" 103 ; 104 programCollection.glslSources.add(name) << glu::FragmentSource(frag.str()); 105} 106 107void sampleShadingWithDynamicSampleCountPrograms (vk::SourceCollections& programCollection, PipelineConstructionType) 108{ 109 initFullScreenQuadVertexProgram(programCollection, "vert"); 110 initBlueAndAtomicCounterFragmentProgram(programCollection, "frag"); 111} 112 113void verifyValueInRange (uint32_t value, uint32_t minValue, uint32_t maxValue, const char* valueDesc) 114{ 115 if (value < minValue || value > maxValue) 116 { 117 std::ostringstream msg; 118 msg << "Unexpected value found for " << valueDesc << ": " << value << " not in range [" << minValue << ", " << maxValue << "]"; 119 TCU_FAIL(msg.str()); 120 } 121} 122/* 123 * begin cmdbuf 124 * bind pipeline with sample shading disabled 125 * call vkCmdSetRasterizationSamplesEXT(samples > 1) 126 * draw 127 * bind pipeline with sample shading enabled 128 * draw 129 * sample shading should work for both draws with the expected number of samples 130 * 131 * Each draw will use one half of the framebuffer, controlled by the viewport and scissor. 132 */ 133tcu::TestStatus sampleShadingWithDynamicSampleCount (Context& context, PipelineConstructionType constructionType) 134{ 135 const auto ctx = context.getContextCommonData(); 136 const tcu::IVec3 fbExtent (2, 2, 1); 137 const auto vkExtent = makeExtent3D(fbExtent); 138 const auto colorFormat = VK_FORMAT_R8G8B8A8_UNORM; 139 const auto colorUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT); 140 const auto descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; 141 const auto descriptorStages = VK_SHADER_STAGE_FRAGMENT_BIT; 142 const auto kNumDraws = 2u; 143 const auto bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; 144 const auto colorSRR = makeDefaultImageSubresourceRange(); 145 const auto kMultiSampleCount = VK_SAMPLE_COUNT_4_BIT; 146 const auto kSingleSampleCount = VK_SAMPLE_COUNT_1_BIT; 147 const tcu::Vec4 clearColor (0.0f, 0.0f, 0.0f, 0.0f); 148 const tcu::Vec4 geomColor (0.0f, 0.0f, 1.0f, 1.0f); // Must match frag shader. 149 const auto topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; 150 151 // Color buffers. 152 ImageWithBuffer colorBuffer (ctx.vkd, ctx.device, ctx.allocator, vkExtent, colorFormat, colorUsage, VK_IMAGE_TYPE_2D, colorSRR, 1u, kMultiSampleCount); 153 ImageWithBuffer resolveBuffer (ctx.vkd, ctx.device, ctx.allocator, vkExtent, colorFormat, colorUsage, VK_IMAGE_TYPE_2D, colorSRR, 1u, kSingleSampleCount); 154 155 // Counter buffers. 156 using BufferPtr = std::unique_ptr<BufferWithMemory>; 157 using BufferVec = std::vector<BufferPtr>; 158 159 const auto counterBufferSize = static_cast<VkDeviceSize>(sizeof(uint32_t)); 160 const auto counterBufferInfo = makeBufferCreateInfo(counterBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); 161 162 BufferVec counterBuffers; 163 164 for (uint32_t drawIdx = 0u; drawIdx < kNumDraws; ++drawIdx) 165 { 166 BufferPtr counterBuffer (new BufferWithMemory(ctx.vkd, ctx.device, ctx.allocator, counterBufferInfo, MemoryRequirement::HostVisible)); 167 auto& counterBufferAlloc = counterBuffer->getAllocation(); 168 void* counterBufferPtr = counterBufferAlloc.getHostPtr(); 169 170 deMemset(counterBufferPtr, 0, static_cast<size_t>(counterBufferSize)); 171 flushAlloc(ctx.vkd, ctx.device, counterBufferAlloc); 172 173 counterBuffers.emplace_back(std::move(counterBuffer)); 174 } 175 176 // Descriptor set layout, pool and set. 177 DescriptorSetLayoutBuilder setLayoutbuilder; 178 setLayoutbuilder.addSingleBinding(descriptorType, descriptorStages); 179 const auto setLayout = setLayoutbuilder.build(ctx.vkd, ctx.device); 180 181 DescriptorPoolBuilder poolBuilder; 182 poolBuilder.addType(descriptorType, kNumDraws); 183 const auto descriptorPool = poolBuilder.build(ctx.vkd, ctx.device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, kNumDraws); 184 185 using DescriptorSetVec = std::vector<Move<VkDescriptorSet>>; 186 DescriptorSetVec descriptorSets; 187 188 for (uint32_t drawIdx = 0u; drawIdx < kNumDraws; ++drawIdx) 189 { 190 descriptorSets.emplace_back(makeDescriptorSet(ctx.vkd, ctx.device, *descriptorPool, *setLayout)); 191 192 DescriptorSetUpdateBuilder updateBuilder; 193 const auto counterBufferDescriptorInfo = makeDescriptorBufferInfo(counterBuffers.at(drawIdx)->get(), 0ull, counterBufferSize); 194 updateBuilder.writeSingle(*descriptorSets.back(), DescriptorSetUpdateBuilder::Location::binding(0u), descriptorType, &counterBufferDescriptorInfo); 195 updateBuilder.update(ctx.vkd, ctx.device); 196 } 197 198 // Render pass and framebuffer. 199 const std::vector<VkAttachmentDescription> attachmentDescs 200 { 201 // Multisample attachment. 202 makeAttachmentDescription( 203 0u, 204 colorFormat, 205 kMultiSampleCount, 206 VK_ATTACHMENT_LOAD_OP_CLEAR, 207 VK_ATTACHMENT_STORE_OP_DONT_CARE, 208 VK_ATTACHMENT_LOAD_OP_DONT_CARE, 209 VK_ATTACHMENT_STORE_OP_DONT_CARE, 210 VK_IMAGE_LAYOUT_UNDEFINED, 211 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL), 212 213 // Resolve attachment. 214 makeAttachmentDescription( 215 0u, 216 colorFormat, 217 kSingleSampleCount, 218 VK_ATTACHMENT_LOAD_OP_DONT_CARE, 219 VK_ATTACHMENT_STORE_OP_STORE, 220 VK_ATTACHMENT_LOAD_OP_DONT_CARE, 221 VK_ATTACHMENT_STORE_OP_DONT_CARE, 222 VK_IMAGE_LAYOUT_UNDEFINED, 223 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL), 224 }; 225 226 const auto colorAttRef = makeAttachmentReference(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); 227 const auto resolveAttRef = makeAttachmentReference(1u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); 228 const auto subpassDescription = makeSubpassDescription(0u, bindPoint, 0u, nullptr, 1u, &colorAttRef, &resolveAttRef, nullptr, 0u, nullptr); 229 230 const VkRenderPassCreateInfo renderPassCreateInfo = 231 { 232 VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType; 233 nullptr, // const void* pNext; 234 0u, // VkRenderPassCreateFlags flags; 235 de::sizeU32(attachmentDescs), // uint32_t attachmentCount; 236 de::dataOrNull(attachmentDescs), // const VkAttachmentDescription* pAttachments; 237 1u, // uint32_t subpassCount; 238 &subpassDescription, // const VkSubpassDescription* pSubpasses; 239 0u, // uint32_t dependencyCount; 240 nullptr, // const VkSubpassDependency* pDependencies; 241 }; 242 auto renderPass = RenderPassWrapper(constructionType, ctx.vkd, ctx.device, &renderPassCreateInfo); 243 244 const std::vector<VkImage> images { colorBuffer.getImage(), resolveBuffer.getImage() }; 245 const std::vector<VkImageView> imageViews { colorBuffer.getImageView(), resolveBuffer.getImageView() }; 246 renderPass.createFramebuffer(ctx.vkd, ctx.device, de::sizeU32(imageViews), de::dataOrNull(images), de::dataOrNull(imageViews), vkExtent.width, vkExtent.height); 247 248 // Pipelines. 249 const auto& binaries = context.getBinaryCollection(); 250 const auto& vertModule = ShaderWrapper(ctx.vkd, ctx.device, binaries.get("vert")); 251 const auto& fragModule = ShaderWrapper(ctx.vkd, ctx.device, binaries.get("frag")); 252 253 const std::vector<VkDynamicState> dynamicStates { 254#ifndef CTS_USES_VULKANSC 255 VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT, 256#endif // CTS_USES_VULKANSC 257 VK_DYNAMIC_STATE_SCISSOR, 258 VK_DYNAMIC_STATE_VIEWPORT, 259 }; 260 261 const VkPipelineDynamicStateCreateInfo dynamicStateInfo = 262 { 263 VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType; 264 nullptr, // const void* pNext; 265 0u, // VkPipelineDynamicStateCreateFlags flags; 266 de::sizeU32(dynamicStates), // uint32_t dynamicStateCount; 267 de::dataOrNull(dynamicStates), // const VkDynamicState* pDynamicStates; 268 }; 269 270 const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = initVulkanStructureConst(); 271 272 VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo = 273 { 274 VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType; 275 nullptr, // const void* pNext; 276 0u, // VkPipelineMultisampleStateCreateFlags flags; 277 VK_SAMPLE_COUNT_64_BIT, // VkSampleCountFlagBits rasterizationSamples; 278 VK_FALSE, // VkBool32 sampleShadingEnable; 279 1.0f, // float minSampleShading; 280 nullptr, // const VkSampleMask* pSampleMask; 281 VK_FALSE, // VkBool32 alphaToCoverageEnable; 282 VK_FALSE, // VkBool32 alphaToOneEnable; 283 }; 284 285 const std::vector<VkViewport> staticViewports (1u, makeViewport(0u, 0u)); 286 const std::vector<VkRect2D> staticScissors (1u, makeRect2D(0u, 0u)); 287 const PipelineLayoutWrapper pipelineLayout (constructionType, ctx.vkd, ctx.device, *setLayout); 288 const auto renderArea = makeRect2D(fbExtent); 289 const int halfWidth = fbExtent.x() / 2; 290 const uint32_t halfWidthU = static_cast<uint32_t>(halfWidth); 291 const float halfWidthF = static_cast<float>(halfWidth); 292 const float heightF = static_cast<float>(vkExtent.height); 293 const std::vector<VkRect2D> dynamicScissors { makeRect2D(0, 0, halfWidthU, vkExtent.height), makeRect2D(halfWidth, 0, halfWidthU, vkExtent.height) }; 294 const std::vector<VkViewport> dynamicViewports 295 { 296 makeViewport(0.0f, 0.0f, halfWidthF, heightF, 0.0f, 1.0f), 297 makeViewport(halfWidthF, 0.0f, halfWidthF, heightF, 0.0f, 1.0f), 298 }; 299 300 using WrapperPtr = std::unique_ptr<GraphicsPipelineWrapper>; 301 using WrapperVec = std::vector<WrapperPtr>; 302 303 WrapperVec wrappers; 304 305 for (const auto sampleShadingEnable : { false, true }) 306 { 307 multisampleStateCreateInfo.sampleShadingEnable = sampleShadingEnable; 308 309 WrapperPtr pipelineWrapper(new GraphicsPipelineWrapper(ctx.vki, ctx.vkd, ctx.physicalDevice, ctx.device, context.getDeviceExtensions(), constructionType)); 310 pipelineWrapper->setDefaultTopology(topology) 311 .setDefaultRasterizationState() 312 .setDefaultColorBlendState() 313 .setDynamicState(&dynamicStateInfo) 314 .setupVertexInputState(&vertexInputStateCreateInfo) 315 .setupPreRasterizationShaderState( 316 staticViewports, 317 staticScissors, 318 pipelineLayout, 319 *renderPass, 320 0u, 321 vertModule) 322 .setupFragmentShaderState( 323 pipelineLayout, 324 *renderPass, 325 0u, 326 fragModule, 327 nullptr, 328 &multisampleStateCreateInfo) 329 .setupFragmentOutputState( 330 *renderPass, 331 0u, 332 nullptr, 333 &multisampleStateCreateInfo) 334 .setMonolithicPipelineLayout(pipelineLayout) 335 .buildPipeline(); 336 337 wrappers.emplace_back(std::move(pipelineWrapper)); 338 } 339 340 CommandPoolWithBuffer cmd (ctx.vkd, ctx.device, ctx.qfIndex); 341 const auto cmdBuffer = cmd.cmdBuffer.get(); 342 343 beginCommandBuffer(ctx.vkd, cmdBuffer); 344 renderPass.begin(ctx.vkd, cmdBuffer, renderArea, clearColor); 345 for (uint32_t drawIdx = 0u; drawIdx < kNumDraws; ++drawIdx) 346 { 347 wrappers.at(drawIdx)->bind(cmdBuffer); 348 if (drawIdx == 0u) 349 { 350#ifndef CTS_USES_VULKANSC 351 ctx.vkd.cmdSetRasterizationSamplesEXT(cmdBuffer, kMultiSampleCount); 352#else 353 DE_ASSERT(false); 354#endif // CTS_USES_VULKANSC 355 } 356#ifndef CTS_USES_VULKANSC 357 if (isConstructionTypeShaderObject(constructionType)) 358 { 359 ctx.vkd.cmdSetScissorWithCount(cmdBuffer, 1u, &dynamicScissors.at(drawIdx)); 360 ctx.vkd.cmdSetViewportWithCount(cmdBuffer, 1u, &dynamicViewports.at(drawIdx)); 361 } 362 else 363#endif // CTS_USES_VULKANSC 364 { 365 ctx.vkd.cmdSetScissor(cmdBuffer, 0u, 1u, &dynamicScissors.at(drawIdx)); 366 ctx.vkd.cmdSetViewport(cmdBuffer, 0u, 1u, &dynamicViewports.at(drawIdx)); 367 } 368 ctx.vkd.cmdBindDescriptorSets(cmdBuffer, bindPoint, *pipelineLayout, 0u, 1u, &descriptorSets.at(drawIdx).get(), 0u, nullptr); 369 ctx.vkd.cmdDraw(cmdBuffer, kVertexCount, 1u, 0u, 0u); 370 } 371 renderPass.end(ctx.vkd, cmdBuffer); 372 copyImageToBuffer( 373 ctx.vkd, 374 cmdBuffer, 375 resolveBuffer.getImage(), 376 resolveBuffer.getBuffer(), 377 fbExtent.swizzle(0, 1), 378 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 379 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 380 1u, 381 VK_IMAGE_ASPECT_COLOR_BIT, 382 VK_IMAGE_ASPECT_COLOR_BIT, 383 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); 384 endCommandBuffer(ctx.vkd, cmdBuffer); 385 submitCommandsAndWait(ctx.vkd, ctx.device, ctx.queue, cmdBuffer); 386 387 // Verify resolve buffer and counter buffers. 388 auto& log = context.getTestContext().getLog(); 389 { 390 const tcu::Vec4 threshold (0.0f, 0.0f, 0.0f, 0.0f); // Expect exact results. 391 const auto tcuFormat = mapVkFormat(colorFormat); 392 const auto& resolveBufferAlloc = resolveBuffer.getBufferAllocation(); 393 const auto resolveBufferData = resolveBufferAlloc.getHostPtr(); 394 395 invalidateAlloc(ctx.vkd, ctx.device, resolveBufferAlloc); 396 const tcu::ConstPixelBufferAccess resultAccess(tcuFormat, fbExtent, resolveBufferData); 397 398 if (!tcu::floatThresholdCompare(log, "Result", "", geomColor, resultAccess, threshold, tcu::COMPARE_LOG_ON_ERROR)) 399 return tcu::TestStatus::fail("Unexpected color buffer results -- check log for details"); 400 } 401 { 402 std::vector<uint32_t> counterResults (kNumDraws, 0u); 403 for (uint32_t drawIdx = 0u; drawIdx < kNumDraws; ++drawIdx) 404 { 405 const auto& bufferAlloc = counterBuffers.at(drawIdx)->getAllocation(); 406 invalidateAlloc(ctx.vkd, ctx.device, bufferAlloc); 407 deMemcpy(&counterResults.at(drawIdx), bufferAlloc.getHostPtr(), sizeof(counterResults.at(drawIdx))); 408 log << tcu::TestLog::Message << "Draw " << drawIdx << ": " << counterResults.at(drawIdx) << " invocations" << tcu::TestLog::EndMessage; 409 } 410 411 // The first result is run without sample shading enabled, so it can have any value from 1 to 4 invocations per pixel. 412 // The second result runs with sample shading enabled, so it must have exactly 4 invocations per pixel. 413 const uint32_t minInvs = (vkExtent.width * vkExtent.height) / 2u; 414 const uint32_t maxInvs = minInvs * static_cast<uint32_t>(kMultiSampleCount); 415 416 verifyValueInRange(counterResults.at(0u), minInvs, maxInvs, "invocation counter without sample shading"); 417 verifyValueInRange(counterResults.at(1u), maxInvs, maxInvs, "invocation counter with sample shading"); 418 } 419 420 return tcu::TestStatus::pass("Pass"); 421} 422 423using GroupPtr = de::MovePtr<tcu::TestCaseGroup>; 424 425} // anonymous namespace 426 427tcu::TestCaseGroup* createExtendedDynamicStateMiscTests (tcu::TestContext& testCtx, vk::PipelineConstructionType pipelineConstructionType) 428{ 429 GroupPtr miscGroup (new tcu::TestCaseGroup(testCtx, "misc")); 430 addFunctionCaseWithPrograms(miscGroup.get(), "sample_shading_dynamic_sample_count", sampleShadingWithDynamicSampleCountSupport, sampleShadingWithDynamicSampleCountPrograms, sampleShadingWithDynamicSampleCount, pipelineConstructionType); 431 return miscGroup.release(); 432} 433 434} // pipeline 435} // vkt 436