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