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