1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
3 * ----------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
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 Rasterization verifier utils.
22 *//*--------------------------------------------------------------------*/
23
24 #include "tcuRasterizationVerifier.hpp"
25 #include "tcuVector.hpp"
26 #include "tcuSurface.hpp"
27 #include "tcuTestLog.hpp"
28 #include "tcuTextureUtil.hpp"
29 #include "tcuVectorUtil.hpp"
30 #include "tcuFloat.hpp"
31
32 #include "deMath.h"
33 #include "deStringUtil.hpp"
34
35 #include "rrRasterizer.hpp"
36
37 #include <limits>
38
39 namespace tcu
40 {
41 namespace
42 {
43
44 bool verifyLineGroupInterpolationWithProjectedWeights (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log);
45
lineLineIntersect(const tcu::Vector<deInt64,2> & line0Beg,const tcu::Vector<deInt64,2> & line0End,const tcu::Vector<deInt64,2> & line1Beg,const tcu::Vector<deInt64,2> & line1End)46 bool lineLineIntersect (const tcu::Vector<deInt64, 2>& line0Beg, const tcu::Vector<deInt64, 2>& line0End, const tcu::Vector<deInt64, 2>& line1Beg, const tcu::Vector<deInt64, 2>& line1End)
47 {
48 typedef tcu::Vector<deInt64, 2> I64Vec2;
49
50 // Lines do not intersect if the other line's endpoints are on the same side
51 // otherwise, the do intersect
52
53 // Test line 0
54 {
55 const I64Vec2 line = line0End - line0Beg;
56 const I64Vec2 v0 = line1Beg - line0Beg;
57 const I64Vec2 v1 = line1End - line0Beg;
58 const deInt64 crossProduct0 = (line.x() * v0.y() - line.y() * v0.x());
59 const deInt64 crossProduct1 = (line.x() * v1.y() - line.y() * v1.x());
60
61 // check signs
62 if ((crossProduct0 < 0 && crossProduct1 < 0) ||
63 (crossProduct0 > 0 && crossProduct1 > 0))
64 return false;
65 }
66
67 // Test line 1
68 {
69 const I64Vec2 line = line1End - line1Beg;
70 const I64Vec2 v0 = line0Beg - line1Beg;
71 const I64Vec2 v1 = line0End - line1Beg;
72 const deInt64 crossProduct0 = (line.x() * v0.y() - line.y() * v0.x());
73 const deInt64 crossProduct1 = (line.x() * v1.y() - line.y() * v1.x());
74
75 // check signs
76 if ((crossProduct0 < 0 && crossProduct1 < 0) ||
77 (crossProduct0 > 0 && crossProduct1 > 0))
78 return false;
79 }
80
81 return true;
82 }
83
isTriangleClockwise(const tcu::Vec4 & p0,const tcu::Vec4 & p1,const tcu::Vec4 & p2)84 bool isTriangleClockwise (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec4& p2)
85 {
86 const tcu::Vec2 u (p1.x() / p1.w() - p0.x() / p0.w(), p1.y() / p1.w() - p0.y() / p0.w());
87 const tcu::Vec2 v (p2.x() / p2.w() - p0.x() / p0.w(), p2.y() / p2.w() - p0.y() / p0.w());
88 const float crossProduct = (u.x() * v.y() - u.y() * v.x());
89
90 return crossProduct > 0.0f;
91 }
92
compareColors(const tcu::RGBA & colorA,const tcu::RGBA & colorB,int redBits,int greenBits,int blueBits)93 bool compareColors (const tcu::RGBA& colorA, const tcu::RGBA& colorB, int redBits, int greenBits, int blueBits)
94 {
95 const int thresholdRed = 1 << (8 - redBits);
96 const int thresholdGreen = 1 << (8 - greenBits);
97 const int thresholdBlue = 1 << (8 - blueBits);
98
99 return deAbs32(colorA.getRed() - colorB.getRed()) <= thresholdRed &&
100 deAbs32(colorA.getGreen() - colorB.getGreen()) <= thresholdGreen &&
101 deAbs32(colorA.getBlue() - colorB.getBlue()) <= thresholdBlue;
102 }
103
pixelNearLineSegment(const tcu::IVec2 & pixel,const tcu::Vec2 & p0,const tcu::Vec2 & p1)104 bool pixelNearLineSegment (const tcu::IVec2& pixel, const tcu::Vec2& p0, const tcu::Vec2& p1)
105 {
106 const tcu::Vec2 pixelCenterPosition = tcu::Vec2((float)pixel.x() + 0.5f, (float)pixel.y() + 0.5f);
107
108 // "Near" = Distance from the line to the pixel is less than 2 * pixel_max_radius. (pixel_max_radius = sqrt(2) / 2)
109 const float maxPixelDistance = 1.414f;
110 const float maxPixelDistanceSquared = 2.0f;
111
112 // Near the line
113 {
114 const tcu::Vec2 line = p1 - p0;
115 const tcu::Vec2 v = pixelCenterPosition - p0;
116 const float crossProduct = (line.x() * v.y() - line.y() * v.x());
117
118 // distance to line: (line x v) / |line|
119 // |(line x v) / |line|| > maxPixelDistance
120 // ==> (line x v)^2 / |line|^2 > maxPixelDistance^2
121 // ==> (line x v)^2 > maxPixelDistance^2 * |line|^2
122
123 if (crossProduct * crossProduct > maxPixelDistanceSquared * tcu::lengthSquared(line))
124 return false;
125 }
126
127 // Between the endpoints
128 {
129 // distance from line endpoint 1 to pixel is less than line length + maxPixelDistance
130 const float maxDistance = tcu::length(p1 - p0) + maxPixelDistance;
131
132 if (tcu::length(pixelCenterPosition - p0) > maxDistance)
133 return false;
134 if (tcu::length(pixelCenterPosition - p1) > maxDistance)
135 return false;
136 }
137
138 return true;
139 }
140
pixelOnlyOnASharedEdge(const tcu::IVec2 & pixel,const TriangleSceneSpec::SceneTriangle & triangle,const tcu::IVec2 & viewportSize)141 bool pixelOnlyOnASharedEdge (const tcu::IVec2& pixel, const TriangleSceneSpec::SceneTriangle& triangle, const tcu::IVec2& viewportSize)
142 {
143 if (triangle.sharedEdge[0] || triangle.sharedEdge[1] || triangle.sharedEdge[2])
144 {
145 const tcu::Vec2 triangleNormalizedDeviceSpace[3] =
146 {
147 tcu::Vec2(triangle.positions[0].x() / triangle.positions[0].w(), triangle.positions[0].y() / triangle.positions[0].w()),
148 tcu::Vec2(triangle.positions[1].x() / triangle.positions[1].w(), triangle.positions[1].y() / triangle.positions[1].w()),
149 tcu::Vec2(triangle.positions[2].x() / triangle.positions[2].w(), triangle.positions[2].y() / triangle.positions[2].w()),
150 };
151 const tcu::Vec2 triangleScreenSpace[3] =
152 {
153 (triangleNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
154 (triangleNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
155 (triangleNormalizedDeviceSpace[2] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
156 };
157
158 const bool pixelOnEdge0 = pixelNearLineSegment(pixel, triangleScreenSpace[0], triangleScreenSpace[1]);
159 const bool pixelOnEdge1 = pixelNearLineSegment(pixel, triangleScreenSpace[1], triangleScreenSpace[2]);
160 const bool pixelOnEdge2 = pixelNearLineSegment(pixel, triangleScreenSpace[2], triangleScreenSpace[0]);
161
162 // If the pixel is on a multiple edges return false
163
164 if (pixelOnEdge0 && !pixelOnEdge1 && !pixelOnEdge2)
165 return triangle.sharedEdge[0];
166 if (!pixelOnEdge0 && pixelOnEdge1 && !pixelOnEdge2)
167 return triangle.sharedEdge[1];
168 if (!pixelOnEdge0 && !pixelOnEdge1 && pixelOnEdge2)
169 return triangle.sharedEdge[2];
170 }
171
172 return false;
173 }
174
triangleArea(const tcu::Vec2 & s0,const tcu::Vec2 & s1,const tcu::Vec2 & s2)175 float triangleArea (const tcu::Vec2& s0, const tcu::Vec2& s1, const tcu::Vec2& s2)
176 {
177 const tcu::Vec2 u (s1.x() - s0.x(), s1.y() - s0.y());
178 const tcu::Vec2 v (s2.x() - s0.x(), s2.y() - s0.y());
179 const float crossProduct = (u.x() * v.y() - u.y() * v.x());
180
181 return crossProduct / 2.0f;
182 }
183
getTriangleAABB(const TriangleSceneSpec::SceneTriangle & triangle,const tcu::IVec2 & viewportSize)184 tcu::IVec4 getTriangleAABB (const TriangleSceneSpec::SceneTriangle& triangle, const tcu::IVec2& viewportSize)
185 {
186 const tcu::Vec2 normalizedDeviceSpace[3] =
187 {
188 tcu::Vec2(triangle.positions[0].x() / triangle.positions[0].w(), triangle.positions[0].y() / triangle.positions[0].w()),
189 tcu::Vec2(triangle.positions[1].x() / triangle.positions[1].w(), triangle.positions[1].y() / triangle.positions[1].w()),
190 tcu::Vec2(triangle.positions[2].x() / triangle.positions[2].w(), triangle.positions[2].y() / triangle.positions[2].w()),
191 };
192 const tcu::Vec2 screenSpace[3] =
193 {
194 (normalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
195 (normalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
196 (normalizedDeviceSpace[2] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
197 };
198
199 tcu::IVec4 aabb;
200
201 aabb.x() = (int)deFloatFloor(de::min(de::min(screenSpace[0].x(), screenSpace[1].x()), screenSpace[2].x()));
202 aabb.y() = (int)deFloatFloor(de::min(de::min(screenSpace[0].y(), screenSpace[1].y()), screenSpace[2].y()));
203 aabb.z() = (int)deFloatCeil (de::max(de::max(screenSpace[0].x(), screenSpace[1].x()), screenSpace[2].x()));
204 aabb.w() = (int)deFloatCeil (de::max(de::max(screenSpace[0].y(), screenSpace[1].y()), screenSpace[2].y()));
205
206 return aabb;
207 }
208
getExponentEpsilonFromULP(int valueExponent,deUint32 ulp)209 float getExponentEpsilonFromULP (int valueExponent, deUint32 ulp)
210 {
211 DE_ASSERT(ulp < (1u<<10));
212
213 // assume mediump precision, using ulp as ulps in a 10 bit mantissa
214 return tcu::Float32::construct(+1, valueExponent, (1u<<23) + (ulp << (23 - 10))).asFloat() - tcu::Float32::construct(+1, valueExponent, (1u<<23)).asFloat();
215 }
216
getValueEpsilonFromULP(float value,deUint32 ulp)217 float getValueEpsilonFromULP (float value, deUint32 ulp)
218 {
219 DE_ASSERT(value != std::numeric_limits<float>::infinity() && value != -std::numeric_limits<float>::infinity());
220
221 const int exponent = tcu::Float32(value).exponent();
222 return getExponentEpsilonFromULP(exponent, ulp);
223 }
224
getMaxValueWithinError(float value,deUint32 ulp)225 float getMaxValueWithinError (float value, deUint32 ulp)
226 {
227 if (value == std::numeric_limits<float>::infinity() || value == -std::numeric_limits<float>::infinity())
228 return value;
229
230 return value + getValueEpsilonFromULP(value, ulp);
231 }
232
getMinValueWithinError(float value,deUint32 ulp)233 float getMinValueWithinError (float value, deUint32 ulp)
234 {
235 if (value == std::numeric_limits<float>::infinity() || value == -std::numeric_limits<float>::infinity())
236 return value;
237
238 return value - getValueEpsilonFromULP(value, ulp);
239 }
240
getMinFlushToZero(float value)241 float getMinFlushToZero (float value)
242 {
243 // flush to zero if that decreases the value
244 // assume mediump precision
245 if (value > 0.0f && value < tcu::Float32::construct(+1, -14, 1u<<23).asFloat())
246 return 0.0f;
247 return value;
248 }
249
getMaxFlushToZero(float value)250 float getMaxFlushToZero (float value)
251 {
252 // flush to zero if that increases the value
253 // assume mediump precision
254 if (value < 0.0f && value > tcu::Float32::construct(-1, -14, 1u<<23).asFloat())
255 return 0.0f;
256 return value;
257 }
258
convertRGB8ToNativeFormat(const tcu::RGBA & color,const RasterizationArguments & args)259 tcu::IVec3 convertRGB8ToNativeFormat (const tcu::RGBA& color, const RasterizationArguments& args)
260 {
261 tcu::IVec3 pixelNativeColor;
262
263 for (int channelNdx = 0; channelNdx < 3; ++channelNdx)
264 {
265 const int channelBitCount = (channelNdx == 0) ? (args.redBits) : (channelNdx == 1) ? (args.greenBits) : (args.blueBits);
266 const int channelPixelValue = (channelNdx == 0) ? (color.getRed()) : (channelNdx == 1) ? (color.getGreen()) : (color.getBlue());
267
268 if (channelBitCount <= 8)
269 pixelNativeColor[channelNdx] = channelPixelValue >> (8 - channelBitCount);
270 else if (channelBitCount == 8)
271 pixelNativeColor[channelNdx] = channelPixelValue;
272 else
273 {
274 // just in case someone comes up with 8+ bits framebuffers pixel formats. But as
275 // we can only read in rgba8, we have to guess the trailing bits. Guessing 0.
276 pixelNativeColor[channelNdx] = channelPixelValue << (channelBitCount - 8);
277 }
278 }
279
280 return pixelNativeColor;
281 }
282
283 /*--------------------------------------------------------------------*//*!
284 * Returns the maximum value of x / y, where x c [minDividend, maxDividend]
285 * and y c [minDivisor, maxDivisor]
286 *//*--------------------------------------------------------------------*/
maximalRangeDivision(float minDividend,float maxDividend,float minDivisor,float maxDivisor)287 float maximalRangeDivision (float minDividend, float maxDividend, float minDivisor, float maxDivisor)
288 {
289 DE_ASSERT(minDividend <= maxDividend);
290 DE_ASSERT(minDivisor <= maxDivisor);
291
292 // special cases
293 if (minDividend == 0.0f && maxDividend == 0.0f)
294 return 0.0f;
295 if (minDivisor <= 0.0f && maxDivisor >= 0.0f)
296 return std::numeric_limits<float>::infinity();
297
298 return de::max(de::max(minDividend / minDivisor, minDividend / maxDivisor), de::max(maxDividend / minDivisor, maxDividend / maxDivisor));
299 }
300
301 /*--------------------------------------------------------------------*//*!
302 * Returns the minimum value of x / y, where x c [minDividend, maxDividend]
303 * and y c [minDivisor, maxDivisor]
304 *//*--------------------------------------------------------------------*/
minimalRangeDivision(float minDividend,float maxDividend,float minDivisor,float maxDivisor)305 float minimalRangeDivision (float minDividend, float maxDividend, float minDivisor, float maxDivisor)
306 {
307 DE_ASSERT(minDividend <= maxDividend);
308 DE_ASSERT(minDivisor <= maxDivisor);
309
310 // special cases
311 if (minDividend == 0.0f && maxDividend == 0.0f)
312 return 0.0f;
313 if (minDivisor <= 0.0f && maxDivisor >= 0.0f)
314 return -std::numeric_limits<float>::infinity();
315
316 return de::min(de::min(minDividend / minDivisor, minDividend / maxDivisor), de::min(maxDividend / minDivisor, maxDividend / maxDivisor));
317 }
318
isLineXMajor(const tcu::Vec2 & lineScreenSpaceP0,const tcu::Vec2 & lineScreenSpaceP1)319 static bool isLineXMajor (const tcu::Vec2& lineScreenSpaceP0, const tcu::Vec2& lineScreenSpaceP1)
320 {
321 return de::abs(lineScreenSpaceP1.x() - lineScreenSpaceP0.x()) >= de::abs(lineScreenSpaceP1.y() - lineScreenSpaceP0.y());
322 }
323
isPackedSSLineXMajor(const tcu::Vec4 & packedLine)324 static bool isPackedSSLineXMajor (const tcu::Vec4& packedLine)
325 {
326 const tcu::Vec2 lineScreenSpaceP0 = packedLine.swizzle(0, 1);
327 const tcu::Vec2 lineScreenSpaceP1 = packedLine.swizzle(2, 3);
328
329 return isLineXMajor(lineScreenSpaceP0, lineScreenSpaceP1);
330 }
331
332 struct InterpolationRange
333 {
334 tcu::Vec3 max;
335 tcu::Vec3 min;
336 };
337
338 struct LineInterpolationRange
339 {
340 tcu::Vec2 max;
341 tcu::Vec2 min;
342 };
343
calcTriangleInterpolationWeights(const tcu::Vec4 & p0,const tcu::Vec4 & p1,const tcu::Vec4 & p2,const tcu::Vec2 & ndpixel)344 InterpolationRange calcTriangleInterpolationWeights (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec4& p2, const tcu::Vec2& ndpixel)
345 {
346 const int roundError = 1;
347 const int barycentricError = 3;
348 const int divError = 8;
349
350 const tcu::Vec2 nd0 = p0.swizzle(0, 1) / p0.w();
351 const tcu::Vec2 nd1 = p1.swizzle(0, 1) / p1.w();
352 const tcu::Vec2 nd2 = p2.swizzle(0, 1) / p2.w();
353
354 const float ka = triangleArea(ndpixel, nd1, nd2);
355 const float kb = triangleArea(ndpixel, nd2, nd0);
356 const float kc = triangleArea(ndpixel, nd0, nd1);
357
358 const float kaMax = getMaxFlushToZero(getMaxValueWithinError(ka, barycentricError));
359 const float kbMax = getMaxFlushToZero(getMaxValueWithinError(kb, barycentricError));
360 const float kcMax = getMaxFlushToZero(getMaxValueWithinError(kc, barycentricError));
361 const float kaMin = getMinFlushToZero(getMinValueWithinError(ka, barycentricError));
362 const float kbMin = getMinFlushToZero(getMinValueWithinError(kb, barycentricError));
363 const float kcMin = getMinFlushToZero(getMinValueWithinError(kc, barycentricError));
364 DE_ASSERT(kaMin <= kaMax);
365 DE_ASSERT(kbMin <= kbMax);
366 DE_ASSERT(kcMin <= kcMax);
367
368 // calculate weights: vec3(ka / p0.w, kb / p1.w, kc / p2.w) / (ka / p0.w + kb / p1.w + kc / p2.w)
369 const float maxPreDivisionValues[3] =
370 {
371 getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(kaMax / p0.w()), divError)),
372 getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(kbMax / p1.w()), divError)),
373 getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(kcMax / p2.w()), divError)),
374 };
375 const float minPreDivisionValues[3] =
376 {
377 getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(kaMin / p0.w()), divError)),
378 getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(kbMin / p1.w()), divError)),
379 getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(kcMin / p2.w()), divError)),
380 };
381 DE_ASSERT(minPreDivisionValues[0] <= maxPreDivisionValues[0]);
382 DE_ASSERT(minPreDivisionValues[1] <= maxPreDivisionValues[1]);
383 DE_ASSERT(minPreDivisionValues[2] <= maxPreDivisionValues[2]);
384
385 const float maxDivisor = getMaxFlushToZero(getMaxValueWithinError(maxPreDivisionValues[0] + maxPreDivisionValues[1] + maxPreDivisionValues[2], 2*roundError));
386 const float minDivisor = getMinFlushToZero(getMinValueWithinError(minPreDivisionValues[0] + minPreDivisionValues[1] + minPreDivisionValues[2], 2*roundError));
387 DE_ASSERT(minDivisor <= maxDivisor);
388
389 InterpolationRange returnValue;
390
391 returnValue.max.x() = getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(maximalRangeDivision(minPreDivisionValues[0], maxPreDivisionValues[0], minDivisor, maxDivisor)), divError));
392 returnValue.max.y() = getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(maximalRangeDivision(minPreDivisionValues[1], maxPreDivisionValues[1], minDivisor, maxDivisor)), divError));
393 returnValue.max.z() = getMaxFlushToZero(getMaxValueWithinError(getMaxFlushToZero(maximalRangeDivision(minPreDivisionValues[2], maxPreDivisionValues[2], minDivisor, maxDivisor)), divError));
394 returnValue.min.x() = getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(minimalRangeDivision(minPreDivisionValues[0], maxPreDivisionValues[0], minDivisor, maxDivisor)), divError));
395 returnValue.min.y() = getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(minimalRangeDivision(minPreDivisionValues[1], maxPreDivisionValues[1], minDivisor, maxDivisor)), divError));
396 returnValue.min.z() = getMinFlushToZero(getMinValueWithinError(getMinFlushToZero(minimalRangeDivision(minPreDivisionValues[2], maxPreDivisionValues[2], minDivisor, maxDivisor)), divError));
397
398 DE_ASSERT(returnValue.min.x() <= returnValue.max.x());
399 DE_ASSERT(returnValue.min.y() <= returnValue.max.y());
400 DE_ASSERT(returnValue.min.z() <= returnValue.max.z());
401
402 return returnValue;
403 }
404
calcLineInterpolationWeights(const tcu::Vec2 & pa,float wa,const tcu::Vec2 & pb,float wb,const tcu::Vec2 & pr)405 LineInterpolationRange calcLineInterpolationWeights (const tcu::Vec2& pa, float wa, const tcu::Vec2& pb, float wb, const tcu::Vec2& pr)
406 {
407 const int roundError = 1;
408 const int divError = 3;
409
410 // calc weights:
411 // (1-t) / wa t / wb
412 // ------------------- , -------------------
413 // (1-t) / wa + t / wb (1-t) / wa + t / wb
414
415 // Allow 1 ULP
416 const float dividend = tcu::dot(pr - pa, pb - pa);
417 const float dividendMax = getMaxValueWithinError(dividend, 1);
418 const float dividendMin = getMinValueWithinError(dividend, 1);
419 DE_ASSERT(dividendMin <= dividendMax);
420
421 // Assuming lengthSquared will not be implemented as sqrt(x)^2, allow 1 ULP
422 const float divisor = tcu::lengthSquared(pb - pa);
423 const float divisorMax = getMaxValueWithinError(divisor, 1);
424 const float divisorMin = getMinValueWithinError(divisor, 1);
425 DE_ASSERT(divisorMin <= divisorMax);
426
427 // Allow 3 ULP precision for division
428 const float tMax = getMaxValueWithinError(maximalRangeDivision(dividendMin, dividendMax, divisorMin, divisorMax), divError);
429 const float tMin = getMinValueWithinError(minimalRangeDivision(dividendMin, dividendMax, divisorMin, divisorMax), divError);
430 DE_ASSERT(tMin <= tMax);
431
432 const float perspectiveTMax = getMaxValueWithinError(maximalRangeDivision(tMin, tMax, wb, wb), divError);
433 const float perspectiveTMin = getMinValueWithinError(minimalRangeDivision(tMin, tMax, wb, wb), divError);
434 DE_ASSERT(perspectiveTMin <= perspectiveTMax);
435
436 const float perspectiveInvTMax = getMaxValueWithinError(maximalRangeDivision((1.0f - tMax), (1.0f - tMin), wa, wa), divError);
437 const float perspectiveInvTMin = getMinValueWithinError(minimalRangeDivision((1.0f - tMax), (1.0f - tMin), wa, wa), divError);
438 DE_ASSERT(perspectiveInvTMin <= perspectiveInvTMax);
439
440 const float perspectiveDivisorMax = getMaxValueWithinError(perspectiveTMax + perspectiveInvTMax, roundError);
441 const float perspectiveDivisorMin = getMinValueWithinError(perspectiveTMin + perspectiveInvTMin, roundError);
442 DE_ASSERT(perspectiveDivisorMin <= perspectiveDivisorMax);
443
444 LineInterpolationRange returnValue;
445 returnValue.max.x() = getMaxValueWithinError(maximalRangeDivision(perspectiveInvTMin, perspectiveInvTMax, perspectiveDivisorMin, perspectiveDivisorMax), divError);
446 returnValue.max.y() = getMaxValueWithinError(maximalRangeDivision(perspectiveTMin, perspectiveTMax, perspectiveDivisorMin, perspectiveDivisorMax), divError);
447 returnValue.min.x() = getMinValueWithinError(minimalRangeDivision(perspectiveInvTMin, perspectiveInvTMax, perspectiveDivisorMin, perspectiveDivisorMax), divError);
448 returnValue.min.y() = getMinValueWithinError(minimalRangeDivision(perspectiveTMin, perspectiveTMax, perspectiveDivisorMin, perspectiveDivisorMax), divError);
449
450 DE_ASSERT(returnValue.min.x() <= returnValue.max.x());
451 DE_ASSERT(returnValue.min.y() <= returnValue.max.y());
452
453 return returnValue;
454 }
455
calcLineInterpolationWeightsAxisProjected(const tcu::Vec2 & pa,float wa,const tcu::Vec2 & pb,float wb,const tcu::Vec2 & pr)456 LineInterpolationRange calcLineInterpolationWeightsAxisProjected (const tcu::Vec2& pa, float wa, const tcu::Vec2& pb, float wb, const tcu::Vec2& pr)
457 {
458 const int roundError = 1;
459 const int divError = 3;
460 const bool isXMajor = isLineXMajor(pa, pb);
461 const int majorAxisNdx = (isXMajor) ? (0) : (1);
462
463 // calc weights:
464 // (1-t) / wa t / wb
465 // ------------------- , -------------------
466 // (1-t) / wa + t / wb (1-t) / wa + t / wb
467
468 // Use axis projected (inaccurate) method, i.e. for X-major lines:
469 // (xd - xa) * (xb - xa) xd - xa
470 // t = --------------------- == -------
471 // ( xb - xa ) ^ 2 xb - xa
472
473 // Allow 1 ULP
474 const float dividend = (pr[majorAxisNdx] - pa[majorAxisNdx]);
475 const float dividendMax = getMaxValueWithinError(dividend, 1);
476 const float dividendMin = getMinValueWithinError(dividend, 1);
477 DE_ASSERT(dividendMin <= dividendMax);
478
479 // Allow 1 ULP
480 const float divisor = (pb[majorAxisNdx] - pa[majorAxisNdx]);
481 const float divisorMax = getMaxValueWithinError(divisor, 1);
482 const float divisorMin = getMinValueWithinError(divisor, 1);
483 DE_ASSERT(divisorMin <= divisorMax);
484
485 // Allow 3 ULP precision for division
486 const float tMax = getMaxValueWithinError(maximalRangeDivision(dividendMin, dividendMax, divisorMin, divisorMax), divError);
487 const float tMin = getMinValueWithinError(minimalRangeDivision(dividendMin, dividendMax, divisorMin, divisorMax), divError);
488 DE_ASSERT(tMin <= tMax);
489
490 const float perspectiveTMax = getMaxValueWithinError(maximalRangeDivision(tMin, tMax, wb, wb), divError);
491 const float perspectiveTMin = getMinValueWithinError(minimalRangeDivision(tMin, tMax, wb, wb), divError);
492 DE_ASSERT(perspectiveTMin <= perspectiveTMax);
493
494 const float perspectiveInvTMax = getMaxValueWithinError(maximalRangeDivision((1.0f - tMax), (1.0f - tMin), wa, wa), divError);
495 const float perspectiveInvTMin = getMinValueWithinError(minimalRangeDivision((1.0f - tMax), (1.0f - tMin), wa, wa), divError);
496 DE_ASSERT(perspectiveInvTMin <= perspectiveInvTMax);
497
498 const float perspectiveDivisorMax = getMaxValueWithinError(perspectiveTMax + perspectiveInvTMax, roundError);
499 const float perspectiveDivisorMin = getMinValueWithinError(perspectiveTMin + perspectiveInvTMin, roundError);
500 DE_ASSERT(perspectiveDivisorMin <= perspectiveDivisorMax);
501
502 LineInterpolationRange returnValue;
503 returnValue.max.x() = getMaxValueWithinError(maximalRangeDivision(perspectiveInvTMin, perspectiveInvTMax, perspectiveDivisorMin, perspectiveDivisorMax), divError);
504 returnValue.max.y() = getMaxValueWithinError(maximalRangeDivision(perspectiveTMin, perspectiveTMax, perspectiveDivisorMin, perspectiveDivisorMax), divError);
505 returnValue.min.x() = getMinValueWithinError(minimalRangeDivision(perspectiveInvTMin, perspectiveInvTMax, perspectiveDivisorMin, perspectiveDivisorMax), divError);
506 returnValue.min.y() = getMinValueWithinError(minimalRangeDivision(perspectiveTMin, perspectiveTMax, perspectiveDivisorMin, perspectiveDivisorMax), divError);
507
508 DE_ASSERT(returnValue.min.x() <= returnValue.max.x());
509 DE_ASSERT(returnValue.min.y() <= returnValue.max.y());
510
511 return returnValue;
512 }
513
514 template <typename WeightEquation>
calcSingleSampleLineInterpolationRangeWithWeightEquation(const tcu::Vec2 & pa,float wa,const tcu::Vec2 & pb,float wb,const tcu::IVec2 & pixel,int subpixelBits,WeightEquation weightEquation)515 LineInterpolationRange calcSingleSampleLineInterpolationRangeWithWeightEquation (const tcu::Vec2& pa,
516 float wa,
517 const tcu::Vec2& pb,
518 float wb,
519 const tcu::IVec2& pixel,
520 int subpixelBits,
521 WeightEquation weightEquation)
522 {
523 // allow interpolation weights anywhere in the central subpixels
524 const float testSquareSize = (2.0f / (float)(1UL << subpixelBits));
525 const float testSquarePos = (0.5f - testSquareSize / 2);
526
527 const tcu::Vec2 corners[4] =
528 {
529 tcu::Vec2((float)pixel.x() + testSquarePos + 0.0f, (float)pixel.y() + testSquarePos + 0.0f),
530 tcu::Vec2((float)pixel.x() + testSquarePos + 0.0f, (float)pixel.y() + testSquarePos + testSquareSize),
531 tcu::Vec2((float)pixel.x() + testSquarePos + testSquareSize, (float)pixel.y() + testSquarePos + testSquareSize),
532 tcu::Vec2((float)pixel.x() + testSquarePos + testSquareSize, (float)pixel.y() + testSquarePos + 0.0f),
533 };
534
535 // calculate interpolation as a line
536 const LineInterpolationRange weights[4] =
537 {
538 weightEquation(pa, wa, pb, wb, corners[0]),
539 weightEquation(pa, wa, pb, wb, corners[1]),
540 weightEquation(pa, wa, pb, wb, corners[2]),
541 weightEquation(pa, wa, pb, wb, corners[3]),
542 };
543
544 const tcu::Vec2 minWeights = tcu::min(tcu::min(weights[0].min, weights[1].min), tcu::min(weights[2].min, weights[3].min));
545 const tcu::Vec2 maxWeights = tcu::max(tcu::max(weights[0].max, weights[1].max), tcu::max(weights[2].max, weights[3].max));
546
547 LineInterpolationRange result;
548 result.min = minWeights;
549 result.max = maxWeights;
550 return result;
551 }
552
calcSingleSampleLineInterpolationRange(const tcu::Vec2 & pa,float wa,const tcu::Vec2 & pb,float wb,const tcu::IVec2 & pixel,int subpixelBits)553 LineInterpolationRange calcSingleSampleLineInterpolationRange (const tcu::Vec2& pa, float wa, const tcu::Vec2& pb, float wb, const tcu::IVec2& pixel, int subpixelBits)
554 {
555 return calcSingleSampleLineInterpolationRangeWithWeightEquation(pa, wa, pb, wb, pixel, subpixelBits, calcLineInterpolationWeights);
556 }
557
calcSingleSampleLineInterpolationRangeAxisProjected(const tcu::Vec2 & pa,float wa,const tcu::Vec2 & pb,float wb,const tcu::IVec2 & pixel,int subpixelBits)558 LineInterpolationRange calcSingleSampleLineInterpolationRangeAxisProjected (const tcu::Vec2& pa, float wa, const tcu::Vec2& pb, float wb, const tcu::IVec2& pixel, int subpixelBits)
559 {
560 return calcSingleSampleLineInterpolationRangeWithWeightEquation(pa, wa, pb, wb, pixel, subpixelBits, calcLineInterpolationWeightsAxisProjected);
561 }
562
563 struct TriangleInterpolator
564 {
565 const TriangleSceneSpec& scene;
566
TriangleInterpolatortcu::__anone1fc38670111::TriangleInterpolator567 TriangleInterpolator (const TriangleSceneSpec& scene_)
568 : scene(scene_)
569 {
570 }
571
interpolatetcu::__anone1fc38670111::TriangleInterpolator572 InterpolationRange interpolate (int primitiveNdx, const tcu::IVec2 pixel, const tcu::IVec2 viewportSize, bool multisample, int subpixelBits) const
573 {
574 // allow anywhere in the pixel area in multisample
575 // allow only in the center subpixels (4 subpixels) in singlesample
576 const float testSquareSize = (multisample) ? (1.0f) : (2.0f / (float)(1UL << subpixelBits));
577 const float testSquarePos = (multisample) ? (0.0f) : (0.5f - testSquareSize / 2);
578 const tcu::Vec2 corners[4] =
579 {
580 tcu::Vec2(((float)pixel.x() + testSquarePos + 0.0f) / (float)viewportSize.x() * 2.0f - 1.0f, ((float)pixel.y() + testSquarePos + 0.0f ) / (float)viewportSize.y() * 2.0f - 1.0f),
581 tcu::Vec2(((float)pixel.x() + testSquarePos + 0.0f) / (float)viewportSize.x() * 2.0f - 1.0f, ((float)pixel.y() + testSquarePos + testSquareSize) / (float)viewportSize.y() * 2.0f - 1.0f),
582 tcu::Vec2(((float)pixel.x() + testSquarePos + testSquareSize) / (float)viewportSize.x() * 2.0f - 1.0f, ((float)pixel.y() + testSquarePos + testSquareSize) / (float)viewportSize.y() * 2.0f - 1.0f),
583 tcu::Vec2(((float)pixel.x() + testSquarePos + testSquareSize) / (float)viewportSize.x() * 2.0f - 1.0f, ((float)pixel.y() + testSquarePos + 0.0f ) / (float)viewportSize.y() * 2.0f - 1.0f),
584 };
585 const InterpolationRange weights[4] =
586 {
587 calcTriangleInterpolationWeights(scene.triangles[primitiveNdx].positions[0], scene.triangles[primitiveNdx].positions[1], scene.triangles[primitiveNdx].positions[2], corners[0]),
588 calcTriangleInterpolationWeights(scene.triangles[primitiveNdx].positions[0], scene.triangles[primitiveNdx].positions[1], scene.triangles[primitiveNdx].positions[2], corners[1]),
589 calcTriangleInterpolationWeights(scene.triangles[primitiveNdx].positions[0], scene.triangles[primitiveNdx].positions[1], scene.triangles[primitiveNdx].positions[2], corners[2]),
590 calcTriangleInterpolationWeights(scene.triangles[primitiveNdx].positions[0], scene.triangles[primitiveNdx].positions[1], scene.triangles[primitiveNdx].positions[2], corners[3]),
591 };
592
593 InterpolationRange result;
594 result.min = tcu::min(tcu::min(weights[0].min, weights[1].min), tcu::min(weights[2].min, weights[3].min));
595 result.max = tcu::max(tcu::max(weights[0].max, weights[1].max), tcu::max(weights[2].max, weights[3].max));
596 return result;
597 }
598 };
599
600 /*--------------------------------------------------------------------*//*!
601 * Used only by verifyMultisampleLineGroupInterpolation to calculate
602 * correct line interpolations for the triangulated lines.
603 *//*--------------------------------------------------------------------*/
604 struct MultisampleLineInterpolator
605 {
606 const LineSceneSpec& scene;
607
MultisampleLineInterpolatortcu::__anone1fc38670111::MultisampleLineInterpolator608 MultisampleLineInterpolator (const LineSceneSpec& scene_)
609 : scene(scene_)
610 {
611 }
612
interpolatetcu::__anone1fc38670111::MultisampleLineInterpolator613 InterpolationRange interpolate (int primitiveNdx, const tcu::IVec2 pixel, const tcu::IVec2 viewportSize, bool multisample, int subpixelBits) const
614 {
615 DE_UNREF(multisample);
616 DE_UNREF(subpixelBits);
617
618 // in triangulation, one line emits two triangles
619 const int lineNdx = primitiveNdx / 2;
620
621 // allow interpolation weights anywhere in the pixel
622 const tcu::Vec2 corners[4] =
623 {
624 tcu::Vec2((float)pixel.x() + 0.0f, (float)pixel.y() + 0.0f),
625 tcu::Vec2((float)pixel.x() + 0.0f, (float)pixel.y() + 1.0f),
626 tcu::Vec2((float)pixel.x() + 1.0f, (float)pixel.y() + 1.0f),
627 tcu::Vec2((float)pixel.x() + 1.0f, (float)pixel.y() + 0.0f),
628 };
629
630 const float wa = scene.lines[lineNdx].positions[0].w();
631 const float wb = scene.lines[lineNdx].positions[1].w();
632 const tcu::Vec2 pa = tcu::Vec2((scene.lines[lineNdx].positions[0].x() / wa + 1.0f) * 0.5f * (float)viewportSize.x(),
633 (scene.lines[lineNdx].positions[0].y() / wa + 1.0f) * 0.5f * (float)viewportSize.y());
634 const tcu::Vec2 pb = tcu::Vec2((scene.lines[lineNdx].positions[1].x() / wb + 1.0f) * 0.5f * (float)viewportSize.x(),
635 (scene.lines[lineNdx].positions[1].y() / wb + 1.0f) * 0.5f * (float)viewportSize.y());
636
637 // calculate interpolation as a line
638 const LineInterpolationRange weights[4] =
639 {
640 calcLineInterpolationWeights(pa, wa, pb, wb, corners[0]),
641 calcLineInterpolationWeights(pa, wa, pb, wb, corners[1]),
642 calcLineInterpolationWeights(pa, wa, pb, wb, corners[2]),
643 calcLineInterpolationWeights(pa, wa, pb, wb, corners[3]),
644 };
645
646 const tcu::Vec2 minWeights = tcu::min(tcu::min(weights[0].min, weights[1].min), tcu::min(weights[2].min, weights[3].min));
647 const tcu::Vec2 maxWeights = tcu::max(tcu::max(weights[0].max, weights[1].max), tcu::max(weights[2].max, weights[3].max));
648
649 // convert to three-component form. For all triangles, the vertex 0 is always emitted by the line starting point, and vertex 2 by the ending point
650 InterpolationRange result;
651 result.min = tcu::Vec3(minWeights.x(), 0.0f, minWeights.y());
652 result.max = tcu::Vec3(maxWeights.x(), 0.0f, maxWeights.y());
653 return result;
654 }
655 };
656
657 template <typename Interpolator>
verifyTriangleGroupInterpolationWithInterpolator(const tcu::Surface & surface,const TriangleSceneSpec & scene,const RasterizationArguments & args,VerifyTriangleGroupInterpolationLogStash & logStash,const Interpolator & interpolator)658 bool verifyTriangleGroupInterpolationWithInterpolator (const tcu::Surface& surface,
659 const TriangleSceneSpec& scene,
660 const RasterizationArguments& args,
661 VerifyTriangleGroupInterpolationLogStash& logStash,
662 const Interpolator& interpolator)
663 {
664 const tcu::RGBA invalidPixelColor = tcu::RGBA(255, 0, 0, 255);
665 const bool multisampled = (args.numSamples != 0);
666 const tcu::IVec2 viewportSize = tcu::IVec2(surface.getWidth(), surface.getHeight());
667 const int errorFloodThreshold = 4;
668 int errorCount = 0;
669 int invalidPixels = 0;
670 int subPixelBits = args.subpixelBits;
671 tcu::Surface errorMask (surface.getWidth(), surface.getHeight());
672
673 tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
674
675 // log format
676
677 logStash.messages.push_back(std::string("Verifying rasterization result. Native format is RGB" + de::toString(args.redBits) + de::toString(args.greenBits) + de::toString(args.blueBits)));
678 if (args.redBits > 8 || args.greenBits > 8 || args.blueBits > 8)
679 logStash.messages.push_back(std::string("Warning! More than 8 bits in a color channel, this may produce false negatives."));
680
681 // subpixel bits in a valid range?
682
683 if (subPixelBits < 0)
684 {
685 logStash.messages.push_back(std::string("Invalid subpixel count (" + de::toString(subPixelBits) + "), assuming 0"));
686 subPixelBits = 0;
687 }
688 else if (subPixelBits > 16)
689 {
690 // At high subpixel bit counts we might overflow. Checking at lower bit count is ok, but is less strict
691 logStash.messages.push_back(std::string("Subpixel count is greater than 16 (" + de::toString(subPixelBits) + ")."
692 " Checking results using less strict 16 bit requirements. This may produce false positives."));
693 subPixelBits = 16;
694 }
695
696 // check pixels
697
698 for (int y = 0; y < surface.getHeight(); ++y)
699 for (int x = 0; x < surface.getWidth(); ++x)
700 {
701 const tcu::RGBA color = surface.getPixel(x, y);
702 bool stackBottomFound = false;
703 int stackSize = 0;
704 tcu::Vec4 colorStackMin;
705 tcu::Vec4 colorStackMax;
706
707 // Iterate triangle coverage front to back, find the stack of pontentially contributing fragments
708 for (int triNdx = (int)scene.triangles.size() - 1; triNdx >= 0; --triNdx)
709 {
710 const CoverageType coverage = calculateTriangleCoverage(scene.triangles[triNdx].positions[0],
711 scene.triangles[triNdx].positions[1],
712 scene.triangles[triNdx].positions[2],
713 tcu::IVec2(x, y),
714 viewportSize,
715 subPixelBits,
716 multisampled);
717
718 if (coverage == COVERAGE_FULL || coverage == COVERAGE_PARTIAL)
719 {
720 // potentially contributes to the result fragment's value
721 const InterpolationRange weights = interpolator.interpolate(triNdx, tcu::IVec2(x, y), viewportSize, multisampled, subPixelBits);
722
723 const tcu::Vec4 fragmentColorMax = de::clamp(weights.max.x(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[0] +
724 de::clamp(weights.max.y(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[1] +
725 de::clamp(weights.max.z(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[2];
726 const tcu::Vec4 fragmentColorMin = de::clamp(weights.min.x(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[0] +
727 de::clamp(weights.min.y(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[1] +
728 de::clamp(weights.min.z(), 0.0f, 1.0f) * scene.triangles[triNdx].colors[2];
729
730 if (stackSize++ == 0)
731 {
732 // first triangle, set the values properly
733 colorStackMin = fragmentColorMin;
734 colorStackMax = fragmentColorMax;
735 }
736 else
737 {
738 // contributing triangle
739 colorStackMin = tcu::min(colorStackMin, fragmentColorMin);
740 colorStackMax = tcu::max(colorStackMax, fragmentColorMax);
741 }
742
743 if (coverage == COVERAGE_FULL)
744 {
745 // loop terminates, this is the bottommost fragment
746 stackBottomFound = true;
747 break;
748 }
749 }
750 }
751
752 // Partial coverage == background may be visible
753 if (stackSize != 0 && !stackBottomFound)
754 {
755 stackSize++;
756 colorStackMin = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
757 }
758
759 // Is the result image color in the valid range.
760 if (stackSize == 0)
761 {
762 // No coverage, allow only background (black, value=0)
763 const tcu::IVec3 pixelNativeColor = convertRGB8ToNativeFormat(color, args);
764 const int threshold = 1;
765
766 if (pixelNativeColor.x() > threshold ||
767 pixelNativeColor.y() > threshold ||
768 pixelNativeColor.z() > threshold)
769 {
770 ++errorCount;
771
772 // don't fill the logs with too much data
773 if (errorCount < errorFloodThreshold)
774 {
775 std::ostringstream str;
776
777 str << "Found an invalid pixel at (" << x << "," << y << ")\n"
778 << "\tPixel color:\t\t" << color << "\n"
779 << "\tExpected background color.\n";
780
781 logStash.messages.push_back(str.str());
782 }
783
784 ++invalidPixels;
785 errorMask.setPixel(x, y, invalidPixelColor);
786 }
787 }
788 else
789 {
790 DE_ASSERT(stackSize);
791
792 // Each additional step in the stack may cause conversion error of 1 bit due to undefined rounding direction
793 const int thresholdRed = stackSize - 1;
794 const int thresholdGreen = stackSize - 1;
795 const int thresholdBlue = stackSize - 1;
796
797 const tcu::Vec3 valueRangeMin = tcu::Vec3(colorStackMin.xyz());
798 const tcu::Vec3 valueRangeMax = tcu::Vec3(colorStackMax.xyz());
799
800 const tcu::IVec3 formatLimit ((1 << args.redBits) - 1, (1 << args.greenBits) - 1, (1 << args.blueBits) - 1);
801 const tcu::Vec3 colorMinF (de::clamp(valueRangeMin.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
802 de::clamp(valueRangeMin.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
803 de::clamp(valueRangeMin.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
804 const tcu::Vec3 colorMaxF (de::clamp(valueRangeMax.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
805 de::clamp(valueRangeMax.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
806 de::clamp(valueRangeMax.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
807 const tcu::IVec3 colorMin ((int)deFloatFloor(colorMinF.x()),
808 (int)deFloatFloor(colorMinF.y()),
809 (int)deFloatFloor(colorMinF.z()));
810 const tcu::IVec3 colorMax ((int)deFloatCeil (colorMaxF.x()),
811 (int)deFloatCeil (colorMaxF.y()),
812 (int)deFloatCeil (colorMaxF.z()));
813
814 // Convert pixel color from rgba8 to the real pixel format. Usually rgba8 or 565
815 const tcu::IVec3 pixelNativeColor = convertRGB8ToNativeFormat(color, args);
816
817 // Validity check
818 if (pixelNativeColor.x() < colorMin.x() - thresholdRed ||
819 pixelNativeColor.y() < colorMin.y() - thresholdGreen ||
820 pixelNativeColor.z() < colorMin.z() - thresholdBlue ||
821 pixelNativeColor.x() > colorMax.x() + thresholdRed ||
822 pixelNativeColor.y() > colorMax.y() + thresholdGreen ||
823 pixelNativeColor.z() > colorMax.z() + thresholdBlue)
824 {
825 ++errorCount;
826
827 // don't fill the logs with too much data
828 if (errorCount <= errorFloodThreshold)
829 {
830 std::ostringstream str;
831
832 str << "Found an invalid pixel at (" << x << "," << y << ")\n"
833 << "\tPixel color:\t\t" << color << "\n"
834 << "\tNative color:\t\t" << pixelNativeColor << "\n"
835 << "\tAllowed error:\t\t" << tcu::IVec3(thresholdRed, thresholdGreen, thresholdBlue) << "\n"
836 << "\tReference native color min: " << tcu::clamp(colorMin - tcu::IVec3(thresholdRed, thresholdGreen, thresholdBlue), tcu::IVec3(0,0,0), formatLimit) << "\n"
837 << "\tReference native color max: " << tcu::clamp(colorMax + tcu::IVec3(thresholdRed, thresholdGreen, thresholdBlue), tcu::IVec3(0,0,0), formatLimit) << "\n"
838 << "\tReference native float min: " << tcu::clamp(colorMinF - tcu::IVec3(thresholdRed, thresholdGreen, thresholdBlue).cast<float>(), tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
839 << "\tReference native float max: " << tcu::clamp(colorMaxF + tcu::IVec3(thresholdRed, thresholdGreen, thresholdBlue).cast<float>(), tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
840 << "\tFmin:\t" << tcu::clamp(valueRangeMin, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n"
841 << "\tFmax:\t" << tcu::clamp(valueRangeMax, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n";
842 logStash.messages.push_back(str.str());
843 }
844
845 ++invalidPixels;
846 errorMask.setPixel(x, y, invalidPixelColor);
847 }
848 }
849 }
850
851 // don't just hide failures
852 if (errorCount > errorFloodThreshold)
853 logStash.messages.push_back(std::string("Omitted " + de::toString(errorCount - errorFloodThreshold) + " pixel error description(s)."));
854
855 logStash.success = (invalidPixels == 0);
856 logStash.invalidPixels = invalidPixels;
857
858 // report result
859 if (!logStash.success)
860 logStash.errorMask = errorMask;
861
862 return logStash.success;
863 }
864
865
calculateIntersectionParameter(const tcu::Vec2 line[2],float w,int componentNdx)866 float calculateIntersectionParameter (const tcu::Vec2 line[2], float w, int componentNdx)
867 {
868 DE_ASSERT(componentNdx < 2);
869 if (line[1][componentNdx] == line[0][componentNdx])
870 return -1.0f;
871
872 return (w - line[0][componentNdx]) / (line[1][componentNdx] - line[0][componentNdx]);
873 }
874
875 // Clips the given line with a ((-w, -w), (-w, w), (w, w), (w, -w)) rectangle
applyClippingBox(tcu::Vec2 line[2],float w)876 void applyClippingBox (tcu::Vec2 line[2], float w)
877 {
878 for (int side = 0; side < 4; ++side)
879 {
880 const int sign = ((side / 2) * -2) + 1;
881 const int component = side % 2;
882 const float t = calculateIntersectionParameter(line, w * (float)sign, component);
883
884 if ((t > 0) && (t < 1))
885 {
886 const float newCoord = t * line[1][1 - component] + (1 - t) * line[0][1 - component];
887
888 if (line[1][component] > (w * (float)sign))
889 {
890 line[1 - side / 2][component] = w * (float)sign;
891 line[1 - side / 2][1 - component] = newCoord;
892 }
893 else
894 {
895 line[side / 2][component] = w * (float)sign;
896 line[side / 2][1 - component] = newCoord;
897 }
898 }
899 }
900 }
901
902 enum ClipMode
903 {
904 CLIPMODE_NO_CLIPPING = 0,
905 CLIPMODE_USE_CLIPPING_BOX,
906
907 CLIPMODE_LAST
908 };
909
verifyMultisampleLineGroupRasterization(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log,ClipMode clipMode,VerifyTriangleGroupRasterizationLogStash * logStash,const bool vulkanLinesTest,const bool strictMode,const bool carryRemainder)910 bool verifyMultisampleLineGroupRasterization (const tcu::Surface& surface,
911 const LineSceneSpec& scene,
912 const RasterizationArguments& args,
913 tcu::TestLog& log,
914 ClipMode clipMode,
915 VerifyTriangleGroupRasterizationLogStash* logStash,
916 const bool vulkanLinesTest,
917 const bool strictMode,
918 const bool carryRemainder)
919 {
920 // Multisampled line == 2 triangles
921
922 const tcu::Vec2 viewportSize = tcu::Vec2((float)surface.getWidth(), (float)surface.getHeight());
923 const float halfLineWidth = scene.lineWidth * 0.5f;
924 TriangleSceneSpec triangleScene;
925
926 deUint32 stippleCounter = 0;
927 float leftoverPhase = 0.0f;
928
929 triangleScene.triangles.resize(2 * scene.lines.size());
930 for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
931 {
932
933 if (!scene.isStrip)
934 {
935 // reset stipple at the start of each line segment
936 stippleCounter = 0;
937 leftoverPhase = 0;
938 }
939
940 // Transform to screen space, add pixel offsets, convert back to normalized device space, and test as triangles
941 tcu::Vec2 lineNormalizedDeviceSpace[2] =
942 {
943 tcu::Vec2(scene.lines[lineNdx].positions[0].x() / scene.lines[lineNdx].positions[0].w(), scene.lines[lineNdx].positions[0].y() / scene.lines[lineNdx].positions[0].w()),
944 tcu::Vec2(scene.lines[lineNdx].positions[1].x() / scene.lines[lineNdx].positions[1].w(), scene.lines[lineNdx].positions[1].y() / scene.lines[lineNdx].positions[1].w()),
945 };
946
947 if (clipMode == CLIPMODE_USE_CLIPPING_BOX)
948 {
949 applyClippingBox(lineNormalizedDeviceSpace, 1.0f);
950 }
951
952 const tcu::Vec2 lineScreenSpace[2] =
953 {
954 (lineNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize,
955 (lineNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize,
956 };
957
958 const tcu::Vec2 lineDir = tcu::normalize(lineScreenSpace[1] - lineScreenSpace[0]);
959 const tcu::Vec2 lineNormalDir = (strictMode || scene.isRectangular) ? tcu::Vec2(lineDir.y(), -lineDir.x())
960 : isLineXMajor(lineScreenSpace[0], lineScreenSpace[1]) ? tcu::Vec2(0.0f, 1.0f)
961 : tcu::Vec2(1.0f, 0.0f);
962
963 if (scene.stippleEnable)
964 {
965 float lineLength = tcu::distance(lineScreenSpace[0], lineScreenSpace[1]);
966 float lineOffset = 0.0f;
967
968 while (lineOffset < lineLength)
969 {
970 float d0 = (float)lineOffset;
971 float d1 = d0 + 1.0f;
972
973 if (carryRemainder)
974 {
975 // "leftoverPhase" carries over a fractional stipple phase that was "unused"
976 // by the last line segment in the strip, if it wasn't an integer length.
977 if (leftoverPhase > lineLength)
978 {
979 DE_ASSERT(d0 == 0.0f);
980 d1 = lineLength;
981 leftoverPhase -= lineLength;
982 }
983 else if (leftoverPhase != 0.0f)
984 {
985 DE_ASSERT(d0 == 0.0f);
986 d1 = leftoverPhase;
987 leftoverPhase = 0.0f;
988 }
989 else
990 {
991 if (d0 + 1.0f > lineLength)
992 {
993 d1 = lineLength;
994 leftoverPhase = d0 + 1.0f - lineLength;
995 }
996 else
997 d1 = d0 + 1.0f;
998 }
999 }
1000 else
1001 {
1002 if (d1 > lineLength)
1003 d1 = lineLength;
1004 }
1005
1006 // set offset for next iteration
1007 lineOffset = d1;
1008
1009 int stippleBit = (stippleCounter / scene.stippleFactor) % 16;
1010 bool stipplePass = (scene.stipplePattern & (1 << stippleBit)) != 0;
1011
1012 if (leftoverPhase == 0)
1013 stippleCounter++;
1014
1015 if (!stipplePass)
1016 continue;
1017
1018 d0 /= lineLength;
1019 d1 /= lineLength;
1020
1021 tcu::Vec2 l0 = mix(lineScreenSpace[0], lineScreenSpace[1], d0);
1022 tcu::Vec2 l1 = mix(lineScreenSpace[0], lineScreenSpace[1], d1);
1023
1024 const tcu::Vec2 lineQuadScreenSpace[4] =
1025 {
1026 l0 + lineNormalDir * halfLineWidth,
1027 l0 - lineNormalDir * halfLineWidth,
1028 l1 - lineNormalDir * halfLineWidth,
1029 l1 + lineNormalDir * halfLineWidth,
1030 };
1031 const tcu::Vec2 lineQuadNormalizedDeviceSpace[4] =
1032 {
1033 lineQuadScreenSpace[0] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1034 lineQuadScreenSpace[1] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1035 lineQuadScreenSpace[2] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1036 lineQuadScreenSpace[3] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1037 };
1038
1039 TriangleSceneSpec::SceneTriangle tri;
1040
1041 tri.positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f); tri.sharedEdge[0] = (d0 != 0.0f);
1042 tri.positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[1].x(), lineQuadNormalizedDeviceSpace[1].y(), 0.0f, 1.0f); tri.sharedEdge[1] = false;
1043 tri.positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f); tri.sharedEdge[2] = true;
1044
1045 triangleScene.triangles.push_back(tri);
1046
1047 tri.positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f); tri.sharedEdge[0] = true;
1048 tri.positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f); tri.sharedEdge[1] = (d1 != 1.0f);
1049 tri.positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[3].x(), lineQuadNormalizedDeviceSpace[3].y(), 0.0f, 1.0f); tri.sharedEdge[2] = false;
1050
1051 triangleScene.triangles.push_back(tri);
1052 }
1053 }
1054 else
1055 {
1056 const tcu::Vec2 lineQuadScreenSpace[4] =
1057 {
1058 lineScreenSpace[0] + lineNormalDir * halfLineWidth,
1059 lineScreenSpace[0] - lineNormalDir * halfLineWidth,
1060 lineScreenSpace[1] - lineNormalDir * halfLineWidth,
1061 lineScreenSpace[1] + lineNormalDir * halfLineWidth,
1062 };
1063 const tcu::Vec2 lineQuadNormalizedDeviceSpace[4] =
1064 {
1065 lineQuadScreenSpace[0] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1066 lineQuadScreenSpace[1] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1067 lineQuadScreenSpace[2] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1068 lineQuadScreenSpace[3] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1069 };
1070
1071 triangleScene.triangles[lineNdx*2 + 0].positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f); triangleScene.triangles[lineNdx*2 + 0].sharedEdge[0] = false;
1072 triangleScene.triangles[lineNdx*2 + 0].positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[1].x(), lineQuadNormalizedDeviceSpace[1].y(), 0.0f, 1.0f); triangleScene.triangles[lineNdx*2 + 0].sharedEdge[1] = false;
1073 triangleScene.triangles[lineNdx*2 + 0].positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f); triangleScene.triangles[lineNdx*2 + 0].sharedEdge[2] = true;
1074
1075 triangleScene.triangles[lineNdx*2 + 1].positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f); triangleScene.triangles[lineNdx*2 + 1].sharedEdge[0] = true;
1076 triangleScene.triangles[lineNdx*2 + 1].positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f); triangleScene.triangles[lineNdx*2 + 1].sharedEdge[1] = false;
1077 triangleScene.triangles[lineNdx*2 + 1].positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[3].x(), lineQuadNormalizedDeviceSpace[3].y(), 0.0f, 1.0f); triangleScene.triangles[lineNdx*2 + 1].sharedEdge[2] = false;
1078 }
1079 }
1080
1081 if (logStash != DE_NULL)
1082 {
1083 logStash->messages.push_back("Rasterization clipping mode: " + std::string(clipMode == CLIPMODE_USE_CLIPPING_BOX ? "CLIPMODE_USE_CLIPPING_BOX" : "CLIPMODE_NO_CLIPPING") + ".");
1084 logStash->messages.push_back("Rasterization line draw strictness mode: " + std::string(strictMode ? "strict" : "non-strict") + ".");
1085 }
1086
1087 return verifyTriangleGroupRasterization(surface, triangleScene, args, log, scene.verificationMode, logStash, vulkanLinesTest);
1088 }
1089
verifyMultisampleLineGroupRasterization(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log,ClipMode clipMode,VerifyTriangleGroupRasterizationLogStash * logStash,const bool vulkanLinesTest,const bool strictMode)1090 bool verifyMultisampleLineGroupRasterization (const tcu::Surface& surface,
1091 const LineSceneSpec& scene,
1092 const RasterizationArguments& args,
1093 tcu::TestLog& log,
1094 ClipMode clipMode,
1095 VerifyTriangleGroupRasterizationLogStash* logStash,
1096 const bool vulkanLinesTest,
1097 const bool strictMode)
1098 {
1099 if (scene.stippleEnable)
1100 return verifyMultisampleLineGroupRasterization(surface, scene, args, log, clipMode, logStash, vulkanLinesTest, strictMode, true) ||
1101 verifyMultisampleLineGroupRasterization(surface, scene, args, log, clipMode, logStash, vulkanLinesTest, strictMode, false);
1102 else
1103 return verifyMultisampleLineGroupRasterization(surface, scene, args, log, clipMode, logStash, vulkanLinesTest, strictMode, true);
1104 }
1105
verifyMultisampleLineGroupInterpolationInternal(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,VerifyTriangleGroupInterpolationLogStash & logStash,const bool strictMode)1106 static bool verifyMultisampleLineGroupInterpolationInternal (const tcu::Surface& surface,
1107 const LineSceneSpec& scene,
1108 const RasterizationArguments& args,
1109 VerifyTriangleGroupInterpolationLogStash& logStash,
1110 const bool strictMode)
1111 {
1112 // Multisampled line == 2 triangles
1113
1114 const tcu::Vec2 viewportSize = tcu::Vec2((float)surface.getWidth(), (float)surface.getHeight());
1115 const float halfLineWidth = scene.lineWidth * 0.5f;
1116 TriangleSceneSpec triangleScene;
1117
1118 triangleScene.triangles.resize(2 * scene.lines.size());
1119 for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
1120 {
1121 // Need the w-coordinates a couple of times
1122 const float wa = scene.lines[lineNdx].positions[0].w();
1123 const float wb = scene.lines[lineNdx].positions[1].w();
1124
1125 // Transform to screen space, add pixel offsets, convert back to normalized device space, and test as triangles
1126 const tcu::Vec2 lineNormalizedDeviceSpace[2] =
1127 {
1128 tcu::Vec2(scene.lines[lineNdx].positions[0].x() / wa, scene.lines[lineNdx].positions[0].y() / wa),
1129 tcu::Vec2(scene.lines[lineNdx].positions[1].x() / wb, scene.lines[lineNdx].positions[1].y() / wb),
1130 };
1131 const tcu::Vec2 lineScreenSpace[2] =
1132 {
1133 (lineNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize,
1134 (lineNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize,
1135 };
1136
1137 const tcu::Vec2 lineDir = tcu::normalize(lineScreenSpace[1] - lineScreenSpace[0]);
1138 const tcu::Vec2 lineNormalDir = (strictMode || scene.isRectangular) ? tcu::Vec2(lineDir.y(), -lineDir.x())
1139 : isLineXMajor(lineScreenSpace[0], lineScreenSpace[1]) ? tcu::Vec2(0.0f, 1.0f)
1140 : tcu::Vec2(1.0f, 0.0f);
1141
1142 const tcu::Vec2 lineQuadScreenSpace[4] =
1143 {
1144 lineScreenSpace[0] + lineNormalDir * halfLineWidth,
1145 lineScreenSpace[0] - lineNormalDir * halfLineWidth,
1146 lineScreenSpace[1] - lineNormalDir * halfLineWidth,
1147 lineScreenSpace[1] + lineNormalDir * halfLineWidth,
1148 };
1149 const tcu::Vec2 lineQuadNormalizedDeviceSpace[4] =
1150 {
1151 lineQuadScreenSpace[0] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1152 lineQuadScreenSpace[1] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1153 lineQuadScreenSpace[2] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1154 lineQuadScreenSpace[3] / viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1155 };
1156
1157 // Re-construct un-projected geometry using the quantised positions
1158 const tcu::Vec4 lineQuadUnprojected[4] =
1159 {
1160 tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x() * wa, lineQuadNormalizedDeviceSpace[0].y() * wa, 0.0f, wa),
1161 tcu::Vec4(lineQuadNormalizedDeviceSpace[1].x() * wa, lineQuadNormalizedDeviceSpace[1].y() * wa, 0.0f, wa),
1162 tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x() * wb, lineQuadNormalizedDeviceSpace[2].y() * wb, 0.0f, wb),
1163 tcu::Vec4(lineQuadNormalizedDeviceSpace[3].x() * wb, lineQuadNormalizedDeviceSpace[3].y() * wb, 0.0f, wb),
1164 };
1165
1166 triangleScene.triangles[lineNdx*2 + 0].positions[0] = lineQuadUnprojected[0];
1167 triangleScene.triangles[lineNdx*2 + 0].positions[1] = lineQuadUnprojected[1];
1168 triangleScene.triangles[lineNdx*2 + 0].positions[2] = lineQuadUnprojected[2];
1169
1170 triangleScene.triangles[lineNdx*2 + 0].sharedEdge[0] = false;
1171 triangleScene.triangles[lineNdx*2 + 0].sharedEdge[1] = false;
1172 triangleScene.triangles[lineNdx*2 + 0].sharedEdge[2] = true;
1173
1174 triangleScene.triangles[lineNdx*2 + 0].colors[0] = scene.lines[lineNdx].colors[0];
1175 triangleScene.triangles[lineNdx*2 + 0].colors[1] = scene.lines[lineNdx].colors[0];
1176 triangleScene.triangles[lineNdx*2 + 0].colors[2] = scene.lines[lineNdx].colors[1];
1177
1178 triangleScene.triangles[lineNdx*2 + 1].positions[0] = lineQuadUnprojected[0];
1179 triangleScene.triangles[lineNdx*2 + 1].positions[1] = lineQuadUnprojected[2];
1180 triangleScene.triangles[lineNdx*2 + 1].positions[2] = lineQuadUnprojected[3];
1181
1182 triangleScene.triangles[lineNdx*2 + 1].sharedEdge[0] = true;
1183 triangleScene.triangles[lineNdx*2 + 1].sharedEdge[1] = false;
1184 triangleScene.triangles[lineNdx*2 + 1].sharedEdge[2] = false;
1185
1186 triangleScene.triangles[lineNdx*2 + 1].colors[0] = scene.lines[lineNdx].colors[0];
1187 triangleScene.triangles[lineNdx*2 + 1].colors[1] = scene.lines[lineNdx].colors[1];
1188 triangleScene.triangles[lineNdx*2 + 1].colors[2] = scene.lines[lineNdx].colors[1];
1189 }
1190
1191 if (strictMode)
1192 {
1193 // Strict mode interpolation should be purely in the direction of the line-segment
1194 logStash.messages.push_back("Verify using line interpolator");
1195 return verifyTriangleGroupInterpolationWithInterpolator(surface, triangleScene, args, logStash, MultisampleLineInterpolator(scene));
1196 }
1197 else
1198 {
1199 // For non-strict lines some allowance needs to be inplace for a few different styles of implementation.
1200 //
1201 // Some implementations duplicate the attributes at the endpoints to the corners of the triangle
1202 // deconstruted parallelogram. Gradients along the line will be seen to travel in the major axis,
1203 // with values effectively duplicated in the minor axis direction. In other cases, implementations
1204 // will use the original parameters of the line to calculate attribute interpolation so it will
1205 // follow the direction of the line-segment.
1206 logStash.messages.push_back("Verify using triangle interpolator");
1207 if (!verifyTriangleGroupInterpolationWithInterpolator(surface, triangleScene, args, logStash, TriangleInterpolator(triangleScene)))
1208 {
1209 logStash.messages.push_back("Verify using line interpolator");
1210 return verifyTriangleGroupInterpolationWithInterpolator(surface, triangleScene, args, logStash, MultisampleLineInterpolator(scene));
1211 }
1212 return true;
1213 }
1214 }
1215
logTriangleGroupnterpolationStash(const tcu::Surface & surface,tcu::TestLog & log,VerifyTriangleGroupInterpolationLogStash & logStash)1216 static void logTriangleGroupnterpolationStash (const tcu::Surface& surface, tcu::TestLog& log, VerifyTriangleGroupInterpolationLogStash& logStash)
1217 {
1218 // Output results
1219 log << tcu::TestLog::Message << "Verifying rasterization result." << tcu::TestLog::EndMessage;
1220
1221 for (size_t msgNdx = 0; msgNdx < logStash.messages.size(); ++msgNdx)
1222 log << tcu::TestLog::Message << logStash.messages[msgNdx] << tcu::TestLog::EndMessage;
1223
1224 // report result
1225 if (!logStash.success)
1226 {
1227 log << tcu::TestLog::Message << logStash.invalidPixels << " invalid pixel(s) found." << tcu::TestLog::EndMessage;
1228 log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1229 << tcu::TestLog::Image("Result", "Result", surface)
1230 << tcu::TestLog::Image("ErrorMask", "ErrorMask", logStash.errorMask)
1231 << tcu::TestLog::EndImageSet;
1232 }
1233 else
1234 {
1235 log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
1236 log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1237 << tcu::TestLog::Image("Result", "Result", surface)
1238 << tcu::TestLog::EndImageSet;
1239 }
1240 }
1241
verifyMultisampleLineGroupInterpolation(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log,const bool strictMode=true,const bool allowBresenhamForNonStrictLines=false)1242 static bool verifyMultisampleLineGroupInterpolation (const tcu::Surface& surface,
1243 const LineSceneSpec& scene,
1244 const RasterizationArguments& args,
1245 tcu::TestLog& log,
1246 const bool strictMode = true,
1247 const bool allowBresenhamForNonStrictLines = false)
1248 {
1249 bool result = false;
1250 VerifyTriangleGroupInterpolationLogStash nonStrictModeLogStash;
1251 VerifyTriangleGroupInterpolationLogStash strictModeLogStash;
1252
1253 nonStrictModeLogStash.messages.push_back("Non-strict line draw mode.");
1254 strictModeLogStash.messages.push_back("Strict mode line draw mode.");
1255
1256 if (strictMode)
1257 {
1258 result = verifyMultisampleLineGroupInterpolationInternal(surface,scene, args, strictModeLogStash, strictMode);
1259
1260 logTriangleGroupnterpolationStash(surface, log, strictModeLogStash);
1261 }
1262 else
1263 {
1264 if (verifyMultisampleLineGroupInterpolationInternal(surface,scene, args, nonStrictModeLogStash, false))
1265 {
1266 logTriangleGroupnterpolationStash(surface, log, nonStrictModeLogStash);
1267
1268 result = true;
1269 }
1270 else if (verifyMultisampleLineGroupInterpolationInternal(surface,scene, args, strictModeLogStash, true))
1271 {
1272 logTriangleGroupnterpolationStash(surface, log, strictModeLogStash);
1273
1274 result = true;
1275 }
1276 else
1277 {
1278 logTriangleGroupnterpolationStash(surface, log, nonStrictModeLogStash);
1279 logTriangleGroupnterpolationStash(surface, log, strictModeLogStash);
1280 }
1281
1282 // In the non-strict line case, bresenham is also permissable, though not specified. This is due
1283 // to a change in how lines are specified in Vulkan versus GLES; in GLES bresenham lines using the
1284 // diamond-exit rule were the preferred way to draw single pixel non-antialiased lines, and not all
1285 // GLES implementations are able to disable this behaviour.
1286 if (result == false)
1287 {
1288 log << tcu::TestLog::Message << "Checking line rasterisation using verifySinglesampleNarrowLineGroupInterpolation for nonStrict lines" << tcu::TestLog::EndMessage;
1289 if (args.numSamples <= 1 &&
1290 allowBresenhamForNonStrictLines &&
1291 verifyLineGroupInterpolationWithProjectedWeights(surface, scene, args, log))
1292 {
1293 log << tcu::TestLog::Message << "verifySinglesampleNarrowLineGroupInterpolation for nonStrict lines Passed" << tcu::TestLog::EndMessage;
1294
1295 result = true;
1296 }
1297 }
1298
1299 }
1300
1301 return result;
1302 }
1303
verifyMultisamplePointGroupRasterization(const tcu::Surface & surface,const PointSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)1304 bool verifyMultisamplePointGroupRasterization (const tcu::Surface& surface, const PointSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
1305 {
1306 // Multisampled point == 2 triangles
1307
1308 const tcu::Vec2 viewportSize = tcu::Vec2((float)surface.getWidth(), (float)surface.getHeight());
1309 TriangleSceneSpec triangleScene;
1310
1311 triangleScene.triangles.resize(2 * scene.points.size());
1312 for (int pointNdx = 0; pointNdx < (int)scene.points.size(); ++pointNdx)
1313 {
1314 // Transform to screen space, add pixel offsets, convert back to normalized device space, and test as triangles
1315 const tcu::Vec2 pointNormalizedDeviceSpace = tcu::Vec2(scene.points[pointNdx].position.x() / scene.points[pointNdx].position.w(), scene.points[pointNdx].position.y() / scene.points[pointNdx].position.w());
1316 const tcu::Vec2 pointScreenSpace = (pointNormalizedDeviceSpace + tcu::Vec2(1.0f, 1.0f)) * 0.5f * viewportSize;
1317 const float offset = scene.points[pointNdx].pointSize * 0.5f;
1318 const tcu::Vec2 lineQuadNormalizedDeviceSpace[4] =
1319 {
1320 (pointScreenSpace + tcu::Vec2(-offset, -offset))/ viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1321 (pointScreenSpace + tcu::Vec2(-offset, offset))/ viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1322 (pointScreenSpace + tcu::Vec2( offset, offset))/ viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1323 (pointScreenSpace + tcu::Vec2( offset, -offset))/ viewportSize * 2.0f - tcu::Vec2(1.0f, 1.0f),
1324 };
1325
1326 triangleScene.triangles[pointNdx*2 + 0].positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f); triangleScene.triangles[pointNdx*2 + 0].sharedEdge[0] = false;
1327 triangleScene.triangles[pointNdx*2 + 0].positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[1].x(), lineQuadNormalizedDeviceSpace[1].y(), 0.0f, 1.0f); triangleScene.triangles[pointNdx*2 + 0].sharedEdge[1] = false;
1328 triangleScene.triangles[pointNdx*2 + 0].positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f); triangleScene.triangles[pointNdx*2 + 0].sharedEdge[2] = true;
1329
1330 triangleScene.triangles[pointNdx*2 + 1].positions[0] = tcu::Vec4(lineQuadNormalizedDeviceSpace[0].x(), lineQuadNormalizedDeviceSpace[0].y(), 0.0f, 1.0f); triangleScene.triangles[pointNdx*2 + 1].sharedEdge[0] = true;
1331 triangleScene.triangles[pointNdx*2 + 1].positions[1] = tcu::Vec4(lineQuadNormalizedDeviceSpace[2].x(), lineQuadNormalizedDeviceSpace[2].y(), 0.0f, 1.0f); triangleScene.triangles[pointNdx*2 + 1].sharedEdge[1] = false;
1332 triangleScene.triangles[pointNdx*2 + 1].positions[2] = tcu::Vec4(lineQuadNormalizedDeviceSpace[3].x(), lineQuadNormalizedDeviceSpace[3].y(), 0.0f, 1.0f); triangleScene.triangles[pointNdx*2 + 1].sharedEdge[2] = false;
1333 }
1334
1335 return verifyTriangleGroupRasterization(surface, triangleScene, args, log);
1336 }
1337
genScreenSpaceLines(std::vector<tcu::Vec4> & screenspaceLines,const std::vector<LineSceneSpec::SceneLine> & lines,const tcu::IVec2 & viewportSize)1338 void genScreenSpaceLines (std::vector<tcu::Vec4>& screenspaceLines, const std::vector<LineSceneSpec::SceneLine>& lines, const tcu::IVec2& viewportSize)
1339 {
1340 DE_ASSERT(screenspaceLines.size() == lines.size());
1341
1342 for (int lineNdx = 0; lineNdx < (int)lines.size(); ++lineNdx)
1343 {
1344 const tcu::Vec2 lineNormalizedDeviceSpace[2] =
1345 {
1346 tcu::Vec2(lines[lineNdx].positions[0].x() / lines[lineNdx].positions[0].w(), lines[lineNdx].positions[0].y() / lines[lineNdx].positions[0].w()),
1347 tcu::Vec2(lines[lineNdx].positions[1].x() / lines[lineNdx].positions[1].w(), lines[lineNdx].positions[1].y() / lines[lineNdx].positions[1].w()),
1348 };
1349 const tcu::Vec4 lineScreenSpace[2] =
1350 {
1351 tcu::Vec4((lineNormalizedDeviceSpace[0].x() + 1.0f) * 0.5f * (float)viewportSize.x(), (lineNormalizedDeviceSpace[0].y() + 1.0f) * 0.5f * (float)viewportSize.y(), 0.0f, 1.0f),
1352 tcu::Vec4((lineNormalizedDeviceSpace[1].x() + 1.0f) * 0.5f * (float)viewportSize.x(), (lineNormalizedDeviceSpace[1].y() + 1.0f) * 0.5f * (float)viewportSize.y(), 0.0f, 1.0f),
1353 };
1354
1355 screenspaceLines[lineNdx] = tcu::Vec4(lineScreenSpace[0].x(), lineScreenSpace[0].y(), lineScreenSpace[1].x(), lineScreenSpace[1].y());
1356 }
1357 }
1358
verifySinglesampleLineGroupRasterization(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)1359 bool verifySinglesampleLineGroupRasterization (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
1360 {
1361 DE_ASSERT(deFloatFrac(scene.lineWidth) != 0.5f); // rounding direction is not defined, disallow undefined cases
1362 DE_ASSERT(scene.lines.size() < 255); // indices are stored as unsigned 8-bit ints
1363
1364 bool allOK = true;
1365 bool overdrawInReference = false;
1366 int referenceFragments = 0;
1367 int resultFragments = 0;
1368 int lineWidth = deFloorFloatToInt32(scene.lineWidth + 0.5f);
1369 std::vector<bool> lineIsXMajor (scene.lines.size());
1370 std::vector<tcu::Vec4> screenspaceLines(scene.lines.size());
1371
1372 // Reference renderer produces correct fragments using the diamond-rule. Make 2D int array, each cell contains the highest index (first index = 1) of the overlapping lines or 0 if no line intersects the pixel
1373 tcu::TextureLevel referenceLineMap(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight());
1374 tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0));
1375
1376 genScreenSpaceLines(screenspaceLines, scene.lines, tcu::IVec2(surface.getWidth(), surface.getHeight()));
1377
1378 rr::SingleSampleLineRasterizer rasterizer(tcu::IVec4(0, 0, surface.getWidth(), surface.getHeight()), args.subpixelBits);
1379 for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
1380 {
1381 rasterizer.init(tcu::Vec4(screenspaceLines[lineNdx][0],
1382 screenspaceLines[lineNdx][1],
1383 0.0f,
1384 1.0f),
1385 tcu::Vec4(screenspaceLines[lineNdx][2],
1386 screenspaceLines[lineNdx][3],
1387 0.0f,
1388 1.0f),
1389 scene.lineWidth,
1390 scene.stippleFactor,
1391 scene.stipplePattern);
1392
1393 if (!scene.isStrip)
1394 rasterizer.resetStipple();
1395
1396 // calculate majority of later use
1397 lineIsXMajor[lineNdx] = isPackedSSLineXMajor(screenspaceLines[lineNdx]);
1398
1399 for (;;)
1400 {
1401 const int maxPackets = 32;
1402 int numRasterized = 0;
1403 rr::FragmentPacket packets[maxPackets];
1404
1405 rasterizer.rasterize(packets, DE_NULL, maxPackets, numRasterized);
1406
1407 for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx)
1408 {
1409 for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
1410 {
1411 if ((deUint32)packets[packetNdx].coverage & (1 << fragNdx))
1412 {
1413 const tcu::IVec2 fragPos = packets[packetNdx].position + tcu::IVec2(fragNdx%2, fragNdx/2);
1414
1415 // Check for overdraw
1416 if (!overdrawInReference)
1417 overdrawInReference = referenceLineMap.getAccess().getPixelInt(fragPos.x(), fragPos.y()).x() != 0;
1418
1419 // Output pixel
1420 referenceLineMap.getAccess().setPixel(tcu::IVec4(lineNdx + 1, 0, 0, 0), fragPos.x(), fragPos.y());
1421 }
1422 }
1423 }
1424
1425 if (numRasterized != maxPackets)
1426 break;
1427 }
1428 }
1429
1430 // Requirement 1: The coordinates of a fragment produced by the algorithm may not deviate by more than one unit
1431 {
1432 tcu::Surface errorMask (surface.getWidth(), surface.getHeight());
1433 bool missingFragments = false;
1434
1435 tcu::clear(errorMask.getAccess(), tcu::IVec4(0, 255, 0, 255));
1436
1437 log << tcu::TestLog::Message << "Searching for deviating fragments." << tcu::TestLog::EndMessage;
1438
1439 for (int y = 0; y < referenceLineMap.getHeight(); ++y)
1440 for (int x = 0; x < referenceLineMap.getWidth(); ++x)
1441 {
1442 const bool reference = referenceLineMap.getAccess().getPixelInt(x, y).x() != 0;
1443 const bool result = compareColors(surface.getPixel(x, y), tcu::RGBA::white(), args.redBits, args.greenBits, args.blueBits);
1444
1445 if (reference)
1446 ++referenceFragments;
1447 if (result)
1448 ++resultFragments;
1449
1450 if (reference == result)
1451 continue;
1452
1453 // Reference fragment here, matching result fragment must be nearby
1454 if (reference && !result)
1455 {
1456 bool foundFragment = false;
1457
1458 if (x == 0 || y == 0 || x == referenceLineMap.getWidth() - 1 || y == referenceLineMap.getHeight() -1)
1459 {
1460 // image boundary, missing fragment could be over the image edge
1461 foundFragment = true;
1462 }
1463
1464 // find nearby fragment
1465 for (int dy = -1; dy < 2 && !foundFragment; ++dy)
1466 for (int dx = -1; dx < 2 && !foundFragment; ++dx)
1467 {
1468 if (compareColors(surface.getPixel(x+dx, y+dy), tcu::RGBA::white(), args.redBits, args.greenBits, args.blueBits))
1469 foundFragment = true;
1470 }
1471
1472 if (!foundFragment)
1473 {
1474 missingFragments = true;
1475 errorMask.setPixel(x, y, tcu::RGBA::red());
1476 }
1477 }
1478 }
1479
1480 if (missingFragments)
1481 {
1482
1483 allOK = false;
1484 }
1485 else
1486 {
1487 log << tcu::TestLog::Message << "No invalid deviations found." << tcu::TestLog::EndMessage;
1488 }
1489 }
1490
1491 // Requirement 2: The total number of fragments produced by the algorithm may differ from
1492 // that produced by the diamond-exit rule by no more than one.
1493 {
1494 // Check is not valid if the primitives intersect or otherwise share same fragments
1495 if (!overdrawInReference)
1496 {
1497 int allowedDeviation = (int)scene.lines.size() * lineWidth; // one pixel per primitive in the major direction
1498
1499 log << tcu::TestLog::Message << "Verifying fragment counts:\n"
1500 << "\tDiamond-exit rule: " << referenceFragments << " fragments.\n"
1501 << "\tResult image: " << resultFragments << " fragments.\n"
1502 << "\tAllowing deviation of " << allowedDeviation << " fragments.\n"
1503 << tcu::TestLog::EndMessage;
1504
1505 if (deAbs32(referenceFragments - resultFragments) > allowedDeviation)
1506 {
1507 tcu::Surface reference(surface.getWidth(), surface.getHeight());
1508
1509 // show a helpful reference image
1510 tcu::clear(reference.getAccess(), tcu::IVec4(0, 0, 0, 255));
1511 for (int y = 0; y < surface.getHeight(); ++y)
1512 for (int x = 0; x < surface.getWidth(); ++x)
1513 if (referenceLineMap.getAccess().getPixelInt(x, y).x())
1514 reference.setPixel(x, y, tcu::RGBA::white());
1515
1516 log << tcu::TestLog::Message << "Invalid fragment count in result image." << tcu::TestLog::EndMessage;
1517 log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1518 << tcu::TestLog::Image("Reference", "Reference", reference)
1519 << tcu::TestLog::Image("Result", "Result", surface)
1520 << tcu::TestLog::EndImageSet;
1521
1522 allOK = false;
1523 }
1524 else
1525 {
1526 log << tcu::TestLog::Message << "Fragment count is valid." << tcu::TestLog::EndMessage;
1527 }
1528 }
1529 else
1530 {
1531 log << tcu::TestLog::Message << "Overdraw in scene. Fragment count cannot be verified. Skipping fragment count checks." << tcu::TestLog::EndMessage;
1532 }
1533 }
1534
1535 // Requirement 3: Line width must be constant
1536 {
1537 bool invalidWidthFound = false;
1538
1539 log << tcu::TestLog::Message << "Verifying line widths of the x-major lines." << tcu::TestLog::EndMessage;
1540 for (int y = 1; y < referenceLineMap.getHeight() - 1; ++y)
1541 {
1542 bool fullyVisibleLine = false;
1543 bool previousPixelUndefined = false;
1544 int currentLine = 0;
1545 int currentWidth = 1;
1546
1547 for (int x = 1; x < referenceLineMap.getWidth() - 1; ++x)
1548 {
1549 const bool result = compareColors(surface.getPixel(x, y), tcu::RGBA::white(), args.redBits, args.greenBits, args.blueBits);
1550 int lineID = 0;
1551
1552 // Which line does this fragment belong to?
1553
1554 if (result)
1555 {
1556 bool multipleNearbyLines = false;
1557 bool renderAtSurfaceEdge = false;
1558
1559 renderAtSurfaceEdge = (x == 1) || (x == referenceLineMap.getWidth() - 2);
1560
1561 for (int dy = -1; dy < 2; ++dy)
1562 for (int dx = -1; dx < 2; ++dx)
1563 {
1564 const int nearbyID = referenceLineMap.getAccess().getPixelInt(x+dx, y+dy).x();
1565 if (nearbyID)
1566 {
1567 if (lineID && lineID != nearbyID)
1568 multipleNearbyLines = true;
1569 }
1570 }
1571
1572 if (multipleNearbyLines || renderAtSurfaceEdge)
1573 {
1574 // Another line is too close, don't try to calculate width here
1575 // Or the render result is outside of surface range
1576 previousPixelUndefined = true;
1577 continue;
1578 }
1579 }
1580
1581 // Only line with id of lineID is nearby
1582
1583 if (previousPixelUndefined)
1584 {
1585 // The line might have been overdrawn or not
1586 currentLine = lineID;
1587 currentWidth = 1;
1588 fullyVisibleLine = false;
1589 previousPixelUndefined = false;
1590 }
1591 else if (lineID == currentLine)
1592 {
1593 // Current line continues
1594 ++currentWidth;
1595 }
1596 else if (lineID > currentLine)
1597 {
1598 // Another line was drawn over or the line ends
1599 currentLine = lineID;
1600 currentWidth = 1;
1601 fullyVisibleLine = true;
1602 }
1603 else
1604 {
1605 // The line ends
1606 if (fullyVisibleLine && !lineIsXMajor[currentLine-1])
1607 {
1608 // check width
1609 if (currentWidth != lineWidth)
1610 {
1611 log << tcu::TestLog::Message << "\tInvalid line width at (" << x - currentWidth << ", " << y << ") - (" << x - 1 << ", " << y << "). Detected width of " << currentWidth << ", expected " << lineWidth << tcu::TestLog::EndMessage;
1612 invalidWidthFound = true;
1613 }
1614 }
1615
1616 currentLine = lineID;
1617 currentWidth = 1;
1618 fullyVisibleLine = false;
1619 }
1620 }
1621 }
1622
1623 log << tcu::TestLog::Message << "Verifying line widths of the y-major lines." << tcu::TestLog::EndMessage;
1624 for (int x = 1; x < referenceLineMap.getWidth() - 1; ++x)
1625 {
1626 bool fullyVisibleLine = false;
1627 bool previousPixelUndefined = false;
1628 int currentLine = 0;
1629 int currentWidth = 1;
1630
1631 for (int y = 1; y < referenceLineMap.getHeight() - 1; ++y)
1632 {
1633 const bool result = compareColors(surface.getPixel(x, y), tcu::RGBA::white(), args.redBits, args.greenBits, args.blueBits);
1634 int lineID = 0;
1635
1636 // Which line does this fragment belong to?
1637
1638 if (result)
1639 {
1640 bool multipleNearbyLines = false;
1641 bool renderAtSurfaceEdge = false;
1642
1643 renderAtSurfaceEdge = (y == 1) || (y == referenceLineMap.getWidth() - 2);
1644
1645 for (int dy = -1; dy < 2; ++dy)
1646 for (int dx = -1; dx < 2; ++dx)
1647 {
1648 const int nearbyID = referenceLineMap.getAccess().getPixelInt(x+dx, y+dy).x();
1649 if (nearbyID)
1650 {
1651 if (lineID && lineID != nearbyID)
1652 multipleNearbyLines = true;
1653 lineID = nearbyID;
1654 }
1655 }
1656
1657 if (multipleNearbyLines || renderAtSurfaceEdge)
1658 {
1659 // Another line is too close, don't try to calculate width here
1660 // Or the render result is outside of surface range
1661 previousPixelUndefined = true;
1662 continue;
1663 }
1664 }
1665
1666 // Only line with id of lineID is nearby
1667
1668 if (previousPixelUndefined)
1669 {
1670 // The line might have been overdrawn or not
1671 currentLine = lineID;
1672 currentWidth = 1;
1673 fullyVisibleLine = false;
1674 previousPixelUndefined = false;
1675 }
1676 else if (lineID == currentLine)
1677 {
1678 // Current line continues
1679 ++currentWidth;
1680 }
1681 else if (lineID > currentLine)
1682 {
1683 // Another line was drawn over or the line ends
1684 currentLine = lineID;
1685 currentWidth = 1;
1686 fullyVisibleLine = true;
1687 }
1688 else
1689 {
1690 // The line ends
1691 if (fullyVisibleLine && lineIsXMajor[currentLine-1])
1692 {
1693 // check width
1694 if (currentWidth != lineWidth)
1695 {
1696 log << tcu::TestLog::Message << "\tInvalid line width at (" << x << ", " << y - currentWidth << ") - (" << x << ", " << y - 1 << "). Detected width of " << currentWidth << ", expected " << lineWidth << tcu::TestLog::EndMessage;
1697 invalidWidthFound = true;
1698 }
1699 }
1700
1701 currentLine = lineID;
1702 currentWidth = 1;
1703 fullyVisibleLine = false;
1704 }
1705 }
1706 }
1707
1708 if (invalidWidthFound)
1709 {
1710 log << tcu::TestLog::Message << "Invalid line width found, image is not valid." << tcu::TestLog::EndMessage;
1711 allOK = false;
1712 }
1713 else
1714 {
1715 log << tcu::TestLog::Message << "Line widths are valid." << tcu::TestLog::EndMessage;
1716 }
1717 }
1718
1719 //\todo [2013-10-24 jarkko].
1720 //Requirement 4. If two line segments share a common endpoint, and both segments are either
1721 //x-major (both left-to-right or both right-to-left) or y-major (both bottom-totop
1722 //or both top-to-bottom), then rasterizing both segments may not produce
1723 //duplicate fragments, nor may any fragments be omitted so as to interrupt
1724 //continuity of the connected segments.
1725
1726 {
1727 tcu::Surface reference(surface.getWidth(), surface.getHeight());
1728 tcu::clear(reference.getAccess(), tcu::IVec4(0, 0, 0, 255));
1729 for (int y = 0; y < surface.getHeight(); ++y)
1730 for (int x = 0; x < surface.getWidth(); ++x)
1731 if (referenceLineMap.getAccess().getPixelInt(x, y).x())
1732 reference.setPixel(x, y, tcu::RGBA::white());
1733 log << tcu::TestLog::Message << "Invalid fragment count in result image." << tcu::TestLog::EndMessage;
1734 log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1735 << tcu::TestLog::Image("Reference", "Reference", reference)
1736 << tcu::TestLog::Image("Result", "Result", surface)
1737 << tcu::TestLog::EndImageSet;
1738 }
1739
1740 return allOK;
1741 }
1742
1743 struct SingleSampleNarrowLineCandidate
1744 {
1745 int lineNdx;
1746 tcu::IVec3 colorMin;
1747 tcu::IVec3 colorMax;
1748 tcu::Vec3 colorMinF;
1749 tcu::Vec3 colorMaxF;
1750 tcu::Vec3 valueRangeMin;
1751 tcu::Vec3 valueRangeMax;
1752 };
1753
setMaskMapCoverageBitForLine(int bitNdx,const tcu::Vec2 & screenSpaceP0,const tcu::Vec2 & screenSpaceP1,float lineWidth,tcu::PixelBufferAccess maskMap,const int subpixelBits)1754 void setMaskMapCoverageBitForLine (int bitNdx, const tcu::Vec2& screenSpaceP0, const tcu::Vec2& screenSpaceP1, float lineWidth, tcu::PixelBufferAccess maskMap, const int subpixelBits)
1755 {
1756 enum
1757 {
1758 MAX_PACKETS = 32,
1759 };
1760
1761 rr::SingleSampleLineRasterizer rasterizer (tcu::IVec4(0, 0, maskMap.getWidth(), maskMap.getHeight()), subpixelBits);
1762 int numRasterized = MAX_PACKETS;
1763 rr::FragmentPacket packets[MAX_PACKETS];
1764
1765 rasterizer.init(tcu::Vec4(screenSpaceP0.x(), screenSpaceP0.y(), 0.0f, 1.0f),
1766 tcu::Vec4(screenSpaceP1.x(), screenSpaceP1.y(), 0.0f, 1.0f),
1767 lineWidth,
1768 1, 0xFFFF);
1769
1770 while (numRasterized == MAX_PACKETS)
1771 {
1772 rasterizer.rasterize(packets, DE_NULL, MAX_PACKETS, numRasterized);
1773
1774 for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx)
1775 {
1776 for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
1777 {
1778 if ((deUint32)packets[packetNdx].coverage & (1 << fragNdx))
1779 {
1780 const tcu::IVec2 fragPos = packets[packetNdx].position + tcu::IVec2(fragNdx%2, fragNdx/2);
1781
1782 DE_ASSERT(deInBounds32(fragPos.x(), 0, maskMap.getWidth()));
1783 DE_ASSERT(deInBounds32(fragPos.y(), 0, maskMap.getHeight()));
1784
1785 const deUint32 previousMask = maskMap.getPixelUint(fragPos.x(), fragPos.y()).x();
1786 const deUint32 newMask = (previousMask) | ((deUint32)1u << bitNdx);
1787
1788 maskMap.setPixel(tcu::UVec4(newMask, 0, 0, 0), fragPos.x(), fragPos.y());
1789 }
1790 }
1791 }
1792 }
1793 }
1794
setMaskMapCoverageBitForLines(const std::vector<tcu::Vec4> & screenspaceLines,float lineWidth,tcu::PixelBufferAccess maskMap,const int subpixelBits)1795 void setMaskMapCoverageBitForLines (const std::vector<tcu::Vec4>& screenspaceLines, float lineWidth, tcu::PixelBufferAccess maskMap, const int subpixelBits)
1796 {
1797 for (int lineNdx = 0; lineNdx < (int)screenspaceLines.size(); ++lineNdx)
1798 {
1799 const tcu::Vec2 pa = screenspaceLines[lineNdx].swizzle(0, 1);
1800 const tcu::Vec2 pb = screenspaceLines[lineNdx].swizzle(2, 3);
1801
1802 setMaskMapCoverageBitForLine(lineNdx, pa, pb, lineWidth, maskMap, subpixelBits);
1803 }
1804 }
1805
1806 // verify line interpolation assuming line pixels are interpolated independently depending only on screen space location
verifyLineGroupPixelIndependentInterpolation(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log,LineInterpolationMethod interpolationMethod)1807 bool verifyLineGroupPixelIndependentInterpolation (const tcu::Surface& surface,
1808 const LineSceneSpec& scene,
1809 const RasterizationArguments& args,
1810 tcu::TestLog& log,
1811 LineInterpolationMethod interpolationMethod)
1812 {
1813 DE_ASSERT(scene.lines.size() < 8); // coverage indices are stored as bitmask in a unsigned 8-bit ints
1814 DE_ASSERT(interpolationMethod == LINEINTERPOLATION_STRICTLY_CORRECT || interpolationMethod == LINEINTERPOLATION_PROJECTED);
1815
1816 const tcu::RGBA invalidPixelColor = tcu::RGBA(255, 0, 0, 255);
1817 const tcu::IVec2 viewportSize = tcu::IVec2(surface.getWidth(), surface.getHeight());
1818 const int errorFloodThreshold = 4;
1819 int errorCount = 0;
1820 tcu::Surface errorMask (surface.getWidth(), surface.getHeight());
1821 int invalidPixels = 0;
1822 std::vector<tcu::Vec4> screenspaceLines (scene.lines.size()); //!< packed (x0, y0, x1, y1)
1823
1824 // Reference renderer produces correct fragments using the diamond-exit-rule. Make 2D int array, store line coverage as a 8-bit bitfield
1825 // The map is used to find lines with potential coverage to a given pixel
1826 tcu::TextureLevel referenceLineMap (tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight());
1827
1828 tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0));
1829 tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
1830
1831 // log format
1832
1833 log << tcu::TestLog::Message << "Verifying rasterization result. Native format is RGB" << args.redBits << args.greenBits << args.blueBits << tcu::TestLog::EndMessage;
1834 if (args.redBits > 8 || args.greenBits > 8 || args.blueBits > 8)
1835 log << tcu::TestLog::Message << "Warning! More than 8 bits in a color channel, this may produce false negatives." << tcu::TestLog::EndMessage;
1836
1837 // prepare lookup map
1838
1839 genScreenSpaceLines(screenspaceLines, scene.lines, viewportSize);
1840 setMaskMapCoverageBitForLines(screenspaceLines, scene.lineWidth, referenceLineMap.getAccess(), args.subpixelBits);
1841
1842 // Find all possible lines with coverage, check pixel color matches one of them
1843
1844 for (int y = 1; y < surface.getHeight() - 1; ++y)
1845 for (int x = 1; x < surface.getWidth() - 1; ++x)
1846 {
1847 const tcu::RGBA color = surface.getPixel(x, y);
1848 const tcu::IVec3 pixelNativeColor = convertRGB8ToNativeFormat(color, args); // Convert pixel color from rgba8 to the real pixel format. Usually rgba8 or 565
1849 int lineCoverageSet = 0; // !< lines that may cover this fragment
1850 int lineSurroundingCoverage = 0xFFFF; // !< lines that will cover this fragment
1851 bool matchFound = false;
1852 const tcu::IVec3 formatLimit ((1 << args.redBits) - 1, (1 << args.greenBits) - 1, (1 << args.blueBits) - 1);
1853
1854 std::vector<SingleSampleNarrowLineCandidate> candidates;
1855
1856 // Find lines with possible coverage
1857
1858 for (int dy = -1; dy < 2; ++dy)
1859 for (int dx = -1; dx < 2; ++dx)
1860 {
1861 const int coverage = referenceLineMap.getAccess().getPixelInt(x+dx, y+dy).x();
1862
1863 lineCoverageSet |= coverage;
1864 lineSurroundingCoverage &= coverage;
1865 }
1866
1867 // background color is possible?
1868 if (lineSurroundingCoverage == 0 && compareColors(color, tcu::RGBA::black(), args.redBits, args.greenBits, args.blueBits))
1869 continue;
1870
1871 // Check those lines
1872
1873 for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
1874 {
1875 if (((lineCoverageSet >> lineNdx) & 0x01) != 0)
1876 {
1877 const float wa = scene.lines[lineNdx].positions[0].w();
1878 const float wb = scene.lines[lineNdx].positions[1].w();
1879 const tcu::Vec2 pa = screenspaceLines[lineNdx].swizzle(0, 1);
1880 const tcu::Vec2 pb = screenspaceLines[lineNdx].swizzle(2, 3);
1881
1882 const LineInterpolationRange range = (interpolationMethod == LINEINTERPOLATION_STRICTLY_CORRECT)
1883 ? (calcSingleSampleLineInterpolationRange(pa, wa, pb, wb, tcu::IVec2(x, y), args.subpixelBits))
1884 : (calcSingleSampleLineInterpolationRangeAxisProjected(pa, wa, pb, wb, tcu::IVec2(x, y), args.subpixelBits));
1885
1886 const tcu::Vec4 valueMin = de::clamp(range.min.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] + de::clamp(range.min.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1];
1887 const tcu::Vec4 valueMax = de::clamp(range.max.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] + de::clamp(range.max.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1];
1888
1889 const tcu::Vec3 colorMinF (de::clamp(valueMin.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
1890 de::clamp(valueMin.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
1891 de::clamp(valueMin.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
1892 const tcu::Vec3 colorMaxF (de::clamp(valueMax.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
1893 de::clamp(valueMax.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
1894 de::clamp(valueMax.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
1895 const tcu::IVec3 colorMin ((int)deFloatFloor(colorMinF.x()),
1896 (int)deFloatFloor(colorMinF.y()),
1897 (int)deFloatFloor(colorMinF.z()));
1898 const tcu::IVec3 colorMax ((int)deFloatCeil (colorMaxF.x()),
1899 (int)deFloatCeil (colorMaxF.y()),
1900 (int)deFloatCeil (colorMaxF.z()));
1901
1902 // Verify validity
1903 if (pixelNativeColor.x() < colorMin.x() ||
1904 pixelNativeColor.y() < colorMin.y() ||
1905 pixelNativeColor.z() < colorMin.z() ||
1906 pixelNativeColor.x() > colorMax.x() ||
1907 pixelNativeColor.y() > colorMax.y() ||
1908 pixelNativeColor.z() > colorMax.z())
1909 {
1910 if (errorCount < errorFloodThreshold)
1911 {
1912 // Store candidate information for logging
1913 SingleSampleNarrowLineCandidate candidate;
1914
1915 candidate.lineNdx = lineNdx;
1916 candidate.colorMin = colorMin;
1917 candidate.colorMax = colorMax;
1918 candidate.colorMinF = colorMinF;
1919 candidate.colorMaxF = colorMaxF;
1920 candidate.valueRangeMin = valueMin.swizzle(0, 1, 2);
1921 candidate.valueRangeMax = valueMax.swizzle(0, 1, 2);
1922
1923 candidates.push_back(candidate);
1924 }
1925 }
1926 else
1927 {
1928 matchFound = true;
1929 break;
1930 }
1931 }
1932 }
1933
1934 if (matchFound)
1935 continue;
1936
1937 // invalid fragment
1938 ++invalidPixels;
1939 errorMask.setPixel(x, y, invalidPixelColor);
1940
1941 ++errorCount;
1942
1943 // don't fill the logs with too much data
1944 if (errorCount < errorFloodThreshold)
1945 {
1946 log << tcu::TestLog::Message
1947 << "Found an invalid pixel at (" << x << "," << y << "), " << (int)candidates.size() << " candidate reference value(s) found:\n"
1948 << "\tPixel color:\t\t" << color << "\n"
1949 << "\tNative color:\t\t" << pixelNativeColor << "\n"
1950 << tcu::TestLog::EndMessage;
1951
1952 for (int candidateNdx = 0; candidateNdx < (int)candidates.size(); ++candidateNdx)
1953 {
1954 const SingleSampleNarrowLineCandidate& candidate = candidates[candidateNdx];
1955
1956 log << tcu::TestLog::Message << "\tCandidate (line " << candidate.lineNdx << "):\n"
1957 << "\t\tReference native color min: " << tcu::clamp(candidate.colorMin, tcu::IVec3(0,0,0), formatLimit) << "\n"
1958 << "\t\tReference native color max: " << tcu::clamp(candidate.colorMax, tcu::IVec3(0,0,0), formatLimit) << "\n"
1959 << "\t\tReference native float min: " << tcu::clamp(candidate.colorMinF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
1960 << "\t\tReference native float max: " << tcu::clamp(candidate.colorMaxF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
1961 << "\t\tFmin:\t" << tcu::clamp(candidate.valueRangeMin, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n"
1962 << "\t\tFmax:\t" << tcu::clamp(candidate.valueRangeMax, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n"
1963 << tcu::TestLog::EndMessage;
1964 }
1965 }
1966 }
1967
1968 // don't just hide failures
1969 if (errorCount > errorFloodThreshold)
1970 log << tcu::TestLog::Message << "Omitted " << (errorCount-errorFloodThreshold) << " pixel error description(s)." << tcu::TestLog::EndMessage;
1971
1972 // report result
1973 if (invalidPixels)
1974 {
1975 log << tcu::TestLog::Message << invalidPixels << " invalid pixel(s) found." << tcu::TestLog::EndMessage;
1976 log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1977 << tcu::TestLog::Image("Result", "Result", surface)
1978 << tcu::TestLog::Image("ErrorMask", "ErrorMask", errorMask)
1979 << tcu::TestLog::EndImageSet;
1980
1981 return false;
1982 }
1983 else
1984 {
1985 log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
1986 log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
1987 << tcu::TestLog::Image("Result", "Result", surface)
1988 << tcu::TestLog::EndImageSet;
1989
1990 return true;
1991 }
1992 }
1993
verifySinglesampleNarrowLineGroupInterpolation(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)1994 bool verifySinglesampleNarrowLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
1995 {
1996 DE_ASSERT(scene.lineWidth == 1.0f);
1997 return verifyLineGroupPixelIndependentInterpolation(surface, scene, args, log, LINEINTERPOLATION_STRICTLY_CORRECT);
1998 }
1999
verifyLineGroupInterpolationWithProjectedWeights(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)2000 bool verifyLineGroupInterpolationWithProjectedWeights (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
2001 {
2002 return verifyLineGroupPixelIndependentInterpolation(surface, scene, args, log, LINEINTERPOLATION_PROJECTED);
2003 }
2004
2005 struct SingleSampleWideLineCandidate
2006 {
2007 struct InterpolationPointCandidate
2008 {
2009 tcu::IVec2 interpolationPoint;
2010 tcu::IVec3 colorMin;
2011 tcu::IVec3 colorMax;
2012 tcu::Vec3 colorMinF;
2013 tcu::Vec3 colorMaxF;
2014 tcu::Vec3 valueRangeMin;
2015 tcu::Vec3 valueRangeMax;
2016 };
2017
2018 int lineNdx;
2019 int numCandidates;
2020 InterpolationPointCandidate interpolationCandidates[3];
2021 };
2022
2023 // return point on line at a given position on a given axis
getLineCoordAtAxisCoord(const tcu::Vec2 & pa,const tcu::Vec2 & pb,bool isXAxis,float axisCoord)2024 tcu::Vec2 getLineCoordAtAxisCoord (const tcu::Vec2& pa, const tcu::Vec2& pb, bool isXAxis, float axisCoord)
2025 {
2026 const int fixedCoordNdx = (isXAxis) ? (0) : (1);
2027 const int varyingCoordNdx = (isXAxis) ? (1) : (0);
2028
2029 const float fixedDifference = pb[fixedCoordNdx] - pa[fixedCoordNdx];
2030 const float varyingDifference = pb[varyingCoordNdx] - pa[varyingCoordNdx];
2031
2032 DE_ASSERT(fixedDifference != 0.0f);
2033
2034 const float resultFixedCoord = axisCoord;
2035 const float resultVaryingCoord = pa[varyingCoordNdx] + (axisCoord - pa[fixedCoordNdx]) * (varyingDifference / fixedDifference);
2036
2037 return (isXAxis) ? (tcu::Vec2(resultFixedCoord, resultVaryingCoord))
2038 : (tcu::Vec2(resultVaryingCoord, resultFixedCoord));
2039 }
2040
isBlack(const tcu::RGBA & c)2041 bool isBlack (const tcu::RGBA& c)
2042 {
2043 return c.getRed() == 0 && c.getGreen() == 0 && c.getBlue() == 0;
2044 }
2045
verifySinglesampleWideLineGroupInterpolation(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)2046 bool verifySinglesampleWideLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
2047 {
2048 DE_ASSERT(deFloatFrac(scene.lineWidth) != 0.5f); // rounding direction is not defined, disallow undefined cases
2049 DE_ASSERT(scene.lines.size() < 8); // coverage indices are stored as bitmask in a unsigned 8-bit ints
2050
2051 enum
2052 {
2053 FLAG_ROOT_NOT_SET = (1u << 16)
2054 };
2055
2056 const tcu::RGBA invalidPixelColor = tcu::RGBA(255, 0, 0, 255);
2057 const tcu::IVec2 viewportSize = tcu::IVec2(surface.getWidth(), surface.getHeight());
2058 const int errorFloodThreshold = 4;
2059 int errorCount = 0;
2060 tcu::Surface errorMask (surface.getWidth(), surface.getHeight());
2061 int invalidPixels = 0;
2062 std::vector<tcu::Vec4> effectiveLines (scene.lines.size()); //!< packed (x0, y0, x1, y1)
2063 std::vector<bool> lineIsXMajor (scene.lines.size());
2064
2065 // for each line, for every distinct major direction fragment, store root pixel location (along
2066 // minor direction);
2067 std::vector<std::vector<deUint32> > rootPixelLocation (scene.lines.size()); //!< packed [16b - flags] [16b - coordinate]
2068
2069 // log format
2070
2071 log << tcu::TestLog::Message << "Verifying rasterization result. Native format is RGB" << args.redBits << args.greenBits << args.blueBits << tcu::TestLog::EndMessage;
2072 if (args.redBits > 8 || args.greenBits > 8 || args.blueBits > 8)
2073 log << tcu::TestLog::Message << "Warning! More than 8 bits in a color channel, this may produce false negatives." << tcu::TestLog::EndMessage;
2074
2075 // Reference renderer produces correct fragments using the diamond-exit-rule. Make 2D int array, store line coverage as a 8-bit bitfield
2076 // The map is used to find lines with potential coverage to a given pixel
2077 tcu::TextureLevel referenceLineMap(tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight());
2078 tcu::clear(referenceLineMap.getAccess(), tcu::IVec4(0, 0, 0, 0));
2079
2080 tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
2081
2082 // calculate mask and effective line coordinates
2083 {
2084 std::vector<tcu::Vec4> screenspaceLines(scene.lines.size());
2085
2086 genScreenSpaceLines(screenspaceLines, scene.lines, viewportSize);
2087 setMaskMapCoverageBitForLines(screenspaceLines, scene.lineWidth, referenceLineMap.getAccess(), args.subpixelBits);
2088
2089 for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
2090 {
2091 const tcu::Vec2 lineScreenSpaceP0 = screenspaceLines[lineNdx].swizzle(0, 1);
2092 const tcu::Vec2 lineScreenSpaceP1 = screenspaceLines[lineNdx].swizzle(2, 3);
2093 const bool isXMajor = isPackedSSLineXMajor(screenspaceLines[lineNdx]);
2094
2095 lineIsXMajor[lineNdx] = isXMajor;
2096
2097 // wide line interpolations are calculated for a line moved in minor direction
2098 {
2099 const float offsetLength = (scene.lineWidth - 1.0f) / 2.0f;
2100 const tcu::Vec2 offsetDirection = (isXMajor) ? (tcu::Vec2(0.0f, -1.0f)) : (tcu::Vec2(-1.0f, 0.0f));
2101 const tcu::Vec2 offset = offsetDirection * offsetLength;
2102
2103 effectiveLines[lineNdx] = tcu::Vec4(lineScreenSpaceP0.x() + offset.x(),
2104 lineScreenSpaceP0.y() + offset.y(),
2105 lineScreenSpaceP1.x() + offset.x(),
2106 lineScreenSpaceP1.y() + offset.y());
2107 }
2108 }
2109 }
2110
2111 for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
2112 {
2113 // Calculate root pixel lookup table for this line. Since the implementation's fragment
2114 // major coordinate range might not be a subset of the correct line range (they are allowed
2115 // to vary by one pixel), we must extend the domain to cover whole viewport along major
2116 // dimension.
2117 //
2118 // Expanding line strip to (effectively) infinite line might result in exit-diamnod set
2119 // that is not a superset of the exit-diamond set of the line strip. In practice, this
2120 // won't be an issue, since the allow-one-pixel-variation rule should tolerate this even
2121 // if the original and extended line would resolve differently a diamond the line just
2122 // touches (precision lost in expansion changes enter/exit status).
2123
2124 {
2125 const bool isXMajor = lineIsXMajor[lineNdx];
2126 const int majorSize = (isXMajor) ? (surface.getWidth()) : (surface.getHeight());
2127 rr::LineExitDiamondGenerator diamondGenerator (args.subpixelBits);
2128 rr::LineExitDiamond diamonds[32];
2129 int numRasterized = DE_LENGTH_OF_ARRAY(diamonds);
2130
2131 // Expand to effectively infinite line (endpoints are just one pixel over viewport boundaries)
2132 const tcu::Vec2 expandedP0 = getLineCoordAtAxisCoord(effectiveLines[lineNdx].swizzle(0, 1), effectiveLines[lineNdx].swizzle(2, 3), isXMajor, -1.0f);
2133 const tcu::Vec2 expandedP1 = getLineCoordAtAxisCoord(effectiveLines[lineNdx].swizzle(0, 1), effectiveLines[lineNdx].swizzle(2, 3), isXMajor, (float)majorSize + 1.0f);
2134
2135 diamondGenerator.init(tcu::Vec4(expandedP0.x(), expandedP0.y(), 0.0f, 1.0f),
2136 tcu::Vec4(expandedP1.x(), expandedP1.y(), 0.0f, 1.0f));
2137
2138 rootPixelLocation[lineNdx].resize(majorSize, FLAG_ROOT_NOT_SET);
2139
2140 while (numRasterized == DE_LENGTH_OF_ARRAY(diamonds))
2141 {
2142 diamondGenerator.rasterize(diamonds, DE_LENGTH_OF_ARRAY(diamonds), numRasterized);
2143
2144 for (int packetNdx = 0; packetNdx < numRasterized; ++packetNdx)
2145 {
2146 const tcu::IVec2 fragPos = diamonds[packetNdx].position;
2147 const int majorPos = (isXMajor) ? (fragPos.x()) : (fragPos.y());
2148 const int rootPos = (isXMajor) ? (fragPos.y()) : (fragPos.x());
2149 const deUint32 packed = (deUint32)((deUint16)((deInt16)rootPos));
2150
2151 // infinite line will generate some diamonds outside the viewport
2152 if (deInBounds32(majorPos, 0, majorSize))
2153 {
2154 DE_ASSERT((rootPixelLocation[lineNdx][majorPos] & FLAG_ROOT_NOT_SET) != 0u);
2155 rootPixelLocation[lineNdx][majorPos] = packed;
2156 }
2157 }
2158 }
2159
2160 // Filled whole lookup table
2161 for (int majorPos = 0; majorPos < majorSize; ++majorPos)
2162 DE_ASSERT((rootPixelLocation[lineNdx][majorPos] & FLAG_ROOT_NOT_SET) == 0u);
2163 }
2164 }
2165
2166 // Find all possible lines with coverage, check pixel color matches one of them
2167
2168 for (int y = 1; y < surface.getHeight() - 1; ++y)
2169 for (int x = 1; x < surface.getWidth() - 1; ++x)
2170 {
2171 const tcu::RGBA color = surface.getPixel(x, y);
2172 const tcu::IVec3 pixelNativeColor = convertRGB8ToNativeFormat(color, args); // Convert pixel color from rgba8 to the real pixel format. Usually rgba8 or 565
2173 int lineCoverageSet = 0; // !< lines that may cover this fragment
2174 int lineSurroundingCoverage = 0xFFFF; // !< lines that will cover this fragment
2175 bool matchFound = false;
2176 const tcu::IVec3 formatLimit ((1 << args.redBits) - 1, (1 << args.greenBits) - 1, (1 << args.blueBits) - 1);
2177
2178 std::vector<SingleSampleWideLineCandidate> candidates;
2179
2180 // Find lines with possible coverage
2181
2182 for (int dy = -1; dy < 2; ++dy)
2183 for (int dx = -1; dx < 2; ++dx)
2184 {
2185 const int coverage = referenceLineMap.getAccess().getPixelInt(x+dx, y+dy).x();
2186
2187 lineCoverageSet |= coverage;
2188 lineSurroundingCoverage &= coverage;
2189 }
2190
2191 // background color is possible?
2192 if (lineSurroundingCoverage == 0 && compareColors(color, tcu::RGBA::black(), args.redBits, args.greenBits, args.blueBits))
2193 continue;
2194
2195 // Check those lines
2196
2197 for (int lineNdx = 0; lineNdx < (int)scene.lines.size(); ++lineNdx)
2198 {
2199 if (((lineCoverageSet >> lineNdx) & 0x01) != 0)
2200 {
2201 const float wa = scene.lines[lineNdx].positions[0].w();
2202 const float wb = scene.lines[lineNdx].positions[1].w();
2203 const tcu::Vec2 pa = effectiveLines[lineNdx].swizzle(0, 1);
2204 const tcu::Vec2 pb = effectiveLines[lineNdx].swizzle(2, 3);
2205
2206 // \note Wide line fragments are generated by replicating the root fragment for each
2207 // fragment column (row for y-major). Calculate interpolation at the root
2208 // fragment.
2209 const bool isXMajor = lineIsXMajor[lineNdx];
2210 const int majorPosition = (isXMajor) ? (x) : (y);
2211 const deUint32 minorInfoPacked = rootPixelLocation[lineNdx][majorPosition];
2212 const int minorPosition = (int)((deInt16)((deUint16)(minorInfoPacked & 0xFFFFu)));
2213 const tcu::IVec2 idealRootPos = (isXMajor) ? (tcu::IVec2(majorPosition, minorPosition)) : (tcu::IVec2(minorPosition, majorPosition));
2214 const tcu::IVec2 minorDirection = (isXMajor) ? (tcu::IVec2(0, 1)) : (tcu::IVec2(1, 0));
2215
2216 SingleSampleWideLineCandidate candidate;
2217
2218 candidate.lineNdx = lineNdx;
2219 candidate.numCandidates = 0;
2220 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(candidate.interpolationCandidates) == 3);
2221
2222 // Interpolation happens at the root fragment, which is then replicated in minor
2223 // direction. Search for implementation's root position near accurate root.
2224 for (int minorOffset = -1; minorOffset < 2; ++minorOffset)
2225 {
2226 const tcu::IVec2 rootPosition = idealRootPos + minorOffset * minorDirection;
2227
2228 // A fragment can be root fragment only if it exists
2229 // \note root fragment can "exist" outside viewport
2230 // \note no pixel format theshold since in this case allowing only black is more conservative
2231 if (deInBounds32(rootPosition.x(), 0, surface.getWidth()) &&
2232 deInBounds32(rootPosition.y(), 0, surface.getHeight()) &&
2233 isBlack(surface.getPixel(rootPosition.x(), rootPosition.y())))
2234 {
2235 continue;
2236 }
2237
2238 const LineInterpolationRange range = calcSingleSampleLineInterpolationRange(pa, wa, pb, wb, rootPosition, args.subpixelBits);
2239
2240 const tcu::Vec4 valueMin = de::clamp(range.min.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] + de::clamp(range.min.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1];
2241 const tcu::Vec4 valueMax = de::clamp(range.max.x(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[0] + de::clamp(range.max.y(), 0.0f, 1.0f) * scene.lines[lineNdx].colors[1];
2242
2243 const tcu::Vec3 colorMinF (de::clamp(valueMin.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
2244 de::clamp(valueMin.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
2245 de::clamp(valueMin.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
2246 const tcu::Vec3 colorMaxF (de::clamp(valueMax.x() * (float)formatLimit.x(), 0.0f, (float)formatLimit.x()),
2247 de::clamp(valueMax.y() * (float)formatLimit.y(), 0.0f, (float)formatLimit.y()),
2248 de::clamp(valueMax.z() * (float)formatLimit.z(), 0.0f, (float)formatLimit.z()));
2249 const tcu::IVec3 colorMin ((int)deFloatFloor(colorMinF.x()),
2250 (int)deFloatFloor(colorMinF.y()),
2251 (int)deFloatFloor(colorMinF.z()));
2252 const tcu::IVec3 colorMax ((int)deFloatCeil (colorMaxF.x()),
2253 (int)deFloatCeil (colorMaxF.y()),
2254 (int)deFloatCeil (colorMaxF.z()));
2255
2256 // Verify validity
2257 if (pixelNativeColor.x() < colorMin.x() ||
2258 pixelNativeColor.y() < colorMin.y() ||
2259 pixelNativeColor.z() < colorMin.z() ||
2260 pixelNativeColor.x() > colorMax.x() ||
2261 pixelNativeColor.y() > colorMax.y() ||
2262 pixelNativeColor.z() > colorMax.z())
2263 {
2264 if (errorCount < errorFloodThreshold)
2265 {
2266 // Store candidate information for logging
2267 SingleSampleWideLineCandidate::InterpolationPointCandidate& interpolationCandidate = candidate.interpolationCandidates[candidate.numCandidates++];
2268 DE_ASSERT(candidate.numCandidates <= DE_LENGTH_OF_ARRAY(candidate.interpolationCandidates));
2269
2270 interpolationCandidate.interpolationPoint = rootPosition;
2271 interpolationCandidate.colorMin = colorMin;
2272 interpolationCandidate.colorMax = colorMax;
2273 interpolationCandidate.colorMinF = colorMinF;
2274 interpolationCandidate.colorMaxF = colorMaxF;
2275 interpolationCandidate.valueRangeMin = valueMin.swizzle(0, 1, 2);
2276 interpolationCandidate.valueRangeMax = valueMax.swizzle(0, 1, 2);
2277 }
2278 }
2279 else
2280 {
2281 matchFound = true;
2282 break;
2283 }
2284 }
2285
2286 if (!matchFound)
2287 {
2288 // store info for logging
2289 if (errorCount < errorFloodThreshold && candidate.numCandidates > 0)
2290 candidates.push_back(candidate);
2291 }
2292 else
2293 {
2294 // no need to check other lines
2295 break;
2296 }
2297 }
2298 }
2299
2300 if (matchFound)
2301 continue;
2302
2303 // invalid fragment
2304 ++invalidPixels;
2305 errorMask.setPixel(x, y, invalidPixelColor);
2306
2307 ++errorCount;
2308
2309 // don't fill the logs with too much data
2310 if (errorCount < errorFloodThreshold)
2311 {
2312 tcu::MessageBuilder msg(&log);
2313
2314 msg << "Found an invalid pixel at (" << x << "," << y << "), " << (int)candidates.size() << " candidate reference value(s) found:\n"
2315 << "\tPixel color:\t\t" << color << "\n"
2316 << "\tNative color:\t\t" << pixelNativeColor << "\n";
2317
2318 for (int lineCandidateNdx = 0; lineCandidateNdx < (int)candidates.size(); ++lineCandidateNdx)
2319 {
2320 const SingleSampleWideLineCandidate& candidate = candidates[lineCandidateNdx];
2321
2322 msg << "\tCandidate line (line " << candidate.lineNdx << "):\n";
2323
2324 for (int interpolationCandidateNdx = 0; interpolationCandidateNdx < candidate.numCandidates; ++interpolationCandidateNdx)
2325 {
2326 const SingleSampleWideLineCandidate::InterpolationPointCandidate& interpolationCandidate = candidate.interpolationCandidates[interpolationCandidateNdx];
2327
2328 msg << "\t\tCandidate interpolation point (index " << interpolationCandidateNdx << "):\n"
2329 << "\t\t\tRoot fragment position (non-replicated fragment): " << interpolationCandidate.interpolationPoint << ":\n"
2330 << "\t\t\tReference native color min: " << tcu::clamp(interpolationCandidate.colorMin, tcu::IVec3(0,0,0), formatLimit) << "\n"
2331 << "\t\t\tReference native color max: " << tcu::clamp(interpolationCandidate.colorMax, tcu::IVec3(0,0,0), formatLimit) << "\n"
2332 << "\t\t\tReference native float min: " << tcu::clamp(interpolationCandidate.colorMinF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
2333 << "\t\t\tReference native float max: " << tcu::clamp(interpolationCandidate.colorMaxF, tcu::Vec3(0.0f, 0.0f, 0.0f), formatLimit.cast<float>()) << "\n"
2334 << "\t\t\tFmin:\t" << tcu::clamp(interpolationCandidate.valueRangeMin, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n"
2335 << "\t\t\tFmax:\t" << tcu::clamp(interpolationCandidate.valueRangeMax, tcu::Vec3(0.0f, 0.0f, 0.0f), tcu::Vec3(1.0f, 1.0f, 1.0f)) << "\n";
2336 }
2337 }
2338
2339 msg << tcu::TestLog::EndMessage;
2340 }
2341 }
2342
2343 // don't just hide failures
2344 if (errorCount > errorFloodThreshold)
2345 log << tcu::TestLog::Message << "Omitted " << (errorCount-errorFloodThreshold) << " pixel error description(s)." << tcu::TestLog::EndMessage;
2346
2347 // report result
2348 if (invalidPixels)
2349 {
2350 log << tcu::TestLog::Message << invalidPixels << " invalid pixel(s) found." << tcu::TestLog::EndMessage;
2351 log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
2352 << tcu::TestLog::Image("Result", "Result", surface)
2353 << tcu::TestLog::Image("ErrorMask", "ErrorMask", errorMask)
2354 << tcu::TestLog::EndImageSet;
2355
2356 return false;
2357 }
2358 else
2359 {
2360 log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
2361 log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
2362 << tcu::TestLog::Image("Result", "Result", surface)
2363 << tcu::TestLog::EndImageSet;
2364
2365 return true;
2366 }
2367 }
2368
2369 } // anonymous
2370
calculateTriangleCoverage(const tcu::Vec4 & p0,const tcu::Vec4 & p1,const tcu::Vec4 & p2,const tcu::IVec2 & pixel,const tcu::IVec2 & viewportSize,int subpixelBits,bool multisample)2371 CoverageType calculateTriangleCoverage (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec4& p2, const tcu::IVec2& pixel, const tcu::IVec2& viewportSize, int subpixelBits, bool multisample)
2372 {
2373 using tcu::I64Vec2;
2374
2375 const deUint64 numSubPixels = ((deUint64)1) << subpixelBits;
2376 const deUint64 pixelHitBoxSize = (multisample) ? (numSubPixels) : 5; //!< 5 = ceil(6 * sqrt(2) / 2) to account for a 3 subpixel fuzz around pixel center
2377 const bool order = isTriangleClockwise(p0, p1, p2); //!< clockwise / counter-clockwise
2378 const tcu::Vec4& orderedP0 = p0; //!< vertices of a clockwise triangle
2379 const tcu::Vec4& orderedP1 = (order) ? (p1) : (p2);
2380 const tcu::Vec4& orderedP2 = (order) ? (p2) : (p1);
2381 const tcu::Vec2 triangleNormalizedDeviceSpace[3] =
2382 {
2383 tcu::Vec2(orderedP0.x() / orderedP0.w(), orderedP0.y() / orderedP0.w()),
2384 tcu::Vec2(orderedP1.x() / orderedP1.w(), orderedP1.y() / orderedP1.w()),
2385 tcu::Vec2(orderedP2.x() / orderedP2.w(), orderedP2.y() / orderedP2.w()),
2386 };
2387 const tcu::Vec2 triangleScreenSpace[3] =
2388 {
2389 (triangleNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
2390 (triangleNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
2391 (triangleNormalizedDeviceSpace[2] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
2392 };
2393
2394 // Broad bounding box - pixel check
2395 {
2396 const float minX = de::min(de::min(triangleScreenSpace[0].x(), triangleScreenSpace[1].x()), triangleScreenSpace[2].x());
2397 const float minY = de::min(de::min(triangleScreenSpace[0].y(), triangleScreenSpace[1].y()), triangleScreenSpace[2].y());
2398 const float maxX = de::max(de::max(triangleScreenSpace[0].x(), triangleScreenSpace[1].x()), triangleScreenSpace[2].x());
2399 const float maxY = de::max(de::max(triangleScreenSpace[0].y(), triangleScreenSpace[1].y()), triangleScreenSpace[2].y());
2400
2401 if ((float)pixel.x() > maxX + 1 ||
2402 (float)pixel.y() > maxY + 1 ||
2403 (float)pixel.x() < minX - 1 ||
2404 (float)pixel.y() < minY - 1)
2405 return COVERAGE_NONE;
2406 }
2407
2408 // Broad triangle - pixel area intersection
2409 {
2410 const DVec2 pixelCenterPosition = DVec2((double)pixel.x(), (double)pixel.y()) * DVec2((double)numSubPixels, (double)numSubPixels) +
2411 DVec2((double)numSubPixels / 2, (double)numSubPixels / 2);
2412 const DVec2 triangleSubPixelSpace[3] =
2413 {
2414 DVec2(triangleScreenSpace[0].x() * (double)numSubPixels, triangleScreenSpace[0].y() * (double)numSubPixels),
2415 DVec2(triangleScreenSpace[1].x() * (double)numSubPixels, triangleScreenSpace[1].y() * (double)numSubPixels),
2416 DVec2(triangleScreenSpace[2].x() * (double)numSubPixels, triangleScreenSpace[2].y() * (double)numSubPixels),
2417 };
2418
2419 // Check (using cross product) if pixel center is
2420 // a) too far from any edge
2421 // b) fully inside all edges
2422 bool insideAllEdges = true;
2423 for (int vtxNdx = 0; vtxNdx < 3; ++vtxNdx)
2424 {
2425 const int otherVtxNdx = (vtxNdx + 1) % 3;
2426 const double maxPixelDistanceSquared = (double)(pixelHitBoxSize * pixelHitBoxSize); // Max distance from the pixel center from within the pixel is (sqrt(2) * boxWidth/2). Use 2x value for rounding tolerance
2427 const DVec2 edge = triangleSubPixelSpace[otherVtxNdx] - triangleSubPixelSpace[vtxNdx];
2428 const DVec2 v = pixelCenterPosition - triangleSubPixelSpace[vtxNdx];
2429 const double crossProduct = (edge.x() * v.y() - edge.y() * v.x());
2430
2431 // distance from edge: (edge x v) / |edge|
2432 // (edge x v) / |edge| > maxPixelDistance
2433 // ==> (edge x v)^2 / edge^2 > maxPixelDistance^2 | edge x v > 0
2434 // ==> (edge x v)^2 > maxPixelDistance^2 * edge^2
2435 if (crossProduct < 0 && crossProduct*crossProduct > maxPixelDistanceSquared * tcu::lengthSquared(edge))
2436 return COVERAGE_NONE;
2437 if (crossProduct < 0 || crossProduct*crossProduct < maxPixelDistanceSquared * tcu::lengthSquared(edge))
2438 insideAllEdges = false;
2439 }
2440
2441 if (insideAllEdges)
2442 return COVERAGE_FULL;
2443 }
2444
2445 // Accurate intersection for edge pixels
2446 {
2447 // In multisampling, the sample points can be anywhere in the pixel, and in single sampling only in the center.
2448 const I64Vec2 pixelCorners[4] =
2449 {
2450 I64Vec2((pixel.x()+0) * numSubPixels, (pixel.y()+0) * numSubPixels),
2451 I64Vec2((pixel.x()+1) * numSubPixels, (pixel.y()+0) * numSubPixels),
2452 I64Vec2((pixel.x()+1) * numSubPixels, (pixel.y()+1) * numSubPixels),
2453 I64Vec2((pixel.x()+0) * numSubPixels, (pixel.y()+1) * numSubPixels),
2454 };
2455
2456 // 3 subpixel tolerance around pixel center to account for accumulated errors during various line rasterization methods
2457 const I64Vec2 pixelCenterCorners[4] =
2458 {
2459 I64Vec2(pixel.x() * numSubPixels + numSubPixels/2 - 3, pixel.y() * numSubPixels + numSubPixels/2 - 3),
2460 I64Vec2(pixel.x() * numSubPixels + numSubPixels/2 + 3, pixel.y() * numSubPixels + numSubPixels/2 - 3),
2461 I64Vec2(pixel.x() * numSubPixels + numSubPixels/2 + 3, pixel.y() * numSubPixels + numSubPixels/2 + 3),
2462 I64Vec2(pixel.x() * numSubPixels + numSubPixels/2 - 3, pixel.y() * numSubPixels + numSubPixels/2 + 3),
2463 };
2464
2465 // both rounding directions
2466 const I64Vec2 triangleSubPixelSpaceFloor[3] =
2467 {
2468 I64Vec2(deFloorFloatToInt32(triangleScreenSpace[0].x() * (float)numSubPixels), deFloorFloatToInt32(triangleScreenSpace[0].y() * (float)numSubPixels)),
2469 I64Vec2(deFloorFloatToInt32(triangleScreenSpace[1].x() * (float)numSubPixels), deFloorFloatToInt32(triangleScreenSpace[1].y() * (float)numSubPixels)),
2470 I64Vec2(deFloorFloatToInt32(triangleScreenSpace[2].x() * (float)numSubPixels), deFloorFloatToInt32(triangleScreenSpace[2].y() * (float)numSubPixels)),
2471 };
2472 const I64Vec2 triangleSubPixelSpaceCeil[3] =
2473 {
2474 I64Vec2(deCeilFloatToInt32(triangleScreenSpace[0].x() * (float)numSubPixels), deCeilFloatToInt32(triangleScreenSpace[0].y() * (float)numSubPixels)),
2475 I64Vec2(deCeilFloatToInt32(triangleScreenSpace[1].x() * (float)numSubPixels), deCeilFloatToInt32(triangleScreenSpace[1].y() * (float)numSubPixels)),
2476 I64Vec2(deCeilFloatToInt32(triangleScreenSpace[2].x() * (float)numSubPixels), deCeilFloatToInt32(triangleScreenSpace[2].y() * (float)numSubPixels)),
2477 };
2478 const I64Vec2* const corners = (multisample) ? (pixelCorners) : (pixelCenterCorners);
2479
2480 // Test if any edge (with any rounding) intersects the pixel (boundary). If it does => Partial. If not => fully inside or outside
2481
2482 for (int edgeNdx = 0; edgeNdx < 3; ++edgeNdx)
2483 for (int startRounding = 0; startRounding < 4; ++startRounding)
2484 for (int endRounding = 0; endRounding < 4; ++endRounding)
2485 {
2486 const int nextEdgeNdx = (edgeNdx+1) % 3;
2487 const I64Vec2 startPos ((startRounding&0x01) ? (triangleSubPixelSpaceFloor[edgeNdx].x()) : (triangleSubPixelSpaceCeil[edgeNdx].x()), (startRounding&0x02) ? (triangleSubPixelSpaceFloor[edgeNdx].y()) : (triangleSubPixelSpaceCeil[edgeNdx].y()));
2488 const I64Vec2 endPos ((endRounding&0x01) ? (triangleSubPixelSpaceFloor[nextEdgeNdx].x()) : (triangleSubPixelSpaceCeil[nextEdgeNdx].x()), (endRounding&0x02) ? (triangleSubPixelSpaceFloor[nextEdgeNdx].y()) : (triangleSubPixelSpaceCeil[nextEdgeNdx].y()));
2489
2490 for (int pixelEdgeNdx = 0; pixelEdgeNdx < 4; ++pixelEdgeNdx)
2491 {
2492 const int pixelEdgeEnd = (pixelEdgeNdx + 1) % 4;
2493
2494 if (lineLineIntersect(startPos, endPos, corners[pixelEdgeNdx], corners[pixelEdgeEnd]))
2495 return COVERAGE_PARTIAL;
2496 }
2497 }
2498
2499 // fully inside or outside
2500 for (int edgeNdx = 0; edgeNdx < 3; ++edgeNdx)
2501 {
2502 const int nextEdgeNdx = (edgeNdx+1) % 3;
2503 const I64Vec2& startPos = triangleSubPixelSpaceFloor[edgeNdx];
2504 const I64Vec2& endPos = triangleSubPixelSpaceFloor[nextEdgeNdx];
2505 const I64Vec2 edge = endPos - startPos;
2506 const I64Vec2 v = corners[0] - endPos;
2507 const deInt64 crossProduct = (edge.x() * v.y() - edge.y() * v.x());
2508
2509 // a corner of the pixel is outside => "fully inside" option is impossible
2510 if (crossProduct < 0)
2511 return COVERAGE_NONE;
2512 }
2513
2514 return COVERAGE_FULL;
2515 }
2516 }
2517
calculateUnderestimateLineCoverage(const tcu::Vec4 & p0,const tcu::Vec4 & p1,const float lineWidth,const tcu::IVec2 & pixel,const tcu::IVec2 & viewportSize)2518 CoverageType calculateUnderestimateLineCoverage (const tcu::Vec4& p0, const tcu::Vec4& p1, const float lineWidth, const tcu::IVec2& pixel, const tcu::IVec2& viewportSize)
2519 {
2520 DE_ASSERT(viewportSize.x() == viewportSize.y() && viewportSize.x() > 0);
2521 DE_ASSERT(p0.w() == 1.0f && p1.w() == 1.0f);
2522
2523 const Vec2 p = Vec2(p0.x(), p0.y());
2524 const Vec2 q = Vec2(p1.x(), p1.y());
2525 const Vec2 pq = Vec2(p1.x() - p0.x(), p1.y() - p0.y());
2526 const Vec2 pqn = normalize(pq);
2527 const Vec2 lw = 0.5f * lineWidth * pqn;
2528 const Vec2 n = Vec2(lw.y(), -lw.x());
2529 const Vec2 vp = Vec2(float(viewportSize.x()), float(viewportSize.y()));
2530 const Vec2 a = 0.5f * (p + Vec2(1.0f, 1.0f)) * vp + n;
2531 const Vec2 b = 0.5f * (p + Vec2(1.0f, 1.0f)) * vp - n;
2532 const Vec2 c = 0.5f * (q + Vec2(1.0f, 1.0f)) * vp - n;
2533 const Vec2 ba = b - a;
2534 const Vec2 bc = b - c;
2535 const float det = ba.x() * bc.y() - ba.y() * bc.x();
2536 int within = 0;
2537
2538 if (det != 0.0f)
2539 {
2540 for (int cornerNdx = 0; cornerNdx < 4; ++cornerNdx)
2541 {
2542 const int pixelCornerOffsetX = ((cornerNdx & 1) ? 1 : 0);
2543 const int pixelCornerOffsetY = ((cornerNdx & 2) ? 1 : 0);
2544 const Vec2 f = Vec2(float(pixel.x() + pixelCornerOffsetX), float(pixel.y() + pixelCornerOffsetY));
2545 const Vec2 bf = b - f;
2546 const float alpha = (bf.x() * bc.y() - bc.x() * bf.y()) / det;
2547 const float beta = (ba.x() * bf.y() - bf.x() * ba.y()) / det;
2548 bool cornerWithin = de::inRange(alpha, 0.0f, 1.0f) && de::inRange(beta, 0.0f, 1.0f);
2549
2550 if (cornerWithin)
2551 within++;
2552 }
2553 }
2554
2555 if (within == 0)
2556 return COVERAGE_NONE;
2557 else if (within == 4)
2558 return COVERAGE_FULL;
2559 else
2560 return COVERAGE_PARTIAL;
2561 }
2562
calculateUnderestimateTriangleCoverage(const tcu::Vec4 & p0,const tcu::Vec4 & p1,const tcu::Vec4 & p2,const tcu::IVec2 & pixel,int subpixelBits,const tcu::IVec2 & viewportSize)2563 CoverageType calculateUnderestimateTriangleCoverage (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec4& p2, const tcu::IVec2& pixel, int subpixelBits, const tcu::IVec2& viewportSize)
2564 {
2565 using tcu::I64Vec2;
2566
2567 const deUint64 numSubPixels = ((deUint64)1) << subpixelBits;
2568 const bool order = isTriangleClockwise(p0, p1, p2); //!< clockwise / counter-clockwise
2569 const tcu::Vec4& orderedP0 = p0; //!< vertices of a clockwise triangle
2570 const tcu::Vec4& orderedP1 = (order) ? (p1) : (p2);
2571 const tcu::Vec4& orderedP2 = (order) ? (p2) : (p1);
2572 const tcu::Vec2 triangleNormalizedDeviceSpace[3] =
2573 {
2574 tcu::Vec2(orderedP0.x() / orderedP0.w(), orderedP0.y() / orderedP0.w()),
2575 tcu::Vec2(orderedP1.x() / orderedP1.w(), orderedP1.y() / orderedP1.w()),
2576 tcu::Vec2(orderedP2.x() / orderedP2.w(), orderedP2.y() / orderedP2.w()),
2577 };
2578 const tcu::Vec2 triangleScreenSpace[3] =
2579 {
2580 (triangleNormalizedDeviceSpace[0] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
2581 (triangleNormalizedDeviceSpace[1] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
2582 (triangleNormalizedDeviceSpace[2] + tcu::Vec2(1.0f, 1.0f)) * 0.5f * tcu::Vec2((float)viewportSize.x(), (float)viewportSize.y()),
2583 };
2584
2585 // Broad bounding box - pixel check
2586 {
2587 const float minX = de::min(de::min(triangleScreenSpace[0].x(), triangleScreenSpace[1].x()), triangleScreenSpace[2].x());
2588 const float minY = de::min(de::min(triangleScreenSpace[0].y(), triangleScreenSpace[1].y()), triangleScreenSpace[2].y());
2589 const float maxX = de::max(de::max(triangleScreenSpace[0].x(), triangleScreenSpace[1].x()), triangleScreenSpace[2].x());
2590 const float maxY = de::max(de::max(triangleScreenSpace[0].y(), triangleScreenSpace[1].y()), triangleScreenSpace[2].y());
2591
2592 if ((float)pixel.x() > maxX + 1 ||
2593 (float)pixel.y() > maxY + 1 ||
2594 (float)pixel.x() < minX - 1 ||
2595 (float)pixel.y() < minY - 1)
2596 return COVERAGE_NONE;
2597 }
2598
2599 // Accurate intersection for edge pixels
2600 {
2601 // In multisampling, the sample points can be anywhere in the pixel, and in single sampling only in the center.
2602 const I64Vec2 pixelCorners[4] =
2603 {
2604 I64Vec2((pixel.x()+0) * numSubPixels, (pixel.y()+0) * numSubPixels),
2605 I64Vec2((pixel.x()+1) * numSubPixels, (pixel.y()+0) * numSubPixels),
2606 I64Vec2((pixel.x()+1) * numSubPixels, (pixel.y()+1) * numSubPixels),
2607 I64Vec2((pixel.x()+0) * numSubPixels, (pixel.y()+1) * numSubPixels),
2608 };
2609 // both rounding directions
2610 const I64Vec2 triangleSubPixelSpaceFloor[3] =
2611 {
2612 I64Vec2(deFloorFloatToInt32(triangleScreenSpace[0].x() * (float)numSubPixels), deFloorFloatToInt32(triangleScreenSpace[0].y() * (float)numSubPixels)),
2613 I64Vec2(deFloorFloatToInt32(triangleScreenSpace[1].x() * (float)numSubPixels), deFloorFloatToInt32(triangleScreenSpace[1].y() * (float)numSubPixels)),
2614 I64Vec2(deFloorFloatToInt32(triangleScreenSpace[2].x() * (float)numSubPixels), deFloorFloatToInt32(triangleScreenSpace[2].y() * (float)numSubPixels)),
2615 };
2616 const I64Vec2 triangleSubPixelSpaceCeil[3] =
2617 {
2618 I64Vec2(deCeilFloatToInt32(triangleScreenSpace[0].x() * (float)numSubPixels), deCeilFloatToInt32(triangleScreenSpace[0].y() * (float)numSubPixels)),
2619 I64Vec2(deCeilFloatToInt32(triangleScreenSpace[1].x() * (float)numSubPixels), deCeilFloatToInt32(triangleScreenSpace[1].y() * (float)numSubPixels)),
2620 I64Vec2(deCeilFloatToInt32(triangleScreenSpace[2].x() * (float)numSubPixels), deCeilFloatToInt32(triangleScreenSpace[2].y() * (float)numSubPixels)),
2621 };
2622
2623 // Test if any edge (with any rounding) intersects the pixel (boundary). If it does => Partial. If not => fully inside or outside
2624
2625 for (int edgeNdx = 0; edgeNdx < 3; ++edgeNdx)
2626 for (int startRounding = 0; startRounding < 4; ++startRounding)
2627 for (int endRounding = 0; endRounding < 4; ++endRounding)
2628 {
2629 const int nextEdgeNdx = (edgeNdx+1) % 3;
2630 const I64Vec2 startPos ((startRounding&0x01) ? (triangleSubPixelSpaceFloor[edgeNdx].x()) : (triangleSubPixelSpaceCeil[edgeNdx].x()), (startRounding&0x02) ? (triangleSubPixelSpaceFloor[edgeNdx].y()) : (triangleSubPixelSpaceCeil[edgeNdx].y()));
2631 const I64Vec2 endPos ((endRounding&0x01) ? (triangleSubPixelSpaceFloor[nextEdgeNdx].x()) : (triangleSubPixelSpaceCeil[nextEdgeNdx].x()), (endRounding&0x02) ? (triangleSubPixelSpaceFloor[nextEdgeNdx].y()) : (triangleSubPixelSpaceCeil[nextEdgeNdx].y()));
2632
2633 for (int pixelEdgeNdx = 0; pixelEdgeNdx < 4; ++pixelEdgeNdx)
2634 {
2635 const int pixelEdgeEnd = (pixelEdgeNdx + 1) % 4;
2636
2637 if (lineLineIntersect(startPos, endPos, pixelCorners[pixelEdgeNdx], pixelCorners[pixelEdgeEnd]))
2638 return COVERAGE_PARTIAL;
2639 }
2640 }
2641
2642 // fully inside or outside
2643 for (int edgeNdx = 0; edgeNdx < 3; ++edgeNdx)
2644 {
2645 const int nextEdgeNdx = (edgeNdx+1) % 3;
2646 const I64Vec2& startPos = triangleSubPixelSpaceFloor[edgeNdx];
2647 const I64Vec2& endPos = triangleSubPixelSpaceFloor[nextEdgeNdx];
2648 const I64Vec2 edge = endPos - startPos;
2649 const I64Vec2 v = pixelCorners[0] - endPos;
2650 const deInt64 crossProduct = (edge.x() * v.y() - edge.y() * v.x());
2651
2652 // a corner of the pixel is outside => "fully inside" option is impossible
2653 if (crossProduct < 0)
2654 return COVERAGE_NONE;
2655 }
2656
2657 return COVERAGE_FULL;
2658 }
2659 }
2660
logTriangleGroupRasterizationStash(const tcu::Surface & surface,tcu::TestLog & log,VerifyTriangleGroupRasterizationLogStash & logStash)2661 static void logTriangleGroupRasterizationStash (const tcu::Surface& surface, tcu::TestLog& log, VerifyTriangleGroupRasterizationLogStash& logStash)
2662 {
2663 // Output results
2664 log << tcu::TestLog::Message << "Verifying rasterization result." << tcu::TestLog::EndMessage;
2665
2666 for (size_t msgNdx = 0; msgNdx < logStash.messages.size(); ++msgNdx)
2667 log << tcu::TestLog::Message << logStash.messages[msgNdx] << tcu::TestLog::EndMessage;
2668
2669 if (!logStash.result)
2670 {
2671 log << tcu::TestLog::Message << "Invalid pixels found:\n\t"
2672 << logStash.missingPixels << " missing pixels. (Marked with purple)\n\t"
2673 << logStash.unexpectedPixels << " incorrectly filled pixels. (Marked with red)\n\t"
2674 << "Unknown (subpixel on edge) pixels are marked with yellow."
2675 << tcu::TestLog::EndMessage;
2676 log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
2677 << tcu::TestLog::Image("Result", "Result", surface)
2678 << tcu::TestLog::Image("ErrorMask", "ErrorMask", logStash.errorMask)
2679 << tcu::TestLog::EndImageSet;
2680 }
2681 else
2682 {
2683 log << tcu::TestLog::Message << "No invalid pixels found." << tcu::TestLog::EndMessage;
2684 log << tcu::TestLog::ImageSet("Verification result", "Result of rendering")
2685 << tcu::TestLog::Image("Result", "Result", surface)
2686 << tcu::TestLog::EndImageSet;
2687 }
2688 }
2689
verifyTriangleGroupRasterization(const tcu::Surface & surface,const TriangleSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log,VerificationMode mode,VerifyTriangleGroupRasterizationLogStash * logStash,const bool vulkanLinesTest)2690 bool verifyTriangleGroupRasterization (const tcu::Surface& surface, const TriangleSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log, VerificationMode mode, VerifyTriangleGroupRasterizationLogStash* logStash, const bool vulkanLinesTest)
2691 {
2692 DE_ASSERT(mode < VERIFICATIONMODE_LAST);
2693
2694 const tcu::RGBA backGroundColor = tcu::RGBA(0, 0, 0, 255);
2695 const tcu::RGBA triangleColor = tcu::RGBA(255, 255, 255, 255);
2696 const tcu::RGBA missingPixelColor = tcu::RGBA(255, 0, 255, 255);
2697 const tcu::RGBA unexpectedPixelColor = tcu::RGBA(255, 0, 0, 255);
2698 const tcu::RGBA partialPixelColor = tcu::RGBA(255, 255, 0, 255);
2699 const tcu::RGBA primitivePixelColor = tcu::RGBA(30, 30, 30, 255);
2700 const int weakVerificationThreshold = 10;
2701 const int weakerVerificationThreshold = 25;
2702 const bool multisampled = (args.numSamples != 0);
2703 const tcu::IVec2 viewportSize = tcu::IVec2(surface.getWidth(), surface.getHeight());
2704 int missingPixels = 0;
2705 int unexpectedPixels = 0;
2706 int subPixelBits = args.subpixelBits;
2707 tcu::TextureLevel coverageMap (tcu::TextureFormat(tcu::TextureFormat::R, tcu::TextureFormat::UNSIGNED_INT8), surface.getWidth(), surface.getHeight());
2708 tcu::Surface errorMask (surface.getWidth(), surface.getHeight());
2709 bool result = false;
2710
2711 // subpixel bits in a valid range?
2712
2713 if (subPixelBits < 0)
2714 {
2715 log << tcu::TestLog::Message << "Invalid subpixel count (" << subPixelBits << "), assuming 0" << tcu::TestLog::EndMessage;
2716 subPixelBits = 0;
2717 }
2718 else if (subPixelBits > 16)
2719 {
2720 // At high subpixel bit counts we might overflow. Checking at lower bit count is ok, but is less strict
2721 log << tcu::TestLog::Message << "Subpixel count is greater than 16 (" << subPixelBits << "). Checking results using less strict 16 bit requirements. This may produce false positives." << tcu::TestLog::EndMessage;
2722 subPixelBits = 16;
2723 }
2724
2725 // generate coverage map
2726
2727 tcu::clear(coverageMap.getAccess(), tcu::IVec4(COVERAGE_NONE, 0, 0, 0));
2728
2729 for (int triNdx = 0; triNdx < (int)scene.triangles.size(); ++triNdx)
2730 {
2731 const tcu::IVec4 aabb = getTriangleAABB(scene.triangles[triNdx], viewportSize);
2732
2733 for (int y = de::max(0, aabb.y()); y <= de::min(aabb.w(), coverageMap.getHeight() - 1); ++y)
2734 for (int x = de::max(0, aabb.x()); x <= de::min(aabb.z(), coverageMap.getWidth() - 1); ++x)
2735 {
2736 if (coverageMap.getAccess().getPixelUint(x, y).x() == COVERAGE_FULL)
2737 continue;
2738
2739 const CoverageType coverage = calculateTriangleCoverage(scene.triangles[triNdx].positions[0],
2740 scene.triangles[triNdx].positions[1],
2741 scene.triangles[triNdx].positions[2],
2742 tcu::IVec2(x, y),
2743 viewportSize,
2744 subPixelBits,
2745 multisampled);
2746
2747 if (coverage == COVERAGE_FULL)
2748 {
2749 coverageMap.getAccess().setPixel(tcu::IVec4(COVERAGE_FULL, 0, 0, 0), x, y);
2750 }
2751 else if (coverage == COVERAGE_PARTIAL)
2752 {
2753 CoverageType resultCoverage = COVERAGE_PARTIAL;
2754
2755 // Sharing an edge with another triangle?
2756 // There should always be such a triangle, but the pixel in the other triangle might be
2757 // on multiple edges, some of which are not shared. In these cases the coverage cannot be determined.
2758 // Assume full coverage if the pixel is only on a shared edge in shared triangle too.
2759 if (pixelOnlyOnASharedEdge(tcu::IVec2(x, y), scene.triangles[triNdx], viewportSize))
2760 {
2761 bool friendFound = false;
2762 for (int friendTriNdx = 0; friendTriNdx < (int)scene.triangles.size(); ++friendTriNdx)
2763 {
2764 if (friendTriNdx == triNdx)
2765 continue;
2766
2767 const CoverageType friendCoverage = calculateTriangleCoverage(scene.triangles[friendTriNdx].positions[0],
2768 scene.triangles[friendTriNdx].positions[1],
2769 scene.triangles[friendTriNdx].positions[2],
2770 tcu::IVec2(x, y),
2771 viewportSize,
2772 subPixelBits,
2773 multisampled);
2774
2775 if (friendCoverage != COVERAGE_NONE && pixelOnlyOnASharedEdge(tcu::IVec2(x, y), scene.triangles[friendTriNdx], viewportSize))
2776 {
2777 friendFound = true;
2778 break;
2779 }
2780 }
2781
2782 if (friendFound)
2783 resultCoverage = COVERAGE_FULL;
2784 }
2785
2786 coverageMap.getAccess().setPixel(tcu::IVec4(resultCoverage, 0, 0, 0), x, y);
2787 }
2788 }
2789 }
2790
2791 // check pixels
2792
2793 tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
2794
2795 // Use these to sanity check there is something drawn when a test expects something else than an empty picture.
2796 bool referenceEmpty = true;
2797 bool resultEmpty = true;
2798
2799 for (int y = 0; y < surface.getHeight(); ++y)
2800 for (int x = 0; x < surface.getWidth(); ++x)
2801 {
2802 const tcu::RGBA color = surface.getPixel(x, y);
2803 const bool imageNoCoverage = compareColors(color, backGroundColor, args.redBits, args.greenBits, args.blueBits);
2804 const bool imageFullCoverage = compareColors(color, triangleColor, args.redBits, args.greenBits, args.blueBits);
2805 CoverageType referenceCoverage = (CoverageType)coverageMap.getAccess().getPixelUint(x, y).x();
2806
2807 if (!imageNoCoverage)
2808 resultEmpty = false;
2809
2810 switch (referenceCoverage)
2811 {
2812 case COVERAGE_NONE:
2813 if (!imageNoCoverage)
2814 {
2815 // coverage where there should not be
2816 ++unexpectedPixels;
2817 errorMask.setPixel(x, y, unexpectedPixelColor);
2818 }
2819 break;
2820
2821 case COVERAGE_PARTIAL:
2822 {
2823 referenceEmpty = false;
2824 bool foundFragment = false;
2825 if (vulkanLinesTest == true)
2826 {
2827 for (int dy = -1; dy < 2 && !foundFragment; ++dy)
2828 for (int dx = -1; dx < 2 && !foundFragment; ++dx)
2829 {
2830 if (x + dx >= 0 && x + dx != surface.getWidth() && y + dy >= 0 && y + dy != surface.getHeight()
2831 && (CoverageType)coverageMap.getAccess().getPixelUint(x + dx, y + dy).x() != COVERAGE_NONE)
2832 {
2833 const tcu::RGBA color2 = surface.getPixel(x + dx , y + dy);
2834 if (compareColors(color2, triangleColor, args.redBits, args.greenBits, args.blueBits))
2835 foundFragment = true;
2836 }
2837 }
2838 }
2839 // anything goes
2840 if (foundFragment == false)
2841 {
2842 errorMask.setPixel(x, y, partialPixelColor);
2843 if (vulkanLinesTest == true)
2844 ++missingPixels;
2845 }
2846 }
2847 break;
2848
2849 case COVERAGE_FULL:
2850 referenceEmpty = false;
2851 if (!imageFullCoverage)
2852 {
2853 // no coverage where there should be
2854 ++missingPixels;
2855 errorMask.setPixel(x, y, missingPixelColor);
2856 }
2857 else
2858 {
2859 errorMask.setPixel(x, y, primitivePixelColor);
2860 }
2861 break;
2862
2863 default:
2864 DE_ASSERT(false);
2865 }
2866 }
2867
2868 if (((mode == VERIFICATIONMODE_STRICT) && (missingPixels + unexpectedPixels > 0)) ||
2869 ((mode == VERIFICATIONMODE_WEAK) && (missingPixels + unexpectedPixels > weakVerificationThreshold)) ||
2870 ((mode == VERIFICATIONMODE_WEAKER) && (missingPixels + unexpectedPixels > weakerVerificationThreshold)) ||
2871 ((mode == VERIFICATIONMODE_SMOOTH) && (missingPixels > weakVerificationThreshold)) ||
2872 referenceEmpty != resultEmpty)
2873 {
2874 result = false;
2875 }
2876 else
2877 {
2878 result = true;
2879 }
2880
2881 // Output or stash results
2882 {
2883 VerifyTriangleGroupRasterizationLogStash* tempLogStash = (logStash == DE_NULL) ? new VerifyTriangleGroupRasterizationLogStash : logStash;
2884
2885 tempLogStash->result = result;
2886 tempLogStash->missingPixels = missingPixels;
2887 tempLogStash->unexpectedPixels = unexpectedPixels;
2888 tempLogStash->errorMask = errorMask;
2889
2890 if (logStash == DE_NULL)
2891 {
2892 logTriangleGroupRasterizationStash(surface, log, *tempLogStash);
2893 delete tempLogStash;
2894 }
2895 }
2896
2897 return result;
2898 }
2899
verifyLineGroupRasterization(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)2900 bool verifyLineGroupRasterization (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
2901 {
2902 const bool multisampled = args.numSamples != 0;
2903
2904 if (multisampled)
2905 return verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_NO_CLIPPING, DE_NULL, false, true);
2906 else
2907 return verifySinglesampleLineGroupRasterization(surface, scene, args, log);
2908 }
2909
verifyClippedTriangulatedLineGroupRasterization(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)2910 bool verifyClippedTriangulatedLineGroupRasterization (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
2911 {
2912 return verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_USE_CLIPPING_BOX, DE_NULL, false, true);
2913 }
2914
verifyRelaxedLineGroupRasterization(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log,const bool vulkanLinesTest,const bool strict)2915 bool verifyRelaxedLineGroupRasterization (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log, const bool vulkanLinesTest, const bool strict)
2916 {
2917 VerifyTriangleGroupRasterizationLogStash useClippingLogStash;
2918 VerifyTriangleGroupRasterizationLogStash noClippingLogStash;
2919 VerifyTriangleGroupRasterizationLogStash useClippingForcedStrictLogStash;
2920 VerifyTriangleGroupRasterizationLogStash noClippingForcedStrictLogStash;
2921
2922 if (verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_USE_CLIPPING_BOX, &useClippingLogStash, vulkanLinesTest, strict))
2923 {
2924 logTriangleGroupRasterizationStash(surface, log, useClippingLogStash);
2925
2926 return true;
2927 }
2928 else if (verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_NO_CLIPPING, &noClippingLogStash, vulkanLinesTest, strict))
2929 {
2930 logTriangleGroupRasterizationStash(surface, log, noClippingLogStash);
2931
2932 return true;
2933 }
2934 else if (strict == false && verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_USE_CLIPPING_BOX, &useClippingForcedStrictLogStash, vulkanLinesTest, true))
2935 {
2936 logTriangleGroupRasterizationStash(surface, log, useClippingForcedStrictLogStash);
2937
2938 return true;
2939 }
2940 else if (strict == false && verifyMultisampleLineGroupRasterization(surface, scene, args, log, CLIPMODE_NO_CLIPPING, &noClippingForcedStrictLogStash, vulkanLinesTest, true))
2941 {
2942 logTriangleGroupRasterizationStash(surface, log, noClippingForcedStrictLogStash);
2943
2944 return true;
2945 }
2946 else if (strict == false && args.numSamples == 0 && verifyLineGroupRasterization(surface, scene, args, log))
2947 {
2948 return true;
2949 }
2950 else
2951 {
2952 log << tcu::TestLog::Message << "Relaxed rasterization failed, details follow." << tcu::TestLog::EndMessage;
2953
2954 logTriangleGroupRasterizationStash(surface, log, useClippingLogStash);
2955 logTriangleGroupRasterizationStash(surface, log, noClippingLogStash);
2956
2957 if (strict == false)
2958 {
2959 logTriangleGroupRasterizationStash(surface, log, useClippingForcedStrictLogStash);
2960 logTriangleGroupRasterizationStash(surface, log, noClippingForcedStrictLogStash);
2961 }
2962
2963 return false;
2964 }
2965 }
2966
verifyPointGroupRasterization(const tcu::Surface & surface,const PointSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)2967 bool verifyPointGroupRasterization (const tcu::Surface& surface, const PointSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
2968 {
2969 // Splitting to triangles is a valid solution in multisampled cases and even in non-multisample cases too.
2970 return verifyMultisamplePointGroupRasterization(surface, scene, args, log);
2971 }
2972
verifyTriangleGroupInterpolation(const tcu::Surface & surface,const TriangleSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)2973 bool verifyTriangleGroupInterpolation (const tcu::Surface& surface, const TriangleSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
2974 {
2975 VerifyTriangleGroupInterpolationLogStash logStash;
2976 const bool result = verifyTriangleGroupInterpolationWithInterpolator(surface, scene, args, logStash, TriangleInterpolator(scene));
2977
2978 logTriangleGroupnterpolationStash(surface, log, logStash);
2979
2980 return result;
2981 }
2982
verifyLineGroupInterpolation(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log)2983 LineInterpolationMethod verifyLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log)
2984 {
2985 const bool multisampled = args.numSamples != 0;
2986
2987 if (multisampled)
2988 {
2989 if (verifyMultisampleLineGroupInterpolation(surface, scene, args, log))
2990 return LINEINTERPOLATION_STRICTLY_CORRECT;
2991 return LINEINTERPOLATION_INCORRECT;
2992 }
2993 else
2994 {
2995 const bool isNarrow = (scene.lineWidth == 1.0f);
2996
2997 // accurate interpolation
2998 if (isNarrow)
2999 {
3000 if (verifySinglesampleNarrowLineGroupInterpolation(surface, scene, args, log))
3001 return LINEINTERPOLATION_STRICTLY_CORRECT;
3002 }
3003 else
3004 {
3005 if (verifySinglesampleWideLineGroupInterpolation(surface, scene, args, log))
3006 return LINEINTERPOLATION_STRICTLY_CORRECT;
3007 }
3008
3009 // check with projected (inaccurate) interpolation
3010 log << tcu::TestLog::Message << "Accurate verification failed, checking with projected weights (inaccurate equation)." << tcu::TestLog::EndMessage;
3011 if (verifyLineGroupInterpolationWithProjectedWeights(surface, scene, args, log))
3012 return LINEINTERPOLATION_PROJECTED;
3013
3014 return LINEINTERPOLATION_INCORRECT;
3015 }
3016 }
3017
verifyTriangulatedLineGroupInterpolation(const tcu::Surface & surface,const LineSceneSpec & scene,const RasterizationArguments & args,tcu::TestLog & log,const bool strictMode,const bool allowBresenhamForNonStrictLines)3018 bool verifyTriangulatedLineGroupInterpolation (const tcu::Surface& surface, const LineSceneSpec& scene, const RasterizationArguments& args, tcu::TestLog& log, const bool strictMode, const bool allowBresenhamForNonStrictLines)
3019 {
3020 return verifyMultisampleLineGroupInterpolation(surface, scene, args, log, strictMode, allowBresenhamForNonStrictLines);
3021 }
3022
3023 } // tcu
3024