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 const float expectedCullDistance = 0.5f;
287 const float clipDistance = color.y();
288 const float cullDistance = color.z();
289
290 if (fabs(clipDistance - expectedClipDistance) > 0.01f)
291 return false;
292 if (hasCullDistance && fabs(cullDistance - expectedCullDistance) > 0.01f)
293 return false;
294 }
295
296 return true;
297 }
298
299 //! Clipping against the default clip volume.
300 namespace ClipVolume
301 {
302
303 //! Used by wide lines test.
304 enum LineOrientation
305 {
306 LINE_ORIENTATION_AXIS_ALIGNED,
307 LINE_ORIENTATION_DIAGONAL,
308 };
309
310 const VkPointClippingBehavior invalidClippingBehavior = VK_POINT_CLIPPING_BEHAVIOR_LAST;
311
getClippingBehavior(const InstanceInterface & vk,VkPhysicalDevice physicalDevice)312 VkPointClippingBehavior getClippingBehavior (const InstanceInterface& vk, VkPhysicalDevice physicalDevice)
313 {
314 VkPhysicalDevicePointClippingProperties behaviorProperties =
315 {
316 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES, // VkStructureType sType
317 DE_NULL, // void* pNext
318 invalidClippingBehavior // VkPointClippingBehavior pointClippingBehavior
319 };
320 VkPhysicalDeviceProperties2 properties2;
321
322 DE_ASSERT(getPointClippingBehaviorName(invalidClippingBehavior) == DE_NULL);
323
324 deMemset(&properties2, 0, sizeof(properties2));
325
326 properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
327 properties2.pNext = &behaviorProperties;
328
329 vk.getPhysicalDeviceProperties2(physicalDevice, &properties2);
330
331 return behaviorProperties.pointClippingBehavior;
332 }
333
addSimplePrograms(SourceCollections & programCollection,const float pointSize=0.0f)334 void addSimplePrograms (SourceCollections& programCollection, const float pointSize = 0.0f)
335 {
336 // Vertex shader
337 {
338 const bool usePointSize = pointSize > 0.0f;
339
340 std::ostringstream src;
341 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
342 << "\n"
343 << "layout(location = 0) in vec4 v_position;\n"
344 << "\n"
345 << "out gl_PerVertex {\n"
346 << " vec4 gl_Position;\n"
347 << (usePointSize ? " float gl_PointSize;\n" : "")
348 << "};\n"
349 << "\n"
350 << "void main (void)\n"
351 << "{\n"
352 << " gl_Position = v_position;\n"
353 << (usePointSize ? " gl_PointSize = " + de::floatToString(pointSize, 1) + ";\n" : "")
354 << "}\n";
355
356 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
357 }
358
359 // Fragment shader
360 {
361 std::ostringstream src;
362 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
363 << "\n"
364 << "layout(location = 0) out vec4 o_color;\n"
365 << "\n"
366 << "void main (void)\n"
367 << "{\n"
368 << " o_color = vec4(1.0, gl_FragCoord.z, 0.0, 1.0);\n"
369 << "}\n";
370
371 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
372 }
373 }
374
initPrograms(SourceCollections & programCollection,const VkPrimitiveTopology topology)375 void initPrograms (SourceCollections& programCollection, const VkPrimitiveTopology topology)
376 {
377 const float pointSize = (topology == VK_PRIMITIVE_TOPOLOGY_POINT_LIST ? 1.0f : 0.0f);
378 addSimplePrograms(programCollection, pointSize);
379 }
380
initPrograms(SourceCollections & programCollection,const LineOrientation lineOrientation)381 void initPrograms (SourceCollections& programCollection, const LineOrientation lineOrientation)
382 {
383 DE_UNREF(lineOrientation);
384 addSimplePrograms(programCollection);
385 }
386
initProgramsPointSize(SourceCollections & programCollection)387 void initProgramsPointSize (SourceCollections& programCollection)
388 {
389 addSimplePrograms(programCollection, 0.75f * static_cast<float>(RENDER_SIZE));
390 }
391
392 //! Primitives fully inside the clip volume.
testPrimitivesInside(Context & context,const VkPrimitiveTopology topology)393 tcu::TestStatus testPrimitivesInside (Context& context, const VkPrimitiveTopology topology)
394 {
395 int minExpectedBlackPixels = 0;
396
397 switch (topology)
398 {
399 case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
400 // We draw only 5 points.
401 minExpectedBlackPixels = NUM_RENDER_PIXELS - 5;
402 break;
403
404 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
405 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
406 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
407 // Fallthrough
408 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
409 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
410 // Allow for some error.
411 minExpectedBlackPixels = NUM_RENDER_PIXELS - 3 * RENDER_SIZE;
412 break;
413
414 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
415 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
416 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
417 // Fallthrough
418 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
419 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
420 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
421 // All render area should be covered.
422 minExpectedBlackPixels = 0;
423 break;
424
425 default:
426 DE_ASSERT(0);
427 break;
428 }
429
430 std::vector<VulkanShader> shaders;
431 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
432 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
433
434 tcu::TestLog& log = context.getTestContext().getLog();
435 int numPassed = 0;
436
437 static const struct
438 {
439 const char* const desc;
440 float zPos;
441 } cases[] =
442 {
443 { "Draw primitives at near clipping plane, z = 0.0", 0.0f, },
444 { "Draw primitives at z = 0.5", 0.5f, },
445 { "Draw primitives at far clipping plane, z = 1.0", 1.0f, },
446 };
447
448 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
449 {
450 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
451
452 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 0.0f);
453 DrawState drawState (topology, RENDER_SIZE, RENDER_SIZE, context.getDeviceProperties().limits.subPixelPrecisionBits);
454 DrawCallData drawCallData (vertices);
455 VulkanProgram vulkanProgram (shaders);
456
457 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram);
458 drawContext.draw();
459
460 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
461 if (numBlackPixels >= minExpectedBlackPixels)
462 ++numPassed;
463 }
464
465 return (numPassed == DE_LENGTH_OF_ARRAY(cases) ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
466 }
467
468 //! Primitives fully outside the clip volume.
testPrimitivesOutside(Context & context,const VkPrimitiveTopology topology)469 tcu::TestStatus testPrimitivesOutside (Context& context, const VkPrimitiveTopology topology)
470 {
471 switch (topology)
472 {
473 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
474 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
475 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
476 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
477 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
478 break;
479 default:
480 break;
481 }
482
483 std::vector<VulkanShader> shaders;
484 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
485 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
486
487 tcu::TestLog& log = context.getTestContext().getLog();
488 int numPassed = 0;
489
490 static const struct
491 {
492 const char* const desc;
493 float zPos;
494 } cases[] =
495 {
496 { "Draw primitives in front of the near clipping plane, z < 0.0", -0.5f, },
497 { "Draw primitives behind the far clipping plane, z > 1.0", 1.5f, },
498 };
499
500 log << tcu::TestLog::Message << "Drawing primitives outside the clip volume. Expecting an empty image." << tcu::TestLog::EndMessage;
501
502 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
503 {
504 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
505
506 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 0.0f);
507 DrawState drawState (topology, RENDER_SIZE, RENDER_SIZE, context.getDeviceProperties().limits.subPixelPrecisionBits);
508 DrawCallData drawCallData (vertices);
509 VulkanProgram vulkanProgram (shaders);
510
511 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram);
512 drawContext.draw();
513
514 // All pixels must be black -- nothing is drawn.
515 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
516 if (numBlackPixels == NUM_RENDER_PIXELS)
517 ++numPassed;
518 }
519
520 return (numPassed == DE_LENGTH_OF_ARRAY(cases) ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
521 }
522
523 //! Primitives partially outside the clip volume, but depth clamped
testPrimitivesDepthClamp(Context & context,const VkPrimitiveTopology topology)524 tcu::TestStatus testPrimitivesDepthClamp (Context& context, const VkPrimitiveTopology topology)
525 {
526 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_DEPTH_CLAMP);
527
528 std::vector<VulkanShader> shaders;
529 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
530 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
531
532 const int numCases = 4;
533 const IVec2 regionSize = IVec2(RENDER_SIZE/2, RENDER_SIZE); //! size of the clamped region
534 const int regionPixels = regionSize.x() * regionSize.y();
535 tcu::TestLog& log = context.getTestContext().getLog();
536 int numPassed = 0;
537
538 static const struct
539 {
540 const char* const desc;
541 float zPos;
542 bool depthClampEnable;
543 IVec2 regionOffset;
544 Vec4 color;
545 } cases[numCases] =
546 {
547 { "Draw primitives intersecting the near clipping plane, depth clamp disabled", -0.5f, false, IVec2(0, 0), Vec4(0.0f, 0.0f, 0.0f, 1.0f) },
548 { "Draw primitives intersecting the near clipping plane, depth clamp enabled", -0.5f, true, IVec2(0, 0), Vec4(1.0f, 0.0f, 0.0f, 1.0f) },
549 { "Draw primitives intersecting the far clipping plane, depth clamp disabled", 0.5f, false, IVec2(RENDER_SIZE/2, 0), Vec4(0.0f, 0.0f, 0.0f, 1.0f) },
550 { "Draw primitives intersecting the far clipping plane, depth clamp enabled", 0.5f, true, IVec2(RENDER_SIZE/2, 0), Vec4(1.0f, 1.0f, 0.0f, 1.0f) },
551 };
552
553 // Per case minimum number of colored pixels.
554 int caseMinPixels[numCases] = { 0, 0, 0, 0 };
555
556 switch (topology)
557 {
558 case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
559 caseMinPixels[0] = caseMinPixels[2] = regionPixels - 1;
560 caseMinPixels[1] = caseMinPixels[3] = 2;
561 break;
562
563 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
564 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
565 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
566 // Fallthrough
567 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
568 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
569 caseMinPixels[0] = regionPixels;
570 caseMinPixels[1] = RENDER_SIZE - 2;
571 caseMinPixels[2] = regionPixels;
572 caseMinPixels[3] = 2 * (RENDER_SIZE - 2);
573 break;
574
575 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
576 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
577 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_GEOMETRY_SHADER);
578 // Fallthrough
579 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
580 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
581 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
582 caseMinPixels[0] = caseMinPixels[1] = caseMinPixels[2] = caseMinPixels[3] = regionPixels;
583 break;
584
585 default:
586 DE_ASSERT(0);
587 break;
588 }
589
590 for (int caseNdx = 0; caseNdx < numCases; ++caseNdx)
591 {
592 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
593
594 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 1.0f);
595
596 DrawState drawState (topology, RENDER_SIZE, RENDER_SIZE, context.getDeviceProperties().limits.subPixelPrecisionBits);
597 DrawCallData drawCallData (vertices);
598 VulkanProgram vulkanProgram (shaders);
599 drawState.depthClampEnable = cases[caseNdx].depthClampEnable;
600
601 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram);
602 drawContext.draw();
603
604 const int numPixels = countPixels(drawContext.getColorPixels(), cases[caseNdx].regionOffset, regionSize, cases[caseNdx].color, Vec4());
605
606 if (numPixels >= caseMinPixels[caseNdx])
607 ++numPassed;
608 }
609
610 return (numPassed == numCases ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
611 }
612
613 //! Primitives partially outside the clip volume, but depth clipped with explicit depth clip control
testPrimitivesDepthClip(Context & context,const VkPrimitiveTopology topology)614 tcu::TestStatus testPrimitivesDepthClip (Context& context, const VkPrimitiveTopology topology)
615 {
616 if (!context.getDepthClipEnableFeaturesEXT().depthClipEnable)
617 throw tcu::NotSupportedError("VK_EXT_depth_clip_enable not supported");
618
619 std::vector<VulkanShader> shaders;
620 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
621 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
622
623 const int numCases = 4;
624 const IVec2 regionSize = IVec2(RENDER_SIZE/2, RENDER_SIZE); //! size of the clamped region
625 const int regionPixels = regionSize.x() * regionSize.y();
626 tcu::TestLog& log = context.getTestContext().getLog();
627 int numPassed = 0;
628
629 static const struct
630 {
631 const char* const desc;
632 float zPos;
633 bool depthClipEnable;
634 IVec2 regionOffset;
635 Vec4 color;
636 } cases[numCases] =
637 {
638 { "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) },
639 { "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) },
640 { "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) },
641 { "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) },
642 };
643
644 // Per case minimum number of colored pixels.
645 int caseMinPixels[numCases] = { 0, 0, 0, 0 };
646
647 switch (topology)
648 {
649 case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
650 caseMinPixels[0] = caseMinPixels[2] = regionPixels - 1;
651 caseMinPixels[1] = caseMinPixels[3] = 2;
652 break;
653
654 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
655 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
656 case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
657 case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
658 caseMinPixels[0] = regionPixels;
659 caseMinPixels[1] = RENDER_SIZE - 2;
660 caseMinPixels[2] = regionPixels;
661 caseMinPixels[3] = 2 * (RENDER_SIZE - 2);
662 break;
663
664 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
665 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
666 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
667 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
668 case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
669 caseMinPixels[0] = caseMinPixels[1] = caseMinPixels[2] = caseMinPixels[3] = regionPixels;
670 break;
671
672 default:
673 DE_ASSERT(0);
674 break;
675 }
676
677 // Test depth clip with depth clamp disabled.
678 numPassed = 0;
679 for (int caseNdx = 0; caseNdx < numCases; ++caseNdx)
680 {
681 log << tcu::TestLog::Message << cases[caseNdx].desc << tcu::TestLog::EndMessage;
682
683 const std::vector<Vec4> vertices = genVertices(topology, Vec4(0.0f, 0.0f, cases[caseNdx].zPos, 0.0f), 1.0f);
684
685 DrawState drawState (topology, RENDER_SIZE, RENDER_SIZE, context.getDeviceProperties().limits.subPixelPrecisionBits);
686 DrawCallData drawCallData (vertices);
687 VulkanProgram vulkanProgram (shaders);
688 drawState.depthClampEnable = false;
689 drawState.explicitDepthClipEnable = true;
690 drawState.depthClipEnable = cases[caseNdx].depthClipEnable;
691
692 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram);
693 drawContext.draw();
694
695 const int numPixels = countPixels(drawContext.getColorPixels(), cases[caseNdx].regionOffset, regionSize, cases[caseNdx].color, Vec4());
696
697 if (numPixels >= caseMinPixels[caseNdx])
698 ++numPassed;
699 }
700
701 if (numPassed < numCases)
702 return tcu::TestStatus::fail("Rendered image(s) are incorrect (depth clip with depth clamp disabled)");
703
704 // Test depth clip with depth clamp enabled.
705 numPassed = 0;
706 if (getPhysicalDeviceFeatures(context.getInstanceInterface(), context.getPhysicalDevice()).depthClamp)
707 {
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
714 DrawState drawState (topology, RENDER_SIZE, RENDER_SIZE, context.getDeviceProperties().limits.subPixelPrecisionBits);
715 DrawCallData drawCallData (vertices);
716 VulkanProgram vulkanProgram (shaders);
717 drawState.depthClampEnable = true;
718 drawState.explicitDepthClipEnable = true;
719 drawState.depthClipEnable = cases[caseNdx].depthClipEnable;
720
721 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram);
722 drawContext.draw();
723
724 const int numPixels = countPixels(drawContext.getColorPixels(), cases[caseNdx].regionOffset, regionSize, cases[caseNdx].color, Vec4());
725
726 if (numPixels >= caseMinPixels[caseNdx])
727 ++numPassed;
728 }
729
730 if (numPassed < numCases)
731 return tcu::TestStatus::fail("Rendered image(s) are incorrect (depth clip with depth clamp enabled)");
732 }
733
734 return tcu::TestStatus::pass("OK");
735 }
736
737 //! Large point clipping
738 //! Spec: If the primitive under consideration is a point, then clipping passes it unchanged if it lies within the clip volume;
739 //! otherwise, it is discarded.
testLargePoints(Context & context)740 tcu::TestStatus testLargePoints (Context& context)
741 {
742 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_LARGE_POINTS);
743
744 bool pointClippingOutside = true;
745
746 if (context.isDeviceFunctionalitySupported("VK_KHR_maintenance2"))
747 {
748 VkPointClippingBehavior clippingBehavior = getClippingBehavior(context.getInstanceInterface(), context.getPhysicalDevice());
749
750 switch (clippingBehavior)
751 {
752 case VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES: pointClippingOutside = true; break;
753 case VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY: pointClippingOutside = false; break;
754 case invalidClippingBehavior: TCU_FAIL("Clipping behavior read failure"); break;
755 default:
756 {
757 TCU_FAIL("Unexpected clipping behavior reported");
758 }
759 }
760 }
761
762 std::vector<VulkanShader> shaders;
763 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
764 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
765
766 std::vector<Vec4> vertices;
767 {
768 const float delta = 0.1f; // much smaller than the point size
769 const float p = 1.0f + delta;
770
771 vertices.push_back(Vec4( -p, -p, 0.1f, 1.0f));
772 vertices.push_back(Vec4( -p, p, 0.2f, 1.0f));
773 vertices.push_back(Vec4( p, p, 0.4f, 1.0f));
774 vertices.push_back(Vec4( p, -p, 0.6f, 1.0f));
775 vertices.push_back(Vec4(0.0f, -p, 0.8f, 1.0f));
776 vertices.push_back(Vec4( p, 0.0f, 0.7f, 1.0f));
777 vertices.push_back(Vec4(0.0f, p, 0.5f, 1.0f));
778 vertices.push_back(Vec4( -p, 0.0f, 0.3f, 1.0f));
779 }
780
781 tcu::TestLog& log = context.getTestContext().getLog();
782
783 log << tcu::TestLog::Message << "Drawing several large points just outside the clip volume. Expecting an empty image or all points rendered." << tcu::TestLog::EndMessage;
784
785 DrawState drawState (VK_PRIMITIVE_TOPOLOGY_POINT_LIST, RENDER_SIZE, RENDER_SIZE, context.getDeviceProperties().limits.subPixelPrecisionBits);
786 DrawCallData drawCallData (vertices);
787 VulkanProgram vulkanProgram (shaders);
788
789 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram);
790 drawContext.draw();
791
792 // Popful case: All pixels must be black -- nothing is drawn.
793 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
794 bool result = false;
795
796 // Pop-free case: All points must be rendered.
797 bool allPointsRendered = true;
798 for (std::vector<Vec4>::iterator i = vertices.begin(); i != vertices.end(); ++i)
799 {
800 if (countPixels(drawContext.getColorPixels(), Vec4(1.0f, i->z(), 0.0f, 1.0f), Vec4(0.01f)) == 0)
801 allPointsRendered = false;
802 }
803
804 if (pointClippingOutside)
805 {
806 result = (numBlackPixels == NUM_RENDER_PIXELS || allPointsRendered);
807 }
808 else
809 {
810 // Rendering pixels without clipping: all points should be drawn.
811 result = (allPointsRendered == true);
812 }
813
814 return (result ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
815 }
816
817 class WideLineVertexShader : public rr::VertexShader
818 {
819 public:
WideLineVertexShader(void)820 WideLineVertexShader (void)
821 : rr::VertexShader(1, 1)
822 {
823 m_inputs[0].type = rr::GENERICVECTYPE_FLOAT;
824 m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
825 }
826
shadeVertices(const rr::VertexAttrib * inputs,rr::VertexPacket * const * packets,const int numPackets) const827 void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const
828 {
829 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
830 {
831 const tcu::Vec4 position = rr::readVertexAttribFloat(inputs[0], packets[packetNdx]->instanceNdx, packets[packetNdx]->vertexNdx);
832
833 packets[packetNdx]->position = position;
834 packets[packetNdx]->outputs[0] = position;
835 }
836 }
837 };
838
839 class WideLineFragmentShader : public rr::FragmentShader
840 {
841 public:
WideLineFragmentShader(void)842 WideLineFragmentShader (void)
843 : rr::FragmentShader(1, 1)
844 {
845 m_inputs[0].type = rr::GENERICVECTYPE_FLOAT;
846 m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
847 }
848
shadeFragments(rr::FragmentPacket * packets,const int numPackets,const rr::FragmentShadingContext & context) const849 void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
850 {
851 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
852 {
853 for (int fragNdx = 0; fragNdx < rr::NUM_FRAGMENTS_PER_PACKET; ++fragNdx)
854 {
855 const float depth = rr::readVarying<float>(packets[packetNdx], context, 0, fragNdx).z();
856 rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, tcu::Vec4(1.0f, depth, 0.0f, 1.0f));
857 }
858 }
859 }
860 };
861 //! Wide line clipping
testWideLines(Context & context,const LineOrientation lineOrientation)862 tcu::TestStatus testWideLines (Context& context, const LineOrientation lineOrientation)
863 {
864 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_WIDE_LINES);
865
866 std::vector<VulkanShader> shaders;
867 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
868 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
869
870 const float delta = 0.1f; // much smaller than the line width
871
872 std::vector<Vec4> vertices;
873 if (lineOrientation == LINE_ORIENTATION_AXIS_ALIGNED)
874 {
875 // Axis-aligned lines just outside the clip volume.
876 const float p = 1.0f + delta;
877 const float q = 0.9f;
878
879 vertices.push_back(Vec4(-p, -q, 0.1f, 1.0f));
880 vertices.push_back(Vec4(-p, q, 0.9f, 1.0f)); // line 0
881 vertices.push_back(Vec4(-q, p, 0.1f, 1.0f));
882 vertices.push_back(Vec4( q, p, 0.9f, 1.0f)); // line 1
883 vertices.push_back(Vec4( p, q, 0.1f, 1.0f));
884 vertices.push_back(Vec4( p, -q, 0.9f, 1.0f)); // line 2
885 vertices.push_back(Vec4( q, -p, 0.1f, 1.0f));
886 vertices.push_back(Vec4(-q, -p, 0.9f, 1.0f)); // line 3
887 }
888 else if (lineOrientation == LINE_ORIENTATION_DIAGONAL)
889 {
890 // Diagonal lines just outside the clip volume.
891 const float p = 2.0f + delta;
892
893 vertices.push_back(Vec4( -p, 0.0f, 0.1f, 1.0f));
894 vertices.push_back(Vec4(0.0f, -p, 0.9f, 1.0f)); // line 0
895 vertices.push_back(Vec4(0.0f, -p, 0.1f, 1.0f));
896 vertices.push_back(Vec4( p, 0.0f, 0.9f, 1.0f)); // line 1
897 vertices.push_back(Vec4( p, 0.0f, 0.1f, 1.0f));
898 vertices.push_back(Vec4(0.0f, p, 0.9f, 1.0f)); // line 2
899 vertices.push_back(Vec4(0.0f, p, 0.1f, 1.0f));
900 vertices.push_back(Vec4( -p, 0.0f, 0.9f, 1.0f)); // line 3
901 }
902 else
903 DE_ASSERT(0);
904
905 const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(context.getInstanceInterface(), context.getPhysicalDevice()).limits;
906
907 const float lineWidth = std::min(static_cast<float>(RENDER_SIZE), limits.lineWidthRange[1]);
908 const bool strictLines = limits.strictLines;
909 tcu::TestLog& log = context.getTestContext().getLog();
910
911 log << tcu::TestLog::Message << "Drawing several wide lines just outside the clip volume. Expecting an empty image or all lines rendered." << tcu::TestLog::EndMessage
912 << tcu::TestLog::Message << "Line width is " << lineWidth << "." << tcu::TestLog::EndMessage
913 << tcu::TestLog::Message << "strictLines is " << (strictLines ? "VK_TRUE." : "VK_FALSE.") << tcu::TestLog::EndMessage;
914
915 DrawState drawState (VK_PRIMITIVE_TOPOLOGY_LINE_LIST, RENDER_SIZE, RENDER_SIZE, context.getDeviceProperties().limits.subPixelPrecisionBits);
916 DrawCallData drawCallData (vertices);
917 VulkanProgram vulkanProgram (shaders);
918 drawState.lineWidth = lineWidth;
919
920 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram);
921 drawContext.draw();
922
923 // Popful case: All pixels must be black -- nothing is drawn.
924 if (countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4()) == NUM_RENDER_PIXELS)
925 {
926 return tcu::TestStatus::pass("OK");
927 }
928 // Pop-free case: All lines must be rendered.
929 else
930 {
931 const float halfWidth = lineWidth / float(RENDER_SIZE);
932 std::vector<Vec4> refVertices;
933
934 // Create reference primitives
935 for (deUint32 lineNdx = 0u; lineNdx < (deUint32)vertices.size() / 2u; lineNdx++)
936 {
937 const deUint32 vertexNdx0 = 2 * lineNdx;
938 const deUint32 vertexNdx1 = 2 * lineNdx + 1;
939
940 const bool xMajorAxis = deFloatAbs(vertices[vertexNdx1].x() - vertices[vertexNdx0].x()) >= deFloatAbs(vertices[vertexNdx1].y() - vertices[vertexNdx0].y());
941 const tcu::Vec2 lineDir = tcu::normalize(tcu::Vec2(vertices[vertexNdx1].x() - vertices[vertexNdx0].x(), vertices[vertexNdx1].y() - vertices[vertexNdx0].y()));
942 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.
943 : (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
944
945 const tcu::Vec4 wideLineVertices[] =
946 {
947 tcu::Vec4(vertices[vertexNdx0] + lineNormalDir * halfWidth),
948 tcu::Vec4(vertices[vertexNdx0] - lineNormalDir * halfWidth),
949 tcu::Vec4(vertices[vertexNdx1] - lineNormalDir * halfWidth),
950 tcu::Vec4(vertices[vertexNdx1] + lineNormalDir * halfWidth)
951 };
952
953 // 1st triangle
954 refVertices.push_back(wideLineVertices[0]);
955 refVertices.push_back(wideLineVertices[1]);
956 refVertices.push_back(wideLineVertices[2]);
957
958 // 2nd triangle
959 refVertices.push_back(wideLineVertices[0]);
960 refVertices.push_back(wideLineVertices[2]);
961 refVertices.push_back(wideLineVertices[3]);
962 }
963
964 WideLineVertexShader vertexShader;
965 WideLineFragmentShader fragmentShader;
966
967 // Draw wide line was two triangles
968 DrawState refDrawState (VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, RENDER_SIZE, RENDER_SIZE, context.getDeviceProperties().limits.subPixelPrecisionBits);
969 DrawCallData refCallData (refVertices);
970 ReferenceDrawContext refDrawContext (refDrawState, refCallData, vertexShader, fragmentShader);
971
972 refDrawContext.draw();
973
974 if (tcu::intThresholdCompare(log, "Compare", "Result comparsion", refDrawContext.getColorPixels(), drawContext.getColorPixels(), tcu::UVec4(1), tcu::COMPARE_LOG_ON_ERROR))
975 return tcu::TestStatus::pass("OK");
976 }
977
978 return tcu::TestStatus::fail("Rendered image(s) are incorrect");
979 }
980
981 } // ClipVolume ns
982
983 namespace ClipDistance
984 {
985
986 struct CaseDefinition
987 {
988 const VkPrimitiveTopology topology;
989 const bool dynamicIndexing;
990 const bool enableTessellation;
991 const bool enableGeometry;
992 const int numClipDistances;
993 const int numCullDistances;
994 const bool readInFragmentShader;
995
CaseDefinitionvkt::clipping::__anonb2c21f0d0111::ClipDistance::CaseDefinition996 CaseDefinition (const VkPrimitiveTopology topology_,
997 const int numClipDistances_,
998 const int numCullDistances_,
999 const bool enableTessellation_,
1000 const bool enableGeometry_,
1001 const bool dynamicIndexing_,
1002 const bool readInFragmentShader_)
1003 : topology (topology_)
1004 , dynamicIndexing (dynamicIndexing_)
1005 , enableTessellation (enableTessellation_)
1006 , enableGeometry (enableGeometry_)
1007 , numClipDistances (numClipDistances_)
1008 , numCullDistances (numCullDistances_)
1009 , readInFragmentShader (readInFragmentShader_)
1010 {
1011 }
1012 };
1013
initPrograms(SourceCollections & programCollection,const CaseDefinition caseDef)1014 void initPrograms (SourceCollections& programCollection, const CaseDefinition caseDef)
1015 {
1016 DE_ASSERT(caseDef.numClipDistances + caseDef.numCullDistances <= MAX_COMBINED_CLIP_AND_CULL_DISTANCES);
1017
1018 std::string perVertexBlock;
1019 {
1020 std::ostringstream str;
1021 str << "gl_PerVertex {\n"
1022 << " vec4 gl_Position;\n";
1023 if (caseDef.numClipDistances > 0)
1024 str << " float gl_ClipDistance[" << caseDef.numClipDistances << "];\n";
1025 if (caseDef.numCullDistances > 0)
1026 str << " float gl_CullDistance[" << caseDef.numCullDistances << "];\n";
1027 str << "}";
1028 perVertexBlock = str.str();
1029 }
1030
1031 // Vertex shader
1032 {
1033 std::ostringstream src;
1034 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1035 << "\n"
1036 << "layout(location = 0) in vec4 v_position;\n"
1037 << "layout(location = 0) out vec4 out_color;\n"
1038 << "\n"
1039 << "out " << perVertexBlock << ";\n"
1040 << "\n"
1041 << "void main (void)\n"
1042 << "{\n"
1043 << " gl_Position = v_position;\n"
1044 << " out_color = vec4(1.0, 0.5 * (v_position.x + 1.0), 0.0, 1.0);\n"
1045 << "\n"
1046 << " const int barNdx = gl_VertexIndex / 6;\n";
1047 if (caseDef.dynamicIndexing)
1048 {
1049 if (caseDef.numClipDistances > 0)
1050 src << " for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
1051 << " gl_ClipDistance[i] = (barNdx == i ? v_position.y : 0.0);\n";
1052 if (caseDef.numCullDistances > 0)
1053 src << " for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n"
1054 << " gl_CullDistance[i] = 0.5;\n";
1055 }
1056 else
1057 {
1058 for (int i = 0; i < caseDef.numClipDistances; ++i)
1059 src << " gl_ClipDistance[" << i << "] = (barNdx == " << i << " ? v_position.y : 0.0);\n";
1060 for (int i = 0; i < caseDef.numCullDistances; ++i)
1061 src << " gl_CullDistance[" << i << "] = 0.5;\n"; // don't cull anything
1062 }
1063 src << "}\n";
1064
1065 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
1066 }
1067
1068 if (caseDef.enableTessellation)
1069 {
1070 std::ostringstream src;
1071 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1072 << "\n"
1073 << "layout(vertices = " << NUM_PATCH_CONTROL_POINTS << ") out;\n"
1074 << "\n"
1075 << "layout(location = 0) in vec4 in_color[];\n"
1076 << "layout(location = 0) out vec4 out_color[];\n"
1077 << "\n"
1078 << "in " << perVertexBlock << " gl_in[gl_MaxPatchVertices];\n"
1079 << "\n"
1080 << "out " << perVertexBlock << " gl_out[];\n"
1081 << "\n"
1082 << "void main (void)\n"
1083 << "{\n"
1084 << " gl_TessLevelInner[0] = 1.0;\n"
1085 << " gl_TessLevelInner[1] = 1.0;\n"
1086 << "\n"
1087 << " gl_TessLevelOuter[0] = 1.0;\n"
1088 << " gl_TessLevelOuter[1] = 1.0;\n"
1089 << " gl_TessLevelOuter[2] = 1.0;\n"
1090 << " gl_TessLevelOuter[3] = 1.0;\n"
1091 << "\n"
1092 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
1093 << " out_color[gl_InvocationID] = in_color[gl_InvocationID];\n"
1094 << "\n";
1095 if (caseDef.dynamicIndexing)
1096 {
1097 if (caseDef.numClipDistances > 0)
1098 src << " for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
1099 << " gl_out[gl_InvocationID].gl_ClipDistance[i] = gl_in[gl_InvocationID].gl_ClipDistance[i];\n";
1100 if (caseDef.numCullDistances > 0)
1101 src << " for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n"
1102 << " gl_out[gl_InvocationID].gl_CullDistance[i] = gl_in[gl_InvocationID].gl_CullDistance[i];\n";
1103 }
1104 else
1105 {
1106 for (int i = 0; i < caseDef.numClipDistances; ++i)
1107 src << " gl_out[gl_InvocationID].gl_ClipDistance[" << i << "] = gl_in[gl_InvocationID].gl_ClipDistance[" << i << "];\n";
1108 for (int i = 0; i < caseDef.numCullDistances; ++i)
1109 src << " gl_out[gl_InvocationID].gl_CullDistance[" << i << "] = gl_in[gl_InvocationID].gl_CullDistance[" << i << "];\n";
1110 }
1111 src << "}\n";
1112
1113 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
1114 }
1115
1116 if (caseDef.enableTessellation)
1117 {
1118 DE_ASSERT(NUM_PATCH_CONTROL_POINTS == 3); // assumed in shader code
1119
1120 std::ostringstream src;
1121 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1122 << "\n"
1123 << "layout(triangles, equal_spacing, ccw) in;\n"
1124 << "\n"
1125 << "layout(location = 0) in vec4 in_color[];\n"
1126 << "layout(location = 0) out vec4 out_color;\n"
1127 << "\n"
1128 << "in " << perVertexBlock << " gl_in[gl_MaxPatchVertices];\n"
1129 << "\n"
1130 << "out " << perVertexBlock << ";\n"
1131 << "\n"
1132 << "void main (void)\n"
1133 << "{\n"
1134 << " vec3 px = gl_TessCoord.x * gl_in[0].gl_Position.xyz;\n"
1135 << " vec3 py = gl_TessCoord.y * gl_in[1].gl_Position.xyz;\n"
1136 << " vec3 pz = gl_TessCoord.z * gl_in[2].gl_Position.xyz;\n"
1137 << " gl_Position = vec4(px + py + pz, 1.0);\n"
1138 << " out_color = (in_color[0] + in_color[1] + in_color[2]) / 3.0;\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_ClipDistance[i] = gl_TessCoord.x * gl_in[0].gl_ClipDistance[i]\n"
1145 << " + gl_TessCoord.y * gl_in[1].gl_ClipDistance[i]\n"
1146 << " + gl_TessCoord.z * gl_in[2].gl_ClipDistance[i];\n";
1147 if (caseDef.numCullDistances > 0)
1148 src << " for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n"
1149 << " gl_CullDistance[i] = gl_TessCoord.x * gl_in[0].gl_CullDistance[i]\n"
1150 << " + gl_TessCoord.y * gl_in[1].gl_CullDistance[i]\n"
1151 << " + gl_TessCoord.z * gl_in[2].gl_CullDistance[i];\n";
1152 }
1153 else
1154 {
1155 for (int i = 0; i < caseDef.numClipDistances; ++i)
1156 src << " gl_ClipDistance[" << i << "] = gl_TessCoord.x * gl_in[0].gl_ClipDistance[" << i << "]\n"
1157 << " + gl_TessCoord.y * gl_in[1].gl_ClipDistance[" << i << "]\n"
1158 << " + gl_TessCoord.z * gl_in[2].gl_ClipDistance[" << i << "];\n";
1159 for (int i = 0; i < caseDef.numCullDistances; ++i)
1160 src << " gl_CullDistance[" << i << "] = gl_TessCoord.x * gl_in[0].gl_CullDistance[" << i << "]\n"
1161 << " + gl_TessCoord.y * gl_in[1].gl_CullDistance[" << i << "]\n"
1162 << " + gl_TessCoord.z * gl_in[2].gl_CullDistance[" << i << "];\n";
1163 }
1164 src << "}\n";
1165
1166 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
1167 }
1168
1169 if (caseDef.enableGeometry)
1170 {
1171 std::ostringstream src;
1172 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1173 << "\n"
1174 << "layout(triangles) in;\n"
1175 << "layout(triangle_strip, max_vertices = 3) out;\n"
1176 << "\n"
1177 << "layout(location = 0) in vec4 in_color[];\n"
1178 << "layout(location = 0) out vec4 out_color;\n"
1179 << "\n"
1180 << "in " << perVertexBlock << " gl_in[];\n"
1181 << "\n"
1182 << "out " << perVertexBlock << ";\n"
1183 << "\n"
1184 << "void main (void)\n"
1185 << "{\n";
1186 for (int vertNdx = 0; vertNdx < 3; ++vertNdx)
1187 {
1188 if (vertNdx > 0)
1189 src << "\n";
1190 src << " gl_Position = gl_in[" << vertNdx << "].gl_Position;\n"
1191 << " out_color = in_color[" << vertNdx << "];\n";
1192 if (caseDef.dynamicIndexing)
1193 {
1194 if (caseDef.numClipDistances > 0)
1195 src << " for (int i = 0; i < " << caseDef.numClipDistances << "; ++i)\n"
1196 << " gl_ClipDistance[i] = gl_in[" << vertNdx << "].gl_ClipDistance[i];\n";
1197 if (caseDef.numCullDistances > 0)
1198 src << " for (int i = 0; i < " << caseDef.numCullDistances << "; ++i)\n"
1199 << " gl_CullDistance[i] = gl_in[" << vertNdx << "].gl_CullDistance[i];\n";
1200 }
1201 else
1202 {
1203 for (int i = 0; i < caseDef.numClipDistances; ++i)
1204 src << " gl_ClipDistance[" << i << "] = gl_in[" << vertNdx << "].gl_ClipDistance[" << i << "];\n";
1205 for (int i = 0; i < caseDef.numCullDistances; ++i)
1206 src << " gl_CullDistance[" << i << "] = gl_in[" << vertNdx << "].gl_CullDistance[" << i << "];\n";
1207 }
1208 src << " EmitVertex();\n";
1209 }
1210 src << "}\n";
1211
1212 programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
1213 }
1214
1215 // Fragment shader
1216 {
1217 std::ostringstream src;
1218 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1219 << "\n"
1220 << "layout(location = 0) in flat vec4 in_color;\n"
1221 << "layout(location = 0) out vec4 o_color;\n";
1222 if (caseDef.readInFragmentShader)
1223 {
1224 if (caseDef.numClipDistances > 0)
1225 src << "in float gl_ClipDistance[" << caseDef.numClipDistances << "];\n";
1226 if (caseDef.numCullDistances > 0)
1227 src << "in float gl_CullDistance[" << caseDef.numCullDistances << "];\n";
1228 }
1229 src << "\n"
1230 << "void main (void)\n"
1231 << "{\n";
1232
1233 if (caseDef.readInFragmentShader)
1234 {
1235 src << " o_color = vec4(in_color.r, "
1236 << (caseDef.numClipDistances > 0 ? std::string("gl_ClipDistance[") + de::toString(caseDef.numClipDistances / 2) + "], " : "0.0, ")
1237 << (caseDef.numCullDistances > 0 ? std::string("gl_CullDistance[") + de::toString(caseDef.numCullDistances / 2) + "], " : "0.0, ")
1238 << " 1.0);\n";
1239 }
1240 else
1241 {
1242 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
1243 }
1244
1245 src << "}\n";
1246
1247 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
1248 }
1249 }
1250
testClipDistance(Context & context,const CaseDefinition caseDef)1251 tcu::TestStatus testClipDistance (Context& context, const CaseDefinition caseDef)
1252 {
1253 // Check test requirements
1254 {
1255 const InstanceInterface& vki = context.getInstanceInterface();
1256 const VkPhysicalDevice physDevice = context.getPhysicalDevice();
1257 const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(vki, physDevice).limits;
1258
1259 FeatureFlags requirements = (FeatureFlags)0;
1260
1261 if (caseDef.numClipDistances > 0)
1262 requirements |= FEATURE_SHADER_CLIP_DISTANCE;
1263 if (caseDef.numCullDistances > 0)
1264 requirements |= FEATURE_SHADER_CULL_DISTANCE;
1265 if (caseDef.enableTessellation)
1266 requirements |= FEATURE_TESSELLATION_SHADER;
1267 if (caseDef.enableGeometry)
1268 requirements |= FEATURE_GEOMETRY_SHADER;
1269
1270 requireFeatures(vki, physDevice, requirements);
1271
1272 // Check limits for supported features
1273
1274 if (caseDef.numClipDistances > 0 && limits.maxClipDistances < MAX_CLIP_DISTANCES)
1275 return tcu::TestStatus::fail("maxClipDistances smaller than the minimum required by the spec");
1276 if (caseDef.numCullDistances > 0 && limits.maxCullDistances < MAX_CULL_DISTANCES)
1277 return tcu::TestStatus::fail("maxCullDistances smaller than the minimum required by the spec");
1278 if (caseDef.numCullDistances > 0 && limits.maxCombinedClipAndCullDistances < MAX_COMBINED_CLIP_AND_CULL_DISTANCES)
1279 return tcu::TestStatus::fail("maxCombinedClipAndCullDistances smaller than the minimum required by the spec");
1280 }
1281
1282 std::vector<VulkanShader> shaders;
1283 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
1284 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
1285 if (caseDef.enableTessellation)
1286 {
1287 shaders.push_back(VulkanShader(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc")));
1288 shaders.push_back(VulkanShader(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese")));
1289 }
1290 if (caseDef.enableGeometry)
1291 shaders.push_back(VulkanShader(VK_SHADER_STAGE_GEOMETRY_BIT, context.getBinaryCollection().get("geom")));
1292
1293 const int numBars = MAX_COMBINED_CLIP_AND_CULL_DISTANCES;
1294
1295 std::vector<Vec4> vertices;
1296 {
1297 const float dx = 2.0f / numBars;
1298 for (int i = 0; i < numBars; ++i)
1299 {
1300 const float x = -1.0f + dx * static_cast<float>(i);
1301
1302 vertices.push_back(Vec4(x, -1.0f, 0.0f, 1.0f));
1303 vertices.push_back(Vec4(x, 1.0f, 0.0f, 1.0f));
1304 vertices.push_back(Vec4(x + dx, -1.0f, 0.0f, 1.0f));
1305
1306 vertices.push_back(Vec4(x, 1.0f, 0.0f, 1.0f));
1307 vertices.push_back(Vec4(x + dx, 1.0f, 0.0f, 1.0f));
1308 vertices.push_back(Vec4(x + dx, -1.0f, 0.0f, 1.0f));
1309 }
1310 }
1311
1312 tcu::TestLog& log = context.getTestContext().getLog();
1313
1314 log << tcu::TestLog::Message << "Drawing " << numBars << " colored bars, clipping the first " << caseDef.numClipDistances << tcu::TestLog::EndMessage
1315 << tcu::TestLog::Message << "Using " << caseDef.numClipDistances << " ClipDistance(s) and " << caseDef.numCullDistances << " CullDistance(s)" << tcu::TestLog::EndMessage
1316 << tcu::TestLog::Message << "Expecting upper half of the clipped bars to be black." << tcu::TestLog::EndMessage;
1317
1318 DrawState drawState (caseDef.topology, RENDER_SIZE, RENDER_SIZE, context.getDeviceProperties().limits.subPixelPrecisionBits);
1319 DrawCallData drawCallData (vertices);
1320 VulkanProgram vulkanProgram (shaders);
1321
1322 if (caseDef.enableTessellation)
1323 drawState.numPatchControlPoints = NUM_PATCH_CONTROL_POINTS;
1324
1325 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram);
1326 drawContext.draw();
1327
1328 // Count black pixels in the whole image.
1329 const int numBlackPixels = countPixels(drawContext.getColorPixels(), Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
1330 const IVec2 clipRegion = IVec2(caseDef.numClipDistances * RENDER_SIZE / numBars, RENDER_SIZE / 2);
1331 const int expectedClippedPixels = clipRegion.x() * clipRegion.y();
1332 // Make sure the bottom half has no black pixels (possible if image became corrupted).
1333 const int guardPixels = countPixels(drawContext.getColorPixels(), IVec2(0, RENDER_SIZE/2), clipRegion, Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4());
1334 const bool fragColorsOk = caseDef.readInFragmentShader ? checkFragColors(drawContext.getColorPixels(), clipRegion, caseDef.numClipDistances / 2, caseDef.numCullDistances > 0) : true;
1335
1336 return (numBlackPixels == expectedClippedPixels && guardPixels == 0 && fragColorsOk ? tcu::TestStatus::pass("OK")
1337 : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
1338 }
1339
1340 } // ClipDistance ns
1341
1342 namespace ClipDistanceComplementarity
1343 {
1344
initPrograms(SourceCollections & programCollection,const int numClipDistances)1345 void initPrograms (SourceCollections& programCollection, const int numClipDistances)
1346 {
1347 // Vertex shader
1348 {
1349 DE_ASSERT(numClipDistances > 0);
1350 const int clipDistanceLastNdx = numClipDistances - 1;
1351
1352 std::ostringstream src;
1353 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1354 << "\n"
1355 << "layout(location = 0) in vec4 v_position; // we are passing ClipDistance in w component\n"
1356 << "\n"
1357 << "out gl_PerVertex {\n"
1358 << " vec4 gl_Position;\n"
1359 << " float gl_ClipDistance[" << numClipDistances << "];\n"
1360 << "};\n"
1361 << "\n"
1362 << "void main (void)\n"
1363 << "{\n"
1364 << " gl_Position = vec4(v_position.xyz, 1.0);\n";
1365 for (int i = 0; i < clipDistanceLastNdx; ++i)
1366 src << " gl_ClipDistance[" << i << "] = 0.0;\n";
1367 src << " gl_ClipDistance[" << clipDistanceLastNdx << "] = v_position.w;\n"
1368 << "}\n";
1369
1370 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
1371 }
1372
1373 // Fragment shader
1374 {
1375 std::ostringstream src;
1376 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
1377 << "\n"
1378 << "layout(location = 0) out vec4 o_color;\n"
1379 << "\n"
1380 << "void main (void)\n"
1381 << "{\n"
1382 << " o_color = vec4(1.0, 1.0, 1.0, 0.5);\n"
1383 << "}\n";
1384
1385 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
1386 }
1387 }
1388
testComplementarity(Context & context,const int numClipDistances)1389 tcu::TestStatus testComplementarity (Context& context, const int numClipDistances)
1390 {
1391 // Check test requirements
1392 {
1393 const InstanceInterface& vki = context.getInstanceInterface();
1394 const VkPhysicalDevice physDevice = context.getPhysicalDevice();
1395
1396 requireFeatures(vki, physDevice, FEATURE_SHADER_CLIP_DISTANCE);
1397 }
1398
1399 std::vector<VulkanShader> shaders;
1400 shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert")));
1401 shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag")));
1402
1403 std::vector<Vec4> vertices;
1404 {
1405 de::Random rnd (1234);
1406 const int numSections = 16;
1407 const int numVerticesPerSection = 4; // logical verticies, due to triangle list topology we actually use 6 per section
1408
1409 DE_ASSERT(RENDER_SIZE_LARGE % numSections == 0);
1410
1411 std::vector<float> clipDistances(numVerticesPerSection * numSections);
1412 for (int i = 0; i < static_cast<int>(clipDistances.size()); ++i)
1413 clipDistances[i] = rnd.getFloat(-1.0f, 1.0f);
1414
1415 // Two sets of identical primitives, but with a different ClipDistance sign.
1416 for (int setNdx = 0; setNdx < 2; ++setNdx)
1417 {
1418 const float sign = (setNdx == 0 ? 1.0f : -1.0f);
1419 const float dx = 2.0f / static_cast<float>(numSections);
1420
1421 for (int i = 0; i < numSections; ++i)
1422 {
1423 const int ndxBase = numVerticesPerSection * i;
1424 const float x = -1.0f + dx * static_cast<float>(i);
1425 const Vec4 p0 = Vec4(x, -1.0f, 0.0f, sign * clipDistances[ndxBase + 0]);
1426 const Vec4 p1 = Vec4(x, 1.0f, 0.0f, sign * clipDistances[ndxBase + 1]);
1427 const Vec4 p2 = Vec4(x + dx, 1.0f, 0.0f, sign * clipDistances[ndxBase + 2]);
1428 const Vec4 p3 = Vec4(x + dx, -1.0f, 0.0f, sign * clipDistances[ndxBase + 3]);
1429
1430 vertices.push_back(p0);
1431 vertices.push_back(p1);
1432 vertices.push_back(p2);
1433
1434 vertices.push_back(p2);
1435 vertices.push_back(p3);
1436 vertices.push_back(p0);
1437 }
1438 }
1439 }
1440
1441 tcu::TestLog& log = context.getTestContext().getLog();
1442
1443 log << tcu::TestLog::Message << "Draw two sets of primitives with blending, differing only with ClipDistance sign." << tcu::TestLog::EndMessage
1444 << tcu::TestLog::Message << "Using " << numClipDistances << " clipping plane(s), one of them possibly having negative values." << tcu::TestLog::EndMessage
1445 << tcu::TestLog::Message << "Expecting a uniform gray area, no missing (black) nor overlapped (white) pixels." << tcu::TestLog::EndMessage;
1446
1447 DrawState drawState (VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, RENDER_SIZE_LARGE, RENDER_SIZE_LARGE, context.getDeviceProperties().limits.subPixelPrecisionBits);
1448 DrawCallData drawCallData (vertices);
1449 VulkanProgram vulkanProgram (shaders);
1450 drawState.blendEnable = true;
1451
1452 VulkanDrawContext drawContext(context, drawState, drawCallData, vulkanProgram);
1453 drawContext.draw();
1454
1455 const int numGrayPixels = countPixels(drawContext.getColorPixels(), Vec4(0.5f, 0.5f, 0.5f, 1.0f), Vec4(0.02f, 0.02f, 0.02f, 0.0f));
1456 const int numExpectedPixels = RENDER_SIZE_LARGE * RENDER_SIZE_LARGE;
1457
1458 return (numGrayPixels == numExpectedPixels ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Rendered image(s) are incorrect"));
1459 }
1460
1461 } // ClipDistanceComplementarity ns
1462
addClippingTests(tcu::TestCaseGroup * clippingTestsGroup)1463 void addClippingTests (tcu::TestCaseGroup* clippingTestsGroup)
1464 {
1465 tcu::TestContext& testCtx = clippingTestsGroup->getTestContext();
1466
1467 // Clipping against the clip volume
1468 {
1469 using namespace ClipVolume;
1470
1471 static const VkPrimitiveTopology cases[] =
1472 {
1473 VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
1474 VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
1475 VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,
1476 VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
1477 VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY,
1478 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
1479 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY,
1480 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
1481 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY,
1482 VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
1483 };
1484
1485 MovePtr<tcu::TestCaseGroup> clipVolumeGroup(new tcu::TestCaseGroup(testCtx, "clip_volume", "clipping with the clip volume"));
1486
1487 // Fully inside the clip volume
1488 {
1489 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "inside", ""));
1490
1491 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1492 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1493 group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), "", initPrograms, testPrimitivesInside, cases[caseNdx]);
1494
1495 clipVolumeGroup->addChild(group.release());
1496 }
1497
1498 // Fully outside the clip volume
1499 {
1500 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "outside", ""));
1501
1502 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1503 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1504 group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), "", initPrograms, testPrimitivesOutside, cases[caseNdx]);
1505
1506 clipVolumeGroup->addChild(group.release());
1507 }
1508
1509 // Depth clamping
1510 {
1511 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "depth_clamp", ""));
1512
1513 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1514 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1515 group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), "", initPrograms, testPrimitivesDepthClamp, cases[caseNdx]);
1516
1517 clipVolumeGroup->addChild(group.release());
1518 }
1519
1520 // Depth clipping
1521 {
1522 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "depth_clip", ""));
1523
1524 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
1525 addFunctionCaseWithPrograms<VkPrimitiveTopology>(
1526 group.get(), getPrimitiveTopologyShortName(cases[caseNdx]), "", initPrograms, testPrimitivesDepthClip, cases[caseNdx]);
1527
1528 clipVolumeGroup->addChild(group.release());
1529 }
1530
1531 // Large points and wide lines
1532 {
1533 // \note For both points and lines, if an unsupported size/width is selected, the nearest supported size will be chosen.
1534 // We do have to check for feature support though.
1535
1536 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "clipped", ""));
1537
1538 addFunctionCaseWithPrograms(group.get(), "large_points", "", initProgramsPointSize, testLargePoints);
1539
1540 addFunctionCaseWithPrograms<LineOrientation>(group.get(), "wide_lines_axis_aligned", "", initPrograms, testWideLines, LINE_ORIENTATION_AXIS_ALIGNED);
1541 addFunctionCaseWithPrograms<LineOrientation>(group.get(), "wide_lines_diagonal", "", initPrograms, testWideLines, LINE_ORIENTATION_DIAGONAL);
1542
1543 clipVolumeGroup->addChild(group.release());
1544 }
1545
1546 clippingTestsGroup->addChild(clipVolumeGroup.release());
1547 }
1548
1549 // User-defined clip planes
1550 {
1551 MovePtr<tcu::TestCaseGroup> clipDistanceGroup(new tcu::TestCaseGroup(testCtx, "user_defined", "user-defined clip planes"));
1552
1553 // ClipDistance, CullDistance and maxCombinedClipAndCullDistances usage
1554 {
1555 using namespace ClipDistance;
1556
1557 static const struct
1558 {
1559 const char* const groupName;
1560 const char* const description;
1561 bool useCullDistance;
1562 } caseGroups[] =
1563 {
1564 { "clip_distance", "use ClipDistance", false },
1565 { "clip_cull_distance", "use ClipDistance and CullDistance at the same time", true },
1566 };
1567
1568 static const struct
1569 {
1570 const char* const name;
1571 bool readInFragmentShader;
1572 } fragmentShaderReads[] =
1573 {
1574
1575 { "", false },
1576 { "_fragmentshader_read", true }
1577 };
1578
1579 const deUint32 flagTessellation = 1u << 0;
1580 const deUint32 flagGeometry = 1u << 1;
1581
1582 for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(caseGroups); ++groupNdx)
1583 for (int indexingMode = 0; indexingMode < 2; ++indexingMode)
1584 {
1585 const bool dynamicIndexing = (indexingMode == 1);
1586 const std::string mainGroupName = de::toString(caseGroups[groupNdx].groupName) + (dynamicIndexing ? "_dynamic_index" : "");
1587
1588 MovePtr<tcu::TestCaseGroup> mainGroup(new tcu::TestCaseGroup(testCtx, mainGroupName.c_str(), ""));
1589
1590 for (deUint32 shaderMask = 0u; shaderMask <= (flagTessellation | flagGeometry); ++shaderMask)
1591 {
1592 const bool useTessellation = (shaderMask & flagTessellation) != 0;
1593 const bool useGeometry = (shaderMask & flagGeometry) != 0;
1594 const std::string shaderGroupName = std::string("vert") + (useTessellation ? "_tess" : "") + (useGeometry ? "_geom" : "");
1595
1596 MovePtr<tcu::TestCaseGroup> shaderGroup(new tcu::TestCaseGroup(testCtx, shaderGroupName.c_str(), ""));
1597
1598 for (int numClipPlanes = 1; numClipPlanes <= MAX_CLIP_DISTANCES; ++numClipPlanes)
1599 for (int fragmentShaderReadNdx = 0; fragmentShaderReadNdx < DE_LENGTH_OF_ARRAY(fragmentShaderReads); ++fragmentShaderReadNdx)
1600 {
1601 const int numCullPlanes = (caseGroups[groupNdx].useCullDistance
1602 ? std::min(static_cast<int>(MAX_CULL_DISTANCES), MAX_COMBINED_CLIP_AND_CULL_DISTANCES - numClipPlanes)
1603 : 0);
1604 const std::string caseName = de::toString(numClipPlanes) + (numCullPlanes > 0 ? "_" + de::toString(numCullPlanes) : "") + de::toString(fragmentShaderReads[fragmentShaderReadNdx].name);
1605 const VkPrimitiveTopology topology = (useTessellation ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
1606
1607 addFunctionCaseWithPrograms<CaseDefinition>(
1608 shaderGroup.get(), caseName, caseGroups[groupNdx].description, initPrograms, testClipDistance,
1609 CaseDefinition(topology, numClipPlanes, numCullPlanes, useTessellation, useGeometry, dynamicIndexing, fragmentShaderReads[fragmentShaderReadNdx].readInFragmentShader));
1610 }
1611 mainGroup->addChild(shaderGroup.release());
1612 }
1613 clipDistanceGroup->addChild(mainGroup.release());
1614 }
1615 }
1616
1617 // Complementarity criterion (i.e. clipped and not clipped areas must add up to a complete primitive with no holes nor overlap)
1618 {
1619 using namespace ClipDistanceComplementarity;
1620
1621 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "complementarity", ""));
1622
1623 for (int numClipDistances = 1; numClipDistances <= MAX_CLIP_DISTANCES; ++numClipDistances)
1624 addFunctionCaseWithPrograms<int>(group.get(), de::toString(numClipDistances).c_str(), "", initPrograms, testComplementarity, numClipDistances);
1625
1626 clippingTestsGroup->addChild(group.release());
1627 }
1628
1629 clippingTestsGroup->addChild(clipDistanceGroup.release());
1630 }
1631 }
1632
1633 } // anonymous
1634
createTests(tcu::TestContext & testCtx)1635 tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx)
1636 {
1637 return createTestGroup(testCtx, "clipping", "Clipping tests", addClippingTests);
1638 }
1639
1640 } // clipping
1641 } // vkt
1642