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