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