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 lookup simulator that is capable of verifying generic
     22  *		  lookup results based on accuracy parameters.
     23  *//*--------------------------------------------------------------------*/
     24 
     25 #include "tcuTexLookupVerifier.hpp"
     26 #include "tcuTexVerifierUtil.hpp"
     27 #include "tcuVectorUtil.hpp"
     28 #include "tcuTextureUtil.hpp"
     29 #include "deMath.h"
     30 
     31 namespace tcu
     32 {
     33 
     34 using namespace TexVerifierUtil;
     35 
     36 // Generic utilities
     37 
     38 #if defined(DE_DEBUG)
     39 static bool isSamplerSupported (const Sampler& sampler)
     40 {
     41 	return sampler.compare == Sampler::COMPAREMODE_NONE &&
     42 		   isWrapModeSupported(sampler.wrapS)			&&
     43 		   isWrapModeSupported(sampler.wrapT)			&&
     44 		   isWrapModeSupported(sampler.wrapR);
     45 }
     46 #endif // DE_DEBUG
     47 
     48 // Color read & compare utilities
     49 
     50 static inline bool coordsInBounds (const ConstPixelBufferAccess& access, int x, int y, int z)
     51 {
     52 	return de::inBounds(x, 0, access.getWidth()) && de::inBounds(y, 0, access.getHeight()) && de::inBounds(z, 0, access.getDepth());
     53 }
     54 
     55 static inline bool isSRGB (TextureFormat format)
     56 {
     57 	return format.order == TextureFormat::sRGB || format.order == TextureFormat::sRGBA;
     58 }
     59 
     60 template<typename ScalarType>
     61 inline Vector<ScalarType, 4> lookup (const ConstPixelBufferAccess& access, const Sampler& sampler, int i, int j, int k)
     62 {
     63 	if (coordsInBounds(access, i, j, k))
     64 		return access.getPixelT<ScalarType>(i, j, k);
     65 	else
     66 		return sampler.borderColor.cast<ScalarType>();
     67 }
     68 
     69 template<>
     70 inline Vector<float, 4> lookup (const ConstPixelBufferAccess& access, const Sampler& sampler, int i, int j, int k)
     71 {
     72 	// Specialization for float lookups: sRGB conversion is performed as specified in format.
     73 	if (coordsInBounds(access, i, j, k))
     74 	{
     75 		const Vec4 p = access.getPixel(i, j, k);
     76 		return isSRGB(access.getFormat()) ? sRGBToLinear(p) : p;
     77 	}
     78 	else
     79 		return sampler.borderColor;
     80 }
     81 
     82 static inline bool isColorValid (const LookupPrecision& prec, const Vec4& ref, const Vec4& result)
     83 {
     84 	const Vec4 diff = abs(ref - result);
     85 	return boolAll(logicalOr(lessThanEqual(diff, prec.colorThreshold), logicalNot(prec.colorMask)));
     86 }
     87 
     88 static inline bool isColorValid (const IntLookupPrecision& prec, const IVec4& ref, const IVec4& result)
     89 {
     90 	return boolAll(logicalOr(lessThanEqual(absDiff(ref, result).asUint(), prec.colorThreshold), logicalNot(prec.colorMask)));
     91 }
     92 
     93 static inline bool isColorValid (const IntLookupPrecision& prec, const UVec4& ref, const UVec4& result)
     94 {
     95 	return boolAll(logicalOr(lessThanEqual(absDiff(ref, result), prec.colorThreshold), logicalNot(prec.colorMask)));
     96 }
     97 
     98 struct ColorQuad
     99 {
    100 	Vec4	p00;		//!< (0, 0)
    101 	Vec4	p01;		//!< (1, 0)
    102 	Vec4	p10;		//!< (0, 1)
    103 	Vec4	p11;		//!< (1, 1)
    104 };
    105 
    106 static void lookupQuad (ColorQuad& dst, const ConstPixelBufferAccess& level, const Sampler& sampler, int x0, int x1, int y0, int y1, int z)
    107 {
    108 	dst.p00	= lookup<float>(level, sampler, x0, y0, z);
    109 	dst.p10	= lookup<float>(level, sampler, x1, y0, z);
    110 	dst.p01	= lookup<float>(level, sampler, x0, y1, z);
    111 	dst.p11	= lookup<float>(level, sampler, x1, y1, z);
    112 }
    113 
    114 struct ColorLine
    115 {
    116 	Vec4	p0;		//!< 0
    117 	Vec4	p1;		//!< 1
    118 };
    119 
    120 static void lookupLine (ColorLine& dst, const ConstPixelBufferAccess& level, const Sampler& sampler, int x0, int x1, int y)
    121 {
    122 	dst.p0 = lookup<float>(level, sampler, x0, y, 0);
    123 	dst.p1 = lookup<float>(level, sampler, x1, y, 0);
    124 }
    125 
    126 template<typename T, int Size>
    127 static T minComp (const Vector<T, Size>& vec)
    128 {
    129 	T minVal = vec[0];
    130 	for (int ndx = 1; ndx < Size; ndx++)
    131 		minVal = de::min(minVal, vec[ndx]);
    132 	return minVal;
    133 }
    134 
    135 template<typename T, int Size>
    136 static T maxComp (const Vector<T, Size>& vec)
    137 {
    138 	T maxVal = vec[0];
    139 	for (int ndx = 1; ndx < Size; ndx++)
    140 		maxVal = de::max(maxVal, vec[ndx]);
    141 	return maxVal;
    142 }
    143 
    144 static float computeBilinearSearchStepFromFloatLine (const LookupPrecision&	prec,
    145 													 const ColorLine&		line)
    146 {
    147 	DE_ASSERT(boolAll(greaterThan(prec.colorThreshold, Vec4(0.0f))));
    148 
    149 	const int		maxSteps	= 1<<16;
    150 	const Vec4		d			= abs(line.p1 - line.p0);
    151 	const Vec4		stepCount	= d / prec.colorThreshold;
    152 	const Vec4		minStep		= 1.0f / (stepCount + 1.0f);
    153 	const float		step		= de::max(minComp(minStep), 1.0f / float(maxSteps));
    154 
    155 	return step;
    156 }
    157 
    158 static float computeBilinearSearchStepFromFloatQuad (const LookupPrecision&	prec,
    159 													 const ColorQuad&		quad)
    160 {
    161 	DE_ASSERT(boolAll(greaterThan(prec.colorThreshold, Vec4(0.0f))));
    162 
    163 	const int		maxSteps	= 1<<16;
    164 	const Vec4		d0			= abs(quad.p10 - quad.p00);
    165 	const Vec4		d1			= abs(quad.p01 - quad.p00);
    166 	const Vec4		d2			= abs(quad.p11 - quad.p10);
    167 	const Vec4		d3			= abs(quad.p11 - quad.p01);
    168 	const Vec4		maxD		= max(d0, max(d1, max(d2, d3)));
    169 	const Vec4		stepCount	= maxD / prec.colorThreshold;
    170 	const Vec4		minStep		= 1.0f / (stepCount + 1.0f);
    171 	const float		step		= de::max(minComp(minStep), 1.0f / float(maxSteps));
    172 
    173 	return step;
    174 }
    175 
    176 static float computeBilinearSearchStepForUnorm (const LookupPrecision& prec)
    177 {
    178 	DE_ASSERT(boolAll(greaterThan(prec.colorThreshold, Vec4(0.0f))));
    179 
    180 	const Vec4		stepCount	= 1.0f / prec.colorThreshold;
    181 	const Vec4		minStep		= 1.0f / (stepCount + 1.0f);
    182 	const float		step		= minComp(minStep);
    183 
    184 	return step;
    185 }
    186 
    187 static float computeBilinearSearchStepForSnorm (const LookupPrecision& prec)
    188 {
    189 	DE_ASSERT(boolAll(greaterThan(prec.colorThreshold, Vec4(0.0f))));
    190 
    191 	const Vec4		stepCount	= 2.0f / prec.colorThreshold;
    192 	const Vec4		minStep		= 1.0f / (stepCount + 1.0f);
    193 	const float		step		= minComp(minStep);
    194 
    195 	return step;
    196 }
    197 
    198 static inline Vec4 min (const ColorLine& line)
    199 {
    200 	return min(line.p0, line.p1);
    201 }
    202 
    203 static inline Vec4 max (const ColorLine& line)
    204 {
    205 	return max(line.p0, line.p1);
    206 }
    207 
    208 static inline Vec4 min (const ColorQuad& quad)
    209 {
    210 	return min(quad.p00, min(quad.p10, min(quad.p01, quad.p11)));
    211 }
    212 
    213 static inline Vec4 max (const ColorQuad& quad)
    214 {
    215 	return max(quad.p00, max(quad.p10, max(quad.p01, quad.p11)));
    216 }
    217 
    218 static bool isInColorBounds (const LookupPrecision& prec, const ColorQuad& quad, const Vec4& result)
    219 {
    220 	const tcu::Vec4 minVal = min(quad) - prec.colorThreshold;
    221 	const tcu::Vec4 maxVal = max(quad) + prec.colorThreshold;
    222 	return boolAll(logicalOr(logicalAnd(greaterThanEqual(result, minVal), lessThanEqual(result, maxVal)), logicalNot(prec.colorMask)));
    223 }
    224 
    225 static bool isInColorBounds (const LookupPrecision& prec, const ColorQuad& quad0, const ColorQuad& quad1, const Vec4& result)
    226 {
    227 	const tcu::Vec4 minVal = min(min(quad0), min(quad1)) - prec.colorThreshold;
    228 	const tcu::Vec4 maxVal = max(max(quad0), max(quad1)) + prec.colorThreshold;
    229 	return boolAll(logicalOr(logicalAnd(greaterThanEqual(result, minVal), lessThanEqual(result, maxVal)), logicalNot(prec.colorMask)));
    230 }
    231 
    232 static bool isInColorBounds (const LookupPrecision& prec, const ColorLine& line0, const ColorLine& line1, const Vec4& result)
    233 {
    234 	const tcu::Vec4 minVal = min(min(line0), min(line1)) - prec.colorThreshold;
    235 	const tcu::Vec4 maxVal = max(max(line0), max(line1)) + prec.colorThreshold;
    236 	return boolAll(logicalOr(logicalAnd(greaterThanEqual(result, minVal), lessThanEqual(result, maxVal)), logicalNot(prec.colorMask)));
    237 }
    238 
    239 static bool isInColorBounds (const LookupPrecision&		prec,
    240 							 const ColorQuad&			quad00,
    241 							 const ColorQuad&			quad01,
    242 							 const ColorQuad&			quad10,
    243 							 const ColorQuad&			quad11,
    244 							 const Vec4&				result)
    245 {
    246 	const tcu::Vec4 minVal = min(min(quad00), min(min(quad01), min(min(quad10), min(quad11)))) - prec.colorThreshold;
    247 	const tcu::Vec4 maxVal = max(max(quad00), max(max(quad01), max(max(quad10), max(quad11)))) + prec.colorThreshold;
    248 	return boolAll(logicalOr(logicalAnd(greaterThanEqual(result, minVal), lessThanEqual(result, maxVal)), logicalNot(prec.colorMask)));
    249 }
    250 
    251 // Range search utilities
    252 
    253 static bool isLinearRangeValid (const LookupPrecision&	prec,
    254 								const Vec4&				c0,
    255 								const Vec4&				c1,
    256 								const Vec2&				fBounds,
    257 								const Vec4&				result)
    258 {
    259 	// This is basically line segment - AABB test. Valid interpolation line is checked
    260 	// against result AABB constructed by applying threshold.
    261 
    262 	const Vec4		i0				= c0*(1.0f - fBounds[0]) + c1*fBounds[0];
    263 	const Vec4		i1				= c0*(1.0f - fBounds[1]) + c1*fBounds[1];
    264 	const Vec4		rMin			= result - prec.colorThreshold;
    265 	const Vec4		rMax			= result + prec.colorThreshold;
    266 	bool			allIntersect	= true;
    267 
    268 	// Algorithm: For each component check whether segment endpoints are inside, or intersect with slab.
    269 	// If all intersect or are inside, line segment intersects the whole 4D AABB.
    270 	for (int compNdx = 0; compNdx < 4; compNdx++)
    271 	{
    272 		if (!prec.colorMask[compNdx])
    273 			continue;
    274 
    275 		// Signs for both bounds: false = left, true = right.
    276 		const bool	sMin0	= i0[compNdx] >= rMin[compNdx];
    277 		const bool	sMin1	= i1[compNdx] >= rMin[compNdx];
    278 		const bool	sMax0	= i0[compNdx] > rMax[compNdx];
    279 		const bool	sMax1	= i1[compNdx] > rMax[compNdx];
    280 
    281 		// If all signs are equal, line segment is outside bounds.
    282 		if (sMin0 == sMin1 && sMin1 == sMax0 && sMax0 == sMax1)
    283 		{
    284 			allIntersect = false;
    285 			break;
    286 		}
    287 	}
    288 
    289 	return allIntersect;
    290 }
    291 
    292 static bool isBilinearRangeValid (const LookupPrecision&	prec,
    293 								  const ColorQuad&			quad,
    294 								  const Vec2&				xBounds,
    295 								  const Vec2&				yBounds,
    296 								  const float				searchStep,
    297 								  const Vec4&				result)
    298 {
    299 	DE_ASSERT(xBounds.x() <= xBounds.y());
    300 	DE_ASSERT(yBounds.x() <= yBounds.y());
    301 
    302 	if (!isInColorBounds(prec, quad, result))
    303 		return false;
    304 
    305 	for (float x = xBounds.x(); x < xBounds.y()+searchStep; x += searchStep)
    306 	{
    307 		const float		a	= de::min(x, xBounds.y());
    308 		const Vec4		c0	= quad.p00*(1.0f - a) + quad.p10*a;
    309 		const Vec4		c1	= quad.p01*(1.0f - a) + quad.p11*a;
    310 
    311 		if (isLinearRangeValid(prec, c0, c1, yBounds, result))
    312 			return true;
    313 	}
    314 
    315 	return false;
    316 }
    317 
    318 static bool isTrilinearRangeValid (const LookupPrecision&	prec,
    319 								   const ColorQuad&			quad0,
    320 								   const ColorQuad&			quad1,
    321 								   const Vec2&				xBounds,
    322 								   const Vec2&				yBounds,
    323 								   const Vec2&				zBounds,
    324 								   const float				searchStep,
    325 								   const Vec4&				result)
    326 {
    327 	DE_ASSERT(xBounds.x() <= xBounds.y());
    328 	DE_ASSERT(yBounds.x() <= yBounds.y());
    329 	DE_ASSERT(zBounds.x() <= zBounds.y());
    330 
    331 	if (!isInColorBounds(prec, quad0, quad1, result))
    332 		return false;
    333 
    334 	for (float x = xBounds.x(); x < xBounds.y()+searchStep; x += searchStep)
    335 	{
    336 		for (float y = yBounds.x(); y < yBounds.y()+searchStep; y += searchStep)
    337 		{
    338 			const float		a	= de::min(x, xBounds.y());
    339 			const float		b	= de::min(y, yBounds.y());
    340 			const Vec4		c0	= quad0.p00*(1.0f-a)*(1.0f-b) + quad0.p10*a*(1.0f-b) + quad0.p01*(1.0f-a)*b + quad0.p11*a*b;
    341 			const Vec4		c1	= quad1.p00*(1.0f-a)*(1.0f-b) + quad1.p10*a*(1.0f-b) + quad1.p01*(1.0f-a)*b + quad1.p11*a*b;
    342 
    343 			if (isLinearRangeValid(prec, c0, c1, zBounds, result))
    344 				return true;
    345 		}
    346 	}
    347 
    348 	return false;
    349 }
    350 
    351 static bool is1DTrilinearFilterResultValid (const LookupPrecision&	prec,
    352 											const ColorLine&		line0,
    353 											const ColorLine&		line1,
    354 											const Vec2&				xBounds0,
    355 											const Vec2&				xBounds1,
    356 											const Vec2&				zBounds,
    357 											const float				searchStep,
    358 											const Vec4&				result)
    359 {
    360 	DE_ASSERT(xBounds0.x() <= xBounds0.y());
    361 	DE_ASSERT(xBounds1.x() <= xBounds1.y());
    362 
    363 	if (!isInColorBounds(prec, line0, line1, result))
    364 		return false;
    365 
    366 	for (float x0 = xBounds0.x(); x0 < xBounds0.y()+searchStep; x0 += searchStep)
    367 	{
    368 		const float		a0	= de::min(x0, xBounds0.y());
    369 		const Vec4		c0	= line0.p0*(1.0f-a0) + line0.p1*a0;
    370 
    371 		for (float x1 = xBounds1.x(); x1 <= xBounds1.y(); x1 += searchStep)
    372 		{
    373 			const float		a1	= de::min(x1, xBounds1.y());
    374 			const Vec4		c1	= line1.p0*(1.0f-a1) + line1.p1*a1;
    375 
    376 			if (isLinearRangeValid(prec, c0, c1, zBounds, result))
    377 				return true;
    378 		}
    379 	}
    380 
    381 	return false;
    382 }
    383 
    384 static bool is2DTrilinearFilterResultValid (const LookupPrecision&	prec,
    385 											const ColorQuad&		quad0,
    386 											const ColorQuad&		quad1,
    387 											const Vec2&				xBounds0,
    388 											const Vec2&				yBounds0,
    389 											const Vec2&				xBounds1,
    390 											const Vec2&				yBounds1,
    391 											const Vec2&				zBounds,
    392 											const float				searchStep,
    393 											const Vec4&				result)
    394 {
    395 	DE_ASSERT(xBounds0.x() <= xBounds0.y());
    396 	DE_ASSERT(yBounds0.x() <= yBounds0.y());
    397 	DE_ASSERT(xBounds1.x() <= xBounds1.y());
    398 	DE_ASSERT(yBounds1.x() <= yBounds1.y());
    399 
    400 	if (!isInColorBounds(prec, quad0, quad1, result))
    401 		return false;
    402 
    403 	for (float x0 = xBounds0.x(); x0 < xBounds0.y()+searchStep; x0 += searchStep)
    404 	{
    405 		for (float y0 = yBounds0.x(); y0 < yBounds0.y()+searchStep; y0 += searchStep)
    406 		{
    407 			const float		a0	= de::min(x0, xBounds0.y());
    408 			const float		b0	= de::min(y0, yBounds0.y());
    409 			const Vec4		c0	= quad0.p00*(1.0f-a0)*(1.0f-b0) + quad0.p10*a0*(1.0f-b0) + quad0.p01*(1.0f-a0)*b0 + quad0.p11*a0*b0;
    410 
    411 			for (float x1 = xBounds1.x(); x1 <= xBounds1.y(); x1 += searchStep)
    412 			{
    413 				for (float y1 = yBounds1.x(); y1 <= yBounds1.y(); y1 += searchStep)
    414 				{
    415 					const float		a1	= de::min(x1, xBounds1.y());
    416 					const float		b1	= de::min(y1, yBounds1.y());
    417 					const Vec4		c1	= quad1.p00*(1.0f-a1)*(1.0f-b1) + quad1.p10*a1*(1.0f-b1) + quad1.p01*(1.0f-a1)*b1 + quad1.p11*a1*b1;
    418 
    419 					if (isLinearRangeValid(prec, c0, c1, zBounds, result))
    420 						return true;
    421 				}
    422 			}
    423 		}
    424 	}
    425 
    426 	return false;
    427 }
    428 
    429 static bool is3DTrilinearFilterResultValid (const LookupPrecision&	prec,
    430 											const ColorQuad&		quad00,
    431 											const ColorQuad&		quad01,
    432 											const ColorQuad&		quad10,
    433 											const ColorQuad&		quad11,
    434 											const Vec2&				xBounds0,
    435 											const Vec2&				yBounds0,
    436 											const Vec2&				zBounds0,
    437 											const Vec2&				xBounds1,
    438 											const Vec2&				yBounds1,
    439 											const Vec2&				zBounds1,
    440 											const Vec2&				wBounds,
    441 											const float				searchStep,
    442 											const Vec4&				result)
    443 {
    444 	DE_ASSERT(xBounds0.x() <= xBounds0.y());
    445 	DE_ASSERT(yBounds0.x() <= yBounds0.y());
    446 	DE_ASSERT(zBounds0.x() <= zBounds0.y());
    447 	DE_ASSERT(xBounds1.x() <= xBounds1.y());
    448 	DE_ASSERT(yBounds1.x() <= yBounds1.y());
    449 	DE_ASSERT(zBounds1.x() <= zBounds1.y());
    450 
    451 	if (!isInColorBounds(prec, quad00, quad01, quad10, quad11, result))
    452 		return false;
    453 
    454 	for (float x0 = xBounds0.x(); x0 < xBounds0.y()+searchStep; x0 += searchStep)
    455 	{
    456 		for (float y0 = yBounds0.x(); y0 < yBounds0.y()+searchStep; y0 += searchStep)
    457 		{
    458 			const float		a0	= de::min(x0, xBounds0.y());
    459 			const float		b0	= de::min(y0, yBounds0.y());
    460 			const Vec4		c00	= quad00.p00*(1.0f-a0)*(1.0f-b0) + quad00.p10*a0*(1.0f-b0) + quad00.p01*(1.0f-a0)*b0 + quad00.p11*a0*b0;
    461 			const Vec4		c01	= quad01.p00*(1.0f-a0)*(1.0f-b0) + quad01.p10*a0*(1.0f-b0) + quad01.p01*(1.0f-a0)*b0 + quad01.p11*a0*b0;
    462 
    463 			for (float z0 = zBounds0.x(); z0 < zBounds0.y()+searchStep; z0 += searchStep)
    464 			{
    465 				const float		c0	= de::min(z0, zBounds0.y());
    466 				const Vec4		cz0	= c00*(1.0f-c0) + c01*c0;
    467 
    468 				for (float x1 = xBounds1.x(); x1 < xBounds1.y()+searchStep; x1 += searchStep)
    469 				{
    470 					for (float y1 = yBounds1.x(); y1 < yBounds1.y()+searchStep; y1 += searchStep)
    471 					{
    472 						const float		a1	= de::min(x1, xBounds1.y());
    473 						const float		b1	= de::min(y1, yBounds1.y());
    474 						const Vec4		c10	= quad10.p00*(1.0f-a1)*(1.0f-b1) + quad10.p10*a1*(1.0f-b1) + quad10.p01*(1.0f-a1)*b1 + quad10.p11*a1*b1;
    475 						const Vec4		c11	= quad11.p00*(1.0f-a1)*(1.0f-b1) + quad11.p10*a1*(1.0f-b1) + quad11.p01*(1.0f-a1)*b1 + quad11.p11*a1*b1;
    476 
    477 						for (float z1 = zBounds1.x(); z1 < zBounds1.y()+searchStep; z1 += searchStep)
    478 						{
    479 							const float		c1	= de::min(z1, zBounds1.y());
    480 							const Vec4		cz1	= c10*(1.0f - c1) + c11*c1;
    481 
    482 							if (isLinearRangeValid(prec, cz0, cz1, wBounds, result))
    483 								return true;
    484 						}
    485 					}
    486 				}
    487 			}
    488 		}
    489 	}
    490 
    491 	return false;
    492 }
    493 
    494 template<typename PrecType, typename ScalarType>
    495 static bool isNearestSampleResultValid (const ConstPixelBufferAccess&		level,
    496 										const Sampler&						sampler,
    497 										const PrecType&						prec,
    498 										const float							coordX,
    499 										const int							coordY,
    500 										const Vector<ScalarType, 4>&		result)
    501 {
    502 	DE_ASSERT(level.getDepth() == 1);
    503 
    504 	const Vec2		uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coordX, prec.coordBits.x(), prec.uvwBits.x());
    505 
    506 	const int		minI			= deFloorFloatToInt32(uBounds.x());
    507 	const int		maxI			= deFloorFloatToInt32(uBounds.y());
    508 
    509 	for (int i = minI; i <= maxI; i++)
    510 	{
    511 		const int					x		= wrap(sampler.wrapS, i, level.getWidth());
    512 		const Vector<ScalarType, 4>	color	= lookup<ScalarType>(level, sampler, x, coordY, 0);
    513 
    514 		if (isColorValid(prec, color, result))
    515 			return true;
    516 	}
    517 
    518 	return false;
    519 }
    520 
    521 template<typename PrecType, typename ScalarType>
    522 static bool isNearestSampleResultValid (const ConstPixelBufferAccess&		level,
    523 										const Sampler&						sampler,
    524 										const PrecType&						prec,
    525 										const Vec2&							coord,
    526 										const int							coordZ,
    527 										const Vector<ScalarType, 4>&		result)
    528 {
    529 	const Vec2		uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    530 	const Vec2		vBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    531 
    532 	// Integer coordinates - without wrap mode
    533 	const int		minI			= deFloorFloatToInt32(uBounds.x());
    534 	const int		maxI			= deFloorFloatToInt32(uBounds.y());
    535 	const int		minJ			= deFloorFloatToInt32(vBounds.x());
    536 	const int		maxJ			= deFloorFloatToInt32(vBounds.y());
    537 
    538 	// \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
    539 
    540 	for (int j = minJ; j <= maxJ; j++)
    541 	{
    542 		for (int i = minI; i <= maxI; i++)
    543 		{
    544 			const int					x		= wrap(sampler.wrapS, i, level.getWidth());
    545 			const int					y		= wrap(sampler.wrapT, j, level.getHeight());
    546 			const Vector<ScalarType, 4>	color	= lookup<ScalarType>(level, sampler, x, y, coordZ);
    547 
    548 			if (isColorValid(prec, color, result))
    549 				return true;
    550 		}
    551 	}
    552 
    553 	return false;
    554 }
    555 
    556 template<typename PrecType, typename ScalarType>
    557 static bool isNearestSampleResultValid (const ConstPixelBufferAccess&		level,
    558 										const Sampler&						sampler,
    559 										const PrecType&						prec,
    560 										const Vec3&							coord,
    561 										const Vector<ScalarType, 4>&		result)
    562 {
    563 	const Vec2		uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    564 	const Vec2		vBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    565 	const Vec2		wBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getDepth(),	coord.z(), prec.coordBits.z(), prec.uvwBits.z());
    566 
    567 	// Integer coordinates - without wrap mode
    568 	const int		minI			= deFloorFloatToInt32(uBounds.x());
    569 	const int		maxI			= deFloorFloatToInt32(uBounds.y());
    570 	const int		minJ			= deFloorFloatToInt32(vBounds.x());
    571 	const int		maxJ			= deFloorFloatToInt32(vBounds.y());
    572 	const int		minK			= deFloorFloatToInt32(wBounds.x());
    573 	const int		maxK			= deFloorFloatToInt32(wBounds.y());
    574 
    575 	// \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
    576 
    577 	for (int k = minK; k <= maxK; k++)
    578 	{
    579 		for (int j = minJ; j <= maxJ; j++)
    580 		{
    581 			for (int i = minI; i <= maxI; i++)
    582 			{
    583 				const int					x		= wrap(sampler.wrapS, i, level.getWidth());
    584 				const int					y		= wrap(sampler.wrapT, j, level.getHeight());
    585 				const int					z		= wrap(sampler.wrapR, k, level.getDepth());
    586 				const Vector<ScalarType, 4>	color	= lookup<ScalarType>(level, sampler, x, y, z);
    587 
    588 				if (isColorValid(prec, color, result))
    589 					return true;
    590 			}
    591 		}
    592 	}
    593 
    594 	return false;
    595 }
    596 
    597 bool isLinearSampleResultValid (const ConstPixelBufferAccess&		level,
    598 								const Sampler&						sampler,
    599 								const LookupPrecision&				prec,
    600 								const float							coordX,
    601 								const int							coordY,
    602 								const Vec4&							result)
    603 {
    604 	const Vec2					uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coordX, prec.coordBits.x(), prec.uvwBits.x());
    605 
    606 	const int					minI			= deFloorFloatToInt32(uBounds.x()-0.5f);
    607 	const int					maxI			= deFloorFloatToInt32(uBounds.y()-0.5f);
    608 
    609 	const int					w				= level.getWidth();
    610 
    611 	for (int i = minI; i <= maxI; i++)
    612 	{
    613 		// Wrapped coordinates
    614 		const int	x0		= wrap(sampler.wrapS, i  , w);
    615 		const int	x1		= wrap(sampler.wrapS, i+1, w);
    616 
    617 		// Bounds for filtering factors
    618 		const float	minA	= de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f);
    619 		const float	maxA	= de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f);
    620 
    621 		const Vec4	colorA	= lookup<float>(level, sampler, x0, coordY, 0);
    622 		const Vec4	colorB	= lookup<float>(level, sampler, x1, coordY, 0);
    623 
    624 		if (isLinearRangeValid(prec, colorA, colorB, Vec2(minA, maxA), result))
    625 			return true;
    626 	}
    627 
    628 	return false;
    629 }
    630 
    631 bool isLinearSampleResultValid (const ConstPixelBufferAccess&		level,
    632 								const Sampler&						sampler,
    633 								const LookupPrecision&				prec,
    634 								const Vec2&							coord,
    635 								const int							coordZ,
    636 								const Vec4&							result)
    637 {
    638 	const Vec2					uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    639 	const Vec2					vBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    640 
    641 	// Integer coordinate bounds for (x0,y0) - without wrap mode
    642 	const int					minI			= deFloorFloatToInt32(uBounds.x()-0.5f);
    643 	const int					maxI			= deFloorFloatToInt32(uBounds.y()-0.5f);
    644 	const int					minJ			= deFloorFloatToInt32(vBounds.x()-0.5f);
    645 	const int					maxJ			= deFloorFloatToInt32(vBounds.y()-0.5f);
    646 
    647 	const int					w				= level.getWidth();
    648 	const int					h				= level.getHeight();
    649 
    650 	const TextureChannelClass	texClass		= getTextureChannelClass(level.getFormat().type);
    651 	float						searchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
    652 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
    653 												  0.0f; // Step is computed for floating-point quads based on texel values.
    654 
    655 	// \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
    656 
    657 	for (int j = minJ; j <= maxJ; j++)
    658 	{
    659 		for (int i = minI; i <= maxI; i++)
    660 		{
    661 			// Wrapped coordinates
    662 			const int	x0		= wrap(sampler.wrapS, i  , w);
    663 			const int	x1		= wrap(sampler.wrapS, i+1, w);
    664 			const int	y0		= wrap(sampler.wrapT, j  , h);
    665 			const int	y1		= wrap(sampler.wrapT, j+1, h);
    666 
    667 			// Bounds for filtering factors
    668 			const float	minA	= de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f);
    669 			const float	maxA	= de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f);
    670 			const float	minB	= de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f);
    671 			const float	maxB	= de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f);
    672 
    673 			ColorQuad quad;
    674 			lookupQuad(quad, level, sampler, x0, x1, y0, y1, coordZ);
    675 
    676 			if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
    677 				searchStep = computeBilinearSearchStepFromFloatQuad(prec, quad);
    678 
    679 			if (isBilinearRangeValid(prec, quad, Vec2(minA, maxA), Vec2(minB, maxB), searchStep, result))
    680 				return true;
    681 		}
    682 	}
    683 
    684 	return false;
    685 }
    686 
    687 static bool isLinearSampleResultValid (const ConstPixelBufferAccess&		level,
    688 									   const Sampler&						sampler,
    689 									   const LookupPrecision&				prec,
    690 									   const Vec3&							coord,
    691 									   const Vec4&							result)
    692 {
    693 	const Vec2					uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    694 	const Vec2					vBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    695 	const Vec2					wBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getDepth(),	coord.z(), prec.coordBits.z(), prec.uvwBits.z());
    696 
    697 	// Integer coordinate bounds for (x0,y0) - without wrap mode
    698 	const int					minI			= deFloorFloatToInt32(uBounds.x()-0.5f);
    699 	const int					maxI			= deFloorFloatToInt32(uBounds.y()-0.5f);
    700 	const int					minJ			= deFloorFloatToInt32(vBounds.x()-0.5f);
    701 	const int					maxJ			= deFloorFloatToInt32(vBounds.y()-0.5f);
    702 	const int					minK			= deFloorFloatToInt32(wBounds.x()-0.5f);
    703 	const int					maxK			= deFloorFloatToInt32(wBounds.y()-0.5f);
    704 
    705 	const int					w				= level.getWidth();
    706 	const int					h				= level.getHeight();
    707 	const int					d				= level.getDepth();
    708 
    709 	const TextureChannelClass	texClass		= getTextureChannelClass(level.getFormat().type);
    710 	float						searchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
    711 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
    712 												  0.0f; // Step is computed for floating-point quads based on texel values.
    713 
    714 	// \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
    715 
    716 	for (int k = minK; k <= maxK; k++)
    717 	{
    718 		for (int j = minJ; j <= maxJ; j++)
    719 		{
    720 			for (int i = minI; i <= maxI; i++)
    721 			{
    722 				// Wrapped coordinates
    723 				const int	x0		= wrap(sampler.wrapS, i  , w);
    724 				const int	x1		= wrap(sampler.wrapS, i+1, w);
    725 				const int	y0		= wrap(sampler.wrapT, j  , h);
    726 				const int	y1		= wrap(sampler.wrapT, j+1, h);
    727 				const int	z0		= wrap(sampler.wrapR, k  , d);
    728 				const int	z1		= wrap(sampler.wrapR, k+1, d);
    729 
    730 				// Bounds for filtering factors
    731 				const float	minA	= de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f);
    732 				const float	maxA	= de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f);
    733 				const float	minB	= de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f);
    734 				const float	maxB	= de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f);
    735 				const float	minC	= de::clamp((wBounds.x()-0.5f)-float(k), 0.0f, 1.0f);
    736 				const float	maxC	= de::clamp((wBounds.y()-0.5f)-float(k), 0.0f, 1.0f);
    737 
    738 				ColorQuad quad0, quad1;
    739 				lookupQuad(quad0, level, sampler, x0, x1, y0, y1, z0);
    740 				lookupQuad(quad1, level, sampler, x0, x1, y0, y1, z1);
    741 
    742 				if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
    743 					searchStep = de::min(computeBilinearSearchStepFromFloatQuad(prec, quad0), computeBilinearSearchStepFromFloatQuad(prec, quad1));
    744 
    745 				if (isTrilinearRangeValid(prec, quad0, quad1, Vec2(minA, maxA), Vec2(minB, maxB), Vec2(minC, maxC), searchStep, result))
    746 					return true;
    747 			}
    748 		}
    749 	}
    750 
    751 	return false;
    752 }
    753 
    754 static bool isNearestMipmapLinearSampleResultValid (const ConstPixelBufferAccess&	level0,
    755 													const ConstPixelBufferAccess&	level1,
    756 													const Sampler&					sampler,
    757 													const LookupPrecision&			prec,
    758 													const float						coord,
    759 													const int						coordY,
    760 													const Vec2&						fBounds,
    761 													const Vec4&						result)
    762 {
    763 	const int		w0				= level0.getWidth();
    764 	const int		w1				= level1.getWidth();
    765 
    766 	const Vec2		uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord, prec.coordBits.x(), prec.uvwBits.x());
    767 	const Vec2		uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord, prec.coordBits.x(), prec.uvwBits.x());
    768 
    769 	// Integer coordinates - without wrap mode
    770 	const int		minI0			= deFloorFloatToInt32(uBounds0.x());
    771 	const int		maxI0			= deFloorFloatToInt32(uBounds0.y());
    772 	const int		minI1			= deFloorFloatToInt32(uBounds1.x());
    773 	const int		maxI1			= deFloorFloatToInt32(uBounds1.y());
    774 
    775 	for (int i0 = minI0; i0 <= maxI0; i0++)
    776 	{
    777 		for (int i1 = minI1; i1 <= maxI1; i1++)
    778 		{
    779 			const Vec4	c0	= lookup<float>(level0, sampler, wrap(sampler.wrapS, i0, w0), coordY, 0);
    780 			const Vec4	c1	= lookup<float>(level1, sampler, wrap(sampler.wrapS, i1, w1), coordY, 0);
    781 
    782 			if (isLinearRangeValid(prec, c0, c1, fBounds, result))
    783 				return true;
    784 		}
    785 	}
    786 
    787 	return false;
    788 }
    789 
    790 static bool isNearestMipmapLinearSampleResultValid (const ConstPixelBufferAccess&	level0,
    791 													const ConstPixelBufferAccess&	level1,
    792 													const Sampler&					sampler,
    793 													const LookupPrecision&			prec,
    794 													const Vec2&						coord,
    795 													const int						coordZ,
    796 													const Vec2&						fBounds,
    797 													const Vec4&						result)
    798 {
    799 	const int		w0				= level0.getWidth();
    800 	const int		w1				= level1.getWidth();
    801 	const int		h0				= level0.getHeight();
    802 	const int		h1				= level1.getHeight();
    803 
    804 	const Vec2		uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    805 	const Vec2		uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    806 	const Vec2		vBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    807 	const Vec2		vBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    808 
    809 	// Integer coordinates - without wrap mode
    810 	const int		minI0			= deFloorFloatToInt32(uBounds0.x());
    811 	const int		maxI0			= deFloorFloatToInt32(uBounds0.y());
    812 	const int		minI1			= deFloorFloatToInt32(uBounds1.x());
    813 	const int		maxI1			= deFloorFloatToInt32(uBounds1.y());
    814 	const int		minJ0			= deFloorFloatToInt32(vBounds0.x());
    815 	const int		maxJ0			= deFloorFloatToInt32(vBounds0.y());
    816 	const int		minJ1			= deFloorFloatToInt32(vBounds1.x());
    817 	const int		maxJ1			= deFloorFloatToInt32(vBounds1.y());
    818 
    819 	for (int j0 = minJ0; j0 <= maxJ0; j0++)
    820 	{
    821 		for (int i0 = minI0; i0 <= maxI0; i0++)
    822 		{
    823 			for (int j1 = minJ1; j1 <= maxJ1; j1++)
    824 			{
    825 				for (int i1 = minI1; i1 <= maxI1; i1++)
    826 				{
    827 					const Vec4	c0	= lookup<float>(level0, sampler, wrap(sampler.wrapS, i0, w0), wrap(sampler.wrapT, j0, h0), coordZ);
    828 					const Vec4	c1	= lookup<float>(level1, sampler, wrap(sampler.wrapS, i1, w1), wrap(sampler.wrapT, j1, h1), coordZ);
    829 
    830 					if (isLinearRangeValid(prec, c0, c1, fBounds, result))
    831 						return true;
    832 				}
    833 			}
    834 		}
    835 	}
    836 
    837 	return false;
    838 }
    839 
    840 static bool isNearestMipmapLinearSampleResultValid (const ConstPixelBufferAccess&	level0,
    841 													const ConstPixelBufferAccess&	level1,
    842 													const Sampler&					sampler,
    843 													const LookupPrecision&			prec,
    844 													const Vec3&						coord,
    845 													const Vec2&						fBounds,
    846 													const Vec4&						result)
    847 {
    848 	const int		w0				= level0.getWidth();
    849 	const int		w1				= level1.getWidth();
    850 	const int		h0				= level0.getHeight();
    851 	const int		h1				= level1.getHeight();
    852 	const int		d0				= level0.getDepth();
    853 	const int		d1				= level1.getDepth();
    854 
    855 	const Vec2		uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    856 	const Vec2		uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    857 	const Vec2		vBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    858 	const Vec2		vBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    859 	const Vec2		wBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, d0,	coord.z(), prec.coordBits.z(), prec.uvwBits.z());
    860 	const Vec2		wBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, d1,	coord.z(), prec.coordBits.z(), prec.uvwBits.z());
    861 
    862 	// Integer coordinates - without wrap mode
    863 	const int		minI0			= deFloorFloatToInt32(uBounds0.x());
    864 	const int		maxI0			= deFloorFloatToInt32(uBounds0.y());
    865 	const int		minI1			= deFloorFloatToInt32(uBounds1.x());
    866 	const int		maxI1			= deFloorFloatToInt32(uBounds1.y());
    867 	const int		minJ0			= deFloorFloatToInt32(vBounds0.x());
    868 	const int		maxJ0			= deFloorFloatToInt32(vBounds0.y());
    869 	const int		minJ1			= deFloorFloatToInt32(vBounds1.x());
    870 	const int		maxJ1			= deFloorFloatToInt32(vBounds1.y());
    871 	const int		minK0			= deFloorFloatToInt32(wBounds0.x());
    872 	const int		maxK0			= deFloorFloatToInt32(wBounds0.y());
    873 	const int		minK1			= deFloorFloatToInt32(wBounds1.x());
    874 	const int		maxK1			= deFloorFloatToInt32(wBounds1.y());
    875 
    876 	for (int k0 = minK0; k0 <= maxK0; k0++)
    877 	{
    878 		for (int j0 = minJ0; j0 <= maxJ0; j0++)
    879 		{
    880 			for (int i0 = minI0; i0 <= maxI0; i0++)
    881 			{
    882 				for (int k1 = minK1; k1 <= maxK1; k1++)
    883 				{
    884 					for (int j1 = minJ1; j1 <= maxJ1; j1++)
    885 					{
    886 						for (int i1 = minI1; i1 <= maxI1; i1++)
    887 						{
    888 							const Vec4	c0	= lookup<float>(level0, sampler, wrap(sampler.wrapS, i0, w0), wrap(sampler.wrapT, j0, h0), wrap(sampler.wrapR, k0, d0));
    889 							const Vec4	c1	= lookup<float>(level1, sampler, wrap(sampler.wrapS, i1, w1), wrap(sampler.wrapT, j1, h1), wrap(sampler.wrapR, k1, d1));
    890 
    891 							if (isLinearRangeValid(prec, c0, c1, fBounds, result))
    892 								return true;
    893 						}
    894 					}
    895 				}
    896 			}
    897 		}
    898 	}
    899 
    900 	return false;
    901 }
    902 
    903 static bool isLinearMipmapLinearSampleResultValid (const ConstPixelBufferAccess&	level0,
    904 												   const ConstPixelBufferAccess&	level1,
    905 												   const Sampler&					sampler,
    906 												   const LookupPrecision&			prec,
    907 												   const float						coordX,
    908 												   const int						coordY,
    909 												   const Vec2&						fBounds,
    910 												   const Vec4&						result)
    911 {
    912 	// \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
    913 	//						   Right now this allows pairing any two valid bilinear quads.
    914 
    915 	const int					w0				= level0.getWidth();
    916 	const int					w1				= level1.getWidth();
    917 
    918 	const Vec2					uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coordX, prec.coordBits.x(), prec.uvwBits.x());
    919 	const Vec2					uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coordX, prec.coordBits.x(), prec.uvwBits.x());
    920 
    921 	// Integer coordinates - without wrap mode
    922 	const int					minI0			= deFloorFloatToInt32(uBounds0.x()-0.5f);
    923 	const int					maxI0			= deFloorFloatToInt32(uBounds0.y()-0.5f);
    924 	const int					minI1			= deFloorFloatToInt32(uBounds1.x()-0.5f);
    925 	const int					maxI1			= deFloorFloatToInt32(uBounds1.y()-0.5f);
    926 
    927 	const TextureChannelClass	texClass		= getTextureChannelClass(level0.getFormat().type);
    928 	const float					cSearchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
    929 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
    930 												  0.0f; // Step is computed for floating-point quads based on texel values.
    931 
    932 	for (int i0 = minI0; i0 <= maxI0; i0++)
    933 	{
    934 		ColorLine	line0;
    935 		float		searchStep0;
    936 
    937 		{
    938 			const int	x0		= wrap(sampler.wrapS, i0  , w0);
    939 			const int	x1		= wrap(sampler.wrapS, i0+1, w0);
    940 			lookupLine(line0, level0, sampler, x0, x1, coordY);
    941 
    942 			if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
    943 				searchStep0 = computeBilinearSearchStepFromFloatLine(prec, line0);
    944 			else
    945 				searchStep0 = cSearchStep;
    946 		}
    947 
    948 		const float	minA0	= de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f);
    949 		const float	maxA0	= de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f);
    950 
    951 		for (int i1 = minI1; i1 <= maxI1; i1++)
    952 		{
    953 			ColorLine	line1;
    954 			float		searchStep1;
    955 
    956 			{
    957 				const int	x0		= wrap(sampler.wrapS, i1  , w1);
    958 				const int	x1		= wrap(sampler.wrapS, i1+1, w1);
    959 				lookupLine(line1, level1, sampler, x0, x1, coordY);
    960 
    961 				if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
    962 					searchStep1 = computeBilinearSearchStepFromFloatLine(prec, line1);
    963 				else
    964 					searchStep1 = cSearchStep;
    965 			}
    966 
    967 			const float	minA1	= de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f);
    968 			const float	maxA1	= de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f);
    969 
    970 			if (is1DTrilinearFilterResultValid(prec, line0, line1, Vec2(minA0, maxA0), Vec2(minA1, maxA1), fBounds, de::min(searchStep0, searchStep1), result))
    971 				return true;
    972 		}
    973 	}
    974 
    975 	return false;
    976 }
    977 
    978 static bool isLinearMipmapLinearSampleResultValid (const ConstPixelBufferAccess&	level0,
    979 												   const ConstPixelBufferAccess&	level1,
    980 												   const Sampler&					sampler,
    981 												   const LookupPrecision&			prec,
    982 												   const Vec2&						coord,
    983 												   const int						coordZ,
    984 												   const Vec2&						fBounds,
    985 												   const Vec4&						result)
    986 {
    987 	// \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
    988 	//						   Right now this allows pairing any two valid bilinear quads.
    989 
    990 	const int					w0				= level0.getWidth();
    991 	const int					w1				= level1.getWidth();
    992 	const int					h0				= level0.getHeight();
    993 	const int					h1				= level1.getHeight();
    994 
    995 	const Vec2					uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    996 	const Vec2					uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
    997 	const Vec2					vBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    998 	const Vec2					vBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
    999 
   1000 	// Integer coordinates - without wrap mode
   1001 	const int					minI0			= deFloorFloatToInt32(uBounds0.x()-0.5f);
   1002 	const int					maxI0			= deFloorFloatToInt32(uBounds0.y()-0.5f);
   1003 	const int					minI1			= deFloorFloatToInt32(uBounds1.x()-0.5f);
   1004 	const int					maxI1			= deFloorFloatToInt32(uBounds1.y()-0.5f);
   1005 	const int					minJ0			= deFloorFloatToInt32(vBounds0.x()-0.5f);
   1006 	const int					maxJ0			= deFloorFloatToInt32(vBounds0.y()-0.5f);
   1007 	const int					minJ1			= deFloorFloatToInt32(vBounds1.x()-0.5f);
   1008 	const int					maxJ1			= deFloorFloatToInt32(vBounds1.y()-0.5f);
   1009 
   1010 	const TextureChannelClass	texClass		= getTextureChannelClass(level0.getFormat().type);
   1011 	const float					cSearchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
   1012 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
   1013 												  0.0f; // Step is computed for floating-point quads based on texel values.
   1014 
   1015 	for (int j0 = minJ0; j0 <= maxJ0; j0++)
   1016 	{
   1017 		for (int i0 = minI0; i0 <= maxI0; i0++)
   1018 		{
   1019 			ColorQuad	quad0;
   1020 			float		searchStep0;
   1021 
   1022 			{
   1023 				const int	x0		= wrap(sampler.wrapS, i0  , w0);
   1024 				const int	x1		= wrap(sampler.wrapS, i0+1, w0);
   1025 				const int	y0		= wrap(sampler.wrapT, j0  , h0);
   1026 				const int	y1		= wrap(sampler.wrapT, j0+1, h0);
   1027 				lookupQuad(quad0, level0, sampler, x0, x1, y0, y1, coordZ);
   1028 
   1029 				if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1030 					searchStep0 = computeBilinearSearchStepFromFloatQuad(prec, quad0);
   1031 				else
   1032 					searchStep0 = cSearchStep;
   1033 			}
   1034 
   1035 			const float	minA0	= de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f);
   1036 			const float	maxA0	= de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f);
   1037 			const float	minB0	= de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f);
   1038 			const float	maxB0	= de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f);
   1039 
   1040 			for (int j1 = minJ1; j1 <= maxJ1; j1++)
   1041 			{
   1042 				for (int i1 = minI1; i1 <= maxI1; i1++)
   1043 				{
   1044 					ColorQuad	quad1;
   1045 					float		searchStep1;
   1046 
   1047 					{
   1048 						const int	x0		= wrap(sampler.wrapS, i1  , w1);
   1049 						const int	x1		= wrap(sampler.wrapS, i1+1, w1);
   1050 						const int	y0		= wrap(sampler.wrapT, j1  , h1);
   1051 						const int	y1		= wrap(sampler.wrapT, j1+1, h1);
   1052 						lookupQuad(quad1, level1, sampler, x0, x1, y0, y1, coordZ);
   1053 
   1054 						if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1055 							searchStep1 = computeBilinearSearchStepFromFloatQuad(prec, quad1);
   1056 						else
   1057 							searchStep1 = cSearchStep;
   1058 					}
   1059 
   1060 					const float	minA1	= de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f);
   1061 					const float	maxA1	= de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f);
   1062 					const float	minB1	= de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f);
   1063 					const float	maxB1	= de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f);
   1064 
   1065 					if (is2DTrilinearFilterResultValid(prec, quad0, quad1, Vec2(minA0, maxA0), Vec2(minB0, maxB0), Vec2(minA1, maxA1), Vec2(minB1, maxB1),
   1066 													   fBounds, de::min(searchStep0, searchStep1), result))
   1067 						return true;
   1068 				}
   1069 			}
   1070 		}
   1071 	}
   1072 
   1073 	return false;
   1074 }
   1075 
   1076 static bool isLinearMipmapLinearSampleResultValid (const ConstPixelBufferAccess&	level0,
   1077 												   const ConstPixelBufferAccess&	level1,
   1078 												   const Sampler&					sampler,
   1079 												   const LookupPrecision&			prec,
   1080 												   const Vec3&						coord,
   1081 												   const Vec2&						fBounds,
   1082 												   const Vec4&						result)
   1083 {
   1084 	// \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
   1085 	//						   Right now this allows pairing any two valid bilinear quads.
   1086 
   1087 	const int					w0				= level0.getWidth();
   1088 	const int					w1				= level1.getWidth();
   1089 	const int					h0				= level0.getHeight();
   1090 	const int					h1				= level1.getHeight();
   1091 	const int					d0				= level0.getDepth();
   1092 	const int					d1				= level1.getDepth();
   1093 
   1094 	const Vec2					uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
   1095 	const Vec2					uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1,	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
   1096 	const Vec2					vBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
   1097 	const Vec2					vBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1,	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
   1098 	const Vec2					wBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, d0,	coord.z(), prec.coordBits.z(), prec.uvwBits.z());
   1099 	const Vec2					wBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, d1,	coord.z(), prec.coordBits.z(), prec.uvwBits.z());
   1100 
   1101 	// Integer coordinates - without wrap mode
   1102 	const int					minI0			= deFloorFloatToInt32(uBounds0.x()-0.5f);
   1103 	const int					maxI0			= deFloorFloatToInt32(uBounds0.y()-0.5f);
   1104 	const int					minI1			= deFloorFloatToInt32(uBounds1.x()-0.5f);
   1105 	const int					maxI1			= deFloorFloatToInt32(uBounds1.y()-0.5f);
   1106 	const int					minJ0			= deFloorFloatToInt32(vBounds0.x()-0.5f);
   1107 	const int					maxJ0			= deFloorFloatToInt32(vBounds0.y()-0.5f);
   1108 	const int					minJ1			= deFloorFloatToInt32(vBounds1.x()-0.5f);
   1109 	const int					maxJ1			= deFloorFloatToInt32(vBounds1.y()-0.5f);
   1110 	const int					minK0			= deFloorFloatToInt32(wBounds0.x()-0.5f);
   1111 	const int					maxK0			= deFloorFloatToInt32(wBounds0.y()-0.5f);
   1112 	const int					minK1			= deFloorFloatToInt32(wBounds1.x()-0.5f);
   1113 	const int					maxK1			= deFloorFloatToInt32(wBounds1.y()-0.5f);
   1114 
   1115 	const TextureChannelClass	texClass		= getTextureChannelClass(level0.getFormat().type);
   1116 	const float					cSearchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
   1117 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
   1118 												  0.0f; // Step is computed for floating-point quads based on texel values.
   1119 
   1120 	for (int k0 = minK0; k0 <= maxK0; k0++)
   1121 	{
   1122 		for (int j0 = minJ0; j0 <= maxJ0; j0++)
   1123 		{
   1124 			for (int i0 = minI0; i0 <= maxI0; i0++)
   1125 			{
   1126 				ColorQuad	quad00, quad01;
   1127 				float		searchStep0;
   1128 
   1129 				{
   1130 					const int	x0		= wrap(sampler.wrapS, i0  , w0);
   1131 					const int	x1		= wrap(sampler.wrapS, i0+1, w0);
   1132 					const int	y0		= wrap(sampler.wrapT, j0  , h0);
   1133 					const int	y1		= wrap(sampler.wrapT, j0+1, h0);
   1134 					const int	z0		= wrap(sampler.wrapR, k0  , d0);
   1135 					const int	z1		= wrap(sampler.wrapR, k0+1, d0);
   1136 					lookupQuad(quad00, level0, sampler, x0, x1, y0, y1, z0);
   1137 					lookupQuad(quad01, level0, sampler, x0, x1, y0, y1, z1);
   1138 
   1139 					if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1140 						searchStep0 = de::min(computeBilinearSearchStepFromFloatQuad(prec, quad00), computeBilinearSearchStepFromFloatQuad(prec, quad01));
   1141 					else
   1142 						searchStep0 = cSearchStep;
   1143 				}
   1144 
   1145 				const float	minA0	= de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f);
   1146 				const float	maxA0	= de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f);
   1147 				const float	minB0	= de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f);
   1148 				const float	maxB0	= de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f);
   1149 				const float	minC0	= de::clamp((wBounds0.x()-0.5f)-float(k0), 0.0f, 1.0f);
   1150 				const float	maxC0	= de::clamp((wBounds0.y()-0.5f)-float(k0), 0.0f, 1.0f);
   1151 
   1152 				for (int k1 = minK1; k1 <= maxK1; k1++)
   1153 				{
   1154 					for (int j1 = minJ1; j1 <= maxJ1; j1++)
   1155 					{
   1156 						for (int i1 = minI1; i1 <= maxI1; i1++)
   1157 						{
   1158 							ColorQuad	quad10, quad11;
   1159 							float		searchStep1;
   1160 
   1161 							{
   1162 								const int	x0		= wrap(sampler.wrapS, i1  , w1);
   1163 								const int	x1		= wrap(sampler.wrapS, i1+1, w1);
   1164 								const int	y0		= wrap(sampler.wrapT, j1  , h1);
   1165 								const int	y1		= wrap(sampler.wrapT, j1+1, h1);
   1166 								const int	z0		= wrap(sampler.wrapR, k1  , d1);
   1167 								const int	z1		= wrap(sampler.wrapR, k1+1, d1);
   1168 								lookupQuad(quad10, level1, sampler, x0, x1, y0, y1, z0);
   1169 								lookupQuad(quad11, level1, sampler, x0, x1, y0, y1, z1);
   1170 
   1171 								if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1172 									searchStep1 = de::min(computeBilinearSearchStepFromFloatQuad(prec, quad10), computeBilinearSearchStepFromFloatQuad(prec, quad11));
   1173 								else
   1174 									searchStep1 = cSearchStep;
   1175 							}
   1176 
   1177 							const float	minA1	= de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f);
   1178 							const float	maxA1	= de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f);
   1179 							const float	minB1	= de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f);
   1180 							const float	maxB1	= de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f);
   1181 							const float	minC1	= de::clamp((wBounds1.x()-0.5f)-float(k1), 0.0f, 1.0f);
   1182 							const float	maxC1	= de::clamp((wBounds1.y()-0.5f)-float(k1), 0.0f, 1.0f);
   1183 
   1184 							if (is3DTrilinearFilterResultValid(prec, quad00, quad01, quad10, quad11,
   1185 															   Vec2(minA0, maxA0), Vec2(minB0, maxB0), Vec2(minC0, maxC0),
   1186 															   Vec2(minA1, maxA1), Vec2(minB1, maxB1), Vec2(minC1, maxC1),
   1187 															   fBounds, de::min(searchStep0, searchStep1), result))
   1188 								return true;
   1189 						}
   1190 					}
   1191 				}
   1192 			}
   1193 		}
   1194 	}
   1195 
   1196 	return false;
   1197 }
   1198 
   1199 static bool isLevelSampleResultValid (const ConstPixelBufferAccess&		level,
   1200 									  const Sampler&					sampler,
   1201 									  const Sampler::FilterMode			filterMode,
   1202 									  const LookupPrecision&			prec,
   1203 									  const float						coordX,
   1204 									  const int							coordY,
   1205 									  const Vec4&						result)
   1206 {
   1207 	if (filterMode == Sampler::LINEAR)
   1208 		return isLinearSampleResultValid(level, sampler, prec, coordX, coordY, result);
   1209 	else
   1210 		return isNearestSampleResultValid(level, sampler, prec, coordX, coordY, result);
   1211 }
   1212 
   1213 static bool isLevelSampleResultValid (const ConstPixelBufferAccess&		level,
   1214 									  const Sampler&					sampler,
   1215 									  const Sampler::FilterMode			filterMode,
   1216 									  const LookupPrecision&			prec,
   1217 									  const Vec2&						coord,
   1218 									  const int							coordZ,
   1219 									  const Vec4&						result)
   1220 {
   1221 	if (filterMode == Sampler::LINEAR)
   1222 		return isLinearSampleResultValid(level, sampler, prec, coord, coordZ, result);
   1223 	else
   1224 		return isNearestSampleResultValid(level, sampler, prec, coord, coordZ, result);
   1225 }
   1226 
   1227 static bool isMipmapLinearSampleResultValid (const ConstPixelBufferAccess&		level0,
   1228 										     const ConstPixelBufferAccess&		level1,
   1229 											 const Sampler&						sampler,
   1230 										     const Sampler::FilterMode			levelFilter,
   1231 										     const LookupPrecision&				prec,
   1232 										     const float						coordX,
   1233 											 const int							coordY,
   1234 										     const Vec2&						fBounds,
   1235 										     const Vec4&						result)
   1236 {
   1237 	if (levelFilter == Sampler::LINEAR)
   1238 		return isLinearMipmapLinearSampleResultValid(level0, level1, sampler, prec, coordX, coordY, fBounds, result);
   1239 	else
   1240 		return isNearestMipmapLinearSampleResultValid(level0, level1, sampler, prec, coordX, coordY, fBounds, result);
   1241 }
   1242 
   1243 static bool isMipmapLinearSampleResultValid (const ConstPixelBufferAccess&		level0,
   1244 										     const ConstPixelBufferAccess&		level1,
   1245 											 const Sampler&						sampler,
   1246 										     const Sampler::FilterMode			levelFilter,
   1247 										     const LookupPrecision&				prec,
   1248 										     const Vec2&						coord,
   1249 											 const int							coordZ,
   1250 										     const Vec2&						fBounds,
   1251 										     const Vec4&						result)
   1252 {
   1253 	if (levelFilter == Sampler::LINEAR)
   1254 		return isLinearMipmapLinearSampleResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, result);
   1255 	else
   1256 		return isNearestMipmapLinearSampleResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, result);
   1257 }
   1258 
   1259 bool isLookupResultValid (const Texture2DView& texture, const Sampler& sampler, const LookupPrecision& prec, const Vec2& coord, const Vec2& lodBounds, const Vec4& result)
   1260 {
   1261 	const float		minLod			= lodBounds.x();
   1262 	const float		maxLod			= lodBounds.y();
   1263 	const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
   1264 	const bool		canBeMinified	= maxLod > sampler.lodThreshold;
   1265 
   1266 	DE_ASSERT(isSamplerSupported(sampler));
   1267 
   1268 	if (canBeMagnified)
   1269 	{
   1270 		if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, result))
   1271 			return true;
   1272 	}
   1273 
   1274 	if (canBeMinified)
   1275 	{
   1276 		const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1277 		const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1278 		const int	minTexLevel		= 0;
   1279 		const int	maxTexLevel		= texture.getNumLevels()-1;
   1280 
   1281 		DE_ASSERT(minTexLevel <= maxTexLevel);
   1282 
   1283 		if (isLinearMipmap && minTexLevel < maxTexLevel)
   1284 		{
   1285 			const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1286 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1287 
   1288 			DE_ASSERT(minLevel <= maxLevel);
   1289 
   1290 			for (int level = minLevel; level <= maxLevel; level++)
   1291 			{
   1292 				const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
   1293 				const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
   1294 
   1295 				if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, Vec2(minF, maxF), result))
   1296 					return true;
   1297 			}
   1298 		}
   1299 		else if (isNearestMipmap)
   1300 		{
   1301 			// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   1302 			//		 decision to allow floor(lod + 0.5) as well.
   1303 			const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   1304 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   1305 
   1306 			DE_ASSERT(minLevel <= maxLevel);
   1307 
   1308 			for (int level = minLevel; level <= maxLevel; level++)
   1309 			{
   1310 				if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, result))
   1311 					return true;
   1312 			}
   1313 		}
   1314 		else
   1315 		{
   1316 			if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, result))
   1317 				return true;
   1318 		}
   1319 	}
   1320 
   1321 	return false;
   1322 }
   1323 
   1324 bool isLookupResultValid (const Texture1DView& texture, const Sampler& sampler, const LookupPrecision& prec, const float coord, const Vec2& lodBounds, const Vec4& result)
   1325 {
   1326 	const float		minLod			= lodBounds.x();
   1327 	const float		maxLod			= lodBounds.y();
   1328 	const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
   1329 	const bool		canBeMinified	= maxLod > sampler.lodThreshold;
   1330 
   1331 	DE_ASSERT(isSamplerSupported(sampler));
   1332 
   1333 	if (canBeMagnified)
   1334 	{
   1335 		if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, result))
   1336 			return true;
   1337 	}
   1338 
   1339 	if (canBeMinified)
   1340 	{
   1341 		const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1342 		const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1343 		const int	minTexLevel		= 0;
   1344 		const int	maxTexLevel		= texture.getNumLevels()-1;
   1345 
   1346 		DE_ASSERT(minTexLevel <= maxTexLevel);
   1347 
   1348 		if (isLinearMipmap && minTexLevel < maxTexLevel)
   1349 		{
   1350 			const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1351 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1352 
   1353 			DE_ASSERT(minLevel <= maxLevel);
   1354 
   1355 			for (int level = minLevel; level <= maxLevel; level++)
   1356 			{
   1357 				const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
   1358 				const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
   1359 
   1360 				if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, Vec2(minF, maxF), result))
   1361 					return true;
   1362 			}
   1363 		}
   1364 		else if (isNearestMipmap)
   1365 		{
   1366 			// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   1367 			//		 decision to allow floor(lod + 0.5) as well.
   1368 			const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   1369 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   1370 
   1371 			DE_ASSERT(minLevel <= maxLevel);
   1372 
   1373 			for (int level = minLevel; level <= maxLevel; level++)
   1374 			{
   1375 				if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, result))
   1376 					return true;
   1377 			}
   1378 		}
   1379 		else
   1380 		{
   1381 			if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, result))
   1382 				return true;
   1383 		}
   1384 	}
   1385 
   1386 	return false;
   1387 }
   1388 
   1389 static bool isSeamlessLinearSampleResultValid (const ConstPixelBufferAccess (&faces)[CUBEFACE_LAST],
   1390 											   const Sampler&				sampler,
   1391 											   const LookupPrecision&		prec,
   1392 											   const CubeFaceFloatCoords&	coords,
   1393 											   const Vec4&					result)
   1394 {
   1395 	const int					size			= faces[coords.face].getWidth();
   1396 
   1397 	const Vec2					uBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x());
   1398 	const Vec2					vBounds			= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y());
   1399 
   1400 	// Integer coordinate bounds for (x0,y0) - without wrap mode
   1401 	const int					minI			= deFloorFloatToInt32(uBounds.x()-0.5f);
   1402 	const int					maxI			= deFloorFloatToInt32(uBounds.y()-0.5f);
   1403 	const int					minJ			= deFloorFloatToInt32(vBounds.x()-0.5f);
   1404 	const int					maxJ			= deFloorFloatToInt32(vBounds.y()-0.5f);
   1405 
   1406 	const TextureChannelClass	texClass		= getTextureChannelClass(faces[coords.face].getFormat().type);
   1407 	float						searchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
   1408 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
   1409 												  0.0f; // Step is computed for floating-point quads based on texel values.
   1410 
   1411 	for (int j = minJ; j <= maxJ; j++)
   1412 	{
   1413 		for (int i = minI; i <= maxI; i++)
   1414 		{
   1415 			const CubeFaceIntCoords	c00	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+0, j+0)), size);
   1416 			const CubeFaceIntCoords	c10	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+1, j+0)), size);
   1417 			const CubeFaceIntCoords	c01	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+0, j+1)), size);
   1418 			const CubeFaceIntCoords	c11	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+1, j+1)), size);
   1419 
   1420 			// If any of samples is out of both edges, implementations can do pretty much anything according to spec.
   1421 			// \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color.
   1422 			if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST)
   1423 				return true;
   1424 
   1425 			// Bounds for filtering factors
   1426 			const float	minA	= de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f);
   1427 			const float	maxA	= de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f);
   1428 			const float	minB	= de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f);
   1429 			const float	maxB	= de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f);
   1430 
   1431 			ColorQuad quad;
   1432 			quad.p00 = lookup<float>(faces[c00.face], sampler, c00.s, c00.t, 0);
   1433 			quad.p10 = lookup<float>(faces[c10.face], sampler, c10.s, c10.t, 0);
   1434 			quad.p01 = lookup<float>(faces[c01.face], sampler, c01.s, c01.t, 0);
   1435 			quad.p11 = lookup<float>(faces[c11.face], sampler, c11.s, c11.t, 0);
   1436 
   1437 			if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1438 				searchStep = computeBilinearSearchStepFromFloatQuad(prec, quad);
   1439 
   1440 			if (isBilinearRangeValid(prec, quad, Vec2(minA, maxA), Vec2(minB, maxB), searchStep, result))
   1441 				return true;
   1442 		}
   1443 	}
   1444 
   1445 	return false;
   1446 }
   1447 
   1448 static bool isSeamplessLinearMipmapLinearSampleResultValid (const ConstPixelBufferAccess	(&faces0)[CUBEFACE_LAST],
   1449 															const ConstPixelBufferAccess	(&faces1)[CUBEFACE_LAST],
   1450 															const Sampler&					sampler,
   1451 															const LookupPrecision&			prec,
   1452 															const CubeFaceFloatCoords&		coords,
   1453 															const Vec2&						fBounds,
   1454 															const Vec4&						result)
   1455 {
   1456 	// \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
   1457 	//						   Right now this allows pairing any two valid bilinear quads.
   1458 
   1459 	const int					size0			= faces0[coords.face].getWidth();
   1460 	const int					size1			= faces1[coords.face].getWidth();
   1461 
   1462 	const Vec2					uBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0,	coords.s, prec.coordBits.x(), prec.uvwBits.x());
   1463 	const Vec2					uBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1,	coords.s, prec.coordBits.x(), prec.uvwBits.x());
   1464 	const Vec2					vBounds0		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0,	coords.t, prec.coordBits.y(), prec.uvwBits.y());
   1465 	const Vec2					vBounds1		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1,	coords.t, prec.coordBits.y(), prec.uvwBits.y());
   1466 
   1467 	// Integer coordinates - without wrap mode
   1468 	const int					minI0			= deFloorFloatToInt32(uBounds0.x()-0.5f);
   1469 	const int					maxI0			= deFloorFloatToInt32(uBounds0.y()-0.5f);
   1470 	const int					minI1			= deFloorFloatToInt32(uBounds1.x()-0.5f);
   1471 	const int					maxI1			= deFloorFloatToInt32(uBounds1.y()-0.5f);
   1472 	const int					minJ0			= deFloorFloatToInt32(vBounds0.x()-0.5f);
   1473 	const int					maxJ0			= deFloorFloatToInt32(vBounds0.y()-0.5f);
   1474 	const int					minJ1			= deFloorFloatToInt32(vBounds1.x()-0.5f);
   1475 	const int					maxJ1			= deFloorFloatToInt32(vBounds1.y()-0.5f);
   1476 
   1477 	const TextureChannelClass	texClass		= getTextureChannelClass(faces0[coords.face].getFormat().type);
   1478 	const float					cSearchStep		= texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT	? computeBilinearSearchStepForUnorm(prec) :
   1479 												  texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT	? computeBilinearSearchStepForSnorm(prec) :
   1480 												  0.0f; // Step is computed for floating-point quads based on texel values.
   1481 
   1482 	for (int j0 = minJ0; j0 <= maxJ0; j0++)
   1483 	{
   1484 		for (int i0 = minI0; i0 <= maxI0; i0++)
   1485 		{
   1486 			ColorQuad	quad0;
   1487 			float		searchStep0;
   1488 
   1489 			{
   1490 				const CubeFaceIntCoords	c00	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+0, j0+0)), size0);
   1491 				const CubeFaceIntCoords	c10	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+1, j0+0)), size0);
   1492 				const CubeFaceIntCoords	c01	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+0, j0+1)), size0);
   1493 				const CubeFaceIntCoords	c11	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+1, j0+1)), size0);
   1494 
   1495 				// If any of samples is out of both edges, implementations can do pretty much anything according to spec.
   1496 				// \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color.
   1497 				if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST)
   1498 					return true;
   1499 
   1500 				quad0.p00 = lookup<float>(faces0[c00.face], sampler, c00.s, c00.t, 0);
   1501 				quad0.p10 = lookup<float>(faces0[c10.face], sampler, c10.s, c10.t, 0);
   1502 				quad0.p01 = lookup<float>(faces0[c01.face], sampler, c01.s, c01.t, 0);
   1503 				quad0.p11 = lookup<float>(faces0[c11.face], sampler, c11.s, c11.t, 0);
   1504 
   1505 				if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1506 					searchStep0 = computeBilinearSearchStepFromFloatQuad(prec, quad0);
   1507 				else
   1508 					searchStep0 = cSearchStep;
   1509 			}
   1510 
   1511 			const float	minA0	= de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f);
   1512 			const float	maxA0	= de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f);
   1513 			const float	minB0	= de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f);
   1514 			const float	maxB0	= de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f);
   1515 
   1516 			for (int j1 = minJ1; j1 <= maxJ1; j1++)
   1517 			{
   1518 				for (int i1 = minI1; i1 <= maxI1; i1++)
   1519 				{
   1520 					ColorQuad	quad1;
   1521 					float		searchStep1;
   1522 
   1523 					{
   1524 						const CubeFaceIntCoords	c00	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+0, j1+0)), size1);
   1525 						const CubeFaceIntCoords	c10	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+1, j1+0)), size1);
   1526 						const CubeFaceIntCoords	c01	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+0, j1+1)), size1);
   1527 						const CubeFaceIntCoords	c11	= remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+1, j1+1)), size1);
   1528 
   1529 						if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST)
   1530 							return true;
   1531 
   1532 						quad1.p00 = lookup<float>(faces1[c00.face], sampler, c00.s, c00.t, 0);
   1533 						quad1.p10 = lookup<float>(faces1[c10.face], sampler, c10.s, c10.t, 0);
   1534 						quad1.p01 = lookup<float>(faces1[c01.face], sampler, c01.s, c01.t, 0);
   1535 						quad1.p11 = lookup<float>(faces1[c11.face], sampler, c11.s, c11.t, 0);
   1536 
   1537 						if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
   1538 							searchStep1 = computeBilinearSearchStepFromFloatQuad(prec, quad1);
   1539 						else
   1540 							searchStep1 = cSearchStep;
   1541 					}
   1542 
   1543 					const float	minA1	= de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f);
   1544 					const float	maxA1	= de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f);
   1545 					const float	minB1	= de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f);
   1546 					const float	maxB1	= de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f);
   1547 
   1548 					if (is2DTrilinearFilterResultValid(prec, quad0, quad1, Vec2(minA0, maxA0), Vec2(minB0, maxB0), Vec2(minA1, maxA1), Vec2(minB1, maxB1),
   1549 													   fBounds, de::min(searchStep0, searchStep1), result))
   1550 						return true;
   1551 				}
   1552 			}
   1553 		}
   1554 	}
   1555 
   1556 	return false;
   1557 }
   1558 
   1559 static bool isCubeLevelSampleResultValid (const ConstPixelBufferAccess		(&level)[CUBEFACE_LAST],
   1560 										  const Sampler&					sampler,
   1561 										  const Sampler::FilterMode			filterMode,
   1562 										  const LookupPrecision&			prec,
   1563 										  const CubeFaceFloatCoords&		coords,
   1564 										  const Vec4&						result)
   1565 {
   1566 	if (filterMode == Sampler::LINEAR)
   1567 	{
   1568 		if (sampler.seamlessCubeMap)
   1569 			return isSeamlessLinearSampleResultValid(level, sampler, prec, coords, result);
   1570 		else
   1571 			return isLinearSampleResultValid(level[coords.face], sampler, prec, Vec2(coords.s, coords.t), 0, result);
   1572 	}
   1573 	else
   1574 		return isNearestSampleResultValid(level[coords.face], sampler, prec, Vec2(coords.s, coords.t), 0, result);
   1575 }
   1576 
   1577 static bool isCubeMipmapLinearSampleResultValid (const ConstPixelBufferAccess	(&faces0)[CUBEFACE_LAST],
   1578 												const ConstPixelBufferAccess	(&faces1)[CUBEFACE_LAST],
   1579 												 const Sampler&					sampler,
   1580 												 const Sampler::FilterMode		levelFilter,
   1581 												 const LookupPrecision&			prec,
   1582 												 const CubeFaceFloatCoords&		coords,
   1583 												 const Vec2&					fBounds,
   1584 												 const Vec4&					result)
   1585 {
   1586 	if (levelFilter == Sampler::LINEAR)
   1587 	{
   1588 		if (sampler.seamlessCubeMap)
   1589 			return isSeamplessLinearMipmapLinearSampleResultValid(faces0, faces1, sampler, prec, coords, fBounds, result);
   1590 		else
   1591 			return isLinearMipmapLinearSampleResultValid(faces0[coords.face], faces1[coords.face], sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, result);
   1592 	}
   1593 	else
   1594 		return isNearestMipmapLinearSampleResultValid(faces0[coords.face], faces1[coords.face], sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, result);
   1595 }
   1596 
   1597 static void getCubeLevelFaces (const TextureCubeView& texture, const int levelNdx, ConstPixelBufferAccess (&out)[CUBEFACE_LAST])
   1598 {
   1599 	for (int faceNdx = 0; faceNdx < CUBEFACE_LAST; faceNdx++)
   1600 		out[faceNdx] = texture.getLevelFace(levelNdx, (CubeFace)faceNdx);
   1601 }
   1602 
   1603 bool isLookupResultValid (const TextureCubeView& texture, const Sampler& sampler, const LookupPrecision& prec, const Vec3& coord, const Vec2& lodBounds, const Vec4& result)
   1604 {
   1605 	int			numPossibleFaces				= 0;
   1606 	CubeFace	possibleFaces[CUBEFACE_LAST];
   1607 
   1608 	DE_ASSERT(isSamplerSupported(sampler));
   1609 
   1610 	getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
   1611 
   1612 	if (numPossibleFaces == 0)
   1613 		return true; // Result is undefined.
   1614 
   1615 	for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
   1616 	{
   1617 		const CubeFaceFloatCoords	faceCoords		(possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], coord));
   1618 		const float					minLod			= lodBounds.x();
   1619 		const float					maxLod			= lodBounds.y();
   1620 		const bool					canBeMagnified	= minLod <= sampler.lodThreshold;
   1621 		const bool					canBeMinified	= maxLod > sampler.lodThreshold;
   1622 
   1623 		if (canBeMagnified)
   1624 		{
   1625 			ConstPixelBufferAccess faces[CUBEFACE_LAST];
   1626 			getCubeLevelFaces(texture, 0, faces);
   1627 
   1628 			if (isCubeLevelSampleResultValid(faces, sampler, sampler.magFilter, prec, faceCoords, result))
   1629 				return true;
   1630 		}
   1631 
   1632 		if (canBeMinified)
   1633 		{
   1634 			const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1635 			const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1636 			const int	minTexLevel		= 0;
   1637 			const int	maxTexLevel		= texture.getNumLevels()-1;
   1638 
   1639 			DE_ASSERT(minTexLevel <= maxTexLevel);
   1640 
   1641 			if (isLinearMipmap && minTexLevel < maxTexLevel)
   1642 			{
   1643 				const int	minLevel	= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1644 				const int	maxLevel	= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1645 
   1646 				DE_ASSERT(minLevel <= maxLevel);
   1647 
   1648 				for (int levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++)
   1649 				{
   1650 					const float				minF	= de::clamp(minLod - float(levelNdx), 0.0f, 1.0f);
   1651 					const float				maxF	= de::clamp(maxLod - float(levelNdx), 0.0f, 1.0f);
   1652 
   1653 					ConstPixelBufferAccess	faces0[CUBEFACE_LAST];
   1654 					ConstPixelBufferAccess	faces1[CUBEFACE_LAST];
   1655 
   1656 					getCubeLevelFaces(texture, levelNdx,		faces0);
   1657 					getCubeLevelFaces(texture, levelNdx + 1,	faces1);
   1658 
   1659 					if (isCubeMipmapLinearSampleResultValid(faces0, faces1, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, Vec2(minF, maxF), result))
   1660 						return true;
   1661 				}
   1662 			}
   1663 			else if (isNearestMipmap)
   1664 			{
   1665 				// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   1666 				//		 decision to allow floor(lod + 0.5) as well.
   1667 				const int	minLevel	= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   1668 				const int	maxLevel	= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   1669 
   1670 				DE_ASSERT(minLevel <= maxLevel);
   1671 
   1672 				for (int levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++)
   1673 				{
   1674 					ConstPixelBufferAccess faces[CUBEFACE_LAST];
   1675 					getCubeLevelFaces(texture, levelNdx, faces);
   1676 
   1677 					if (isCubeLevelSampleResultValid(faces, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, result))
   1678 						return true;
   1679 				}
   1680 			}
   1681 			else
   1682 			{
   1683 				ConstPixelBufferAccess faces[CUBEFACE_LAST];
   1684 				getCubeLevelFaces(texture, 0, faces);
   1685 
   1686 				if (isCubeLevelSampleResultValid(faces, sampler, sampler.minFilter, prec, faceCoords, result))
   1687 					return true;
   1688 			}
   1689 		}
   1690 	}
   1691 
   1692 	return false;
   1693 }
   1694 
   1695 static inline IVec2 computeLayerRange (int numLayers, int numCoordBits, float layerCoord)
   1696 {
   1697 	const float	err		= computeFloatingPointError(layerCoord, numCoordBits);
   1698 	const int	minL	= (int)deFloatFloor(layerCoord - err + 0.5f);		// Round down
   1699 	const int	maxL	= (int)deFloatCeil(layerCoord + err + 0.5f) - 1;	// Round up
   1700 
   1701 	DE_ASSERT(minL <= maxL);
   1702 
   1703 	return IVec2(de::clamp(minL, 0, numLayers-1), de::clamp(maxL, 0, numLayers-1));
   1704 }
   1705 
   1706 bool isLookupResultValid (const Texture1DArrayView& texture, const Sampler& sampler, const LookupPrecision& prec, const Vec2& coord, const Vec2& lodBounds, const Vec4& result)
   1707 {
   1708 	const IVec2		layerRange		= computeLayerRange(texture.getNumLayers(), prec.coordBits.y(), coord.y());
   1709 	const float		coordX			= coord.x();
   1710 	const float		minLod			= lodBounds.x();
   1711 	const float		maxLod			= lodBounds.y();
   1712 	const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
   1713 	const bool		canBeMinified	= maxLod > sampler.lodThreshold;
   1714 
   1715 	DE_ASSERT(isSamplerSupported(sampler));
   1716 
   1717 	for (int layer = layerRange.x(); layer <= layerRange.y(); layer++)
   1718 	{
   1719 		if (canBeMagnified)
   1720 		{
   1721 			if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coordX, layer, result))
   1722 				return true;
   1723 		}
   1724 
   1725 		if (canBeMinified)
   1726 		{
   1727 			const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1728 			const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1729 			const int	minTexLevel		= 0;
   1730 			const int	maxTexLevel		= texture.getNumLevels()-1;
   1731 
   1732 			DE_ASSERT(minTexLevel <= maxTexLevel);
   1733 
   1734 			if (isLinearMipmap && minTexLevel < maxTexLevel)
   1735 			{
   1736 				const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1737 				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1738 
   1739 				DE_ASSERT(minLevel <= maxLevel);
   1740 
   1741 				for (int level = minLevel; level <= maxLevel; level++)
   1742 				{
   1743 					const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
   1744 					const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
   1745 
   1746 					if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coordX, layer, Vec2(minF, maxF), result))
   1747 						return true;
   1748 				}
   1749 			}
   1750 			else if (isNearestMipmap)
   1751 			{
   1752 				// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   1753 				//		 decision to allow floor(lod + 0.5) as well.
   1754 				const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   1755 				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   1756 
   1757 				DE_ASSERT(minLevel <= maxLevel);
   1758 
   1759 				for (int level = minLevel; level <= maxLevel; level++)
   1760 				{
   1761 					if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coordX, layer, result))
   1762 						return true;
   1763 				}
   1764 			}
   1765 			else
   1766 			{
   1767 				if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coordX, layer, result))
   1768 					return true;
   1769 			}
   1770 		}
   1771 	}
   1772 
   1773 	return false;
   1774 }
   1775 
   1776 bool isLookupResultValid (const Texture2DArrayView& texture, const Sampler& sampler, const LookupPrecision& prec, const Vec3& coord, const Vec2& lodBounds, const Vec4& result)
   1777 {
   1778 	const IVec2		layerRange		= computeLayerRange(texture.getNumLayers(), prec.coordBits.z(), coord.z());
   1779 	const Vec2		coordXY			= coord.swizzle(0,1);
   1780 	const float		minLod			= lodBounds.x();
   1781 	const float		maxLod			= lodBounds.y();
   1782 	const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
   1783 	const bool		canBeMinified	= maxLod > sampler.lodThreshold;
   1784 
   1785 	DE_ASSERT(isSamplerSupported(sampler));
   1786 
   1787 	for (int layer = layerRange.x(); layer <= layerRange.y(); layer++)
   1788 	{
   1789 		if (canBeMagnified)
   1790 		{
   1791 			if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coordXY, layer, result))
   1792 				return true;
   1793 		}
   1794 
   1795 		if (canBeMinified)
   1796 		{
   1797 			const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1798 			const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1799 			const int	minTexLevel		= 0;
   1800 			const int	maxTexLevel		= texture.getNumLevels()-1;
   1801 
   1802 			DE_ASSERT(minTexLevel <= maxTexLevel);
   1803 
   1804 			if (isLinearMipmap && minTexLevel < maxTexLevel)
   1805 			{
   1806 				const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1807 				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1808 
   1809 				DE_ASSERT(minLevel <= maxLevel);
   1810 
   1811 				for (int level = minLevel; level <= maxLevel; level++)
   1812 				{
   1813 					const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
   1814 					const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
   1815 
   1816 					if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coordXY, layer, Vec2(minF, maxF), result))
   1817 						return true;
   1818 				}
   1819 			}
   1820 			else if (isNearestMipmap)
   1821 			{
   1822 				// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   1823 				//		 decision to allow floor(lod + 0.5) as well.
   1824 				const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   1825 				const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   1826 
   1827 				DE_ASSERT(minLevel <= maxLevel);
   1828 
   1829 				for (int level = minLevel; level <= maxLevel; level++)
   1830 				{
   1831 					if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coordXY, layer, result))
   1832 						return true;
   1833 				}
   1834 			}
   1835 			else
   1836 			{
   1837 				if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coordXY, layer, result))
   1838 					return true;
   1839 			}
   1840 		}
   1841 	}
   1842 
   1843 	return false;
   1844 }
   1845 
   1846 static bool isLevelSampleResultValid (const ConstPixelBufferAccess&		level,
   1847 									  const Sampler&					sampler,
   1848 									  const Sampler::FilterMode			filterMode,
   1849 									  const LookupPrecision&			prec,
   1850 									  const Vec3&						coord,
   1851 									  const Vec4&						result)
   1852 {
   1853 	if (filterMode == Sampler::LINEAR)
   1854 		return isLinearSampleResultValid(level, sampler, prec, coord, result);
   1855 	else
   1856 		return isNearestSampleResultValid(level, sampler, prec, coord, result);
   1857 }
   1858 
   1859 static bool isMipmapLinearSampleResultValid (const ConstPixelBufferAccess&		level0,
   1860 										     const ConstPixelBufferAccess&		level1,
   1861 											 const Sampler&						sampler,
   1862 										     const Sampler::FilterMode			levelFilter,
   1863 										     const LookupPrecision&				prec,
   1864 										     const Vec3&						coord,
   1865 										     const Vec2&						fBounds,
   1866 										     const Vec4&						result)
   1867 {
   1868 	if (levelFilter == Sampler::LINEAR)
   1869 		return isLinearMipmapLinearSampleResultValid(level0, level1, sampler, prec, coord, fBounds, result);
   1870 	else
   1871 		return isNearestMipmapLinearSampleResultValid(level0, level1, sampler, prec, coord, fBounds, result);
   1872 }
   1873 
   1874 bool isLookupResultValid (const Texture3DView& texture, const Sampler& sampler, const LookupPrecision& prec, const Vec3& coord, const Vec2& lodBounds, const Vec4& result)
   1875 {
   1876 	const float		minLod			= lodBounds.x();
   1877 	const float		maxLod			= lodBounds.y();
   1878 	const bool		canBeMagnified	= minLod <= sampler.lodThreshold;
   1879 	const bool		canBeMinified	= maxLod > sampler.lodThreshold;
   1880 
   1881 	DE_ASSERT(isSamplerSupported(sampler));
   1882 
   1883 	if (canBeMagnified)
   1884 	{
   1885 		if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, result))
   1886 			return true;
   1887 	}
   1888 
   1889 	if (canBeMinified)
   1890 	{
   1891 		const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1892 		const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1893 		const int	minTexLevel		= 0;
   1894 		const int	maxTexLevel		= texture.getNumLevels()-1;
   1895 
   1896 		DE_ASSERT(minTexLevel <= maxTexLevel);
   1897 
   1898 		if (isLinearMipmap && minTexLevel < maxTexLevel)
   1899 		{
   1900 			const int		minLevel		= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1901 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1902 
   1903 			DE_ASSERT(minLevel <= maxLevel);
   1904 
   1905 			for (int level = minLevel; level <= maxLevel; level++)
   1906 			{
   1907 				const float		minF	= de::clamp(minLod - float(level), 0.0f, 1.0f);
   1908 				const float		maxF	= de::clamp(maxLod - float(level), 0.0f, 1.0f);
   1909 
   1910 				if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord, Vec2(minF, maxF), result))
   1911 					return true;
   1912 			}
   1913 		}
   1914 		else if (isNearestMipmap)
   1915 		{
   1916 			// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   1917 			//		 decision to allow floor(lod + 0.5) as well.
   1918 			const int		minLevel		= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   1919 			const int		maxLevel		= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   1920 
   1921 			DE_ASSERT(minLevel <= maxLevel);
   1922 
   1923 			for (int level = minLevel; level <= maxLevel; level++)
   1924 			{
   1925 				if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord, result))
   1926 					return true;
   1927 			}
   1928 		}
   1929 		else
   1930 		{
   1931 			if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, result))
   1932 				return true;
   1933 		}
   1934 	}
   1935 
   1936 	return false;
   1937 }
   1938 
   1939 static void getCubeArrayLevelFaces (const TextureCubeArrayView& texture, const int levelNdx, const int layerNdx, ConstPixelBufferAccess (&out)[CUBEFACE_LAST])
   1940 {
   1941 	const ConstPixelBufferAccess&	level		= texture.getLevel(levelNdx);
   1942 	const int						layerDepth	= layerNdx * 6;
   1943 
   1944 	for (int faceNdx = 0; faceNdx < CUBEFACE_LAST; faceNdx++)
   1945 	{
   1946 		const CubeFace face = (CubeFace)faceNdx;
   1947 		out[faceNdx] = getSubregion(level, 0, 0, layerDepth + getCubeArrayFaceIndex(face), level.getWidth(), level.getHeight(), 1);
   1948 	}
   1949 }
   1950 
   1951 bool isLookupResultValid (const TextureCubeArrayView& texture, const Sampler& sampler, const LookupPrecision& prec, const IVec4& coordBits, const Vec4& coord, const Vec2& lodBounds, const Vec4& result)
   1952 {
   1953 	const IVec2	layerRange						= computeLayerRange(texture.getNumLayers(), coordBits.w(), coord.w());
   1954 	const Vec3	layerCoord						= coord.toWidth<3>();
   1955 	int			numPossibleFaces				= 0;
   1956 	CubeFace	possibleFaces[CUBEFACE_LAST];
   1957 
   1958 	DE_ASSERT(isSamplerSupported(sampler));
   1959 
   1960 	getPossibleCubeFaces(layerCoord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
   1961 
   1962 	if (numPossibleFaces == 0)
   1963 		return true; // Result is undefined.
   1964 
   1965 	for (int layerNdx = layerRange.x(); layerNdx <= layerRange.y(); layerNdx++)
   1966 	{
   1967 		for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
   1968 		{
   1969 			const CubeFaceFloatCoords	faceCoords		(possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], layerCoord));
   1970 			const float					minLod			= lodBounds.x();
   1971 			const float					maxLod			= lodBounds.y();
   1972 			const bool					canBeMagnified	= minLod <= sampler.lodThreshold;
   1973 			const bool					canBeMinified	= maxLod > sampler.lodThreshold;
   1974 
   1975 			if (canBeMagnified)
   1976 			{
   1977 				ConstPixelBufferAccess faces[CUBEFACE_LAST];
   1978 				getCubeArrayLevelFaces(texture, 0, layerNdx, faces);
   1979 
   1980 				if (isCubeLevelSampleResultValid(faces, sampler, sampler.magFilter, prec, faceCoords, result))
   1981 					return true;
   1982 			}
   1983 
   1984 			if (canBeMinified)
   1985 			{
   1986 				const bool	isNearestMipmap	= isNearestMipmapFilter(sampler.minFilter);
   1987 				const bool	isLinearMipmap	= isLinearMipmapFilter(sampler.minFilter);
   1988 				const int	minTexLevel		= 0;
   1989 				const int	maxTexLevel		= texture.getNumLevels()-1;
   1990 
   1991 				DE_ASSERT(minTexLevel <= maxTexLevel);
   1992 
   1993 				if (isLinearMipmap && minTexLevel < maxTexLevel)
   1994 				{
   1995 					const int	minLevel	= de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1);
   1996 					const int	maxLevel	= de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1);
   1997 
   1998 					DE_ASSERT(minLevel <= maxLevel);
   1999 
   2000 					for (int levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++)
   2001 					{
   2002 						const float		minF	= de::clamp(minLod - float(levelNdx), 0.0f, 1.0f);
   2003 						const float		maxF	= de::clamp(maxLod - float(levelNdx), 0.0f, 1.0f);
   2004 
   2005 						ConstPixelBufferAccess	faces0[CUBEFACE_LAST];
   2006 						ConstPixelBufferAccess	faces1[CUBEFACE_LAST];
   2007 
   2008 						getCubeArrayLevelFaces(texture, levelNdx,		layerNdx,	faces0);
   2009 						getCubeArrayLevelFaces(texture, levelNdx + 1,	layerNdx,	faces1);
   2010 
   2011 						if (isCubeMipmapLinearSampleResultValid(faces0, faces1, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, Vec2(minF, maxF), result))
   2012 							return true;
   2013 					}
   2014 				}
   2015 				else if (isNearestMipmap)
   2016 				{
   2017 					// \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
   2018 					//		 decision to allow floor(lod + 0.5) as well.
   2019 					const int	minLevel	= de::clamp((int)deFloatCeil(minLod + 0.5f) - 1,	minTexLevel, maxTexLevel);
   2020 					const int	maxLevel	= de::clamp((int)deFloatFloor(maxLod + 0.5f),		minTexLevel, maxTexLevel);
   2021 
   2022 					DE_ASSERT(minLevel <= maxLevel);
   2023 
   2024 					for (int levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++)
   2025 					{
   2026 						ConstPixelBufferAccess faces[CUBEFACE_LAST];
   2027 						getCubeArrayLevelFaces(texture, levelNdx, layerNdx, faces);
   2028 
   2029 						if (isCubeLevelSampleResultValid(faces, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, result))
   2030 							return true;
   2031 					}
   2032 				}
   2033 				else
   2034 				{
   2035 					ConstPixelBufferAccess faces[CUBEFACE_LAST];
   2036 					getCubeArrayLevelFaces(texture, 0, layerNdx, faces);
   2037 
   2038 					if (isCubeLevelSampleResultValid(faces, sampler, sampler.minFilter, prec, faceCoords, result))
   2039 						return true;
   2040 				}
   2041 			}
   2042 		}
   2043 	}
   2044 
   2045 	return false;
   2046 }
   2047 
   2048 Vec4 computeFixedPointThreshold (const IVec4& bits)
   2049 {
   2050 	return computeFixedPointError(bits);
   2051 }
   2052 
   2053 Vec4 computeFloatingPointThreshold (const IVec4& bits, const Vec4& value)
   2054 {
   2055 	return computeFloatingPointError(value, bits);
   2056 }
   2057 
   2058 Vec2 computeLodBoundsFromDerivates (const float dudx, const float dvdx, const float dwdx, const float dudy, const float dvdy, const float dwdy, const LodPrecision& prec)
   2059 {
   2060 	const float		mu			= de::max(deFloatAbs(dudx), deFloatAbs(dudy));
   2061 	const float		mv			= de::max(deFloatAbs(dvdx), deFloatAbs(dvdy));
   2062 	const float		mw			= de::max(deFloatAbs(dwdx), deFloatAbs(dwdy));
   2063 	const float		minDBound	= de::max(de::max(mu, mv), mw);
   2064 	const float		maxDBound	= mu + mv + mw;
   2065 	const float		minDErr		= computeFloatingPointError(minDBound, prec.derivateBits);
   2066 	const float		maxDErr		= computeFloatingPointError(maxDBound, prec.derivateBits);
   2067 	const float		minLod		= deFloatLog2(minDBound-minDErr);
   2068 	const float		maxLod		= deFloatLog2(maxDBound+maxDErr);
   2069 	const float		lodErr		= computeFixedPointError(prec.lodBits);
   2070 
   2071 	DE_ASSERT(minLod <= maxLod);
   2072 	return Vec2(minLod-lodErr, maxLod+lodErr);
   2073 }
   2074 
   2075 Vec2 computeLodBoundsFromDerivates (const float dudx, const float dvdx, const float dudy, const float dvdy, const LodPrecision& prec)
   2076 {
   2077 	return computeLodBoundsFromDerivates(dudx, dvdx, 0.0f, dudy, dvdy, 0.0f, prec);
   2078 }
   2079 
   2080 Vec2 computeLodBoundsFromDerivates (const float dudx, const float dudy, const LodPrecision& prec)
   2081 {
   2082 	return computeLodBoundsFromDerivates(dudx, 0.0f, 0.0f, dudy, 0.0f, 0.0f, prec);
   2083 }
   2084 
   2085 Vec2 computeCubeLodBoundsFromDerivates (const Vec3& coord, const Vec3& coordDx, const Vec3& coordDy, const int faceSize, const LodPrecision& prec)
   2086 {
   2087 	const bool			allowBrokenEdgeDerivate		= false;
   2088 	const CubeFace		face						= selectCubeFace(coord);
   2089 	int					maNdx						= 0;
   2090 	int					sNdx						= 0;
   2091 	int					tNdx						= 0;
   2092 
   2093 	// \note Derivate signs don't matter when computing lod
   2094 	switch (face)
   2095 	{
   2096 		case CUBEFACE_NEGATIVE_X:
   2097 		case CUBEFACE_POSITIVE_X: maNdx = 0; sNdx = 2; tNdx = 1; break;
   2098 		case CUBEFACE_NEGATIVE_Y:
   2099 		case CUBEFACE_POSITIVE_Y: maNdx = 1; sNdx = 0; tNdx = 2; break;
   2100 		case CUBEFACE_NEGATIVE_Z:
   2101 		case CUBEFACE_POSITIVE_Z: maNdx = 2; sNdx = 0; tNdx = 1; break;
   2102 		default:
   2103 			DE_ASSERT(DE_FALSE);
   2104 	}
   2105 
   2106 	{
   2107 		const float		sc		= coord[sNdx];
   2108 		const float		tc		= coord[tNdx];
   2109 		const float		ma		= de::abs(coord[maNdx]);
   2110 		const float		scdx	= coordDx[sNdx];
   2111 		const float		tcdx	= coordDx[tNdx];
   2112 		const float		madx	= de::abs(coordDx[maNdx]);
   2113 		const float		scdy	= coordDy[sNdx];
   2114 		const float		tcdy	= coordDy[tNdx];
   2115 		const float		mady	= de::abs(coordDy[maNdx]);
   2116 		const float		dudx	= float(faceSize) * 0.5f * (scdx*ma - sc*madx) / (ma*ma);
   2117 		const float		dvdx	= float(faceSize) * 0.5f * (tcdx*ma - tc*madx) / (ma*ma);
   2118 		const float		dudy	= float(faceSize) * 0.5f * (scdy*ma - sc*mady) / (ma*ma);
   2119 		const float		dvdy	= float(faceSize) * 0.5f * (tcdy*ma - tc*mady) / (ma*ma);
   2120 		const Vec2		bounds	= computeLodBoundsFromDerivates(dudx, dvdx, dudy, dvdy, prec);
   2121 
   2122 		// Implementations may compute derivate from projected (s, t) resulting in incorrect values at edges.
   2123 		if (allowBrokenEdgeDerivate)
   2124 		{
   2125 			const Vec3			dxErr		= computeFloatingPointError(coordDx, IVec3(prec.derivateBits));
   2126 			const Vec3			dyErr		= computeFloatingPointError(coordDy, IVec3(prec.derivateBits));
   2127 			const Vec3			xoffs		= abs(coordDx) + dxErr;
   2128 			const Vec3			yoffs		= abs(coordDy) + dyErr;
   2129 
   2130 			if (selectCubeFace(coord + xoffs) != face ||
   2131 				selectCubeFace(coord - xoffs) != face ||
   2132 				selectCubeFace(coord + yoffs) != face ||
   2133 				selectCubeFace(coord - yoffs) != face)
   2134 			{
   2135 				return Vec2(bounds.x(), 1000.0f);
   2136 			}
   2137 		}
   2138 
   2139 		return bounds;
   2140 	}
   2141 }
   2142 
   2143 Vec2 clampLodBounds (const Vec2& lodBounds, const Vec2& lodMinMax, const LodPrecision& prec)
   2144 {
   2145 	const float lodErr	= computeFixedPointError(prec.lodBits);
   2146 	const float	a		= lodMinMax.x();
   2147 	const float	b		= lodMinMax.y();
   2148 	return Vec2(de::clamp(lodBounds.x(), a-lodErr, b-lodErr), de::clamp(lodBounds.y(), a+lodErr, b+lodErr));
   2149 }
   2150 
   2151 bool isLevel1DLookupResultValid (const ConstPixelBufferAccess&	access,
   2152 								 const Sampler&					sampler,
   2153 								 TexLookupScaleMode				scaleMode,
   2154 								 const LookupPrecision&			prec,
   2155 								 const float					coordX,
   2156 								 const int						coordY,
   2157 								 const Vec4&					result)
   2158 {
   2159 	const Sampler::FilterMode filterMode = scaleMode == TEX_LOOKUP_SCALE_MAGNIFY ? sampler.magFilter : sampler.minFilter;
   2160 	return isLevelSampleResultValid(access, sampler, filterMode, prec, coordX, coordY, result);
   2161 }
   2162 
   2163 bool isLevel1DLookupResultValid (const ConstPixelBufferAccess&	access,
   2164 								 const Sampler&					sampler,
   2165 								 TexLookupScaleMode				scaleMode,
   2166 								 const IntLookupPrecision&		prec,
   2167 								 const float					coordX,
   2168 								 const int						coordY,
   2169 								 const IVec4&					result)
   2170 {
   2171 	DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
   2172 	DE_UNREF(scaleMode);
   2173 	return isNearestSampleResultValid(access, sampler, prec, coordX, coordY, result);
   2174 }
   2175 
   2176 bool isLevel1DLookupResultValid (const ConstPixelBufferAccess&	access,
   2177 								 const Sampler&					sampler,
   2178 								 TexLookupScaleMode				scaleMode,
   2179 								 const IntLookupPrecision&		prec,
   2180 								 const float					coordX,
   2181 								 const int						coordY,
   2182 								 const UVec4&					result)
   2183 {
   2184 	DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
   2185 	DE_UNREF(scaleMode);
   2186 	return isNearestSampleResultValid(access, sampler, prec, coordX, coordY, result);
   2187 }
   2188 
   2189 bool isLevel2DLookupResultValid (const ConstPixelBufferAccess&	access,
   2190 								 const Sampler&					sampler,
   2191 								 TexLookupScaleMode				scaleMode,
   2192 								 const LookupPrecision&			prec,
   2193 								 const Vec2&					coord,
   2194 								 const int						coordZ,
   2195 								 const Vec4&					result)
   2196 {
   2197 	const Sampler::FilterMode filterMode = scaleMode == TEX_LOOKUP_SCALE_MAGNIFY ? sampler.magFilter : sampler.minFilter;
   2198 	return isLevelSampleResultValid(access, sampler, filterMode, prec, coord, coordZ, result);
   2199 }
   2200 
   2201 bool isLevel2DLookupResultValid (const ConstPixelBufferAccess&	access,
   2202 								 const Sampler&					sampler,
   2203 								 TexLookupScaleMode				scaleMode,
   2204 								 const IntLookupPrecision&		prec,
   2205 								 const Vec2&					coord,
   2206 								 const int						coordZ,
   2207 								 const IVec4&					result)
   2208 {
   2209 	DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
   2210 	DE_UNREF(scaleMode);
   2211 	return isNearestSampleResultValid(access, sampler, prec, coord, coordZ, result);
   2212 }
   2213 
   2214 bool isLevel2DLookupResultValid (const ConstPixelBufferAccess&	access,
   2215 								 const Sampler&					sampler,
   2216 								 TexLookupScaleMode				scaleMode,
   2217 								 const IntLookupPrecision&		prec,
   2218 								 const Vec2&					coord,
   2219 								 const int						coordZ,
   2220 								 const UVec4&					result)
   2221 {
   2222 	DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
   2223 	DE_UNREF(scaleMode);
   2224 	return isNearestSampleResultValid(access, sampler, prec, coord, coordZ, result);
   2225 }
   2226 
   2227 bool isLevel3DLookupResultValid (const ConstPixelBufferAccess&	access,
   2228 								 const Sampler&					sampler,
   2229 								 TexLookupScaleMode				scaleMode,
   2230 								 const LookupPrecision&			prec,
   2231 								 const Vec3&					coord,
   2232 								 const Vec4&					result)
   2233 {
   2234 	const tcu::Sampler::FilterMode filterMode = scaleMode == TEX_LOOKUP_SCALE_MAGNIFY ? sampler.magFilter : sampler.minFilter;
   2235 	return isLevelSampleResultValid(access, sampler, filterMode, prec, coord, result);
   2236 }
   2237 
   2238 bool isLevel3DLookupResultValid (const ConstPixelBufferAccess&	access,
   2239 								 const Sampler&					sampler,
   2240 								 TexLookupScaleMode				scaleMode,
   2241 								 const IntLookupPrecision&		prec,
   2242 								 const Vec3&					coord,
   2243 								 const IVec4&					result)
   2244 {
   2245 	DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
   2246 	DE_UNREF(scaleMode);
   2247 	return isNearestSampleResultValid(access, sampler, prec, coord, result);
   2248 }
   2249 
   2250 bool isLevel3DLookupResultValid (const ConstPixelBufferAccess&	access,
   2251 								 const Sampler&					sampler,
   2252 								 TexLookupScaleMode				scaleMode,
   2253 								 const IntLookupPrecision&		prec,
   2254 								 const Vec3&					coord,
   2255 								 const UVec4&					result)
   2256 {
   2257 	DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
   2258 	DE_UNREF(scaleMode);
   2259 	return isNearestSampleResultValid(access, sampler, prec, coord, result);
   2260 }
   2261 
   2262 template<typename PrecType, typename ScalarType>
   2263 static bool isGatherOffsetsResultValid (const ConstPixelBufferAccess&	level,
   2264 										const Sampler&					sampler,
   2265 										const PrecType&					prec,
   2266 										const Vec2&						coord,
   2267 										int								coordZ,
   2268 										int								componentNdx,
   2269 										const IVec2						(&offsets)[4],
   2270 										const Vector<ScalarType, 4>&	result)
   2271 {
   2272 	const Vec2	uBounds		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(),	coord.x(), prec.coordBits.x(), prec.uvwBits.x());
   2273 	const Vec2	vBounds		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(),	coord.y(), prec.coordBits.y(), prec.uvwBits.y());
   2274 
   2275 	// Integer coordinate bounds for (x0, y0) - without wrap mode
   2276 	const int	minI		= deFloorFloatToInt32(uBounds.x()-0.5f);
   2277 	const int	maxI		= deFloorFloatToInt32(uBounds.y()-0.5f);
   2278 	const int	minJ		= deFloorFloatToInt32(vBounds.x()-0.5f);
   2279 	const int	maxJ		= deFloorFloatToInt32(vBounds.y()-0.5f);
   2280 
   2281 	const int	w			= level.getWidth();
   2282 	const int	h			= level.getHeight();
   2283 
   2284 	for (int j = minJ; j <= maxJ; j++)
   2285 	{
   2286 		for (int i = minI; i <= maxI; i++)
   2287 		{
   2288 			Vector<ScalarType, 4> color;
   2289 			for (int offNdx = 0; offNdx < 4; offNdx++)
   2290 			{
   2291 				// offNdx-th coordinate offset and then wrapped.
   2292 				const int x = wrap(sampler.wrapS, i+offsets[offNdx].x(), w);
   2293 				const int y = wrap(sampler.wrapT, j+offsets[offNdx].y(), h);
   2294 				color[offNdx] = lookup<ScalarType>(level, sampler, x, y, coordZ)[componentNdx];
   2295 			}
   2296 
   2297 			if (isColorValid(prec, color, result))
   2298 				return true;
   2299 		}
   2300 	}
   2301 
   2302 	return false;
   2303 }
   2304 
   2305 bool isGatherOffsetsResultValid (const Texture2DView&			texture,
   2306 								 const Sampler&					sampler,
   2307 								 const LookupPrecision&			prec,
   2308 								 const Vec2&					coord,
   2309 								 int							componentNdx,
   2310 								 const IVec2					(&offsets)[4],
   2311 								 const Vec4&					result)
   2312 {
   2313 	return isGatherOffsetsResultValid(texture.getLevel(0), sampler, prec, coord, 0, componentNdx, offsets, result);
   2314 }
   2315 
   2316 bool isGatherOffsetsResultValid (const Texture2DView&			texture,
   2317 								 const Sampler&					sampler,
   2318 								 const IntLookupPrecision&		prec,
   2319 								 const Vec2&					coord,
   2320 								 int							componentNdx,
   2321 								 const IVec2					(&offsets)[4],
   2322 								 const IVec4&					result)
   2323 {
   2324 	return isGatherOffsetsResultValid(texture.getLevel(0), sampler, prec, coord, 0, componentNdx, offsets, result);
   2325 }
   2326 
   2327 bool isGatherOffsetsResultValid (const Texture2DView&			texture,
   2328 								 const Sampler&					sampler,
   2329 								 const IntLookupPrecision&		prec,
   2330 								 const Vec2&					coord,
   2331 								 int							componentNdx,
   2332 								 const IVec2					(&offsets)[4],
   2333 								 const UVec4&					result)
   2334 {
   2335 	return isGatherOffsetsResultValid(texture.getLevel(0), sampler, prec, coord, 0, componentNdx, offsets, result);
   2336 }
   2337 
   2338 template <typename PrecType, typename ScalarType>
   2339 static bool is2DArrayGatherOffsetsResultValid (const Texture2DArrayView&		texture,
   2340 											   const Sampler&					sampler,
   2341 											   const PrecType&					prec,
   2342 											   const Vec3&						coord,
   2343 											   int								componentNdx,
   2344 											   const IVec2						(&offsets)[4],
   2345 											   const Vector<ScalarType, 4>&		result)
   2346 {
   2347 	const IVec2 layerRange = computeLayerRange(texture.getNumLayers(), prec.coordBits.z(), coord.z());
   2348 	for (int layer = layerRange.x(); layer <= layerRange.y(); layer++)
   2349 	{
   2350 		if (isGatherOffsetsResultValid(texture.getLevel(0), sampler, prec, coord.swizzle(0,1), layer, componentNdx, offsets, result))
   2351 			return true;
   2352 	}
   2353 	return false;
   2354 }
   2355 
   2356 bool isGatherOffsetsResultValid (const Texture2DArrayView&		texture,
   2357 								 const Sampler&					sampler,
   2358 								 const LookupPrecision&			prec,
   2359 								 const Vec3&					coord,
   2360 								 int							componentNdx,
   2361 								 const IVec2					(&offsets)[4],
   2362 								 const Vec4&					result)
   2363 {
   2364 	return is2DArrayGatherOffsetsResultValid(texture, sampler, prec, coord, componentNdx, offsets, result);
   2365 }
   2366 
   2367 bool isGatherOffsetsResultValid (const Texture2DArrayView&		texture,
   2368 								 const Sampler&					sampler,
   2369 								 const IntLookupPrecision&		prec,
   2370 								 const Vec3&					coord,
   2371 								 int							componentNdx,
   2372 								 const IVec2					(&offsets)[4],
   2373 								 const IVec4&					result)
   2374 {
   2375 	return is2DArrayGatherOffsetsResultValid(texture, sampler, prec, coord, componentNdx, offsets, result);
   2376 }
   2377 
   2378 bool isGatherOffsetsResultValid (const Texture2DArrayView&		texture,
   2379 								 const Sampler&					sampler,
   2380 								 const IntLookupPrecision&		prec,
   2381 								 const Vec3&					coord,
   2382 								 int							componentNdx,
   2383 								 const IVec2					(&offsets)[4],
   2384 								 const UVec4&					result)
   2385 {
   2386 	return is2DArrayGatherOffsetsResultValid(texture, sampler, prec, coord, componentNdx, offsets, result);
   2387 }
   2388 
   2389 template<typename PrecType, typename ScalarType>
   2390 static bool isGatherResultValid (const TextureCubeView&			texture,
   2391 								 const Sampler&					sampler,
   2392 								 const PrecType&				prec,
   2393 								 const CubeFaceFloatCoords&		coords,
   2394 								 int							componentNdx,
   2395 								 const Vector<ScalarType, 4>&	result)
   2396 {
   2397 	const int	size		= texture.getLevelFace(0, coords.face).getWidth();
   2398 
   2399 	const Vec2	uBounds		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x());
   2400 	const Vec2	vBounds		= computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y());
   2401 
   2402 	// Integer coordinate bounds for (x0,y0) - without wrap mode
   2403 	const int	minI		= deFloorFloatToInt32(uBounds.x()-0.5f);
   2404 	const int	maxI		= deFloorFloatToInt32(uBounds.y()-0.5f);
   2405 	const int	minJ		= deFloorFloatToInt32(vBounds.x()-0.5f);
   2406 	const int	maxJ		= deFloorFloatToInt32(vBounds.y()-0.5f);
   2407 
   2408 	// Face accesses
   2409 	ConstPixelBufferAccess faces[CUBEFACE_LAST];
   2410 	for (int face = 0; face < CUBEFACE_LAST; face++)
   2411 		faces[face] = texture.getLevelFace(0, CubeFace(face));
   2412 
   2413 	for (int j = minJ; j <= maxJ; j++)
   2414 	{
   2415 		for (int i = minI; i <= maxI; i++)
   2416 		{
   2417 			static const IVec2 offsets[4] =
   2418 			{
   2419 				IVec2(0, 1),
   2420 				IVec2(1, 1),
   2421 				IVec2(1, 0),
   2422 				IVec2(0, 0)
   2423 			};
   2424 
   2425 			Vector<ScalarType, 4> color;
   2426 			for (int offNdx = 0; offNdx < 4; offNdx++)
   2427 			{
   2428 				const CubeFaceIntCoords c = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, i+offsets[offNdx].x(), j+offsets[offNdx].y()), size);
   2429 				// If any of samples is out of both edges, implementations can do pretty much anything according to spec.
   2430 				// \todo [2014-06-05 nuutti] Test the special case where all corner pixels have exactly the same color.
   2431 				//							 See also isSeamlessLinearSampleResultValid and similar.
   2432 				if (c.face == CUBEFACE_LAST)
   2433 					return true;
   2434 
   2435 				color[offNdx] = lookup<ScalarType>(faces[c.face], sampler, c.s, c.t, 0)[componentNdx];
   2436 			}
   2437 
   2438 			if (isColorValid(prec, color, result))
   2439 				return true;
   2440 		}
   2441 	}
   2442 
   2443 	return false;
   2444 }
   2445 
   2446 template <typename PrecType, typename ScalarType>
   2447 static bool isCubeGatherResultValid (const TextureCubeView&			texture,
   2448 									 const Sampler&					sampler,
   2449 									 const PrecType&				prec,
   2450 									 const Vec3&					coord,
   2451 									 int							componentNdx,
   2452 									 const Vector<ScalarType, 4>&	result)
   2453 {
   2454 	int			numPossibleFaces				= 0;
   2455 	CubeFace	possibleFaces[CUBEFACE_LAST];
   2456 
   2457 	getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
   2458 
   2459 	if (numPossibleFaces == 0)
   2460 		return true; // Result is undefined.
   2461 
   2462 	for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
   2463 	{
   2464 		const CubeFaceFloatCoords faceCoords(possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], coord));
   2465 
   2466 		if (isGatherResultValid(texture, sampler, prec, faceCoords, componentNdx, result))
   2467 			return true;
   2468 	}
   2469 
   2470 	return false;
   2471 }
   2472 
   2473 bool isGatherResultValid (const TextureCubeView&		texture,
   2474 								 const Sampler&				sampler,
   2475 								 const LookupPrecision&		prec,
   2476 								 const Vec3&				coord,
   2477 								 int						componentNdx,
   2478 								 const Vec4&				result)
   2479 {
   2480 	return isCubeGatherResultValid(texture, sampler, prec, coord, componentNdx, result);
   2481 }
   2482 
   2483 bool isGatherResultValid (const TextureCubeView&		texture,
   2484 								 const Sampler&				sampler,
   2485 								 const IntLookupPrecision&	prec,
   2486 								 const Vec3&				coord,
   2487 								 int						componentNdx,
   2488 								 const IVec4&				result)
   2489 {
   2490 	return isCubeGatherResultValid(texture, sampler, prec, coord, componentNdx, result);
   2491 }
   2492 
   2493 bool isGatherResultValid (const TextureCubeView&		texture,
   2494 								 const Sampler&				sampler,
   2495 								 const IntLookupPrecision&	prec,
   2496 								 const Vec3&				coord,
   2497 								 int						componentNdx,
   2498 								 const UVec4&				result)
   2499 {
   2500 	return isCubeGatherResultValid(texture, sampler, prec, coord, componentNdx, result);
   2501 }
   2502 
   2503 } // tcu
   2504