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
37namespace tcu
38{
39
40namespace
41{
42
43void 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
90static 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 *//*--------------------------------------------------------------------*/
209bool 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 *//*--------------------------------------------------------------------*/
278bool 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
283static 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 *//*--------------------------------------------------------------------*/
327int 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 *//*--------------------------------------------------------------------*/
383int 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 *//*--------------------------------------------------------------------*/
410bool 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 *//*--------------------------------------------------------------------*/
503bool 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 *//*--------------------------------------------------------------------*/
590bool 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 *//*--------------------------------------------------------------------*/
672bool 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 *//*--------------------------------------------------------------------*/
767bool 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 *//*--------------------------------------------------------------------*/
846bool 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 *//*--------------------------------------------------------------------*/
913bool 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 *//*--------------------------------------------------------------------*/
936bool 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