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