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