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 Image comparison utilities.
22 *//*--------------------------------------------------------------------*/
23
24 #include "tcuImageCompare.hpp"
25 #include "tcuSurface.hpp"
26 #include "tcuFuzzyImageCompare.hpp"
27 #include "tcuBilinearImageCompare.hpp"
28 #include "tcuTestLog.hpp"
29 #include "tcuVector.hpp"
30 #include "tcuVectorUtil.hpp"
31 #include "tcuRGBA.hpp"
32 #include "tcuTexture.hpp"
33 #include "tcuTextureUtil.hpp"
34 #include "tcuFloat.hpp"
35
36 #include <string.h>
37
38 namespace tcu
39 {
40
41 namespace
42 {
43
computeScaleAndBias(const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,tcu::Vec4 & scale,tcu::Vec4 & bias)44 void computeScaleAndBias(const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
45 tcu::Vec4 &scale, tcu::Vec4 &bias)
46 {
47 Vec4 minVal;
48 Vec4 maxVal;
49 const float eps = 0.0001f;
50
51 {
52 Vec4 refMin;
53 Vec4 refMax;
54 estimatePixelValueRange(reference, refMin, refMax);
55
56 minVal = refMin;
57 maxVal = refMax;
58 }
59
60 {
61 Vec4 resMin;
62 Vec4 resMax;
63
64 estimatePixelValueRange(result, resMin, resMax);
65
66 minVal[0] = de::min(minVal[0], resMin[0]);
67 minVal[1] = de::min(minVal[1], resMin[1]);
68 minVal[2] = de::min(minVal[2], resMin[2]);
69 minVal[3] = de::min(minVal[3], resMin[3]);
70
71 maxVal[0] = de::max(maxVal[0], resMax[0]);
72 maxVal[1] = de::max(maxVal[1], resMax[1]);
73 maxVal[2] = de::max(maxVal[2], resMax[2]);
74 maxVal[3] = de::max(maxVal[3], resMax[3]);
75 }
76
77 for (int c = 0; c < 4; c++)
78 {
79 if (maxVal[c] - minVal[c] < eps)
80 {
81 scale[c] = (maxVal[c] < eps) ? 1.0f : (1.0f / maxVal[c]);
82 bias[c] = (c == 3) ? (1.0f - maxVal[c] * scale[c]) : (0.0f - minVal[c] * scale[c]);
83 }
84 else
85 {
86 scale[c] = 1.0f / (maxVal[c] - minVal[c]);
87 bias[c] = 0.0f - minVal[c] * scale[c];
88 }
89 }
90 }
91
findNumPositionDeviationFailingPixels(const PixelBufferAccess & errorMask,const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const UVec4 & threshold,const tcu::IVec3 & maxPositionDeviation,bool acceptOutOfBoundsAsAnyValue)92 static int findNumPositionDeviationFailingPixels(const PixelBufferAccess &errorMask,
93 const ConstPixelBufferAccess &reference,
94 const ConstPixelBufferAccess &result, const UVec4 &threshold,
95 const tcu::IVec3 &maxPositionDeviation,
96 bool acceptOutOfBoundsAsAnyValue)
97 {
98 const tcu::IVec4 okColor(0, 255, 0, 255);
99 const tcu::IVec4 errorColor(255, 0, 0, 255);
100 const int width = reference.getWidth();
101 const int height = reference.getHeight();
102 const int depth = reference.getDepth();
103 int numFailingPixels = 0;
104
105 // Accept pixels "sampling" over the image bounds pixels since "taps" could be anything
106 const int beginX = (acceptOutOfBoundsAsAnyValue) ? (maxPositionDeviation.x()) : (0);
107 const int beginY = (acceptOutOfBoundsAsAnyValue) ? (maxPositionDeviation.y()) : (0);
108 const int beginZ = (acceptOutOfBoundsAsAnyValue) ? (maxPositionDeviation.z()) : (0);
109 const int endX = (acceptOutOfBoundsAsAnyValue) ? (width - maxPositionDeviation.x()) : (width);
110 const int endY = (acceptOutOfBoundsAsAnyValue) ? (height - maxPositionDeviation.y()) : (height);
111 const int endZ = (acceptOutOfBoundsAsAnyValue) ? (depth - maxPositionDeviation.z()) : (depth);
112
113 TCU_CHECK_INTERNAL(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth);
114 DE_ASSERT(endX > 0 && endY > 0 && endZ > 0); // most likely a bug
115
116 tcu::clear(errorMask, okColor);
117
118 for (int z = beginZ; z < endZ; z++)
119 {
120 for (int y = beginY; y < endY; y++)
121 {
122 for (int x = beginX; x < endX; x++)
123 {
124 const IVec4 refPix = reference.getPixelInt(x, y, z);
125 const IVec4 cmpPix = result.getPixelInt(x, y, z);
126
127 // Exact match
128 {
129 const UVec4 diff = abs(refPix - cmpPix).cast<uint32_t>();
130 const bool isOk = boolAll(lessThanEqual(diff, threshold));
131
132 if (isOk)
133 continue;
134 }
135
136 // Find matching pixels for both result and reference pixel
137
138 {
139 bool pixelFoundForReference = false;
140
141 // Find deviated result pixel for reference
142
143 for (int sz = de::max(0, z - maxPositionDeviation.z());
144 sz <= de::min(depth - 1, z + maxPositionDeviation.z()) && !pixelFoundForReference; ++sz)
145 for (int sy = de::max(0, y - maxPositionDeviation.y());
146 sy <= de::min(height - 1, y + maxPositionDeviation.y()) && !pixelFoundForReference; ++sy)
147 for (int sx = de::max(0, x - maxPositionDeviation.x());
148 sx <= de::min(width - 1, x + maxPositionDeviation.x()) && !pixelFoundForReference;
149 ++sx)
150 {
151 const IVec4 deviatedCmpPix = result.getPixelInt(sx, sy, sz);
152 const UVec4 diff = abs(refPix - deviatedCmpPix).cast<uint32_t>();
153 const bool isOk = boolAll(lessThanEqual(diff, threshold));
154
155 pixelFoundForReference = isOk;
156 }
157
158 if (!pixelFoundForReference)
159 {
160 errorMask.setPixel(errorColor, x, y, z);
161 ++numFailingPixels;
162 continue;
163 }
164 }
165 {
166 bool pixelFoundForResult = false;
167
168 // Find deviated reference pixel for result
169
170 for (int sz = de::max(0, z - maxPositionDeviation.z());
171 sz <= de::min(depth - 1, z + maxPositionDeviation.z()) && !pixelFoundForResult; ++sz)
172 for (int sy = de::max(0, y - maxPositionDeviation.y());
173 sy <= de::min(height - 1, y + maxPositionDeviation.y()) && !pixelFoundForResult; ++sy)
174 for (int sx = de::max(0, x - maxPositionDeviation.x());
175 sx <= de::min(width - 1, x + maxPositionDeviation.x()) && !pixelFoundForResult; ++sx)
176 {
177 const IVec4 deviatedRefPix = reference.getPixelInt(sx, sy, sz);
178 const UVec4 diff = abs(cmpPix - deviatedRefPix).cast<uint32_t>();
179 const bool isOk = boolAll(lessThanEqual(diff, threshold));
180
181 pixelFoundForResult = isOk;
182 }
183
184 if (!pixelFoundForResult)
185 {
186 errorMask.setPixel(errorColor, x, y, z);
187 ++numFailingPixels;
188 continue;
189 }
190 }
191 }
192 }
193 }
194
195 return numFailingPixels;
196 }
197
198 } // namespace
199
200 /*--------------------------------------------------------------------*//*!
201 * \brief Fuzzy image comparison
202 *
203 * This image comparison is designed for comparing images rendered by 3D
204 * graphics APIs such as OpenGL. The comparison allows small local differences
205 * and compensates for aliasing.
206 *
207 * The algorithm first performs light blurring on both images and then
208 * does per-pixel analysis. Pixels are compared to 3x3 bilinear surface
209 * defined by adjecent pixels. This compensates for both 1-pixel deviations
210 * in geometry and aliasing in texture data.
211 *
212 * Error metric is computed based on the differences. On valid images the
213 * metric is usually <0.01. Thus good threshold values are in range 0.02 to
214 * 0.05.
215 *
216 * On failure error image is generated that shows where the failing pixels
217 * are.
218 *
219 * \note Currently supports only UNORM_INT8 formats
220 * \param log Test log for results
221 * \param imageSetName Name for image set when logging results
222 * \param imageSetDesc Description for image set
223 * \param reference Reference image
224 * \param result Result image
225 * \param threshold Error metric threshold (good values are 0.02-0.05)
226 * \param logMode Logging mode
227 * \return true if comparison passes, false otherwise
228 *//*--------------------------------------------------------------------*/
fuzzyCompare(TestLog & log,const char * imageSetName,const char * imageSetDesc,const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,float threshold,CompareLogMode logMode)229 bool fuzzyCompare(TestLog &log, const char *imageSetName, const char *imageSetDesc,
230 const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result, float threshold,
231 CompareLogMode logMode)
232 {
233 FuzzyCompareParams params; // Use defaults.
234 TextureLevel errorMask(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), reference.getWidth(),
235 reference.getHeight());
236 float difference = fuzzyCompare(params, reference, result, errorMask.getAccess());
237 bool isOk = difference <= threshold;
238 Vec4 pixelBias(0.0f, 0.0f, 0.0f, 0.0f);
239 Vec4 pixelScale(1.0f, 1.0f, 1.0f, 1.0f);
240
241 if (!isOk || logMode == COMPARE_LOG_EVERYTHING)
242 {
243 // Generate more accurate error mask.
244 params.maxSampleSkip = 0;
245 fuzzyCompare(params, reference, result, errorMask.getAccess());
246
247 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8) &&
248 reference.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
249 computeScaleAndBias(reference, result, pixelScale, pixelBias);
250
251 if (!isOk)
252 log << TestLog::Message << "Image comparison failed: difference = " << difference
253 << ", threshold = " << threshold << TestLog::EndMessage;
254
255 log << TestLog::ImageSet(imageSetName, imageSetDesc)
256 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
257 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
258 << TestLog::Image("ErrorMask", "Error mask", errorMask) << TestLog::EndImageSet;
259 }
260 else if (logMode == COMPARE_LOG_RESULT)
261 {
262 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
263 computePixelScaleBias(result, pixelScale, pixelBias);
264
265 log << TestLog::ImageSet(imageSetName, imageSetDesc)
266 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) << TestLog::EndImageSet;
267 }
268
269 return isOk;
270 }
271
272 /*--------------------------------------------------------------------*//*!
273 * \brief Per-pixel bitwise comparison
274 *
275 * This compare expects bit precision between result and reference image.
276 * Comparison fails if any pixels do not match bitwise.
277 * Reference and result format must match.
278 *
279 * This comparison can be used for any type texture formats since it does
280 * not care about types.
281 *
282 * On failure error image is generated that shows where the failing pixels
283 * are.
284 *
285 * \param log Test log for results
286 * \param imageSetName Name for image set when logging results
287 * \param imageSetDesc Description for image set
288 * \param reference Reference image
289 * \param result Result image
290 * \param logMode Logging mode
291 * \return true if comparison passes, false otherwise
292 *//*--------------------------------------------------------------------*/
bitwiseCompare(TestLog & log,const char * imageSetName,const char * imageSetDesc,const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,CompareLogMode logMode)293 bool bitwiseCompare(TestLog &log, const char *imageSetName, const char *imageSetDesc,
294 const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
295 CompareLogMode logMode)
296 {
297 int width = reference.getWidth();
298 int height = reference.getHeight();
299 int depth = reference.getDepth();
300 TCU_CHECK_INTERNAL(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth);
301
302 // Enforce texture has same channel count and channel size
303 TCU_CHECK_INTERNAL(reference.getFormat() == result.getFormat());
304 result.getPixelPitch();
305
306 TextureLevel errorMaskStorage(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth);
307 PixelBufferAccess errorMask = errorMaskStorage.getAccess();
308 Vec4 pixelBias(0.0f, 0.0f, 0.0f, 0.0f);
309 Vec4 pixelScale(1.0f, 1.0f, 1.0f, 1.0f);
310 bool compareOk = true;
311
312 for (int z = 0; z < depth; z++)
313 {
314 for (int y = 0; y < height; y++)
315 {
316 for (int x = 0; x < width; x++)
317 {
318 const U64Vec4 refPix = reference.getPixelBitsAsUint64(x, y, z);
319 const U64Vec4 cmpPix = result.getPixelBitsAsUint64(x, y, z);
320 const bool isOk = (refPix == cmpPix);
321
322 errorMask.setPixel(isOk ? IVec4(0, 0xff, 0, 0xff) : IVec4(0xff, 0, 0, 0xff), x, y, z);
323 compareOk &= isOk;
324 }
325 }
326 }
327
328 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING)
329 {
330 {
331 const auto refChannelClass = tcu::getTextureChannelClass(reference.getFormat().type);
332 const auto resChannelClass = tcu::getTextureChannelClass(result.getFormat().type);
333
334 const bool refIsUint8 = (reference.getFormat().type == TextureFormat::UNSIGNED_INT8);
335 const bool resIsUint8 = (result.getFormat().type == TextureFormat::UNSIGNED_INT8);
336
337 const bool calcScaleBias =
338 ((refChannelClass != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT && !refIsUint8) ||
339 (resChannelClass != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT && !resIsUint8));
340
341 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images.
342 if (calcScaleBias)
343 {
344 computeScaleAndBias(reference, result, pixelScale, pixelBias);
345 log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale
346 << " + " << pixelBias << TestLog::EndMessage;
347 }
348 }
349
350 if (!compareOk)
351 log << TestLog::Message
352 << "Image comparison failed: Pixels with different values were found when bitwise precision is expected"
353 << TestLog::EndMessage;
354
355 log << TestLog::ImageSet(imageSetName, imageSetDesc)
356 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
357 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
358 << TestLog::Image("ErrorMask", "Error mask", errorMask) << TestLog::EndImageSet;
359 }
360 else if (logMode == COMPARE_LOG_RESULT)
361 {
362 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
363 computePixelScaleBias(result, pixelScale, pixelBias);
364
365 log << TestLog::ImageSet(imageSetName, imageSetDesc)
366 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) << TestLog::EndImageSet;
367 }
368
369 return compareOk;
370 }
371
372 /*--------------------------------------------------------------------*//*!
373 * \brief Fuzzy image comparison
374 *
375 * This image comparison is designed for comparing images rendered by 3D
376 * graphics APIs such as OpenGL. The comparison allows small local differences
377 * and compensates for aliasing.
378 *
379 * The algorithm first performs light blurring on both images and then
380 * does per-pixel analysis. Pixels are compared to 3x3 bilinear surface
381 * defined by adjecent pixels. This compensates for both 1-pixel deviations
382 * in geometry and aliasing in texture data.
383 *
384 * Error metric is computed based on the differences. On valid images the
385 * metric is usually <0.01. Thus good threshold values are in range 0.02 to
386 * 0.05.
387 *
388 * On failure error image is generated that shows where the failing pixels
389 * are.
390 *
391 * \note Currently supports only UNORM_INT8 formats
392 * \param log Test log for results
393 * \param imageSetName Name for image set when logging results
394 * \param imageSetDesc Description for image set
395 * \param reference Reference image
396 * \param result Result image
397 * \param threshold Error metric threshold (good values are 0.02-0.05)
398 * \param logMode Logging mode
399 * \return true if comparison passes, false otherwise
400 *//*--------------------------------------------------------------------*/
fuzzyCompare(TestLog & log,const char * imageSetName,const char * imageSetDesc,const Surface & reference,const Surface & result,float threshold,CompareLogMode logMode)401 bool fuzzyCompare(TestLog &log, const char *imageSetName, const char *imageSetDesc, const Surface &reference,
402 const Surface &result, float threshold, CompareLogMode logMode)
403 {
404 return fuzzyCompare(log, imageSetName, imageSetDesc, reference.getAccess(), result.getAccess(), threshold, logMode);
405 }
406
computeSquaredDiffSum(const ConstPixelBufferAccess & ref,const ConstPixelBufferAccess & cmp,const PixelBufferAccess & diffMask,int diffFactor)407 static int64_t computeSquaredDiffSum(const ConstPixelBufferAccess &ref, const ConstPixelBufferAccess &cmp,
408 const PixelBufferAccess &diffMask, int diffFactor)
409 {
410 TCU_CHECK_INTERNAL(ref.getFormat().type == TextureFormat::UNORM_INT8 &&
411 cmp.getFormat().type == TextureFormat::UNORM_INT8);
412 DE_ASSERT(ref.getWidth() == cmp.getWidth() && ref.getWidth() == diffMask.getWidth());
413 DE_ASSERT(ref.getHeight() == cmp.getHeight() && ref.getHeight() == diffMask.getHeight());
414
415 int64_t diffSum = 0;
416
417 for (int y = 0; y < cmp.getHeight(); y++)
418 {
419 for (int x = 0; x < cmp.getWidth(); x++)
420 {
421 IVec4 a = ref.getPixelInt(x, y);
422 IVec4 b = cmp.getPixelInt(x, y);
423 IVec4 diff = abs(a - b);
424 int sum = diff.x() + diff.y() + diff.z() + diff.w();
425 int sqSum = diff.x() * diff.x() + diff.y() * diff.y() + diff.z() * diff.z() + diff.w() * diff.w();
426
427 diffMask.setPixel(
428 tcu::RGBA(deClamp32(sum * diffFactor, 0, 255), deClamp32(255 - sum * diffFactor, 0, 255), 0, 255)
429 .toVec(),
430 x, y);
431
432 diffSum += (int64_t)sqSum;
433 }
434 }
435
436 return diffSum;
437 }
438
439 /*--------------------------------------------------------------------*//*!
440 * \brief Per-pixel difference accuracy metric
441 *
442 * Computes accuracy metric using per-pixel differences between reference
443 * and result images.
444 *
445 * \note Supports only integer- and fixed-point formats
446 * \param log Test log for results
447 * \param imageSetName Name for image set when logging results
448 * \param imageSetDesc Description for image set
449 * \param reference Reference image
450 * \param result Result image
451 * \param bestScoreDiff Scaling factor
452 * \param worstScoreDiff Scaling factor
453 * \param logMode Logging mode
454 * \return true if comparison passes, false otherwise
455 *//*--------------------------------------------------------------------*/
measurePixelDiffAccuracy(TestLog & log,const char * imageSetName,const char * imageSetDesc,const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,int bestScoreDiff,int worstScoreDiff,CompareLogMode logMode)456 int measurePixelDiffAccuracy(TestLog &log, const char *imageSetName, const char *imageSetDesc,
457 const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
458 int bestScoreDiff, int worstScoreDiff, CompareLogMode logMode)
459 {
460 TextureLevel diffMask(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), reference.getWidth(),
461 reference.getHeight());
462 int diffFactor = 8;
463 int64_t squaredSum = computeSquaredDiffSum(reference, result, diffMask.getAccess(), diffFactor);
464 float sum = deFloatSqrt((float)squaredSum);
465 int score = deClamp32(
466 deFloorFloatToInt32(
467 100.0f - (de::max(sum - (float)bestScoreDiff, 0.0f) / (float)(worstScoreDiff - bestScoreDiff)) * 100.0f),
468 0, 100);
469 const int failThreshold = 10;
470 Vec4 pixelBias(0.0f, 0.0f, 0.0f, 0.0f);
471 Vec4 pixelScale(1.0f, 1.0f, 1.0f, 1.0f);
472
473 if (logMode == COMPARE_LOG_EVERYTHING || score <= failThreshold)
474 {
475 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8) &&
476 reference.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
477 computeScaleAndBias(reference, result, pixelScale, pixelBias);
478
479 log << TestLog::ImageSet(imageSetName, imageSetDesc)
480 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
481 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
482 << TestLog::Image("DiffMask", "Difference", diffMask) << TestLog::EndImageSet;
483 }
484 else if (logMode == COMPARE_LOG_RESULT)
485 {
486 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
487 computePixelScaleBias(result, pixelScale, pixelBias);
488
489 log << TestLog::ImageSet(imageSetName, imageSetDesc)
490 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) << TestLog::EndImageSet;
491 }
492
493 if (logMode != COMPARE_LOG_ON_ERROR || score <= failThreshold)
494 log << TestLog::Integer("DiffSum", "Squared difference sum", "", QP_KEY_TAG_NONE, squaredSum)
495 << TestLog::Integer("Score", "Score", "", QP_KEY_TAG_QUALITY, score);
496
497 return score;
498 }
499
500 /*--------------------------------------------------------------------*//*!
501 * \brief Per-pixel difference accuracy metric
502 *
503 * Computes accuracy metric using per-pixel differences between reference
504 * and result images.
505 *
506 * \note Supports only integer- and fixed-point formats
507 * \param log Test log for results
508 * \param imageSetName Name for image set when logging results
509 * \param imageSetDesc Description for image set
510 * \param reference Reference image
511 * \param result Result image
512 * \param bestScoreDiff Scaling factor
513 * \param worstScoreDiff Scaling factor
514 * \param logMode Logging mode
515 * \return true if comparison passes, false otherwise
516 *//*--------------------------------------------------------------------*/
measurePixelDiffAccuracy(TestLog & log,const char * imageSetName,const char * imageSetDesc,const Surface & reference,const Surface & result,int bestScoreDiff,int worstScoreDiff,CompareLogMode logMode)517 int measurePixelDiffAccuracy(TestLog &log, const char *imageSetName, const char *imageSetDesc, const Surface &reference,
518 const Surface &result, int bestScoreDiff, int worstScoreDiff, CompareLogMode logMode)
519 {
520 return measurePixelDiffAccuracy(log, imageSetName, imageSetDesc, reference.getAccess(), result.getAccess(),
521 bestScoreDiff, worstScoreDiff, logMode);
522 }
523
524 /*--------------------------------------------------------------------*//*!
525 * Returns the index of float in a float space without denormals
526 * so that:
527 * 1) f(0.0) = 0
528 * 2) f(-0.0) = 0
529 * 3) f(b) = f(a) + 1 <==> b = nextAfter(a)
530 *
531 * See computeFloatFlushRelaxedULPDiff for details
532 *//*--------------------------------------------------------------------*/
getPositionOfIEEEFloatWithoutDenormals(float x)533 static int32_t getPositionOfIEEEFloatWithoutDenormals(float x)
534 {
535 DE_ASSERT(!deIsNaN(x)); // not sane
536
537 if (x == 0.0f)
538 return 0;
539 else if (x < 0.0f)
540 return -getPositionOfIEEEFloatWithoutDenormals(-x);
541 else
542 {
543 DE_ASSERT(x > 0.0f);
544
545 const tcu::Float32 f(x);
546
547 if (f.isDenorm())
548 {
549 // Denorms are flushed to zero
550 return 0;
551 }
552 else
553 {
554 // sign is 0, and it's a normal number. Natural position is its bit
555 // pattern but since we've collapsed the denorms, we must remove
556 // the gap here too to keep the float enumeration continuous.
557 //
558 // Denormals occupy one exponent pattern. Removing one from
559 // exponent should to the trick. Add one since the removed range
560 // contained one representable value, 0.
561 return (int32_t)(f.bits() - (1u << 23u) + 1u);
562 }
563 }
564 }
565
computeFloatFlushRelaxedULPDiff(float a,float b)566 static uint32_t computeFloatFlushRelaxedULPDiff(float a, float b)
567 {
568 if (deIsNaN(a) && deIsNaN(b))
569 return 0;
570 else if (deIsNaN(a) || deIsNaN(b))
571 {
572 return 0xFFFFFFFFu;
573 }
574 else
575 {
576 // Using the "definition 5" in Muller, Jean-Michel. "On the definition of ulp (x)" (2005)
577 // assuming a floating point space is IEEE single precision floating point space without
578 // denormals (and signed zeros).
579 const int32_t aIndex = getPositionOfIEEEFloatWithoutDenormals(a);
580 const int32_t bIndex = getPositionOfIEEEFloatWithoutDenormals(b);
581 return (uint32_t)de::abs(aIndex - bIndex);
582 }
583 }
584
computeFlushRelaxedULPDiff(const tcu::Vec4 & a,const tcu::Vec4 & b)585 static tcu::UVec4 computeFlushRelaxedULPDiff(const tcu::Vec4 &a, const tcu::Vec4 &b)
586 {
587 return tcu::UVec4(computeFloatFlushRelaxedULPDiff(a.x(), b.x()), computeFloatFlushRelaxedULPDiff(a.y(), b.y()),
588 computeFloatFlushRelaxedULPDiff(a.z(), b.z()), computeFloatFlushRelaxedULPDiff(a.w(), b.w()));
589 }
590
591 /*--------------------------------------------------------------------*//*!
592 * \brief Per-pixel threshold-based comparison
593 *
594 * This compare computes per-pixel differences between result and reference
595 * image. Comparison fails if any pixels exceed the given threshold value.
596 *
597 * This comparison uses ULP (units in last place) metric for computing the
598 * difference between floating-point values and thus this function can
599 * be used only for comparing floating-point texture data. In ULP calculation
600 * the denormal numbers are allowed to be flushed to zero.
601 *
602 * On failure error image is generated that shows where the failing pixels
603 * are.
604 *
605 * \param log Test log for results
606 * \param imageSetName Name for image set when logging results
607 * \param imageSetDesc Description for image set
608 * \param reference Reference image
609 * \param result Result image
610 * \param threshold Maximum allowed difference
611 * \param logMode Logging mode
612 * \return true if comparison passes, false otherwise
613 *//*--------------------------------------------------------------------*/
floatUlpThresholdCompare(TestLog & log,const char * imageSetName,const char * imageSetDesc,const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const UVec4 & threshold,CompareLogMode logMode)614 bool floatUlpThresholdCompare(TestLog &log, const char *imageSetName, const char *imageSetDesc,
615 const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
616 const UVec4 &threshold, CompareLogMode logMode)
617 {
618 int width = reference.getWidth();
619 int height = reference.getHeight();
620 int depth = reference.getDepth();
621 TextureLevel errorMaskStorage(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth);
622 PixelBufferAccess errorMask = errorMaskStorage.getAccess();
623 UVec4 maxDiff(0, 0, 0, 0);
624 Vec4 pixelBias(0.0f, 0.0f, 0.0f, 0.0f);
625 Vec4 pixelScale(1.0f, 1.0f, 1.0f, 1.0f);
626
627 TCU_CHECK(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth);
628
629 for (int z = 0; z < depth; z++)
630 {
631 for (int y = 0; y < height; y++)
632 {
633 for (int x = 0; x < width; x++)
634 {
635 const Vec4 refPix = reference.getPixel(x, y, z);
636 const Vec4 cmpPix = result.getPixel(x, y, z);
637 const UVec4 diff = computeFlushRelaxedULPDiff(refPix, cmpPix);
638 const bool isOk = boolAll(lessThanEqual(diff, threshold));
639
640 maxDiff = max(maxDiff, diff);
641
642 errorMask.setPixel(isOk ? Vec4(0.0f, 1.0f, 0.0f, 1.0f) : Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y, z);
643 }
644 }
645 }
646
647 bool compareOk = boolAll(lessThanEqual(maxDiff, threshold));
648
649 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING)
650 {
651 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images.
652 if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
653 tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
654 {
655 computeScaleAndBias(reference, result, pixelScale, pixelBias);
656 log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale
657 << " + " << pixelBias << TestLog::EndMessage;
658 }
659
660 if (!compareOk)
661 log << TestLog::Message << "Image comparison failed: max difference = " << maxDiff
662 << ", threshold = " << threshold << TestLog::EndMessage;
663
664 log << TestLog::ImageSet(imageSetName, imageSetDesc)
665 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
666 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
667 << TestLog::Image("ErrorMask", "Error mask", errorMask) << TestLog::EndImageSet;
668 }
669 else if (logMode == COMPARE_LOG_RESULT)
670 {
671 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
672 computePixelScaleBias(result, pixelScale, pixelBias);
673
674 log << TestLog::ImageSet(imageSetName, imageSetDesc)
675 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) << TestLog::EndImageSet;
676 }
677
678 return compareOk;
679 }
680
681 /*--------------------------------------------------------------------*//*!
682 * \brief Per-pixel threshold-based comparison
683 *
684 * This compare computes per-pixel differences between result and reference
685 * image. Comparison fails if any pixels exceed the given threshold value.
686 *
687 * This comparison can be used for floating-point and fixed-point formats.
688 * Difference is computed in floating-point space.
689 *
690 * On failure an error image is generated that shows where the failing
691 * pixels are.
692 *
693 * \param log Test log for results
694 * \param imageSetName Name for image set when logging results
695 * \param imageSetDesc Description for image set
696 * \param reference Reference image
697 * \param result Result image
698 * \param threshold Maximum allowed difference
699 * \param logMode Logging mode
700 * \return true if comparison passes, false otherwise
701 *//*--------------------------------------------------------------------*/
floatThresholdCompare(TestLog & log,const char * imageSetName,const char * imageSetDesc,const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const Vec4 & threshold,CompareLogMode logMode)702 bool floatThresholdCompare(TestLog &log, const char *imageSetName, const char *imageSetDesc,
703 const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
704 const Vec4 &threshold, CompareLogMode logMode)
705 {
706 int width = reference.getWidth();
707 int height = reference.getHeight();
708 int depth = reference.getDepth();
709 TextureLevel errorMaskStorage(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth);
710 PixelBufferAccess errorMask = errorMaskStorage.getAccess();
711 Vec4 maxDiff(0.0f, 0.0f, 0.0f, 0.0f);
712 Vec4 pixelBias(0.0f, 0.0f, 0.0f, 0.0f);
713 Vec4 pixelScale(1.0f, 1.0f, 1.0f, 1.0f);
714
715 TCU_CHECK_INTERNAL(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth);
716
717 for (int z = 0; z < depth; z++)
718 {
719 for (int y = 0; y < height; y++)
720 {
721 for (int x = 0; x < width; x++)
722 {
723 Vec4 refPix = reference.getPixel(x, y, z);
724 Vec4 cmpPix = result.getPixel(x, y, z);
725
726 Vec4 diff = abs(refPix - cmpPix);
727 bool isOk = boolAll(lessThanEqual(diff, threshold));
728
729 maxDiff = max(maxDiff, diff);
730
731 errorMask.setPixel(isOk ? Vec4(0.0f, 1.0f, 0.0f, 1.0f) : Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y, z);
732 }
733 }
734 }
735
736 bool compareOk = boolAll(lessThanEqual(maxDiff, threshold));
737
738 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING)
739 {
740 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images.
741 if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
742 tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
743 {
744 computeScaleAndBias(reference, result, pixelScale, pixelBias);
745 log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale
746 << " + " << pixelBias << TestLog::EndMessage;
747 }
748
749 if (!compareOk)
750 log << TestLog::Message << "Image comparison failed: max difference = " << maxDiff
751 << ", threshold = " << threshold << TestLog::EndMessage;
752
753 log << TestLog::ImageSet(imageSetName, imageSetDesc)
754 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
755 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
756 << TestLog::Image("ErrorMask", "Error mask", errorMask) << TestLog::EndImageSet;
757 }
758 else if (logMode == COMPARE_LOG_RESULT)
759 {
760 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
761 computePixelScaleBias(result, pixelScale, pixelBias);
762
763 log << TestLog::ImageSet(imageSetName, imageSetDesc)
764 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) << TestLog::EndImageSet;
765 }
766
767 return compareOk;
768 }
769
770 /*--------------------------------------------------------------------*//*!
771 * \brief Per-pixel threshold-based comparison with ignore key
772 *
773 * This compare computes per-pixel differences between result and reference
774 * image. Comparison fails if any pixels exceed the given threshold value.
775 *
776 * Any pixels in reference that match the ignore key are ignored.
777 *
778 * This comparison can be used for floating-point and fixed-point formats.
779 * Difference is computed in floating-point space.
780 *
781 * On failure an error image is generated that shows where the failing
782 * pixels are.
783 *
784 * \param log Test log for results
785 * \param imageSetName Name for image set when logging results
786 * \param imageSetDesc Description for image set
787 * \param reference Reference image
788 * \param result Result image
789 * \param ignorekey Ignore key
790 * \param threshold Maximum allowed difference
791 * \param logMode Logging mode
792 * \return true if comparison passes, false otherwise
793 *//*--------------------------------------------------------------------*/
floatThresholdCompare(TestLog & log,const char * imageSetName,const char * imageSetDesc,const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const Vec4 & ignorekey,const Vec4 & threshold,CompareLogMode logMode)794 bool floatThresholdCompare(TestLog &log, const char *imageSetName, const char *imageSetDesc,
795 const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
796 const Vec4 &ignorekey, const Vec4 &threshold, CompareLogMode logMode)
797 {
798 int width = reference.getWidth();
799 int height = reference.getHeight();
800 int depth = reference.getDepth();
801 TextureLevel errorMaskStorage(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth);
802 PixelBufferAccess errorMask = errorMaskStorage.getAccess();
803 Vec4 maxDiff(0.0f, 0.0f, 0.0f, 0.0f);
804 Vec4 pixelBias(0.0f, 0.0f, 0.0f, 0.0f);
805 Vec4 pixelScale(1.0f, 1.0f, 1.0f, 1.0f);
806
807 TCU_CHECK_INTERNAL(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth);
808
809 for (int z = 0; z < depth; z++)
810 {
811 for (int y = 0; y < height; y++)
812 {
813 for (int x = 0; x < width; x++)
814 {
815 Vec4 refPix = reference.getPixel(x, y, z);
816 Vec4 cmpPix = result.getPixel(x, y, z);
817
818 if (refPix != ignorekey)
819 {
820
821 Vec4 diff = abs(refPix - cmpPix);
822 bool isOk = boolAll(lessThanEqual(diff, threshold));
823
824 maxDiff = max(maxDiff, diff);
825
826 errorMask.setPixel(isOk ? Vec4(0.0f, 1.0f, 0.0f, 1.0f) : Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y, z);
827 }
828 }
829 }
830 }
831
832 bool compareOk = boolAll(lessThanEqual(maxDiff, threshold));
833
834 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING)
835 {
836 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images.
837 if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
838 tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
839 {
840 computeScaleAndBias(reference, result, pixelScale, pixelBias);
841 log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale
842 << " + " << pixelBias << TestLog::EndMessage;
843 }
844
845 if (!compareOk)
846 log << TestLog::Message << "Image comparison failed: max difference = " << maxDiff
847 << ", threshold = " << threshold << TestLog::EndMessage;
848
849 log << TestLog::ImageSet(imageSetName, imageSetDesc)
850 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
851 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
852 << TestLog::Image("ErrorMask", "Error mask", errorMask) << TestLog::EndImageSet;
853 }
854 else if (logMode == COMPARE_LOG_RESULT)
855 {
856 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
857 computePixelScaleBias(result, pixelScale, pixelBias);
858
859 log << TestLog::ImageSet(imageSetName, imageSetDesc)
860 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) << TestLog::EndImageSet;
861 }
862
863 return compareOk;
864 }
865
866 /*--------------------------------------------------------------------*//*!
867 * \brief Per-pixel threshold-based comparison
868 *
869 * This compare computes per-pixel differences between result and reference
870 * color. Comparison fails if any pixels exceed the given threshold value.
871 *
872 * This comparison can be used for floating-point and fixed-point formats.
873 * Difference is computed in floating-point space.
874 *
875 * On failure an error image is generated that shows where the failing
876 * pixels are.
877 *
878 * \param log Test log for results
879 * \param imageSetName Name for image set when logging results
880 * \param imageSetDesc Description for image set
881 * \param reference Reference color
882 * \param result Result image
883 * \param threshold Maximum allowed difference
884 * \param logMode Logging mode
885 * \return true if comparison passes, false otherwise
886 *//*--------------------------------------------------------------------*/
floatThresholdCompare(TestLog & log,const char * imageSetName,const char * imageSetDesc,const Vec4 & reference,const ConstPixelBufferAccess & result,const Vec4 & threshold,CompareLogMode logMode)887 bool floatThresholdCompare(TestLog &log, const char *imageSetName, const char *imageSetDesc, const Vec4 &reference,
888 const ConstPixelBufferAccess &result, const Vec4 &threshold, CompareLogMode logMode)
889 {
890 const int width = result.getWidth();
891 const int height = result.getHeight();
892 const int depth = result.getDepth();
893
894 TextureLevel errorMaskStorage(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth);
895 PixelBufferAccess errorMask = errorMaskStorage.getAccess();
896 Vec4 maxDiff(0.0f, 0.0f, 0.0f, 0.0f);
897 Vec4 pixelBias(0.0f, 0.0f, 0.0f, 0.0f);
898 Vec4 pixelScale(1.0f, 1.0f, 1.0f, 1.0f);
899
900 for (int z = 0; z < depth; z++)
901 {
902 for (int y = 0; y < height; y++)
903 {
904 for (int x = 0; x < width; x++)
905 {
906 const Vec4 cmpPix = result.getPixel(x, y, z);
907 const Vec4 diff = abs(reference - cmpPix);
908 const bool isOk = boolAll(lessThanEqual(diff, threshold));
909
910 maxDiff = max(maxDiff, diff);
911
912 if (isOk)
913 errorMask.setPixel(Vec4(0.0f, 1.0f, 0.0f, 1.0f), x, y, z);
914 else
915 errorMask.setPixel(Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y, z);
916 }
917 }
918 }
919
920 bool compareOk = boolAll(lessThanEqual(maxDiff, threshold));
921
922 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING)
923 {
924 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images.
925 if (tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
926 {
927 computeScaleAndBias(result, result, pixelScale, pixelBias);
928 log << TestLog::Message << "Result image is normalized with formula p * " << pixelScale << " + "
929 << pixelBias << TestLog::EndMessage;
930 }
931
932 if (!compareOk)
933 log << TestLog::Message << "Image comparison failed: max difference = " << maxDiff
934 << ", threshold = " << threshold << ", reference = " << reference << TestLog::EndMessage;
935
936 log << TestLog::ImageSet(imageSetName, imageSetDesc)
937 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
938 << TestLog::Image("ErrorMask", "Error mask", errorMask) << TestLog::EndImageSet;
939 }
940 else if (logMode == COMPARE_LOG_RESULT)
941 {
942 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
943 computePixelScaleBias(result, pixelScale, pixelBias);
944
945 log << TestLog::ImageSet(imageSetName, imageSetDesc)
946 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) << TestLog::EndImageSet;
947 }
948
949 return compareOk;
950 }
951
952 /*--------------------------------------------------------------------*//*!
953 * \brief Per-pixel threshold-based comparison
954 *
955 * This compare computes per-pixel differences between result and reference
956 * image. Comparison fails if any pixels exceed the given threshold value.
957 *
958 * This comparison can be used for integer- and fixed-point texture formats.
959 * Difference is computed in integer space.
960 *
961 * On failure error image is generated that shows where the failing pixels
962 * are.
963 *
964 * \param log Test log for results
965 * \param imageSetName Name for image set when logging results
966 * \param imageSetDesc Description for image set
967 * \param reference Reference image
968 * \param result Result image
969 * \param threshold Maximum allowed difference
970 * \param logMode Logging mode
971 * \param use64Bits Use 64-bit components when reading image data.
972 * \return true if comparison passes, false otherwise
973 *//*--------------------------------------------------------------------*/
intThresholdCompare(TestLog & log,const char * imageSetName,const char * imageSetDesc,const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const UVec4 & threshold,CompareLogMode logMode,bool use64Bits)974 bool intThresholdCompare(TestLog &log, const char *imageSetName, const char *imageSetDesc,
975 const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
976 const UVec4 &threshold, CompareLogMode logMode, bool use64Bits)
977 {
978 int width = reference.getWidth();
979 int height = reference.getHeight();
980 int depth = reference.getDepth();
981 TextureLevel errorMaskStorage(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth);
982 PixelBufferAccess errorMask = errorMaskStorage.getAccess();
983 U64Vec4 maxDiff(0u, 0u, 0u, 0u);
984 U64Vec4 diff(0u, 0u, 0u, 0u);
985 const U64Vec4 threshold64 = threshold.cast<uint64_t>();
986 Vec4 pixelBias(0.0f, 0.0f, 0.0f, 0.0f);
987 Vec4 pixelScale(1.0f, 1.0f, 1.0f, 1.0f);
988
989 I64Vec4 refPix64;
990 I64Vec4 cmpPix64;
991 IVec4 refPix;
992 IVec4 cmpPix;
993
994 TCU_CHECK_INTERNAL(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth);
995
996 for (int z = 0; z < depth; z++)
997 {
998 for (int y = 0; y < height; y++)
999 {
1000 for (int x = 0; x < width; x++)
1001 {
1002 if (use64Bits)
1003 {
1004 refPix64 = reference.getPixelInt64(x, y, z);
1005 cmpPix64 = result.getPixelInt64(x, y, z);
1006 diff = abs(refPix64 - cmpPix64).cast<uint64_t>();
1007 }
1008 else
1009 {
1010 refPix = reference.getPixelInt(x, y, z);
1011 cmpPix = result.getPixelInt(x, y, z);
1012 diff = abs(refPix - cmpPix).cast<uint64_t>();
1013 }
1014
1015 maxDiff = max(maxDiff, diff);
1016
1017 const bool isOk = boolAll(lessThanEqual(diff, threshold64));
1018 if (isOk)
1019 errorMask.setPixel(IVec4(0, 0xff, 0, 0xff), x, y, z);
1020 else
1021 errorMask.setPixel(IVec4(0xff, 0, 0, 0xff), x, y, z);
1022 }
1023 }
1024 }
1025
1026 bool compareOk = boolAll(lessThanEqual(maxDiff, threshold64));
1027
1028 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING)
1029 {
1030 {
1031 const auto refChannelClass = tcu::getTextureChannelClass(reference.getFormat().type);
1032 const auto resChannelClass = tcu::getTextureChannelClass(result.getFormat().type);
1033
1034 const bool refIsUint8 = (reference.getFormat().type == TextureFormat::UNSIGNED_INT8);
1035 const bool resIsUint8 = (result.getFormat().type == TextureFormat::UNSIGNED_INT8);
1036
1037 const bool calcScaleBias =
1038 ((refChannelClass != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT && !refIsUint8) ||
1039 (resChannelClass != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT && !resIsUint8));
1040
1041 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images.
1042 if (calcScaleBias)
1043 {
1044 computeScaleAndBias(reference, result, pixelScale, pixelBias);
1045 log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale
1046 << " + " << pixelBias << TestLog::EndMessage;
1047 }
1048 }
1049
1050 if (!compareOk)
1051 log << TestLog::Message << "Image comparison failed: max difference = " << maxDiff
1052 << ", threshold = " << threshold << TestLog::EndMessage;
1053
1054 log << TestLog::ImageSet(imageSetName, imageSetDesc)
1055 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
1056 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
1057 << TestLog::Image("ErrorMask", "Error mask", errorMask) << TestLog::EndImageSet;
1058 }
1059 else if (logMode == COMPARE_LOG_RESULT)
1060 {
1061 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
1062 computePixelScaleBias(result, pixelScale, pixelBias);
1063
1064 log << TestLog::ImageSet(imageSetName, imageSetDesc)
1065 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) << TestLog::EndImageSet;
1066 }
1067
1068 return compareOk;
1069 }
1070
1071 /*--------------------------------------------------------------------*//*!
1072 * \brief Per-pixel depth/stencil threshold-based comparison
1073 *
1074 * This compare computes per-pixel differences between result and reference
1075 * image. Comparison fails if any pixels exceed the given threshold value.
1076 *
1077 * This comparison can be used for depth and depth/stencil images.
1078 * Difference is computed in integer space.
1079 *
1080 * On failure error image is generated that shows where the failing pixels
1081 * are.
1082 *
1083 * \param log Test log for results
1084 * \param imageSetName Name for image set when logging results
1085 * \param imageSetDesc Description for image set
1086 * \param reference Reference image
1087 * \param result Result image
1088 * \param threshold Maximum allowed depth difference (stencil must be exact)
1089 * \param logMode Logging mode
1090 * \return true if comparison passes, false otherwise
1091 *//*--------------------------------------------------------------------*/
dsThresholdCompare(TestLog & log,const char * imageSetName,const char * imageSetDesc,const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const float threshold,CompareLogMode logMode)1092 bool dsThresholdCompare(TestLog &log, const char *imageSetName, const char *imageSetDesc,
1093 const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
1094 const float threshold, CompareLogMode logMode)
1095 {
1096 int width = reference.getWidth();
1097 int height = reference.getHeight();
1098 int depth = reference.getDepth();
1099 TextureLevel errorLevelDepth(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth);
1100 TextureLevel errorLevelStencil(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth);
1101 PixelBufferAccess errorMaskDepth = errorLevelDepth.getAccess();
1102 PixelBufferAccess errorMaskStencil = errorLevelStencil.getAccess();
1103 float maxDiff = 0.0;
1104 bool allStencilOk = true;
1105 bool hasDepth = tcu::hasDepthComponent(result.getFormat().order);
1106 bool hasStencil = tcu::hasStencilComponent(result.getFormat().order);
1107
1108 TCU_CHECK_INTERNAL(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth);
1109
1110 for (int z = 0; z < depth; z++)
1111 {
1112 for (int y = 0; y < height; y++)
1113 {
1114 for (int x = 0; x < width; x++)
1115 {
1116 if (hasDepth)
1117 {
1118 float refDepth = reference.getPixDepth(x, y, z);
1119 float cmpDepth = result.getPixDepth(x, y, z);
1120 float diff = de::abs(refDepth - cmpDepth);
1121
1122 const bool depthOk = (diff <= threshold);
1123 maxDiff = (float)deMax(maxDiff, diff);
1124
1125 if (depthOk)
1126 errorMaskDepth.setPixel(Vec4(0.0f, 1.0f, 0.0f, 1.0f), x, y, z);
1127 else
1128 errorMaskDepth.setPixel(Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y, z);
1129 }
1130
1131 if (hasStencil)
1132 {
1133 uint8_t refStencil = (uint8_t)reference.getPixStencil(x, y, z);
1134 uint8_t cmpStencil = (uint8_t)result.getPixStencil(x, y, z);
1135
1136 const bool isStencilOk = (refStencil == cmpStencil);
1137
1138 if (isStencilOk)
1139 errorMaskStencil.setPixel(Vec4(0.0f, 1.0f, 0.0f, 1.0f), x, y, z);
1140 else
1141 errorMaskStencil.setPixel(Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y, z);
1142
1143 allStencilOk = allStencilOk && isStencilOk;
1144 }
1145 }
1146 }
1147 }
1148
1149 const bool allDepthOk = (maxDiff <= threshold);
1150 bool compareOk = allDepthOk && allStencilOk;
1151
1152 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING)
1153 {
1154 if (!compareOk)
1155 {
1156 if (maxDiff > threshold)
1157 log << TestLog::Message << "Depth comparison failed: max difference = " << maxDiff
1158 << ", threshold = " << threshold << TestLog::EndMessage;
1159 if (!allStencilOk)
1160 log << TestLog::Message << "Stencil comparison failed" << TestLog::EndMessage;
1161 }
1162
1163 log << TestLog::ImageSet(imageSetName, imageSetDesc);
1164
1165 if (!allDepthOk)
1166 {
1167 TextureLevel refDepthLevel(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height,
1168 depth);
1169 TextureLevel resDepthLevel(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height,
1170 depth);
1171 tcu::PixelBufferAccess refDepthAccess = refDepthLevel.getAccess();
1172 tcu::PixelBufferAccess resDepthAccess = resDepthLevel.getAccess();
1173
1174 for (int z = 0; z < depth; z++)
1175 for (int y = 0; y < height; y++)
1176 for (int x = 0; x < width; x++)
1177 {
1178 const float refDepth = reference.getPixDepth(x, y, z);
1179 const float resDepth = result.getPixDepth(x, y, z);
1180 refDepthAccess.setPixel(Vec4(refDepth, refDepth, refDepth, 1.0f), x, y, z);
1181 resDepthAccess.setPixel(Vec4(resDepth, resDepth, resDepth, 1.0f), x, y, z);
1182 }
1183
1184 log << TestLog::Image("ResultDepth", "", resDepthAccess)
1185 << TestLog::Image("ReferenceDepth", "", refDepthAccess)
1186 << TestLog::Image("ErrorMaskDepth", "", errorMaskDepth);
1187 }
1188
1189 if (!allStencilOk)
1190 {
1191 TextureLevel refStencilLevel(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height,
1192 depth);
1193 TextureLevel resStencilLevel(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height,
1194 depth);
1195 tcu::PixelBufferAccess refStencilAccess = refStencilLevel.getAccess();
1196 tcu::PixelBufferAccess resStencilAccess = resStencilLevel.getAccess();
1197
1198 for (int z = 0; z < depth; z++)
1199 for (int y = 0; y < height; y++)
1200 for (int x = 0; x < width; x++)
1201 {
1202 const float refStencil = static_cast<float>(reference.getPixStencil(x, y, z)) / 255.0f;
1203 const float resStencil = static_cast<float>(result.getPixStencil(x, y, z)) / 255.0f;
1204 refStencilAccess.setPixel(Vec4(refStencil, refStencil, refStencil, 1.0f), x, y, z);
1205 resStencilAccess.setPixel(Vec4(resStencil, resStencil, resStencil, 1.0f), x, y, z);
1206 }
1207
1208 log << TestLog::Image("ResultStencil", "", resStencilAccess)
1209 << TestLog::Image("ReferenceStencil", "", refStencilAccess)
1210 << TestLog::Image("ErrorMaskStencil", "", errorMaskDepth);
1211 }
1212
1213 log << TestLog::EndImageSet;
1214 }
1215 else if (logMode == COMPARE_LOG_RESULT)
1216 {
1217 #if 0
1218 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
1219 computePixelScaleBias(result, pixelScale, pixelBias);
1220
1221 log << TestLog::ImageSet(imageSetName, imageSetDesc)
1222 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
1223 << TestLog::EndImageSet;
1224 #endif
1225 }
1226
1227 return compareOk;
1228 }
1229
1230 /*--------------------------------------------------------------------*//*!
1231 * \brief Per-pixel threshold-based deviation-ignoring comparison
1232 *
1233 * This compare computes per-pixel differences between result and reference
1234 * image. Comparison fails if there is no pixel matching the given threshold
1235 * value in the search volume.
1236 *
1237 * If the search volume contains out-of-bounds pixels, comparison can be set
1238 * to either ignore these pixels in search or to accept any pixel that has
1239 * out-of-bounds pixels in its search volume.
1240 *
1241 * This comparison can be used for integer- and fixed-point texture formats.
1242 * Difference is computed in integer space.
1243 *
1244 * On failure error image is generated that shows where the failing pixels
1245 * are.
1246 *
1247 * \param log Test log for results
1248 * \param imageSetName Name for image set when logging results
1249 * \param imageSetDesc Description for image set
1250 * \param reference Reference image
1251 * \param result Result image
1252 * \param threshold Maximum allowed difference
1253 * \param maxPositionDeviation Maximum allowed distance in the search
1254 * volume.
1255 * \param acceptOutOfBoundsAsAnyValue Accept any pixel in the boundary region
1256 * \param logMode Logging mode
1257 * \return true if comparison passes, false otherwise
1258 *//*--------------------------------------------------------------------*/
intThresholdPositionDeviationCompare(TestLog & log,const char * imageSetName,const char * imageSetDesc,const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const UVec4 & threshold,const tcu::IVec3 & maxPositionDeviation,bool acceptOutOfBoundsAsAnyValue,CompareLogMode logMode)1259 bool intThresholdPositionDeviationCompare(TestLog &log, const char *imageSetName, const char *imageSetDesc,
1260 const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
1261 const UVec4 &threshold, const tcu::IVec3 &maxPositionDeviation,
1262 bool acceptOutOfBoundsAsAnyValue, CompareLogMode logMode)
1263 {
1264 const int width = reference.getWidth();
1265 const int height = reference.getHeight();
1266 const int depth = reference.getDepth();
1267 TextureLevel errorMaskStorage(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth);
1268 PixelBufferAccess errorMask = errorMaskStorage.getAccess();
1269 const int numFailingPixels = findNumPositionDeviationFailingPixels(
1270 errorMask, reference, result, threshold, maxPositionDeviation, acceptOutOfBoundsAsAnyValue);
1271 const bool compareOk = numFailingPixels == 0;
1272 Vec4 pixelBias(0.0f, 0.0f, 0.0f, 0.0f);
1273 Vec4 pixelScale(1.0f, 1.0f, 1.0f, 1.0f);
1274
1275 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING)
1276 {
1277 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images.
1278 if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
1279 tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
1280 {
1281 computeScaleAndBias(reference, result, pixelScale, pixelBias);
1282 log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale
1283 << " + " << pixelBias << TestLog::EndMessage;
1284 }
1285
1286 if (!compareOk)
1287 log << TestLog::Message << "Image comparison failed:\n"
1288 << "\tallowed position deviation = " << maxPositionDeviation << "\n"
1289 << "\tcolor threshold = " << threshold << TestLog::EndMessage;
1290
1291 log << TestLog::ImageSet(imageSetName, imageSetDesc)
1292 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
1293 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
1294 << TestLog::Image("ErrorMask", "Error mask", errorMask) << TestLog::EndImageSet;
1295 }
1296 else if (logMode == COMPARE_LOG_RESULT)
1297 {
1298 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
1299 computePixelScaleBias(result, pixelScale, pixelBias);
1300
1301 log << TestLog::ImageSet(imageSetName, imageSetDesc)
1302 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) << TestLog::EndImageSet;
1303 }
1304
1305 return compareOk;
1306 }
1307
1308 /*--------------------------------------------------------------------*//*!
1309 * \brief Per-pixel threshold-based deviation-ignoring comparison
1310 *
1311 * This compare computes per-pixel differences between result and reference
1312 * image. Pixel fails the test if there is no pixel matching the given
1313 * threshold value in the search volume. Comparison fails if the number of
1314 * failing pixels exceeds the given limit.
1315 *
1316 * If the search volume contains out-of-bounds pixels, comparison can be set
1317 * to either ignore these pixels in search or to accept any pixel that has
1318 * out-of-bounds pixels in its search volume.
1319 *
1320 * This comparison can be used for integer- and fixed-point texture formats.
1321 * Difference is computed in integer space.
1322 *
1323 * On failure error image is generated that shows where the failing pixels
1324 * are.
1325 *
1326 * \param log Test log for results
1327 * \param imageSetName Name for image set when logging results
1328 * \param imageSetDesc Description for image set
1329 * \param reference Reference image
1330 * \param result Result image
1331 * \param threshold Maximum allowed difference
1332 * \param maxPositionDeviation Maximum allowed distance in the search
1333 * volume.
1334 * \param acceptOutOfBoundsAsAnyValue Accept any pixel in the boundary region
1335 * \param maxAllowedFailingPixels Maximum number of failing pixels
1336 * \param logMode Logging mode
1337 * \return true if comparison passes, false otherwise
1338 *//*--------------------------------------------------------------------*/
intThresholdPositionDeviationErrorThresholdCompare(TestLog & log,const char * imageSetName,const char * imageSetDesc,const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const UVec4 & threshold,const tcu::IVec3 & maxPositionDeviation,bool acceptOutOfBoundsAsAnyValue,int maxAllowedFailingPixels,CompareLogMode logMode)1339 bool intThresholdPositionDeviationErrorThresholdCompare(
1340 TestLog &log, const char *imageSetName, const char *imageSetDesc, const ConstPixelBufferAccess &reference,
1341 const ConstPixelBufferAccess &result, const UVec4 &threshold, const tcu::IVec3 &maxPositionDeviation,
1342 bool acceptOutOfBoundsAsAnyValue, int maxAllowedFailingPixels, CompareLogMode logMode)
1343 {
1344 const int width = reference.getWidth();
1345 const int height = reference.getHeight();
1346 const int depth = reference.getDepth();
1347 TextureLevel errorMaskStorage(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth);
1348 PixelBufferAccess errorMask = errorMaskStorage.getAccess();
1349 const int numFailingPixels = findNumPositionDeviationFailingPixels(
1350 errorMask, reference, result, threshold, maxPositionDeviation, acceptOutOfBoundsAsAnyValue);
1351 const bool compareOk = numFailingPixels <= maxAllowedFailingPixels;
1352 Vec4 pixelBias(0.0f, 0.0f, 0.0f, 0.0f);
1353 Vec4 pixelScale(1.0f, 1.0f, 1.0f, 1.0f);
1354
1355 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING)
1356 {
1357 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images.
1358 if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
1359 tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
1360 {
1361 computeScaleAndBias(reference, result, pixelScale, pixelBias);
1362 log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale
1363 << " + " << pixelBias << TestLog::EndMessage;
1364 }
1365
1366 if (!compareOk)
1367 log << TestLog::Message << "Image comparison failed:\n"
1368 << "\tallowed position deviation = " << maxPositionDeviation << "\n"
1369 << "\tcolor threshold = " << threshold << TestLog::EndMessage;
1370 log << TestLog::Message << "Number of failing pixels = " << numFailingPixels
1371 << ", max allowed = " << maxAllowedFailingPixels << TestLog::EndMessage;
1372
1373 log << TestLog::ImageSet(imageSetName, imageSetDesc)
1374 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
1375 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
1376 << TestLog::Image("ErrorMask", "Error mask", errorMask) << TestLog::EndImageSet;
1377 }
1378 else if (logMode == COMPARE_LOG_RESULT)
1379 {
1380 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
1381 computePixelScaleBias(result, pixelScale, pixelBias);
1382
1383 log << TestLog::ImageSet(imageSetName, imageSetDesc)
1384 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) << TestLog::EndImageSet;
1385 }
1386
1387 return compareOk;
1388 }
1389
1390 /*--------------------------------------------------------------------*//*!
1391 * \brief Per-pixel threshold-based comparison
1392 *
1393 * This compare computes per-pixel differences between result and reference
1394 * image. Comparison fails if any pixels exceed the given threshold value.
1395 *
1396 * On failure error image is generated that shows where the failing pixels
1397 * are.
1398 *
1399 * \param log Test log for results
1400 * \param imageSetName Name for image set when logging results
1401 * \param imageSetDesc Description for image set
1402 * \param reference Reference image
1403 * \param result Result image
1404 * \param threshold Maximum allowed difference
1405 * \param logMode Logging mode
1406 * \return true if comparison passes, false otherwise
1407 *//*--------------------------------------------------------------------*/
pixelThresholdCompare(TestLog & log,const char * imageSetName,const char * imageSetDesc,const Surface & reference,const Surface & result,const RGBA & threshold,CompareLogMode logMode)1408 bool pixelThresholdCompare(TestLog &log, const char *imageSetName, const char *imageSetDesc, const Surface &reference,
1409 const Surface &result, const RGBA &threshold, CompareLogMode logMode)
1410 {
1411 return intThresholdCompare(log, imageSetName, imageSetDesc, reference.getAccess(), result.getAccess(),
1412 threshold.toIVec().cast<uint32_t>(), logMode);
1413 }
1414
1415 /*--------------------------------------------------------------------*//*!
1416 * \brief Bilinear image comparison
1417 *
1418 * \todo [pyry] Describe
1419 *
1420 * On failure error image is generated that shows where the failing pixels
1421 * are.
1422 *
1423 * \note Currently supports only RGBA, UNORM_INT8 formats
1424 * \param log Test log for results
1425 * \param imageSetName Name for image set when logging results
1426 * \param imageSetDesc Description for image set
1427 * \param reference Reference image
1428 * \param result Result image
1429 * \param threshold Maximum local difference
1430 * \param logMode Logging mode
1431 * \return true if comparison passes, false otherwise
1432 *//*--------------------------------------------------------------------*/
bilinearCompare(TestLog & log,const char * imageSetName,const char * imageSetDesc,const ConstPixelBufferAccess & reference,const ConstPixelBufferAccess & result,const RGBA threshold,CompareLogMode logMode)1433 bool bilinearCompare(TestLog &log, const char *imageSetName, const char *imageSetDesc,
1434 const ConstPixelBufferAccess &reference, const ConstPixelBufferAccess &result,
1435 const RGBA threshold, CompareLogMode logMode)
1436 {
1437 TextureLevel errorMask(TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), reference.getWidth(),
1438 reference.getHeight());
1439 bool isOk = bilinearCompare(reference, result, errorMask, threshold);
1440 Vec4 pixelBias(0.0f, 0.0f, 0.0f, 0.0f);
1441 Vec4 pixelScale(1.0f, 1.0f, 1.0f, 1.0f);
1442
1443 if (!isOk || logMode == COMPARE_LOG_EVERYTHING)
1444 {
1445 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8) &&
1446 reference.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
1447 computeScaleAndBias(reference, result, pixelScale, pixelBias);
1448
1449 if (!isOk)
1450 log << TestLog::Message << "Image comparison failed, threshold = " << threshold << TestLog::EndMessage;
1451
1452 log << TestLog::ImageSet(imageSetName, imageSetDesc)
1453 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
1454 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
1455 << TestLog::Image("ErrorMask", "Error mask", errorMask) << TestLog::EndImageSet;
1456 }
1457 else if (logMode == COMPARE_LOG_RESULT)
1458 {
1459 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
1460 computePixelScaleBias(result, pixelScale, pixelBias);
1461
1462 log << TestLog::ImageSet(imageSetName, imageSetDesc)
1463 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) << TestLog::EndImageSet;
1464 }
1465
1466 return isOk;
1467 }
1468
1469 } // namespace tcu
1470