1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Tester Core
3  * ----------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Image comparison utilities.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "tcuImageCompare.hpp"
25 #include "tcuSurface.hpp"
26 #include "tcuFuzzyImageCompare.hpp"
27 #include "tcuBilinearImageCompare.hpp"
28 #include "tcuTestLog.hpp"
29 #include "tcuVector.hpp"
30 #include "tcuVectorUtil.hpp"
31 #include "tcuRGBA.hpp"
32 #include "tcuTexture.hpp"
33 #include "tcuTextureUtil.hpp"
34 #include "tcuFloat.hpp"
35 
36 #include <string.h>
37 
38 namespace tcu
39 {
40 
41 namespace
42 {
43 
computeScaleAndBias(const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, tcu::Vec4& scale, tcu::Vec4& bias)44 void computeScaleAndBias (const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, 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 
findNumPositionDeviationFailingPixels(const PixelBufferAccess& errorMask, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const UVec4& threshold, const tcu::IVec3& maxPositionDeviation, bool acceptOutOfBoundsAsAnyValue)91 static 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  *//*--------------------------------------------------------------------*/
fuzzyCompare(TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, float threshold, CompareLogMode logMode)217 bool 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  *//*--------------------------------------------------------------------*/
bitwiseCompare(TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, CompareLogMode logMode)278 bool 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  *//*--------------------------------------------------------------------*/
fuzzyCompare(TestLog& log, const char* imageSetName, const char* imageSetDesc, const Surface& reference, const Surface& result, float threshold, CompareLogMode logMode)381 bool 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 
computeSquaredDiffSum(const ConstPixelBufferAccess& ref, const ConstPixelBufferAccess& cmp, const PixelBufferAccess& diffMask, int diffFactor)386 static 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  *//*--------------------------------------------------------------------*/
measurePixelDiffAccuracy(TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, int bestScoreDiff, int worstScoreDiff, CompareLogMode logMode)430 int 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  *//*--------------------------------------------------------------------*/
measurePixelDiffAccuracy(TestLog& log, const char* imageSetName, const char* imageSetDesc, const Surface& reference, const Surface& result, int bestScoreDiff, int worstScoreDiff, CompareLogMode logMode)486 int 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  *//*--------------------------------------------------------------------*/
getPositionOfIEEEFloatWithoutDenormals(float x)500 static 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 
computeFloatFlushRelaxedULPDiff(float a, float b)533 static 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 
computeFlushRelaxedULPDiff(const tcu::Vec4& a, const tcu::Vec4& b)552 static 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  *//*--------------------------------------------------------------------*/
floatUlpThresholdCompare(TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const UVec4& threshold, CompareLogMode logMode)583 bool 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  *//*--------------------------------------------------------------------*/
floatThresholdCompare(TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const Vec4& threshold, CompareLogMode logMode)669 bool 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  *//*--------------------------------------------------------------------*/
floatThresholdCompare(TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const Vec4& ignorekey, const Vec4& threshold, CompareLogMode logMode)759 bool 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  *//*--------------------------------------------------------------------*/
floatThresholdCompare(TestLog& log, const char* imageSetName, const char* imageSetDesc, const Vec4& reference, const ConstPixelBufferAccess& result, const Vec4& threshold, CompareLogMode logMode)850 bool 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  *//*--------------------------------------------------------------------*/
intThresholdCompare(TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const UVec4& threshold, CompareLogMode logMode, bool use64Bits)936 bool 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  *//*--------------------------------------------------------------------*/
dsThresholdCompare(TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const float threshold, CompareLogMode logMode)1043 bool 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  *//*--------------------------------------------------------------------*/
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)1153 bool intThresholdPositionDeviationCompare (TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const UVec4& threshold, const tcu::IVec3& maxPositionDeviation, bool acceptOutOfBoundsAsAnyValue, CompareLogMode logMode)
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  *//*--------------------------------------------------------------------*/
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)1232 bool intThresholdPositionDeviationErrorThresholdCompare (TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const UVec4& threshold, const tcu::IVec3& maxPositionDeviation, bool acceptOutOfBoundsAsAnyValue, int maxAllowedFailingPixels, CompareLogMode logMode)
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  *//*--------------------------------------------------------------------*/
pixelThresholdCompare(TestLog& log, const char* imageSetName, const char* imageSetDesc, const Surface& reference, const Surface& result, const RGBA& threshold, CompareLogMode logMode)1299 bool 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  *//*--------------------------------------------------------------------*/
bilinearCompare(TestLog& log, const char* imageSetName, const char* imageSetDesc, const ConstPixelBufferAccess& reference, const ConstPixelBufferAccess& result, const RGBA threshold, CompareLogMode logMode)1322 bool 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