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