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