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 Texture compare (shadow) result verifier.
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuTexCompareVerifier.hpp"
25#include "tcuTexVerifierUtil.hpp"
26#include "tcuTextureUtil.hpp"
27#include "tcuVectorUtil.hpp"
28#include "deMath.h"
29
30namespace tcu
31{
32
33using namespace TexVerifierUtil;
34
35// Generic utilities
36
37#if defined(DE_DEBUG)
38static bool isSamplerSupported (const Sampler& sampler)
39{
40	return sampler.compare != Sampler::COMPAREMODE_NONE &&
41		   isWrapModeSupported(sampler.wrapS)			&&
42		   isWrapModeSupported(sampler.wrapT)			&&
43		   isWrapModeSupported(sampler.wrapR);
44}
45#endif // DE_DEBUG
46
47struct CmpResultSet
48{
49	bool	isTrue;
50	bool	isFalse;
51
52	CmpResultSet (void)
53		: isTrue	(false)
54		, isFalse	(false)
55	{
56	}
57};
58
59static CmpResultSet execCompare (const Sampler::CompareMode	compareMode,
60								 const float				cmpValue_,
61								 const float				cmpReference_,
62								 const int					referenceBits,
63								 const bool					isFixedPoint)
64{
65	const bool		clampValues		= isFixedPoint;	// if comparing against a floating point texture, ref (and value) is not clamped
66	const float		cmpValue		= (clampValues) ? (de::clamp(cmpValue_, 0.0f, 1.0f)) : (cmpValue_);
67	const float		cmpReference	= (clampValues) ? (de::clamp(cmpReference_, 0.0f, 1.0f)) : (cmpReference_);
68	const float		err				= computeFixedPointError(referenceBits);
69	CmpResultSet	res;
70
71	switch (compareMode)
72	{
73		case Sampler::COMPAREMODE_LESS:
74			res.isTrue	= cmpReference-err < cmpValue;
75			res.isFalse	= cmpReference+err >= cmpValue;
76			break;
77
78		case Sampler::COMPAREMODE_LESS_OR_EQUAL:
79			res.isTrue	= cmpReference-err <= cmpValue;
80			res.isFalse	= cmpReference+err > cmpValue;
81			break;
82
83		case Sampler::COMPAREMODE_GREATER:
84			res.isTrue	= cmpReference+err > cmpValue;
85			res.isFalse	= cmpReference-err <= cmpValue;
86			break;
87
88		case Sampler::COMPAREMODE_GREATER_OR_EQUAL:
89			res.isTrue	= cmpReference+err >= cmpValue;
90			res.isFalse	= cmpReference-err < cmpValue;
91			break;
92
93		case Sampler::COMPAREMODE_EQUAL:
94			res.isTrue	= de::inRange(cmpValue, cmpReference-err, cmpReference+err);
95			res.isFalse	= err != 0.0f || cmpValue != cmpReference;
96			break;
97
98		case Sampler::COMPAREMODE_NOT_EQUAL:
99			res.isTrue	= err != 0.0f || cmpValue != cmpReference;
100			res.isFalse	= de::inRange(cmpValue, cmpReference-err, cmpReference+err);
101			break;
102
103		case Sampler::COMPAREMODE_ALWAYS:
104			res.isTrue	= true;
105			break;
106
107		case Sampler::COMPAREMODE_NEVER:
108			res.isFalse	= true;
109			break;
110
111		default:
112			DE_ASSERT(false);
113	}
114
115	DE_ASSERT(res.isTrue || res.isFalse);
116	return res;
117}
118
119static inline bool isResultInSet (const CmpResultSet resultSet, const float result, const int resultBits)
120{
121	const float err		= computeFixedPointError(resultBits);
122	const float	minR	= result-err;
123	const float	maxR	= result+err;
124
125	return (resultSet.isTrue	&& de::inRange(1.0f, minR, maxR)) ||
126		   (resultSet.isFalse	&& de::inRange(0.0f, minR, maxR));
127}
128
129// Values are in order (0,0), (1,0), (0,1), (1,1)
130static float bilinearInterpolate (const Vec4& values, const float x, const float y)
131{
132	const float		v00		= values[0];
133	const float		v10		= values[1];
134	const float		v01		= values[2];
135	const float		v11		= values[3];
136	const float		res		= v00*(1.0f-x)*(1.0f-y) + v10*x*(1.0f-y) + v01*(1.0f-x)*y + v11*x*y;
137	return res;
138}
139
140static bool isFixedPointDepthTextureFormat (const tcu::TextureFormat& format)
141{
142	const tcu::TextureChannelClass channelClass = tcu::getTextureChannelClass(format.type);
143
144	if (format.order == TextureFormat::D)
145	{
146		// depth internal formats cannot be non-normalized integers
147		return channelClass != tcu::TEXTURECHANNELCLASS_FLOATING_POINT;
148	}
149	else if (format.order == TextureFormat::DS)
150	{
151		// combined formats have no single channel class, detect format manually
152		switch (format.type)
153		{
154			case tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV:	return false;
155			case tcu::TextureFormat::UNSIGNED_INT_24_8:				return true;
156
157			default:
158			{
159				// unknown format
160				DE_ASSERT(false);
161				return true;
162			}
163		}
164	}
165
166	return false;
167}
168
169static bool isLinearCompareValid (const Sampler::CompareMode	compareMode,
170								  const TexComparePrecision&	prec,
171								  const Vec2&					depths,
172								  const Vec2&					fBounds,
173								  const float					cmpReference,
174								  const float					result,
175								  const bool					isFixedPointDepth)
176{
177	DE_ASSERT(0.0f <= fBounds.x() && fBounds.x() <= fBounds.y() && fBounds.y() <= 1.0f);
178
179	const float			d0			= depths[0];
180	const float			d1			= depths[1];
181
182	const CmpResultSet	cmp0		= execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth);
183	const CmpResultSet	cmp1		= execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth);
184
185	const deUint32		isTrue		= (deUint32(cmp0.isTrue)<<0)
186									| (deUint32(cmp1.isTrue)<<1);
187	const deUint32		isFalse		= (deUint32(cmp0.isFalse)<<0)
188									| (deUint32(cmp1.isFalse)<<1);
189
190	// Interpolation parameters
191	const float			f0			= fBounds.x();
192	const float			f1			= fBounds.y();
193
194	// Error parameters
195	const float			pcfErr		= computeFixedPointError(prec.pcfBits);
196	const float			resErr		= computeFixedPointError(prec.resultBits);
197	const float			totalErr	= pcfErr+resErr;
198
199	// Iterate over all valid combinations.
200	for (deUint32 comb = 0; comb < (1<<2); comb++)
201	{
202		// Filter out invalid combinations.
203		if (((comb & isTrue) | (~comb & isFalse)) != (1<<2)-1)
204			continue;
205
206		const bool		cmp0True	= ((comb>>0)&1) != 0;
207		const bool		cmp1True	= ((comb>>1)&1) != 0;
208
209		const float		ref0		= cmp0True ? 1.0f : 0.0f;
210		const float		ref1		= cmp1True ? 1.0f : 0.0f;
211
212		const float		v0			= ref0*(1.0f-f0) + ref1*f0;
213		const float		v1			= ref0*(1.0f-f1) + ref1*f1;
214		const float		minV		= de::min(v0, v1);
215		const float		maxV		= de::max(v0, v1);
216		const float		minR		= minV-totalErr;
217		const float		maxR		= maxV+totalErr;
218
219		if (de::inRange(result, minR, maxR))
220			return true;
221	}
222
223	return false;
224}
225
226static inline BVec4 extractBVec4 (const deUint32 val, int offset)
227{
228	return BVec4(((val>>(offset+0))&1) != 0,
229				 ((val>>(offset+1))&1) != 0,
230				 ((val>>(offset+2))&1) != 0,
231				 ((val>>(offset+3))&1) != 0);
232}
233
234static bool isBilinearAnyCompareValid (const Sampler::CompareMode	compareMode,
235									   const TexComparePrecision&	prec,
236									   const Vec4&					depths,
237									   const float					cmpReference,
238									   const float					result,
239									   const bool					isFixedPointDepth)
240{
241	DE_ASSERT(prec.pcfBits == 0);
242
243	const float			d0			= depths[0];
244	const float			d1			= depths[1];
245	const float			d2			= depths[2];
246	const float			d3			= depths[3];
247
248	const CmpResultSet	cmp0		= execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth);
249	const CmpResultSet	cmp1		= execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth);
250	const CmpResultSet	cmp2		= execCompare(compareMode, d2, cmpReference, prec.referenceBits, isFixedPointDepth);
251	const CmpResultSet	cmp3		= execCompare(compareMode, d3, cmpReference, prec.referenceBits, isFixedPointDepth);
252
253	const bool			canBeTrue	= cmp0.isTrue || cmp1.isTrue || cmp2.isTrue || cmp3.isTrue;
254	const bool			canBeFalse	= cmp0.isFalse || cmp1.isFalse || cmp2.isFalse || cmp3.isFalse;
255
256	const float			resErr		= computeFixedPointError(prec.resultBits);
257
258	const float			minBound	= canBeFalse ? 0.0f : 1.0f;
259	const float			maxBound	= canBeTrue ? 1.0f : 0.0f;
260
261	return de::inRange(result, minBound-resErr, maxBound+resErr);
262}
263
264static bool isBilinearPCFCompareValid (const Sampler::CompareMode	compareMode,
265									   const TexComparePrecision&	prec,
266									   const Vec4&					depths,
267									   const Vec2&					xBounds,
268									   const Vec2&					yBounds,
269									   const float					cmpReference,
270									   const float					result,
271									   const bool					isFixedPointDepth)
272{
273	DE_ASSERT(0.0f <= xBounds.x() && xBounds.x() <= xBounds.y() && xBounds.y() <= 1.0f);
274	DE_ASSERT(0.0f <= yBounds.x() && yBounds.x() <= yBounds.y() && yBounds.y() <= 1.0f);
275	DE_ASSERT(prec.pcfBits > 0);
276
277	const float			d0			= depths[0];
278	const float			d1			= depths[1];
279	const float			d2			= depths[2];
280	const float			d3			= depths[3];
281
282	const CmpResultSet	cmp0		= execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth);
283	const CmpResultSet	cmp1		= execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth);
284	const CmpResultSet	cmp2		= execCompare(compareMode, d2, cmpReference, prec.referenceBits, isFixedPointDepth);
285	const CmpResultSet	cmp3		= execCompare(compareMode, d3, cmpReference, prec.referenceBits, isFixedPointDepth);
286
287	const deUint32		isTrue		= (deUint32(cmp0.isTrue)<<0)
288									| (deUint32(cmp1.isTrue)<<1)
289									| (deUint32(cmp2.isTrue)<<2)
290									| (deUint32(cmp3.isTrue)<<3);
291	const deUint32		isFalse		= (deUint32(cmp0.isFalse)<<0)
292									| (deUint32(cmp1.isFalse)<<1)
293									| (deUint32(cmp2.isFalse)<<2)
294									| (deUint32(cmp3.isFalse)<<3);
295
296	// Interpolation parameters
297	const float			x0			= xBounds.x();
298	const float			x1			= xBounds.y();
299	const float			y0			= yBounds.x();
300	const float			y1			= yBounds.y();
301
302	// Error parameters
303	const float			pcfErr		= computeFixedPointError(prec.pcfBits);
304	const float			resErr		= computeFixedPointError(prec.resultBits);
305	const float			totalErr	= pcfErr+resErr;
306
307	// Iterate over all valid combinations.
308	// \note It is not enough to compute minmax over all possible result sets, as ranges may
309	//		 not necessarily overlap, i.e. there are gaps between valid ranges.
310	for (deUint32 comb = 0; comb < (1<<4); comb++)
311	{
312		// Filter out invalid combinations:
313		//  1) True bit is set in comb but not in isTrue => sample can not be true
314		//  2) True bit is NOT set in comb and not in isFalse => sample can not be false
315		if (((comb & isTrue) | (~comb & isFalse)) != (1<<4)-1)
316			continue;
317
318		const BVec4		cmpTrue		= extractBVec4(comb, 0);
319		const Vec4		refVal		= select(Vec4(1.0f), Vec4(0.0f), cmpTrue);
320
321		const float		v0			= bilinearInterpolate(refVal, x0, y0);
322		const float		v1			= bilinearInterpolate(refVal, x1, y0);
323		const float		v2			= bilinearInterpolate(refVal, x0, y1);
324		const float		v3			= bilinearInterpolate(refVal, x1, y1);
325		const float		minV		= de::min(v0, de::min(v1, de::min(v2, v3)));
326		const float		maxV		= de::max(v0, de::max(v1, de::max(v2, v3)));
327		const float		minR		= minV-totalErr;
328		const float		maxR		= maxV+totalErr;
329
330		if (de::inRange(result, minR, maxR))
331			return true;
332	}
333
334	return false;
335}
336
337static bool isBilinearCompareValid (const Sampler::CompareMode	compareMode,
338									const TexComparePrecision&	prec,
339									const Vec4&					depths,
340									const Vec2&					xBounds,
341									const Vec2&					yBounds,
342									const float					cmpReference,
343									const float					result,
344									const bool					isFixedPointDepth)
345{
346	if (prec.pcfBits > 0)
347		return isBilinearPCFCompareValid(compareMode, prec, depths, xBounds, yBounds, cmpReference, result, isFixedPointDepth);
348	else
349		return isBilinearAnyCompareValid(compareMode, prec, depths, cmpReference, result, isFixedPointDepth);
350}
351
352static bool isTrilinearAnyCompareValid (const Sampler::CompareMode	compareMode,
353										const TexComparePrecision&	prec,
354										const Vec4&					depths0,
355										const Vec4&					depths1,
356										const float					cmpReference,
357										const float					result,
358										const bool					isFixedPointDepth)
359{
360	DE_ASSERT(prec.pcfBits == 0);
361
362	const CmpResultSet	cmp00		= execCompare(compareMode, depths0[0], cmpReference, prec.referenceBits, isFixedPointDepth);
363	const CmpResultSet	cmp01		= execCompare(compareMode, depths0[1], cmpReference, prec.referenceBits, isFixedPointDepth);
364	const CmpResultSet	cmp02		= execCompare(compareMode, depths0[2], cmpReference, prec.referenceBits, isFixedPointDepth);
365	const CmpResultSet	cmp03		= execCompare(compareMode, depths0[3], cmpReference, prec.referenceBits, isFixedPointDepth);
366
367	const CmpResultSet	cmp10		= execCompare(compareMode, depths1[0], cmpReference, prec.referenceBits, isFixedPointDepth);
368	const CmpResultSet	cmp11		= execCompare(compareMode, depths1[1], cmpReference, prec.referenceBits, isFixedPointDepth);
369	const CmpResultSet	cmp12		= execCompare(compareMode, depths1[2], cmpReference, prec.referenceBits, isFixedPointDepth);
370	const CmpResultSet	cmp13		= execCompare(compareMode, depths1[3], cmpReference, prec.referenceBits, isFixedPointDepth);
371
372	const bool			canBeTrue	= cmp00.isTrue ||
373									  cmp01.isTrue ||
374									  cmp02.isTrue ||
375									  cmp03.isTrue ||
376									  cmp10.isTrue ||
377									  cmp11.isTrue ||
378									  cmp12.isTrue ||
379									  cmp13.isTrue;
380	const bool			canBeFalse	= cmp00.isFalse ||
381									  cmp01.isFalse ||
382									  cmp02.isFalse ||
383									  cmp03.isFalse ||
384									  cmp10.isFalse ||
385									  cmp11.isFalse ||
386									  cmp12.isFalse ||
387									  cmp13.isFalse;
388
389	const float			resErr		= computeFixedPointError(prec.resultBits);
390
391	const float			minBound	= canBeFalse ? 0.0f : 1.0f;
392	const float			maxBound	= canBeTrue ? 1.0f : 0.0f;
393
394	return de::inRange(result, minBound-resErr, maxBound+resErr);
395}
396
397static bool isTrilinearPCFCompareValid (const Sampler::CompareMode	compareMode,
398										const TexComparePrecision&	prec,
399										const Vec4&					depths0,
400										const Vec4&					depths1,
401										const Vec2&					xBounds0,
402										const Vec2&					yBounds0,
403										const Vec2&					xBounds1,
404										const Vec2&					yBounds1,
405										const Vec2&					fBounds,
406										const float					cmpReference,
407										const float					result,
408										const bool					isFixedPointDepth)
409{
410	DE_ASSERT(0.0f <= xBounds0.x() && xBounds0.x() <= xBounds0.y() && xBounds0.y() <= 1.0f);
411	DE_ASSERT(0.0f <= yBounds0.x() && yBounds0.x() <= yBounds0.y() && yBounds0.y() <= 1.0f);
412	DE_ASSERT(0.0f <= xBounds1.x() && xBounds1.x() <= xBounds1.y() && xBounds1.y() <= 1.0f);
413	DE_ASSERT(0.0f <= yBounds1.x() && yBounds1.x() <= yBounds1.y() && yBounds1.y() <= 1.0f);
414	DE_ASSERT(0.0f <= fBounds.x() && fBounds.x() <= fBounds.y() && fBounds.y() <= 1.0f);
415	DE_ASSERT(prec.pcfBits > 0);
416
417	const CmpResultSet	cmp00		= execCompare(compareMode, depths0[0], cmpReference, prec.referenceBits, isFixedPointDepth);
418	const CmpResultSet	cmp01		= execCompare(compareMode, depths0[1], cmpReference, prec.referenceBits, isFixedPointDepth);
419	const CmpResultSet	cmp02		= execCompare(compareMode, depths0[2], cmpReference, prec.referenceBits, isFixedPointDepth);
420	const CmpResultSet	cmp03		= execCompare(compareMode, depths0[3], cmpReference, prec.referenceBits, isFixedPointDepth);
421
422	const CmpResultSet	cmp10		= execCompare(compareMode, depths1[0], cmpReference, prec.referenceBits, isFixedPointDepth);
423	const CmpResultSet	cmp11		= execCompare(compareMode, depths1[1], cmpReference, prec.referenceBits, isFixedPointDepth);
424	const CmpResultSet	cmp12		= execCompare(compareMode, depths1[2], cmpReference, prec.referenceBits, isFixedPointDepth);
425	const CmpResultSet	cmp13		= execCompare(compareMode, depths1[3], cmpReference, prec.referenceBits, isFixedPointDepth);
426
427	const deUint32		isTrue		= (deUint32(cmp00.isTrue)<<0)
428									| (deUint32(cmp01.isTrue)<<1)
429									| (deUint32(cmp02.isTrue)<<2)
430									| (deUint32(cmp03.isTrue)<<3)
431									| (deUint32(cmp10.isTrue)<<4)
432									| (deUint32(cmp11.isTrue)<<5)
433									| (deUint32(cmp12.isTrue)<<6)
434									| (deUint32(cmp13.isTrue)<<7);
435	const deUint32		isFalse		= (deUint32(cmp00.isFalse)<<0)
436									| (deUint32(cmp01.isFalse)<<1)
437									| (deUint32(cmp02.isFalse)<<2)
438									| (deUint32(cmp03.isFalse)<<3)
439									| (deUint32(cmp10.isFalse)<<4)
440									| (deUint32(cmp11.isFalse)<<5)
441									| (deUint32(cmp12.isFalse)<<6)
442									| (deUint32(cmp13.isFalse)<<7);
443
444	// Error parameters
445	const float			pcfErr		= computeFixedPointError(prec.pcfBits);
446	const float			resErr		= computeFixedPointError(prec.resultBits);
447	const float			totalErr	= pcfErr+resErr;
448
449	// Iterate over all valid combinations.
450	for (deUint32 comb = 0; comb < (1<<8); comb++)
451	{
452		// Filter out invalid combinations.
453		if (((comb & isTrue) | (~comb & isFalse)) != (1<<8)-1)
454			continue;
455
456		const BVec4		cmpTrue0	= extractBVec4(comb, 0);
457		const BVec4		cmpTrue1	= extractBVec4(comb, 4);
458		const Vec4		refVal0		= select(Vec4(1.0f), Vec4(0.0f), cmpTrue0);
459		const Vec4		refVal1		= select(Vec4(1.0f), Vec4(0.0f), cmpTrue1);
460
461		// Bilinear interpolation within levels.
462		const float		v00			= bilinearInterpolate(refVal0, xBounds0.x(), yBounds0.x());
463		const float		v01			= bilinearInterpolate(refVal0, xBounds0.y(), yBounds0.x());
464		const float		v02			= bilinearInterpolate(refVal0, xBounds0.x(), yBounds0.y());
465		const float		v03			= bilinearInterpolate(refVal0, xBounds0.y(), yBounds0.y());
466		const float		minV0		= de::min(v00, de::min(v01, de::min(v02, v03)));
467		const float		maxV0		= de::max(v00, de::max(v01, de::max(v02, v03)));
468
469		const float		v10			= bilinearInterpolate(refVal1, xBounds1.x(), yBounds1.x());
470		const float		v11			= bilinearInterpolate(refVal1, xBounds1.y(), yBounds1.x());
471		const float		v12			= bilinearInterpolate(refVal1, xBounds1.x(), yBounds1.y());
472		const float		v13			= bilinearInterpolate(refVal1, xBounds1.y(), yBounds1.y());
473		const float		minV1		= de::min(v10, de::min(v11, de::min(v12, v13)));
474		const float		maxV1		= de::max(v10, de::max(v11, de::max(v12, v13)));
475
476		// Compute min-max bounds by filtering between minimum bounds and maximum bounds between levels.
477		// HW can end up choosing pretty much any of samples between levels, and thus interpolating
478		// between minimums should yield lower bound for range, and same for upper bound.
479		// \todo [2013-07-17 pyry] This seems separable? Can this be optimized? At least ranges could be pre-computed and later combined.
480		const float		minF0		= minV0*(1.0f-fBounds.x()) + minV1*fBounds.x();
481		const float		minF1		= minV0*(1.0f-fBounds.y()) + minV1*fBounds.y();
482		const float		maxF0		= maxV0*(1.0f-fBounds.x()) + maxV1*fBounds.x();
483		const float		maxF1		= maxV0*(1.0f-fBounds.y()) + maxV1*fBounds.y();
484
485		const float		minF		= de::min(minF0, minF1);
486		const float		maxF		= de::max(maxF0, maxF1);
487
488		const float		minR		= minF-totalErr;
489		const float		maxR		= maxF+totalErr;
490
491		if (de::inRange(result, minR, maxR))
492			return true;
493	}
494
495	return false;
496}
497
498static bool isTrilinearCompareValid (const Sampler::CompareMode	compareMode,
499									 const TexComparePrecision&	prec,
500									 const Vec4&				depths0,
501									 const Vec4&				depths1,
502									 const Vec2&				xBounds0,
503									 const Vec2&				yBounds0,
504									 const Vec2&				xBounds1,
505									 const Vec2&				yBounds1,
506									 const Vec2&				fBounds,
507									 const float				cmpReference,
508									 const float				result,
509									 const bool					isFixedPointDepth)
510{
511	if (prec.pcfBits > 0)
512		return isTrilinearPCFCompareValid(compareMode, prec, depths0, depths1, xBounds0, yBounds0, xBounds1, yBounds1, fBounds, cmpReference, result, isFixedPointDepth);
513	else
514		return isTrilinearAnyCompareValid(compareMode, prec, depths0, depths1, cmpReference, result, isFixedPointDepth);
515}
516
517static bool isNearestCompareResultValid (const ConstPixelBufferAccess&		level,
518										 const Sampler&						sampler,
519										 const TexComparePrecision&			prec,
520										 const Vec2&						coord,
521										 const int							coordZ,
522										 const float						cmpReference,
523										 const float						result)
524{
525	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(level.getFormat());
526	const Vec2	uBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
527	const Vec2	vBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
528
529	// Integer coordinates - without wrap mode
530	const int	minI		= deFloorFloatToInt32(uBounds.x());
531	const int	maxI		= deFloorFloatToInt32(uBounds.y());
532	const int	minJ		= deFloorFloatToInt32(vBounds.x());
533	const int	maxJ		= deFloorFloatToInt32(vBounds.y());
534
535	for (int j = minJ; j <= maxJ; j++)
536	{
537		for (int i = minI; i <= maxI; i++)
538		{
539			const int			x		= wrap(sampler.wrapS, i, level.getWidth());
540			const int			y		= wrap(sampler.wrapT, j, level.getHeight());
541			const float			depth	= level.getPixDepth(x, y, coordZ);
542			const CmpResultSet	resSet	= execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth);
543
544			if (isResultInSet(resSet, result, prec.resultBits))
545				return true;
546		}
547	}
548
549	return false;
550}
551
552static bool isLinearCompareResultValid (const ConstPixelBufferAccess&		level,
553										const Sampler&						sampler,
554										const TexComparePrecision&			prec,
555										const Vec2&							coord,
556										const int							coordZ,
557										const float							cmpReference,
558										const float							result)
559{
560	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(level.getFormat());
561	const Vec2	uBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
562	const Vec2	vBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
563
564	// Integer coordinate bounds for (x0,y0) - without wrap mode
565	const int	minI		= deFloorFloatToInt32(uBounds.x()-0.5f);
566	const int	maxI		= deFloorFloatToInt32(uBounds.y()-0.5f);
567	const int	minJ		= deFloorFloatToInt32(vBounds.x()-0.5f);
568	const int	maxJ		= deFloorFloatToInt32(vBounds.y()-0.5f);
569
570	const int	w			= level.getWidth();
571	const int	h			= level.getHeight();
572
573	// \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
574
575	for (int j = minJ; j <= maxJ; j++)
576	{
577		for (int i = minI; i <= maxI; i++)
578		{
579			// Wrapped coordinates
580			const int	x0		= wrap(sampler.wrapS, i  , w);
581			const int	x1		= wrap(sampler.wrapS, i+1, w);
582			const int	y0		= wrap(sampler.wrapT, j  , h);
583			const int	y1		= wrap(sampler.wrapT, j+1, h);
584
585			// Bounds for filtering factors
586			const float	minA	= de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f);
587			const float	maxA	= de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f);
588			const float	minB	= de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f);
589			const float	maxB	= de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f);
590
591			const Vec4	depths	(level.getPixDepth(x0, y0, coordZ),
592								 level.getPixDepth(x1, y0, coordZ),
593								 level.getPixDepth(x0, y1, coordZ),
594								 level.getPixDepth(x1, y1, coordZ));
595
596			if (isBilinearCompareValid(sampler.compare, prec, depths, Vec2(minA, maxA), Vec2(minB, maxB), cmpReference, result, isFixedPointDepth))
597				return true;
598		}
599	}
600
601	return false;
602}
603
604static bool isLevelCompareResultValid (const ConstPixelBufferAccess&	level,
605									   const Sampler&					sampler,
606									   const Sampler::FilterMode		filterMode,
607									   const TexComparePrecision&		prec,
608									   const Vec2&						coord,
609									   const int						coordZ,
610									   const float						cmpReference,
611									   const float						result)
612{
613	if (filterMode == Sampler::LINEAR)
614		return isLinearCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result);
615	else
616		return isNearestCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result);
617}
618
619static bool isNearestMipmapLinearCompareResultValid (const ConstPixelBufferAccess&	level0,
620													 const ConstPixelBufferAccess&	level1,
621													 const Sampler&					sampler,
622													 const TexComparePrecision&		prec,
623													 const Vec2&					coord,
624													 const int						coordZ,
625													 const Vec2&					fBounds,
626													 const float					cmpReference,
627													 const float					result)
628{
629	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(level0.getFormat());
630
631	const int	w0					= level0.getWidth();
632	const int	w1					= level1.getWidth();
633	const int	h0					= level0.getHeight();
634	const int	h1					= level1.getHeight();
635
636	const Vec2	uBounds0			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
637	const Vec2	uBounds1			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
638	const Vec2	vBounds0			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
639	const Vec2	vBounds1			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
640
641	// Integer coordinates - without wrap mode
642	const int	minI0				= deFloorFloatToInt32(uBounds0.x());
643	const int	maxI0				= deFloorFloatToInt32(uBounds0.y());
644	const int	minI1				= deFloorFloatToInt32(uBounds1.x());
645	const int	maxI1				= deFloorFloatToInt32(uBounds1.y());
646	const int	minJ0				= deFloorFloatToInt32(vBounds0.x());
647	const int	maxJ0				= deFloorFloatToInt32(vBounds0.y());
648	const int	minJ1				= deFloorFloatToInt32(vBounds1.x());
649	const int	maxJ1				= deFloorFloatToInt32(vBounds1.y());
650
651	for (int j0 = minJ0; j0 <= maxJ0; j0++)
652	{
653		for (int i0 = minI0; i0 <= maxI0; i0++)
654		{
655			const float	depth0	= level0.getPixDepth(wrap(sampler.wrapS, i0, w0), wrap(sampler.wrapT, j0, h0), coordZ);
656
657			for (int j1 = minJ1; j1 <= maxJ1; j1++)
658			{
659				for (int i1 = minI1; i1 <= maxI1; i1++)
660				{
661					const float	depth1	= level1.getPixDepth(wrap(sampler.wrapS, i1, w1), wrap(sampler.wrapT, j1, h1), coordZ);
662
663					if (isLinearCompareValid(sampler.compare, prec, Vec2(depth0, depth1), fBounds, cmpReference, result, isFixedPointDepth))
664						return true;
665				}
666			}
667		}
668	}
669
670	return false;
671}
672
673static bool isLinearMipmapLinearCompareResultValid (const ConstPixelBufferAccess&	level0,
674													const ConstPixelBufferAccess&	level1,
675													const Sampler&					sampler,
676													const TexComparePrecision&		prec,
677													const Vec2&						coord,
678													const int						coordZ,
679													const Vec2&						fBounds,
680													const float						cmpReference,
681													const float						result)
682{
683	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(level0.getFormat());
684
685	// \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
686	//						   Right now this allows pairing any two valid bilinear quads.
687
688	const int	w0					= level0.getWidth();
689	const int	w1					= level1.getWidth();
690	const int	h0					= level0.getHeight();
691	const int	h1					= level1.getHeight();
692
693	const Vec2	uBounds0			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
694	const Vec2	uBounds1			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
695	const Vec2	vBounds0			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
696	const Vec2	vBounds1			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
697
698	// Integer coordinates - without wrap mode
699	const int	minI0				= deFloorFloatToInt32(uBounds0.x()-0.5f);
700	const int	maxI0				= deFloorFloatToInt32(uBounds0.y()-0.5f);
701	const int	minI1				= deFloorFloatToInt32(uBounds1.x()-0.5f);
702	const int	maxI1				= deFloorFloatToInt32(uBounds1.y()-0.5f);
703	const int	minJ0				= deFloorFloatToInt32(vBounds0.x()-0.5f);
704	const int	maxJ0				= deFloorFloatToInt32(vBounds0.y()-0.5f);
705	const int	minJ1				= deFloorFloatToInt32(vBounds1.x()-0.5f);
706	const int	maxJ1				= deFloorFloatToInt32(vBounds1.y()-0.5f);
707
708	for (int j0 = minJ0; j0 <= maxJ0; j0++)
709	{
710		for (int i0 = minI0; i0 <= maxI0; i0++)
711		{
712			const float	minA0	= de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f);
713			const float	maxA0	= de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f);
714			const float	minB0	= de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f);
715			const float	maxB0	= de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f);
716			Vec4		depths0;
717
718			{
719				const int	x0		= wrap(sampler.wrapS, i0  , w0);
720				const int	x1		= wrap(sampler.wrapS, i0+1, w0);
721				const int	y0		= wrap(sampler.wrapT, j0  , h0);
722				const int	y1		= wrap(sampler.wrapT, j0+1, h0);
723
724				depths0[0] = level0.getPixDepth(x0, y0, coordZ);
725				depths0[1] = level0.getPixDepth(x1, y0, coordZ);
726				depths0[2] = level0.getPixDepth(x0, y1, coordZ);
727				depths0[3] = level0.getPixDepth(x1, y1, coordZ);
728			}
729
730			for (int j1 = minJ1; j1 <= maxJ1; j1++)
731			{
732				for (int i1 = minI1; i1 <= maxI1; i1++)
733				{
734					const float	minA1	= de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f);
735					const float	maxA1	= de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f);
736					const float	minB1	= de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f);
737					const float	maxB1	= de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f);
738					Vec4		depths1;
739
740					{
741						const int	x0		= wrap(sampler.wrapS, i1  , w1);
742						const int	x1		= wrap(sampler.wrapS, i1+1, w1);
743						const int	y0		= wrap(sampler.wrapT, j1  , h1);
744						const int	y1		= wrap(sampler.wrapT, j1+1, h1);
745
746						depths1[0] = level1.getPixDepth(x0, y0, coordZ);
747						depths1[1] = level1.getPixDepth(x1, y0, coordZ);
748						depths1[2] = level1.getPixDepth(x0, y1, coordZ);
749						depths1[3] = level1.getPixDepth(x1, y1, coordZ);
750					}
751
752					if (isTrilinearCompareValid(sampler.compare, prec, depths0, depths1,
753												Vec2(minA0, maxA0), Vec2(minB0, maxB0),
754												Vec2(minA1, maxA1), Vec2(minB1, maxB1),
755												fBounds, cmpReference, result, isFixedPointDepth))
756						return true;
757				}
758			}
759		}
760	}
761
762	return false;
763}
764
765static bool isMipmapLinearCompareResultValid (const ConstPixelBufferAccess&		level0,
766											  const ConstPixelBufferAccess&		level1,
767											  const Sampler&					sampler,
768											  const Sampler::FilterMode			levelFilter,
769											  const TexComparePrecision&		prec,
770											  const Vec2&						coord,
771											  const int							coordZ,
772											  const Vec2&						fBounds,
773											  const float						cmpReference,
774											  const float						result)
775{
776	if (levelFilter == Sampler::LINEAR)
777		return isLinearMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, cmpReference, result);
778	else
779		return isNearestMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, cmpReference, result);
780}
781
782bool isTexCompareResultValid (const Texture2DView&			texture,
783							  const Sampler&				sampler,
784							  const TexComparePrecision&	prec,
785							  const Vec2&					coord,
786							  const Vec2&					lodBounds,
787							  const float					cmpReference,
788							  const float					result)
789{
790	const float		minLod			= lodBounds.x();
791	const float		maxLod			= lodBounds.y();
792	const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
793	const bool		canBeMinified	= maxLod > sampler.lodThreshold;
794
795	DE_ASSERT(isSamplerSupported(sampler));
796
797	if (canBeMagnified)
798	{
799		if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, cmpReference, result))
800			return true;
801	}
802
803	if (canBeMinified)
804	{
805		const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
806		const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
807		const int	minTexLevel		= 0;
808		const int	maxTexLevel		= texture.getNumLevels()-1;
809
810		DE_ASSERT(minTexLevel < maxTexLevel);
811
812		if (isLinearMipmap)
813		{
814			const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
815			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
816
817			DE_ASSERT(minLevel <= maxLevel);
818
819			for (int level = minLevel; level <= maxLevel; level++)
820			{
821				const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
822				const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
823
824				if (isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, Vec2(minF, maxF), cmpReference, result))
825					return true;
826			}
827		}
828		else if (isNearestMipmap)
829		{
830			// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
831			//		 decision to allow floor(lod + 0.5) as well.
832			const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
833			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
834
835			DE_ASSERT(minLevel <= maxLevel);
836
837			for (int level = minLevel; level <= maxLevel; level++)
838			{
839				if (isLevelCompareResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, cmpReference, result))
840					return true;
841			}
842		}
843		else
844		{
845			if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, cmpReference, result))
846				return true;
847		}
848	}
849
850	return false;
851}
852
853static bool isSeamplessLinearMipmapLinearCompareResultValid (const TextureCubeView&			texture,
854															 const int						baseLevelNdx,
855															 const Sampler&					sampler,
856															 const TexComparePrecision&		prec,
857															 const CubeFaceFloatCoords&		coords,
858															 const Vec2&					fBounds,
859															 const float					cmpReference,
860															 const float					result)
861{
862	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(texture.getLevelFace(baseLevelNdx, CUBEFACE_NEGATIVE_X).getFormat());
863	const int	size0				= texture.getLevelFace(baseLevelNdx,	coords.face).getWidth();
864	const int	size1				= texture.getLevelFace(baseLevelNdx+1,	coords.face).getWidth();
865
866	const Vec2	uBounds0			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0,	coords.s, prec.coordBits.x(), prec.uvwBits.x());
867	const Vec2	uBounds1			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1,	coords.s, prec.coordBits.x(), prec.uvwBits.x());
868	const Vec2	vBounds0			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0,	coords.t, prec.coordBits.y(), prec.uvwBits.y());
869	const Vec2	vBounds1			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1,	coords.t, prec.coordBits.y(), prec.uvwBits.y());
870
871	// Integer coordinates - without wrap mode
872	const int	minI0				= deFloorFloatToInt32(uBounds0.x()-0.5f);
873	const int	maxI0				= deFloorFloatToInt32(uBounds0.y()-0.5f);
874	const int	minI1				= deFloorFloatToInt32(uBounds1.x()-0.5f);
875	const int	maxI1				= deFloorFloatToInt32(uBounds1.y()-0.5f);
876	const int	minJ0				= deFloorFloatToInt32(vBounds0.x()-0.5f);
877	const int	maxJ0				= deFloorFloatToInt32(vBounds0.y()-0.5f);
878	const int	minJ1				= deFloorFloatToInt32(vBounds1.x()-0.5f);
879	const int	maxJ1				= deFloorFloatToInt32(vBounds1.y()-0.5f);
880
881	tcu::ConstPixelBufferAccess faces0[CUBEFACE_LAST];
882	tcu::ConstPixelBufferAccess faces1[CUBEFACE_LAST];
883
884	for (int face = 0; face < CUBEFACE_LAST; face++)
885	{
886		faces0[face] = texture.getLevelFace(baseLevelNdx,	CubeFace(face));
887		faces1[face] = texture.getLevelFace(baseLevelNdx+1,	CubeFace(face));
888	}
889
890	for (int j0 = minJ0; j0 <= maxJ0; j0++)
891	{
892		for (int i0 = minI0; i0 <= maxI0; i0++)
893		{
894			const float	minA0	= de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f);
895			const float	maxA0	= de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f);
896			const float	minB0	= de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f);
897			const float	maxB0	= de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f);
898			Vec4		depths0;
899
900			{
901				const CubeFaceIntCoords	c00	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+0, j0+0)), size0);
902				const CubeFaceIntCoords	c10	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+1, j0+0)), size0);
903				const CubeFaceIntCoords	c01	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+0, j0+1)), size0);
904				const CubeFaceIntCoords	c11	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+1, j0+1)), size0);
905
906				// If any of samples is out of both edges, implementations can do pretty much anything according to spec.
907				// \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color.
908				if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST)
909					return true;
910
911				depths0[0] = faces0[c00.face].getPixDepth(c00.s, c00.t);
912				depths0[1] = faces0[c10.face].getPixDepth(c10.s, c10.t);
913				depths0[2] = faces0[c01.face].getPixDepth(c01.s, c01.t);
914				depths0[3] = faces0[c11.face].getPixDepth(c11.s, c11.t);
915			}
916
917			for (int j1 = minJ1; j1 <= maxJ1; j1++)
918			{
919				for (int i1 = minI1; i1 <= maxI1; i1++)
920				{
921					const float	minA1	= de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f);
922					const float	maxA1	= de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f);
923					const float	minB1	= de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f);
924					const float	maxB1	= de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f);
925					Vec4		depths1;
926
927					{
928						const CubeFaceIntCoords	c00	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+0, j1+0)), size1);
929						const CubeFaceIntCoords	c10	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+1, j1+0)), size1);
930						const CubeFaceIntCoords	c01	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+0, j1+1)), size1);
931						const CubeFaceIntCoords	c11	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+1, j1+1)), size1);
932
933						if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST)
934							return true;
935
936						depths1[0] = faces1[c00.face].getPixDepth(c00.s, c00.t);
937						depths1[1] = faces1[c10.face].getPixDepth(c10.s, c10.t);
938						depths1[2] = faces1[c01.face].getPixDepth(c01.s, c01.t);
939						depths1[3] = faces1[c11.face].getPixDepth(c11.s, c11.t);
940					}
941
942
943					if (isTrilinearCompareValid(sampler.compare, prec, depths0, depths1,
944												Vec2(minA0, maxA0), Vec2(minB0, maxB0),
945												Vec2(minA1, maxA1), Vec2(minB1, maxB1),
946												fBounds, cmpReference, result, isFixedPointDepth))
947						return true;
948				}
949			}
950		}
951	}
952
953	return false;
954}
955
956static bool isCubeMipmapLinearCompareResultValid (const TextureCubeView&		texture,
957												  const int						baseLevelNdx,
958												  const Sampler&				sampler,
959												  const Sampler::FilterMode		levelFilter,
960												  const TexComparePrecision&	prec,
961												  const CubeFaceFloatCoords&	coords,
962												  const Vec2&					fBounds,
963												  const float					cmpReference,
964												  const float					result)
965{
966	if (levelFilter == Sampler::LINEAR)
967	{
968		if (sampler.seamlessCubeMap)
969			return isSeamplessLinearMipmapLinearCompareResultValid(texture, baseLevelNdx, sampler, prec, coords, fBounds, cmpReference, result);
970		else
971			return isLinearMipmapLinearCompareResultValid(texture.getLevelFace(baseLevelNdx,	coords.face),
972														  texture.getLevelFace(baseLevelNdx+1,	coords.face),
973														  sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, cmpReference, result);
974	}
975	else
976		return isNearestMipmapLinearCompareResultValid(texture.getLevelFace(baseLevelNdx,	coords.face),
977													   texture.getLevelFace(baseLevelNdx+1,	coords.face),
978													   sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, cmpReference, result);
979}
980
981static bool isSeamlessLinearCompareResultValid (const TextureCubeView&		texture,
982												const int					levelNdx,
983												const Sampler&				sampler,
984												const TexComparePrecision&	prec,
985												const CubeFaceFloatCoords&	coords,
986												const float					cmpReference,
987												const float					result)
988{
989	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(texture.getLevelFace(levelNdx, CUBEFACE_NEGATIVE_X).getFormat());
990	const int	size				= texture.getLevelFace(levelNdx, coords.face).getWidth();
991
992	const Vec2	uBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x());
993	const Vec2	vBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y());
994
995	// Integer coordinate bounds for (x0,y0) - without wrap mode
996	const int	minI				= deFloorFloatToInt32(uBounds.x()-0.5f);
997	const int	maxI				= deFloorFloatToInt32(uBounds.y()-0.5f);
998	const int	minJ				= deFloorFloatToInt32(vBounds.x()-0.5f);
999	const int	maxJ				= deFloorFloatToInt32(vBounds.y()-0.5f);
1000
1001	// Face accesses
1002	ConstPixelBufferAccess faces[CUBEFACE_LAST];
1003	for (int face = 0; face < CUBEFACE_LAST; face++)
1004		faces[face] = texture.getLevelFace(levelNdx, CubeFace(face));
1005
1006	for (int j = minJ; j <= maxJ; j++)
1007	{
1008		for (int i = minI; i <= maxI; i++)
1009		{
1010			const CubeFaceIntCoords	c00	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+0, j+0)), size);
1011			const CubeFaceIntCoords	c10	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+1, j+0)), size);
1012			const CubeFaceIntCoords	c01	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+0, j+1)), size);
1013			const CubeFaceIntCoords	c11	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+1, j+1)), size);
1014
1015			// If any of samples is out of both edges, implementations can do pretty much anything according to spec.
1016			// \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color.
1017			if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST)
1018				return true;
1019
1020			// Bounds for filtering factors
1021			const float	minA	= de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f);
1022			const float	maxA	= de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f);
1023			const float	minB	= de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f);
1024			const float	maxB	= de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f);
1025
1026			Vec4 depths;
1027			depths[0] = faces[c00.face].getPixDepth(c00.s, c00.t);
1028			depths[1] = faces[c10.face].getPixDepth(c10.s, c10.t);
1029			depths[2] = faces[c01.face].getPixDepth(c01.s, c01.t);
1030			depths[3] = faces[c11.face].getPixDepth(c11.s, c11.t);
1031
1032			if (isBilinearCompareValid(sampler.compare, prec, depths, Vec2(minA, maxA), Vec2(minB, maxB), cmpReference, result, isFixedPointDepth))
1033				return true;
1034		}
1035	}
1036
1037	return false;
1038}
1039
1040static bool isCubeLevelCompareResultValid (const TextureCubeView&			texture,
1041										   const int						levelNdx,
1042										   const Sampler&					sampler,
1043										   const Sampler::FilterMode		filterMode,
1044										   const TexComparePrecision&		prec,
1045										   const CubeFaceFloatCoords&		coords,
1046										   const float						cmpReference,
1047										   const float						result)
1048{
1049	if (filterMode == Sampler::LINEAR)
1050	{
1051		if (sampler.seamlessCubeMap)
1052			return isSeamlessLinearCompareResultValid(texture, levelNdx, sampler, prec, coords, cmpReference, result);
1053		else
1054			return isLinearCompareResultValid(texture.getLevelFace(levelNdx, coords.face), sampler, prec, Vec2(coords.s, coords.t), 0, cmpReference, result);
1055	}
1056	else
1057		return isNearestCompareResultValid(texture.getLevelFace(levelNdx, coords.face), sampler, prec, Vec2(coords.s, coords.t), 0, cmpReference, result);
1058}
1059
1060bool isTexCompareResultValid (const TextureCubeView& texture, const Sampler& sampler, const TexComparePrecision& prec, const Vec3& coord, const Vec2& lodBounds, const float cmpReference, const float result)
1061{
1062	int			numPossibleFaces				= 0;
1063	CubeFace	possibleFaces[CUBEFACE_LAST];
1064
1065	DE_ASSERT(isSamplerSupported(sampler));
1066
1067	getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
1068
1069	if (numPossibleFaces == 0)
1070		return true; // Result is undefined.
1071
1072	for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
1073	{
1074		const CubeFaceFloatCoords	faceCoords		(possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], coord));
1075		const float					minLod			= lodBounds.x();
1076		const float					maxLod			= lodBounds.y();
1077		const bool					canBeMagnified	= minLod <= sampler.lodThreshold;
1078		const bool					canBeMinified	= maxLod > sampler.lodThreshold;
1079
1080		if (canBeMagnified)
1081		{
1082			if (isCubeLevelCompareResultValid(texture, 0, sampler, sampler.magFilter, prec, faceCoords, cmpReference, result))
1083				return true;
1084		}
1085
1086		if (canBeMinified)
1087		{
1088			const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
1089			const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
1090			const int	minTexLevel		= 0;
1091			const int	maxTexLevel		= texture.getNumLevels()-1;
1092
1093			DE_ASSERT(minTexLevel < maxTexLevel);
1094
1095			if (isLinearMipmap)
1096			{
1097				const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
1098				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
1099
1100				DE_ASSERT(minLevel <= maxLevel);
1101
1102				for (int level = minLevel; level <= maxLevel; level++)
1103				{
1104					const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
1105					const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
1106
1107					if (isCubeMipmapLinearCompareResultValid(texture, level, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, Vec2(minF, maxF), cmpReference, result))
1108						return true;
1109				}
1110			}
1111			else if (isNearestMipmap)
1112			{
1113				// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
1114				//		 decision to allow floor(lod + 0.5) as well.
1115				const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
1116				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
1117
1118				DE_ASSERT(minLevel <= maxLevel);
1119
1120				for (int level = minLevel; level <= maxLevel; level++)
1121				{
1122					if (isCubeLevelCompareResultValid(texture, level, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, cmpReference, result))
1123						return true;
1124				}
1125			}
1126			else
1127			{
1128				if (isCubeLevelCompareResultValid(texture, 0, sampler, sampler.minFilter, prec, faceCoords, cmpReference, result))
1129					return true;
1130			}
1131		}
1132	}
1133
1134	return false;
1135}
1136
1137bool isTexCompareResultValid (const Texture2DArrayView& texture, const Sampler& sampler, const TexComparePrecision& prec, const Vec3& coord, const Vec2& lodBounds, const float cmpReference, const float result)
1138{
1139	const float		depthErr	= computeFloatingPointError(coord.z(), prec.coordBits.z()) + computeFixedPointError(prec.uvwBits.z());
1140	const float		minZ		= coord.z()-depthErr;
1141	const float		maxZ		= coord.z()+depthErr;
1142	const int		minLayer	= de::clamp(deFloorFloatToInt32(minZ + 0.5f), 0, texture.getNumLayers()-1);
1143	const int		maxLayer	= de::clamp(deFloorFloatToInt32(maxZ + 0.5f), 0, texture.getNumLayers()-1);
1144
1145	for (int layer = minLayer; layer <= maxLayer; layer++)
1146	{
1147		const float		minLod			= lodBounds.x();
1148		const float		maxLod			= lodBounds.y();
1149		const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
1150		const bool		canBeMinified	= maxLod > sampler.lodThreshold;
1151
1152		DE_ASSERT(isSamplerSupported(sampler));
1153
1154		if (canBeMagnified)
1155		{
1156			if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord.swizzle(0,1), layer, cmpReference, result))
1157				return true;
1158		}
1159
1160		if (canBeMinified)
1161		{
1162			const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
1163			const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
1164			const int	minTexLevel		= 0;
1165			const int	maxTexLevel		= texture.getNumLevels()-1;
1166
1167			DE_ASSERT(minTexLevel < maxTexLevel);
1168
1169			if (isLinearMipmap)
1170			{
1171				const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
1172				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
1173
1174				DE_ASSERT(minLevel <= maxLevel);
1175
1176				for (int level = minLevel; level <= maxLevel; level++)
1177				{
1178					const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
1179					const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
1180
1181					if (isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord.swizzle(0,1), layer, Vec2(minF, maxF), cmpReference, result))
1182						return true;
1183				}
1184			}
1185			else if (isNearestMipmap)
1186			{
1187				// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
1188				//		 decision to allow floor(lod + 0.5) as well.
1189				const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
1190				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
1191
1192				DE_ASSERT(minLevel <= maxLevel);
1193
1194				for (int level = minLevel; level <= maxLevel; level++)
1195				{
1196					if (isLevelCompareResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord.swizzle(0,1), layer, cmpReference, result))
1197						return true;
1198				}
1199			}
1200			else
1201			{
1202				if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord.swizzle(0,1), layer, cmpReference, result))
1203					return true;
1204			}
1205		}
1206	}
1207
1208	return false;
1209}
1210
1211static bool isGatherOffsetsCompareResultValid (const ConstPixelBufferAccess&	texture,
1212											   const Sampler&					sampler,
1213											   const TexComparePrecision&		prec,
1214											   const Vec2&						coord,
1215											   int								coordZ,
1216											   const IVec2						(&offsets)[4],
1217											   float							cmpReference,
1218											   const Vec4&						result)
1219{
1220	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(texture.getFormat());
1221	const Vec2	uBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, texture.getWidth(),		coord.x(), prec.coordBits.x(), prec.uvwBits.x());
1222	const Vec2	vBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, texture.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
1223
1224	// Integer coordinate bounds for (x0, y0) - without wrap mode
1225	const int	minI				= deFloorFloatToInt32(uBounds.x()-0.5f);
1226	const int	maxI				= deFloorFloatToInt32(uBounds.y()-0.5f);
1227	const int	minJ				= deFloorFloatToInt32(vBounds.x()-0.5f);
1228	const int	maxJ				= deFloorFloatToInt32(vBounds.y()-0.5f);
1229
1230	const int	w					= texture.getWidth();
1231	const int	h					= texture.getHeight();
1232
1233	for (int j = minJ; j <= maxJ; j++)
1234	{
1235		for (int i = minI; i <= maxI; i++)
1236		{
1237			bool isCurrentPixelValid = true;
1238
1239			for (int offNdx = 0; offNdx < 4 && isCurrentPixelValid; offNdx++)
1240			{
1241				// offNdx-th coordinate offset and then wrapped.
1242				const int			x		= wrap(sampler.wrapS, i+offsets[offNdx].x(), w);
1243				const int			y		= wrap(sampler.wrapT, j+offsets[offNdx].y(), h);
1244				const float			depth	= texture.getPixDepth(x, y, coordZ);
1245				const CmpResultSet	resSet	= execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth);
1246
1247				if (!isResultInSet(resSet, result[offNdx], prec.resultBits))
1248					isCurrentPixelValid = false;
1249			}
1250
1251			if (isCurrentPixelValid)
1252				return true;
1253		}
1254	}
1255
1256	return false;
1257}
1258
1259bool isGatherOffsetsCompareResultValid (const Texture2DView&		texture,
1260										const Sampler&				sampler,
1261										const TexComparePrecision&	prec,
1262										const Vec2&					coord,
1263										const IVec2					(&offsets)[4],
1264										float						cmpReference,
1265										const Vec4&					result)
1266{
1267	return isGatherOffsetsCompareResultValid(texture.getLevel(0), sampler, prec, coord, 0, offsets, cmpReference, result);
1268}
1269
1270bool isGatherOffsetsCompareResultValid (const Texture2DArrayView&	texture,
1271										const Sampler&				sampler,
1272										const TexComparePrecision&	prec,
1273										const Vec3&					coord,
1274										const IVec2					(&offsets)[4],
1275										float						cmpReference,
1276										const Vec4&					result)
1277{
1278	const float		depthErr	= computeFloatingPointError(coord.z(), prec.coordBits.z()) + computeFixedPointError(prec.uvwBits.z());
1279	const float		minZ		= coord.z()-depthErr;
1280	const float		maxZ		= coord.z()+depthErr;
1281	const int		minLayer	= de::clamp(deFloorFloatToInt32(minZ + 0.5f), 0, texture.getNumLayers()-1);
1282	const int		maxLayer	= de::clamp(deFloorFloatToInt32(maxZ + 0.5f), 0, texture.getNumLayers()-1);
1283
1284	for (int layer = minLayer; layer <= maxLayer; layer++)
1285	{
1286		if (isGatherOffsetsCompareResultValid(texture.getLevel(0), sampler, prec, coord.swizzle(0,1), layer, offsets, cmpReference, result))
1287			return true;
1288	}
1289	return false;
1290}
1291
1292static bool isGatherCompareResultValid (const TextureCubeView&		texture,
1293										const Sampler&				sampler,
1294										const TexComparePrecision&	prec,
1295										const CubeFaceFloatCoords&	coords,
1296										float						cmpReference,
1297										const Vec4&					result)
1298{
1299	const bool	isFixedPointDepth	= isFixedPointDepthTextureFormat(texture.getLevelFace(0, coords.face).getFormat());
1300	const int	size				= texture.getLevelFace(0, coords.face).getWidth();
1301	const Vec2	uBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x());
1302	const Vec2	vBounds				= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y());
1303
1304	// Integer coordinate bounds for (x0,y0) - without wrap mode
1305	const int	minI				= deFloorFloatToInt32(uBounds.x()-0.5f);
1306	const int	maxI				= deFloorFloatToInt32(uBounds.y()-0.5f);
1307	const int	minJ				= deFloorFloatToInt32(vBounds.x()-0.5f);
1308	const int	maxJ				= deFloorFloatToInt32(vBounds.y()-0.5f);
1309
1310	// Face accesses
1311	ConstPixelBufferAccess faces[CUBEFACE_LAST];
1312	for (int face = 0; face < CUBEFACE_LAST; face++)
1313		faces[face] = texture.getLevelFace(0, CubeFace(face));
1314
1315	for (int j = minJ; j <= maxJ; j++)
1316	{
1317		for (int i = minI; i <= maxI; i++)
1318		{
1319			static const IVec2 offsets[4] =
1320			{
1321				IVec2(0, 1),
1322				IVec2(1, 1),
1323				IVec2(1, 0),
1324				IVec2(0, 0)
1325			};
1326
1327			bool isCurrentPixelValid = true;
1328
1329			for (int offNdx = 0; offNdx < 4 && isCurrentPixelValid; offNdx++)
1330			{
1331				const CubeFaceIntCoords c = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, i+offsets[offNdx].x(), j+offsets[offNdx].y()), size);
1332				// If any of samples is out of both edges, implementations can do pretty much anything according to spec.
1333				// \todo [2014-06-05 nuutti] Test the special case where all corner pixels have exactly the same color.
1334				//							 See also isSeamlessLinearCompareResultValid and similar.
1335				if (c.face == CUBEFACE_LAST)
1336					return true;
1337
1338				const float			depth	= faces[c.face].getPixDepth(c.s, c.t);
1339				const CmpResultSet	resSet	= execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth);
1340
1341				if (!isResultInSet(resSet, result[offNdx], prec.resultBits))
1342					isCurrentPixelValid = false;
1343			}
1344
1345			if (isCurrentPixelValid)
1346				return true;
1347		}
1348	}
1349
1350	return false;
1351}
1352
1353bool isGatherCompareResultValid (const TextureCubeView&			texture,
1354								 const Sampler&					sampler,
1355								 const TexComparePrecision&		prec,
1356								 const Vec3&					coord,
1357								 float							cmpReference,
1358								 const Vec4&					result)
1359{
1360	int			numPossibleFaces				= 0;
1361	CubeFace	possibleFaces[CUBEFACE_LAST];
1362
1363	getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
1364
1365	if (numPossibleFaces == 0)
1366		return true; // Result is undefined.
1367
1368	for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
1369	{
1370		const CubeFaceFloatCoords faceCoords(possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], coord));
1371
1372		if (isGatherCompareResultValid(texture, sampler, prec, faceCoords, cmpReference, result))
1373			return true;
1374	}
1375
1376	return false;
1377}
1378
1379} // tcu
1380