1 /*------------------------------------------------------------------------ 2 * Vulkan Conformance Tests 3 * ------------------------ 4 * 5 * Copyright (c) 2014 The Android Open Source Project 6 * Copyright (c) 2016 The Khronos Group Inc. 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 Tessellation Geometry Interaction - Passthrough 23 *//*--------------------------------------------------------------------*/ 24 25 #include "vktTessellationGeometryPassthroughTests.hpp" 26 #include "vktTestCaseUtil.hpp" 27 #include "vktTessellationUtil.hpp" 28 29 #include "tcuTestLog.hpp" 30 #include "tcuImageCompare.hpp" 31 32 #include "vkDefs.hpp" 33 #include "vkQueryUtil.hpp" 34 #include "vkBuilderUtil.hpp" 35 #include "vkTypeUtil.hpp" 36 #include "vkImageUtil.hpp" 37 38 #include "deUniquePtr.hpp" 39 40 #include <string> 41 #include <vector> 42 43 namespace vkt 44 { 45 namespace tessellation 46 { 47 48 using namespace vk; 49 50 namespace 51 { 52 53 void addVertexAndFragmentShaders (vk::SourceCollections& programCollection) 54 { 55 // Vertex shader 56 { 57 std::ostringstream src; 58 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 59 << "\n" 60 << "layout(location = 0) in highp vec4 a_position;\n" 61 << "layout(location = 0) out highp vec4 v_vertex_color;\n" 62 << "\n" 63 << "void main (void)\n" 64 << "{\n" 65 << " gl_Position = a_position;\n" 66 << " v_vertex_color = vec4(a_position.x * 0.5 + 0.5, a_position.y * 0.5 + 0.5, 1.0, 0.4);\n" 67 << "}\n"; 68 69 programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); 70 } 71 72 // Fragment shader 73 { 74 std::ostringstream src; 75 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 76 << "\n" 77 << "layout(location = 0) in highp vec4 v_fragment_color;\n" 78 << "layout(location = 0) out mediump vec4 fragColor;\n" 79 << "void main (void)\n" 80 << "{\n" 81 << " fragColor = v_fragment_color;\n" 82 << "}\n"; 83 84 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str()); 85 } 86 } 87 88 //! Tessellation evaluation shader used in passthrough geometry shader case. 89 std::string generateTessellationEvaluationShader (const TessPrimitiveType primitiveType, const std::string& colorOutputName) 90 { 91 std::ostringstream src; 92 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 93 << "#extension GL_EXT_tessellation_shader : require\n" 94 << "layout(" << getTessPrimitiveTypeShaderName(primitiveType) << ") in;\n" 95 << "\n" 96 << "layout(location = 0) in highp vec4 v_patch_color[];\n" 97 << "layout(location = 0) out highp vec4 " << colorOutputName << ";\n" 98 << "\n" 99 << "// note: No need to use precise gl_Position since we do not require gapless geometry\n" 100 << "void main (void)\n" 101 << "{\n"; 102 103 if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 104 src << " vec3 weights = vec3(pow(gl_TessCoord.x, 1.3), pow(gl_TessCoord.y, 1.3), pow(gl_TessCoord.z, 1.3));\n" 105 << " vec3 cweights = gl_TessCoord;\n" 106 << " gl_Position = vec4(weights.x * gl_in[0].gl_Position.xyz + weights.y * gl_in[1].gl_Position.xyz + weights.z * gl_in[2].gl_Position.xyz, 1.0);\n" 107 << " " << colorOutputName << " = cweights.x * v_patch_color[0] + cweights.y * v_patch_color[1] + cweights.z * v_patch_color[2];\n"; 108 else if (primitiveType == TESSPRIMITIVETYPE_QUADS || primitiveType == TESSPRIMITIVETYPE_ISOLINES) 109 src << " vec2 normalizedCoord = (gl_TessCoord.xy * 2.0 - vec2(1.0));\n" 110 << " vec2 normalizedWeights = normalizedCoord * (vec2(1.0) - 0.3 * cos(normalizedCoord.yx * 1.57));\n" 111 << " vec2 weights = normalizedWeights * 0.5 + vec2(0.5);\n" 112 << " vec2 cweights = gl_TessCoord.xy;\n" 113 << " gl_Position = mix(mix(gl_in[0].gl_Position, gl_in[1].gl_Position, weights.y), mix(gl_in[2].gl_Position, gl_in[3].gl_Position, weights.y), weights.x);\n" 114 << " " << colorOutputName << " = mix(mix(v_patch_color[0], v_patch_color[1], cweights.y), mix(v_patch_color[2], v_patch_color[3], cweights.y), cweights.x);\n"; 115 else 116 DE_ASSERT(false); 117 118 src << "}\n"; 119 120 return src.str(); 121 } 122 123 class IdentityGeometryShaderTestCase : public TestCase 124 { 125 public: 126 void initPrograms (vk::SourceCollections& programCollection) const; 127 TestInstance* createInstance (Context& context) const; 128 129 IdentityGeometryShaderTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType) 130 : TestCase (testCtx, name, description) 131 , m_primitiveType (primitiveType) 132 { 133 } 134 135 private: 136 const TessPrimitiveType m_primitiveType; 137 }; 138 139 void IdentityGeometryShaderTestCase::initPrograms (vk::SourceCollections& programCollection) const 140 { 141 addVertexAndFragmentShaders(programCollection); 142 143 // Tessellation control 144 { 145 std::ostringstream src; 146 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 147 << "#extension GL_EXT_tessellation_shader : require\n" 148 << "layout(vertices = 4) out;\n" 149 << "\n" 150 << "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n" 151 << " float inner0;\n" 152 << " float inner1;\n" 153 << " float outer0;\n" 154 << " float outer1;\n" 155 << " float outer2;\n" 156 << " float outer3;\n" 157 << "} sb_levels;\n" 158 << "\n" 159 << "layout(location = 0) in highp vec4 v_vertex_color[];\n" 160 << "layout(location = 0) out highp vec4 v_patch_color[];\n" 161 << "\n" 162 << "void main (void)\n" 163 << "{\n" 164 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 165 << " v_patch_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n" 166 << "\n" 167 << " gl_TessLevelInner[0] = sb_levels.inner0;\n" 168 << " gl_TessLevelInner[1] = sb_levels.inner1;\n" 169 << " gl_TessLevelOuter[0] = sb_levels.outer0;\n" 170 << " gl_TessLevelOuter[1] = sb_levels.outer1;\n" 171 << " gl_TessLevelOuter[2] = sb_levels.outer2;\n" 172 << " gl_TessLevelOuter[3] = sb_levels.outer3;\n" 173 << "}\n"; 174 175 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str()); 176 } 177 178 // Tessellation evaluation shader 179 { 180 programCollection.glslSources.add("tese_to_frag") 181 << glu::TessellationEvaluationSource(generateTessellationEvaluationShader(m_primitiveType, "v_fragment_color")); 182 programCollection.glslSources.add("tese_to_geom") 183 << glu::TessellationEvaluationSource(generateTessellationEvaluationShader(m_primitiveType, "v_evaluated_color")); 184 } 185 186 // Geometry shader 187 { 188 std::ostringstream src; 189 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 190 << "#extension GL_EXT_geometry_shader : require\n" 191 << "layout(" << getGeometryShaderInputPrimitiveTypeShaderName(m_primitiveType, false) << ") in;\n" 192 << "layout(" << getGeometryShaderOutputPrimitiveTypeShaderName(m_primitiveType, false) 193 << ", max_vertices=" << numVerticesPerPrimitive(m_primitiveType, false) << ") out;\n" 194 << "\n" 195 << "layout(location = 0) in highp vec4 v_evaluated_color[];\n" 196 << "layout(location = 0) out highp vec4 v_fragment_color;\n" 197 << "\n" 198 << "void main (void)\n" 199 << "{\n" 200 << " for (int ndx = 0; ndx < gl_in.length(); ++ndx)\n" 201 << " {\n" 202 << " gl_Position = gl_in[ndx].gl_Position;\n" 203 << " v_fragment_color = v_evaluated_color[ndx];\n" 204 << " EmitVertex();\n" 205 << " }\n" 206 << "}\n"; 207 208 programCollection.glslSources.add("geom") << glu::GeometrySource(src.str()); 209 } 210 } 211 212 class IdentityTessellationShaderTestCase : public TestCase 213 { 214 public: 215 void initPrograms (vk::SourceCollections& programCollection) const; 216 TestInstance* createInstance (Context& context) const; 217 218 IdentityTessellationShaderTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType) 219 : TestCase (testCtx, name, description) 220 , m_primitiveType (primitiveType) 221 { 222 } 223 224 private: 225 const TessPrimitiveType m_primitiveType; 226 }; 227 228 //! Geometry shader used in passthrough tessellation shader case. 229 std::string generateGeometryShader (const TessPrimitiveType primitiveType, const std::string& colorSourceName) 230 { 231 const int numEmitVertices = (primitiveType == TESSPRIMITIVETYPE_ISOLINES ? 11 : 8); 232 233 std::ostringstream src; 234 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 235 << "#extension GL_EXT_geometry_shader : require\n" 236 << "layout(" << getGeometryShaderInputPrimitiveTypeShaderName(primitiveType, false) << ") in;\n" 237 << "layout(" << getGeometryShaderOutputPrimitiveTypeShaderName(primitiveType, false) 238 << ", max_vertices=" << numEmitVertices << ") out;\n" 239 << "\n" 240 << "layout(location = 0) in highp vec4 " << colorSourceName << "[];\n" 241 << "layout(location = 0) out highp vec4 v_fragment_color;\n" 242 << "\n" 243 << "void main (void)\n" 244 << "{\n"; 245 246 if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 247 { 248 src << " vec4 centerPos = (gl_in[0].gl_Position + gl_in[1].gl_Position + gl_in[2].gl_Position) / 3.0f;\n" 249 << "\n" 250 << " for (int ndx = 0; ndx < 4; ++ndx)\n" 251 << " {\n" 252 << " gl_Position = centerPos + (centerPos - gl_in[ndx % 3].gl_Position);\n" 253 << " v_fragment_color = " << colorSourceName << "[ndx % 3];\n" 254 << " EmitVertex();\n" 255 << "\n" 256 << " gl_Position = centerPos + 0.7 * (centerPos - gl_in[ndx % 3].gl_Position);\n" 257 << " v_fragment_color = " << colorSourceName << "[ndx % 3];\n" 258 << " EmitVertex();\n" 259 << " }\n"; 260 } 261 else if (primitiveType == TESSPRIMITIVETYPE_ISOLINES) 262 { 263 src << " vec4 mdir = vec4(gl_in[0].gl_Position.y - gl_in[1].gl_Position.y, gl_in[1].gl_Position.x - gl_in[0].gl_Position.x, 0.0, 0.0);\n" 264 << " for (int i = 0; i <= 10; ++i)\n" 265 << " {\n" 266 << " float xweight = cos(float(i) / 10.0 * 6.28) * 0.5 + 0.5;\n" 267 << " float mweight = sin(float(i) / 10.0 * 6.28) * 0.1 + 0.1;\n" 268 << " gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, xweight) + mweight * mdir;\n" 269 << " v_fragment_color = mix(" << colorSourceName << "[0], " << colorSourceName << "[1], xweight);\n" 270 << " EmitVertex();\n" 271 << " }\n"; 272 } 273 else 274 DE_ASSERT(false); 275 276 src << "}\n"; 277 278 return src.str(); 279 } 280 281 void IdentityTessellationShaderTestCase::initPrograms (vk::SourceCollections& programCollection) const 282 { 283 addVertexAndFragmentShaders(programCollection); 284 285 // Tessellation control 286 { 287 std::ostringstream src; 288 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 289 << "#extension GL_EXT_tessellation_shader : require\n" 290 << "layout(vertices = " << numVerticesPerPrimitive(m_primitiveType, false) << ") out;\n" 291 << "\n" 292 << "layout(location = 0) in highp vec4 v_vertex_color[];\n" 293 << "layout(location = 0) out highp vec4 v_control_color[];\n" 294 << "\n" 295 << "void main (void)\n" 296 << "{\n" 297 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 298 << " v_control_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n" 299 << "\n" 300 << " gl_TessLevelInner[0] = 1.0;\n" 301 << " gl_TessLevelInner[1] = 1.0;\n" 302 << " gl_TessLevelOuter[0] = 1.0;\n" 303 << " gl_TessLevelOuter[1] = 1.0;\n" 304 << " gl_TessLevelOuter[2] = 1.0;\n" 305 << " gl_TessLevelOuter[3] = 1.0;\n" 306 << "}\n"; 307 308 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str()); 309 } 310 311 // Tessellation evaluation shader 312 { 313 std::ostringstream src; 314 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 315 << "#extension GL_EXT_tessellation_shader : require\n" 316 << "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ") in;\n" 317 << "\n" 318 << "layout(location = 0) in highp vec4 v_control_color[];\n" 319 << "layout(location = 0) out highp vec4 v_evaluated_color;\n" 320 << "\n" 321 << "// note: No need to use precise gl_Position since we do not require gapless geometry\n" 322 << "void main (void)\n" 323 << "{\n"; 324 325 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 326 src << " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position + gl_TessCoord.y * gl_in[1].gl_Position + gl_TessCoord.z * gl_in[2].gl_Position;\n" 327 << " v_evaluated_color = gl_TessCoord.x * v_control_color[0] + gl_TessCoord.y * v_control_color[1] + gl_TessCoord.z * v_control_color[2];\n"; 328 else if (m_primitiveType == TESSPRIMITIVETYPE_ISOLINES) 329 src << " gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x);\n" 330 << " v_evaluated_color = mix(v_control_color[0], v_control_color[1], gl_TessCoord.x);\n"; 331 else 332 DE_ASSERT(false); 333 334 src << "}\n"; 335 336 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str()); 337 } 338 339 // Geometry shader 340 { 341 programCollection.glslSources.add("geom_from_tese") << glu::GeometrySource( 342 generateGeometryShader(m_primitiveType, "v_evaluated_color")); 343 programCollection.glslSources.add("geom_from_vert") << glu::GeometrySource( 344 generateGeometryShader(m_primitiveType, "v_vertex_color")); 345 } 346 } 347 348 inline tcu::ConstPixelBufferAccess getPixelBufferAccess (const DeviceInterface& vk, 349 const VkDevice device, 350 const Buffer& colorBuffer, 351 const VkFormat colorFormat, 352 const VkDeviceSize colorBufferSizeBytes, 353 const tcu::IVec2& renderSize) 354 { 355 const Allocation& alloc = colorBuffer.getAllocation(); 356 invalidateMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), colorBufferSizeBytes); 357 return tcu::ConstPixelBufferAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, alloc.getHostPtr()); 358 } 359 360 //! When a test case disables tessellation stage and we need to derive a primitive type. 361 VkPrimitiveTopology getPrimitiveTopology (const TessPrimitiveType primitiveType) 362 { 363 switch (primitiveType) 364 { 365 case TESSPRIMITIVETYPE_TRIANGLES: 366 case TESSPRIMITIVETYPE_QUADS: 367 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; 368 369 case TESSPRIMITIVETYPE_ISOLINES: 370 return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; 371 372 default: 373 DE_ASSERT(false); 374 return VK_PRIMITIVE_TOPOLOGY_LAST; 375 } 376 } 377 378 enum Constants 379 { 380 PIPELINE_CASES = 2, 381 RENDER_SIZE = 256, 382 }; 383 384 class PassthroughTestInstance : public TestInstance 385 { 386 public: 387 struct PipelineDescription 388 { 389 bool useTessellation; 390 bool useGeometry; 391 std::string tessEvalShaderName; 392 std::string geomShaderName; 393 std::string description; 394 395 PipelineDescription (void) : useTessellation(), useGeometry() {} 396 }; 397 398 struct Params 399 { 400 bool useTessLevels; 401 TessLevels tessLevels; 402 TessPrimitiveType primitiveType; 403 int inputPatchVertices; 404 std::vector<tcu::Vec4> vertices; 405 PipelineDescription pipelineCases[PIPELINE_CASES]; //!< Each test case renders with two pipelines and compares results 406 std::string message; 407 408 Params (void) : useTessLevels(), tessLevels(), primitiveType(), inputPatchVertices() {} 409 }; 410 411 PassthroughTestInstance (Context& context, const Params& params) : TestInstance(context), m_params(params) {} 412 tcu::TestStatus iterate (void); 413 414 private: 415 const Params m_params; 416 }; 417 418 tcu::TestStatus PassthroughTestInstance::iterate (void) 419 { 420 requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER); 421 DE_STATIC_ASSERT(PIPELINE_CASES == 2); 422 423 const DeviceInterface& vk = m_context.getDeviceInterface(); 424 const VkDevice device = m_context.getDevice(); 425 const VkQueue queue = m_context.getUniversalQueue(); 426 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); 427 Allocator& allocator = m_context.getDefaultAllocator(); 428 429 // Tessellation levels 430 const Buffer tessLevelsBuffer (vk, device, allocator, makeBufferCreateInfo(sizeof(TessLevels), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible); 431 432 if (m_params.useTessLevels) 433 { 434 const Allocation& alloc = tessLevelsBuffer.getAllocation(); 435 TessLevels* const bufferTessLevels = static_cast<TessLevels*>(alloc.getHostPtr()); 436 *bufferTessLevels = m_params.tessLevels; 437 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels)); 438 } 439 440 // Vertex attributes 441 442 const VkDeviceSize vertexDataSizeBytes = sizeInBytes(m_params.vertices); 443 const VkFormat vertexFormat = VK_FORMAT_R32G32B32A32_SFLOAT; 444 const Buffer vertexBuffer (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible); 445 446 { 447 const Allocation& alloc = vertexBuffer.getAllocation(); 448 deMemcpy(alloc.getHostPtr(), &m_params.vertices[0], static_cast<std::size_t>(vertexDataSizeBytes)); 449 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes); 450 } 451 452 // Descriptors - make descriptor for tessellation levels, even if we don't use them, to simplify code 453 454 const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder() 455 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) 456 .build(vk, device)); 457 458 const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder() 459 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) 460 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u)); 461 462 const Unique<VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout)); 463 const VkDescriptorBufferInfo tessLevelsBufferInfo = makeDescriptorBufferInfo(*tessLevelsBuffer, 0ull, sizeof(TessLevels)); 464 465 DescriptorSetUpdateBuilder() 466 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo) 467 .update(vk, device); 468 469 // Color attachment 470 471 const tcu::IVec2 renderSize = tcu::IVec2(RENDER_SIZE, RENDER_SIZE); 472 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM; 473 const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u); 474 const Image colorAttachmentImage (vk, device, allocator, 475 makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u), 476 MemoryRequirement::Any); 477 478 // Color output buffer: image will be copied here for verification. 479 // We use two buffers, one for each case. 480 481 const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat)); 482 const Buffer colorBuffer1 (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible); 483 const Buffer colorBuffer2 (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible); 484 const Buffer* const colorBuffer[PIPELINE_CASES] = { &colorBuffer1, &colorBuffer2 }; 485 486 // Pipeline 487 488 const Unique<VkImageView> colorAttachmentView(makeImageView (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange)); 489 const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat)); 490 const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u)); 491 const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout (vk, device, *descriptorSetLayout)); 492 const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex)); 493 const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer (vk, device, *cmdPool)); 494 495 // Message explaining the test 496 { 497 tcu::TestLog& log = m_context.getTestContext().getLog(); 498 log << tcu::TestLog::Message << m_params.message << tcu::TestLog::EndMessage; 499 500 if (m_params.useTessLevels) 501 log << tcu::TestLog::Message << "Tessellation levels: " << getTessellationLevelsString(m_params.tessLevels, m_params.primitiveType) << tcu::TestLog::EndMessage; 502 } 503 504 for (int pipelineNdx = 0; pipelineNdx < PIPELINE_CASES; ++pipelineNdx) 505 { 506 const PipelineDescription& pipelineDescription = m_params.pipelineCases[pipelineNdx]; 507 GraphicsPipelineBuilder pipelineBuilder; 508 509 pipelineBuilder 510 .setPrimitiveTopology (getPrimitiveTopology(m_params.primitiveType)) 511 .setRenderSize (renderSize) 512 .setBlend (true) 513 .setVertexInputSingleAttribute(vertexFormat, tcu::getPixelSize(mapVkFormat(vertexFormat))) 514 .setPatchControlPoints (m_params.inputPatchVertices) 515 .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL) 516 .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL); 517 518 if (pipelineDescription.useTessellation) 519 pipelineBuilder 520 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL) 521 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get(pipelineDescription.tessEvalShaderName), DE_NULL); 522 523 if (pipelineDescription.useGeometry) 524 pipelineBuilder 525 .setShader (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT, m_context.getBinaryCollection().get(pipelineDescription.geomShaderName), DE_NULL); 526 527 const Unique<VkPipeline> pipeline (pipelineBuilder.build(vk, device, *pipelineLayout, *renderPass)); 528 529 // Draw commands 530 531 beginCommandBuffer(vk, *cmdBuffer); 532 533 // Change color attachment image layout 534 { 535 // State is slightly different on the first iteration. 536 const VkImageLayout currentLayout = (pipelineNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); 537 const VkAccessFlags srcFlags = (pipelineNdx == 0 ? (VkAccessFlags)0 : (VkAccessFlags)VK_ACCESS_TRANSFER_READ_BIT); 538 539 const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier( 540 srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 541 currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 542 *colorAttachmentImage, colorImageSubresourceRange); 543 544 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u, 545 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier); 546 } 547 548 // Begin render pass 549 { 550 const VkRect2D renderArea = { 551 makeOffset2D(0, 0), 552 makeExtent2D(renderSize.x(), renderSize.y()), 553 }; 554 const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f); 555 556 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor); 557 } 558 559 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 560 { 561 const VkDeviceSize vertexBufferOffset = 0ull; 562 vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset); 563 } 564 565 if (m_params.useTessLevels) 566 vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL); 567 568 vk.cmdDraw(*cmdBuffer, static_cast<deUint32>(m_params.vertices.size()), 1u, 0u, 0u); 569 endRenderPass(vk, *cmdBuffer); 570 571 // Copy render result to a host-visible buffer 572 { 573 const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier( 574 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, 575 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 576 *colorAttachmentImage, colorImageSubresourceRange); 577 578 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 579 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier); 580 } 581 { 582 const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 1), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u)); 583 vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorBuffer[pipelineNdx]->get(), 1u, ©Region); 584 } 585 { 586 const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier( 587 VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, colorBuffer[pipelineNdx]->get(), 0ull, colorBufferSizeBytes); 588 589 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 590 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL); 591 } 592 593 endCommandBuffer(vk, *cmdBuffer); 594 submitCommandsAndWait(vk, device, queue, *cmdBuffer); 595 } 596 597 // Verify results 598 599 tcu::ConstPixelBufferAccess image0 = getPixelBufferAccess(vk, device, *colorBuffer[0], colorFormat, colorBufferSizeBytes, renderSize); 600 tcu::ConstPixelBufferAccess image1 = getPixelBufferAccess(vk, device, *colorBuffer[1], colorFormat, colorBufferSizeBytes, renderSize); 601 602 const tcu::UVec4 colorThreshold (8, 8, 8, 255); 603 const tcu::IVec3 positionDeviation (1, 1, 0); // 3x3 search kernel 604 const bool ignoreOutOfBounds = true; 605 606 tcu::TestLog& log = m_context.getTestContext().getLog(); 607 log << tcu::TestLog::Message 608 << "In image comparison:\n" 609 << " Reference - " << m_params.pipelineCases[0].description << "\n" 610 << " Result - " << m_params.pipelineCases[1].description << "\n" 611 << tcu::TestLog::EndMessage; 612 613 const bool ok = tcu::intThresholdPositionDeviationCompare( 614 log, "ImageCompare", "Image comparison", image0, image1, colorThreshold, positionDeviation, ignoreOutOfBounds, tcu::COMPARE_LOG_RESULT); 615 616 return (ok ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image comparison failed")); 617 } 618 619 TestInstance* IdentityGeometryShaderTestCase::createInstance (Context& context) const 620 { 621 PassthroughTestInstance::Params params; 622 623 const float level = 14.0; 624 params.useTessLevels = true; 625 params.tessLevels.inner[0] = level; 626 params.tessLevels.inner[1] = level; 627 params.tessLevels.outer[0] = level; 628 params.tessLevels.outer[1] = level; 629 params.tessLevels.outer[2] = level; 630 params.tessLevels.outer[3] = level; 631 632 params.primitiveType = m_primitiveType; 633 params.inputPatchVertices = (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4); 634 635 params.vertices.push_back(tcu::Vec4( -0.9f, -0.9f, 0.0f, 1.0f )); 636 params.vertices.push_back(tcu::Vec4( -0.9f, 0.9f, 0.0f, 1.0f )); 637 params.vertices.push_back(tcu::Vec4( 0.9f, -0.9f, 0.0f, 1.0f )); 638 params.vertices.push_back(tcu::Vec4( 0.9f, 0.9f, 0.0f, 1.0f )); 639 640 params.pipelineCases[0].useTessellation = true; 641 params.pipelineCases[0].useGeometry = true; 642 params.pipelineCases[0].tessEvalShaderName = "tese_to_geom"; 643 params.pipelineCases[0].geomShaderName = "geom"; 644 params.pipelineCases[0].description = "passthrough geometry shader"; 645 646 params.pipelineCases[1].useTessellation = true; 647 params.pipelineCases[1].useGeometry = false; 648 params.pipelineCases[1].tessEvalShaderName = "tese_to_frag"; 649 params.pipelineCases[1].geomShaderName = "geom"; 650 params.pipelineCases[1].description = "no geometry shader in the pipeline"; 651 652 params.message = "Testing tessellating shader program output does not change when a passthrough geometry shader is attached.\n" 653 "Rendering two images, first with and second without a geometry shader. Expecting similar results.\n" 654 "Using additive blending to detect overlap.\n"; 655 656 return new PassthroughTestInstance(context, params); 657 }; 658 659 TestInstance* IdentityTessellationShaderTestCase::createInstance (Context& context) const 660 { 661 PassthroughTestInstance::Params params; 662 663 params.useTessLevels = false; 664 params.primitiveType = m_primitiveType; 665 params.inputPatchVertices = (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2); 666 667 params.vertices.push_back( tcu::Vec4( -0.4f, 0.4f, 0.0f, 1.0f )); 668 params.vertices.push_back( tcu::Vec4( 0.0f, -0.5f, 0.0f, 1.0f )); 669 if (params.inputPatchVertices == 3) 670 params.vertices.push_back(tcu::Vec4( 0.4f, 0.4f, 0.0f, 1.0f )); 671 672 params.pipelineCases[0].useTessellation = true; 673 params.pipelineCases[0].useGeometry = true; 674 params.pipelineCases[0].tessEvalShaderName = "tese"; 675 params.pipelineCases[0].geomShaderName = "geom_from_tese"; 676 params.pipelineCases[0].description = "passthrough tessellation shaders"; 677 678 params.pipelineCases[1].useTessellation = false; 679 params.pipelineCases[1].useGeometry = true; 680 params.pipelineCases[1].tessEvalShaderName = "tese"; 681 params.pipelineCases[1].geomShaderName = "geom_from_vert"; 682 params.pipelineCases[1].description = "no tessellation shaders in the pipeline"; 683 684 params.message = "Testing geometry shading shader program output does not change when a passthrough tessellation shader is attached.\n" 685 "Rendering two images, first with and second without a tessellation shader. Expecting similar results.\n" 686 "Using additive blending to detect overlap.\n"; 687 688 return new PassthroughTestInstance(context, params); 689 }; 690 691 inline TestCase* makeIdentityGeometryShaderCase (tcu::TestContext& testCtx, const TessPrimitiveType primitiveType) 692 { 693 return new IdentityGeometryShaderTestCase( 694 testCtx, 695 "tessellate_" + de::toString(getTessPrimitiveTypeShaderName(primitiveType)) + "_passthrough_geometry_no_change", 696 "Passthrough geometry shader has no effect", 697 primitiveType); 698 } 699 700 inline TestCase* makeIdentityTessellationShaderCase (tcu::TestContext& testCtx, const TessPrimitiveType primitiveType) 701 { 702 return new IdentityTessellationShaderTestCase( 703 testCtx, 704 "passthrough_tessellation_geometry_shade_" + de::toString(getTessPrimitiveTypeShaderName(primitiveType)) + "_no_change", 705 "Passthrough tessellation shader has no effect", 706 primitiveType); 707 } 708 709 } // anonymous 710 711 712 //! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.render.passthrough.* 713 tcu::TestCaseGroup* createGeometryPassthroughTests (tcu::TestContext& testCtx) 714 { 715 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "passthrough", "Render various types with either passthrough geometry or tessellation shader")); 716 717 // Passthrough geometry shader 718 group->addChild(makeIdentityGeometryShaderCase(testCtx, TESSPRIMITIVETYPE_TRIANGLES)); 719 group->addChild(makeIdentityGeometryShaderCase(testCtx, TESSPRIMITIVETYPE_QUADS)); 720 group->addChild(makeIdentityGeometryShaderCase(testCtx, TESSPRIMITIVETYPE_ISOLINES)); 721 722 // Passthrough tessellation shader 723 group->addChild(makeIdentityTessellationShaderCase(testCtx, TESSPRIMITIVETYPE_TRIANGLES)); 724 group->addChild(makeIdentityTessellationShaderCase(testCtx, TESSPRIMITIVETYPE_ISOLINES)); 725 726 return group.release(); 727 } 728 729 } // tessellation 730 } // vkt 731