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 35 #include <string.h> 36 37 namespace tcu 38 { 39 40 namespace 41 { 42 43 void computeScaleAndBias (const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, tcu::Vec4& scale, tcu::Vec4& bias) 44 { 45 Vec4 minVal; 46 Vec4 maxVal; 47 const float eps = 0.0001f; 48 49 { 50 Vec4 refMin; 51 Vec4 refMax; 52 estimatePixelValueRange(reference, refMin, refMax); 53 54 minVal = refMin; 55 maxVal = refMax; 56 } 57 58 { 59 Vec4 resMin; 60 Vec4 resMax; 61 62 estimatePixelValueRange(result, resMin, resMax); 63 64 minVal[0] = de::min(minVal[0], resMin[0]); 65 minVal[1] = de::min(minVal[1], resMin[1]); 66 minVal[2] = de::min(minVal[2], resMin[2]); 67 minVal[3] = de::min(minVal[3], resMin[3]); 68 69 maxVal[0] = de::max(maxVal[0], resMax[0]); 70 maxVal[1] = de::max(maxVal[1], resMax[1]); 71 maxVal[2] = de::max(maxVal[2], resMax[2]); 72 maxVal[3] = de::max(maxVal[3], resMax[3]); 73 } 74 75 for (int c = 0; c < 4; c++) 76 { 77 if (maxVal[c] - minVal[c] < eps) 78 { 79 scale[c] = (maxVal[c] < eps) ? 1.0f : (1.0f / maxVal[c]); 80 bias[c] = (c == 3) ? (1.0f - maxVal[c]*scale[c]) : (0.0f - minVal[c]*scale[c]); 81 } 82 else 83 { 84 scale[c] = 1.0f / (maxVal[c] - minVal[c]); 85 bias[c] = 0.0f - minVal[c]*scale[c]; 86 } 87 } 88 } 89 90 static int findNumPositionDeviationFailingPixels (const PixelBufferAccess& errorMask, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const UVec4& threshold, const tcu::IVec3& maxPositionDeviation, bool acceptOutOfBoundsAsAnyValue) 91 { 92 const int width = reference.getWidth(); 93 const int height = reference.getHeight(); 94 const int depth = reference.getDepth(); 95 int numFailingPixels = 0; 96 97 TCU_CHECK_INTERNAL(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth); 98 99 for (int z = 0; z < depth; z++) 100 { 101 for (int y = 0; y < height; y++) 102 { 103 for (int x = 0; x < width; x++) 104 { 105 const IVec4 refPix = reference.getPixelInt(x, y, z); 106 const IVec4 cmpPix = result.getPixelInt(x, y, z); 107 108 // Exact match 109 { 110 const UVec4 diff = abs(refPix - cmpPix).cast<deUint32>(); 111 const bool isOk = boolAll(lessThanEqual(diff, threshold)); 112 113 if (isOk) 114 { 115 errorMask.setPixel(IVec4(0, 0xff, 0, 0xff), x, y, z); 116 continue; 117 } 118 } 119 120 // Accept over the image bounds pixels since they could be anything 121 122 if (acceptOutOfBoundsAsAnyValue && 123 (x < maxPositionDeviation.x() || x + maxPositionDeviation.x() >= width || 124 y < maxPositionDeviation.y() || y + maxPositionDeviation.y() >= height || 125 z < maxPositionDeviation.z() || z + maxPositionDeviation.z() >= depth)) 126 { 127 errorMask.setPixel(IVec4(0, 0xff, 0, 0xff), x, y, z); 128 continue; 129 } 130 131 // Find matching pixels for both result and reference pixel 132 133 { 134 bool pixelFoundForReference = false; 135 bool pixelFoundForResult = false; 136 137 // Find deviated result pixel for reference 138 139 for (int sz = de::max(0, z - maxPositionDeviation.z()); sz <= de::min(depth - 1, z + maxPositionDeviation.z()) && !pixelFoundForReference; ++sz) 140 for (int sy = de::max(0, y - maxPositionDeviation.y()); sy <= de::min(height - 1, y + maxPositionDeviation.y()) && !pixelFoundForReference; ++sy) 141 for (int sx = de::max(0, x - maxPositionDeviation.x()); sx <= de::min(width - 1, x + maxPositionDeviation.x()) && !pixelFoundForReference; ++sx) 142 { 143 const IVec4 deviatedCmpPix = result.getPixelInt(sx, sy, sz); 144 const UVec4 diff = abs(refPix - deviatedCmpPix).cast<deUint32>(); 145 const bool isOk = boolAll(lessThanEqual(diff, threshold)); 146 147 pixelFoundForReference |= isOk; 148 } 149 150 // Find deviated reference pixel for result 151 152 for (int sz = de::max(0, z - maxPositionDeviation.z()); sz <= de::min(depth - 1, z + maxPositionDeviation.z()) && !pixelFoundForResult; ++sz) 153 for (int sy = de::max(0, y - maxPositionDeviation.y()); sy <= de::min(height - 1, y + maxPositionDeviation.y()) && !pixelFoundForResult; ++sy) 154 for (int sx = de::max(0, x - maxPositionDeviation.x()); sx <= de::min(width - 1, x + maxPositionDeviation.x()) && !pixelFoundForResult; ++sx) 155 { 156 const IVec4 deviatedRefPix = reference.getPixelInt(sx, sy, sz); 157 const UVec4 diff = abs(cmpPix - deviatedRefPix).cast<deUint32>(); 158 const bool isOk = boolAll(lessThanEqual(diff, threshold)); 159 160 pixelFoundForResult |= isOk; 161 } 162 163 if (pixelFoundForReference && pixelFoundForResult) 164 errorMask.setPixel(IVec4(0, 0xff, 0, 0xff), x, y, z); 165 else 166 { 167 errorMask.setPixel(IVec4(0xff, 0, 0, 0xff), x, y, z); 168 ++numFailingPixels; 169 } 170 } 171 } 172 } 173 } 174 175 return numFailingPixels; 176 } 177 178 } // anonymous 179 180 /*--------------------------------------------------------------------*//*! 181 * \brief Fuzzy image comparison 182 * 183 * This image comparison is designed for comparing images rendered by 3D 184 * graphics APIs such as OpenGL. The comparison allows small local differences 185 * and compensates for aliasing. 186 * 187 * The algorithm first performs light blurring on both images and then 188 * does per-pixel analysis. Pixels are compared to 3x3 bilinear surface 189 * defined by adjecent pixels. This compensates for both 1-pixel deviations 190 * in geometry and aliasing in texture data. 191 * 192 * Error metric is computed based on the differences. On valid images the 193 * metric is usually <0.01. Thus good threshold values are in range 0.02 to 194 * 0.05. 195 * 196 * On failure error image is generated that shows where the failing pixels 197 * are. 198 * 199 * \note Currently supports only UNORM_INT8 formats 200 * \param log Test log for results 201 * \param imageSetName Name for image set when logging results 202 * \param imageSetDesc Description for image set 203 * \param reference Reference image 204 * \param result Result image 205 * \param threshold Error metric threshold (good values are 0.02-0.05) 206 * \param logMode Logging mode 207 * \return true if comparison passes, false otherwise 208 *//*--------------------------------------------------------------------*/ 209 bool fuzzyCompare (TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, float threshold, CompareLogMode logMode) 210 { 211 FuzzyCompareParams params; // Use defaults. 212 TextureLevel errorMask (TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), reference.getWidth(), reference.getHeight()); 213 float difference = fuzzyCompare(params, reference, result, errorMask.getAccess()); 214 bool isOk = difference <= threshold; 215 Vec4 pixelBias (0.0f, 0.0f, 0.0f, 0.0f); 216 Vec4 pixelScale (1.0f, 1.0f, 1.0f, 1.0f); 217 218 if (!isOk || logMode == COMPARE_LOG_EVERYTHING) 219 { 220 // Generate more accurate error mask. 221 params.maxSampleSkip = 0; 222 fuzzyCompare(params, reference, result, errorMask.getAccess()); 223 224 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8) && reference.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) 225 computeScaleAndBias(reference, result, pixelScale, pixelBias); 226 227 if (!isOk) 228 log << TestLog::Message << "Image comparison failed: difference = " << difference << ", threshold = " << threshold << TestLog::EndMessage; 229 230 log << TestLog::ImageSet(imageSetName, imageSetDesc) 231 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 232 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias) 233 << TestLog::Image("ErrorMask", "Error mask", errorMask) 234 << TestLog::EndImageSet; 235 } 236 else if (logMode == COMPARE_LOG_RESULT) 237 { 238 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) 239 computePixelScaleBias(result, pixelScale, pixelBias); 240 241 log << TestLog::ImageSet(imageSetName, imageSetDesc) 242 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 243 << TestLog::EndImageSet; 244 } 245 246 return isOk; 247 } 248 249 /*--------------------------------------------------------------------*//*! 250 * \brief Fuzzy image comparison 251 * 252 * This image comparison is designed for comparing images rendered by 3D 253 * graphics APIs such as OpenGL. The comparison allows small local differences 254 * and compensates for aliasing. 255 * 256 * The algorithm first performs light blurring on both images and then 257 * does per-pixel analysis. Pixels are compared to 3x3 bilinear surface 258 * defined by adjecent pixels. This compensates for both 1-pixel deviations 259 * in geometry and aliasing in texture data. 260 * 261 * Error metric is computed based on the differences. On valid images the 262 * metric is usually <0.01. Thus good threshold values are in range 0.02 to 263 * 0.05. 264 * 265 * On failure error image is generated that shows where the failing pixels 266 * are. 267 * 268 * \note Currently supports only UNORM_INT8 formats 269 * \param log Test log for results 270 * \param imageSetName Name for image set when logging results 271 * \param imageSetDesc Description for image set 272 * \param reference Reference image 273 * \param result Result image 274 * \param threshold Error metric threshold (good values are 0.02-0.05) 275 * \param logMode Logging mode 276 * \return true if comparison passes, false otherwise 277 *//*--------------------------------------------------------------------*/ 278 bool fuzzyCompare (TestLog& log, const char* imageSetName, const char* imageSetDesc, const Surface& reference, const Surface& result, float threshold, CompareLogMode logMode) 279 { 280 return fuzzyCompare(log, imageSetName, imageSetDesc, reference.getAccess(), result.getAccess(), threshold, logMode); 281 } 282 283 static deInt64 computeSquaredDiffSum (const ConstPixelBufferAccess& ref, const ConstPixelBufferAccess& cmp, const PixelBufferAccess& diffMask, int diffFactor) 284 { 285 TCU_CHECK_INTERNAL(ref.getFormat().type == TextureFormat::UNORM_INT8 && cmp.getFormat().type == TextureFormat::UNORM_INT8); 286 DE_ASSERT(ref.getWidth() == cmp.getWidth() && ref.getWidth() == diffMask.getWidth()); 287 DE_ASSERT(ref.getHeight() == cmp.getHeight() && ref.getHeight() == diffMask.getHeight()); 288 289 deInt64 diffSum = 0; 290 291 for (int y = 0; y < cmp.getHeight(); y++) 292 { 293 for (int x = 0; x < cmp.getWidth(); x++) 294 { 295 IVec4 a = ref.getPixelInt(x, y); 296 IVec4 b = cmp.getPixelInt(x, y); 297 IVec4 diff = abs(a - b); 298 int sum = diff.x() + diff.y() + diff.z() + diff.w(); 299 int sqSum = diff.x()*diff.x() + diff.y()*diff.y() + diff.z()*diff.z() + diff.w()*diff.w(); 300 301 diffMask.setPixel(tcu::RGBA(deClamp32(sum*diffFactor, 0, 255), deClamp32(255-sum*diffFactor, 0, 255), 0, 255).toVec(), x, y); 302 303 diffSum += (deInt64)sqSum; 304 } 305 } 306 307 return diffSum; 308 } 309 310 /*--------------------------------------------------------------------*//*! 311 * \brief Per-pixel difference accuracy metric 312 * 313 * Computes accuracy metric using per-pixel differences between reference 314 * and result images. 315 * 316 * \note Supports only integer- and fixed-point formats 317 * \param log Test log for results 318 * \param imageSetName Name for image set when logging results 319 * \param imageSetDesc Description for image set 320 * \param reference Reference image 321 * \param result Result image 322 * \param bestScoreDiff Scaling factor 323 * \param worstScoreDiff Scaling factor 324 * \param logMode Logging mode 325 * \return true if comparison passes, false otherwise 326 *//*--------------------------------------------------------------------*/ 327 int measurePixelDiffAccuracy (TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, int bestScoreDiff, int worstScoreDiff, CompareLogMode logMode) 328 { 329 TextureLevel diffMask (TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), reference.getWidth(), reference.getHeight()); 330 int diffFactor = 8; 331 deInt64 squaredSum = computeSquaredDiffSum(reference, result, diffMask.getAccess(), diffFactor); 332 float sum = deFloatSqrt((float)squaredSum); 333 int score = deClamp32(deFloorFloatToInt32(100.0f - (de::max(sum-(float)bestScoreDiff, 0.0f) / (float)(worstScoreDiff-bestScoreDiff))*100.0f), 0, 100); 334 const int failThreshold = 10; 335 Vec4 pixelBias (0.0f, 0.0f, 0.0f, 0.0f); 336 Vec4 pixelScale (1.0f, 1.0f, 1.0f, 1.0f); 337 338 if (logMode == COMPARE_LOG_EVERYTHING || score <= failThreshold) 339 { 340 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8) && reference.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) 341 computeScaleAndBias(reference, result, pixelScale, pixelBias); 342 343 log << TestLog::ImageSet(imageSetName, imageSetDesc) 344 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 345 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias) 346 << TestLog::Image("DiffMask", "Difference", diffMask) 347 << TestLog::EndImageSet; 348 } 349 else if (logMode == COMPARE_LOG_RESULT) 350 { 351 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) 352 computePixelScaleBias(result, pixelScale, pixelBias); 353 354 log << TestLog::ImageSet(imageSetName, imageSetDesc) 355 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 356 << TestLog::EndImageSet; 357 } 358 359 if (logMode != COMPARE_LOG_ON_ERROR || score <= failThreshold) 360 log << TestLog::Integer("DiffSum", "Squared difference sum", "", QP_KEY_TAG_NONE, squaredSum) 361 << TestLog::Integer("Score", "Score", "", QP_KEY_TAG_QUALITY, score); 362 363 return score; 364 } 365 366 /*--------------------------------------------------------------------*//*! 367 * \brief Per-pixel difference accuracy metric 368 * 369 * Computes accuracy metric using per-pixel differences between reference 370 * and result images. 371 * 372 * \note Supports only integer- and fixed-point formats 373 * \param log Test log for results 374 * \param imageSetName Name for image set when logging results 375 * \param imageSetDesc Description for image set 376 * \param reference Reference image 377 * \param result Result image 378 * \param bestScoreDiff Scaling factor 379 * \param worstScoreDiff Scaling factor 380 * \param logMode Logging mode 381 * \return true if comparison passes, false otherwise 382 *//*--------------------------------------------------------------------*/ 383 int measurePixelDiffAccuracy (TestLog& log, const char* imageSetName, const char* imageSetDesc, const Surface& reference, const Surface& result, int bestScoreDiff, int worstScoreDiff, CompareLogMode logMode) 384 { 385 return measurePixelDiffAccuracy(log, imageSetName, imageSetDesc, reference.getAccess(), result.getAccess(), bestScoreDiff, worstScoreDiff, logMode); 386 } 387 388 /*--------------------------------------------------------------------*//*! 389 * \brief Per-pixel threshold-based comparison 390 * 391 * This compare computes per-pixel differences between result and reference 392 * image. Comparison fails if any pixels exceed the given threshold value. 393 * 394 * This comparison uses ULP (units in last place) metric for computing the 395 * difference between floating-point values and thus this function can 396 * be used only for comparing floating-point texture data. 397 * 398 * On failure error image is generated that shows where the failing pixels 399 * are. 400 * 401 * \param log Test log for results 402 * \param imageSetName Name for image set when logging results 403 * \param imageSetDesc Description for image set 404 * \param reference Reference image 405 * \param result Result image 406 * \param threshold Maximum allowed difference 407 * \param logMode Logging mode 408 * \return true if comparison passes, false otherwise 409 *//*--------------------------------------------------------------------*/ 410 bool floatUlpThresholdCompare (TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const UVec4& threshold, CompareLogMode logMode) 411 { 412 int width = reference.getWidth(); 413 int height = reference.getHeight(); 414 int depth = reference.getDepth(); 415 TextureLevel errorMaskStorage (TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth); 416 PixelBufferAccess errorMask = errorMaskStorage.getAccess(); 417 UVec4 maxDiff (0, 0, 0, 0); 418 Vec4 pixelBias (0.0f, 0.0f, 0.0f, 0.0f); 419 Vec4 pixelScale (1.0f, 1.0f, 1.0f, 1.0f); 420 421 TCU_CHECK(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth); 422 423 for (int z = 0; z < depth; z++) 424 { 425 for (int y = 0; y < height; y++) 426 { 427 for (int x = 0; x < width; x++) 428 { 429 Vec4 refPix = reference.getPixel(x, y, z); 430 Vec4 cmpPix = result.getPixel(x, y, z); 431 UVec4 refBits; 432 UVec4 cmpBits; 433 434 // memcpy() is the way to do float->uint32 reinterpretation. 435 memcpy(refBits.getPtr(), refPix.getPtr(), 4*sizeof(deUint32)); 436 memcpy(cmpBits.getPtr(), cmpPix.getPtr(), 4*sizeof(deUint32)); 437 438 UVec4 diff = abs(refBits.cast<int>() - cmpBits.cast<int>()).cast<deUint32>(); 439 bool isOk = boolAll(lessThanEqual(diff, threshold)); 440 441 maxDiff = max(maxDiff, diff); 442 443 errorMask.setPixel(isOk ? Vec4(0.0f, 1.0f, 0.0f, 1.0f) : Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y, z); 444 } 445 } 446 } 447 448 bool compareOk = boolAll(lessThanEqual(maxDiff, threshold)); 449 450 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING) 451 { 452 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images. 453 if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT || 454 tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT) 455 { 456 computeScaleAndBias(reference, result, pixelScale, pixelBias); 457 log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale << " + " << pixelBias << TestLog::EndMessage; 458 } 459 460 if (!compareOk) 461 log << TestLog::Message << "Image comparison failed: max difference = " << maxDiff << ", threshold = " << threshold << TestLog::EndMessage; 462 463 log << TestLog::ImageSet(imageSetName, imageSetDesc) 464 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 465 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias) 466 << TestLog::Image("ErrorMask", "Error mask", errorMask) 467 << TestLog::EndImageSet; 468 } 469 else if (logMode == COMPARE_LOG_RESULT) 470 { 471 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) 472 computePixelScaleBias(result, pixelScale, pixelBias); 473 474 log << TestLog::ImageSet(imageSetName, imageSetDesc) 475 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 476 << TestLog::EndImageSet; 477 } 478 479 return compareOk; 480 } 481 482 /*--------------------------------------------------------------------*//*! 483 * \brief Per-pixel threshold-based comparison 484 * 485 * This compare computes per-pixel differences between result and reference 486 * image. Comparison fails if any pixels exceed the given threshold value. 487 * 488 * This comparison can be used for floating-point and fixed-point formats. 489 * Difference is computed in floating-point space. 490 * 491 * On failure an error image is generated that shows where the failing 492 * pixels are. 493 * 494 * \param log Test log for results 495 * \param imageSetName Name for image set when logging results 496 * \param imageSetDesc Description for image set 497 * \param reference Reference image 498 * \param result Result image 499 * \param threshold Maximum allowed difference 500 * \param logMode Logging mode 501 * \return true if comparison passes, false otherwise 502 *//*--------------------------------------------------------------------*/ 503 bool floatThresholdCompare (TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const Vec4& threshold, CompareLogMode logMode) 504 { 505 int width = reference.getWidth(); 506 int height = reference.getHeight(); 507 int depth = reference.getDepth(); 508 TextureLevel errorMaskStorage (TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth); 509 PixelBufferAccess errorMask = errorMaskStorage.getAccess(); 510 Vec4 maxDiff (0.0f, 0.0f, 0.0f, 0.0f); 511 Vec4 pixelBias (0.0f, 0.0f, 0.0f, 0.0f); 512 Vec4 pixelScale (1.0f, 1.0f, 1.0f, 1.0f); 513 514 TCU_CHECK_INTERNAL(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth); 515 516 for (int z = 0; z < depth; z++) 517 { 518 for (int y = 0; y < height; y++) 519 { 520 for (int x = 0; x < width; x++) 521 { 522 Vec4 refPix = reference.getPixel(x, y, z); 523 Vec4 cmpPix = result.getPixel(x, y, z); 524 525 Vec4 diff = abs(refPix - cmpPix); 526 bool isOk = boolAll(lessThanEqual(diff, threshold)); 527 528 maxDiff = max(maxDiff, diff); 529 530 errorMask.setPixel(isOk ? Vec4(0.0f, 1.0f, 0.0f, 1.0f) : Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y, z); 531 } 532 } 533 } 534 535 bool compareOk = boolAll(lessThanEqual(maxDiff, threshold)); 536 537 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING) 538 { 539 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images. 540 if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT || 541 tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT) 542 { 543 computeScaleAndBias(reference, result, pixelScale, pixelBias); 544 log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale << " + " << pixelBias << TestLog::EndMessage; 545 } 546 547 if (!compareOk) 548 log << TestLog::Message << "Image comparison failed: max difference = " << maxDiff << ", threshold = " << threshold << TestLog::EndMessage; 549 550 log << TestLog::ImageSet(imageSetName, imageSetDesc) 551 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 552 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias) 553 << TestLog::Image("ErrorMask", "Error mask", errorMask) 554 << TestLog::EndImageSet; 555 } 556 else if (logMode == COMPARE_LOG_RESULT) 557 { 558 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) 559 computePixelScaleBias(result, pixelScale, pixelBias); 560 561 log << TestLog::ImageSet(imageSetName, imageSetDesc) 562 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 563 << TestLog::EndImageSet; 564 } 565 566 return compareOk; 567 } 568 569 /*--------------------------------------------------------------------*//*! 570 * \brief Per-pixel threshold-based comparison 571 * 572 * This compare computes per-pixel differences between result and reference 573 * color. Comparison fails if any pixels exceed the given threshold value. 574 * 575 * This comparison can be used for floating-point and fixed-point formats. 576 * Difference is computed in floating-point space. 577 * 578 * On failure an error image is generated that shows where the failing 579 * pixels are. 580 * 581 * \param log Test log for results 582 * \param imageSetName Name for image set when logging results 583 * \param imageSetDesc Description for image set 584 * \param reference Reference color 585 * \param result Result image 586 * \param threshold Maximum allowed difference 587 * \param logMode Logging mode 588 * \return true if comparison passes, false otherwise 589 *//*--------------------------------------------------------------------*/ 590 bool floatThresholdCompare (TestLog& log, const char* imageSetName, const char* imageSetDesc, const Vec4& reference, const ConstPixelBufferAccess& result, const Vec4& threshold, CompareLogMode logMode) 591 { 592 const int width = result.getWidth(); 593 const int height = result.getHeight(); 594 const int depth = result.getDepth(); 595 596 TextureLevel errorMaskStorage (TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth); 597 PixelBufferAccess errorMask = errorMaskStorage.getAccess(); 598 Vec4 maxDiff (0.0f, 0.0f, 0.0f, 0.0f); 599 Vec4 pixelBias (0.0f, 0.0f, 0.0f, 0.0f); 600 Vec4 pixelScale (1.0f, 1.0f, 1.0f, 1.0f); 601 602 for (int z = 0; z < depth; z++) 603 { 604 for (int y = 0; y < height; y++) 605 { 606 for (int x = 0; x < width; x++) 607 { 608 const Vec4 cmpPix = result.getPixel(x, y, z); 609 const Vec4 diff = abs(reference - cmpPix); 610 const bool isOk = boolAll(lessThanEqual(diff, threshold)); 611 612 maxDiff = max(maxDiff, diff); 613 614 errorMask.setPixel(isOk ? Vec4(0.0f, 1.0f, 0.0f, 1.0f) : Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y, z); 615 } 616 } 617 } 618 619 bool compareOk = boolAll(lessThanEqual(maxDiff, threshold)); 620 621 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING) 622 { 623 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images. 624 if (tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT) 625 { 626 computeScaleAndBias(result, result, pixelScale, pixelBias); 627 log << TestLog::Message << "Result image is normalized with formula p * " << pixelScale << " + " << pixelBias << TestLog::EndMessage; 628 } 629 630 if (!compareOk) 631 log << TestLog::Message << "Image comparison failed: max difference = " << maxDiff << ", threshold = " << threshold << ", reference = " << reference << TestLog::EndMessage; 632 633 log << TestLog::ImageSet(imageSetName, imageSetDesc) 634 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 635 << TestLog::Image("ErrorMask", "Error mask", errorMask) 636 << TestLog::EndImageSet; 637 } 638 else if (logMode == COMPARE_LOG_RESULT) 639 { 640 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) 641 computePixelScaleBias(result, pixelScale, pixelBias); 642 643 log << TestLog::ImageSet(imageSetName, imageSetDesc) 644 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 645 << TestLog::EndImageSet; 646 } 647 648 return compareOk; 649 } 650 651 /*--------------------------------------------------------------------*//*! 652 * \brief Per-pixel threshold-based comparison 653 * 654 * This compare computes per-pixel differences between result and reference 655 * image. Comparison fails if any pixels exceed the given threshold value. 656 * 657 * This comparison can be used for integer- and fixed-point texture formats. 658 * Difference is computed in integer space. 659 * 660 * On failure error image is generated that shows where the failing pixels 661 * are. 662 * 663 * \param log Test log for results 664 * \param imageSetName Name for image set when logging results 665 * \param imageSetDesc Description for image set 666 * \param reference Reference image 667 * \param result Result image 668 * \param threshold Maximum allowed difference 669 * \param logMode Logging mode 670 * \return true if comparison passes, false otherwise 671 *//*--------------------------------------------------------------------*/ 672 bool intThresholdCompare (TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const UVec4& threshold, CompareLogMode logMode) 673 { 674 int width = reference.getWidth(); 675 int height = reference.getHeight(); 676 int depth = reference.getDepth(); 677 TextureLevel errorMaskStorage (TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth); 678 PixelBufferAccess errorMask = errorMaskStorage.getAccess(); 679 UVec4 maxDiff (0, 0, 0, 0); 680 Vec4 pixelBias (0.0f, 0.0f, 0.0f, 0.0f); 681 Vec4 pixelScale (1.0f, 1.0f, 1.0f, 1.0f); 682 683 TCU_CHECK_INTERNAL(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth); 684 685 for (int z = 0; z < depth; z++) 686 { 687 for (int y = 0; y < height; y++) 688 { 689 for (int x = 0; x < width; x++) 690 { 691 IVec4 refPix = reference.getPixelInt(x, y, z); 692 IVec4 cmpPix = result.getPixelInt(x, y, z); 693 694 UVec4 diff = abs(refPix - cmpPix).cast<deUint32>(); 695 bool isOk = boolAll(lessThanEqual(diff, threshold)); 696 697 maxDiff = max(maxDiff, diff); 698 699 errorMask.setPixel(isOk ? IVec4(0, 0xff, 0, 0xff) : IVec4(0xff, 0, 0, 0xff), x, y, z); 700 } 701 } 702 } 703 704 bool compareOk = boolAll(lessThanEqual(maxDiff, threshold)); 705 706 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING) 707 { 708 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images. 709 if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT || 710 tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT) 711 { 712 computeScaleAndBias(reference, result, pixelScale, pixelBias); 713 log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale << " + " << pixelBias << TestLog::EndMessage; 714 } 715 716 if (!compareOk) 717 log << TestLog::Message << "Image comparison failed: max difference = " << maxDiff << ", threshold = " << threshold << TestLog::EndMessage; 718 719 log << TestLog::ImageSet(imageSetName, imageSetDesc) 720 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 721 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias) 722 << TestLog::Image("ErrorMask", "Error mask", errorMask) 723 << TestLog::EndImageSet; 724 } 725 else if (logMode == COMPARE_LOG_RESULT) 726 { 727 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) 728 computePixelScaleBias(result, pixelScale, pixelBias); 729 730 log << TestLog::ImageSet(imageSetName, imageSetDesc) 731 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 732 << TestLog::EndImageSet; 733 } 734 735 return compareOk; 736 } 737 738 /*--------------------------------------------------------------------*//*! 739 * \brief Per-pixel threshold-based deviation-ignoring comparison 740 * 741 * This compare computes per-pixel differences between result and reference 742 * image. Comparison fails if there is no pixel matching the given threshold 743 * value in the search volume. 744 * 745 * If the search volume contains out-of-bounds pixels, comparison can be set 746 * to either ignore these pixels in search or to accept any pixel that has 747 * out-of-bounds pixels in its search volume. 748 * 749 * This comparison can be used for integer- and fixed-point texture formats. 750 * Difference is computed in integer space. 751 * 752 * On failure error image is generated that shows where the failing pixels 753 * are. 754 * 755 * \param log Test log for results 756 * \param imageSetName Name for image set when logging results 757 * \param imageSetDesc Description for image set 758 * \param reference Reference image 759 * \param result Result image 760 * \param threshold Maximum allowed difference 761 * \param maxPositionDeviation Maximum allowed distance in the search 762 * volume. 763 * \param acceptOutOfBoundsAsAnyValue Accept any pixel in the boundary region 764 * \param logMode Logging mode 765 * \return true if comparison passes, false otherwise 766 *//*--------------------------------------------------------------------*/ 767 bool 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) 768 { 769 const int width = reference.getWidth(); 770 const int height = reference.getHeight(); 771 const int depth = reference.getDepth(); 772 TextureLevel errorMaskStorage (TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth); 773 PixelBufferAccess errorMask = errorMaskStorage.getAccess(); 774 const int numFailingPixels = findNumPositionDeviationFailingPixels(errorMask, reference, result, threshold, maxPositionDeviation, acceptOutOfBoundsAsAnyValue); 775 const bool compareOk = numFailingPixels == 0; 776 Vec4 pixelBias (0.0f, 0.0f, 0.0f, 0.0f); 777 Vec4 pixelScale (1.0f, 1.0f, 1.0f, 1.0f); 778 779 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING) 780 { 781 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images. 782 if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT || 783 tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT) 784 { 785 computeScaleAndBias(reference, result, pixelScale, pixelBias); 786 log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale << " + " << pixelBias << TestLog::EndMessage; 787 } 788 789 if (!compareOk) 790 log << TestLog::Message 791 << "Image comparison failed:\n" 792 << "\tallowed position deviation = " << maxPositionDeviation << "\n" 793 << "\tcolor threshold = " << threshold 794 << TestLog::EndMessage; 795 796 log << TestLog::ImageSet(imageSetName, imageSetDesc) 797 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 798 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias) 799 << TestLog::Image("ErrorMask", "Error mask", errorMask) 800 << TestLog::EndImageSet; 801 } 802 else if (logMode == COMPARE_LOG_RESULT) 803 { 804 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) 805 computePixelScaleBias(result, pixelScale, pixelBias); 806 807 log << TestLog::ImageSet(imageSetName, imageSetDesc) 808 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 809 << TestLog::EndImageSet; 810 } 811 812 return compareOk; 813 } 814 815 /*--------------------------------------------------------------------*//*! 816 * \brief Per-pixel threshold-based deviation-ignoring comparison 817 * 818 * This compare computes per-pixel differences between result and reference 819 * image. Pixel fails the test if there is no pixel matching the given 820 * threshold value in the search volume. Comparison fails if the number of 821 * failing pixels exceeds the given limit. 822 * 823 * If the search volume contains out-of-bounds pixels, comparison can be set 824 * to either ignore these pixels in search or to accept any pixel that has 825 * out-of-bounds pixels in its search volume. 826 * 827 * This comparison can be used for integer- and fixed-point texture formats. 828 * Difference is computed in integer space. 829 * 830 * On failure error image is generated that shows where the failing pixels 831 * are. 832 * 833 * \param log Test log for results 834 * \param imageSetName Name for image set when logging results 835 * \param imageSetDesc Description for image set 836 * \param reference Reference image 837 * \param result Result image 838 * \param threshold Maximum allowed difference 839 * \param maxPositionDeviation Maximum allowed distance in the search 840 * volume. 841 * \param acceptOutOfBoundsAsAnyValue Accept any pixel in the boundary region 842 * \param maxAllowedFailingPixels Maximum number of failing pixels 843 * \param logMode Logging mode 844 * \return true if comparison passes, false otherwise 845 *//*--------------------------------------------------------------------*/ 846 bool 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) 847 { 848 const int width = reference.getWidth(); 849 const int height = reference.getHeight(); 850 const int depth = reference.getDepth(); 851 TextureLevel errorMaskStorage (TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), width, height, depth); 852 PixelBufferAccess errorMask = errorMaskStorage.getAccess(); 853 const int numFailingPixels = findNumPositionDeviationFailingPixels(errorMask, reference, result, threshold, maxPositionDeviation, acceptOutOfBoundsAsAnyValue); 854 const bool compareOk = numFailingPixels <= maxAllowedFailingPixels; 855 Vec4 pixelBias (0.0f, 0.0f, 0.0f, 0.0f); 856 Vec4 pixelScale (1.0f, 1.0f, 1.0f, 1.0f); 857 858 if (!compareOk || logMode == COMPARE_LOG_EVERYTHING) 859 { 860 // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images. 861 if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT || 862 tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT) 863 { 864 computeScaleAndBias(reference, result, pixelScale, pixelBias); 865 log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale << " + " << pixelBias << TestLog::EndMessage; 866 } 867 868 if (!compareOk) 869 log << TestLog::Message 870 << "Image comparison failed:\n" 871 << "\tallowed position deviation = " << maxPositionDeviation << "\n" 872 << "\tcolor threshold = " << threshold 873 << TestLog::EndMessage; 874 log << TestLog::Message << "Number of failing pixels = " << numFailingPixels << ", max allowed = " << maxAllowedFailingPixels << TestLog::EndMessage; 875 876 log << TestLog::ImageSet(imageSetName, imageSetDesc) 877 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 878 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias) 879 << TestLog::Image("ErrorMask", "Error mask", errorMask) 880 << TestLog::EndImageSet; 881 } 882 else if (logMode == COMPARE_LOG_RESULT) 883 { 884 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) 885 computePixelScaleBias(result, pixelScale, pixelBias); 886 887 log << TestLog::ImageSet(imageSetName, imageSetDesc) 888 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 889 << TestLog::EndImageSet; 890 } 891 892 return compareOk; 893 } 894 895 /*--------------------------------------------------------------------*//*! 896 * \brief Per-pixel threshold-based comparison 897 * 898 * This compare computes per-pixel differences between result and reference 899 * image. Comparison fails if any pixels exceed the given threshold value. 900 * 901 * On failure error image is generated that shows where the failing pixels 902 * are. 903 * 904 * \param log Test log for results 905 * \param imageSetName Name for image set when logging results 906 * \param imageSetDesc Description for image set 907 * \param reference Reference image 908 * \param result Result image 909 * \param threshold Maximum allowed difference 910 * \param logMode Logging mode 911 * \return true if comparison passes, false otherwise 912 *//*--------------------------------------------------------------------*/ 913 bool pixelThresholdCompare (TestLog& log, const char* imageSetName, const char* imageSetDesc, const Surface& reference, const Surface& result, const RGBA& threshold, CompareLogMode logMode) 914 { 915 return intThresholdCompare(log, imageSetName, imageSetDesc, reference.getAccess(), result.getAccess(), threshold.toIVec().cast<deUint32>(), logMode); 916 } 917 918 /*--------------------------------------------------------------------*//*! 919 * \brief Bilinear image comparison 920 * 921 * \todo [pyry] Describe 922 * 923 * On failure error image is generated that shows where the failing pixels 924 * are. 925 * 926 * \note Currently supports only RGBA, UNORM_INT8 formats 927 * \param log Test log for results 928 * \param imageSetName Name for image set when logging results 929 * \param imageSetDesc Description for image set 930 * \param reference Reference image 931 * \param result Result image 932 * \param threshold Maximum local difference 933 * \param logMode Logging mode 934 * \return true if comparison passes, false otherwise 935 *//*--------------------------------------------------------------------*/ 936 bool bilinearCompare (TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const RGBA threshold, CompareLogMode logMode) 937 { 938 TextureLevel errorMask (TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8), reference.getWidth(), reference.getHeight()); 939 bool isOk = bilinearCompare(reference, result, errorMask, threshold); 940 Vec4 pixelBias (0.0f, 0.0f, 0.0f, 0.0f); 941 Vec4 pixelScale (1.0f, 1.0f, 1.0f, 1.0f); 942 943 if (!isOk || logMode == COMPARE_LOG_EVERYTHING) 944 { 945 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8) && reference.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) 946 computeScaleAndBias(reference, result, pixelScale, pixelBias); 947 948 if (!isOk) 949 log << TestLog::Message << "Image comparison failed, threshold = " << threshold << TestLog::EndMessage; 950 951 log << TestLog::ImageSet(imageSetName, imageSetDesc) 952 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 953 << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias) 954 << TestLog::Image("ErrorMask", "Error mask", errorMask) 955 << TestLog::EndImageSet; 956 } 957 else if (logMode == COMPARE_LOG_RESULT) 958 { 959 if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) 960 computePixelScaleBias(result, pixelScale, pixelBias); 961 962 log << TestLog::ImageSet(imageSetName, imageSetDesc) 963 << TestLog::Image("Result", "Result", result, pixelScale, pixelBias) 964 << TestLog::EndImageSet; 965 } 966 967 return isOk; 968 } 969 970 } // tcu 971