1/*------------------------------------------------------------------------ 2* Vulkan Conformance Tests 3* ------------------------ 4* 5* Copyright (c) 2022 The Khronos Group Inc. 6* Copyright (c) 2022 Valve Corporation. 7* Copyright (c) 2023 LunarG, Inc. 8* Copyright (c) 2023 Nintendo 9* 10* Licensed under the Apache License, Version 2.0 (the "License"); 11* you may not use this file except in compliance with the License. 12* You may obtain a copy of the License at 13* 14* http://www.apache.org/licenses/LICENSE-2.0 15* 16* Unless required by applicable law or agreed to in writing, software 17* distributed under the License is distributed on an "AS IS" BASIS, 18* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19* See the License for the specific language governing permissions and 20* limitations under the License. 21* 22*//* 23 * \file 24 * \brief Tests involving dynamic patch control points 25*//*--------------------------------------------------------------------*/ 26 27#include "vktPipelineDynamicControlPoints.hpp" 28#include "vktTestCase.hpp" 29#include "vkPrograms.hpp" 30#include "vkRefUtil.hpp" 31#include "vktTestCaseUtil.hpp" 32#include "vkImageWithMemory.hpp" 33 34#include "vktPipelineImageUtil.hpp" 35#include "vktTestCase.hpp" 36 37#include "vkDefs.hpp" 38#include "vkTypeUtil.hpp" 39#include "vkQueryUtil.hpp" 40#include "vkObjUtil.hpp" 41#include "vkBufferWithMemory.hpp" 42#include "vkImageWithMemory.hpp" 43#include "vkBuilderUtil.hpp" 44#include "vkCmdUtil.hpp" 45#include "vkImageUtil.hpp" 46 47#include "tcuVector.hpp" 48#include "tcuMaybe.hpp" 49#include "tcuImageCompare.hpp" 50#include "tcuDefs.hpp" 51#include "tcuTextureUtil.hpp" 52#include "tcuTestLog.hpp" 53#include "tcuVectorUtil.hpp" 54#include "tcuStringTemplate.hpp" 55 56#include "deUniquePtr.hpp" 57#include "deStringUtil.hpp" 58 59#include <vector> 60#include <sstream> 61#include <algorithm> 62#include <utility> 63#include <iterator> 64#include <string> 65#include <limits> 66#include <memory> 67#include <functional> 68#include <cstddef> 69#include <set> 70 71namespace vkt 72{ 73namespace pipeline 74{ 75 76struct TestConfig { 77 vk::PipelineConstructionType constructionType; 78 bool changeOutput; 79 bool firstClockwise; 80 bool secondClockwise; 81 vk::VkCullModeFlags cullMode; 82 tcu::Vec4 expectedFirst; 83 tcu::Vec4 expectedSecond; 84}; 85 86class DynamicControlPointsTestCase : public vkt::TestCase 87{ 88public: 89 DynamicControlPointsTestCase(tcu::TestContext& context, const std::string& name, TestConfig config); 90 void initPrograms (vk::SourceCollections& programCollection) const override; 91 TestInstance* createInstance (Context& context) const override; 92 void checkSupport (Context& context) const override; 93 94private: 95 TestConfig m_config; 96}; 97 98 99class DynamicControlPointsTestInstance : public vkt::TestInstance 100{ 101public: 102 DynamicControlPointsTestInstance(Context& context, TestConfig config); 103 virtual tcu::TestStatus iterate (void); 104private: 105 TestConfig m_config; 106}; 107 108 109DynamicControlPointsTestCase::DynamicControlPointsTestCase(tcu::TestContext& context, const std::string& name, 110 TestConfig config) : vkt::TestCase (context, name), m_config(config) 111{ 112} 113 114void DynamicControlPointsTestCase::checkSupport(Context& context) const 115{ 116 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_TESSELLATION_SHADER); 117 checkPipelineConstructionRequirements(context.getInstanceInterface(), context.getPhysicalDevice(), m_config.constructionType); 118 const auto& eds2Features = context.getExtendedDynamicState2FeaturesEXT(); 119 if (!eds2Features.extendedDynamicState2PatchControlPoints) { 120 TCU_THROW(NotSupportedError, "Dynamic patch control points aren't supported"); 121 } 122} 123 124void DynamicControlPointsTestCase::initPrograms(vk::SourceCollections& collection) const 125{ 126 const std::string firstWinding = m_config.firstClockwise ? "cw" : "ccw"; 127 const std::string secondWinding = m_config.secondClockwise ? "cw" : "ccw"; 128 129 { 130 std::ostringstream src; 131 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 132 << "vec2 positions[6] = vec2[](\n" 133 << " vec2(-1.0, -1.0)," 134 << " vec2(-1.0, 1.0)," 135 << " vec2(1.0, -1.0)," 136 << " vec2(1.0, -1.0)," 137 << " vec2(-1.0, 1.0)," 138 << " vec2(1.0, 1.0)" 139 << ");\n" 140 << "void main() {\n" 141 << " gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);\n" 142 << "}"; 143 collection.glslSources.add("vert") << glu::VertexSource(src.str()); 144 } 145 146 { 147 std::ostringstream src; 148 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 149 << "layout(location = 0) out vec4 outColor;\n" 150 << "layout(location = 0) in vec3 fragColor;" 151 << "void main() {\n" 152 << " outColor = vec4(fragColor, 1.0);\n" 153 << "}"; 154 collection.glslSources.add("frag") << glu::FragmentSource(src.str()); 155 } 156 157 { 158 std::ostringstream src; 159 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 160 << "layout(vertices = 3) out;\n" 161 << "void main (void)\n" 162 << "{\n" 163 << " gl_TessLevelInner[0] = 2.0;\n" 164 << " gl_TessLevelOuter[0] = 2.0;\n" 165 << " gl_TessLevelOuter[1] = 2.0;\n" 166 << " gl_TessLevelOuter[2] = 2.0;\n" 167 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 168 << "}\n"; 169 collection.glslSources.add("tesc") << glu::TessellationControlSource(src.str()); 170 } 171 172 { 173 std::ostringstream src; 174 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 175 << "layout(triangles, " << firstWinding << ") in;\n" 176 << "layout(location = 0) out vec3 fragColor;" 177 << "\n" 178 << "void main (void)\n" 179 << "{\n" 180 << " vec4 p0 = gl_TessCoord.x * gl_in[0].gl_Position;\n" 181 << " vec4 p1 = gl_TessCoord.y * gl_in[1].gl_Position;\n" 182 << " vec4 p2 = gl_TessCoord.z * gl_in[2].gl_Position;\n" 183 << " gl_Position = p0 + p1 + p2;\n" 184 << " fragColor = vec3(1.0, 0.0, 1.0); " 185 << "}\n"; 186 collection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str()); 187 } 188 189 { 190 std::ostringstream src; 191 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 192 << "layout(vertices = " << (m_config.changeOutput ? 4 : 3) << ") out;\n" 193 << "void main (void)\n" 194 << "{\n" 195 << " gl_TessLevelInner[0] = 2;\n" 196 << " gl_TessLevelOuter[0] = 2.0;\n" 197 << " gl_TessLevelOuter[1] = 2.0;\n" 198 << " gl_TessLevelOuter[2] = 2.0;\n" 199 << "if (gl_InvocationID < 3) {" 200 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 201 << "} else {" 202 << " gl_out[gl_InvocationID].gl_Position = vec4(1.0, 0.0, 1.0, 1.0);" 203 << "}" 204 << "}\n"; 205 collection.glslSources.add("tesc2") << glu::TessellationControlSource(src.str()); 206 } 207 208 { 209 std::ostringstream src; 210 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 211 << "layout(triangles, " << secondWinding << ") in;\n" 212 << "layout(location = 0) out vec3 fragColor;" 213 << "\n" 214 << "void main (void)\n" 215 << "{\n" 216 << " vec4 p0 = gl_TessCoord.x * gl_in[0].gl_Position;\n" 217 << " vec4 p1 = gl_TessCoord.y * gl_in[1].gl_Position;\n" 218 << " vec4 p2 = gl_TessCoord.z * gl_in[2].gl_Position;\n" 219 << " gl_Position = p0 + p1 + p2;\n"; 220 if (m_config.changeOutput) { 221 src << " fragColor = vec3(gl_in[3].gl_Position.xyz);"; 222 } else { 223 src << " fragColor = vec3(1.0, 0.0, 1.0);"; 224 } 225 src << "}\n"; 226 collection.glslSources.add("tese2") << glu::TessellationEvaluationSource(src.str()); 227 } 228} 229 230TestInstance* DynamicControlPointsTestCase::createInstance(Context& context) const 231{ 232 return new DynamicControlPointsTestInstance(context, m_config); 233} 234 235DynamicControlPointsTestInstance::DynamicControlPointsTestInstance(Context& context, TestConfig config) : 236 vkt::TestInstance (context), m_config (config) { } 237 238//make a buffer to read an image back after rendering 239std::unique_ptr<vk::BufferWithMemory> makeBufferForImage(const vk::DeviceInterface& vkd, const vk::VkDevice device, vk::Allocator& allocator, tcu::TextureFormat tcuFormat, vk::VkExtent3D imageExtent) 240{ 241 const auto outBufferSize = static_cast<vk::VkDeviceSize>(static_cast<uint32_t>(tcu::getPixelSize(tcuFormat)) * imageExtent.width * imageExtent.height); 242 const auto outBufferUsage = vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT; 243 const auto outBufferInfo = makeBufferCreateInfo(outBufferSize, outBufferUsage); 244 auto outBuffer = std::unique_ptr<vk::BufferWithMemory>(new vk::BufferWithMemory(vkd, device, allocator, outBufferInfo, vk::MemoryRequirement::HostVisible)); 245 246 return outBuffer; 247} 248 249tcu::TestStatus DynamicControlPointsTestInstance::iterate(void) 250{ 251 const auto& vki = m_context.getInstanceInterface(); 252 const auto& vkd = m_context.getDeviceInterface(); 253 const auto physicalDevice = m_context.getPhysicalDevice(); 254 const auto device = m_context.getDevice(); 255 auto& alloc = m_context.getDefaultAllocator(); 256 auto imageFormat = vk::VK_FORMAT_R8G8B8A8_UNORM; 257 auto imageExtent = vk::makeExtent3D(4, 4, 1u); 258 auto mappedFormat = mapVkFormat(imageFormat); 259 260 const tcu::IVec3 imageDim (static_cast<int>(imageExtent.width), static_cast<int>(imageExtent.height), static_cast<int>(imageExtent.depth)); 261 const tcu::IVec2 imageSize (imageDim.x(), imageDim.y()); 262 263 de::MovePtr<vk::ImageWithMemory> colorAttachment; 264 265 vk::GraphicsPipelineWrapper pipeline1(vki, vkd, physicalDevice, device, m_context.getDeviceExtensions(), m_config.constructionType); 266 vk::GraphicsPipelineWrapper pipeline2(vki, vkd, physicalDevice, device, m_context.getDeviceExtensions(), m_config.constructionType); 267 const auto qIndex = m_context.getUniversalQueueFamilyIndex(); 268 269 const auto imageUsage = static_cast<vk::VkImageUsageFlags>(vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT); 270 const vk::VkImageCreateInfo imageCreateInfo = 271 { 272 vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; 273 nullptr, // const void* pNext; 274 0u, // VkImageCreateFlags flags; 275 vk::VK_IMAGE_TYPE_2D, // VkImageType imageType; 276 imageFormat, // VkFormat format; 277 imageExtent, // VkExtent3D extent; 278 1u, // deUint32 mipLevels; 279 1u, // deUint32 arrayLayers; 280 vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; 281 vk::VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; 282 imageUsage, // VkImageUsageFlags usage; 283 vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 284 0, // deUint32 queueFamilyIndexCount; 285 nullptr, // const deUint32* pQueueFamilyIndices; 286 vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; 287 }; 288 289 const auto subresourceRange = vk::makeImageSubresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u); 290 colorAttachment = de::MovePtr<vk::ImageWithMemory>(new vk::ImageWithMemory(vkd, device, alloc, imageCreateInfo, vk::MemoryRequirement::Any)); 291 auto colorAttachmentView = vk::makeImageView(vkd, device, colorAttachment->get(), vk::VK_IMAGE_VIEW_TYPE_2D, imageFormat, subresourceRange); 292 293 vk::RenderPassWrapper renderPass (m_config.constructionType, vkd, device, imageFormat); 294 renderPass.createFramebuffer(vkd, device, **colorAttachment, colorAttachmentView.get(), imageExtent.width, imageExtent.height); 295 296 //buffer to read the output image 297 auto outBuffer = makeBufferForImage(vkd, device, alloc, mappedFormat, imageExtent); 298 auto& outBufferAlloc = outBuffer->getAllocation(); 299 void* outBufferData = outBufferAlloc.getHostPtr(); 300 301 const vk::VkPipelineVertexInputStateCreateInfo vertexInputState = vk::initVulkanStructure(); 302 303 const std::vector<vk::VkViewport> viewport_left { vk::makeViewport(0.0f, 0.0f, (float)imageExtent.width / 2, (float)imageExtent.height, 0.0f, 1.0f) }; 304 const std::vector<vk::VkViewport> viewport_right { vk::makeViewport((float)imageExtent.width / 2, 0.0f, (float)imageExtent.width / 2, (float)imageExtent.height, 0.0f, 1.0f) }; 305 const std::vector<vk::VkRect2D> scissors_left { vk::makeRect2D(0.0f, 0.0f, imageExtent.width / 2, imageExtent.height) }; 306 const std::vector<vk::VkRect2D> scissors_right { vk::makeRect2D(imageExtent.width / 2, 0.0, imageExtent.width / 2, imageExtent.height) }; 307 const vk::PipelineLayoutWrapper graphicsPipelineLayout (m_config.constructionType, vkd, device); 308 309 auto vtxshader = vk::ShaderWrapper(vkd, device, m_context.getBinaryCollection().get("vert")); 310 auto frgshader = vk::ShaderWrapper(vkd, device, m_context.getBinaryCollection().get("frag")); 311 auto tscshader1 = vk::ShaderWrapper(vkd, device, m_context.getBinaryCollection().get("tesc")); 312 auto tscshader2 = vk::ShaderWrapper(vkd, device, 313 m_config.changeOutput ? m_context.getBinaryCollection().get("tesc2") : m_context.getBinaryCollection().get("tesc")); 314 auto tseshader1 = vk::ShaderWrapper(vkd, device, m_context.getBinaryCollection().get("tese")); 315 auto tseshader2 = vk::ShaderWrapper(vkd, device, m_context.getBinaryCollection().get("tese2")); 316 317 vk::VkPipelineRasterizationStateCreateInfo raster = { 318 vk::VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType 319 DE_NULL, // const void* pNext 320 0u, // VkPipelineRasterizationStateCreateFlags flags 321 VK_FALSE, // VkBool32 depthClampEnable 322 VK_FALSE, // VkBool32 rasterizerDiscardEnable 323 vk::VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode 324 m_config.cullMode, // VkCullModeFlags cullMode 325 vk::VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace 326 VK_FALSE, // VkBool32 depthBiasEnable 327 0.0f, // float depthBiasConstantFactor 328 0.0f, // float depthBiasClamp 329 0.0f, // float depthBiasSlopeFactor 330 1.0f // float lineWidth 331 }; 332 333 vk::VkDynamicState dynamicStates = vk::VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT; 334 vk::VkPipelineDynamicStateCreateInfo dynamicInfo = { 335 vk::VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, 336 nullptr, 337 0, 338 1, 339 &dynamicStates 340 }; 341 342 pipeline1.setDefaultTopology(vk::VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) 343 .setDynamicState(&dynamicInfo) 344 .setDefaultRasterizationState() 345 .setDefaultMultisampleState() 346 .setDefaultDepthStencilState() 347 .setDefaultColorBlendState() 348 .setupVertexInputState(&vertexInputState) 349 .setupPreRasterizationShaderState( 350 viewport_left, 351 scissors_left, 352 graphicsPipelineLayout, 353 *renderPass, 354 0u, 355 vtxshader, &raster, 356 tscshader1, tseshader1) 357 .setupFragmentShaderState(graphicsPipelineLayout, *renderPass, 0u, 358 frgshader, 0) 359 .setupFragmentOutputState(*renderPass, 0u) 360 .setMonolithicPipelineLayout(graphicsPipelineLayout).buildPipeline(); 361 362 pipeline2.setDefaultTopology(vk::VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) 363 .setDynamicState(&dynamicInfo) 364 .setDefaultRasterizationState() 365 .setDefaultMultisampleState() 366 .setDefaultDepthStencilState() 367 .setDefaultColorBlendState() 368 .setupVertexInputState(&vertexInputState) 369 .setupPreRasterizationShaderState( 370 viewport_right, 371 scissors_right, 372 graphicsPipelineLayout, 373 *renderPass, 374 0u, 375 vtxshader, &raster, 376 tscshader2, tseshader2) 377 .setupFragmentShaderState(graphicsPipelineLayout, *renderPass, 0u, 378 frgshader, 0) 379 .setupFragmentOutputState(*renderPass, 0u) 380 .setMonolithicPipelineLayout(graphicsPipelineLayout).buildPipeline(); 381 382 auto commandPool = createCommandPool(vkd, device, vk::VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, qIndex); 383 auto commandBuffer = vk::allocateCommandBuffer(vkd, device, commandPool.get(), vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY); 384 385 const tcu::Vec4 clearColor(1.0f, 1.0f, 1.0f, 1.0f); 386 387 const vk::VkRect2D renderArea = 388 { 389 { 0u, 0u }, 390 { imageExtent.width, imageExtent.height } 391 }; 392 393 //render 2 triangles with each pipeline, covering the entire screen 394 //depending on the test settings one of them might be culled 395 vk::beginCommandBuffer(vkd, commandBuffer.get()); 396 renderPass.begin(vkd, *commandBuffer, renderArea, clearColor); 397 vkd.cmdSetPatchControlPointsEXT(commandBuffer.get(), 3); 398 pipeline1.bind(commandBuffer.get()); 399 vkd.cmdDraw(commandBuffer.get(), 6, 1, 0, 0); 400 pipeline2.bind(commandBuffer.get()); 401 vkd.cmdDraw(commandBuffer.get(), 6, 1, 0, 0); 402 renderPass.end(vkd, commandBuffer.get()); 403 vk::copyImageToBuffer(vkd, commandBuffer.get(), colorAttachment.get()->get(), (*outBuffer).get(), imageSize); 404 vk::endCommandBuffer(vkd, commandBuffer.get()); 405 vk::submitCommandsAndWait(vkd, device, m_context.getUniversalQueue(), commandBuffer.get()); 406 407 invalidateAlloc(vkd, device, outBufferAlloc); 408 tcu::ConstPixelBufferAccess outPixels(mappedFormat, imageDim, outBufferData); 409 410 auto expectedFirst = m_config.expectedFirst; 411 auto expectedSecond = m_config.expectedSecond; 412 413 tcu::TextureLevel referenceLevel(mappedFormat, imageExtent.height, imageExtent.height); 414 tcu::PixelBufferAccess reference = referenceLevel.getAccess(); 415 tcu::clear(getSubregion(reference, 0, 0, imageExtent.width / 2, imageExtent.height), expectedFirst); 416 tcu::clear(getSubregion(reference, imageExtent.width / 2, 0, imageExtent.width / 2, imageExtent.height), expectedSecond); 417 418 if (!tcu::floatThresholdCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", reference, outPixels, tcu::Vec4(0.0), tcu::COMPARE_LOG_ON_ERROR)) 419 return tcu::TestStatus::fail("Color output does not match reference, image added to log"); 420 421 return tcu::TestStatus::pass("Pass"); 422} 423 424tcu::TestCaseGroup* createDynamicControlPointTests (tcu::TestContext& testCtx, vk::PipelineConstructionType pipelineConstructionType) 425{ 426 // Tests checking dynamic bind points and switching pipelines 427 de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "dynamic_control_points")); 428 429 // test switching pipelines with dynamic control points while changing the number of tcs invocations 430 group->addChild(new DynamicControlPointsTestCase(testCtx, "change_output", TestConfig { 431 pipelineConstructionType, 432 true, 433 false, 434 false, 435 vk::VK_CULL_MODE_NONE, 436 tcu::Vec4(1.0, 0.0, 1.0, 1.0), 437 tcu::Vec4(1.0, 0.0, 1.0, 1.0), 438 })); 439 440 // test switching pipelines with dynamic control points while switching winding 441 group->addChild(new DynamicControlPointsTestCase(testCtx, "change_winding", TestConfig { 442 pipelineConstructionType, 443 false, 444 true, 445 false, 446 vk::VK_CULL_MODE_FRONT_BIT, 447 tcu::Vec4(1.0, 1.0, 1.0, 1.0), 448 tcu::Vec4(1.0, 0.0, 1.0, 1.0) 449 })); 450 451 // test switching pipelines with dynamic control points while switching winding and number of tcs invocations 452 group->addChild(new DynamicControlPointsTestCase(testCtx, "change_output_winding", TestConfig { 453 pipelineConstructionType, 454 true, 455 true, 456 false, 457 vk::VK_CULL_MODE_FRONT_BIT, 458 tcu::Vec4(1.0, 1.0, 1.0, 1.0), 459 tcu::Vec4(1.0, 0.0, 1.0, 1.0) 460 })); 461 return group.release(); 462} 463 464} // pipeline 465} // vkt 466