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