• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
isSamplerSupported(const Sampler & sampler)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 
coordsInBounds(const ConstPixelBufferAccess & access,int x,int y,int z)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 
isSRGB(TextureFormat format)55 static inline bool isSRGB (TextureFormat format)
56 {
57 	return format.order == TextureFormat::sRGB || format.order == TextureFormat::sRGBA;
58 }
59 
60 template<typename ScalarType>
lookup(const ConstPixelBufferAccess & access,const Sampler & sampler,int i,int j,int k)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<>
lookup(const ConstPixelBufferAccess & access,const Sampler & sampler,int i,int j,int k)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 
isColorValid(const LookupPrecision & prec,const Vec4 & ref,const Vec4 & result)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 
isColorValid(const IntLookupPrecision & prec,const IVec4 & ref,const IVec4 & result)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 
isColorValid(const IntLookupPrecision & prec,const UVec4 & ref,const UVec4 & result)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 
lookupQuad(ColorQuad & dst,const ConstPixelBufferAccess & level,const Sampler & sampler,int x0,int x1,int y0,int y1,int z)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 
lookupLine(ColorLine & dst,const ConstPixelBufferAccess & level,const Sampler & sampler,int x0,int x1,int y)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>
minComp(const Vector<T,Size> & vec)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>
maxComp(const Vector<T,Size> & vec)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 
computeBilinearSearchStepFromFloatLine(const LookupPrecision & prec,const ColorLine & line)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 
computeBilinearSearchStepFromFloatQuad(const LookupPrecision & prec,const ColorQuad & quad)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 
computeBilinearSearchStepForUnorm(const LookupPrecision & prec)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 
computeBilinearSearchStepForSnorm(const LookupPrecision & prec)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 
min(const ColorLine & line)198 static inline Vec4 min (const ColorLine& line)
199 {
200 	return min(line.p0, line.p1);
201 }
202 
max(const ColorLine & line)203 static inline Vec4 max (const ColorLine& line)
204 {
205 	return max(line.p0, line.p1);
206 }
207 
min(const ColorQuad & quad)208 static inline Vec4 min (const ColorQuad& quad)
209 {
210 	return min(quad.p00, min(quad.p10, min(quad.p01, quad.p11)));
211 }
212 
max(const ColorQuad & quad)213 static inline Vec4 max (const ColorQuad& quad)
214 {
215 	return max(quad.p00, max(quad.p10, max(quad.p01, quad.p11)));
216 }
217 
isInColorBounds(const LookupPrecision & prec,const ColorQuad & quad,const Vec4 & result)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 
isInColorBounds(const LookupPrecision & prec,const ColorQuad & quad0,const ColorQuad & quad1,const Vec4 & result)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 
isInColorBounds(const LookupPrecision & prec,const ColorLine & line0,const ColorLine & line1,const Vec4 & result)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 
isInColorBounds(const LookupPrecision & prec,const ColorQuad & quad00,const ColorQuad & quad01,const ColorQuad & quad10,const ColorQuad & quad11,const Vec4 & result)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 
isLinearRangeValid(const LookupPrecision & prec,const Vec4 & c0,const Vec4 & c1,const Vec2 & fBounds,const Vec4 & result)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 
isBilinearRangeValid(const LookupPrecision & prec,const ColorQuad & quad,const Vec2 & xBounds,const Vec2 & yBounds,const float searchStep,const Vec4 & result)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 
isTrilinearRangeValid(const LookupPrecision & prec,const ColorQuad & quad0,const ColorQuad & quad1,const Vec2 & xBounds,const Vec2 & yBounds,const Vec2 & zBounds,const float searchStep,const Vec4 & result)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 
is1DTrilinearFilterResultValid(const LookupPrecision & prec,const ColorLine & line0,const ColorLine & line1,const Vec2 & xBounds0,const Vec2 & xBounds1,const Vec2 & zBounds,const float searchStep,const Vec4 & result)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 
is2DTrilinearFilterResultValid(const LookupPrecision & prec,const ColorQuad & quad0,const ColorQuad & quad1,const Vec2 & xBounds0,const Vec2 & yBounds0,const Vec2 & xBounds1,const Vec2 & yBounds1,const Vec2 & zBounds,const float searchStep,const Vec4 & result)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 
is3DTrilinearFilterResultValid(const LookupPrecision & prec,const ColorQuad & quad00,const ColorQuad & quad01,const ColorQuad & quad10,const ColorQuad & quad11,const Vec2 & xBounds0,const Vec2 & yBounds0,const Vec2 & zBounds0,const Vec2 & xBounds1,const Vec2 & yBounds1,const Vec2 & zBounds1,const Vec2 & wBounds,const float searchStep,const Vec4 & result)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>
isNearestSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const PrecType & prec,const float coordX,const int coordY,const Vector<ScalarType,4> & result)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>
isNearestSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const PrecType & prec,const Vec2 & coord,const int coordZ,const Vector<ScalarType,4> & result)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>
isNearestSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const PrecType & prec,const Vec3 & coord,const Vector<ScalarType,4> & result)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 
isLinearSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const LookupPrecision & prec,const float coordX,const int coordY,const Vec4 & result)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 
isLinearSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec4 & result)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 
isLinearSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec4 & result)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 
isNearestMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const float coord,const int coordY,const Vec2 & fBounds,const Vec4 & result)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 
isNearestMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec2 & fBounds,const Vec4 & result)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 
isNearestMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & fBounds,const Vec4 & result)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 
isLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const float coordX,const int coordY,const Vec2 & fBounds,const Vec4 & result)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 
isLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec2 & fBounds,const Vec4 & result)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 
isLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & fBounds,const Vec4 & result)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 
isLevelSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const Sampler::FilterMode filterMode,const LookupPrecision & prec,const float coordX,const int coordY,const Vec4 & result)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 
isLevelSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const Sampler::FilterMode filterMode,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec4 & result)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 
isMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const Sampler::FilterMode levelFilter,const LookupPrecision & prec,const float coordX,const int coordY,const Vec2 & fBounds,const Vec4 & result)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 
isMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const Sampler::FilterMode levelFilter,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec2 & fBounds,const Vec4 & result)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 
isLookupResultValid(const Texture2DView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const Vec2 & lodBounds,const Vec4 & result)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 
isLookupResultValid(const Texture1DView & texture,const Sampler & sampler,const LookupPrecision & prec,const float coord,const Vec2 & lodBounds,const Vec4 & result)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 
isSeamlessLinearSampleResultValid(const ConstPixelBufferAccess (& faces)[CUBEFACE_LAST],const Sampler & sampler,const LookupPrecision & prec,const CubeFaceFloatCoords & coords,const Vec4 & result)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 
isSeamplessLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess (& faces0)[CUBEFACE_LAST],const ConstPixelBufferAccess (& faces1)[CUBEFACE_LAST],const Sampler & sampler,const LookupPrecision & prec,const CubeFaceFloatCoords & coords,const Vec2 & fBounds,const Vec4 & result)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 
isCubeLevelSampleResultValid(const ConstPixelBufferAccess (& level)[CUBEFACE_LAST],const Sampler & sampler,const Sampler::FilterMode filterMode,const LookupPrecision & prec,const CubeFaceFloatCoords & coords,const Vec4 & result)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 
isCubeMipmapLinearSampleResultValid(const ConstPixelBufferAccess (& faces0)[CUBEFACE_LAST],const ConstPixelBufferAccess (& faces1)[CUBEFACE_LAST],const Sampler & sampler,const Sampler::FilterMode levelFilter,const LookupPrecision & prec,const CubeFaceFloatCoords & coords,const Vec2 & fBounds,const Vec4 & result)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 
getCubeLevelFaces(const TextureCubeView & texture,const int levelNdx,ConstPixelBufferAccess (& out)[CUBEFACE_LAST])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 
isLookupResultValid(const TextureCubeView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & lodBounds,const Vec4 & result)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 
computeLayerRange(int numLayers,int numCoordBits,float layerCoord)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 
isLookupResultValid(const Texture1DArrayView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const Vec2 & lodBounds,const Vec4 & result)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 
isLookupResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & lodBounds,const Vec4 & result)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 
isLevelSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const Sampler::FilterMode filterMode,const LookupPrecision & prec,const Vec3 & coord,const Vec4 & result)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 
isMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const Sampler::FilterMode levelFilter,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & fBounds,const Vec4 & result)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 
isLookupResultValid(const Texture3DView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & lodBounds,const Vec4 & result)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 
getCubeArrayLevelFaces(const TextureCubeArrayView & texture,const int levelNdx,const int layerNdx,ConstPixelBufferAccess (& out)[CUBEFACE_LAST])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 
isLookupResultValid(const TextureCubeArrayView & texture,const Sampler & sampler,const LookupPrecision & prec,const IVec4 & coordBits,const Vec4 & coord,const Vec2 & lodBounds,const Vec4 & result)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 
computeFixedPointThreshold(const IVec4 & bits)2048 Vec4 computeFixedPointThreshold (const IVec4& bits)
2049 {
2050 	return computeFixedPointError(bits);
2051 }
2052 
computeFloatingPointThreshold(const IVec4 & bits,const Vec4 & value)2053 Vec4 computeFloatingPointThreshold (const IVec4& bits, const Vec4& value)
2054 {
2055 	return computeFloatingPointError(value, bits);
2056 }
2057 
computeLodBoundsFromDerivates(const float dudx,const float dvdx,const float dwdx,const float dudy,const float dvdy,const float dwdy,const LodPrecision & prec)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 
computeLodBoundsFromDerivates(const float dudx,const float dvdx,const float dudy,const float dvdy,const LodPrecision & prec)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 
computeLodBoundsFromDerivates(const float dudx,const float dudy,const LodPrecision & prec)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 
computeCubeLodBoundsFromDerivates(const Vec3 & coord,const Vec3 & coordDx,const Vec3 & coordDy,const int faceSize,const LodPrecision & prec)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 
clampLodBounds(const Vec2 & lodBounds,const Vec2 & lodMinMax,const LodPrecision & prec)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 
isLevel1DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const LookupPrecision & prec,const float coordX,const int coordY,const Vec4 & result)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 
isLevel1DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const float coordX,const int coordY,const IVec4 & result)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 
isLevel1DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const float coordX,const int coordY,const UVec4 & result)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 
isLevel2DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec4 & result)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 
isLevel2DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const Vec2 & coord,const int coordZ,const IVec4 & result)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 
isLevel2DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const Vec2 & coord,const int coordZ,const UVec4 & result)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 
isLevel3DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const LookupPrecision & prec,const Vec3 & coord,const Vec4 & result)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 
isLevel3DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const Vec3 & coord,const IVec4 & result)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 
isLevel3DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const Vec3 & coord,const UVec4 & result)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>
isGatherOffsetsResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const PrecType & prec,const Vec2 & coord,int coordZ,int componentNdx,const IVec2 (& offsets)[4],const Vector<ScalarType,4> & result)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 
isGatherOffsetsResultValid(const Texture2DView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,int componentNdx,const IVec2 (& offsets)[4],const Vec4 & result)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 
isGatherOffsetsResultValid(const Texture2DView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec2 & coord,int componentNdx,const IVec2 (& offsets)[4],const IVec4 & result)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 
isGatherOffsetsResultValid(const Texture2DView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec2 & coord,int componentNdx,const IVec2 (& offsets)[4],const UVec4 & result)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>
is2DArrayGatherOffsetsResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const PrecType & prec,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4],const Vector<ScalarType,4> & result)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 
isGatherOffsetsResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4],const Vec4 & result)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 
isGatherOffsetsResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4],const IVec4 & result)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 
isGatherOffsetsResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4],const UVec4 & result)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>
isGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const PrecType & prec,const CubeFaceFloatCoords & coords,int componentNdx,const Vector<ScalarType,4> & result)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>
isCubeGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const PrecType & prec,const Vec3 & coord,int componentNdx,const Vector<ScalarType,4> & result)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 
isGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,int componentNdx,const Vec4 & result)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 
isGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec3 & coord,int componentNdx,const IVec4 & result)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 
isGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec3 & coord,int componentNdx,const UVec4 & result)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