1 /*------------------------------------------------------------------------ 2 * Vulkan Conformance Tests 3 * ------------------------ 4 * 5 * Copyright (c) 2016 The Khronos Group Inc. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 *//*! 20 * \file 21 * \brief Clipping tests 22 *//*--------------------------------------------------------------------*/ 23 24 #include "vktClippingTests.hpp" 25 #include "vktTestCase.hpp" 26 #include "vktTestGroupUtil.hpp" 27 #include "vktTestCaseUtil.hpp" 28 #include "vktDrawUtil.hpp" 29 #include "vkRefUtil.hpp" 30 #include "vkTypeUtil.hpp" 31 #include "vkImageUtil.hpp" 32 #include "tcuImageCompare.hpp" 33 #include "tcuTestLog.hpp" 34 #include "tcuVectorUtil.hpp" 35 #include "deUniquePtr.hpp" 36 #include "deStringUtil.hpp" 37 #include "deRandom.hpp" 38 39 namespace vkt 40 { 41 namespace clipping 42 { 43 namespace 44 { 45 using namespace vk; 46 using de::MovePtr; 47 using tcu::UVec2; 48 using tcu::Vec4; 49 using tcu::IVec2; 50 using namespace drawutil; 51 52 enum TestConstants 53 { 54 RENDER_SIZE = 16, 55 RENDER_SIZE_LARGE = 128, 56 NUM_RENDER_PIXELS = RENDER_SIZE * RENDER_SIZE, 57 NUM_PATCH_CONTROL_POINTS = 3, 58 MAX_CLIP_DISTANCES = 8, 59 MAX_CULL_DISTANCES = 8, 60 MAX_COMBINED_CLIP_AND_CULL_DISTANCES = 8, 61 }; 62 63 enum FeatureFlagBits 64 { 65 FEATURE_TESSELLATION_SHADER = 1u << 0, 66 FEATURE_GEOMETRY_SHADER = 1u << 1, 67 FEATURE_SHADER_FLOAT_64 = 1u << 2, 68 FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS = 1u << 3, 69 FEATURE_FRAGMENT_STORES_AND_ATOMICS = 1u << 4, 70 FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE = 1u << 5, 71 FEATURE_DEPTH_CLAMP = 1u << 6, 72 FEATURE_LARGE_POINTS = 1u << 7, 73 FEATURE_WIDE_LINES = 1u << 8, 74 FEATURE_SHADER_CLIP_DISTANCE = 1u << 9, 75 FEATURE_SHADER_CULL_DISTANCE = 1u << 10, 76 }; 77 typedef deUint32 FeatureFlags; 78 79 void requireFeatures (const InstanceInterface& vki, const VkPhysicalDevice physDevice, const FeatureFlags flags) 80 { 81 const VkPhysicalDeviceFeatures features = getPhysicalDeviceFeatures(vki, physDevice); 82 83 if (((flags & FEATURE_TESSELLATION_SHADER) != 0) && !features.tessellationShader) 84 throw tcu::NotSupportedError("Tessellation shader not supported"); 85 86 if (((flags & FEATURE_GEOMETRY_SHADER) != 0) && !features.geometryShader) 87 throw tcu::NotSupportedError("Geometry shader not supported"); 88 89 if (((flags & FEATURE_SHADER_FLOAT_64) != 0) && !features.shaderFloat64) 90 throw tcu::NotSupportedError("Double-precision floats not supported"); 91 92 if (((flags & FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS) != 0) && !features.vertexPipelineStoresAndAtomics) 93 throw tcu::NotSupportedError("SSBO and image writes not supported in vertex pipeline"); 94 95 if (((flags & FEATURE_FRAGMENT_STORES_AND_ATOMICS) != 0) && !features.fragmentStoresAndAtomics) 96 throw tcu::NotSupportedError("SSBO and image writes not supported in fragment shader"); 97 98 if (((flags & FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE) != 0) && !features.shaderTessellationAndGeometryPointSize) 99 throw tcu::NotSupportedError("Tessellation and geometry shaders don't support PointSize built-in"); 100 101 if (((flags & FEATURE_DEPTH_CLAMP) != 0) && !features.depthClamp) 102 throw tcu::NotSupportedError("Depth clamp not supported"); 103 104 if (((flags & FEATURE_LARGE_POINTS) != 0) && !features.largePoints) 105 throw tcu::NotSupportedError("Large points not supported"); 106 107 if (((flags & FEATURE_WIDE_LINES) != 0) && !features.wideLines) 108 throw tcu::NotSupportedError("Wide lines not supported"); 109 110 if (((flags & FEATURE_SHADER_CLIP_DISTANCE) != 0) && !features.shaderClipDistance) 111 throw tcu::NotSupportedError("Shader ClipDistance not supported"); 112 113 if (((flags & FEATURE_SHADER_CULL_DISTANCE) != 0) && !features.shaderCullDistance) 114 throw tcu::NotSupportedError("Shader CullDistance not supported"); 115 } 116 117 std::vector<Vec4> genVertices (const VkPrimitiveTopology topology, const Vec4& offset, const float slope) 118 { 119 const float p = 1.0f; 120 const float hp = 0.5f; 121 const float z = 0.0f; 122 const float w = 1.0f; 123 124 std::vector<Vec4> vertices; 125 126 // We're setting adjacent vertices to zero where needed, as we don't use them in meaningful way. 127 128 switch (topology) 129 { 130 case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: 131 vertices.push_back(offset + Vec4(0.0f, 0.0f, slope/2.0f + z, w)); 132 vertices.push_back(offset + Vec4( -hp, -hp, z, w)); 133 vertices.push_back(offset + Vec4( hp, -hp, slope + z, w)); 134 vertices.push_back(offset + Vec4( -hp, hp, z, w)); 135 vertices.push_back(offset + Vec4( hp, hp, slope + z, w)); 136 break; 137 138 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: 139 vertices.push_back(offset + Vec4(-p, -p, z, w)); 140 vertices.push_back(offset + Vec4( p, p, slope + z, w)); // line 0 141 vertices.push_back(offset + Vec4( p, p, slope + z, w)); 142 vertices.push_back(offset + Vec4( p, -p, slope + z, w)); // line 1 143 vertices.push_back(offset + Vec4( p, -p, slope + z, w)); 144 vertices.push_back(offset + Vec4(-p, p, z, w)); // line 2 145 break; 146 147 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: 148 vertices.push_back(Vec4()); 149 vertices.push_back(offset + Vec4(-p, -p, z, w)); 150 vertices.push_back(offset + Vec4( p, p, slope + z, w)); // line 0 151 vertices.push_back(Vec4()); 152 vertices.push_back(Vec4()); 153 vertices.push_back(offset + Vec4( p, p, slope + z, w)); 154 vertices.push_back(offset + Vec4( p, -p, slope + z, w)); // line 1 155 vertices.push_back(Vec4()); 156 vertices.push_back(Vec4()); 157 vertices.push_back(offset + Vec4( p, -p, slope + z, w)); 158 vertices.push_back(offset + Vec4(-p, p, z, w)); // line 2 159 vertices.push_back(Vec4()); 160 break; 161 162 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: 163 vertices.push_back(offset + Vec4(-p, -p, z, w)); 164 vertices.push_back(offset + Vec4( p, p, slope + z, w)); // line 0 165 vertices.push_back(offset + Vec4( p, -p, slope + z, w)); // line 1 166 vertices.push_back(offset + Vec4(-p, p, z, w)); // line 2 167 break; 168 169 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY: 170 vertices.push_back(Vec4()); 171 vertices.push_back(offset + Vec4(-p, -p, z, w)); 172 vertices.push_back(offset + Vec4( p, p, slope + z, w)); // line 0 173 vertices.push_back(offset + Vec4( p, -p, slope + z, w)); // line 1 174 vertices.push_back(offset + Vec4(-p, p, z, w)); // line 2 175 vertices.push_back(Vec4()); 176 break; 177 178 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: 179 vertices.push_back(offset + Vec4( p, -p, slope + z, w)); 180 vertices.push_back(offset + Vec4(-p, -p, z, w)); 181 vertices.push_back(offset + Vec4(-p, p, z, w)); // triangle 0 182 vertices.push_back(offset + Vec4(-p, p, z, w)); 183 vertices.push_back(offset + Vec4( p, p, slope + z, w)); 184 vertices.push_back(offset + Vec4( p, -p, slope + z, w)); // triangle 1 185 break; 186 187 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: 188 vertices.push_back(offset + Vec4( p, -p, slope + z, w)); 189 vertices.push_back(Vec4()); 190 vertices.push_back(offset + Vec4(-p, -p, z, w)); 191 vertices.push_back(Vec4()); 192 vertices.push_back(offset + Vec4(-p, p, z, w)); // triangle 0 193 vertices.push_back(Vec4()); 194 vertices.push_back(offset + Vec4(-p, p, z, w)); 195 vertices.push_back(Vec4()); 196 vertices.push_back(offset + Vec4( p, p, slope + z, w)); 197 vertices.push_back(Vec4()); 198 vertices.push_back(offset + Vec4( p, -p, slope + z, w)); // triangle 1 199 vertices.push_back(Vec4()); 200 break; 201 202 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: 203 vertices.push_back(offset + Vec4(-p, -p, z, w)); 204 vertices.push_back(offset + Vec4(-p, p, z, w)); 205 vertices.push_back(offset + Vec4( p, -p, slope + z, w)); // triangle 0 206 vertices.push_back(offset + Vec4( p, p, slope + z, w)); // triangle 1 207 break; 208 209 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY: 210 vertices.push_back(offset + Vec4(-p, -p, z, w)); 211 vertices.push_back(Vec4()); 212 vertices.push_back(offset + Vec4(-p, p, z, w)); 213 vertices.push_back(Vec4()); 214 vertices.push_back(offset + Vec4( p, -p, slope + z, w)); // triangle 0 215 vertices.push_back(Vec4()); 216 vertices.push_back(offset + Vec4( p, p, slope + z, w)); // triangle 1 217 vertices.push_back(Vec4()); 218 break; 219 220 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: 221 vertices.push_back(offset + Vec4( p, -p, slope + z, w)); 222 vertices.push_back(offset + Vec4(-p, -p, z, w)); 223 vertices.push_back(offset + Vec4(-p, p, z, w)); // triangle 0 224 vertices.push_back(offset + Vec4( p, p, slope + z, w)); // triangle 1 225 break; 226 227 case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST: 228 DE_ASSERT(0); 229 break; 230 231 default: 232 DE_ASSERT(0); 233 break; 234 } 235 return vertices; 236 } 237 238 bool inline isColorInRange (const Vec4& color, const Vec4& minColor, const Vec4& maxColor) 239 { 240 return (minColor.x() <= color.x() && color.x() <= maxColor.x()) 241 && (minColor.y() <= color.y() && color.y() <= maxColor.y()) 242 && (minColor.z() <= color.z() && color.z() <= maxColor.z()) 243 && (minColor.w() <= color.w() && color.w() <= maxColor.w()); 244 } 245 246 //! Count pixels that match color within threshold, in the specified region. 247 int countPixels (const tcu::ConstPixelBufferAccess pixels, const IVec2& regionOffset, const IVec2& regionSize, const Vec4& color, const Vec4& colorThreshold) 248 { 249 const Vec4 minColor = color - colorThreshold; 250 const Vec4 maxColor = color + colorThreshold; 251 const int xEnd = regionOffset.x() + regionSize.x(); 252 const int yEnd = regionOffset.y() + regionSize.y(); 253 int numPixels = 0; 254 255 DE_ASSERT(xEnd <= pixels.getWidth()); 256 DE_ASSERT(yEnd <= pixels.getHeight()); 257 258 for (int y = regionOffset.y(); y < yEnd; ++y) 259 for (int x = regionOffset.x(); x < xEnd; ++x) 260 { 261 if (isColorInRange(pixels.getPixel(x, y), minColor, maxColor)) 262 ++numPixels; 263 } 264 265 return numPixels; 266 } 267 268 int countPixels (const tcu::ConstPixelBufferAccess pixels, const Vec4& color, const Vec4& colorThreshold) 269 { 270 return countPixels(pixels, IVec2(), IVec2(pixels.getWidth(), pixels.getHeight()), color, colorThreshold); 271 } 272 273 //! Check for correct cull and clip distance values. Middle bar should contain clip distance with linear values between 0 and 1. Cull distance is always 0.5 when enabled. 274 bool checkFragColors (const tcu::ConstPixelBufferAccess pixels, IVec2 clipRegion, int barIdx, bool hasCullDistance) 275 { 276 for (int y = 0; y < pixels.getHeight(); ++y) 277 for (int x = 0; x < pixels.getWidth(); ++x) 278 { 279 if (x < clipRegion.x() && y < clipRegion.y()) 280 continue; 281 282 const tcu::Vec4 color = pixels.getPixel(x, y); 283 const int barWidth = pixels.getWidth() / 8; 284 const bool insideBar = x >= barWidth * barIdx && x < barWidth * (barIdx + 1); 285 const float expectedClipDistance = insideBar ? (((((float)y + 0.5f) / (float)pixels.getHeight()) - 0.5f) * 2.0f) : 0.0f; 286 const float expectedCullDistance = 0.5f; 287 const float clipDistance = color.y(); 288 const float cullDistance = color.z(); 289 290 if (fabs(clipDistance - expectedClipDistance) > 0.01f) 291 return false; 292 if (hasCullDistance && fabs(cullDistance - expectedCullDistance) > 0.01f) 293 return false; 294 } 295 296 return true; 297 } 298 299 //! Clipping against the default clip volume. 300 namespace ClipVolume 301 { 302 303 //! Used by wide lines test. 304 enum LineOrientation 305 { 306 LINE_ORIENTATION_AXIS_ALIGNED, 307 LINE_ORIENTATION_DIAGONAL, 308 }; 309 310 const VkPointClippingBehavior invalidClippingBehavior = VK_POINT_CLIPPING_BEHAVIOR_LAST; 311 312 VkPointClippingBehavior getClippingBehavior (const InstanceInterface& vk, VkPhysicalDevice physicalDevice) 313 { 314 VkPhysicalDevicePointClippingProperties behaviorProperties = 315 { 316 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES, // VkStructureType sType 317 DE_NULL, // void* pNext 318 invalidClippingBehavior // VkPointClippingBehavior pointClippingBehavior 319 }; 320 VkPhysicalDeviceProperties2 properties2; 321 322 DE_ASSERT(getPointClippingBehaviorName(invalidClippingBehavior) == DE_NULL); 323 324 deMemset(&properties2, 0, sizeof(properties2)); 325 326 properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; 327 properties2.pNext = &behaviorProperties; 328 329 vk.getPhysicalDeviceProperties2(physicalDevice, &properties2); 330 331 return behaviorProperties.pointClippingBehavior; 332 } 333 334 void addSimplePrograms (SourceCollections& programCollection, const float pointSize = 0.0f) 335 { 336 // Vertex shader 337 { 338 const bool usePointSize = pointSize > 0.0f; 339 340 std::ostringstream src; 341 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 342 << "\n" 343 << "layout(location = 0) in vec4 v_position;\n" 344 << "\n" 345 << "out gl_PerVertex {\n" 346 << " vec4 gl_Position;\n" 347 << (usePointSize ? " float gl_PointSize;\n" : "") 348 << "};\n" 349 << "\n" 350 << "void main (void)\n" 351 << "{\n" 352 << " gl_Position = v_position;\n" 353 << (usePointSize ? " gl_PointSize = " + de::floatToString(pointSize, 1) + ";\n" : "") 354 << "}\n"; 355 356 programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); 357 } 358 359 // Fragment shader 360 { 361 std::ostringstream src; 362 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 363 << "\n" 364 << "layout(location = 0) out vec4 o_color;\n" 365 << "\n" 366 << "void main (void)\n" 367 << "{\n" 368 << " o_color = vec4(1.0, gl_FragCoord.z, 0.0, 1.0);\n" 369 << "}\n"; 370 371 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str()); 372 } 373 } 374 375 void initPrograms (SourceCollections& programCollection, const VkPrimitiveTopology topology) 376 { 377 const float pointSize = (topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST ? 1.0f : 0.0f); 378 addSimplePrograms(programCollection, pointSize); 379 } 380 381 void initPrograms (SourceCollections& programCollection, const LineOrientation lineOrientation) 382 { 383 DE_UNREF(lineOrientation); 384 addSimplePrograms(programCollection); 385 } 386 387 void initProgramsPointSize (SourceCollections& programCollection) 388 { 389 addSimplePrograms(programCollection, 0.75f * RENDER_SIZE); 390 } 391 392 //! Primitives fully inside the clip volume. 393 tcu::TestStatus testPrimitivesInside (Context& context, const VkPrimitiveTopology topology) 394 { 395 int minExpectedBlackPixels = 0; 396 397 switch (topology) 398 { 399 case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: 400 // We draw only 5 points. 401 minExpectedBlackPixels = NUM_RENDER_PIXELS - 5; 402 break; 403 404 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: 405 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY: 406 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER); 407 // Fallthrough 408 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: 409 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: 410 // Allow for some error. 411 minExpectedBlackPixels = NUM_RENDER_PIXELS - 3 * RENDER_SIZE; 412 break; 413 414 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: 415 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY: 416 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER); 417 // Fallthrough 418 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: 419 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: 420 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: 421 // All render area should be covered. 422 minExpectedBlackPixels = 0; 423 break; 424 425 default: 426 DE_ASSERT(0); 427 break; 428 } 429 430 std::vector<VulkanShader> shaders; 431 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"))); 432 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"))); 433 434 tcu::TestLog& log = context.getTestContext().getLog(); 435 int numPassed = 0; 436 437 static const struct 438 { 439 const char* const desc; 440 float zPos; 441 } cases[] = 442 { 443 { "Draw primitives at near clipping plane, z = 0.0", 0.0f, }, 444 { "Draw primitives at z = 0.5", 0.5f, }, 445 { "Draw primitives at far clipping plane, z = 1.0", 1.0f, }, 446 }; 447 448 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx) 449 { 450 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage; 451 452 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 0.0f); 453 DrawState drawState (topology, RENDER_SIZE, RENDER_SIZE); 454 DrawCallData drawCallData (vertices); 455 VulkanProgram vulkanProgram (shaders); 456 457 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram); 458 drawContext.draw(); 459 460 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4()); 461 if (numBlackPixels >= minExpectedBlackPixels) 462 ++numPassed; 463 } 464 465 return (numPassed == DE_LENGTH_OF_ARRAY(cases) ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect")); 466 } 467 468 //! Primitives fully outside the clip volume. 469 tcu::TestStatus testPrimitivesOutside (Context& context, const VkPrimitiveTopology topology) 470 { 471 switch (topology) 472 { 473 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: 474 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY: 475 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: 476 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY: 477 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER); 478 break; 479 default: 480 break; 481 } 482 483 std::vector<VulkanShader> shaders; 484 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"))); 485 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"))); 486 487 tcu::TestLog& log = context.getTestContext().getLog(); 488 int numPassed = 0; 489 490 static const struct 491 { 492 const char* const desc; 493 float zPos; 494 } cases[] = 495 { 496 { "Draw primitives in front of the near clipping plane, z < 0.0", -0.5f, }, 497 { "Draw primitives behind the far clipping plane, z > 1.0", 1.5f, }, 498 }; 499 500 log << tcu::TestLog::Message << "Drawing primitives outside the clip volume. Expecting an empty image." << tcu::TestLog::EndMessage; 501 502 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx) 503 { 504 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage; 505 506 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 0.0f); 507 DrawState drawState (topology, RENDER_SIZE, RENDER_SIZE); 508 DrawCallData drawCallData (vertices); 509 VulkanProgram vulkanProgram (shaders); 510 511 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram); 512 drawContext.draw(); 513 514 // All pixels must be black -- nothing is drawn. 515 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4()); 516 if (numBlackPixels == NUM_RENDER_PIXELS) 517 ++numPassed; 518 } 519 520 return (numPassed == DE_LENGTH_OF_ARRAY(cases) ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect")); 521 } 522 523 //! Primitives partially outside the clip volume, but depth clamped 524 tcu::TestStatus testPrimitivesDepthClamp (Context& context, const VkPrimitiveTopology topology) 525 { 526 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_DEPTH_CLAMP); 527 528 std::vector<VulkanShader> shaders; 529 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"))); 530 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"))); 531 532 const int numCases = 4; 533 const IVec2 regionSize = IVec2(RENDER_SIZE/2, RENDER_SIZE); //! size of the clamped region 534 const int regionPixels = regionSize.x() * regionSize.y(); 535 tcu::TestLog& log = context.getTestContext().getLog(); 536 int numPassed = 0; 537 538 static const struct 539 { 540 const char* const desc; 541 float zPos; 542 bool depthClampEnable; 543 IVec2 regionOffset; 544 Vec4 color; 545 } cases[numCases] = 546 { 547 { "Draw primitives intersecting the near clipping plane, depth clamp disabled", -0.5f, false, IVec2(0, 0), Vec4(0.0f, 0.0f, 0.0f, 1.0f) }, 548 { "Draw primitives intersecting the near clipping plane, depth clamp enabled", -0.5f, true, IVec2(0, 0), Vec4(1.0f, 0.0f, 0.0f, 1.0f) }, 549 { "Draw primitives intersecting the far clipping plane, depth clamp disabled", 0.5f, false, IVec2(RENDER_SIZE/2, 0), Vec4(0.0f, 0.0f, 0.0f, 1.0f) }, 550 { "Draw primitives intersecting the far clipping plane, depth clamp enabled", 0.5f, true, IVec2(RENDER_SIZE/2, 0), Vec4(1.0f, 1.0f, 0.0f, 1.0f) }, 551 }; 552 553 // Per case minimum number of colored pixels. 554 int caseMinPixels[numCases] = { 0, 0, 0, 0 }; 555 556 switch (topology) 557 { 558 case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: 559 caseMinPixels[0] = caseMinPixels[2] = regionPixels - 1; 560 caseMinPixels[1] = caseMinPixels[3] = 2; 561 break; 562 563 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: 564 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: 565 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: 566 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY: 567 caseMinPixels[0] = regionPixels; 568 caseMinPixels[1] = RENDER_SIZE - 2; 569 caseMinPixels[2] = regionPixels; 570 caseMinPixels[3] = 2 * (RENDER_SIZE - 2); 571 break; 572 573 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: 574 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: 575 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN: 576 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: 577 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY: 578 caseMinPixels[0] = caseMinPixels[1] = caseMinPixels[2] = caseMinPixels[3] = regionPixels; 579 break; 580 581 default: 582 DE_ASSERT(0); 583 break; 584 } 585 586 for (int caseNdx = 0; caseNdx < numCases; ++caseNdx) 587 { 588 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage; 589 590 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 1.0f); 591 592 DrawState drawState (topology, RENDER_SIZE, RENDER_SIZE); 593 DrawCallData drawCallData (vertices); 594 VulkanProgram vulkanProgram (shaders); 595 drawState.depthClampEnable = cases[caseNdx].depthClampEnable; 596 597 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram); 598 drawContext.draw(); 599 600 const int numPixels = countPixels(drawContext.getColorPixels(), cases[caseNdx].regionOffset, regionSize, cases[caseNdx].color, Vec4()); 601 602 if (numPixels >= caseMinPixels[caseNdx]) 603 ++numPassed; 604 } 605 606 return (numPassed == numCases ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect")); 607 } 608 609 //! Large point clipping 610 //! Spec: If the primitive under consideration is a point, then clipping passes it unchanged if it lies within the clip volume; 611 //! otherwise, it is discarded. 612 tcu::TestStatus testLargePoints (Context& context) 613 { 614 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_LARGE_POINTS); 615 616 bool pointClippingOutside = true; 617 618 if (isDeviceExtensionSupported(context.getUsedApiVersion(), context.getDeviceExtensions(), "VK_KHR_maintenance2")) 619 { 620 VkPointClippingBehavior clippingBehavior = getClippingBehavior(context.getInstanceInterface(), context.getPhysicalDevice()); 621 622 switch (clippingBehavior) 623 { 624 case VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES: pointClippingOutside = true; break; 625 case VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY: pointClippingOutside = false; break; 626 case invalidClippingBehavior: TCU_FAIL("Clipping behavior read failure"); break; 627 default: 628 { 629 TCU_FAIL("Unexpected clipping behavior reported"); 630 } 631 } 632 } 633 634 std::vector<VulkanShader> shaders; 635 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"))); 636 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"))); 637 638 std::vector<Vec4> vertices; 639 { 640 const float delta = 0.1f; // much smaller than the point size 641 const float p = 1.0f + delta; 642 643 vertices.push_back(Vec4( -p, -p, 0.1f, 1.0f)); 644 vertices.push_back(Vec4( -p, p, 0.2f, 1.0f)); 645 vertices.push_back(Vec4( p, p, 0.4f, 1.0f)); 646 vertices.push_back(Vec4( p, -p, 0.6f, 1.0f)); 647 vertices.push_back(Vec4(0.0f, -p, 0.8f, 1.0f)); 648 vertices.push_back(Vec4( p, 0.0f, 0.7f, 1.0f)); 649 vertices.push_back(Vec4(0.0f, p, 0.5f, 1.0f)); 650 vertices.push_back(Vec4( -p, 0.0f, 0.3f, 1.0f)); 651 } 652 653 tcu::TestLog& log = context.getTestContext().getLog(); 654 655 log << tcu::TestLog::Message << "Drawing several large points just outside the clip volume. Expecting an empty image or all points rendered." << tcu::TestLog::EndMessage; 656 657 DrawState drawState (VK_PRIMITIVE_TOPOLOGY_POINT_LIST, RENDER_SIZE, RENDER_SIZE); 658 DrawCallData drawCallData (vertices); 659 VulkanProgram vulkanProgram (shaders); 660 661 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram); 662 drawContext.draw(); 663 664 // Popful case: All pixels must be black -- nothing is drawn. 665 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4()); 666 bool result = false; 667 668 // Pop-free case: All points must be rendered. 669 bool allPointsRendered = true; 670 for (std::vector<Vec4>::iterator i = vertices.begin(); i != vertices.end(); ++i) 671 { 672 if (countPixels(drawContext.getColorPixels(), Vec4(1.0f, i->z(), 0.0f, 1.0f), Vec4(0.01f)) == 0) 673 allPointsRendered = false; 674 } 675 676 if (pointClippingOutside) 677 { 678 result = (numBlackPixels == NUM_RENDER_PIXELS || allPointsRendered); 679 } 680 else 681 { 682 // Rendering pixels without clipping: all points should be drawn. 683 result = (allPointsRendered == true); 684 } 685 686 return (result ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect")); 687 } 688 689 class WideLineVertexShader : public rr::VertexShader 690 { 691 public: 692 WideLineVertexShader (void) 693 : rr::VertexShader(1, 1) 694 { 695 m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; 696 m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; 697 } 698 699 void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const 700 { 701 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) 702 { 703 const tcu::Vec4 position = rr::readVertexAttribFloat(inputs[0], packets[packetNdx]->instanceNdx, packets[packetNdx]->vertexNdx); 704 705 packets[packetNdx]->position = position; 706 packets[packetNdx]->outputs[0] = position; 707 } 708 } 709 }; 710 711 class WideLineFragmentShader : public rr::FragmentShader 712 { 713 public: 714 WideLineFragmentShader (void) 715 : rr::FragmentShader(1, 1) 716 { 717 m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; 718 m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; 719 } 720 721 void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const 722 { 723 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) 724 { 725 for (int fragNdx = 0; fragNdx < rr::NUM_FRAGMENTS_PER_PACKET; ++fragNdx) 726 { 727 const float depth = rr::readVarying<float>(packets[packetNdx], context, 0, fragNdx).z(); 728 rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, tcu::Vec4(1.0f, depth, 0.0f, 1.0f)); 729 } 730 } 731 } 732 }; 733 //! Wide line clipping 734 tcu::TestStatus testWideLines (Context& context, const LineOrientation lineOrientation) 735 { 736 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_WIDE_LINES); 737 738 std::vector<VulkanShader> shaders; 739 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"))); 740 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"))); 741 742 const float delta = 0.1f; // much smaller than the line width 743 744 std::vector<Vec4> vertices; 745 if (lineOrientation == LINE_ORIENTATION_AXIS_ALIGNED) 746 { 747 // Axis-aligned lines just outside the clip volume. 748 const float p = 1.0f + delta; 749 const float q = 0.9f; 750 751 vertices.push_back(Vec4(-p, -q, 0.1f, 1.0f)); 752 vertices.push_back(Vec4(-p, q, 0.9f, 1.0f)); // line 0 753 vertices.push_back(Vec4(-q, p, 0.1f, 1.0f)); 754 vertices.push_back(Vec4( q, p, 0.9f, 1.0f)); // line 1 755 vertices.push_back(Vec4( p, q, 0.1f, 1.0f)); 756 vertices.push_back(Vec4( p, -q, 0.9f, 1.0f)); // line 2 757 vertices.push_back(Vec4( q, -p, 0.1f, 1.0f)); 758 vertices.push_back(Vec4(-q, -p, 0.9f, 1.0f)); // line 3 759 } 760 else if (lineOrientation == LINE_ORIENTATION_DIAGONAL) 761 { 762 // Diagonal lines just outside the clip volume. 763 const float p = 2.0f + delta; 764 765 vertices.push_back(Vec4( -p, 0.0f, 0.1f, 1.0f)); 766 vertices.push_back(Vec4(0.0f, -p, 0.9f, 1.0f)); // line 0 767 vertices.push_back(Vec4(0.0f, -p, 0.1f, 1.0f)); 768 vertices.push_back(Vec4( p, 0.0f, 0.9f, 1.0f)); // line 1 769 vertices.push_back(Vec4( p, 0.0f, 0.1f, 1.0f)); 770 vertices.push_back(Vec4(0.0f, p, 0.9f, 1.0f)); // line 2 771 vertices.push_back(Vec4(0.0f, p, 0.1f, 1.0f)); 772 vertices.push_back(Vec4( -p, 0.0f, 0.9f, 1.0f)); // line 3 773 } 774 else 775 DE_ASSERT(0); 776 777 const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(context.getInstanceInterface(), context.getPhysicalDevice()).limits; 778 779 const float lineWidth = std::min(static_cast<float>(RENDER_SIZE), limits.lineWidthRange[1]); 780 const bool strictLines = limits.strictLines; 781 tcu::TestLog& log = context.getTestContext().getLog(); 782 783 log << tcu::TestLog::Message << "Drawing several wide lines just outside the clip volume. Expecting an empty image or all lines rendered." << tcu::TestLog::EndMessage 784 << tcu::TestLog::Message << "Line width is " << lineWidth << "." << tcu::TestLog::EndMessage 785 << tcu::TestLog::Message << "strictLines is " << (strictLines ? "VK_TRUE." : "VK_FALSE.") << tcu::TestLog::EndMessage; 786 787 DrawState drawState (VK_PRIMITIVE_TOPOLOGY_LINE_LIST, RENDER_SIZE, RENDER_SIZE); 788 DrawCallData drawCallData (vertices); 789 VulkanProgram vulkanProgram (shaders); 790 drawState.lineWidth = lineWidth; 791 792 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram); 793 drawContext.draw(); 794 795 // Popful case: All pixels must be black -- nothing is drawn. 796 if (countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4()) == NUM_RENDER_PIXELS) 797 { 798 return tcu::TestStatus::pass("OK"); 799 } 800 // Pop-free case: All lines must be rendered. 801 else 802 { 803 const float halfWidth = lineWidth / float(RENDER_SIZE); 804 std::vector<Vec4> refVertices; 805 806 // Create reference primitives 807 for (deUint32 lineNdx = 0u; lineNdx < (deUint32)vertices.size() / 2u; lineNdx++) 808 { 809 const deUint32 vertexNdx0 = 2 * lineNdx; 810 const deUint32 vertexNdx1 = 2 * lineNdx + 1; 811 812 const bool xMajorAxis = deFloatAbs(vertices[vertexNdx1].x() - vertices[vertexNdx0].x()) >= deFloatAbs(vertices[vertexNdx1].y() - vertices[vertexNdx0].y()); 813 const tcu::Vec2 lineDir = tcu::normalize(tcu::Vec2(vertices[vertexNdx1].x() - vertices[vertexNdx0].x(), vertices[vertexNdx1].y() - vertices[vertexNdx0].y())); 814 const tcu::Vec4 lineNormalDir = (strictLines) ? tcu::Vec4(lineDir.y(), -lineDir.x(), 0.0f, 0.0f) // Line caps are perpendicular to the direction of the line segment. 815 : (xMajorAxis) ? tcu::Vec4(0.0f, 1.0f, 0.0f, 0.0f) : tcu::Vec4(1.0f, 0.0f, 0.0f, 0.0f); // Line caps are aligned to the minor axis 816 817 const tcu::Vec4 wideLineVertices[] = 818 { 819 tcu::Vec4(vertices[vertexNdx0] + lineNormalDir * halfWidth), 820 tcu::Vec4(vertices[vertexNdx0] - lineNormalDir * halfWidth), 821 tcu::Vec4(vertices[vertexNdx1] - lineNormalDir * halfWidth), 822 tcu::Vec4(vertices[vertexNdx1] + lineNormalDir * halfWidth) 823 }; 824 825 // 1st triangle 826 refVertices.push_back(wideLineVertices[0]); 827 refVertices.push_back(wideLineVertices[1]); 828 refVertices.push_back(wideLineVertices[2]); 829 830 // 2nd triangle 831 refVertices.push_back(wideLineVertices[0]); 832 refVertices.push_back(wideLineVertices[2]); 833 refVertices.push_back(wideLineVertices[3]); 834 } 835 836 WideLineVertexShader vertexShader; 837 WideLineFragmentShader fragmentShader; 838 839 // Draw wide line was two triangles 840 DrawState refDrawState (VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, RENDER_SIZE, RENDER_SIZE); 841 DrawCallData refCallData (refVertices); 842 ReferenceDrawContext refDrawContext (refDrawState, refCallData, vertexShader, fragmentShader); 843 844 refDrawContext.draw(); 845 846 if (tcu::intThresholdCompare(log, "Compare", "Result comparsion", refDrawContext.getColorPixels(), drawContext.getColorPixels(), tcu::UVec4(1), tcu::COMPARE_LOG_ON_ERROR)) 847 return tcu::TestStatus::pass("OK"); 848 } 849 850 return tcu::TestStatus::fail("Rendered image(s) are incorrect"); 851 } 852 853 } // ClipVolume ns 854 855 namespace ClipDistance 856 { 857 858 struct CaseDefinition 859 { 860 const VkPrimitiveTopology topology; 861 const bool dynamicIndexing; 862 const bool enableTessellation; 863 const bool enableGeometry; 864 const int numClipDistances; 865 const int numCullDistances; 866 const bool readInFragmentShader; 867 868 CaseDefinition (const VkPrimitiveTopology topology_, 869 const int numClipDistances_, 870 const int numCullDistances_, 871 const bool enableTessellation_, 872 const bool enableGeometry_, 873 const bool dynamicIndexing_, 874 const bool readInFragmentShader_) 875 : topology (topology_) 876 , dynamicIndexing (dynamicIndexing_) 877 , enableTessellation (enableTessellation_) 878 , enableGeometry (enableGeometry_) 879 , numClipDistances (numClipDistances_) 880 , numCullDistances (numCullDistances_) 881 , readInFragmentShader (readInFragmentShader_) 882 { 883 } 884 }; 885 886 void initPrograms (SourceCollections& programCollection, const CaseDefinition caseDef) 887 { 888 DE_ASSERT(caseDef.numClipDistances + caseDef.numCullDistances <= MAX_COMBINED_CLIP_AND_CULL_DISTANCES); 889 890 std::string perVertexBlock; 891 { 892 std::ostringstream str; 893 str << "gl_PerVertex {\n" 894 << " vec4 gl_Position;\n"; 895 if (caseDef.numClipDistances > 0) 896 str << " float gl_ClipDistance[" << caseDef.numClipDistances << "];\n"; 897 if (caseDef.numCullDistances > 0) 898 str << " float gl_CullDistance[" << caseDef.numCullDistances << "];\n"; 899 str << "}"; 900 perVertexBlock = str.str(); 901 } 902 903 // Vertex shader 904 { 905 std::ostringstream src; 906 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 907 << "\n" 908 << "layout(location = 0) in vec4 v_position;\n" 909 << "layout(location = 0) out vec4 out_color;\n" 910 << "\n" 911 << "out " << perVertexBlock << ";\n" 912 << "\n" 913 << "void main (void)\n" 914 << "{\n" 915 << " gl_Position = v_position;\n" 916 << " out_color = vec4(1.0, 0.5 * (v_position.x + 1.0), 0.0, 1.0);\n" 917 << "\n" 918 << " const int barNdx = gl_VertexIndex / 6;\n"; 919 if (caseDef.dynamicIndexing) 920 { 921 if (caseDef.numClipDistances > 0) 922 src << " for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n" 923 << " gl_ClipDistance[i] = (barNdx == i ? v_position.y : 0.0);\n"; 924 if (caseDef.numCullDistances > 0) 925 src << " for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n" 926 << " gl_CullDistance[i] = 0.5;\n"; 927 } 928 else 929 { 930 for (int i = 0; i < caseDef.numClipDistances; ++i) 931 src << " gl_ClipDistance[" << i << "] = (barNdx == " << i << " ? v_position.y : 0.0);\n"; 932 for (int i = 0; i < caseDef.numCullDistances; ++i) 933 src << " gl_CullDistance[" << i << "] = 0.5;\n"; // don't cull anything 934 } 935 src << "}\n"; 936 937 programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); 938 } 939 940 if (caseDef.enableTessellation) 941 { 942 std::ostringstream src; 943 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 944 << "\n" 945 << "layout(vertices = " << NUM_PATCH_CONTROL_POINTS << ") out;\n" 946 << "\n" 947 << "layout(location = 0) in vec4 in_color[];\n" 948 << "layout(location = 0) out vec4 out_color[];\n" 949 << "\n" 950 << "in " << perVertexBlock << " gl_in[gl_MaxPatchVertices];\n" 951 << "\n" 952 << "out " << perVertexBlock << " gl_out[];\n" 953 << "\n" 954 << "void main (void)\n" 955 << "{\n" 956 << " gl_TessLevelInner[0] = 1.0;\n" 957 << " gl_TessLevelInner[1] = 1.0;\n" 958 << "\n" 959 << " gl_TessLevelOuter[0] = 1.0;\n" 960 << " gl_TessLevelOuter[1] = 1.0;\n" 961 << " gl_TessLevelOuter[2] = 1.0;\n" 962 << " gl_TessLevelOuter[3] = 1.0;\n" 963 << "\n" 964 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 965 << " out_color[gl_InvocationID] = in_color[gl_InvocationID];\n" 966 << "\n"; 967 if (caseDef.dynamicIndexing) 968 { 969 if (caseDef.numClipDistances > 0) 970 src << " for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n" 971 << " gl_out[gl_InvocationID].gl_ClipDistance[i] = gl_in[gl_InvocationID].gl_ClipDistance[i];\n"; 972 if (caseDef.numCullDistances > 0) 973 src << " for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n" 974 << " gl_out[gl_InvocationID].gl_CullDistance[i] = gl_in[gl_InvocationID].gl_CullDistance[i];\n"; 975 } 976 else 977 { 978 for (int i = 0; i < caseDef.numClipDistances; ++i) 979 src << " gl_out[gl_InvocationID].gl_ClipDistance[" << i << "] = gl_in[gl_InvocationID].gl_ClipDistance[" << i << "];\n"; 980 for (int i = 0; i < caseDef.numCullDistances; ++i) 981 src << " gl_out[gl_InvocationID].gl_CullDistance[" << i << "] = gl_in[gl_InvocationID].gl_CullDistance[" << i << "];\n"; 982 } 983 src << "}\n"; 984 985 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str()); 986 } 987 988 if (caseDef.enableTessellation) 989 { 990 DE_ASSERT(NUM_PATCH_CONTROL_POINTS == 3); // assumed in shader code 991 992 std::ostringstream src; 993 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 994 << "\n" 995 << "layout(triangles, equal_spacing, ccw) in;\n" 996 << "\n" 997 << "layout(location = 0) in vec4 in_color[];\n" 998 << "layout(location = 0) out vec4 out_color;\n" 999 << "\n" 1000 << "in " << perVertexBlock << " gl_in[gl_MaxPatchVertices];\n" 1001 << "\n" 1002 << "out " << perVertexBlock << ";\n" 1003 << "\n" 1004 << "void main (void)\n" 1005 << "{\n" 1006 << " vec3 px = gl_TessCoord.x * gl_in[0].gl_Position.xyz;\n" 1007 << " vec3 py = gl_TessCoord.y * gl_in[1].gl_Position.xyz;\n" 1008 << " vec3 pz = gl_TessCoord.z * gl_in[2].gl_Position.xyz;\n" 1009 << " gl_Position = vec4(px + py + pz, 1.0);\n" 1010 << " out_color = (in_color[0] + in_color[1] + in_color[2]) / 3.0;\n" 1011 << "\n"; 1012 if (caseDef.dynamicIndexing) 1013 { 1014 if (caseDef.numClipDistances > 0) 1015 src << " for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n" 1016 << " gl_ClipDistance[i] = gl_TessCoord.x * gl_in[0].gl_ClipDistance[i]\n" 1017 << " + gl_TessCoord.y * gl_in[1].gl_ClipDistance[i]\n" 1018 << " + gl_TessCoord.z * gl_in[2].gl_ClipDistance[i];\n"; 1019 if (caseDef.numCullDistances > 0) 1020 src << " for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n" 1021 << " gl_CullDistance[i] = gl_TessCoord.x * gl_in[0].gl_CullDistance[i]\n" 1022 << " + gl_TessCoord.y * gl_in[1].gl_CullDistance[i]\n" 1023 << " + gl_TessCoord.z * gl_in[2].gl_CullDistance[i];\n"; 1024 } 1025 else 1026 { 1027 for (int i = 0; i < caseDef.numClipDistances; ++i) 1028 src << " gl_ClipDistance[" << i << "] = gl_TessCoord.x * gl_in[0].gl_ClipDistance[" << i << "]\n" 1029 << " + gl_TessCoord.y * gl_in[1].gl_ClipDistance[" << i << "]\n" 1030 << " + gl_TessCoord.z * gl_in[2].gl_ClipDistance[" << i << "];\n"; 1031 for (int i = 0; i < caseDef.numCullDistances; ++i) 1032 src << " gl_CullDistance[" << i << "] = gl_TessCoord.x * gl_in[0].gl_CullDistance[" << i << "]\n" 1033 << " + gl_TessCoord.y * gl_in[1].gl_CullDistance[" << i << "]\n" 1034 << " + gl_TessCoord.z * gl_in[2].gl_CullDistance[" << i << "];\n"; 1035 } 1036 src << "}\n"; 1037 1038 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str()); 1039 } 1040 1041 if (caseDef.enableGeometry) 1042 { 1043 std::ostringstream src; 1044 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 1045 << "\n" 1046 << "layout(triangles) in;\n" 1047 << "layout(triangle_strip, max_vertices = 3) out;\n" 1048 << "\n" 1049 << "layout(location = 0) in vec4 in_color[];\n" 1050 << "layout(location = 0) out vec4 out_color;\n" 1051 << "\n" 1052 << "in " << perVertexBlock << " gl_in[];\n" 1053 << "\n" 1054 << "out " << perVertexBlock << ";\n" 1055 << "\n" 1056 << "void main (void)\n" 1057 << "{\n"; 1058 for (int vertNdx = 0; vertNdx < 3; ++vertNdx) 1059 { 1060 if (vertNdx > 0) 1061 src << "\n"; 1062 src << " gl_Position = gl_in[" << vertNdx << "].gl_Position;\n" 1063 << " out_color = in_color[" << vertNdx << "];\n"; 1064 if (caseDef.dynamicIndexing) 1065 { 1066 if (caseDef.numClipDistances > 0) 1067 src << " for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n" 1068 << " gl_ClipDistance[i] = gl_in[" << vertNdx << "].gl_ClipDistance[i];\n"; 1069 if (caseDef.numCullDistances > 0) 1070 src << " for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n" 1071 << " gl_CullDistance[i] = gl_in[" << vertNdx << "].gl_CullDistance[i];\n"; 1072 } 1073 else 1074 { 1075 for (int i = 0; i < caseDef.numClipDistances; ++i) 1076 src << " gl_ClipDistance[" << i << "] = gl_in[" << vertNdx << "].gl_ClipDistance[" << i << "];\n"; 1077 for (int i = 0; i < caseDef.numCullDistances; ++i) 1078 src << " gl_CullDistance[" << i << "] = gl_in[" << vertNdx << "].gl_CullDistance[" << i << "];\n"; 1079 } 1080 src << " EmitVertex();\n"; 1081 } 1082 src << "}\n"; 1083 1084 programCollection.glslSources.add("geom") << glu::GeometrySource(src.str()); 1085 } 1086 1087 // Fragment shader 1088 { 1089 std::ostringstream src; 1090 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 1091 << "\n" 1092 << "layout(location = 0) in flat vec4 in_color;\n" 1093 << "layout(location = 0) out vec4 o_color;\n"; 1094 if (caseDef.readInFragmentShader) 1095 { 1096 if (caseDef.numClipDistances > 0) 1097 src << "in float gl_ClipDistance[" << caseDef.numClipDistances << "];\n"; 1098 if (caseDef.numCullDistances > 0) 1099 src << "in float gl_CullDistance[" << caseDef.numCullDistances << "];\n"; 1100 } 1101 src << "\n" 1102 << "void main (void)\n" 1103 << "{\n"; 1104 1105 if (caseDef.readInFragmentShader) 1106 { 1107 src << " o_color = vec4(in_color.r, " 1108 << (caseDef.numClipDistances > 0 ? std::string("gl_ClipDistance[") + de::toString(caseDef.numClipDistances / 2) + "], " : "0.0, ") 1109 << (caseDef.numCullDistances > 0 ? std::string("gl_CullDistance[") + de::toString(caseDef.numCullDistances / 2) + "], " : "0.0, ") 1110 << " 1.0);\n"; 1111 } 1112 else 1113 { 1114 src << " o_color = vec4(in_color.rgb + vec3(0.0, 0.0, 0.5), 1.0);\n"; // mix with a constant color in case variable wasn't passed correctly through stages 1115 } 1116 1117 src << "}\n"; 1118 1119 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str()); 1120 } 1121 } 1122 1123 tcu::TestStatus testClipDistance (Context& context, const CaseDefinition caseDef) 1124 { 1125 // Check test requirements 1126 { 1127 const InstanceInterface& vki = context.getInstanceInterface(); 1128 const VkPhysicalDevice physDevice = context.getPhysicalDevice(); 1129 const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(vki, physDevice).limits; 1130 1131 FeatureFlags requirements = (FeatureFlags)0; 1132 1133 if (caseDef.numClipDistances > 0) 1134 requirements |= FEATURE_SHADER_CLIP_DISTANCE; 1135 if (caseDef.numCullDistances > 0) 1136 requirements |= FEATURE_SHADER_CULL_DISTANCE; 1137 if (caseDef.enableTessellation) 1138 requirements |= FEATURE_TESSELLATION_SHADER; 1139 if (caseDef.enableGeometry) 1140 requirements |= FEATURE_GEOMETRY_SHADER; 1141 1142 requireFeatures(vki, physDevice, requirements); 1143 1144 // Check limits for supported features 1145 1146 if (caseDef.numClipDistances > 0 && limits.maxClipDistances < MAX_CLIP_DISTANCES) 1147 return tcu::TestStatus::fail("maxClipDistances smaller than the minimum required by the spec"); 1148 if (caseDef.numCullDistances > 0 && limits.maxCullDistances < MAX_CULL_DISTANCES) 1149 return tcu::TestStatus::fail("maxCullDistances smaller than the minimum required by the spec"); 1150 if (caseDef.numCullDistances > 0 && limits.maxCombinedClipAndCullDistances < MAX_COMBINED_CLIP_AND_CULL_DISTANCES) 1151 return tcu::TestStatus::fail("maxCombinedClipAndCullDistances smaller than the minimum required by the spec"); 1152 } 1153 1154 std::vector<VulkanShader> shaders; 1155 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"))); 1156 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"))); 1157 if (caseDef.enableTessellation) 1158 { 1159 shaders.push_back(VulkanShader(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc"))); 1160 shaders.push_back(VulkanShader(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"))); 1161 } 1162 if (caseDef.enableGeometry) 1163 shaders.push_back(VulkanShader(VK_SHADER_STAGE_GEOMETRY_BIT, context.getBinaryCollection().get("geom"))); 1164 1165 const int numBars = MAX_COMBINED_CLIP_AND_CULL_DISTANCES; 1166 1167 std::vector<Vec4> vertices; 1168 { 1169 const float dx = 2.0f / numBars; 1170 for (int i = 0; i < numBars; ++i) 1171 { 1172 const float x = -1.0f + dx * static_cast<float>(i); 1173 1174 vertices.push_back(Vec4(x, -1.0f, 0.0f, 1.0f)); 1175 vertices.push_back(Vec4(x, 1.0f, 0.0f, 1.0f)); 1176 vertices.push_back(Vec4(x + dx, -1.0f, 0.0f, 1.0f)); 1177 1178 vertices.push_back(Vec4(x, 1.0f, 0.0f, 1.0f)); 1179 vertices.push_back(Vec4(x + dx, 1.0f, 0.0f, 1.0f)); 1180 vertices.push_back(Vec4(x + dx, -1.0f, 0.0f, 1.0f)); 1181 } 1182 } 1183 1184 tcu::TestLog& log = context.getTestContext().getLog(); 1185 1186 log << tcu::TestLog::Message << "Drawing " << numBars << " colored bars, clipping the first " << caseDef.numClipDistances << tcu::TestLog::EndMessage 1187 << tcu::TestLog::Message << "Using " << caseDef.numClipDistances << " ClipDistance(s) and " << caseDef.numCullDistances << " CullDistance(s)" << tcu::TestLog::EndMessage 1188 << tcu::TestLog::Message << "Expecting upper half of the clipped bars to be black." << tcu::TestLog::EndMessage; 1189 1190 DrawState drawState (caseDef.topology, RENDER_SIZE, RENDER_SIZE); 1191 DrawCallData drawCallData (vertices); 1192 VulkanProgram vulkanProgram (shaders); 1193 1194 if (caseDef.enableTessellation) 1195 drawState.numPatchControlPoints = NUM_PATCH_CONTROL_POINTS; 1196 1197 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram); 1198 drawContext.draw(); 1199 1200 // Count black pixels in the whole image. 1201 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4()); 1202 const IVec2 clipRegion = IVec2(caseDef.numClipDistances * RENDER_SIZE / numBars, RENDER_SIZE / 2); 1203 const int expectedClippedPixels = clipRegion.x() * clipRegion.y(); 1204 // Make sure the bottom half has no black pixels (possible if image became corrupted). 1205 const int guardPixels = countPixels(drawContext.getColorPixels(), IVec2(0, RENDER_SIZE/2), clipRegion, Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4()); 1206 const bool fragColorsOk = caseDef.readInFragmentShader ? checkFragColors(drawContext.getColorPixels(), clipRegion, caseDef.numClipDistances / 2, caseDef.numCullDistances > 0) : true; 1207 1208 return (numBlackPixels == expectedClippedPixels && guardPixels == 0 && fragColorsOk ? tcu::TestStatus::pass("OK") 1209 : tcu::TestStatus::fail("Rendered image(s) are incorrect")); 1210 } 1211 1212 } // ClipDistance ns 1213 1214 namespace ClipDistanceComplementarity 1215 { 1216 1217 void initPrograms (SourceCollections& programCollection, const int numClipDistances) 1218 { 1219 // Vertex shader 1220 { 1221 DE_ASSERT(numClipDistances > 0); 1222 const int clipDistanceLastNdx = numClipDistances - 1; 1223 1224 std::ostringstream src; 1225 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 1226 << "\n" 1227 << "layout(location = 0) in vec4 v_position; // we are passing ClipDistance in w component\n" 1228 << "\n" 1229 << "out gl_PerVertex {\n" 1230 << " vec4 gl_Position;\n" 1231 << " float gl_ClipDistance[" << numClipDistances << "];\n" 1232 << "};\n" 1233 << "\n" 1234 << "void main (void)\n" 1235 << "{\n" 1236 << " gl_Position = vec4(v_position.xyz, 1.0);\n"; 1237 for (int i = 0; i < clipDistanceLastNdx; ++i) 1238 src << " gl_ClipDistance[" << i << "] = 0.0;\n"; 1239 src << " gl_ClipDistance[" << clipDistanceLastNdx << "] = v_position.w;\n" 1240 << "}\n"; 1241 1242 programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); 1243 } 1244 1245 // Fragment shader 1246 { 1247 std::ostringstream src; 1248 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 1249 << "\n" 1250 << "layout(location = 0) out vec4 o_color;\n" 1251 << "\n" 1252 << "void main (void)\n" 1253 << "{\n" 1254 << " o_color = vec4(1.0, 1.0, 1.0, 0.5);\n" 1255 << "}\n"; 1256 1257 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str()); 1258 } 1259 } 1260 1261 tcu::TestStatus testComplementarity (Context& context, const int numClipDistances) 1262 { 1263 // Check test requirements 1264 { 1265 const InstanceInterface& vki = context.getInstanceInterface(); 1266 const VkPhysicalDevice physDevice = context.getPhysicalDevice(); 1267 1268 requireFeatures(vki, physDevice, FEATURE_SHADER_CLIP_DISTANCE); 1269 } 1270 1271 std::vector<VulkanShader> shaders; 1272 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"))); 1273 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"))); 1274 1275 std::vector<Vec4> vertices; 1276 { 1277 de::Random rnd (1234); 1278 const int numSections = 16; 1279 const int numVerticesPerSection = 4; // logical verticies, due to triangle list topology we actually use 6 per section 1280 1281 DE_ASSERT(RENDER_SIZE_LARGE % numSections == 0); 1282 1283 std::vector<float> clipDistances(numVerticesPerSection * numSections); 1284 for (int i = 0; i < static_cast<int>(clipDistances.size()); ++i) 1285 clipDistances[i] = rnd.getFloat(-1.0f, 1.0f); 1286 1287 // Two sets of identical primitives, but with a different ClipDistance sign. 1288 for (int setNdx = 0; setNdx < 2; ++setNdx) 1289 { 1290 const float sign = (setNdx == 0 ? 1.0f : -1.0f); 1291 const float dx = 2.0f / static_cast<float>(numSections); 1292 1293 for (int i = 0; i < numSections; ++i) 1294 { 1295 const int ndxBase = numVerticesPerSection * i; 1296 const float x = -1.0f + dx * static_cast<float>(i); 1297 const Vec4 p0 = Vec4(x, -1.0f, 0.0f, sign * clipDistances[ndxBase + 0]); 1298 const Vec4 p1 = Vec4(x, 1.0f, 0.0f, sign * clipDistances[ndxBase + 1]); 1299 const Vec4 p2 = Vec4(x + dx, 1.0f, 0.0f, sign * clipDistances[ndxBase + 2]); 1300 const Vec4 p3 = Vec4(x + dx, -1.0f, 0.0f, sign * clipDistances[ndxBase + 3]); 1301 1302 vertices.push_back(p0); 1303 vertices.push_back(p1); 1304 vertices.push_back(p2); 1305 1306 vertices.push_back(p2); 1307 vertices.push_back(p3); 1308 vertices.push_back(p0); 1309 } 1310 } 1311 } 1312 1313 tcu::TestLog& log = context.getTestContext().getLog(); 1314 1315 log << tcu::TestLog::Message << "Draw two sets of primitives with blending, differing only with ClipDistance sign." << tcu::TestLog::EndMessage 1316 << tcu::TestLog::Message << "Using " << numClipDistances << " clipping plane(s), one of them possibly having negative values." << tcu::TestLog::EndMessage 1317 << tcu::TestLog::Message << "Expecting a uniform gray area, no missing (black) nor overlapped (white) pixels." << tcu::TestLog::EndMessage; 1318 1319 DrawState drawState (VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, RENDER_SIZE_LARGE, RENDER_SIZE_LARGE); 1320 DrawCallData drawCallData (vertices); 1321 VulkanProgram vulkanProgram (shaders); 1322 drawState.blendEnable = true; 1323 1324 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram); 1325 drawContext.draw(); 1326 1327 const int numGrayPixels = countPixels(drawContext.getColorPixels(), Vec4(0.5f, 0.5f, 0.5f, 1.0f), Vec4(0.02f, 0.02f, 0.02f, 0.0f)); 1328 const int numExpectedPixels = RENDER_SIZE_LARGE * RENDER_SIZE_LARGE; 1329 1330 return (numGrayPixels == numExpectedPixels ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect")); 1331 } 1332 1333 } // ClipDistanceComplementarity ns 1334 1335 void addClippingTests (tcu::TestCaseGroup* clippingTestsGroup) 1336 { 1337 tcu::TestContext& testCtx = clippingTestsGroup->getTestContext(); 1338 1339 // Clipping against the clip volume 1340 { 1341 using namespace ClipVolume; 1342 1343 static const VkPrimitiveTopology cases[] = 1344 { 1345 VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 1346 VK_PRIMITIVE_TOPOLOGY_LINE_LIST, 1347 VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY, 1348 VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, 1349 VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY, 1350 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 1351 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY, 1352 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 1353 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY, 1354 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, 1355 }; 1356 1357 MovePtr<tcu::TestCaseGroup> clipVolumeGroup(new tcu::TestCaseGroup(testCtx, "clip_volume", "clipping with the clip volume")); 1358 1359 // Fully inside the clip volume 1360 { 1361 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "inside", "")); 1362 1363 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx) 1364 addFunctionCaseWithPrograms<VkPrimitiveTopology>( 1365 group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), "", initPrograms, testPrimitivesInside, cases[caseNdx]); 1366 1367 clipVolumeGroup->addChild(group.release()); 1368 } 1369 1370 // Fully outside the clip volume 1371 { 1372 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "outside", "")); 1373 1374 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx) 1375 addFunctionCaseWithPrograms<VkPrimitiveTopology>( 1376 group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), "", initPrograms, testPrimitivesOutside, cases[caseNdx]); 1377 1378 clipVolumeGroup->addChild(group.release()); 1379 } 1380 1381 // Depth clamping 1382 { 1383 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "depth_clamp", "")); 1384 1385 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx) 1386 addFunctionCaseWithPrograms<VkPrimitiveTopology>( 1387 group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), "", initPrograms, testPrimitivesDepthClamp, cases[caseNdx]); 1388 1389 clipVolumeGroup->addChild(group.release()); 1390 } 1391 1392 // Large points and wide lines 1393 { 1394 // \note For both points and lines, if an unsupported size/width is selected, the nearest supported size will be chosen. 1395 // We do have to check for feature support though. 1396 1397 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "clipped", "")); 1398 1399 addFunctionCaseWithPrograms(group.get(), "large_points", "", initProgramsPointSize, testLargePoints); 1400 1401 addFunctionCaseWithPrograms<LineOrientation>(group.get(), "wide_lines_axis_aligned", "", initPrograms, testWideLines, LINE_ORIENTATION_AXIS_ALIGNED); 1402 addFunctionCaseWithPrograms<LineOrientation>(group.get(), "wide_lines_diagonal", "", initPrograms, testWideLines, LINE_ORIENTATION_DIAGONAL); 1403 1404 clipVolumeGroup->addChild(group.release()); 1405 } 1406 1407 clippingTestsGroup->addChild(clipVolumeGroup.release()); 1408 } 1409 1410 // User-defined clip planes 1411 { 1412 MovePtr<tcu::TestCaseGroup> clipDistanceGroup(new tcu::TestCaseGroup(testCtx, "user_defined", "user-defined clip planes")); 1413 1414 // ClipDistance, CullDistance and maxCombinedClipAndCullDistances usage 1415 { 1416 using namespace ClipDistance; 1417 1418 static const struct 1419 { 1420 const char* const groupName; 1421 const char* const description; 1422 bool useCullDistance; 1423 } caseGroups[] = 1424 { 1425 { "clip_distance", "use ClipDistance", false }, 1426 { "clip_cull_distance", "use ClipDistance and CullDistance at the same time", true }, 1427 }; 1428 1429 static const struct 1430 { 1431 const char* const name; 1432 bool readInFragmentShader; 1433 } fragmentShaderReads[] = 1434 { 1435 1436 { "", false }, 1437 { "_fragmentshader_read", true } 1438 }; 1439 1440 const deUint32 flagTessellation = 1u << 0; 1441 const deUint32 flagGeometry = 1u << 1; 1442 1443 for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(caseGroups); ++groupNdx) 1444 for (int indexingMode = 0; indexingMode < 2; ++indexingMode) 1445 { 1446 const bool dynamicIndexing = (indexingMode == 1); 1447 const std::string mainGroupName = de::toString(caseGroups[groupNdx].groupName) + (dynamicIndexing ? "_dynamic_index" : ""); 1448 1449 MovePtr<tcu::TestCaseGroup> mainGroup(new tcu::TestCaseGroup(testCtx, mainGroupName.c_str(), "")); 1450 1451 for (deUint32 shaderMask = 0u; shaderMask <= (flagTessellation | flagGeometry); ++shaderMask) 1452 { 1453 const bool useTessellation = (shaderMask & flagTessellation) != 0; 1454 const bool useGeometry = (shaderMask & flagGeometry) != 0; 1455 const std::string shaderGroupName = std::string("vert") + (useTessellation ? "_tess" : "") + (useGeometry ? "_geom" : ""); 1456 1457 MovePtr<tcu::TestCaseGroup> shaderGroup(new tcu::TestCaseGroup(testCtx, shaderGroupName.c_str(), "")); 1458 1459 for (int numClipPlanes = 1; numClipPlanes <= MAX_CLIP_DISTANCES; ++numClipPlanes) 1460 for (int fragmentShaderReadNdx = 0; fragmentShaderReadNdx < DE_LENGTH_OF_ARRAY(fragmentShaderReads); ++fragmentShaderReadNdx) 1461 { 1462 const int numCullPlanes = (caseGroups[groupNdx].useCullDistance 1463 ? std::min(static_cast<int>(MAX_CULL_DISTANCES), MAX_COMBINED_CLIP_AND_CULL_DISTANCES - numClipPlanes) 1464 : 0); 1465 const std::string caseName = de::toString(numClipPlanes) + (numCullPlanes > 0 ? "_" + de::toString(numCullPlanes) : "") + de::toString(fragmentShaderReads[fragmentShaderReadNdx].name); 1466 const VkPrimitiveTopology topology = (useTessellation ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); 1467 1468 addFunctionCaseWithPrograms<CaseDefinition>( 1469 shaderGroup.get(), caseName, caseGroups[groupNdx].description, initPrograms, testClipDistance, 1470 CaseDefinition(topology, numClipPlanes, numCullPlanes, useTessellation, useGeometry, dynamicIndexing, fragmentShaderReads[fragmentShaderReadNdx].readInFragmentShader)); 1471 } 1472 mainGroup->addChild(shaderGroup.release()); 1473 } 1474 clipDistanceGroup->addChild(mainGroup.release()); 1475 } 1476 } 1477 1478 // Complementarity criterion (i.e. clipped and not clipped areas must add up to a complete primitive with no holes nor overlap) 1479 { 1480 using namespace ClipDistanceComplementarity; 1481 1482 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "complementarity", "")); 1483 1484 for (int numClipDistances = 1; numClipDistances <= MAX_CLIP_DISTANCES; ++numClipDistances) 1485 addFunctionCaseWithPrograms<int>(group.get(), de::toString(numClipDistances).c_str(), "", initPrograms, testComplementarity, numClipDistances); 1486 1487 clippingTestsGroup->addChild(group.release()); 1488 } 1489 1490 clippingTestsGroup->addChild(clipDistanceGroup.release()); 1491 } 1492 } 1493 1494 } // anonymous 1495 1496 tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx) 1497 { 1498 return createTestGroup(testCtx, "clipping", "Clipping tests", addClippingTests); 1499 } 1500 1501 } // clipping 1502 } // vkt 1503