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