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 "tcuCommandLine.hpp"
36 #include "deUniquePtr.hpp"
37 #include "deStringUtil.hpp"
38 #include "deRandom.hpp"
39
40 namespace vkt
41 {
42 namespace clipping
43 {
44 namespace
45 {
46 using namespace vk;
47 using de::MovePtr;
48 using tcu::IVec2;
49 using tcu::UVec2;
50 using tcu::Vec4;
51 using namespace drawutil;
52
53 enum TestConstants
54 {
55 RENDER_SIZE = 16,
56 RENDER_SIZE_LARGE = 128,
57 NUM_RENDER_PIXELS = RENDER_SIZE * RENDER_SIZE,
58 NUM_PATCH_CONTROL_POINTS = 3,
59 MAX_CLIP_DISTANCES = 8,
60 MAX_CULL_DISTANCES = 8,
61 MAX_COMBINED_CLIP_AND_CULL_DISTANCES = 8,
62 };
63
64 enum FeatureFlagBits
65 {
66 FEATURE_TESSELLATION_SHADER = 1u << 0,
67 FEATURE_GEOMETRY_SHADER = 1u << 1,
68 FEATURE_SHADER_FLOAT_64 = 1u << 2,
69 FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS = 1u << 3,
70 FEATURE_FRAGMENT_STORES_AND_ATOMICS = 1u << 4,
71 FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE = 1u << 5,
72 FEATURE_DEPTH_CLAMP = 1u << 6,
73 FEATURE_LARGE_POINTS = 1u << 7,
74 FEATURE_WIDE_LINES = 1u << 8,
75 FEATURE_SHADER_CLIP_DISTANCE = 1u << 9,
76 FEATURE_SHADER_CULL_DISTANCE = 1u << 10,
77 };
78 typedef uint32_t FeatureFlags;
79
requireFeatures(const InstanceInterface & vki,const VkPhysicalDevice physDevice,const FeatureFlags flags)80 void requireFeatures(const InstanceInterface &vki, const VkPhysicalDevice physDevice, const FeatureFlags flags)
81 {
82 const VkPhysicalDeviceFeatures features = getPhysicalDeviceFeatures(vki, physDevice);
83
84 if (((flags & FEATURE_TESSELLATION_SHADER) != 0) && !features.tessellationShader)
85 throw tcu::NotSupportedError("Tessellation shader not supported");
86
87 if (((flags & FEATURE_GEOMETRY_SHADER) != 0) && !features.geometryShader)
88 throw tcu::NotSupportedError("Geometry shader not supported");
89
90 if (((flags & FEATURE_SHADER_FLOAT_64) != 0) && !features.shaderFloat64)
91 throw tcu::NotSupportedError("Double-precision floats not supported");
92
93 if (((flags & FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS) != 0) && !features.vertexPipelineStoresAndAtomics)
94 throw tcu::NotSupportedError("SSBO and image writes not supported in vertex pipeline");
95
96 if (((flags & FEATURE_FRAGMENT_STORES_AND_ATOMICS) != 0) && !features.fragmentStoresAndAtomics)
97 throw tcu::NotSupportedError("SSBO and image writes not supported in fragment shader");
98
99 if (((flags & FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE) != 0) &&
100 !features.shaderTessellationAndGeometryPointSize)
101 throw tcu::NotSupportedError("Tessellation and geometry shaders don't support PointSize built-in");
102
103 if (((flags & FEATURE_DEPTH_CLAMP) != 0) && !features.depthClamp)
104 throw tcu::NotSupportedError("Depth clamp not supported");
105
106 if (((flags & FEATURE_LARGE_POINTS) != 0) && !features.largePoints)
107 throw tcu::NotSupportedError("Large points not supported");
108
109 if (((flags & FEATURE_WIDE_LINES) != 0) && !features.wideLines)
110 throw tcu::NotSupportedError("Wide lines not supported");
111
112 if (((flags & FEATURE_SHADER_CLIP_DISTANCE) != 0) && !features.shaderClipDistance)
113 throw tcu::NotSupportedError("Shader ClipDistance not supported");
114
115 if (((flags & FEATURE_SHADER_CULL_DISTANCE) != 0) && !features.shaderCullDistance)
116 throw tcu::NotSupportedError("Shader CullDistance not supported");
117 }
118
genVertices(const VkPrimitiveTopology topology,const Vec4 & offset,const float slope)119 std::vector<Vec4> genVertices(const VkPrimitiveTopology topology, const Vec4 &offset, const float slope)
120 {
121 const float p = 1.0f;
122 const float hp = 0.5f;
123 const float z = 0.0f;
124 const float w = 1.0f;
125
126 std::vector<Vec4> vertices;
127
128 // We're setting adjacent vertices to zero where needed, as we don't use them in meaningful way.
129
130 switch (topology)
131 {
132 case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
133 vertices.push_back(offset + Vec4(0.0f, 0.0f, slope / 2.0f + z, w));
134 vertices.push_back(offset + Vec4(-hp, -hp, z, w));
135 vertices.push_back(offset + Vec4(hp, -hp, slope + z, w));
136 vertices.push_back(offset + Vec4(-hp, hp, z, w));
137 vertices.push_back(offset + Vec4(hp, hp, slope + z, w));
138 break;
139
140 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
141 vertices.push_back(offset + Vec4(-p, -p, z, w));
142 vertices.push_back(offset + Vec4(p, p, slope + z, w)); // line 0
143 vertices.push_back(offset + Vec4(p, p, slope + z, w));
144 vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // line 1
145 vertices.push_back(offset + Vec4(p, -p, slope + z, w));
146 vertices.push_back(offset + Vec4(-p, p, z, w)); // line 2
147 break;
148
149 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
150 vertices.push_back(Vec4());
151 vertices.push_back(offset + Vec4(-p, -p, z, w));
152 vertices.push_back(offset + Vec4(p, p, slope + z, w)); // line 0
153 vertices.push_back(Vec4());
154 vertices.push_back(Vec4());
155 vertices.push_back(offset + Vec4(p, p, slope + z, w));
156 vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // line 1
157 vertices.push_back(Vec4());
158 vertices.push_back(Vec4());
159 vertices.push_back(offset + Vec4(p, -p, slope + z, w));
160 vertices.push_back(offset + Vec4(-p, p, z, w)); // line 2
161 vertices.push_back(Vec4());
162 break;
163
164 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
165 vertices.push_back(offset + Vec4(-p, -p, z, w));
166 vertices.push_back(offset + Vec4(p, p, slope + z, w)); // line 0
167 vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // line 1
168 vertices.push_back(offset + Vec4(-p, p, z, w)); // line 2
169 break;
170
171 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
172 vertices.push_back(Vec4());
173 vertices.push_back(offset + Vec4(-p, -p, z, w));
174 vertices.push_back(offset + Vec4(p, p, slope + z, w)); // line 0
175 vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // line 1
176 vertices.push_back(offset + Vec4(-p, p, z, w)); // line 2
177 vertices.push_back(Vec4());
178 break;
179
180 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
181 vertices.push_back(offset + Vec4(p, -p, slope + z, w));
182 vertices.push_back(offset + Vec4(-p, -p, z, w));
183 vertices.push_back(offset + Vec4(-p, p, z, w)); // triangle 0
184 vertices.push_back(offset + Vec4(-p, p, z, w));
185 vertices.push_back(offset + Vec4(p, p, slope + z, w));
186 vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // triangle 1
187 break;
188
189 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
190 vertices.push_back(offset + Vec4(p, -p, slope + z, w));
191 vertices.push_back(Vec4());
192 vertices.push_back(offset + Vec4(-p, -p, z, w));
193 vertices.push_back(Vec4());
194 vertices.push_back(offset + Vec4(-p, p, z, w)); // triangle 0
195 vertices.push_back(Vec4());
196 vertices.push_back(offset + Vec4(-p, p, z, w));
197 vertices.push_back(Vec4());
198 vertices.push_back(offset + Vec4(p, p, slope + z, w));
199 vertices.push_back(Vec4());
200 vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // triangle 1
201 vertices.push_back(Vec4());
202 break;
203
204 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
205 vertices.push_back(offset + Vec4(-p, -p, z, w));
206 vertices.push_back(offset + Vec4(-p, p, z, w));
207 vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // triangle 0
208 vertices.push_back(offset + Vec4(p, p, slope + z, w)); // triangle 1
209 break;
210
211 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
212 vertices.push_back(offset + Vec4(-p, -p, z, w));
213 vertices.push_back(Vec4());
214 vertices.push_back(offset + Vec4(-p, p, z, w));
215 vertices.push_back(Vec4());
216 vertices.push_back(offset + Vec4(p, -p, slope + z, w)); // triangle 0
217 vertices.push_back(Vec4());
218 vertices.push_back(offset + Vec4(p, p, slope + z, w)); // triangle 1
219 vertices.push_back(Vec4());
220 break;
221
222 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
223 vertices.push_back(offset + Vec4(p, -p, slope + z, w));
224 vertices.push_back(offset + Vec4(-p, -p, z, w));
225 vertices.push_back(offset + Vec4(-p, p, z, w)); // triangle 0
226 vertices.push_back(offset + Vec4(p, p, slope + z, w)); // triangle 1
227 break;
228
229 case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST:
230 DE_ASSERT(0);
231 break;
232
233 default:
234 DE_ASSERT(0);
235 break;
236 }
237 return vertices;
238 }
239
isColorInRange(const Vec4 & color,const Vec4 & minColor,const Vec4 & maxColor)240 bool inline isColorInRange(const Vec4 &color, const Vec4 &minColor, const Vec4 &maxColor)
241 {
242 return (minColor.x() <= color.x() && color.x() <= maxColor.x()) &&
243 (minColor.y() <= color.y() && color.y() <= maxColor.y()) &&
244 (minColor.z() <= color.z() && color.z() <= maxColor.z()) &&
245 (minColor.w() <= color.w() && color.w() <= maxColor.w());
246 }
247
248 //! Count pixels that match color within threshold, in the specified region.
countPixels(const tcu::ConstPixelBufferAccess pixels,const IVec2 & regionOffset,const IVec2 & regionSize,const Vec4 & color,const Vec4 & colorThreshold)249 int countPixels(const tcu::ConstPixelBufferAccess pixels, const IVec2 ®ionOffset, const IVec2 ®ionSize,
250 const Vec4 &color, const Vec4 &colorThreshold)
251 {
252 const Vec4 minColor = color - colorThreshold;
253 const Vec4 maxColor = color + colorThreshold;
254 const int xEnd = regionOffset.x() + regionSize.x();
255 const int yEnd = regionOffset.y() + regionSize.y();
256 int numPixels = 0;
257
258 DE_ASSERT(xEnd <= pixels.getWidth());
259 DE_ASSERT(yEnd <= pixels.getHeight());
260
261 for (int y = regionOffset.y(); y < yEnd; ++y)
262 for (int x = regionOffset.x(); x < xEnd; ++x)
263 {
264 if (isColorInRange(pixels.getPixel(x, y), minColor, maxColor))
265 ++numPixels;
266 }
267
268 return numPixels;
269 }
270
countPixels(const tcu::ConstPixelBufferAccess pixels,const Vec4 & color,const Vec4 & colorThreshold)271 int countPixels(const tcu::ConstPixelBufferAccess pixels, const Vec4 &color, const Vec4 &colorThreshold)
272 {
273 return countPixels(pixels, IVec2(), IVec2(pixels.getWidth(), pixels.getHeight()), color, colorThreshold);
274 }
275
276 //! 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.
checkFragColors(const tcu::ConstPixelBufferAccess pixels,IVec2 clipRegion,int barIdx,bool hasCullDistance)277 bool checkFragColors(const tcu::ConstPixelBufferAccess pixels, IVec2 clipRegion, int barIdx, bool hasCullDistance)
278 {
279 for (int y = 0; y < pixels.getHeight(); ++y)
280 for (int x = 0; x < pixels.getWidth(); ++x)
281 {
282 if (x < clipRegion.x() && y < clipRegion.y())
283 continue;
284
285 const tcu::Vec4 color = pixels.getPixel(x, y);
286 const int barWidth = pixels.getWidth() / 8;
287 const bool insideBar = x >= barWidth * barIdx && x < barWidth * (barIdx + 1);
288 const float expectedClipDistance =
289 insideBar ? (((((float)y + 0.5f) / (float)pixels.getHeight()) - 0.5f) * 2.0f) : 0.0f;
290 float expectedCullDistance = 0.5f;
291 const float clipDistance = color.y();
292 const float cullDistance = color.z();
293 const float height = (float)pixels.getHeight();
294
295 if (hasCullDistance)
296 {
297 /* Linear interpolation of the cull distance.
298 * Remember there are precision errors due to 8-bit UNORM, but they should fall inside 0.01f threshold.
299 *
300 * Notes about the results:
301 * - linear interpolation of gl_CullDistance[i] = [0.0f, 0.5f]. Correct.
302 * - Constant value:
303 * + 0.1f: value written by vertex shader when there are other geometry-related shaders. It means the value was not overriden. Failure.
304 * + 0.2f: value written by tessc shader when cull distance value from vertex is not 0.1f. Failure.
305 * + 0.3f: value written by tessc shader when cull distance value from vertex is 0.1f and there is geometry shader. Failure.
306 * + 0.4f: value written by geometry shader when cull distance is not either 0.1f (if no tess is present) or 0.3f (tess present). Failure.
307 */
308 if (y >= (pixels.getHeight() / 2))
309 expectedCullDistance = expectedCullDistance * (1.0f + (2.0f * (float)y) - height) / height;
310 else
311 expectedCullDistance = 0.0f;
312 }
313
314 if (fabs(clipDistance - expectedClipDistance) > 0.01f)
315 return false;
316 if (hasCullDistance && fabs(cullDistance - expectedCullDistance) > 0.01f)
317 return false;
318 }
319
320 return true;
321 }
322
323 //! Clipping against the default clip volume.
324 namespace ClipVolume
325 {
326
327 //! Used by wide lines test.
328 enum LineOrientation
329 {
330 LINE_ORIENTATION_AXIS_ALIGNED,
331 LINE_ORIENTATION_DIAGONAL,
332 };
333
334 const VkPointClippingBehavior invalidClippingBehavior = VK_POINT_CLIPPING_BEHAVIOR_LAST;
335
getClippingBehavior(const InstanceInterface & vk,VkPhysicalDevice physicalDevice)336 VkPointClippingBehavior getClippingBehavior(const InstanceInterface &vk, VkPhysicalDevice physicalDevice)
337 {
338 VkPhysicalDevicePointClippingProperties behaviorProperties = {
339 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES, // VkStructureType sType
340 DE_NULL, // void* pNext
341 invalidClippingBehavior // VkPointClippingBehavior pointClippingBehavior
342 };
343 VkPhysicalDeviceProperties2 properties2;
344
345 DE_ASSERT(getPointClippingBehaviorName(invalidClippingBehavior) == DE_NULL);
346
347 deMemset(&properties2, 0, sizeof(properties2));
348
349 properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
350 properties2.pNext = &behaviorProperties;
351
352 vk.getPhysicalDeviceProperties2(physicalDevice, &properties2);
353
354 return behaviorProperties.pointClippingBehavior;
355 }
356
addSimplePrograms(SourceCollections & programCollection,const float pointSize=0.0f)357 void addSimplePrograms(SourceCollections &programCollection, const float pointSize = 0.0f)
358 {
359 // Vertex shader
360 {
361 const bool usePointSize = pointSize > 0.0f;
362
363 std::ostringstream src;
364 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
365 << "\n"
366 << "layout(location = 0) in vec4 v_position;\n"
367 << "\n"
368 << "out gl_PerVertex {\n"
369 << " vec4 gl_Position;\n"
370 << (usePointSize ? " float gl_PointSize;\n" : "") << "};\n"
371 << "\n"
372 << "void main (void)\n"
373 << "{\n"
374 << " gl_Position = v_position;\n"
375 << (usePointSize ? " gl_PointSize = " + de::floatToString(pointSize, 1) + ";\n" : "") << "}\n";
376
377 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
378 }
379
380 // Fragment shader
381 {
382 std::ostringstream src;
383 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
384 << "\n"
385 << "layout(location = 0) out vec4 o_color;\n"
386 << "\n"
387 << "void main (void)\n"
388 << "{\n"
389 << " o_color = vec4(1.0, gl_FragCoord.z, 0.0, 1.0);\n"
390 << "}\n";
391
392 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
393 }
394 }
395
initPrograms(SourceCollections & programCollection,const VkPrimitiveTopology topology)396 void initPrograms(SourceCollections &programCollection, const VkPrimitiveTopology topology)
397 {
398 const float pointSize = (topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST ? 1.0f : 0.0f);
399 addSimplePrograms(programCollection, pointSize);
400 }
401
initPrograms(SourceCollections & programCollection,const LineOrientation lineOrientation)402 void initPrograms(SourceCollections &programCollection, const LineOrientation lineOrientation)
403 {
404 DE_UNREF(lineOrientation);
405 addSimplePrograms(programCollection);
406 }
407
initProgramsPointSize(SourceCollections & programCollection)408 void initProgramsPointSize(SourceCollections &programCollection)
409 {
410 addSimplePrograms(programCollection, 0.75f * static_cast<float>(RENDER_SIZE));
411 }
412
413 //! Primitives fully inside the clip volume.
testPrimitivesInside(Context & context,const VkPrimitiveTopology topology)414 tcu::TestStatus testPrimitivesInside(Context &context, const VkPrimitiveTopology topology)
415 {
416 int minExpectedBlackPixels = 0;
417
418 switch (topology)
419 {
420 case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
421 // We draw only 5 points.
422 minExpectedBlackPixels = NUM_RENDER_PIXELS - 5;
423 break;
424
425 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
426 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
427 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
428 // Fallthrough
429 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
430 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
431 // Allow for some error.
432 minExpectedBlackPixels = NUM_RENDER_PIXELS - 3 * RENDER_SIZE;
433 break;
434
435 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
436 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
437 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
438 // Fallthrough
439 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
440 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
441 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
442 // All render area should be covered.
443 minExpectedBlackPixels = 0;
444 break;
445
446 default:
447 DE_ASSERT(0);
448 break;
449 }
450
451 std::vector<VulkanShader> shaders;
452 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
453 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
454
455 tcu::TestLog &log = context.getTestContext().getLog();
456 int numPassed = 0;
457
458 static const struct
459 {
460 const char *const desc;
461 float zPos;
462 } cases[] = {
463 {
464 "Draw primitives at near clipping plane, z = 0.0",
465 0.0f,
466 },
467 {
468 "Draw primitives at z = 0.5",
469 0.5f,
470 },
471 {
472 "Draw primitives at far clipping plane, z = 1.0",
473 1.0f,
474 },
475 };
476
477 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
478 {
479 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
480
481 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 0.0f);
482 FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
483 PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
484 DrawCallData drawCallData(topology, vertices);
485 VulkanProgram vulkanProgram(shaders);
486
487 VulkanDrawContext drawContext(context, framebufferState);
488 drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
489 drawContext.draw();
490
491 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
492 if (numBlackPixels >= minExpectedBlackPixels)
493 ++numPassed;
494 }
495
496 return (numPassed == DE_LENGTH_OF_ARRAY(cases) ? tcu::TestStatus::pass("OK") :
497 tcu::TestStatus::fail("Rendered image(s) are incorrect"));
498 }
499
500 //! Primitives fully outside the clip volume.
testPrimitivesOutside(Context & context,const VkPrimitiveTopology topology)501 tcu::TestStatus testPrimitivesOutside(Context &context, const VkPrimitiveTopology topology)
502 {
503 switch (topology)
504 {
505 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
506 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
507 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
508 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
509 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
510 break;
511 default:
512 break;
513 }
514
515 std::vector<VulkanShader> shaders;
516 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
517 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
518
519 tcu::TestLog &log = context.getTestContext().getLog();
520 int numPassed = 0;
521
522 static const struct
523 {
524 const char *const desc;
525 float zPos;
526 } cases[] = {
527 {
528 "Draw primitives in front of the near clipping plane, z < 0.0",
529 -0.5f,
530 },
531 {
532 "Draw primitives behind the far clipping plane, z > 1.0",
533 1.5f,
534 },
535 };
536
537 log << tcu::TestLog::Message << "Drawing primitives outside the clip volume. Expecting an empty image."
538 << tcu::TestLog::EndMessage;
539
540 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
541 {
542 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
543
544 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 0.0f);
545 FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
546 PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
547 DrawCallData drawCallData(topology, vertices);
548 VulkanProgram vulkanProgram(shaders);
549
550 VulkanDrawContext drawContext(context, framebufferState);
551 drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
552 drawContext.draw();
553
554 // All pixels must be black -- nothing is drawn.
555 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
556 if (numBlackPixels == NUM_RENDER_PIXELS)
557 ++numPassed;
558 }
559
560 return (numPassed == DE_LENGTH_OF_ARRAY(cases) ? tcu::TestStatus::pass("OK") :
561 tcu::TestStatus::fail("Rendered image(s) are incorrect"));
562 }
563
564 //! Primitives partially outside the clip volume, but depth clamped
testPrimitivesDepthClamp(Context & context,const VkPrimitiveTopology topology)565 tcu::TestStatus testPrimitivesDepthClamp(Context &context, const VkPrimitiveTopology topology)
566 {
567 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_DEPTH_CLAMP);
568
569 std::vector<VulkanShader> shaders;
570 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
571 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
572
573 const int numCases = 4;
574 const IVec2 regionSize = IVec2(RENDER_SIZE / 2, RENDER_SIZE); //! size of the clamped region
575 const int regionPixels = regionSize.x() * regionSize.y();
576 tcu::TestLog &log = context.getTestContext().getLog();
577 int numPassed = 0;
578
579 static const struct
580 {
581 const char *const desc;
582 float zPos;
583 bool depthClampEnable;
584 IVec2 regionOffset;
585 Vec4 color;
586 } cases[numCases] = {
587 {"Draw primitives intersecting the near clipping plane, depth clamp disabled", -0.5f, false, IVec2(0, 0),
588 Vec4(0.0f, 0.0f, 0.0f, 1.0f)},
589 {"Draw primitives intersecting the near clipping plane, depth clamp enabled", -0.5f, true, IVec2(0, 0),
590 Vec4(1.0f, 0.0f, 0.0f, 1.0f)},
591 {"Draw primitives intersecting the far clipping plane, depth clamp disabled", 0.5f, false,
592 IVec2(RENDER_SIZE / 2, 0), Vec4(0.0f, 0.0f, 0.0f, 1.0f)},
593 {"Draw primitives intersecting the far clipping plane, depth clamp enabled", 0.5f, true,
594 IVec2(RENDER_SIZE / 2, 0), Vec4(1.0f, 1.0f, 0.0f, 1.0f)},
595 };
596
597 // Per case minimum number of colored pixels.
598 int caseMinPixels[numCases] = {0, 0, 0, 0};
599
600 switch (topology)
601 {
602 case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
603 caseMinPixels[0] = caseMinPixels[2] = regionPixels - 1;
604 caseMinPixels[1] = caseMinPixels[3] = 2;
605 break;
606
607 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
608 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
609 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
610 // Fallthrough
611 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
612 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
613 caseMinPixels[0] = regionPixels;
614 caseMinPixels[1] = RENDER_SIZE - 2;
615 caseMinPixels[2] = regionPixels;
616 caseMinPixels[3] = 2 * (RENDER_SIZE - 2);
617 break;
618
619 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
620 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
621 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
622 // Fallthrough
623 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
624 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
625 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
626 caseMinPixels[0] = caseMinPixels[1] = caseMinPixels[2] = caseMinPixels[3] = regionPixels;
627 break;
628
629 default:
630 DE_ASSERT(0);
631 break;
632 }
633
634 for (int caseNdx = 0; caseNdx < numCases; ++caseNdx)
635 {
636 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
637
638 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 1.0f);
639 FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
640 PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
641 pipelineState.depthClampEnable = cases[caseNdx].depthClampEnable;
642 DrawCallData drawCallData(topology, vertices);
643 VulkanProgram vulkanProgram(shaders);
644
645 VulkanDrawContext drawContext(context, framebufferState);
646 drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
647 drawContext.draw();
648
649 const int numPixels = countPixels(drawContext.getColorPixels(), cases[caseNdx].regionOffset, regionSize,
650 cases[caseNdx].color, Vec4());
651
652 if (numPixels >= caseMinPixels[caseNdx])
653 ++numPassed;
654 }
655
656 return (numPassed == numCases ? tcu::TestStatus::pass("OK") :
657 tcu::TestStatus::fail("Rendered image(s) are incorrect"));
658 }
659
660 //! Primitives partially outside the clip volume, but depth clipped with explicit depth clip control
testPrimitivesDepthClip(Context & context,const VkPrimitiveTopology topology)661 tcu::TestStatus testPrimitivesDepthClip(Context &context, const VkPrimitiveTopology topology)
662 {
663 if (!context.getDepthClipEnableFeaturesEXT().depthClipEnable)
664 throw tcu::NotSupportedError("VK_EXT_depth_clip_enable not supported");
665
666 std::vector<VulkanShader> shaders;
667 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
668 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
669
670 const int numCases = 4;
671 const IVec2 regionSize = IVec2(RENDER_SIZE / 2, RENDER_SIZE); //! size of the clamped region
672 const int regionPixels = regionSize.x() * regionSize.y();
673 tcu::TestLog &log = context.getTestContext().getLog();
674 int numPassed = 0;
675
676 static const struct
677 {
678 const char *const desc;
679 float zPos;
680 bool depthClipEnable;
681 IVec2 regionOffset;
682 Vec4 color;
683 } cases[numCases] = {
684 {"Draw primitives intersecting the near clipping plane, depth clip enabled", -0.5f, true, IVec2(0, 0),
685 Vec4(0.0f, 0.0f, 0.0f, 1.0f)},
686 {"Draw primitives intersecting the near clipping plane, depth clip disabled", -0.5f, false, IVec2(0, 0),
687 Vec4(1.0f, 0.0f, 0.0f, 1.0f)},
688 {"Draw primitives intersecting the far clipping plane, depth clip enabled", 0.5f, true,
689 IVec2(RENDER_SIZE / 2, 0), Vec4(0.0f, 0.0f, 0.0f, 1.0f)},
690 {"Draw primitives intersecting the far clipping plane, depth clip disabled", 0.5f, false,
691 IVec2(RENDER_SIZE / 2, 0), Vec4(1.0f, 1.0f, 0.0f, 1.0f)},
692 };
693
694 // Per case minimum number of colored pixels.
695 int caseMinPixels[numCases] = {0, 0, 0, 0};
696
697 switch (topology)
698 {
699 case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
700 caseMinPixels[0] = caseMinPixels[2] = regionPixels - 1;
701 caseMinPixels[1] = caseMinPixels[3] = 2;
702 break;
703
704 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
705 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
706 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
707 // Fallthrough
708 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
709 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
710 caseMinPixels[0] = regionPixels;
711 caseMinPixels[1] = RENDER_SIZE - 2;
712 caseMinPixels[2] = regionPixels;
713 caseMinPixels[3] = 2 * (RENDER_SIZE - 2);
714 break;
715
716 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
717 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
718 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
719 // Fallthrough
720 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
721 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
722 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
723 caseMinPixels[0] = caseMinPixels[1] = caseMinPixels[2] = caseMinPixels[3] = regionPixels;
724 break;
725
726 default:
727 DE_ASSERT(0);
728 break;
729 }
730
731 // Test depth clip with depth clamp disabled.
732 numPassed = 0;
733 for (int caseNdx = 0; caseNdx < numCases; ++caseNdx)
734 {
735 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
736
737 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 1.0f);
738 FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
739 PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
740 pipelineState.depthClampEnable = false;
741 pipelineState.explicitDepthClipEnable = true;
742 pipelineState.depthClipEnable = cases[caseNdx].depthClipEnable;
743 DrawCallData drawCallData(topology, vertices);
744 VulkanProgram vulkanProgram(shaders);
745
746 VulkanDrawContext drawContext(context, framebufferState);
747 drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
748 drawContext.draw();
749
750 const int numPixels = countPixels(drawContext.getColorPixels(), cases[caseNdx].regionOffset, regionSize,
751 cases[caseNdx].color, Vec4());
752
753 if (numPixels >= caseMinPixels[caseNdx])
754 ++numPassed;
755 }
756
757 #ifdef CTS_USES_VULKANSC
758 if (context.getTestContext().getCommandLine().isSubProcess())
759 #endif // CTS_USES_VULKANSC
760 {
761 if (numPassed < numCases)
762 return tcu::TestStatus::fail("Rendered image(s) are incorrect (depth clip with depth clamp disabled)");
763 }
764
765 // Test depth clip with depth clamp enabled.
766 numPassed = 0;
767 if (getPhysicalDeviceFeatures(context.getInstanceInterface(), context.getPhysicalDevice()).depthClamp)
768 {
769 for (int caseNdx = 0; caseNdx < numCases; ++caseNdx)
770 {
771 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
772
773 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 1.0f);
774 FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
775 PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
776 pipelineState.depthClampEnable = true;
777 pipelineState.explicitDepthClipEnable = true;
778 pipelineState.depthClipEnable = cases[caseNdx].depthClipEnable;
779 DrawCallData drawCallData(topology, vertices);
780 VulkanProgram vulkanProgram(shaders);
781
782 VulkanDrawContext drawContext(context, framebufferState);
783 drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
784 drawContext.draw();
785
786 const int numPixels = countPixels(drawContext.getColorPixels(), cases[caseNdx].regionOffset, regionSize,
787 cases[caseNdx].color, Vec4());
788
789 if (numPixels >= caseMinPixels[caseNdx])
790 ++numPassed;
791 }
792
793 if (numPassed < numCases)
794 return tcu::TestStatus::fail("Rendered image(s) are incorrect (depth clip with depth clamp enabled)");
795 }
796
797 return tcu::TestStatus::pass("OK");
798 }
799
800 //! Large point clipping
801 //! Spec: If the primitive under consideration is a point, then clipping passes it unchanged if it lies within the clip volume;
802 //! otherwise, it is discarded.
testLargePoints(Context & context)803 tcu::TestStatus testLargePoints(Context &context)
804 {
805 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_LARGE_POINTS);
806
807 bool pointClippingOutside = true;
808
809 if (context.isDeviceFunctionalitySupported("VK_KHR_maintenance2"))
810 {
811 VkPointClippingBehavior clippingBehavior =
812 getClippingBehavior(context.getInstanceInterface(), context.getPhysicalDevice());
813
814 switch (clippingBehavior)
815 {
816 case VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES:
817 pointClippingOutside = true;
818 break;
819 case VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY:
820 pointClippingOutside = false;
821 break;
822 case invalidClippingBehavior:
823 TCU_FAIL("Clipping behavior read failure"); // Does not fall through
824 default:
825 {
826 TCU_FAIL("Unexpected clipping behavior reported");
827 }
828 }
829 }
830
831 std::vector<VulkanShader> shaders;
832 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
833 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
834
835 std::vector<Vec4> vertices;
836 {
837 const float delta = 0.1f; // much smaller than the point size
838 const float p = 1.0f + delta;
839
840 vertices.push_back(Vec4(-p, -p, 0.1f, 1.0f));
841 vertices.push_back(Vec4(-p, p, 0.2f, 1.0f));
842 vertices.push_back(Vec4(p, p, 0.4f, 1.0f));
843 vertices.push_back(Vec4(p, -p, 0.6f, 1.0f));
844 vertices.push_back(Vec4(0.0f, -p, 0.8f, 1.0f));
845 vertices.push_back(Vec4(p, 0.0f, 0.7f, 1.0f));
846 vertices.push_back(Vec4(0.0f, p, 0.5f, 1.0f));
847 vertices.push_back(Vec4(-p, 0.0f, 0.3f, 1.0f));
848 }
849
850 tcu::TestLog &log = context.getTestContext().getLog();
851
852 log << tcu::TestLog::Message
853 << "Drawing several large points just outside the clip volume. Expecting an empty image or all points rendered."
854 << tcu::TestLog::EndMessage;
855
856 FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
857 PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
858 DrawCallData drawCallData(VK_PRIMITIVE_TOPOLOGY_POINT_LIST, vertices);
859 VulkanProgram vulkanProgram(shaders);
860
861 VulkanDrawContext drawContext(context, framebufferState);
862 drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
863 drawContext.draw();
864
865 // Popful case: All pixels must be black -- nothing is drawn.
866 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
867 bool result = false;
868
869 // Pop-free case: All points must be rendered.
870 bool allPointsRendered = true;
871 for (std::vector<Vec4>::iterator i = vertices.begin(); i != vertices.end(); ++i)
872 {
873 if (countPixels(drawContext.getColorPixels(), Vec4(1.0f, i->z(), 0.0f, 1.0f), Vec4(0.01f)) == 0)
874 allPointsRendered = false;
875 }
876
877 if (pointClippingOutside)
878 {
879 result = (numBlackPixels == NUM_RENDER_PIXELS || allPointsRendered);
880 }
881 else
882 {
883 // Rendering pixels without clipping: all points should be drawn.
884 result = (allPointsRendered == true);
885 }
886
887 return (result ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
888 }
889
890 class WideLineVertexShader : public rr::VertexShader
891 {
892 public:
WideLineVertexShader(void)893 WideLineVertexShader(void) : rr::VertexShader(1, 1)
894 {
895 m_inputs[0].type = rr::GENERICVECTYPE_FLOAT;
896 m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
897 }
898
~WideLineVertexShader()899 virtual ~WideLineVertexShader()
900 {
901 }
902
shadeVertices(const rr::VertexAttrib * inputs,rr::VertexPacket * const * packets,const int numPackets) const903 void shadeVertices(const rr::VertexAttrib *inputs, rr::VertexPacket *const *packets, const int numPackets) const
904 {
905 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
906 {
907 const tcu::Vec4 position =
908 rr::readVertexAttribFloat(inputs[0], packets[packetNdx]->instanceNdx, packets[packetNdx]->vertexNdx);
909
910 packets[packetNdx]->position = position;
911 packets[packetNdx]->outputs[0] = position;
912 }
913 }
914 };
915
916 class WideLineFragmentShader : public rr::FragmentShader
917 {
918 public:
WideLineFragmentShader(void)919 WideLineFragmentShader(void) : rr::FragmentShader(1, 1)
920 {
921 m_inputs[0].type = rr::GENERICVECTYPE_FLOAT;
922 m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
923 }
924
~WideLineFragmentShader()925 virtual ~WideLineFragmentShader()
926 {
927 }
928
shadeFragments(rr::FragmentPacket * packets,const int numPackets,const rr::FragmentShadingContext & context) const929 void shadeFragments(rr::FragmentPacket *packets, const int numPackets,
930 const rr::FragmentShadingContext &context) const
931 {
932 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
933 {
934 for (int fragNdx = 0; fragNdx < rr::NUM_FRAGMENTS_PER_PACKET; ++fragNdx)
935 {
936 const float depth = rr::readVarying<float>(packets[packetNdx], context, 0, fragNdx).z();
937 rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, tcu::Vec4(1.0f, depth, 0.0f, 1.0f));
938 }
939 }
940 }
941 };
942 //! Wide line clipping
testWideLines(Context & context,const LineOrientation lineOrientation)943 tcu::TestStatus testWideLines(Context &context, const LineOrientation lineOrientation)
944 {
945 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_WIDE_LINES);
946
947 std::vector<VulkanShader> shaders;
948 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
949 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
950
951 const float delta = 0.1f; // much smaller than the line width
952
953 std::vector<Vec4> vertices;
954 if (lineOrientation == LINE_ORIENTATION_AXIS_ALIGNED)
955 {
956 // Axis-aligned lines just outside the clip volume.
957 const float p = 1.0f + delta;
958 const float q = 0.9f;
959
960 vertices.push_back(Vec4(-p, -q, 0.1f, 1.0f));
961 vertices.push_back(Vec4(-p, q, 0.9f, 1.0f)); // line 0
962 vertices.push_back(Vec4(-q, p, 0.1f, 1.0f));
963 vertices.push_back(Vec4(q, p, 0.9f, 1.0f)); // line 1
964 vertices.push_back(Vec4(p, q, 0.1f, 1.0f));
965 vertices.push_back(Vec4(p, -q, 0.9f, 1.0f)); // line 2
966 vertices.push_back(Vec4(q, -p, 0.1f, 1.0f));
967 vertices.push_back(Vec4(-q, -p, 0.9f, 1.0f)); // line 3
968 }
969 else if (lineOrientation == LINE_ORIENTATION_DIAGONAL)
970 {
971 // Diagonal lines just outside the clip volume.
972 const float p = 2.0f + delta;
973
974 vertices.push_back(Vec4(-p, 0.0f, 0.1f, 1.0f));
975 vertices.push_back(Vec4(0.0f, -p, 0.9f, 1.0f)); // line 0
976 vertices.push_back(Vec4(0.0f, -p, 0.1f, 1.0f));
977 vertices.push_back(Vec4(p, 0.0f, 0.9f, 1.0f)); // line 1
978 vertices.push_back(Vec4(p, 0.0f, 0.1f, 1.0f));
979 vertices.push_back(Vec4(0.0f, p, 0.9f, 1.0f)); // line 2
980 vertices.push_back(Vec4(0.0f, p, 0.1f, 1.0f));
981 vertices.push_back(Vec4(-p, 0.0f, 0.9f, 1.0f)); // line 3
982 }
983 else
984 DE_ASSERT(0);
985
986 const VkPhysicalDeviceLimits limits =
987 getPhysicalDeviceProperties(context.getInstanceInterface(), context.getPhysicalDevice()).limits;
988
989 const float lineWidth = std::min(static_cast<float>(RENDER_SIZE), limits.lineWidthRange[1]);
990 const bool strictLines = limits.strictLines;
991 tcu::TestLog &log = context.getTestContext().getLog();
992
993 log << tcu::TestLog::Message
994 << "Drawing several wide lines just outside the clip volume. Expecting an empty image or all lines rendered."
995 << tcu::TestLog::EndMessage << tcu::TestLog::Message << "Line width is " << lineWidth << "."
996 << tcu::TestLog::EndMessage << tcu::TestLog::Message << "strictLines is "
997 << (strictLines ? "VK_TRUE." : "VK_FALSE.") << tcu::TestLog::EndMessage;
998
999 FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
1000 PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
1001 DrawCallData drawCallData(VK_PRIMITIVE_TOPOLOGY_LINE_LIST, vertices);
1002 VulkanProgram vulkanProgram(shaders);
1003
1004 VulkanDrawContext drawContext(context, framebufferState);
1005 drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
1006 drawContext.draw();
1007
1008 // Popful case: All pixels must be black -- nothing is drawn.
1009 if (countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4()) == NUM_RENDER_PIXELS)
1010 {
1011 return tcu::TestStatus::pass("OK");
1012 }
1013 // Pop-free case: All lines must be rendered.
1014 else
1015 {
1016 const float halfWidth = lineWidth / float(RENDER_SIZE);
1017 std::vector<Vec4> refVertices;
1018
1019 // Create reference primitives
1020 for (uint32_t lineNdx = 0u; lineNdx < (uint32_t)vertices.size() / 2u; lineNdx++)
1021 {
1022 const uint32_t vertexNdx0 = 2 * lineNdx;
1023 const uint32_t vertexNdx1 = 2 * lineNdx + 1;
1024
1025 const bool xMajorAxis = deFloatAbs(vertices[vertexNdx1].x() - vertices[vertexNdx0].x()) >=
1026 deFloatAbs(vertices[vertexNdx1].y() - vertices[vertexNdx0].y());
1027 const tcu::Vec2 lineDir = tcu::normalize(tcu::Vec2(vertices[vertexNdx1].x() - vertices[vertexNdx0].x(),
1028 vertices[vertexNdx1].y() - vertices[vertexNdx0].y()));
1029 const tcu::Vec4 lineNormalDir =
1030 (strictLines) ? tcu::Vec4(lineDir.y(), -lineDir.x(), 0.0f,
1031 0.0f) // Line caps are perpendicular to the direction of the line segment.
1032 :
1033 (xMajorAxis) ? tcu::Vec4(0.0f, 1.0f, 0.0f, 0.0f) :
1034 tcu::Vec4(1.0f, 0.0f, 0.0f, 0.0f); // Line caps are aligned to the minor axis
1035
1036 const tcu::Vec4 wideLineVertices[] = {tcu::Vec4(vertices[vertexNdx0] + lineNormalDir * halfWidth),
1037 tcu::Vec4(vertices[vertexNdx0] - lineNormalDir * halfWidth),
1038 tcu::Vec4(vertices[vertexNdx1] - lineNormalDir * halfWidth),
1039 tcu::Vec4(vertices[vertexNdx1] + lineNormalDir * halfWidth)};
1040
1041 // 1st triangle
1042 refVertices.push_back(wideLineVertices[0]);
1043 refVertices.push_back(wideLineVertices[1]);
1044 refVertices.push_back(wideLineVertices[2]);
1045
1046 // 2nd triangle
1047 refVertices.push_back(wideLineVertices[0]);
1048 refVertices.push_back(wideLineVertices[2]);
1049 refVertices.push_back(wideLineVertices[3]);
1050 }
1051
1052 std::shared_ptr<rr::VertexShader> vertexShader = std::make_shared<WideLineVertexShader>();
1053 std::shared_ptr<rr::FragmentShader> fragmentShader = std::make_shared<WideLineFragmentShader>();
1054
1055 // Draw wide line was two triangles
1056 DrawCallData refCallData(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, refVertices);
1057
1058 ReferenceDrawContext refDrawContext(framebufferState);
1059 refDrawContext.registerDrawObject(pipelineState, vertexShader, fragmentShader, refCallData);
1060 refDrawContext.draw();
1061
1062 if (tcu::intThresholdCompare(log, "Compare", "Result comparsion", refDrawContext.getColorPixels(),
1063 drawContext.getColorPixels(), tcu::UVec4(1), tcu::COMPARE_LOG_ON_ERROR))
1064 return tcu::TestStatus::pass("OK");
1065 }
1066
1067 return tcu::TestStatus::fail("Rendered image(s) are incorrect");
1068 }
1069
1070 } // namespace ClipVolume
1071
1072 namespace ClipDistance
1073 {
1074
1075 struct CaseDefinition
1076 {
1077 const VkPrimitiveTopology topology;
1078 const bool dynamicIndexing;
1079 const bool enableTessellation;
1080 const bool enableGeometry;
1081 const int numClipDistances;
1082 const int numCullDistances;
1083 const bool readInFragmentShader;
1084
CaseDefinitionvkt::clipping::__anon0be3e7980111::ClipDistance::CaseDefinition1085 CaseDefinition(const VkPrimitiveTopology topology_, const int numClipDistances_, const int numCullDistances_,
1086 const bool enableTessellation_, const bool enableGeometry_, const bool dynamicIndexing_,
1087 const bool readInFragmentShader_)
1088 : topology(topology_)
1089 , dynamicIndexing(dynamicIndexing_)
1090 , enableTessellation(enableTessellation_)
1091 , enableGeometry(enableGeometry_)
1092 , numClipDistances(numClipDistances_)
1093 , numCullDistances(numCullDistances_)
1094 , readInFragmentShader(readInFragmentShader_)
1095 {
1096 }
1097 };
1098
initPrograms(SourceCollections & programCollection,const CaseDefinition caseDef)1099 void initPrograms(SourceCollections &programCollection, const CaseDefinition caseDef)
1100 {
1101 DE_ASSERT(caseDef.numClipDistances + caseDef.numCullDistances <= MAX_COMBINED_CLIP_AND_CULL_DISTANCES);
1102
1103 std::string perVertexBlock;
1104 {
1105 std::ostringstream str;
1106 str << "gl_PerVertex {\n"
1107 << " vec4 gl_Position;\n";
1108 if (caseDef.numClipDistances > 0)
1109 str << " float gl_ClipDistance[" << caseDef.numClipDistances << "];\n";
1110 if (caseDef.numCullDistances > 0)
1111 str << " float gl_CullDistance[" << caseDef.numCullDistances << "];\n";
1112 str << "}";
1113 perVertexBlock = str.str();
1114 }
1115
1116 // Vertex shader
1117 {
1118 std::ostringstream src;
1119 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1120 << "\n"
1121 << "layout(location = 0) in vec4 v_position;\n"
1122 << "layout(location = 0) out vec4 out_color;\n"
1123 << "\n"
1124 << "out " << perVertexBlock << ";\n"
1125 << "\n"
1126 << "void main (void)\n"
1127 << "{\n"
1128 << " gl_Position = v_position;\n"
1129 << " out_color = vec4(1.0, 0.5 * (v_position.x + 1.0), 0.0, 1.0);\n"
1130 << "\n"
1131 << " const int barNdx = gl_VertexIndex / 6;\n";
1132 if (caseDef.dynamicIndexing)
1133 {
1134 if (caseDef.numClipDistances > 0)
1135 src << " for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
1136 << " gl_ClipDistance[i] = (barNdx == i ? v_position.y : 0.0);\n";
1137 if (caseDef.numCullDistances > 0)
1138 {
1139 src << " for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n";
1140 if (!caseDef.readInFragmentShader)
1141 {
1142 src << " gl_CullDistance[i] = (gl_Position.x >= 0.75f) ? -0.5f : 0.5f;\n";
1143 }
1144 else
1145 {
1146 if (caseDef.enableTessellation || caseDef.enableGeometry)
1147 src << " gl_CullDistance[i] = 0.1f;\n";
1148 else
1149 src << " gl_CullDistance[i] = (gl_Position.y < 0) ? -0.5f : 0.5f;\n";
1150 }
1151 }
1152 }
1153 else
1154 {
1155 for (int i = 0; i < caseDef.numClipDistances; ++i)
1156 src << " gl_ClipDistance[" << i << "] = (barNdx == " << i << " ? v_position.y : 0.0);\n";
1157
1158 for (int i = 0; i < caseDef.numCullDistances; ++i)
1159 {
1160 if (!caseDef.readInFragmentShader)
1161 {
1162 src << " gl_CullDistance[" << i << "] = (gl_Position.x >= 0.75f) ? -0.5f : 0.5f;\n";
1163 }
1164 else
1165 {
1166 if (caseDef.enableTessellation || caseDef.enableGeometry)
1167 src << " gl_CullDistance[" << i << "] = 0.1f;\n";
1168 else
1169 src << " gl_CullDistance[" << i << "] = (gl_Position.y < 0) ? -0.5f : 0.5f;\n";
1170 }
1171 }
1172 }
1173 src << "}\n";
1174
1175 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
1176 }
1177
1178 if (caseDef.enableTessellation)
1179 {
1180 std::ostringstream src;
1181 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1182 << "\n"
1183 << "layout(vertices = " << NUM_PATCH_CONTROL_POINTS << ") out;\n"
1184 << "\n"
1185 << "layout(location = 0) in vec4 in_color[];\n"
1186 << "layout(location = 0) out vec4 out_color[];\n"
1187 << "\n"
1188 << "in " << perVertexBlock << " gl_in[gl_MaxPatchVertices];\n"
1189 << "\n"
1190 << "out " << perVertexBlock << " gl_out[];\n"
1191 << "\n"
1192 << "void main (void)\n"
1193 << "{\n"
1194 << " gl_TessLevelInner[0] = 1.0;\n"
1195 << " gl_TessLevelInner[1] = 1.0;\n"
1196 << "\n"
1197 << " gl_TessLevelOuter[0] = 1.0;\n"
1198 << " gl_TessLevelOuter[1] = 1.0;\n"
1199 << " gl_TessLevelOuter[2] = 1.0;\n"
1200 << " gl_TessLevelOuter[3] = 1.0;\n"
1201 << "\n"
1202 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
1203 << " out_color[gl_InvocationID] = in_color[gl_InvocationID];\n"
1204 << "\n";
1205 if (caseDef.dynamicIndexing)
1206 {
1207 if (caseDef.numClipDistances > 0)
1208 src << " for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
1209 << " gl_out[gl_InvocationID].gl_ClipDistance[i] = "
1210 "gl_in[gl_InvocationID].gl_ClipDistance[i];\n";
1211 if (caseDef.numCullDistances > 0)
1212 {
1213 src << " for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n";
1214 src << " {\n";
1215 if (!caseDef.readInFragmentShader)
1216 {
1217 src << " gl_out[gl_InvocationID].gl_CullDistance[i] = (gl_in[gl_InvocationID].gl_Position.x >= "
1218 "0.75f) ? -0.5f : 0.5f;\n";
1219 }
1220 else
1221 {
1222 src << " gl_out[gl_InvocationID].gl_CullDistance[i] = "
1223 "(gl_in[gl_InvocationID].gl_CullDistance[i] == 0.1f) ? ";
1224 if (caseDef.enableGeometry)
1225 src << "0.3f";
1226 else
1227 src << "((gl_in[gl_InvocationID].gl_Position.y < 0) ? -0.5f : 0.5f)";
1228 src << " : 0.2f;\n";
1229 }
1230 src << " }\n";
1231 }
1232 }
1233 else
1234 {
1235 for (int i = 0; i < caseDef.numClipDistances; ++i)
1236 src << " gl_out[gl_InvocationID].gl_ClipDistance[" << i
1237 << "] = gl_in[gl_InvocationID].gl_ClipDistance[" << i << "];\n";
1238 for (int i = 0; i < caseDef.numCullDistances; ++i)
1239 {
1240 if (!caseDef.readInFragmentShader)
1241 {
1242 src << " gl_out[gl_InvocationID].gl_CullDistance[" << i
1243 << "] = (gl_in[gl_InvocationID].gl_Position.x >= 0.75f) ? -0.5f : 0.5f;\n";
1244 }
1245 else
1246 {
1247 src << " gl_out[gl_InvocationID].gl_CullDistance[" << i
1248 << "] = (gl_in[gl_InvocationID].gl_CullDistance[" << i << "] == 0.1f) ? ";
1249 if (caseDef.enableGeometry)
1250 src << "0.3f";
1251 else
1252 src << "((gl_in[gl_InvocationID].gl_Position.y < 0) ? -0.5f : 0.5f)";
1253 src << " : 0.2f;\n";
1254 }
1255 }
1256 }
1257 src << "}\n";
1258
1259 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
1260 }
1261
1262 if (caseDef.enableTessellation)
1263 {
1264 DE_ASSERT(NUM_PATCH_CONTROL_POINTS == 3); // assumed in shader code
1265
1266 std::ostringstream src;
1267 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1268 << "\n"
1269 << "layout(triangles, equal_spacing, ccw) in;\n"
1270 << "\n"
1271 << "layout(location = 0) in vec4 in_color[];\n"
1272 << "layout(location = 0) out vec4 out_color;\n"
1273 << "\n"
1274 << "in " << perVertexBlock << " gl_in[gl_MaxPatchVertices];\n"
1275 << "\n"
1276 << "out " << perVertexBlock << ";\n"
1277 << "\n"
1278 << "void main (void)\n"
1279 << "{\n"
1280 << " vec3 px = gl_TessCoord.x * gl_in[0].gl_Position.xyz;\n"
1281 << " vec3 py = gl_TessCoord.y * gl_in[1].gl_Position.xyz;\n"
1282 << " vec3 pz = gl_TessCoord.z * gl_in[2].gl_Position.xyz;\n"
1283 << " gl_Position = vec4(px + py + pz, 1.0);\n"
1284 << " out_color = (in_color[0] + in_color[1] + in_color[2]) / 3.0;\n"
1285 << "\n";
1286 if (caseDef.dynamicIndexing)
1287 {
1288 if (caseDef.numClipDistances > 0)
1289 src << " for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
1290 << " gl_ClipDistance[i] = gl_TessCoord.x * gl_in[0].gl_ClipDistance[i]\n"
1291 << " + gl_TessCoord.y * gl_in[1].gl_ClipDistance[i]\n"
1292 << " + gl_TessCoord.z * gl_in[2].gl_ClipDistance[i];\n";
1293 if (caseDef.numCullDistances > 0)
1294 src << " for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n"
1295 << " gl_CullDistance[i] = gl_TessCoord.x * gl_in[0].gl_CullDistance[i]\n"
1296 << " + gl_TessCoord.y * gl_in[1].gl_CullDistance[i]\n"
1297 << " + gl_TessCoord.z * gl_in[2].gl_CullDistance[i];\n";
1298 }
1299 else
1300 {
1301 for (int i = 0; i < caseDef.numClipDistances; ++i)
1302 src << " gl_ClipDistance[" << i << "] = gl_TessCoord.x * gl_in[0].gl_ClipDistance[" << i << "]\n"
1303 << " + gl_TessCoord.y * gl_in[1].gl_ClipDistance[" << i << "]\n"
1304 << " + gl_TessCoord.z * gl_in[2].gl_ClipDistance[" << i << "];\n";
1305 for (int i = 0; i < caseDef.numCullDistances; ++i)
1306 src << " gl_CullDistance[" << i << "] = gl_TessCoord.x * gl_in[0].gl_CullDistance[" << i << "]\n"
1307 << " + gl_TessCoord.y * gl_in[1].gl_CullDistance[" << i << "]\n"
1308 << " + gl_TessCoord.z * gl_in[2].gl_CullDistance[" << i << "];\n";
1309 }
1310 src << "}\n";
1311
1312 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
1313 }
1314
1315 if (caseDef.enableGeometry)
1316 {
1317 std::ostringstream src;
1318 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1319 << "\n"
1320 << "layout(triangles) in;\n"
1321 << "layout(triangle_strip, max_vertices = 3) out;\n"
1322 << "\n"
1323 << "layout(location = 0) in vec4 in_color[];\n"
1324 << "layout(location = 0) out vec4 out_color;\n"
1325 << "\n"
1326 << "in " << perVertexBlock << " gl_in[];\n"
1327 << "\n"
1328 << "out " << perVertexBlock << ";\n"
1329 << "\n"
1330 << "void main (void)\n"
1331 << "{\n";
1332 for (int vertNdx = 0; vertNdx < 3; ++vertNdx)
1333 {
1334 if (vertNdx > 0)
1335 src << "\n";
1336 src << " gl_Position = gl_in[" << vertNdx << "].gl_Position;\n"
1337 << " out_color = in_color[" << vertNdx << "];\n";
1338 if (caseDef.dynamicIndexing)
1339 {
1340 if (caseDef.numClipDistances > 0)
1341 src << " for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
1342 << " gl_ClipDistance[i] = gl_in[" << vertNdx << "].gl_ClipDistance[i];\n";
1343 if (caseDef.numCullDistances > 0)
1344 {
1345 src << " for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n";
1346 src << " {\n";
1347 if (!caseDef.readInFragmentShader)
1348 {
1349 src << " gl_CullDistance[i] = (gl_in[" << vertNdx
1350 << "].gl_Position.x >= 0.75f) ? -0.5f : 0.5f;\n";
1351 }
1352 else
1353 {
1354 src << " gl_CullDistance[i] = (gl_in[" << vertNdx << "].gl_CullDistance[i] == ";
1355 if (caseDef.enableTessellation)
1356 src << "0.3f";
1357 else
1358 src << "0.1f";
1359 src << ") ? ((gl_in[" << vertNdx << "].gl_Position.y < 0) ? -0.5f : 0.5f) : 0.4f;\n";
1360 }
1361 src << " }\n";
1362 }
1363 }
1364 else
1365 {
1366 for (int i = 0; i < caseDef.numClipDistances; ++i)
1367 src << " gl_ClipDistance[" << i << "] = gl_in[" << vertNdx << "].gl_ClipDistance[" << i
1368 << "];\n";
1369
1370 for (int i = 0; i < caseDef.numCullDistances; ++i)
1371 {
1372 if (!caseDef.readInFragmentShader)
1373 {
1374 src << " gl_CullDistance[" << i << "] = (gl_in[" << vertNdx
1375 << "].gl_Position.x >= 0.75f) ? -0.5f : 0.5f;\n";
1376 }
1377 else
1378 {
1379 src << " gl_CullDistance[" << i << "] = (gl_in[" << vertNdx << "].gl_CullDistance[" << i
1380 << "] == ";
1381 if (caseDef.enableTessellation)
1382 src << "0.3f";
1383 else
1384 src << "0.1f";
1385 src << ") ? ((gl_in[" << vertNdx << "].gl_Position.y < 0) ? -0.5f : 0.5f) : 0.4f;\n";
1386 }
1387 }
1388 }
1389 src << " EmitVertex();\n";
1390 }
1391 src << "}\n";
1392
1393 programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
1394 }
1395
1396 // Fragment shader
1397 {
1398 std::ostringstream src;
1399 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1400 << "\n"
1401 << "layout(location = 0) in flat vec4 in_color;\n"
1402 << "layout(location = 0) out vec4 o_color;\n";
1403 if (caseDef.readInFragmentShader)
1404 {
1405 if (caseDef.numClipDistances > 0)
1406 src << "in float gl_ClipDistance[" << caseDef.numClipDistances << "];\n";
1407 if (caseDef.numCullDistances > 0)
1408 src << "in float gl_CullDistance[" << caseDef.numCullDistances << "];\n";
1409 }
1410 src << "\n"
1411 << "void main (void)\n"
1412 << "{\n";
1413
1414 if (caseDef.readInFragmentShader)
1415 {
1416 src << " o_color = vec4(in_color.r, "
1417 << (caseDef.numClipDistances > 0 ?
1418 std::string("gl_ClipDistance[") + de::toString(caseDef.numClipDistances / 2) + "], " :
1419 "0.0, ")
1420 << (caseDef.numCullDistances > 0 ?
1421 std::string("gl_CullDistance[") + de::toString(caseDef.numCullDistances / 2) + "], " :
1422 "0.0, ")
1423 << " 1.0);\n";
1424 }
1425 else
1426 {
1427 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
1428 }
1429
1430 src << "}\n";
1431
1432 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
1433 }
1434 }
1435
testClipDistance(Context & context,const CaseDefinition caseDef)1436 tcu::TestStatus testClipDistance(Context &context, const CaseDefinition caseDef)
1437 {
1438 // Check test requirements
1439 {
1440 const InstanceInterface &vki = context.getInstanceInterface();
1441 const VkPhysicalDevice physDevice = context.getPhysicalDevice();
1442 const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(vki, physDevice).limits;
1443
1444 FeatureFlags requirements = (FeatureFlags)0;
1445
1446 if (caseDef.numClipDistances > 0)
1447 requirements |= FEATURE_SHADER_CLIP_DISTANCE;
1448 if (caseDef.numCullDistances > 0)
1449 requirements |= FEATURE_SHADER_CULL_DISTANCE;
1450 if (caseDef.enableTessellation)
1451 requirements |= FEATURE_TESSELLATION_SHADER;
1452 if (caseDef.enableGeometry)
1453 requirements |= FEATURE_GEOMETRY_SHADER;
1454
1455 requireFeatures(vki, physDevice, requirements);
1456
1457 // Check limits for supported features
1458
1459 if (caseDef.numClipDistances > 0 && limits.maxClipDistances < MAX_CLIP_DISTANCES)
1460 return tcu::TestStatus::fail("maxClipDistances smaller than the minimum required by the spec");
1461 if (caseDef.numCullDistances > 0 && limits.maxCullDistances < MAX_CULL_DISTANCES)
1462 return tcu::TestStatus::fail("maxCullDistances smaller than the minimum required by the spec");
1463 if (caseDef.numCullDistances > 0 &&
1464 limits.maxCombinedClipAndCullDistances < MAX_COMBINED_CLIP_AND_CULL_DISTANCES)
1465 return tcu::TestStatus::fail(
1466 "maxCombinedClipAndCullDistances smaller than the minimum required by the spec");
1467 }
1468
1469 std::vector<VulkanShader> shaders;
1470 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
1471 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
1472 if (caseDef.enableTessellation)
1473 {
1474 shaders.push_back(
1475 VulkanShader(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc")));
1476 shaders.push_back(
1477 VulkanShader(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese")));
1478 }
1479 if (caseDef.enableGeometry)
1480 shaders.push_back(VulkanShader(VK_SHADER_STAGE_GEOMETRY_BIT, context.getBinaryCollection().get("geom")));
1481
1482 const int numBars = MAX_COMBINED_CLIP_AND_CULL_DISTANCES;
1483
1484 std::vector<Vec4> vertices;
1485 {
1486 const float dx = 2.0f / numBars;
1487 for (int i = 0; i < numBars; ++i)
1488 {
1489 const float x = -1.0f + dx * static_cast<float>(i);
1490
1491 vertices.push_back(Vec4(x, -1.0f, 0.0f, 1.0f));
1492 vertices.push_back(Vec4(x, 1.0f, 0.0f, 1.0f));
1493 vertices.push_back(Vec4(x + dx, -1.0f, 0.0f, 1.0f));
1494
1495 vertices.push_back(Vec4(x, 1.0f, 0.0f, 1.0f));
1496 vertices.push_back(Vec4(x + dx, 1.0f, 0.0f, 1.0f));
1497 vertices.push_back(Vec4(x + dx, -1.0f, 0.0f, 1.0f));
1498 }
1499 }
1500
1501 tcu::TestLog &log = context.getTestContext().getLog();
1502
1503 log << tcu::TestLog::Message << "Drawing " << numBars << " colored bars, clipping the first "
1504 << caseDef.numClipDistances << tcu::TestLog::EndMessage << tcu::TestLog::Message << "Using "
1505 << caseDef.numClipDistances << " ClipDistance(s) and " << caseDef.numCullDistances << " CullDistance(s)"
1506 << tcu::TestLog::EndMessage << tcu::TestLog::Message << "Expecting upper half of the clipped bars to be black."
1507 << tcu::TestLog::EndMessage;
1508
1509 FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
1510 PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
1511 if (caseDef.enableTessellation)
1512 pipelineState.numPatchControlPoints = NUM_PATCH_CONTROL_POINTS;
1513 DrawCallData drawCallData(caseDef.topology, vertices);
1514 VulkanProgram vulkanProgram(shaders);
1515
1516 VulkanDrawContext drawContext(context, framebufferState);
1517 drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
1518 drawContext.draw();
1519
1520 // Count black pixels in the whole image.
1521 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
1522 const IVec2 clipRegion = IVec2(caseDef.numClipDistances * RENDER_SIZE / numBars, RENDER_SIZE / 2);
1523 // Cull is set to > 0.75 in the shader if caseDef.readInFragmentShader is false
1524 const int barsCulled = (int)deFloor((0.25f) / (1.0f / numBars));
1525 const IVec2 cullRegion =
1526 (caseDef.readInFragmentShader || caseDef.numCullDistances == 0) ? IVec2(0, 0) : IVec2(barsCulled, RENDER_SIZE);
1527 const int expectedClippedPixels = clipRegion.x() * clipRegion.y() + cullRegion.x() * cullRegion.y();
1528 // Make sure the bottom half has no black pixels (possible if image became corrupted).
1529 const int guardPixels = countPixels(drawContext.getColorPixels(), IVec2(0, RENDER_SIZE / 2), clipRegion,
1530 Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
1531 const bool fragColorsOk = caseDef.readInFragmentShader ?
1532 checkFragColors(drawContext.getColorPixels(), clipRegion,
1533 caseDef.numClipDistances / 2, caseDef.numCullDistances > 0) :
1534 true;
1535
1536 return (numBlackPixels == expectedClippedPixels && guardPixels == 0 && fragColorsOk ?
1537 tcu::TestStatus::pass("OK") :
1538 tcu::TestStatus::fail("Rendered image(s) are incorrect"));
1539 }
1540
1541 } // namespace ClipDistance
1542
1543 namespace ClipDistanceComplementarity
1544 {
1545
initPrograms(SourceCollections & programCollection,const int numClipDistances)1546 void initPrograms(SourceCollections &programCollection, const int numClipDistances)
1547 {
1548 // Vertex shader
1549 {
1550 DE_ASSERT(numClipDistances > 0);
1551 const int clipDistanceLastNdx = numClipDistances - 1;
1552
1553 std::ostringstream src;
1554 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1555 << "\n"
1556 << "layout(location = 0) in vec4 v_position; // we are passing ClipDistance in w component\n"
1557 << "\n"
1558 << "out gl_PerVertex {\n"
1559 << " vec4 gl_Position;\n"
1560 << " float gl_ClipDistance[" << numClipDistances << "];\n"
1561 << "};\n"
1562 << "\n"
1563 << "void main (void)\n"
1564 << "{\n"
1565 << " gl_Position = vec4(v_position.xyz, 1.0);\n";
1566 for (int i = 0; i < clipDistanceLastNdx; ++i)
1567 src << " gl_ClipDistance[" << i << "] = 0.0;\n";
1568 src << " gl_ClipDistance[" << clipDistanceLastNdx << "] = v_position.w;\n"
1569 << "}\n";
1570
1571 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
1572 }
1573
1574 // Fragment shader
1575 {
1576 std::ostringstream src;
1577 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1578 << "\n"
1579 << "layout(location = 0) out vec4 o_color;\n"
1580 << "\n"
1581 << "void main (void)\n"
1582 << "{\n"
1583 << " o_color = vec4(1.0, 1.0, 1.0, 0.5);\n"
1584 << "}\n";
1585
1586 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
1587 }
1588 }
1589
testComplementarity(Context & context,const int numClipDistances)1590 tcu::TestStatus testComplementarity(Context &context, const int numClipDistances)
1591 {
1592 // Check test requirements
1593 {
1594 const InstanceInterface &vki = context.getInstanceInterface();
1595 const VkPhysicalDevice physDevice = context.getPhysicalDevice();
1596
1597 requireFeatures(vki, physDevice, FEATURE_SHADER_CLIP_DISTANCE);
1598 }
1599
1600 std::vector<VulkanShader> shaders;
1601 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
1602 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
1603
1604 std::vector<Vec4> vertices;
1605 {
1606 de::Random rnd(1234);
1607 const int numSections = 16;
1608 const int numVerticesPerSection =
1609 4; // logical verticies, due to triangle list topology we actually use 6 per section
1610
1611 DE_ASSERT(RENDER_SIZE_LARGE % numSections == 0);
1612
1613 std::vector<float> clipDistances(numVerticesPerSection * numSections);
1614 for (int i = 0; i < static_cast<int>(clipDistances.size()); ++i)
1615 clipDistances[i] = rnd.getFloat(-1.0f, 1.0f);
1616
1617 // Two sets of identical primitives, but with a different ClipDistance sign.
1618 for (int setNdx = 0; setNdx < 2; ++setNdx)
1619 {
1620 const float sign = (setNdx == 0 ? 1.0f : -1.0f);
1621 const float dx = 2.0f / static_cast<float>(numSections);
1622
1623 for (int i = 0; i < numSections; ++i)
1624 {
1625 const int ndxBase = numVerticesPerSection * i;
1626 const float x = -1.0f + dx * static_cast<float>(i);
1627 const Vec4 p0 = Vec4(x, -1.0f, 0.0f, sign * clipDistances[ndxBase + 0]);
1628 const Vec4 p1 = Vec4(x, 1.0f, 0.0f, sign * clipDistances[ndxBase + 1]);
1629 const Vec4 p2 = Vec4(x + dx, 1.0f, 0.0f, sign * clipDistances[ndxBase + 2]);
1630 const Vec4 p3 = Vec4(x + dx, -1.0f, 0.0f, sign * clipDistances[ndxBase + 3]);
1631
1632 vertices.push_back(p0);
1633 vertices.push_back(p1);
1634 vertices.push_back(p2);
1635
1636 vertices.push_back(p2);
1637 vertices.push_back(p3);
1638 vertices.push_back(p0);
1639 }
1640 }
1641 }
1642
1643 tcu::TestLog &log = context.getTestContext().getLog();
1644
1645 log << tcu::TestLog::Message << "Draw two sets of primitives with blending, differing only with ClipDistance sign."
1646 << tcu::TestLog::EndMessage << tcu::TestLog::Message << "Using " << numClipDistances
1647 << " clipping plane(s), one of them possibly having negative values." << tcu::TestLog::EndMessage
1648 << tcu::TestLog::Message << "Expecting a uniform gray area, no missing (black) nor overlapped (white) pixels."
1649 << tcu::TestLog::EndMessage;
1650
1651 FrameBufferState framebufferState(RENDER_SIZE_LARGE, RENDER_SIZE_LARGE);
1652 PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
1653 pipelineState.blendEnable = true;
1654 DrawCallData drawCallData(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, vertices);
1655 VulkanProgram vulkanProgram(shaders);
1656
1657 VulkanDrawContext drawContext(context, framebufferState);
1658 drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
1659 drawContext.draw();
1660
1661 const int numGrayPixels =
1662 countPixels(drawContext.getColorPixels(), Vec4(0.5f, 0.5f, 0.5f, 1.0f), Vec4(0.02f, 0.02f, 0.02f, 0.0f));
1663 const int numExpectedPixels = RENDER_SIZE_LARGE * RENDER_SIZE_LARGE;
1664
1665 return (numGrayPixels == numExpectedPixels ? tcu::TestStatus::pass("OK") :
1666 tcu::TestStatus::fail("Rendered image(s) are incorrect"));
1667 }
1668
1669 } // namespace ClipDistanceComplementarity
1670
1671 namespace CullDistance
1672 {
checkSupport(Context & context)1673 void checkSupport(Context &context)
1674 {
1675 const InstanceInterface &vki = context.getInstanceInterface();
1676 const VkPhysicalDevice physDevice = context.getPhysicalDevice();
1677
1678 requireFeatures(vki, physDevice, FEATURE_SHADER_CULL_DISTANCE);
1679 }
1680
initPrograms(SourceCollections & programCollection)1681 void initPrograms(SourceCollections &programCollection)
1682 {
1683 // setup triangle with three per-vertex cull distance values:
1684 // v0: gl_CullDistance = { 0.0, 0.0, -1.0 };
1685 // v1: gl_CullDistance = { 0.0, -1.0, 0.0 };
1686 // v2: gl_CullDistance = { -1.0, 0.0, 0.0 };
1687 // each vertex has a negative cull distance value but the triangle must not
1688 // be culled because none of the three half-spaces is negative for all vertices
1689
1690 programCollection.glslSources.add("vert")
1691 << glu::VertexSource("#version 450\n"
1692 "layout(location = 0) in vec4 v_position;\n"
1693 "out gl_PerVertex {\n"
1694 " vec4 gl_Position;\n"
1695 " float gl_CullDistance[3];\n"
1696 "};\n"
1697 "void main (void)\n"
1698 "{\n"
1699 " gl_Position = v_position;\n"
1700 " gl_CullDistance[0] = 0.0 - float(gl_VertexIndex == 2);\n"
1701 " gl_CullDistance[1] = 0.0 - float(gl_VertexIndex == 1);\n"
1702 " gl_CullDistance[2] = 0.0 - float(gl_VertexIndex == 0);\n"
1703 "}\n");
1704
1705 programCollection.glslSources.add("frag") << glu::FragmentSource("#version 450\n"
1706 "layout(location = 0) out vec4 o_color;\n"
1707 "void main (void)\n"
1708 "{\n"
1709 " o_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
1710 "}\n");
1711 }
1712
testCullDistance(Context & context)1713 tcu::TestStatus testCullDistance(Context &context)
1714 {
1715 std::vector<VulkanShader> shaders{
1716 VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")),
1717 VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"))};
1718
1719 std::vector<Vec4> vertices{
1720 {-3.0f, 0.0f, 0.0f, 1.0f},
1721 {0.0f, 3.0f, 0.0f, 1.0f},
1722 {0.0f, -3.0f, 0.0f, 1.0f},
1723 };
1724
1725 VulkanProgram vulkanProgram(shaders);
1726 FrameBufferState framebufferState(RENDER_SIZE, RENDER_SIZE);
1727 PipelineState pipelineState(context.getDeviceProperties().limits.subPixelPrecisionBits);
1728 DrawCallData drawCallData(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, vertices);
1729 VulkanDrawContext drawContext(context, framebufferState);
1730
1731 drawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
1732 drawContext.draw();
1733
1734 const int numDrawnPixels =
1735 countPixels(drawContext.getColorPixels(), Vec4(1.0f, 0.0f, 0.0f, 1.0f), Vec4(0.02f, 0.02f, 0.02f, 0.0f));
1736 const int numExpectedPixels = RENDER_SIZE * RENDER_SIZE / 2;
1737
1738 // triangle should be drawn and half of framebuffer should be filled with red color
1739 if (numDrawnPixels == numExpectedPixels)
1740 return tcu::TestStatus::pass("OK");
1741
1742 return tcu::TestStatus::fail("Triangle was not drawn");
1743 }
1744
1745 } // namespace CullDistance
1746
checkTopologySupport(Context & context,const VkPrimitiveTopology topology)1747 void checkTopologySupport(Context &context, const VkPrimitiveTopology topology)
1748 {
1749 #ifndef CTS_USES_VULKANSC
1750 if (topology == VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN &&
1751 context.isDeviceFunctionalitySupported("VK_KHR_portability_subset") &&
1752 !context.getPortabilitySubsetFeatures().triangleFans)
1753 {
1754 TCU_THROW(NotSupportedError,
1755 "VK_KHR_portability_subset: Triangle fans are not supported by this implementation");
1756 }
1757 #else
1758 DE_UNREF(context);
1759 DE_UNREF(topology);
1760 #endif // CTS_USES_VULKANSC
1761 }
1762
addClippingTests(tcu::TestCaseGroup * clippingTestsGroup)1763 void addClippingTests(tcu::TestCaseGroup *clippingTestsGroup)
1764 {
1765 tcu::TestContext &testCtx = clippingTestsGroup->getTestContext();
1766
1767 // Clipping against the clip volume
1768 {
1769 using namespace ClipVolume;
1770
1771 static const VkPrimitiveTopology cases[] = {
1772 VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
1773 VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
1774 VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,
1775 VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
1776 VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY,
1777 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
1778 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY,
1779 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
1780 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY,
1781 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
1782 };
1783
1784 MovePtr<tcu::TestCaseGroup> clipVolumeGroup(new tcu::TestCaseGroup(testCtx, "clip_volume"));
1785
1786 // Fully inside the clip volume
1787 {
1788 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "inside"));
1789
1790 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1791 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1792 group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), checkTopologySupport, initPrograms,
1793 testPrimitivesInside, cases[caseNdx]);
1794
1795 clipVolumeGroup->addChild(group.release());
1796 }
1797
1798 // Fully outside the clip volume
1799 {
1800 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "outside"));
1801
1802 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1803 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1804 group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), checkTopologySupport, initPrograms,
1805 testPrimitivesOutside, cases[caseNdx]);
1806
1807 clipVolumeGroup->addChild(group.release());
1808 }
1809
1810 // Depth clamping
1811 {
1812 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "depth_clamp"));
1813
1814 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1815 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1816 group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), checkTopologySupport, initPrograms,
1817 testPrimitivesDepthClamp, cases[caseNdx]);
1818
1819 clipVolumeGroup->addChild(group.release());
1820 }
1821
1822 // Depth clipping
1823 {
1824 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "depth_clip"));
1825
1826 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1827 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1828 group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), checkTopologySupport, initPrograms,
1829 testPrimitivesDepthClip, cases[caseNdx]);
1830
1831 clipVolumeGroup->addChild(group.release());
1832 }
1833
1834 // Large points and wide lines
1835 {
1836 // \note For both points and lines, if an unsupported size/width is selected, the nearest supported size will be chosen.
1837 // We do have to check for feature support though.
1838
1839 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "clipped"));
1840
1841 addFunctionCaseWithPrograms(group.get(), "large_points", initProgramsPointSize, testLargePoints);
1842
1843 addFunctionCaseWithPrograms<LineOrientation>(group.get(), "wide_lines_axis_aligned", initPrograms,
1844 testWideLines, LINE_ORIENTATION_AXIS_ALIGNED);
1845 addFunctionCaseWithPrograms<LineOrientation>(group.get(), "wide_lines_diagonal", initPrograms,
1846 testWideLines, LINE_ORIENTATION_DIAGONAL);
1847
1848 clipVolumeGroup->addChild(group.release());
1849 }
1850
1851 clippingTestsGroup->addChild(clipVolumeGroup.release());
1852 }
1853
1854 // User-defined clip planes
1855 {
1856 MovePtr<tcu::TestCaseGroup> clipDistanceGroup(new tcu::TestCaseGroup(testCtx, "user_defined"));
1857
1858 // ClipDistance, CullDistance and maxCombinedClipAndCullDistances usage
1859 {
1860 using namespace ClipDistance;
1861
1862 static const struct
1863 {
1864 const char *const groupName;
1865 bool useCullDistance;
1866 } caseGroups[] = {
1867 {"clip_distance", false},
1868 {"clip_cull_distance", true},
1869 };
1870
1871 static const struct
1872 {
1873 const char *const name;
1874 bool readInFragmentShader;
1875 } fragmentShaderReads[] = {
1876
1877 {"", false}, {"_fragmentshader_read", true}};
1878
1879 const uint32_t flagTessellation = 1u << 0;
1880 const uint32_t flagGeometry = 1u << 1;
1881
1882 for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(caseGroups); ++groupNdx)
1883 for (int indexingMode = 0; indexingMode < 2; ++indexingMode)
1884 {
1885 const bool dynamicIndexing = (indexingMode == 1);
1886 const std::string mainGroupName =
1887 de::toString(caseGroups[groupNdx].groupName) + (dynamicIndexing ? "_dynamic_index" : "");
1888
1889 MovePtr<tcu::TestCaseGroup> mainGroup(new tcu::TestCaseGroup(testCtx, mainGroupName.c_str()));
1890
1891 for (uint32_t shaderMask = 0u; shaderMask <= (flagTessellation | flagGeometry); ++shaderMask)
1892 {
1893 const bool useTessellation = (shaderMask & flagTessellation) != 0;
1894 const bool useGeometry = (shaderMask & flagGeometry) != 0;
1895 const std::string shaderGroupName =
1896 std::string("vert") + (useTessellation ? "_tess" : "") + (useGeometry ? "_geom" : "");
1897
1898 MovePtr<tcu::TestCaseGroup> shaderGroup(
1899 new tcu::TestCaseGroup(testCtx, shaderGroupName.c_str()));
1900
1901 for (int numClipPlanes = 1; numClipPlanes <= MAX_CLIP_DISTANCES; ++numClipPlanes)
1902 for (int fragmentShaderReadNdx = 0;
1903 fragmentShaderReadNdx < DE_LENGTH_OF_ARRAY(fragmentShaderReads);
1904 ++fragmentShaderReadNdx)
1905 {
1906 const int numCullPlanes =
1907 (caseGroups[groupNdx].useCullDistance ?
1908 std::min(static_cast<int>(MAX_CULL_DISTANCES),
1909 MAX_COMBINED_CLIP_AND_CULL_DISTANCES - numClipPlanes) :
1910 0);
1911 const std::string caseName =
1912 de::toString(numClipPlanes) +
1913 (numCullPlanes > 0 ? "_" + de::toString(numCullPlanes) : "") +
1914 de::toString(fragmentShaderReads[fragmentShaderReadNdx].name);
1915 const VkPrimitiveTopology topology =
1916 (useTessellation ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST :
1917 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
1918
1919 addFunctionCaseWithPrograms<CaseDefinition>(
1920 shaderGroup.get(), caseName, initPrograms, testClipDistance,
1921 CaseDefinition(topology, numClipPlanes, numCullPlanes, useTessellation, useGeometry,
1922 dynamicIndexing,
1923 fragmentShaderReads[fragmentShaderReadNdx].readInFragmentShader));
1924 }
1925 mainGroup->addChild(shaderGroup.release());
1926 }
1927 clipDistanceGroup->addChild(mainGroup.release());
1928 }
1929 }
1930 clippingTestsGroup->addChild(clipDistanceGroup.release());
1931
1932 // Complementarity criterion (i.e. clipped and not clipped areas must add up to a complete primitive with no holes nor overlap)
1933 {
1934 using namespace ClipDistanceComplementarity;
1935
1936 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "complementarity"));
1937
1938 for (int numClipDistances = 1; numClipDistances <= MAX_CLIP_DISTANCES; ++numClipDistances)
1939 addFunctionCaseWithPrograms<int>(group.get(), de::toString(numClipDistances).c_str(), initPrograms,
1940 testComplementarity, numClipDistances);
1941
1942 clippingTestsGroup->addChild(group.release());
1943 }
1944
1945 {
1946 using namespace CullDistance;
1947
1948 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "misc"));
1949
1950 addFunctionCaseWithPrograms(group.get(), "negative_and_non_negative_cull_distance", checkSupport,
1951 initPrograms, testCullDistance);
1952
1953 clippingTestsGroup->addChild(group.release());
1954 }
1955 }
1956 }
1957
1958 } // namespace
1959
createTests(tcu::TestContext & testCtx,const std::string & name)1960 tcu::TestCaseGroup *createTests(tcu::TestContext &testCtx, const std::string &name)
1961 {
1962 return createTestGroup(testCtx, name.c_str(), addClippingTests);
1963 }
1964
1965 } // namespace clipping
1966 } // namespace vkt
1967