Home | History | Annotate | Download | only in common
      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 
     30 namespace tcu
     31 {
     32 
     33 using namespace TexVerifierUtil;
     34 
     35 // Generic utilities
     36 
     37 #if defined(DE_DEBUG)
     38 static 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 
     47 struct CmpResultSet
     48 {
     49 	bool	isTrue;
     50 	bool	isFalse;
     51 
     52 	CmpResultSet (void)
     53 		: isTrue	(false)
     54 		, isFalse	(false)
     55 	{
     56 	}
     57 };
     58 
     59 static 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 
    119 static 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)
    130 static 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 
    140 static 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 
    169 static 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 
    226 static 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 
    234 static 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 
    264 static 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 
    337 static 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 
    352 static 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 
    397 static 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 
    498 static 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 
    517 static 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 
    552 static 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 
    604 static 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 
    619 static 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 
    673 static 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 
    765 static 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 
    782 bool 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 
    853 static 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 
    956 static 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 
    981 static 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 
   1040 static 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 
   1060 bool 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 
   1137 bool 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 
   1211 static 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 
   1259 bool 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 
   1270 bool 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 
   1292 static 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 
   1353 bool 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