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