1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL (ES) Module 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 test utilities. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "glsTextureTestUtil.hpp" 25 #include "gluDefs.hpp" 26 #include "gluDrawUtil.hpp" 27 #include "gluRenderContext.hpp" 28 #include "deRandom.hpp" 29 #include "tcuTestLog.hpp" 30 #include "tcuVectorUtil.hpp" 31 #include "tcuTextureUtil.hpp" 32 #include "tcuImageCompare.hpp" 33 #include "tcuStringTemplate.hpp" 34 #include "tcuTexLookupVerifier.hpp" 35 #include "tcuTexCompareVerifier.hpp" 36 #include "glwEnums.hpp" 37 #include "glwFunctions.hpp" 38 #include "qpWatchDog.h" 39 #include "deStringUtil.hpp" 40 41 using tcu::TestLog; 42 using std::vector; 43 using std::string; 44 using std::map; 45 46 namespace deqp 47 { 48 namespace gls 49 { 50 namespace TextureTestUtil 51 { 52 53 enum 54 { 55 MIN_SUBPIXEL_BITS = 4 56 }; 57 58 SamplerType getSamplerType (tcu::TextureFormat format) 59 { 60 using tcu::TextureFormat; 61 62 switch (format.type) 63 { 64 case TextureFormat::SIGNED_INT8: 65 case TextureFormat::SIGNED_INT16: 66 case TextureFormat::SIGNED_INT32: 67 return SAMPLERTYPE_INT; 68 69 case TextureFormat::UNSIGNED_INT8: 70 case TextureFormat::UNSIGNED_INT32: 71 case TextureFormat::UNSIGNED_INT_1010102_REV: 72 return SAMPLERTYPE_UINT; 73 74 // Texture formats used in depth/stencil textures. 75 case TextureFormat::UNSIGNED_INT16: 76 case TextureFormat::UNSIGNED_INT_24_8: 77 return (format.order == TextureFormat::D || format.order == TextureFormat::DS) ? SAMPLERTYPE_FLOAT : SAMPLERTYPE_UINT; 78 79 default: 80 return SAMPLERTYPE_FLOAT; 81 } 82 } 83 84 SamplerType getFetchSamplerType (tcu::TextureFormat format) 85 { 86 using tcu::TextureFormat; 87 88 switch (format.type) 89 { 90 case TextureFormat::SIGNED_INT8: 91 case TextureFormat::SIGNED_INT16: 92 case TextureFormat::SIGNED_INT32: 93 return SAMPLERTYPE_FETCH_INT; 94 95 case TextureFormat::UNSIGNED_INT8: 96 case TextureFormat::UNSIGNED_INT32: 97 case TextureFormat::UNSIGNED_INT_1010102_REV: 98 return SAMPLERTYPE_FETCH_UINT; 99 100 // Texture formats used in depth/stencil textures. 101 case TextureFormat::UNSIGNED_INT16: 102 case TextureFormat::UNSIGNED_INT_24_8: 103 return (format.order == TextureFormat::D || format.order == TextureFormat::DS) ? SAMPLERTYPE_FETCH_FLOAT : SAMPLERTYPE_FETCH_UINT; 104 105 default: 106 return SAMPLERTYPE_FETCH_FLOAT; 107 } 108 } 109 110 static tcu::Texture1DView getSubView (const tcu::Texture1DView& view, int baseLevel, int maxLevel) 111 { 112 const int clampedBase = de::clamp(baseLevel, 0, view.getNumLevels()-1); 113 const int clampedMax = de::clamp(maxLevel, clampedBase, view.getNumLevels()-1); 114 const int numLevels = clampedMax-clampedBase+1; 115 return tcu::Texture1DView(numLevels, view.getLevels()+clampedBase); 116 } 117 118 static tcu::Texture2DView getSubView (const tcu::Texture2DView& view, int baseLevel, int maxLevel) 119 { 120 const int clampedBase = de::clamp(baseLevel, 0, view.getNumLevels()-1); 121 const int clampedMax = de::clamp(maxLevel, clampedBase, view.getNumLevels()-1); 122 const int numLevels = clampedMax-clampedBase+1; 123 return tcu::Texture2DView(numLevels, view.getLevels()+clampedBase); 124 } 125 126 static tcu::TextureCubeView getSubView (const tcu::TextureCubeView& view, int baseLevel, int maxLevel) 127 { 128 const int clampedBase = de::clamp(baseLevel, 0, view.getNumLevels()-1); 129 const int clampedMax = de::clamp(maxLevel, clampedBase, view.getNumLevels()-1); 130 const int numLevels = clampedMax-clampedBase+1; 131 const tcu::ConstPixelBufferAccess* levels[tcu::CUBEFACE_LAST]; 132 133 for (int face = 0; face < tcu::CUBEFACE_LAST; face++) 134 levels[face] = view.getFaceLevels((tcu::CubeFace)face) + clampedBase; 135 136 return tcu::TextureCubeView(numLevels, levels); 137 } 138 139 static tcu::Texture3DView getSubView (const tcu::Texture3DView& view, int baseLevel, int maxLevel) 140 { 141 const int clampedBase = de::clamp(baseLevel, 0, view.getNumLevels()-1); 142 const int clampedMax = de::clamp(maxLevel, clampedBase, view.getNumLevels()-1); 143 const int numLevels = clampedMax-clampedBase+1; 144 return tcu::Texture3DView(numLevels, view.getLevels()+clampedBase); 145 } 146 147 static tcu::TextureCubeArrayView getSubView (const tcu::TextureCubeArrayView& view, int baseLevel, int maxLevel) 148 { 149 const int clampedBase = de::clamp(baseLevel, 0, view.getNumLevels()-1); 150 const int clampedMax = de::clamp(maxLevel, clampedBase, view.getNumLevels()-1); 151 const int numLevels = clampedMax-clampedBase+1; 152 return tcu::TextureCubeArrayView(numLevels, view.getLevels()+clampedBase); 153 } 154 155 inline float linearInterpolate (float t, float minVal, float maxVal) 156 { 157 return minVal + (maxVal - minVal) * t; 158 } 159 160 inline tcu::Vec4 linearInterpolate (float t, const tcu::Vec4& a, const tcu::Vec4& b) 161 { 162 return a + (b - a) * t; 163 } 164 165 inline float bilinearInterpolate (float x, float y, const tcu::Vec4& quad) 166 { 167 float w00 = (1.0f-x)*(1.0f-y); 168 float w01 = (1.0f-x)*y; 169 float w10 = x*(1.0f-y); 170 float w11 = x*y; 171 return quad.x()*w00 + quad.y()*w10 + quad.z()*w01 + quad.w()*w11; 172 } 173 174 inline float triangleInterpolate (float v0, float v1, float v2, float x, float y) 175 { 176 return v0 + (v2-v0)*x + (v1-v0)*y; 177 } 178 179 inline float triangleInterpolate (const tcu::Vec3& v, float x, float y) 180 { 181 return triangleInterpolate(v.x(), v.y(), v.z(), x, y); 182 } 183 184 SurfaceAccess::SurfaceAccess (tcu::Surface& surface, const tcu::PixelFormat& colorFmt, int x, int y, int width, int height) 185 : m_surface (&surface) 186 , m_colorMask (getColorMask(colorFmt)) 187 , m_x (x) 188 , m_y (y) 189 , m_width (width) 190 , m_height (height) 191 { 192 } 193 194 SurfaceAccess::SurfaceAccess (tcu::Surface& surface, const tcu::PixelFormat& colorFmt) 195 : m_surface (&surface) 196 , m_colorMask (getColorMask(colorFmt)) 197 , m_x (0) 198 , m_y (0) 199 , m_width (surface.getWidth()) 200 , m_height (surface.getHeight()) 201 { 202 } 203 204 SurfaceAccess::SurfaceAccess (const SurfaceAccess& parent, int x, int y, int width, int height) 205 : m_surface (parent.m_surface) 206 , m_colorMask (parent.m_colorMask) 207 , m_x (parent.m_x + x) 208 , m_y (parent.m_y + y) 209 , m_width (width) 210 , m_height (height) 211 { 212 } 213 214 // 1D lookup LOD computation. 215 216 inline float computeLodFromDerivates (LodMode mode, float dudx, float dudy) 217 { 218 float p = 0.0f; 219 switch (mode) 220 { 221 // \note [mika] Min and max bounds equal to exact with 1D textures 222 case LODMODE_EXACT: 223 case LODMODE_MIN_BOUND: 224 case LODMODE_MAX_BOUND: 225 p = de::max(deFloatAbs(dudx), deFloatAbs(dudy)); 226 break; 227 228 default: 229 DE_ASSERT(DE_FALSE); 230 } 231 232 return deFloatLog2(p); 233 } 234 235 static float computeNonProjectedTriLod (LodMode mode, const tcu::IVec2& dstSize, deInt32 srcSize, const tcu::Vec3& sq) 236 { 237 float dux = (sq.z() - sq.x()) * (float)srcSize; 238 float duy = (sq.y() - sq.x()) * (float)srcSize; 239 float dx = (float)dstSize.x(); 240 float dy = (float)dstSize.y(); 241 242 return computeLodFromDerivates(mode, dux/dx, duy/dy); 243 } 244 245 // 2D lookup LOD computation. 246 247 inline float computeLodFromDerivates (LodMode mode, float dudx, float dvdx, float dudy, float dvdy) 248 { 249 float p = 0.0f; 250 switch (mode) 251 { 252 case LODMODE_EXACT: 253 p = de::max(deFloatSqrt(dudx*dudx + dvdx*dvdx), deFloatSqrt(dudy*dudy + dvdy*dvdy)); 254 break; 255 256 case LODMODE_MIN_BOUND: 257 case LODMODE_MAX_BOUND: 258 { 259 float mu = de::max(deFloatAbs(dudx), deFloatAbs(dudy)); 260 float mv = de::max(deFloatAbs(dvdx), deFloatAbs(dvdy)); 261 262 p = mode == LODMODE_MIN_BOUND ? de::max(mu, mv) : mu + mv; 263 break; 264 } 265 266 default: 267 DE_ASSERT(DE_FALSE); 268 } 269 270 return deFloatLog2(p); 271 } 272 273 static float computeNonProjectedTriLod (LodMode mode, const tcu::IVec2& dstSize, const tcu::IVec2& srcSize, const tcu::Vec3& sq, const tcu::Vec3& tq) 274 { 275 float dux = (sq.z() - sq.x()) * (float)srcSize.x(); 276 float duy = (sq.y() - sq.x()) * (float)srcSize.x(); 277 float dvx = (tq.z() - tq.x()) * (float)srcSize.y(); 278 float dvy = (tq.y() - tq.x()) * (float)srcSize.y(); 279 float dx = (float)dstSize.x(); 280 float dy = (float)dstSize.y(); 281 282 return computeLodFromDerivates(mode, dux/dx, dvx/dx, duy/dy, dvy/dy); 283 } 284 285 // 3D lookup LOD computation. 286 287 inline float computeLodFromDerivates (LodMode mode, float dudx, float dvdx, float dwdx, float dudy, float dvdy, float dwdy) 288 { 289 float p = 0.0f; 290 switch (mode) 291 { 292 case LODMODE_EXACT: 293 p = de::max(deFloatSqrt(dudx*dudx + dvdx*dvdx + dwdx*dwdx), deFloatSqrt(dudy*dudy + dvdy*dvdy + dwdy*dwdy)); 294 break; 295 296 case LODMODE_MIN_BOUND: 297 case LODMODE_MAX_BOUND: 298 { 299 float mu = de::max(deFloatAbs(dudx), deFloatAbs(dudy)); 300 float mv = de::max(deFloatAbs(dvdx), deFloatAbs(dvdy)); 301 float mw = de::max(deFloatAbs(dwdx), deFloatAbs(dwdy)); 302 303 p = mode == LODMODE_MIN_BOUND ? de::max(de::max(mu, mv), mw) : (mu + mv + mw); 304 break; 305 } 306 307 default: 308 DE_ASSERT(DE_FALSE); 309 } 310 311 return deFloatLog2(p); 312 } 313 314 static float computeNonProjectedTriLod (LodMode mode, const tcu::IVec2& dstSize, const tcu::IVec3& srcSize, const tcu::Vec3& sq, const tcu::Vec3& tq, const tcu::Vec3& rq) 315 { 316 float dux = (sq.z() - sq.x()) * (float)srcSize.x(); 317 float duy = (sq.y() - sq.x()) * (float)srcSize.x(); 318 float dvx = (tq.z() - tq.x()) * (float)srcSize.y(); 319 float dvy = (tq.y() - tq.x()) * (float)srcSize.y(); 320 float dwx = (rq.z() - rq.x()) * (float)srcSize.z(); 321 float dwy = (rq.y() - rq.x()) * (float)srcSize.z(); 322 float dx = (float)dstSize.x(); 323 float dy = (float)dstSize.y(); 324 325 return computeLodFromDerivates(mode, dux/dx, dvx/dx, dwx/dx, duy/dy, dvy/dy, dwy/dy); 326 } 327 328 static inline float projectedTriInterpolate (const tcu::Vec3& s, const tcu::Vec3& w, float nx, float ny) 329 { 330 return (s[0]*(1.0f-nx-ny)/w[0] + s[1]*ny/w[1] + s[2]*nx/w[2]) / ((1.0f-nx-ny)/w[0] + ny/w[1] + nx/w[2]); 331 } 332 333 static inline float triDerivateX (const tcu::Vec3& s, const tcu::Vec3& w, float wx, float width, float ny) 334 { 335 float d = w[1]*w[2]*(width*(ny - 1.0f) + wx) - w[0]*(w[2]*width*ny + w[1]*wx); 336 return (w[0]*w[1]*w[2]*width * (w[1]*(s[0] - s[2])*(ny - 1.0f) + ny*(w[2]*(s[1] - s[0]) + w[0]*(s[2] - s[1])))) / (d*d); 337 } 338 339 static inline float triDerivateY (const tcu::Vec3& s, const tcu::Vec3& w, float wy, float height, float nx) 340 { 341 float d = w[1]*w[2]*(height*(nx - 1.0f) + wy) - w[0]*(w[1]*height*nx + w[2]*wy); 342 return (w[0]*w[1]*w[2]*height * (w[2]*(s[0] - s[1])*(nx - 1.0f) + nx*(w[0]*(s[1] - s[2]) + w[1]*(s[2] - s[0])))) / (d*d); 343 } 344 345 // 1D lookup LOD. 346 static float computeProjectedTriLod (LodMode mode, const tcu::Vec3& u, const tcu::Vec3& projection, float wx, float wy, float width, float height) 347 { 348 // Exact derivatives. 349 float dudx = triDerivateX(u, projection, wx, width, wy/height); 350 float dudy = triDerivateY(u, projection, wy, height, wx/width); 351 352 return computeLodFromDerivates(mode, dudx, dudy); 353 } 354 355 // 2D lookup LOD. 356 static float computeProjectedTriLod (LodMode mode, const tcu::Vec3& u, const tcu::Vec3& v, const tcu::Vec3& projection, float wx, float wy, float width, float height) 357 { 358 // Exact derivatives. 359 float dudx = triDerivateX(u, projection, wx, width, wy/height); 360 float dvdx = triDerivateX(v, projection, wx, width, wy/height); 361 float dudy = triDerivateY(u, projection, wy, height, wx/width); 362 float dvdy = triDerivateY(v, projection, wy, height, wx/width); 363 364 return computeLodFromDerivates(mode, dudx, dvdx, dudy, dvdy); 365 } 366 367 // 3D lookup LOD. 368 static float computeProjectedTriLod (LodMode mode, const tcu::Vec3& u, const tcu::Vec3& v, const tcu::Vec3& w, const tcu::Vec3& projection, float wx, float wy, float width, float height) 369 { 370 // Exact derivatives. 371 float dudx = triDerivateX(u, projection, wx, width, wy/height); 372 float dvdx = triDerivateX(v, projection, wx, width, wy/height); 373 float dwdx = triDerivateX(w, projection, wx, width, wy/height); 374 float dudy = triDerivateY(u, projection, wy, height, wx/width); 375 float dvdy = triDerivateY(v, projection, wy, height, wx/width); 376 float dwdy = triDerivateY(w, projection, wy, height, wx/width); 377 378 return computeLodFromDerivates(mode, dudx, dvdx, dwdx, dudy, dvdy, dwdy); 379 } 380 381 static inline tcu::Vec4 execSample (const tcu::Texture1DView& src, const ReferenceParams& params, float s, float lod) 382 { 383 if (params.samplerType == SAMPLERTYPE_SHADOW) 384 return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, lod), 0.0, 0.0, 1.0f); 385 else 386 return src.sample(params.sampler, s, lod); 387 } 388 389 static inline tcu::Vec4 execSample (const tcu::Texture2DView& src, const ReferenceParams& params, float s, float t, float lod) 390 { 391 if (params.samplerType == SAMPLERTYPE_SHADOW) 392 return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, lod), 0.0, 0.0, 1.0f); 393 else 394 return src.sample(params.sampler, s, t, lod); 395 } 396 397 static inline tcu::Vec4 execSample (const tcu::TextureCubeView& src, const ReferenceParams& params, float s, float t, float r, float lod) 398 { 399 if (params.samplerType == SAMPLERTYPE_SHADOW) 400 return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, r, lod), 0.0, 0.0, 1.0f); 401 else 402 return src.sample(params.sampler, s, t, r, lod); 403 } 404 405 static inline tcu::Vec4 execSample (const tcu::Texture2DArrayView& src, const ReferenceParams& params, float s, float t, float r, float lod) 406 { 407 if (params.samplerType == SAMPLERTYPE_SHADOW) 408 return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, r, lod), 0.0, 0.0, 1.0f); 409 else 410 return src.sample(params.sampler, s, t, r, lod); 411 } 412 413 static inline tcu::Vec4 execSample (const tcu::TextureCubeArrayView& src, const ReferenceParams& params, float s, float t, float r, float q, float lod) 414 { 415 if (params.samplerType == SAMPLERTYPE_SHADOW) 416 return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, r, q, lod), 0.0, 0.0, 1.0f); 417 else 418 return src.sample(params.sampler, s, t, r, q, lod); 419 } 420 421 static inline tcu::Vec4 execSample (const tcu::Texture1DArrayView& src, const ReferenceParams& params, float s, float t, float lod) 422 { 423 if (params.samplerType == SAMPLERTYPE_SHADOW) 424 return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, lod), 0.0, 0.0, 1.0f); 425 else 426 return src.sample(params.sampler, s, t, lod); 427 } 428 429 static void sampleTextureNonProjected (const SurfaceAccess& dst, const tcu::Texture1DView& rawSrc, const tcu::Vec4& sq, const ReferenceParams& params) 430 { 431 // Separate combined DS formats 432 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 433 const tcu::Texture1DView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); 434 435 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; 436 437 tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight()); 438 int srcSize = src.getWidth(); 439 440 // Coordinates and lod per triangle. 441 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 442 float triLod[2] = { de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0]) + lodBias, params.minLod, params.maxLod), 443 de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1]) + lodBias, params.minLod, params.maxLod) }; 444 445 for (int y = 0; y < dst.getHeight(); y++) 446 { 447 for (int x = 0; x < dst.getWidth(); x++) 448 { 449 float yf = ((float)y + 0.5f) / (float)dst.getHeight(); 450 float xf = ((float)x + 0.5f) / (float)dst.getWidth(); 451 452 int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule. 453 float triX = triNdx ? 1.0f-xf : xf; 454 float triY = triNdx ? 1.0f-yf : yf; 455 456 float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY); 457 float lod = triLod[triNdx]; 458 459 dst.setPixel(execSample(src, params, s, lod) * params.colorScale + params.colorBias, x, y); 460 } 461 } 462 } 463 464 static void sampleTextureNonProjected (const SurfaceAccess& dst, const tcu::Texture2DView& rawSrc, const tcu::Vec4& sq, const tcu::Vec4& tq, const ReferenceParams& params) 465 { 466 // Separate combined DS formats 467 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 468 const tcu::Texture2DView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); 469 470 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; 471 472 tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight()); 473 tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight()); 474 475 // Coordinates and lod per triangle. 476 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 477 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 478 float triLod[2] = { de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0], triT[0]) + lodBias, params.minLod, params.maxLod), 479 de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1], triT[1]) + lodBias, params.minLod, params.maxLod) }; 480 481 for (int y = 0; y < dst.getHeight(); y++) 482 { 483 for (int x = 0; x < dst.getWidth(); x++) 484 { 485 float yf = ((float)y + 0.5f) / (float)dst.getHeight(); 486 float xf = ((float)x + 0.5f) / (float)dst.getWidth(); 487 488 int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule. 489 float triX = triNdx ? 1.0f-xf : xf; 490 float triY = triNdx ? 1.0f-yf : yf; 491 492 float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY); 493 float t = triangleInterpolate(triT[triNdx].x(), triT[triNdx].y(), triT[triNdx].z(), triX, triY); 494 float lod = triLod[triNdx]; 495 496 dst.setPixel(execSample(src, params, s, t, lod) * params.colorScale + params.colorBias, x, y); 497 } 498 } 499 } 500 501 static void sampleTextureProjected (const SurfaceAccess& dst, const tcu::Texture1DView& rawSrc, const tcu::Vec4& sq, const ReferenceParams& params) 502 { 503 // Separate combined DS formats 504 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 505 const tcu::Texture1DView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); 506 507 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; 508 float dstW = (float)dst.getWidth(); 509 float dstH = (float)dst.getHeight(); 510 511 tcu::Vec4 uq = sq * (float)src.getWidth(); 512 513 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 514 tcu::Vec3 triU[2] = { uq.swizzle(0, 1, 2), uq.swizzle(3, 2, 1) }; 515 tcu::Vec3 triW[2] = { params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1) }; 516 517 for (int py = 0; py < dst.getHeight(); py++) 518 { 519 for (int px = 0; px < dst.getWidth(); px++) 520 { 521 float wx = (float)px + 0.5f; 522 float wy = (float)py + 0.5f; 523 float nx = wx / dstW; 524 float ny = wy / dstH; 525 526 int triNdx = nx + ny >= 1.0f ? 1 : 0; 527 float triWx = triNdx ? dstW - wx : wx; 528 float triWy = triNdx ? dstH - wy : wy; 529 float triNx = triNdx ? 1.0f - nx : nx; 530 float triNy = triNdx ? 1.0f - ny : ny; 531 532 float s = projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy); 533 float lod = computeProjectedTriLod(params.lodMode, triU[triNdx], triW[triNdx], triWx, triWy, (float)dst.getWidth(), (float)dst.getHeight()) 534 + lodBias; 535 536 dst.setPixel(execSample(src, params, s, lod) * params.colorScale + params.colorBias, px, py); 537 } 538 } 539 } 540 541 static void sampleTextureProjected (const SurfaceAccess& dst, const tcu::Texture2DView& rawSrc, const tcu::Vec4& sq, const tcu::Vec4& tq, const ReferenceParams& params) 542 { 543 // Separate combined DS formats 544 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 545 const tcu::Texture2DView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); 546 547 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; 548 float dstW = (float)dst.getWidth(); 549 float dstH = (float)dst.getHeight(); 550 551 tcu::Vec4 uq = sq * (float)src.getWidth(); 552 tcu::Vec4 vq = tq * (float)src.getHeight(); 553 554 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 555 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 556 tcu::Vec3 triU[2] = { uq.swizzle(0, 1, 2), uq.swizzle(3, 2, 1) }; 557 tcu::Vec3 triV[2] = { vq.swizzle(0, 1, 2), vq.swizzle(3, 2, 1) }; 558 tcu::Vec3 triW[2] = { params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1) }; 559 560 for (int py = 0; py < dst.getHeight(); py++) 561 { 562 for (int px = 0; px < dst.getWidth(); px++) 563 { 564 float wx = (float)px + 0.5f; 565 float wy = (float)py + 0.5f; 566 float nx = wx / dstW; 567 float ny = wy / dstH; 568 569 int triNdx = nx + ny >= 1.0f ? 1 : 0; 570 float triWx = triNdx ? dstW - wx : wx; 571 float triWy = triNdx ? dstH - wy : wy; 572 float triNx = triNdx ? 1.0f - nx : nx; 573 float triNy = triNdx ? 1.0f - ny : ny; 574 575 float s = projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy); 576 float t = projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy); 577 float lod = computeProjectedTriLod(params.lodMode, triU[triNdx], triV[triNdx], triW[triNdx], triWx, triWy, (float)dst.getWidth(), (float)dst.getHeight()) 578 + lodBias; 579 580 dst.setPixel(execSample(src, params, s, t, lod) * params.colorScale + params.colorBias, px, py); 581 } 582 } 583 } 584 585 void sampleTexture (const SurfaceAccess& dst, const tcu::Texture2DView& src, const float* texCoord, const ReferenceParams& params) 586 { 587 const tcu::Texture2DView view = getSubView(src, params.baseLevel, params.maxLevel); 588 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[2+0], texCoord[4+0], texCoord[6+0]); 589 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[2+1], texCoord[4+1], texCoord[6+1]); 590 591 if (params.flags & ReferenceParams::PROJECTED) 592 sampleTextureProjected(dst, view, sq, tq, params); 593 else 594 sampleTextureNonProjected(dst, view, sq, tq, params); 595 } 596 597 void sampleTexture (const SurfaceAccess& dst, const tcu::Texture1DView& src, const float* texCoord, const ReferenceParams& params) 598 { 599 const tcu::Texture1DView view = getSubView(src, params.baseLevel, params.maxLevel); 600 const tcu::Vec4 sq = tcu::Vec4(texCoord[0], texCoord[1], texCoord[2], texCoord[3]); 601 602 if (params.flags & ReferenceParams::PROJECTED) 603 sampleTextureProjected(dst, view, sq, params); 604 else 605 sampleTextureNonProjected(dst, view, sq, params); 606 } 607 608 static float computeCubeLodFromDerivates (LodMode lodMode, const tcu::Vec3& coord, const tcu::Vec3& coordDx, const tcu::Vec3& coordDy, const int faceSize) 609 { 610 const tcu::CubeFace face = tcu::selectCubeFace(coord); 611 int maNdx = 0; 612 int sNdx = 0; 613 int tNdx = 0; 614 615 // \note Derivate signs don't matter when computing lod 616 switch (face) 617 { 618 case tcu::CUBEFACE_NEGATIVE_X: 619 case tcu::CUBEFACE_POSITIVE_X: maNdx = 0; sNdx = 2; tNdx = 1; break; 620 case tcu::CUBEFACE_NEGATIVE_Y: 621 case tcu::CUBEFACE_POSITIVE_Y: maNdx = 1; sNdx = 0; tNdx = 2; break; 622 case tcu::CUBEFACE_NEGATIVE_Z: 623 case tcu::CUBEFACE_POSITIVE_Z: maNdx = 2; sNdx = 0; tNdx = 1; break; 624 default: 625 DE_ASSERT(DE_FALSE); 626 } 627 628 { 629 const float sc = coord[sNdx]; 630 const float tc = coord[tNdx]; 631 const float ma = de::abs(coord[maNdx]); 632 const float scdx = coordDx[sNdx]; 633 const float tcdx = coordDx[tNdx]; 634 const float madx = de::abs(coordDx[maNdx]); 635 const float scdy = coordDy[sNdx]; 636 const float tcdy = coordDy[tNdx]; 637 const float mady = de::abs(coordDy[maNdx]); 638 const float dudx = float(faceSize) * 0.5f * (scdx*ma - sc*madx) / (ma*ma); 639 const float dvdx = float(faceSize) * 0.5f * (tcdx*ma - tc*madx) / (ma*ma); 640 const float dudy = float(faceSize) * 0.5f * (scdy*ma - sc*mady) / (ma*ma); 641 const float dvdy = float(faceSize) * 0.5f * (tcdy*ma - tc*mady) / (ma*ma); 642 643 return computeLodFromDerivates(lodMode, dudx, dvdx, dudy, dvdy); 644 } 645 } 646 647 static void sampleTextureCube (const SurfaceAccess& dst, const tcu::TextureCubeView& rawSrc, const tcu::Vec4& sq, const tcu::Vec4& tq, const tcu::Vec4& rq, const ReferenceParams& params) 648 { 649 // Separate combined DS formats 650 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 651 const tcu::TextureCubeView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); 652 653 const tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight()); 654 const float dstW = float(dstSize.x()); 655 const float dstH = float(dstSize.y()); 656 const int srcSize = src.getSize(); 657 658 // Coordinates per triangle. 659 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 660 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 661 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) }; 662 const tcu::Vec3 triW[2] = { params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1) }; 663 664 const float lodBias ((params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f); 665 666 for (int py = 0; py < dst.getHeight(); py++) 667 { 668 for (int px = 0; px < dst.getWidth(); px++) 669 { 670 const float wx = (float)px + 0.5f; 671 const float wy = (float)py + 0.5f; 672 const float nx = wx / dstW; 673 const float ny = wy / dstH; 674 675 const int triNdx = nx + ny >= 1.0f ? 1 : 0; 676 const float triNx = triNdx ? 1.0f - nx : nx; 677 const float triNy = triNdx ? 1.0f - ny : ny; 678 679 const tcu::Vec3 coord (triangleInterpolate(triS[triNdx], triNx, triNy), 680 triangleInterpolate(triT[triNdx], triNx, triNy), 681 triangleInterpolate(triR[triNdx], triNx, triNy)); 682 const tcu::Vec3 coordDx (triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), 683 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy), 684 triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)); 685 const tcu::Vec3 coordDy (triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), 686 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx), 687 triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)); 688 689 const float lod = de::clamp(computeCubeLodFromDerivates(params.lodMode, coord, coordDx, coordDy, srcSize) + lodBias, params.minLod, params.maxLod); 690 691 dst.setPixel(execSample(src, params, coord.x(), coord.y(), coord.z(), lod) * params.colorScale + params.colorBias, px, py); 692 } 693 } 694 } 695 696 void sampleTexture (const SurfaceAccess& dst, const tcu::TextureCubeView& src, const float* texCoord, const ReferenceParams& params) 697 { 698 const tcu::TextureCubeView view = getSubView(src, params.baseLevel, params.maxLevel); 699 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]); 700 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]); 701 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]); 702 703 return sampleTextureCube(dst, view, sq, tq, rq, params); 704 } 705 706 static void sampleTextureNonProjected (const SurfaceAccess& dst, const tcu::Texture2DArrayView& rawSrc, const tcu::Vec4& sq, const tcu::Vec4& tq, const tcu::Vec4& rq, const ReferenceParams& params) 707 { 708 // Separate combined DS formats 709 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 710 const tcu::Texture2DArrayView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); 711 712 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; 713 714 tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight()); 715 tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight()); 716 717 // Coordinates and lod per triangle. 718 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 719 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 720 tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) }; 721 float triLod[2] = { computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0], triT[0]) + lodBias, 722 computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1], triT[1]) + lodBias}; 723 724 for (int y = 0; y < dst.getHeight(); y++) 725 { 726 for (int x = 0; x < dst.getWidth(); x++) 727 { 728 float yf = ((float)y + 0.5f) / (float)dst.getHeight(); 729 float xf = ((float)x + 0.5f) / (float)dst.getWidth(); 730 731 int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule. 732 float triX = triNdx ? 1.0f-xf : xf; 733 float triY = triNdx ? 1.0f-yf : yf; 734 735 float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY); 736 float t = triangleInterpolate(triT[triNdx].x(), triT[triNdx].y(), triT[triNdx].z(), triX, triY); 737 float r = triangleInterpolate(triR[triNdx].x(), triR[triNdx].y(), triR[triNdx].z(), triX, triY); 738 float lod = triLod[triNdx]; 739 740 dst.setPixel(execSample(src, params, s, t, r, lod) * params.colorScale + params.colorBias, x, y); 741 } 742 } 743 } 744 745 void sampleTexture (const SurfaceAccess& dst, const tcu::Texture2DArrayView& src, const float* texCoord, const ReferenceParams& params) 746 { 747 tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]); 748 tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]); 749 tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]); 750 751 DE_ASSERT(!(params.flags & ReferenceParams::PROJECTED)); // \todo [2012-02-17 pyry] Support projected lookups. 752 sampleTextureNonProjected(dst, src, sq, tq, rq, params); 753 } 754 755 static void sampleTextureNonProjected (const SurfaceAccess& dst, const tcu::Texture1DArrayView& rawSrc, const tcu::Vec4& sq, const tcu::Vec4& tq, const ReferenceParams& params) 756 { 757 // Separate combined DS formats 758 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 759 const tcu::Texture1DArrayView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); 760 761 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; 762 763 tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight()); 764 deInt32 srcSize = src.getWidth(); 765 766 // Coordinates and lod per triangle. 767 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 768 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 769 float triLod[2] = { computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0]) + lodBias, 770 computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1]) + lodBias}; 771 772 for (int y = 0; y < dst.getHeight(); y++) 773 { 774 for (int x = 0; x < dst.getWidth(); x++) 775 { 776 float yf = ((float)y + 0.5f) / (float)dst.getHeight(); 777 float xf = ((float)x + 0.5f) / (float)dst.getWidth(); 778 779 int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule. 780 float triX = triNdx ? 1.0f-xf : xf; 781 float triY = triNdx ? 1.0f-yf : yf; 782 783 float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY); 784 float t = triangleInterpolate(triT[triNdx].x(), triT[triNdx].y(), triT[triNdx].z(), triX, triY); 785 float lod = triLod[triNdx]; 786 787 dst.setPixel(execSample(src, params, s, t, lod) * params.colorScale + params.colorBias, x, y); 788 } 789 } 790 } 791 792 void sampleTexture (const SurfaceAccess& dst, const tcu::Texture1DArrayView& src, const float* texCoord, const ReferenceParams& params) 793 { 794 tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[2+0], texCoord[4+0], texCoord[6+0]); 795 tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[2+1], texCoord[4+1], texCoord[6+1]); 796 797 DE_ASSERT(!(params.flags & ReferenceParams::PROJECTED)); // \todo [2014-06-09 mika] Support projected lookups. 798 sampleTextureNonProjected(dst, src, sq, tq, params); 799 } 800 801 static void sampleTextureNonProjected (const SurfaceAccess& dst, const tcu::Texture3DView& rawSrc, const tcu::Vec4& sq, const tcu::Vec4& tq, const tcu::Vec4& rq, const ReferenceParams& params) 802 { 803 // Separate combined DS formats 804 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 805 const tcu::Texture3DView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); 806 807 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; 808 809 tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight()); 810 tcu::IVec3 srcSize = tcu::IVec3(src.getWidth(), src.getHeight(), src.getDepth()); 811 812 // Coordinates and lod per triangle. 813 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 814 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 815 tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) }; 816 float triLod[2] = { de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0], triT[0], triR[0]) + lodBias, params.minLod, params.maxLod), 817 de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1], triT[1], triR[1]) + lodBias, params.minLod, params.maxLod) }; 818 819 for (int y = 0; y < dst.getHeight(); y++) 820 { 821 for (int x = 0; x < dst.getWidth(); x++) 822 { 823 float yf = ((float)y + 0.5f) / (float)dst.getHeight(); 824 float xf = ((float)x + 0.5f) / (float)dst.getWidth(); 825 826 int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule. 827 float triX = triNdx ? 1.0f-xf : xf; 828 float triY = triNdx ? 1.0f-yf : yf; 829 830 float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY); 831 float t = triangleInterpolate(triT[triNdx].x(), triT[triNdx].y(), triT[triNdx].z(), triX, triY); 832 float r = triangleInterpolate(triR[triNdx].x(), triR[triNdx].y(), triR[triNdx].z(), triX, triY); 833 float lod = triLod[triNdx]; 834 835 dst.setPixel(src.sample(params.sampler, s, t, r, lod) * params.colorScale + params.colorBias, x, y); 836 } 837 } 838 } 839 840 static void sampleTextureProjected (const SurfaceAccess& dst, const tcu::Texture3DView& rawSrc, const tcu::Vec4& sq, const tcu::Vec4& tq, const tcu::Vec4& rq, const ReferenceParams& params) 841 { 842 // Separate combined DS formats 843 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 844 const tcu::Texture3DView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); 845 846 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; 847 float dstW = (float)dst.getWidth(); 848 float dstH = (float)dst.getHeight(); 849 850 tcu::Vec4 uq = sq * (float)src.getWidth(); 851 tcu::Vec4 vq = tq * (float)src.getHeight(); 852 tcu::Vec4 wq = rq * (float)src.getDepth(); 853 854 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 855 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 856 tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) }; 857 tcu::Vec3 triU[2] = { uq.swizzle(0, 1, 2), uq.swizzle(3, 2, 1) }; 858 tcu::Vec3 triV[2] = { vq.swizzle(0, 1, 2), vq.swizzle(3, 2, 1) }; 859 tcu::Vec3 triW[2] = { wq.swizzle(0, 1, 2), wq.swizzle(3, 2, 1) }; 860 tcu::Vec3 triP[2] = { params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1) }; 861 862 for (int py = 0; py < dst.getHeight(); py++) 863 { 864 for (int px = 0; px < dst.getWidth(); px++) 865 { 866 float wx = (float)px + 0.5f; 867 float wy = (float)py + 0.5f; 868 float nx = wx / dstW; 869 float ny = wy / dstH; 870 871 int triNdx = nx + ny >= 1.0f ? 1 : 0; 872 float triWx = triNdx ? dstW - wx : wx; 873 float triWy = triNdx ? dstH - wy : wy; 874 float triNx = triNdx ? 1.0f - nx : nx; 875 float triNy = triNdx ? 1.0f - ny : ny; 876 877 float s = projectedTriInterpolate(triS[triNdx], triP[triNdx], triNx, triNy); 878 float t = projectedTriInterpolate(triT[triNdx], triP[triNdx], triNx, triNy); 879 float r = projectedTriInterpolate(triR[triNdx], triP[triNdx], triNx, triNy); 880 float lod = computeProjectedTriLod(params.lodMode, triU[triNdx], triV[triNdx], triW[triNdx], triP[triNdx], triWx, triWy, (float)dst.getWidth(), (float)dst.getHeight()) 881 + lodBias; 882 883 dst.setPixel(src.sample(params.sampler, s, t, r, lod) * params.colorScale + params.colorBias, px, py); 884 } 885 } 886 } 887 888 void sampleTexture (const SurfaceAccess& dst, const tcu::Texture3DView& src, const float* texCoord, const ReferenceParams& params) 889 { 890 const tcu::Texture3DView view = getSubView(src, params.baseLevel, params.maxLevel); 891 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]); 892 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]); 893 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]); 894 895 if (params.flags & ReferenceParams::PROJECTED) 896 sampleTextureProjected(dst, view, sq, tq, rq, params); 897 else 898 sampleTextureNonProjected(dst, view, sq, tq, rq, params); 899 } 900 901 static void sampleTextureCubeArray (const SurfaceAccess& dst, const tcu::TextureCubeArrayView& rawSrc, const tcu::Vec4& sq, const tcu::Vec4& tq, const tcu::Vec4& rq, const tcu::Vec4& qq, const ReferenceParams& params) 902 { 903 // Separate combined DS formats 904 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 905 const tcu::TextureCubeArrayView src = getEffectiveTextureView(rawSrc, srcLevelStorage, params.sampler); 906 907 const float dstW = (float)dst.getWidth(); 908 const float dstH = (float)dst.getHeight(); 909 910 // Coordinates per triangle. 911 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 912 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 913 tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) }; 914 tcu::Vec3 triQ[2] = { qq.swizzle(0, 1, 2), qq.swizzle(3, 2, 1) }; 915 const tcu::Vec3 triW[2] = { params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1) }; 916 917 const float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f; 918 919 for (int py = 0; py < dst.getHeight(); py++) 920 { 921 for (int px = 0; px < dst.getWidth(); px++) 922 { 923 const float wx = (float)px + 0.5f; 924 const float wy = (float)py + 0.5f; 925 const float nx = wx / dstW; 926 const float ny = wy / dstH; 927 928 const int triNdx = nx + ny >= 1.0f ? 1 : 0; 929 const float triNx = triNdx ? 1.0f - nx : nx; 930 const float triNy = triNdx ? 1.0f - ny : ny; 931 932 const tcu::Vec3 coord (triangleInterpolate(triS[triNdx], triNx, triNy), 933 triangleInterpolate(triT[triNdx], triNx, triNy), 934 triangleInterpolate(triR[triNdx], triNx, triNy)); 935 936 const float coordQ = triangleInterpolate(triQ[triNdx], triNx, triNy); 937 938 const tcu::Vec3 coordDx (triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), 939 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy), 940 triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)); 941 const tcu::Vec3 coordDy (triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), 942 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx), 943 triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)); 944 945 const float lod = de::clamp(computeCubeLodFromDerivates(params.lodMode, coord, coordDx, coordDy, src.getSize()) + lodBias, params.minLod, params.maxLod); 946 947 dst.setPixel(execSample(src, params, coord.x(), coord.y(), coord.z(), coordQ, lod) * params.colorScale + params.colorBias, px, py); 948 } 949 } 950 } 951 952 void sampleTexture (const SurfaceAccess& dst, const tcu::TextureCubeArrayView& src, const float* texCoord, const ReferenceParams& params) 953 { 954 tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[4+0], texCoord[8+0], texCoord[12+0]); 955 tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[4+1], texCoord[8+1], texCoord[12+1]); 956 tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[4+2], texCoord[8+2], texCoord[12+2]); 957 tcu::Vec4 qq = tcu::Vec4(texCoord[0+3], texCoord[4+3], texCoord[8+3], texCoord[12+3]); 958 959 sampleTextureCubeArray(dst, src, sq, tq, rq, qq, params); 960 } 961 962 void fetchTexture (const SurfaceAccess& dst, const tcu::ConstPixelBufferAccess& src, const float* texCoord, const tcu::Vec4& colorScale, const tcu::Vec4& colorBias) 963 { 964 const tcu::Vec4 sq = tcu::Vec4(texCoord[0], texCoord[1], texCoord[2], texCoord[3]); 965 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 966 967 for (int y = 0; y < dst.getHeight(); y++) 968 { 969 for (int x = 0; x < dst.getWidth(); x++) 970 { 971 const float yf = ((float)y + 0.5f) / (float)dst.getHeight(); 972 const float xf = ((float)x + 0.5f) / (float)dst.getWidth(); 973 974 const int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule. 975 const float triX = triNdx ? 1.0f-xf : xf; 976 const float triY = triNdx ? 1.0f-yf : yf; 977 978 const float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY); 979 980 dst.setPixel(src.getPixel((int)s, 0) * colorScale + colorBias, x, y); 981 } 982 } 983 } 984 985 void clear (const SurfaceAccess& dst, const tcu::Vec4& color) 986 { 987 for (int y = 0; y < dst.getHeight(); y++) 988 for (int x = 0; x < dst.getWidth(); x++) 989 dst.setPixel(color, x, y); 990 } 991 992 bool compareImages (TestLog& log, const tcu::Surface& reference, const tcu::Surface& rendered, tcu::RGBA threshold) 993 { 994 return tcu::pixelThresholdCompare(log, "Result", "Image comparison result", reference, rendered, threshold, tcu::COMPARE_LOG_RESULT); 995 } 996 997 bool compareImages (TestLog& log, const char* name, const char* desc, const tcu::Surface& reference, const tcu::Surface& rendered, tcu::RGBA threshold) 998 { 999 return tcu::pixelThresholdCompare(log, name, desc, reference, rendered, threshold, tcu::COMPARE_LOG_RESULT); 1000 } 1001 1002 int measureAccuracy (tcu::TestLog& log, const tcu::Surface& reference, const tcu::Surface& rendered, int bestScoreDiff, int worstScoreDiff) 1003 { 1004 return tcu::measurePixelDiffAccuracy(log, "Result", "Image comparison result", reference, rendered, bestScoreDiff, worstScoreDiff, tcu::COMPARE_LOG_EVERYTHING); 1005 } 1006 1007 inline int rangeDiff (int x, int a, int b) 1008 { 1009 if (x < a) 1010 return a-x; 1011 else if (x > b) 1012 return x-b; 1013 else 1014 return 0; 1015 } 1016 1017 inline tcu::RGBA rangeDiff (tcu::RGBA p, tcu::RGBA a, tcu::RGBA b) 1018 { 1019 int rMin = de::min(a.getRed(), b.getRed()); 1020 int rMax = de::max(a.getRed(), b.getRed()); 1021 int gMin = de::min(a.getGreen(), b.getGreen()); 1022 int gMax = de::max(a.getGreen(), b.getGreen()); 1023 int bMin = de::min(a.getBlue(), b.getBlue()); 1024 int bMax = de::max(a.getBlue(), b.getBlue()); 1025 int aMin = de::min(a.getAlpha(), b.getAlpha()); 1026 int aMax = de::max(a.getAlpha(), b.getAlpha()); 1027 1028 return tcu::RGBA(rangeDiff(p.getRed(), rMin, rMax), 1029 rangeDiff(p.getGreen(), gMin, gMax), 1030 rangeDiff(p.getBlue(), bMin, bMax), 1031 rangeDiff(p.getAlpha(), aMin, aMax)); 1032 } 1033 1034 inline bool rangeCompare (tcu::RGBA p, tcu::RGBA a, tcu::RGBA b, tcu::RGBA threshold) 1035 { 1036 tcu::RGBA diff = rangeDiff(p, a, b); 1037 return diff.getRed() <= threshold.getRed() && 1038 diff.getGreen() <= threshold.getGreen() && 1039 diff.getBlue() <= threshold.getBlue() && 1040 diff.getAlpha() <= threshold.getAlpha(); 1041 } 1042 1043 RandomViewport::RandomViewport (const tcu::RenderTarget& renderTarget, int preferredWidth, int preferredHeight, deUint32 seed) 1044 : x (0) 1045 , y (0) 1046 , width (deMin32(preferredWidth, renderTarget.getWidth())) 1047 , height (deMin32(preferredHeight, renderTarget.getHeight())) 1048 { 1049 de::Random rnd(seed); 1050 x = rnd.getInt(0, renderTarget.getWidth() - width); 1051 y = rnd.getInt(0, renderTarget.getHeight() - height); 1052 } 1053 1054 ProgramLibrary::ProgramLibrary (const glu::RenderContext& context, tcu::TestLog& log, glu::GLSLVersion glslVersion, glu::Precision texCoordPrecision) 1055 : m_context (context) 1056 , m_log (log) 1057 , m_glslVersion (glslVersion) 1058 , m_texCoordPrecision (texCoordPrecision) 1059 { 1060 } 1061 1062 ProgramLibrary::~ProgramLibrary (void) 1063 { 1064 clear(); 1065 } 1066 1067 void ProgramLibrary::clear (void) 1068 { 1069 for (map<Program, glu::ShaderProgram*>::iterator i = m_programs.begin(); i != m_programs.end(); i++) 1070 { 1071 delete i->second; 1072 i->second = DE_NULL; 1073 } 1074 m_programs.clear(); 1075 } 1076 1077 glu::ShaderProgram* ProgramLibrary::getProgram (Program program) 1078 { 1079 if (m_programs.find(program) != m_programs.end()) 1080 return m_programs[program]; // Return from cache. 1081 1082 static const char* vertShaderTemplate = 1083 "${VTX_HEADER}" 1084 "${VTX_IN} highp vec4 a_position;\n" 1085 "${VTX_IN} ${PRECISION} ${TEXCOORD_TYPE} a_texCoord;\n" 1086 "${VTX_OUT} ${PRECISION} ${TEXCOORD_TYPE} v_texCoord;\n" 1087 "\n" 1088 "void main (void)\n" 1089 "{\n" 1090 " gl_Position = a_position;\n" 1091 " v_texCoord = a_texCoord;\n" 1092 "}\n"; 1093 static const char* fragShaderTemplate = 1094 "${FRAG_HEADER}" 1095 "${FRAG_IN} ${PRECISION} ${TEXCOORD_TYPE} v_texCoord;\n" 1096 "uniform ${PRECISION} float u_bias;\n" 1097 "uniform ${PRECISION} float u_ref;\n" 1098 "uniform ${PRECISION} vec4 u_colorScale;\n" 1099 "uniform ${PRECISION} vec4 u_colorBias;\n" 1100 "uniform ${PRECISION} ${SAMPLER_TYPE} u_sampler;\n" 1101 "\n" 1102 "void main (void)\n" 1103 "{\n" 1104 " ${FRAG_COLOR} = ${LOOKUP} * u_colorScale + u_colorBias;\n" 1105 "}\n"; 1106 1107 map<string, string> params; 1108 1109 bool isCube = de::inRange<int>(program, PROGRAM_CUBE_FLOAT, PROGRAM_CUBE_SHADOW_BIAS); 1110 bool isArray = de::inRange<int>(program, PROGRAM_2D_ARRAY_FLOAT, PROGRAM_2D_ARRAY_SHADOW) 1111 || de::inRange<int>(program, PROGRAM_1D_ARRAY_FLOAT, PROGRAM_1D_ARRAY_SHADOW); 1112 1113 bool is1D = de::inRange<int>(program, PROGRAM_1D_FLOAT, PROGRAM_1D_UINT_BIAS) 1114 || de::inRange<int>(program, PROGRAM_1D_ARRAY_FLOAT, PROGRAM_1D_ARRAY_SHADOW) 1115 || de::inRange<int>(program, PROGRAM_BUFFER_FLOAT, PROGRAM_BUFFER_UINT); 1116 1117 bool is2D = de::inRange<int>(program, PROGRAM_2D_FLOAT, PROGRAM_2D_UINT_BIAS) 1118 || de::inRange<int>(program, PROGRAM_2D_ARRAY_FLOAT, PROGRAM_2D_ARRAY_SHADOW); 1119 1120 bool is3D = de::inRange<int>(program, PROGRAM_3D_FLOAT, PROGRAM_3D_UINT_BIAS); 1121 bool isCubeArray = de::inRange<int>(program, PROGRAM_CUBE_ARRAY_FLOAT, PROGRAM_CUBE_ARRAY_SHADOW); 1122 bool isBuffer = de::inRange<int>(program, PROGRAM_BUFFER_FLOAT, PROGRAM_BUFFER_UINT); 1123 1124 if (m_glslVersion == glu::GLSL_VERSION_100_ES) 1125 { 1126 params["FRAG_HEADER"] = ""; 1127 params["VTX_HEADER"] = ""; 1128 params["VTX_IN"] = "attribute"; 1129 params["VTX_OUT"] = "varying"; 1130 params["FRAG_IN"] = "varying"; 1131 params["FRAG_COLOR"] = "gl_FragColor"; 1132 } 1133 else if (m_glslVersion == glu::GLSL_VERSION_300_ES || m_glslVersion == glu::GLSL_VERSION_310_ES || m_glslVersion == glu::GLSL_VERSION_330) 1134 { 1135 const string version = glu::getGLSLVersionDeclaration(m_glslVersion); 1136 const char* ext = DE_NULL; 1137 1138 if (isCubeArray && glu::glslVersionIsES(m_glslVersion)) 1139 ext = "GL_EXT_texture_cube_map_array"; 1140 else if (isBuffer && glu::glslVersionIsES(m_glslVersion)) 1141 ext = "GL_EXT_texture_buffer"; 1142 1143 params["FRAG_HEADER"] = version + (ext ? string("\n#extension ") + ext + " : require" : string()) + "\nlayout(location = 0) out mediump vec4 dEQP_FragColor;\n"; 1144 params["VTX_HEADER"] = version + "\n"; 1145 params["VTX_IN"] = "in"; 1146 params["VTX_OUT"] = "out"; 1147 params["FRAG_IN"] = "in"; 1148 params["FRAG_COLOR"] = "dEQP_FragColor"; 1149 } 1150 else 1151 DE_ASSERT(!"Unsupported version"); 1152 1153 params["PRECISION"] = glu::getPrecisionName(m_texCoordPrecision); 1154 1155 if (isCubeArray) 1156 params["TEXCOORD_TYPE"] = "vec4"; 1157 else if (isCube || (is2D && isArray) || is3D) 1158 params["TEXCOORD_TYPE"] = "vec3"; 1159 else if ((is1D && isArray) || is2D) 1160 params["TEXCOORD_TYPE"] = "vec2"; 1161 else if (is1D) 1162 params["TEXCOORD_TYPE"] = "float"; 1163 else 1164 DE_ASSERT(DE_FALSE); 1165 1166 const char* sampler = DE_NULL; 1167 const char* lookup = DE_NULL; 1168 1169 if (m_glslVersion == glu::GLSL_VERSION_300_ES || m_glslVersion == glu::GLSL_VERSION_310_ES || m_glslVersion == glu::GLSL_VERSION_330) 1170 { 1171 switch (program) 1172 { 1173 case PROGRAM_2D_FLOAT: sampler = "sampler2D"; lookup = "texture(u_sampler, v_texCoord)"; break; 1174 case PROGRAM_2D_INT: sampler = "isampler2D"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1175 case PROGRAM_2D_UINT: sampler = "usampler2D"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1176 case PROGRAM_2D_SHADOW: sampler = "sampler2DShadow"; lookup = "vec4(texture(u_sampler, vec3(v_texCoord, u_ref)), 0.0, 0.0, 1.0)"; break; 1177 case PROGRAM_2D_FLOAT_BIAS: sampler = "sampler2D"; lookup = "texture(u_sampler, v_texCoord, u_bias)"; break; 1178 case PROGRAM_2D_INT_BIAS: sampler = "isampler2D"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break; 1179 case PROGRAM_2D_UINT_BIAS: sampler = "usampler2D"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break; 1180 case PROGRAM_2D_SHADOW_BIAS: sampler = "sampler2DShadow"; lookup = "vec4(texture(u_sampler, vec3(v_texCoord, u_ref), u_bias), 0.0, 0.0, 1.0)"; break; 1181 case PROGRAM_1D_FLOAT: sampler = "sampler1D"; lookup = "texture(u_sampler, v_texCoord)"; break; 1182 case PROGRAM_1D_INT: sampler = "isampler1D"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1183 case PROGRAM_1D_UINT: sampler = "usampler1D"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1184 case PROGRAM_1D_SHADOW: sampler = "sampler1DShadow"; lookup = "vec4(texture(u_sampler, vec3(v_texCoord, u_ref)), 0.0, 0.0, 1.0)"; break; 1185 case PROGRAM_1D_FLOAT_BIAS: sampler = "sampler1D"; lookup = "texture(u_sampler, v_texCoord, u_bias)"; break; 1186 case PROGRAM_1D_INT_BIAS: sampler = "isampler1D"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break; 1187 case PROGRAM_1D_UINT_BIAS: sampler = "usampler1D"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break; 1188 case PROGRAM_1D_SHADOW_BIAS: sampler = "sampler1DShadow"; lookup = "vec4(texture(u_sampler, vec3(v_texCoord, u_ref), u_bias), 0.0, 0.0, 1.0)"; break; 1189 case PROGRAM_CUBE_FLOAT: sampler = "samplerCube"; lookup = "texture(u_sampler, v_texCoord)"; break; 1190 case PROGRAM_CUBE_INT: sampler = "isamplerCube"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1191 case PROGRAM_CUBE_UINT: sampler = "usamplerCube"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1192 case PROGRAM_CUBE_SHADOW: sampler = "samplerCubeShadow"; lookup = "vec4(texture(u_sampler, vec4(v_texCoord, u_ref)), 0.0, 0.0, 1.0)"; break; 1193 case PROGRAM_CUBE_FLOAT_BIAS: sampler = "samplerCube"; lookup = "texture(u_sampler, v_texCoord, u_bias)"; break; 1194 case PROGRAM_CUBE_INT_BIAS: sampler = "isamplerCube"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break; 1195 case PROGRAM_CUBE_UINT_BIAS: sampler = "usamplerCube"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break; 1196 case PROGRAM_CUBE_SHADOW_BIAS: sampler = "samplerCubeShadow"; lookup = "vec4(texture(u_sampler, vec4(v_texCoord, u_ref), u_bias), 0.0, 0.0, 1.0)"; break; 1197 case PROGRAM_2D_ARRAY_FLOAT: sampler = "sampler2DArray"; lookup = "texture(u_sampler, v_texCoord)"; break; 1198 case PROGRAM_2D_ARRAY_INT: sampler = "isampler2DArray"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1199 case PROGRAM_2D_ARRAY_UINT: sampler = "usampler2DArray"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1200 case PROGRAM_2D_ARRAY_SHADOW: sampler = "sampler2DArrayShadow"; lookup = "vec4(texture(u_sampler, vec4(v_texCoord, u_ref)), 0.0, 0.0, 1.0)"; break; 1201 case PROGRAM_3D_FLOAT: sampler = "sampler3D"; lookup = "texture(u_sampler, v_texCoord)"; break; 1202 case PROGRAM_3D_INT: sampler = "isampler3D"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1203 case PROGRAM_3D_UINT: sampler = "usampler3D"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1204 case PROGRAM_3D_FLOAT_BIAS: sampler = "sampler3D"; lookup = "texture(u_sampler, v_texCoord, u_bias)"; break; 1205 case PROGRAM_3D_INT_BIAS: sampler = "isampler3D"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break; 1206 case PROGRAM_3D_UINT_BIAS: sampler = "usampler3D"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break; 1207 case PROGRAM_CUBE_ARRAY_FLOAT: sampler = "samplerCubeArray"; lookup = "texture(u_sampler, v_texCoord)"; break; 1208 case PROGRAM_CUBE_ARRAY_INT: sampler = "isamplerCubeArray"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1209 case PROGRAM_CUBE_ARRAY_UINT: sampler = "usamplerCubeArray"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1210 case PROGRAM_CUBE_ARRAY_SHADOW: sampler = "samplerCubeArrayShadow"; lookup = "vec4(texture(u_sampler, vec4(v_texCoord, u_ref)), 0.0, 0.0, 1.0)"; break; 1211 case PROGRAM_1D_ARRAY_FLOAT: sampler = "sampler1DArray"; lookup = "texture(u_sampler, v_texCoord)"; break; 1212 case PROGRAM_1D_ARRAY_INT: sampler = "isampler1DArray"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1213 case PROGRAM_1D_ARRAY_UINT: sampler = "usampler1DArray"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break; 1214 case PROGRAM_1D_ARRAY_SHADOW: sampler = "sampler1DArrayShadow"; lookup = "vec4(texture(u_sampler, vec4(v_texCoord, u_ref)), 0.0, 0.0, 1.0)"; break; 1215 case PROGRAM_BUFFER_FLOAT: sampler = "samplerBuffer"; lookup = "texelFetch(u_sampler, int(v_texCoord))"; break; 1216 case PROGRAM_BUFFER_INT: sampler = "isamplerBuffer"; lookup = "vec4(texelFetch(u_sampler, int(v_texCoord)))"; break; 1217 case PROGRAM_BUFFER_UINT: sampler = "usamplerBuffer"; lookup = "vec4(texelFetch(u_sampler, int(v_texCoord)))"; break; 1218 default: 1219 DE_ASSERT(false); 1220 } 1221 } 1222 else if (m_glslVersion == glu::GLSL_VERSION_100_ES) 1223 { 1224 sampler = isCube ? "samplerCube" : "sampler2D"; 1225 1226 switch (program) 1227 { 1228 case PROGRAM_2D_FLOAT: lookup = "texture2D(u_sampler, v_texCoord)"; break; 1229 case PROGRAM_2D_FLOAT_BIAS: lookup = "texture2D(u_sampler, v_texCoord, u_bias)"; break; 1230 case PROGRAM_CUBE_FLOAT: lookup = "textureCube(u_sampler, v_texCoord)"; break; 1231 case PROGRAM_CUBE_FLOAT_BIAS: lookup = "textureCube(u_sampler, v_texCoord, u_bias)"; break; 1232 default: 1233 DE_ASSERT(false); 1234 } 1235 } 1236 else 1237 DE_ASSERT(!"Unsupported version"); 1238 1239 params["SAMPLER_TYPE"] = sampler; 1240 params["LOOKUP"] = lookup; 1241 1242 std::string vertSrc = tcu::StringTemplate(vertShaderTemplate).specialize(params); 1243 std::string fragSrc = tcu::StringTemplate(fragShaderTemplate).specialize(params); 1244 1245 glu::ShaderProgram* progObj = new glu::ShaderProgram(m_context, glu::makeVtxFragSources(vertSrc, fragSrc)); 1246 if (!progObj->isOk()) 1247 { 1248 m_log << *progObj; 1249 delete progObj; 1250 TCU_FAIL("Failed to compile shader program"); 1251 } 1252 1253 try 1254 { 1255 m_programs[program] = progObj; 1256 } 1257 catch (...) 1258 { 1259 delete progObj; 1260 throw; 1261 } 1262 1263 return progObj; 1264 } 1265 1266 TextureRenderer::TextureRenderer (const glu::RenderContext& context, tcu::TestLog& log, glu::GLSLVersion glslVersion, glu::Precision texCoordPrecision) 1267 : m_renderCtx (context) 1268 , m_log (log) 1269 , m_programLibrary (context, log, glslVersion, texCoordPrecision) 1270 { 1271 } 1272 1273 TextureRenderer::~TextureRenderer (void) 1274 { 1275 clear(); 1276 } 1277 1278 void TextureRenderer::clear (void) 1279 { 1280 m_programLibrary.clear(); 1281 } 1282 1283 void TextureRenderer::renderQuad (int texUnit, const float* texCoord, TextureType texType) 1284 { 1285 renderQuad(texUnit, texCoord, RenderParams(texType)); 1286 } 1287 1288 void TextureRenderer::renderQuad (int texUnit, const float* texCoord, const RenderParams& params) 1289 { 1290 const glw::Functions& gl = m_renderCtx.getFunctions(); 1291 tcu::Vec4 wCoord = params.flags & RenderParams::PROJECTED ? params.w : tcu::Vec4(1.0f); 1292 bool useBias = !!(params.flags & RenderParams::USE_BIAS); 1293 bool logUniforms = !!(params.flags & RenderParams::LOG_UNIFORMS); 1294 1295 // Render quad with texture. 1296 float position[] = 1297 { 1298 -1.0f*wCoord.x(), -1.0f*wCoord.x(), 0.0f, wCoord.x(), 1299 -1.0f*wCoord.y(), +1.0f*wCoord.y(), 0.0f, wCoord.y(), 1300 +1.0f*wCoord.z(), -1.0f*wCoord.z(), 0.0f, wCoord.z(), 1301 +1.0f*wCoord.w(), +1.0f*wCoord.w(), 0.0f, wCoord.w() 1302 }; 1303 static const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 }; 1304 1305 Program progSpec = PROGRAM_LAST; 1306 int numComps = 0; 1307 if (params.texType == TEXTURETYPE_2D) 1308 { 1309 numComps = 2; 1310 1311 switch (params.samplerType) 1312 { 1313 case SAMPLERTYPE_FLOAT: progSpec = useBias ? PROGRAM_2D_FLOAT_BIAS : PROGRAM_2D_FLOAT; break; 1314 case SAMPLERTYPE_INT: progSpec = useBias ? PROGRAM_2D_INT_BIAS : PROGRAM_2D_INT; break; 1315 case SAMPLERTYPE_UINT: progSpec = useBias ? PROGRAM_2D_UINT_BIAS : PROGRAM_2D_UINT; break; 1316 case SAMPLERTYPE_SHADOW: progSpec = useBias ? PROGRAM_2D_SHADOW_BIAS : PROGRAM_2D_SHADOW; break; 1317 default: DE_ASSERT(false); 1318 } 1319 } 1320 else if (params.texType == TEXTURETYPE_1D) 1321 { 1322 numComps = 1; 1323 1324 switch (params.samplerType) 1325 { 1326 case SAMPLERTYPE_FLOAT: progSpec = useBias ? PROGRAM_1D_FLOAT_BIAS : PROGRAM_1D_FLOAT; break; 1327 case SAMPLERTYPE_INT: progSpec = useBias ? PROGRAM_1D_INT_BIAS : PROGRAM_1D_INT; break; 1328 case SAMPLERTYPE_UINT: progSpec = useBias ? PROGRAM_1D_UINT_BIAS : PROGRAM_1D_UINT; break; 1329 case SAMPLERTYPE_SHADOW: progSpec = useBias ? PROGRAM_1D_SHADOW_BIAS : PROGRAM_1D_SHADOW; break; 1330 default: DE_ASSERT(false); 1331 } 1332 } 1333 else if (params.texType == TEXTURETYPE_CUBE) 1334 { 1335 numComps = 3; 1336 1337 switch (params.samplerType) 1338 { 1339 case SAMPLERTYPE_FLOAT: progSpec = useBias ? PROGRAM_CUBE_FLOAT_BIAS : PROGRAM_CUBE_FLOAT; break; 1340 case SAMPLERTYPE_INT: progSpec = useBias ? PROGRAM_CUBE_INT_BIAS : PROGRAM_CUBE_INT; break; 1341 case SAMPLERTYPE_UINT: progSpec = useBias ? PROGRAM_CUBE_UINT_BIAS : PROGRAM_CUBE_UINT; break; 1342 case SAMPLERTYPE_SHADOW: progSpec = useBias ? PROGRAM_CUBE_SHADOW_BIAS : PROGRAM_CUBE_SHADOW; break; 1343 default: DE_ASSERT(false); 1344 } 1345 } 1346 else if (params.texType == TEXTURETYPE_3D) 1347 { 1348 numComps = 3; 1349 1350 switch (params.samplerType) 1351 { 1352 case SAMPLERTYPE_FLOAT: progSpec = useBias ? PROGRAM_3D_FLOAT_BIAS : PROGRAM_3D_FLOAT; break; 1353 case SAMPLERTYPE_INT: progSpec = useBias ? PROGRAM_3D_INT_BIAS : PROGRAM_3D_INT; break; 1354 case SAMPLERTYPE_UINT: progSpec = useBias ? PROGRAM_3D_UINT_BIAS : PROGRAM_3D_UINT; break; 1355 default: DE_ASSERT(false); 1356 } 1357 } 1358 else if (params.texType == TEXTURETYPE_2D_ARRAY) 1359 { 1360 DE_ASSERT(!useBias); // \todo [2012-02-17 pyry] Support bias. 1361 1362 numComps = 3; 1363 1364 switch (params.samplerType) 1365 { 1366 case SAMPLERTYPE_FLOAT: progSpec = PROGRAM_2D_ARRAY_FLOAT; break; 1367 case SAMPLERTYPE_INT: progSpec = PROGRAM_2D_ARRAY_INT; break; 1368 case SAMPLERTYPE_UINT: progSpec = PROGRAM_2D_ARRAY_UINT; break; 1369 case SAMPLERTYPE_SHADOW: progSpec = PROGRAM_2D_ARRAY_SHADOW; break; 1370 default: DE_ASSERT(false); 1371 } 1372 } 1373 else if (params.texType == TEXTURETYPE_CUBE_ARRAY) 1374 { 1375 DE_ASSERT(!useBias); 1376 1377 numComps = 4; 1378 1379 switch (params.samplerType) 1380 { 1381 case SAMPLERTYPE_FLOAT: progSpec = PROGRAM_CUBE_ARRAY_FLOAT; break; 1382 case SAMPLERTYPE_INT: progSpec = PROGRAM_CUBE_ARRAY_INT; break; 1383 case SAMPLERTYPE_UINT: progSpec = PROGRAM_CUBE_ARRAY_UINT; break; 1384 case SAMPLERTYPE_SHADOW: progSpec = PROGRAM_CUBE_ARRAY_SHADOW; break; 1385 default: DE_ASSERT(false); 1386 } 1387 } 1388 else if (params.texType == TEXTURETYPE_1D_ARRAY) 1389 { 1390 DE_ASSERT(!useBias); // \todo [2012-02-17 pyry] Support bias. 1391 1392 numComps = 2; 1393 1394 switch (params.samplerType) 1395 { 1396 case SAMPLERTYPE_FLOAT: progSpec = PROGRAM_1D_ARRAY_FLOAT; break; 1397 case SAMPLERTYPE_INT: progSpec = PROGRAM_1D_ARRAY_INT; break; 1398 case SAMPLERTYPE_UINT: progSpec = PROGRAM_1D_ARRAY_UINT; break; 1399 case SAMPLERTYPE_SHADOW: progSpec = PROGRAM_1D_ARRAY_SHADOW; break; 1400 default: DE_ASSERT(false); 1401 } 1402 } 1403 else if (params.texType == TEXTURETYPE_BUFFER) 1404 { 1405 numComps = 1; 1406 1407 switch (params.samplerType) 1408 { 1409 case SAMPLERTYPE_FETCH_FLOAT: progSpec = PROGRAM_BUFFER_FLOAT; break; 1410 case SAMPLERTYPE_FETCH_INT: progSpec = PROGRAM_BUFFER_INT; break; 1411 case SAMPLERTYPE_FETCH_UINT: progSpec = PROGRAM_BUFFER_UINT; break; 1412 default: DE_ASSERT(false); 1413 } 1414 } 1415 else 1416 DE_ASSERT(DE_FALSE); 1417 1418 glu::ShaderProgram* program = m_programLibrary.getProgram(progSpec); 1419 1420 // \todo [2012-09-26 pyry] Move to ProgramLibrary and log unique programs only(?) 1421 if (params.flags & RenderParams::LOG_PROGRAMS) 1422 m_log << *program; 1423 1424 GLU_EXPECT_NO_ERROR(gl.getError(), "Set vertex attributes"); 1425 1426 // Program and uniforms. 1427 deUint32 prog = program->getProgram(); 1428 gl.useProgram(prog); 1429 1430 gl.uniform1i(gl.getUniformLocation(prog, "u_sampler"), texUnit); 1431 if (logUniforms) 1432 m_log << TestLog::Message << "u_sampler = " << texUnit << TestLog::EndMessage; 1433 1434 if (useBias) 1435 { 1436 gl.uniform1f(gl.getUniformLocation(prog, "u_bias"), params.bias); 1437 if (logUniforms) 1438 m_log << TestLog::Message << "u_bias = " << params.bias << TestLog::EndMessage; 1439 } 1440 1441 if (params.samplerType == SAMPLERTYPE_SHADOW) 1442 { 1443 gl.uniform1f(gl.getUniformLocation(prog, "u_ref"), params.ref); 1444 if (logUniforms) 1445 m_log << TestLog::Message << "u_ref = " << params.ref << TestLog::EndMessage; 1446 } 1447 1448 gl.uniform4fv(gl.getUniformLocation(prog, "u_colorScale"), 1, params.colorScale.getPtr()); 1449 gl.uniform4fv(gl.getUniformLocation(prog, "u_colorBias"), 1, params.colorBias.getPtr()); 1450 1451 if (logUniforms) 1452 { 1453 m_log << TestLog::Message << "u_colorScale = " << params.colorScale << TestLog::EndMessage; 1454 m_log << TestLog::Message << "u_colorBias = " << params.colorBias << TestLog::EndMessage; 1455 } 1456 1457 GLU_EXPECT_NO_ERROR(gl.getError(), "Set program state"); 1458 1459 { 1460 const glu::VertexArrayBinding vertexArrays[] = 1461 { 1462 glu::va::Float("a_position", 4, 4, 0, &position[0]), 1463 glu::va::Float("a_texCoord", numComps, 4, 0, texCoord) 1464 }; 1465 glu::draw(m_renderCtx, prog, DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0], 1466 glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0])); 1467 } 1468 } 1469 1470 void computeQuadTexCoord1D (std::vector<float>& dst, float left, float right) 1471 { 1472 dst.resize(4); 1473 1474 dst[0] = left; 1475 dst[1] = left; 1476 dst[2] = right; 1477 dst[3] = right; 1478 } 1479 1480 void computeQuadTexCoord1DArray (std::vector<float>& dst, int layerNdx, float left, float right) 1481 { 1482 dst.resize(4*2); 1483 1484 dst[0] = left; dst[1] = (float)layerNdx; 1485 dst[2] = left; dst[3] = (float)layerNdx; 1486 dst[4] = right; dst[5] = (float)layerNdx; 1487 dst[6] = right; dst[7] = (float)layerNdx; 1488 } 1489 1490 void computeQuadTexCoord2D (std::vector<float>& dst, const tcu::Vec2& bottomLeft, const tcu::Vec2& topRight) 1491 { 1492 dst.resize(4*2); 1493 1494 dst[0] = bottomLeft.x(); dst[1] = bottomLeft.y(); 1495 dst[2] = bottomLeft.x(); dst[3] = topRight.y(); 1496 dst[4] = topRight.x(); dst[5] = bottomLeft.y(); 1497 dst[6] = topRight.x(); dst[7] = topRight.y(); 1498 } 1499 1500 void computeQuadTexCoord2DArray (std::vector<float>& dst, int layerNdx, const tcu::Vec2& bottomLeft, const tcu::Vec2& topRight) 1501 { 1502 dst.resize(4*3); 1503 1504 dst[0] = bottomLeft.x(); dst[ 1] = bottomLeft.y(); dst[ 2] = (float)layerNdx; 1505 dst[3] = bottomLeft.x(); dst[ 4] = topRight.y(); dst[ 5] = (float)layerNdx; 1506 dst[6] = topRight.x(); dst[ 7] = bottomLeft.y(); dst[ 8] = (float)layerNdx; 1507 dst[9] = topRight.x(); dst[10] = topRight.y(); dst[11] = (float)layerNdx; 1508 } 1509 1510 void computeQuadTexCoord3D (std::vector<float>& dst, const tcu::Vec3& p0, const tcu::Vec3& p1, const tcu::IVec3& dirSwz) 1511 { 1512 tcu::Vec3 f0 = tcu::Vec3(0.0f, 0.0f, 0.0f).swizzle(dirSwz[0], dirSwz[1], dirSwz[2]); 1513 tcu::Vec3 f1 = tcu::Vec3(0.0f, 1.0f, 0.0f).swizzle(dirSwz[0], dirSwz[1], dirSwz[2]); 1514 tcu::Vec3 f2 = tcu::Vec3(1.0f, 0.0f, 0.0f).swizzle(dirSwz[0], dirSwz[1], dirSwz[2]); 1515 tcu::Vec3 f3 = tcu::Vec3(1.0f, 1.0f, 0.0f).swizzle(dirSwz[0], dirSwz[1], dirSwz[2]); 1516 1517 tcu::Vec3 v0 = p0 + (p1-p0)*f0; 1518 tcu::Vec3 v1 = p0 + (p1-p0)*f1; 1519 tcu::Vec3 v2 = p0 + (p1-p0)*f2; 1520 tcu::Vec3 v3 = p0 + (p1-p0)*f3; 1521 1522 dst.resize(4*3); 1523 1524 dst[0] = v0.x(); dst[ 1] = v0.y(); dst[ 2] = v0.z(); 1525 dst[3] = v1.x(); dst[ 4] = v1.y(); dst[ 5] = v1.z(); 1526 dst[6] = v2.x(); dst[ 7] = v2.y(); dst[ 8] = v2.z(); 1527 dst[9] = v3.x(); dst[10] = v3.y(); dst[11] = v3.z(); 1528 } 1529 1530 void computeQuadTexCoordCube (std::vector<float>& dst, tcu::CubeFace face) 1531 { 1532 static const float texCoordNegX[] = 1533 { 1534 -1.0f, 1.0f, -1.0f, 1535 -1.0f, -1.0f, -1.0f, 1536 -1.0f, 1.0f, 1.0f, 1537 -1.0f, -1.0f, 1.0f 1538 }; 1539 static const float texCoordPosX[] = 1540 { 1541 +1.0f, 1.0f, 1.0f, 1542 +1.0f, -1.0f, 1.0f, 1543 +1.0f, 1.0f, -1.0f, 1544 +1.0f, -1.0f, -1.0f 1545 }; 1546 static const float texCoordNegY[] = 1547 { 1548 -1.0f, -1.0f, 1.0f, 1549 -1.0f, -1.0f, -1.0f, 1550 1.0f, -1.0f, 1.0f, 1551 1.0f, -1.0f, -1.0f 1552 }; 1553 static const float texCoordPosY[] = 1554 { 1555 -1.0f, +1.0f, -1.0f, 1556 -1.0f, +1.0f, 1.0f, 1557 1.0f, +1.0f, -1.0f, 1558 1.0f, +1.0f, 1.0f 1559 }; 1560 static const float texCoordNegZ[] = 1561 { 1562 1.0f, 1.0f, -1.0f, 1563 1.0f, -1.0f, -1.0f, 1564 -1.0f, 1.0f, -1.0f, 1565 -1.0f, -1.0f, -1.0f 1566 }; 1567 static const float texCoordPosZ[] = 1568 { 1569 -1.0f, 1.0f, +1.0f, 1570 -1.0f, -1.0f, +1.0f, 1571 1.0f, 1.0f, +1.0f, 1572 1.0f, -1.0f, +1.0f 1573 }; 1574 1575 const float* texCoord = DE_NULL; 1576 int texCoordSize = DE_LENGTH_OF_ARRAY(texCoordNegX); 1577 1578 switch (face) 1579 { 1580 case tcu::CUBEFACE_NEGATIVE_X: texCoord = texCoordNegX; break; 1581 case tcu::CUBEFACE_POSITIVE_X: texCoord = texCoordPosX; break; 1582 case tcu::CUBEFACE_NEGATIVE_Y: texCoord = texCoordNegY; break; 1583 case tcu::CUBEFACE_POSITIVE_Y: texCoord = texCoordPosY; break; 1584 case tcu::CUBEFACE_NEGATIVE_Z: texCoord = texCoordNegZ; break; 1585 case tcu::CUBEFACE_POSITIVE_Z: texCoord = texCoordPosZ; break; 1586 default: 1587 DE_ASSERT(DE_FALSE); 1588 return; 1589 } 1590 1591 dst.resize(texCoordSize); 1592 std::copy(texCoord, texCoord+texCoordSize, dst.begin()); 1593 } 1594 1595 void computeQuadTexCoordCube (std::vector<float>& dst, tcu::CubeFace face, const tcu::Vec2& bottomLeft, const tcu::Vec2& topRight) 1596 { 1597 int sRow = 0; 1598 int tRow = 0; 1599 int mRow = 0; 1600 float sSign = 1.0f; 1601 float tSign = 1.0f; 1602 float mSign = 1.0f; 1603 1604 switch (face) 1605 { 1606 case tcu::CUBEFACE_NEGATIVE_X: mRow = 0; sRow = 2; tRow = 1; mSign = -1.0f; tSign = -1.0f; break; 1607 case tcu::CUBEFACE_POSITIVE_X: mRow = 0; sRow = 2; tRow = 1; sSign = -1.0f; tSign = -1.0f; break; 1608 case tcu::CUBEFACE_NEGATIVE_Y: mRow = 1; sRow = 0; tRow = 2; mSign = -1.0f; tSign = -1.0f; break; 1609 case tcu::CUBEFACE_POSITIVE_Y: mRow = 1; sRow = 0; tRow = 2; break; 1610 case tcu::CUBEFACE_NEGATIVE_Z: mRow = 2; sRow = 0; tRow = 1; mSign = -1.0f; sSign = -1.0f; tSign = -1.0f; break; 1611 case tcu::CUBEFACE_POSITIVE_Z: mRow = 2; sRow = 0; tRow = 1; tSign = -1.0f; break; 1612 default: 1613 DE_ASSERT(DE_FALSE); 1614 return; 1615 } 1616 1617 dst.resize(3*4); 1618 1619 dst[0+mRow] = mSign; 1620 dst[3+mRow] = mSign; 1621 dst[6+mRow] = mSign; 1622 dst[9+mRow] = mSign; 1623 1624 dst[0+sRow] = sSign * bottomLeft.x(); 1625 dst[3+sRow] = sSign * bottomLeft.x(); 1626 dst[6+sRow] = sSign * topRight.x(); 1627 dst[9+sRow] = sSign * topRight.x(); 1628 1629 dst[0+tRow] = tSign * bottomLeft.y(); 1630 dst[3+tRow] = tSign * topRight.y(); 1631 dst[6+tRow] = tSign * bottomLeft.y(); 1632 dst[9+tRow] = tSign * topRight.y(); 1633 } 1634 1635 void computeQuadTexCoordCubeArray (std::vector<float>& dst, tcu::CubeFace face, const tcu::Vec2& bottomLeft, const tcu::Vec2& topRight, const tcu::Vec2& layerRange) 1636 { 1637 int sRow = 0; 1638 int tRow = 0; 1639 int mRow = 0; 1640 const int qRow = 3; 1641 float sSign = 1.0f; 1642 float tSign = 1.0f; 1643 float mSign = 1.0f; 1644 const float l0 = layerRange.x(); 1645 const float l1 = layerRange.y(); 1646 1647 switch (face) 1648 { 1649 case tcu::CUBEFACE_NEGATIVE_X: mRow = 0; sRow = 2; tRow = 1; mSign = -1.0f; tSign = -1.0f; break; 1650 case tcu::CUBEFACE_POSITIVE_X: mRow = 0; sRow = 2; tRow = 1; sSign = -1.0f; tSign = -1.0f; break; 1651 case tcu::CUBEFACE_NEGATIVE_Y: mRow = 1; sRow = 0; tRow = 2; mSign = -1.0f; tSign = -1.0f; break; 1652 case tcu::CUBEFACE_POSITIVE_Y: mRow = 1; sRow = 0; tRow = 2; break; 1653 case tcu::CUBEFACE_NEGATIVE_Z: mRow = 2; sRow = 0; tRow = 1; mSign = -1.0f; sSign = -1.0f; tSign = -1.0f; break; 1654 case tcu::CUBEFACE_POSITIVE_Z: mRow = 2; sRow = 0; tRow = 1; tSign = -1.0f; break; 1655 default: 1656 DE_ASSERT(DE_FALSE); 1657 return; 1658 } 1659 1660 dst.resize(4*4); 1661 1662 dst[ 0+mRow] = mSign; 1663 dst[ 4+mRow] = mSign; 1664 dst[ 8+mRow] = mSign; 1665 dst[12+mRow] = mSign; 1666 1667 dst[ 0+sRow] = sSign * bottomLeft.x(); 1668 dst[ 4+sRow] = sSign * bottomLeft.x(); 1669 dst[ 8+sRow] = sSign * topRight.x(); 1670 dst[12+sRow] = sSign * topRight.x(); 1671 1672 dst[ 0+tRow] = tSign * bottomLeft.y(); 1673 dst[ 4+tRow] = tSign * topRight.y(); 1674 dst[ 8+tRow] = tSign * bottomLeft.y(); 1675 dst[12+tRow] = tSign * topRight.y(); 1676 1677 if (l0 != l1) 1678 { 1679 dst[ 0+qRow] = l0; 1680 dst[ 4+qRow] = l0*0.5f + l1*0.5f; 1681 dst[ 8+qRow] = l0*0.5f + l1*0.5f; 1682 dst[12+qRow] = l1; 1683 } 1684 else 1685 { 1686 dst[ 0+qRow] = l0; 1687 dst[ 4+qRow] = l0; 1688 dst[ 8+qRow] = l0; 1689 dst[12+qRow] = l0; 1690 } 1691 } 1692 1693 // Texture result verification 1694 1695 //! Verifies texture lookup results and returns number of failed pixels. 1696 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result, 1697 const tcu::ConstPixelBufferAccess& reference, 1698 const tcu::PixelBufferAccess& errorMask, 1699 const tcu::Texture1DView& baseView, 1700 const float* texCoord, 1701 const ReferenceParams& sampleParams, 1702 const tcu::LookupPrecision& lookupPrec, 1703 const tcu::LodPrecision& lodPrec, 1704 qpWatchDog* watchDog) 1705 { 1706 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); 1707 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); 1708 1709 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 1710 const tcu::Texture1DView src = getEffectiveTextureView(getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel), srcLevelStorage, sampleParams.sampler); 1711 1712 const tcu::Vec4 sq = tcu::Vec4(texCoord[0], texCoord[1], texCoord[2], texCoord[3]); 1713 1714 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); 1715 const float dstW = float(dstSize.x()); 1716 const float dstH = float(dstSize.y()); 1717 const int srcSize = src.getWidth(); 1718 1719 // Coordinates and lod per triangle. 1720 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 1721 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) }; 1722 1723 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); 1724 1725 int numFailed = 0; 1726 1727 const tcu::Vec2 lodOffsets[] = 1728 { 1729 tcu::Vec2(-1, 0), 1730 tcu::Vec2(+1, 0), 1731 tcu::Vec2( 0, -1), 1732 tcu::Vec2( 0, +1), 1733 }; 1734 1735 tcu::clear(errorMask, tcu::RGBA::green.toVec()); 1736 1737 for (int py = 0; py < result.getHeight(); py++) 1738 { 1739 // Ugly hack, validation can take way too long at the moment. 1740 if (watchDog) 1741 qpWatchDog_touch(watchDog); 1742 1743 for (int px = 0; px < result.getWidth(); px++) 1744 { 1745 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 1746 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 1747 1748 // Try comparison to ideal reference first, and if that fails use slower verificator. 1749 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) 1750 { 1751 const float wx = (float)px + 0.5f; 1752 const float wy = (float)py + 0.5f; 1753 const float nx = wx / dstW; 1754 const float ny = wy / dstH; 1755 1756 const int triNdx = nx + ny >= 1.0f ? 1 : 0; 1757 const float triWx = triNdx ? dstW - wx : wx; 1758 const float triWy = triNdx ? dstH - wy : wy; 1759 const float triNx = triNdx ? 1.0f - nx : nx; 1760 const float triNy = triNdx ? 1.0f - ny : ny; 1761 1762 const float coord = projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy); 1763 const float coordDx = triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy) * float(srcSize); 1764 const float coordDy = triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx) * float(srcSize); 1765 1766 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx, coordDy, lodPrec); 1767 1768 // Compute lod bounds across lodOffsets range. 1769 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) 1770 { 1771 const float wxo = triWx + lodOffsets[lodOffsNdx].x(); 1772 const float wyo = triWy + lodOffsets[lodOffsNdx].y(); 1773 const float nxo = wxo/dstW; 1774 const float nyo = wyo/dstH; 1775 1776 const float coordDxo = triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo) * float(srcSize); 1777 const float coordDyo = triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo) * float(srcSize); 1778 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo, coordDyo, lodPrec); 1779 1780 lodBounds.x() = de::min(lodBounds.x(), lodO.x()); 1781 lodBounds.y() = de::max(lodBounds.y(), lodO.y()); 1782 } 1783 1784 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); 1785 const bool isOk = tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix); 1786 1787 if (!isOk) 1788 { 1789 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py); 1790 numFailed += 1; 1791 } 1792 } 1793 } 1794 } 1795 1796 return numFailed; 1797 } 1798 1799 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result, 1800 const tcu::ConstPixelBufferAccess& reference, 1801 const tcu::PixelBufferAccess& errorMask, 1802 const tcu::Texture2DView& baseView, 1803 const float* texCoord, 1804 const ReferenceParams& sampleParams, 1805 const tcu::LookupPrecision& lookupPrec, 1806 const tcu::LodPrecision& lodPrec, 1807 qpWatchDog* watchDog) 1808 { 1809 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); 1810 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); 1811 1812 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 1813 const tcu::Texture2DView src = getEffectiveTextureView(getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel), srcLevelStorage, sampleParams.sampler); 1814 1815 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[2+0], texCoord[4+0], texCoord[6+0]); 1816 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[2+1], texCoord[4+1], texCoord[6+1]); 1817 1818 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); 1819 const float dstW = float(dstSize.x()); 1820 const float dstH = float(dstSize.y()); 1821 const tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight()); 1822 1823 // Coordinates and lod per triangle. 1824 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 1825 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 1826 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) }; 1827 1828 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); 1829 1830 int numFailed = 0; 1831 1832 const tcu::Vec2 lodOffsets[] = 1833 { 1834 tcu::Vec2(-1, 0), 1835 tcu::Vec2(+1, 0), 1836 tcu::Vec2( 0, -1), 1837 tcu::Vec2( 0, +1), 1838 }; 1839 1840 tcu::clear(errorMask, tcu::RGBA::green.toVec()); 1841 1842 for (int py = 0; py < result.getHeight(); py++) 1843 { 1844 // Ugly hack, validation can take way too long at the moment. 1845 if (watchDog) 1846 qpWatchDog_touch(watchDog); 1847 1848 for (int px = 0; px < result.getWidth(); px++) 1849 { 1850 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 1851 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 1852 1853 // Try comparison to ideal reference first, and if that fails use slower verificator. 1854 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) 1855 { 1856 const float wx = (float)px + 0.5f; 1857 const float wy = (float)py + 0.5f; 1858 const float nx = wx / dstW; 1859 const float ny = wy / dstH; 1860 1861 const int triNdx = nx + ny >= 1.0f ? 1 : 0; 1862 const float triWx = triNdx ? dstW - wx : wx; 1863 const float triWy = triNdx ? dstH - wy : wy; 1864 const float triNx = triNdx ? 1.0f - nx : nx; 1865 const float triNy = triNdx ? 1.0f - ny : ny; 1866 1867 const tcu::Vec2 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), 1868 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy)); 1869 const tcu::Vec2 coordDx = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), 1870 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize.asFloat(); 1871 const tcu::Vec2 coordDy = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), 1872 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize.asFloat(); 1873 1874 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDy.x(), coordDy.y(), lodPrec); 1875 1876 // Compute lod bounds across lodOffsets range. 1877 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) 1878 { 1879 const float wxo = triWx + lodOffsets[lodOffsNdx].x(); 1880 const float wyo = triWy + lodOffsets[lodOffsNdx].y(); 1881 const float nxo = wxo/dstW; 1882 const float nyo = wyo/dstH; 1883 1884 const tcu::Vec2 coordDxo = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), 1885 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize.asFloat(); 1886 const tcu::Vec2 coordDyo = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), 1887 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize.asFloat(); 1888 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDyo.x(), coordDyo.y(), lodPrec); 1889 1890 lodBounds.x() = de::min(lodBounds.x(), lodO.x()); 1891 lodBounds.y() = de::max(lodBounds.y(), lodO.y()); 1892 } 1893 1894 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); 1895 const bool isOk = tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix); 1896 1897 if (!isOk) 1898 { 1899 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py); 1900 numFailed += 1; 1901 } 1902 } 1903 } 1904 } 1905 1906 return numFailed; 1907 } 1908 1909 bool verifyTextureResult (tcu::TestContext& testCtx, 1910 const tcu::ConstPixelBufferAccess& result, 1911 const tcu::Texture1DView& src, 1912 const float* texCoord, 1913 const ReferenceParams& sampleParams, 1914 const tcu::LookupPrecision& lookupPrec, 1915 const tcu::LodPrecision& lodPrec, 1916 const tcu::PixelFormat& pixelFormat) 1917 { 1918 tcu::TestLog& log = testCtx.getLog(); 1919 tcu::Surface reference (result.getWidth(), result.getHeight()); 1920 tcu::Surface errorMask (result.getWidth(), result.getHeight()); 1921 int numFailedPixels; 1922 1923 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); 1924 1925 sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); 1926 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog()); 1927 1928 if (numFailedPixels > 0) 1929 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage; 1930 1931 log << TestLog::ImageSet("VerifyResult", "Verification result") 1932 << TestLog::Image("Rendered", "Rendered image", result); 1933 1934 if (numFailedPixels > 0) 1935 { 1936 log << TestLog::Image("Reference", "Ideal reference image", reference) 1937 << TestLog::Image("ErrorMask", "Error mask", errorMask); 1938 } 1939 1940 log << TestLog::EndImageSet; 1941 1942 return numFailedPixels == 0; 1943 } 1944 1945 bool verifyTextureResult (tcu::TestContext& testCtx, 1946 const tcu::ConstPixelBufferAccess& result, 1947 const tcu::Texture2DView& src, 1948 const float* texCoord, 1949 const ReferenceParams& sampleParams, 1950 const tcu::LookupPrecision& lookupPrec, 1951 const tcu::LodPrecision& lodPrec, 1952 const tcu::PixelFormat& pixelFormat) 1953 { 1954 tcu::TestLog& log = testCtx.getLog(); 1955 tcu::Surface reference (result.getWidth(), result.getHeight()); 1956 tcu::Surface errorMask (result.getWidth(), result.getHeight()); 1957 int numFailedPixels; 1958 1959 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); 1960 1961 sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); 1962 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog()); 1963 1964 if (numFailedPixels > 0) 1965 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage; 1966 1967 log << TestLog::ImageSet("VerifyResult", "Verification result") 1968 << TestLog::Image("Rendered", "Rendered image", result); 1969 1970 if (numFailedPixels > 0) 1971 { 1972 log << TestLog::Image("Reference", "Ideal reference image", reference) 1973 << TestLog::Image("ErrorMask", "Error mask", errorMask); 1974 } 1975 1976 log << TestLog::EndImageSet; 1977 1978 return numFailedPixels == 0; 1979 } 1980 1981 //! Verifies texture lookup results and returns number of failed pixels. 1982 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result, 1983 const tcu::ConstPixelBufferAccess& reference, 1984 const tcu::PixelBufferAccess& errorMask, 1985 const tcu::TextureCubeView& baseView, 1986 const float* texCoord, 1987 const ReferenceParams& sampleParams, 1988 const tcu::LookupPrecision& lookupPrec, 1989 const tcu::LodPrecision& lodPrec, 1990 qpWatchDog* watchDog) 1991 { 1992 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); 1993 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); 1994 1995 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 1996 const tcu::TextureCubeView src = getEffectiveTextureView(getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel), srcLevelStorage, sampleParams.sampler); 1997 1998 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]); 1999 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]); 2000 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]); 2001 2002 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); 2003 const float dstW = float(dstSize.x()); 2004 const float dstH = float(dstSize.y()); 2005 const int srcSize = src.getSize(); 2006 2007 // Coordinates per triangle. 2008 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 2009 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 2010 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) }; 2011 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) }; 2012 2013 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); 2014 2015 const float posEps = 1.0f / float(1<<MIN_SUBPIXEL_BITS); 2016 2017 int numFailed = 0; 2018 2019 const tcu::Vec2 lodOffsets[] = 2020 { 2021 tcu::Vec2(-1, 0), 2022 tcu::Vec2(+1, 0), 2023 tcu::Vec2( 0, -1), 2024 tcu::Vec2( 0, +1), 2025 2026 // \note Not strictly allowed by spec, but implementations do this in practice. 2027 tcu::Vec2(-1, -1), 2028 tcu::Vec2(-1, +1), 2029 tcu::Vec2(+1, -1), 2030 tcu::Vec2(+1, +1), 2031 }; 2032 2033 tcu::clear(errorMask, tcu::RGBA::green.toVec()); 2034 2035 for (int py = 0; py < result.getHeight(); py++) 2036 { 2037 // Ugly hack, validation can take way too long at the moment. 2038 if (watchDog) 2039 qpWatchDog_touch(watchDog); 2040 2041 for (int px = 0; px < result.getWidth(); px++) 2042 { 2043 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 2044 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 2045 2046 // Try comparison to ideal reference first, and if that fails use slower verificator. 2047 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) 2048 { 2049 const float wx = (float)px + 0.5f; 2050 const float wy = (float)py + 0.5f; 2051 const float nx = wx / dstW; 2052 const float ny = wy / dstH; 2053 2054 const bool tri0 = (wx-posEps)/dstW + (wy-posEps)/dstH <= 1.0f; 2055 const bool tri1 = (wx+posEps)/dstW + (wy+posEps)/dstH >= 1.0f; 2056 2057 bool isOk = false; 2058 2059 DE_ASSERT(tri0 || tri1); 2060 2061 // Pixel can belong to either of the triangles if it lies close enough to the edge. 2062 for (int triNdx = (tri0?0:1); triNdx <= (tri1?1:0); triNdx++) 2063 { 2064 const float triWx = triNdx ? dstW - wx : wx; 2065 const float triWy = triNdx ? dstH - wy : wy; 2066 const float triNx = triNdx ? 1.0f - nx : nx; 2067 const float triNy = triNdx ? 1.0f - ny : ny; 2068 2069 const tcu::Vec3 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), 2070 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy), 2071 projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy)); 2072 const tcu::Vec3 coordDx (triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), 2073 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy), 2074 triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)); 2075 const tcu::Vec3 coordDy (triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), 2076 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx), 2077 triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)); 2078 2079 tcu::Vec2 lodBounds = tcu::computeCubeLodBoundsFromDerivates(coord, coordDx, coordDy, srcSize, lodPrec); 2080 2081 // Compute lod bounds across lodOffsets range. 2082 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) 2083 { 2084 const float wxo = triWx + lodOffsets[lodOffsNdx].x(); 2085 const float wyo = triWy + lodOffsets[lodOffsNdx].y(); 2086 const float nxo = wxo/dstW; 2087 const float nyo = wyo/dstH; 2088 2089 const tcu::Vec3 coordO (projectedTriInterpolate(triS[triNdx], triW[triNdx], nxo, nyo), 2090 projectedTriInterpolate(triT[triNdx], triW[triNdx], nxo, nyo), 2091 projectedTriInterpolate(triR[triNdx], triW[triNdx], nxo, nyo)); 2092 const tcu::Vec3 coordDxo (triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), 2093 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo), 2094 triDerivateX(triR[triNdx], triW[triNdx], wxo, dstW, nyo)); 2095 const tcu::Vec3 coordDyo (triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), 2096 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo), 2097 triDerivateY(triR[triNdx], triW[triNdx], wyo, dstH, nxo)); 2098 const tcu::Vec2 lodO = tcu::computeCubeLodBoundsFromDerivates(coordO, coordDxo, coordDyo, srcSize, lodPrec); 2099 2100 lodBounds.x() = de::min(lodBounds.x(), lodO.x()); 2101 lodBounds.y() = de::max(lodBounds.y(), lodO.y()); 2102 } 2103 2104 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); 2105 2106 if (tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix)) 2107 { 2108 isOk = true; 2109 break; 2110 } 2111 } 2112 2113 if (!isOk) 2114 { 2115 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py); 2116 numFailed += 1; 2117 } 2118 } 2119 } 2120 } 2121 2122 return numFailed; 2123 } 2124 2125 bool verifyTextureResult (tcu::TestContext& testCtx, 2126 const tcu::ConstPixelBufferAccess& result, 2127 const tcu::TextureCubeView& src, 2128 const float* texCoord, 2129 const ReferenceParams& sampleParams, 2130 const tcu::LookupPrecision& lookupPrec, 2131 const tcu::LodPrecision& lodPrec, 2132 const tcu::PixelFormat& pixelFormat) 2133 { 2134 tcu::TestLog& log = testCtx.getLog(); 2135 tcu::Surface reference (result.getWidth(), result.getHeight()); 2136 tcu::Surface errorMask (result.getWidth(), result.getHeight()); 2137 int numFailedPixels; 2138 2139 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); 2140 2141 sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); 2142 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog()); 2143 2144 if (numFailedPixels > 0) 2145 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage; 2146 2147 log << TestLog::ImageSet("VerifyResult", "Verification result") 2148 << TestLog::Image("Rendered", "Rendered image", result); 2149 2150 if (numFailedPixels > 0) 2151 { 2152 log << TestLog::Image("Reference", "Ideal reference image", reference) 2153 << TestLog::Image("ErrorMask", "Error mask", errorMask); 2154 } 2155 2156 log << TestLog::EndImageSet; 2157 2158 return numFailedPixels == 0; 2159 } 2160 2161 //! Verifies texture lookup results and returns number of failed pixels. 2162 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result, 2163 const tcu::ConstPixelBufferAccess& reference, 2164 const tcu::PixelBufferAccess& errorMask, 2165 const tcu::Texture3DView& baseView, 2166 const float* texCoord, 2167 const ReferenceParams& sampleParams, 2168 const tcu::LookupPrecision& lookupPrec, 2169 const tcu::LodPrecision& lodPrec, 2170 qpWatchDog* watchDog) 2171 { 2172 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); 2173 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); 2174 2175 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 2176 const tcu::Texture3DView src = getEffectiveTextureView(getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel), srcLevelStorage, sampleParams.sampler); 2177 2178 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]); 2179 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]); 2180 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]); 2181 2182 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); 2183 const float dstW = float(dstSize.x()); 2184 const float dstH = float(dstSize.y()); 2185 const tcu::IVec3 srcSize = tcu::IVec3(src.getWidth(), src.getHeight(), src.getDepth()); 2186 2187 // Coordinates and lod per triangle. 2188 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 2189 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 2190 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) }; 2191 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) }; 2192 2193 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); 2194 2195 const float posEps = 1.0f / float(1<<MIN_SUBPIXEL_BITS); 2196 2197 int numFailed = 0; 2198 2199 const tcu::Vec2 lodOffsets[] = 2200 { 2201 tcu::Vec2(-1, 0), 2202 tcu::Vec2(+1, 0), 2203 tcu::Vec2( 0, -1), 2204 tcu::Vec2( 0, +1), 2205 }; 2206 2207 tcu::clear(errorMask, tcu::RGBA::green.toVec()); 2208 2209 for (int py = 0; py < result.getHeight(); py++) 2210 { 2211 // Ugly hack, validation can take way too long at the moment. 2212 if (watchDog) 2213 qpWatchDog_touch(watchDog); 2214 2215 for (int px = 0; px < result.getWidth(); px++) 2216 { 2217 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 2218 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 2219 2220 // Try comparison to ideal reference first, and if that fails use slower verificator. 2221 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) 2222 { 2223 const float wx = (float)px + 0.5f; 2224 const float wy = (float)py + 0.5f; 2225 const float nx = wx / dstW; 2226 const float ny = wy / dstH; 2227 2228 const bool tri0 = (wx-posEps)/dstW + (wy-posEps)/dstH <= 1.0f; 2229 const bool tri1 = (wx+posEps)/dstW + (wy+posEps)/dstH >= 1.0f; 2230 2231 bool isOk = false; 2232 2233 DE_ASSERT(tri0 || tri1); 2234 2235 // Pixel can belong to either of the triangles if it lies close enough to the edge. 2236 for (int triNdx = (tri0?0:1); triNdx <= (tri1?1:0); triNdx++) 2237 { 2238 const float triWx = triNdx ? dstW - wx : wx; 2239 const float triWy = triNdx ? dstH - wy : wy; 2240 const float triNx = triNdx ? 1.0f - nx : nx; 2241 const float triNy = triNdx ? 1.0f - ny : ny; 2242 2243 const tcu::Vec3 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), 2244 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy), 2245 projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy)); 2246 const tcu::Vec3 coordDx = tcu::Vec3(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), 2247 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy), 2248 triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize.asFloat(); 2249 const tcu::Vec3 coordDy = tcu::Vec3(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), 2250 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx), 2251 triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize.asFloat(); 2252 2253 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDx.z(), coordDy.x(), coordDy.y(), coordDy.z(), lodPrec); 2254 2255 // Compute lod bounds across lodOffsets range. 2256 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) 2257 { 2258 const float wxo = triWx + lodOffsets[lodOffsNdx].x(); 2259 const float wyo = triWy + lodOffsets[lodOffsNdx].y(); 2260 const float nxo = wxo/dstW; 2261 const float nyo = wyo/dstH; 2262 2263 const tcu::Vec3 coordDxo = tcu::Vec3(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), 2264 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo), 2265 triDerivateX(triR[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize.asFloat(); 2266 const tcu::Vec3 coordDyo = tcu::Vec3(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), 2267 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo), 2268 triDerivateY(triR[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize.asFloat(); 2269 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDxo.z(), coordDyo.x(), coordDyo.y(), coordDyo.z(), lodPrec); 2270 2271 lodBounds.x() = de::min(lodBounds.x(), lodO.x()); 2272 lodBounds.y() = de::max(lodBounds.y(), lodO.y()); 2273 } 2274 2275 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); 2276 2277 if (tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix)) 2278 { 2279 isOk = true; 2280 break; 2281 } 2282 } 2283 2284 if (!isOk) 2285 { 2286 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py); 2287 numFailed += 1; 2288 } 2289 } 2290 } 2291 } 2292 2293 return numFailed; 2294 } 2295 2296 bool verifyTextureResult (tcu::TestContext& testCtx, 2297 const tcu::ConstPixelBufferAccess& result, 2298 const tcu::Texture3DView& src, 2299 const float* texCoord, 2300 const ReferenceParams& sampleParams, 2301 const tcu::LookupPrecision& lookupPrec, 2302 const tcu::LodPrecision& lodPrec, 2303 const tcu::PixelFormat& pixelFormat) 2304 { 2305 tcu::TestLog& log = testCtx.getLog(); 2306 tcu::Surface reference (result.getWidth(), result.getHeight()); 2307 tcu::Surface errorMask (result.getWidth(), result.getHeight()); 2308 int numFailedPixels; 2309 2310 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); 2311 2312 sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); 2313 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog()); 2314 2315 if (numFailedPixels > 0) 2316 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage; 2317 2318 log << TestLog::ImageSet("VerifyResult", "Verification result") 2319 << TestLog::Image("Rendered", "Rendered image", result); 2320 2321 if (numFailedPixels > 0) 2322 { 2323 log << TestLog::Image("Reference", "Ideal reference image", reference) 2324 << TestLog::Image("ErrorMask", "Error mask", errorMask); 2325 } 2326 2327 log << TestLog::EndImageSet; 2328 2329 return numFailedPixels == 0; 2330 } 2331 2332 //! Verifies texture lookup results and returns number of failed pixels. 2333 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result, 2334 const tcu::ConstPixelBufferAccess& reference, 2335 const tcu::PixelBufferAccess& errorMask, 2336 const tcu::Texture1DArrayView& baseView, 2337 const float* texCoord, 2338 const ReferenceParams& sampleParams, 2339 const tcu::LookupPrecision& lookupPrec, 2340 const tcu::LodPrecision& lodPrec, 2341 qpWatchDog* watchDog) 2342 { 2343 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); 2344 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); 2345 2346 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 2347 const tcu::Texture1DArrayView src = getEffectiveTextureView(baseView, srcLevelStorage, sampleParams.sampler); 2348 2349 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[2+0], texCoord[4+0], texCoord[6+0]); 2350 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[2+1], texCoord[4+1], texCoord[6+1]); 2351 2352 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); 2353 const float dstW = float(dstSize.x()); 2354 const float dstH = float(dstSize.y()); 2355 const float srcSize = float(src.getWidth()); // For lod computation, thus #layers is ignored. 2356 2357 // Coordinates and lod per triangle. 2358 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 2359 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 2360 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) }; 2361 2362 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); 2363 2364 int numFailed = 0; 2365 2366 const tcu::Vec2 lodOffsets[] = 2367 { 2368 tcu::Vec2(-1, 0), 2369 tcu::Vec2(+1, 0), 2370 tcu::Vec2( 0, -1), 2371 tcu::Vec2( 0, +1), 2372 }; 2373 2374 tcu::clear(errorMask, tcu::RGBA::green.toVec()); 2375 2376 for (int py = 0; py < result.getHeight(); py++) 2377 { 2378 // Ugly hack, validation can take way too long at the moment. 2379 if (watchDog) 2380 qpWatchDog_touch(watchDog); 2381 2382 for (int px = 0; px < result.getWidth(); px++) 2383 { 2384 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 2385 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 2386 2387 // Try comparison to ideal reference first, and if that fails use slower verificator. 2388 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) 2389 { 2390 const float wx = (float)px + 0.5f; 2391 const float wy = (float)py + 0.5f; 2392 const float nx = wx / dstW; 2393 const float ny = wy / dstH; 2394 2395 const int triNdx = nx + ny >= 1.0f ? 1 : 0; 2396 const float triWx = triNdx ? dstW - wx : wx; 2397 const float triWy = triNdx ? dstH - wy : wy; 2398 const float triNx = triNdx ? 1.0f - nx : nx; 2399 const float triNy = triNdx ? 1.0f - ny : ny; 2400 2401 const tcu::Vec2 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), 2402 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy)); 2403 const float coordDx = triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy) * srcSize; 2404 const float coordDy = triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx) * srcSize; 2405 2406 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx, coordDy, lodPrec); 2407 2408 // Compute lod bounds across lodOffsets range. 2409 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) 2410 { 2411 const float wxo = triWx + lodOffsets[lodOffsNdx].x(); 2412 const float wyo = triWy + lodOffsets[lodOffsNdx].y(); 2413 const float nxo = wxo/dstW; 2414 const float nyo = wyo/dstH; 2415 2416 const float coordDxo = triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo) * srcSize; 2417 const float coordDyo = triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo) * srcSize; 2418 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo, coordDyo, lodPrec); 2419 2420 lodBounds.x() = de::min(lodBounds.x(), lodO.x()); 2421 lodBounds.y() = de::max(lodBounds.y(), lodO.y()); 2422 } 2423 2424 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); 2425 const bool isOk = tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix); 2426 2427 if (!isOk) 2428 { 2429 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py); 2430 numFailed += 1; 2431 } 2432 } 2433 } 2434 } 2435 2436 return numFailed; 2437 } 2438 2439 //! Verifies texture lookup results and returns number of failed pixels. 2440 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result, 2441 const tcu::ConstPixelBufferAccess& reference, 2442 const tcu::PixelBufferAccess& errorMask, 2443 const tcu::Texture2DArrayView& baseView, 2444 const float* texCoord, 2445 const ReferenceParams& sampleParams, 2446 const tcu::LookupPrecision& lookupPrec, 2447 const tcu::LodPrecision& lodPrec, 2448 qpWatchDog* watchDog) 2449 { 2450 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); 2451 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); 2452 2453 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 2454 const tcu::Texture2DArrayView src = getEffectiveTextureView(baseView, srcLevelStorage, sampleParams.sampler); 2455 2456 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]); 2457 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]); 2458 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]); 2459 2460 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); 2461 const float dstW = float(dstSize.x()); 2462 const float dstH = float(dstSize.y()); 2463 const tcu::Vec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight()).asFloat(); // For lod computation, thus #layers is ignored. 2464 2465 // Coordinates and lod per triangle. 2466 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 2467 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 2468 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) }; 2469 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) }; 2470 2471 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); 2472 2473 int numFailed = 0; 2474 2475 const tcu::Vec2 lodOffsets[] = 2476 { 2477 tcu::Vec2(-1, 0), 2478 tcu::Vec2(+1, 0), 2479 tcu::Vec2( 0, -1), 2480 tcu::Vec2( 0, +1), 2481 }; 2482 2483 tcu::clear(errorMask, tcu::RGBA::green.toVec()); 2484 2485 for (int py = 0; py < result.getHeight(); py++) 2486 { 2487 // Ugly hack, validation can take way too long at the moment. 2488 if (watchDog) 2489 qpWatchDog_touch(watchDog); 2490 2491 for (int px = 0; px < result.getWidth(); px++) 2492 { 2493 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 2494 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 2495 2496 // Try comparison to ideal reference first, and if that fails use slower verificator. 2497 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) 2498 { 2499 const float wx = (float)px + 0.5f; 2500 const float wy = (float)py + 0.5f; 2501 const float nx = wx / dstW; 2502 const float ny = wy / dstH; 2503 2504 const int triNdx = nx + ny >= 1.0f ? 1 : 0; 2505 const float triWx = triNdx ? dstW - wx : wx; 2506 const float triWy = triNdx ? dstH - wy : wy; 2507 const float triNx = triNdx ? 1.0f - nx : nx; 2508 const float triNy = triNdx ? 1.0f - ny : ny; 2509 2510 const tcu::Vec3 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), 2511 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy), 2512 projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy)); 2513 const tcu::Vec2 coordDx = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), 2514 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize; 2515 const tcu::Vec2 coordDy = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), 2516 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize; 2517 2518 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDy.x(), coordDy.y(), lodPrec); 2519 2520 // Compute lod bounds across lodOffsets range. 2521 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) 2522 { 2523 const float wxo = triWx + lodOffsets[lodOffsNdx].x(); 2524 const float wyo = triWy + lodOffsets[lodOffsNdx].y(); 2525 const float nxo = wxo/dstW; 2526 const float nyo = wyo/dstH; 2527 2528 const tcu::Vec2 coordDxo = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), 2529 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize; 2530 const tcu::Vec2 coordDyo = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), 2531 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize; 2532 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDyo.x(), coordDyo.y(), lodPrec); 2533 2534 lodBounds.x() = de::min(lodBounds.x(), lodO.x()); 2535 lodBounds.y() = de::max(lodBounds.y(), lodO.y()); 2536 } 2537 2538 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); 2539 const bool isOk = tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix); 2540 2541 if (!isOk) 2542 { 2543 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py); 2544 numFailed += 1; 2545 } 2546 } 2547 } 2548 } 2549 2550 return numFailed; 2551 } 2552 2553 bool verifyTextureResult (tcu::TestContext& testCtx, 2554 const tcu::ConstPixelBufferAccess& result, 2555 const tcu::Texture1DArrayView& src, 2556 const float* texCoord, 2557 const ReferenceParams& sampleParams, 2558 const tcu::LookupPrecision& lookupPrec, 2559 const tcu::LodPrecision& lodPrec, 2560 const tcu::PixelFormat& pixelFormat) 2561 { 2562 tcu::TestLog& log = testCtx.getLog(); 2563 tcu::Surface reference (result.getWidth(), result.getHeight()); 2564 tcu::Surface errorMask (result.getWidth(), result.getHeight()); 2565 int numFailedPixels; 2566 2567 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); 2568 2569 sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); 2570 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog()); 2571 2572 if (numFailedPixels > 0) 2573 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage; 2574 2575 log << TestLog::ImageSet("VerifyResult", "Verification result") 2576 << TestLog::Image("Rendered", "Rendered image", result); 2577 2578 if (numFailedPixels > 0) 2579 { 2580 log << TestLog::Image("Reference", "Ideal reference image", reference) 2581 << TestLog::Image("ErrorMask", "Error mask", errorMask); 2582 } 2583 2584 log << TestLog::EndImageSet; 2585 2586 return numFailedPixels == 0; 2587 } 2588 2589 bool verifyTextureResult (tcu::TestContext& testCtx, 2590 const tcu::ConstPixelBufferAccess& result, 2591 const tcu::Texture2DArrayView& src, 2592 const float* texCoord, 2593 const ReferenceParams& sampleParams, 2594 const tcu::LookupPrecision& lookupPrec, 2595 const tcu::LodPrecision& lodPrec, 2596 const tcu::PixelFormat& pixelFormat) 2597 { 2598 tcu::TestLog& log = testCtx.getLog(); 2599 tcu::Surface reference (result.getWidth(), result.getHeight()); 2600 tcu::Surface errorMask (result.getWidth(), result.getHeight()); 2601 int numFailedPixels; 2602 2603 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); 2604 2605 sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); 2606 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog()); 2607 2608 if (numFailedPixels > 0) 2609 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage; 2610 2611 log << TestLog::ImageSet("VerifyResult", "Verification result") 2612 << TestLog::Image("Rendered", "Rendered image", result); 2613 2614 if (numFailedPixels > 0) 2615 { 2616 log << TestLog::Image("Reference", "Ideal reference image", reference) 2617 << TestLog::Image("ErrorMask", "Error mask", errorMask); 2618 } 2619 2620 log << TestLog::EndImageSet; 2621 2622 return numFailedPixels == 0; 2623 } 2624 2625 //! Verifies texture lookup results and returns number of failed pixels. 2626 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result, 2627 const tcu::ConstPixelBufferAccess& reference, 2628 const tcu::PixelBufferAccess& errorMask, 2629 const tcu::TextureCubeArrayView& baseView, 2630 const float* texCoord, 2631 const ReferenceParams& sampleParams, 2632 const tcu::LookupPrecision& lookupPrec, 2633 const tcu::IVec4& coordBits, 2634 const tcu::LodPrecision& lodPrec, 2635 qpWatchDog* watchDog) 2636 { 2637 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); 2638 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); 2639 2640 std::vector<tcu::ConstPixelBufferAccess> srcLevelStorage; 2641 const tcu::TextureCubeArrayView src = getEffectiveTextureView(getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel), srcLevelStorage, sampleParams.sampler); 2642 2643 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[4+0], texCoord[8+0], texCoord[12+0]); 2644 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[4+1], texCoord[8+1], texCoord[12+1]); 2645 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[4+2], texCoord[8+2], texCoord[12+2]); 2646 const tcu::Vec4 qq = tcu::Vec4(texCoord[0+3], texCoord[4+3], texCoord[8+3], texCoord[12+3]); 2647 2648 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); 2649 const float dstW = float(dstSize.x()); 2650 const float dstH = float(dstSize.y()); 2651 const int srcSize = src.getSize(); 2652 2653 // Coordinates per triangle. 2654 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 2655 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 2656 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) }; 2657 const tcu::Vec3 triQ[2] = { qq.swizzle(0, 1, 2), qq.swizzle(3, 2, 1) }; 2658 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) }; 2659 2660 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); 2661 2662 const float posEps = 1.0f / float((1<<4) + 1); // ES3 requires at least 4 subpixel bits. 2663 2664 int numFailed = 0; 2665 2666 const tcu::Vec2 lodOffsets[] = 2667 { 2668 tcu::Vec2(-1, 0), 2669 tcu::Vec2(+1, 0), 2670 tcu::Vec2( 0, -1), 2671 tcu::Vec2( 0, +1), 2672 2673 // \note Not strictly allowed by spec, but implementations do this in practice. 2674 tcu::Vec2(-1, -1), 2675 tcu::Vec2(-1, +1), 2676 tcu::Vec2(+1, -1), 2677 tcu::Vec2(+1, +1), 2678 }; 2679 2680 tcu::clear(errorMask, tcu::RGBA::green.toVec()); 2681 2682 for (int py = 0; py < result.getHeight(); py++) 2683 { 2684 // Ugly hack, validation can take way too long at the moment. 2685 if (watchDog) 2686 qpWatchDog_touch(watchDog); 2687 2688 for (int px = 0; px < result.getWidth(); px++) 2689 { 2690 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 2691 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale; 2692 2693 // Try comparison to ideal reference first, and if that fails use slower verificator. 2694 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold))) 2695 { 2696 const float wx = (float)px + 0.5f; 2697 const float wy = (float)py + 0.5f; 2698 const float nx = wx / dstW; 2699 const float ny = wy / dstH; 2700 2701 const bool tri0 = nx + ny - posEps <= 1.0f; 2702 const bool tri1 = nx + ny + posEps >= 1.0f; 2703 2704 bool isOk = false; 2705 2706 DE_ASSERT(tri0 || tri1); 2707 2708 // Pixel can belong to either of the triangles if it lies close enough to the edge. 2709 for (int triNdx = (tri0?0:1); triNdx <= (tri1?1:0); triNdx++) 2710 { 2711 const float triWx = triNdx ? dstW - wx : wx; 2712 const float triWy = triNdx ? dstH - wy : wy; 2713 const float triNx = triNdx ? 1.0f - nx : nx; 2714 const float triNy = triNdx ? 1.0f - ny : ny; 2715 2716 const tcu::Vec4 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), 2717 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy), 2718 projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy), 2719 projectedTriInterpolate(triQ[triNdx], triW[triNdx], triNx, triNy)); 2720 const tcu::Vec3 coordDx (triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), 2721 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy), 2722 triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)); 2723 const tcu::Vec3 coordDy (triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), 2724 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx), 2725 triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)); 2726 2727 tcu::Vec2 lodBounds = tcu::computeCubeLodBoundsFromDerivates(coord.toWidth<3>(), coordDx, coordDy, srcSize, lodPrec); 2728 2729 // Compute lod bounds across lodOffsets range. 2730 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) 2731 { 2732 const float wxo = triWx + lodOffsets[lodOffsNdx].x(); 2733 const float wyo = triWy + lodOffsets[lodOffsNdx].y(); 2734 const float nxo = wxo/dstW; 2735 const float nyo = wyo/dstH; 2736 2737 const tcu::Vec3 coordO (projectedTriInterpolate(triS[triNdx], triW[triNdx], nxo, nyo), 2738 projectedTriInterpolate(triT[triNdx], triW[triNdx], nxo, nyo), 2739 projectedTriInterpolate(triR[triNdx], triW[triNdx], nxo, nyo)); 2740 const tcu::Vec3 coordDxo (triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), 2741 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo), 2742 triDerivateX(triR[triNdx], triW[triNdx], wxo, dstW, nyo)); 2743 const tcu::Vec3 coordDyo (triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), 2744 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo), 2745 triDerivateY(triR[triNdx], triW[triNdx], wyo, dstH, nxo)); 2746 const tcu::Vec2 lodO = tcu::computeCubeLodBoundsFromDerivates(coordO, coordDxo, coordDyo, srcSize, lodPrec); 2747 2748 lodBounds.x() = de::min(lodBounds.x(), lodO.x()); 2749 lodBounds.y() = de::max(lodBounds.y(), lodO.y()); 2750 } 2751 2752 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); 2753 2754 if (tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coordBits, coord, clampedLod, resPix)) 2755 { 2756 isOk = true; 2757 break; 2758 } 2759 } 2760 2761 if (!isOk) 2762 { 2763 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py); 2764 numFailed += 1; 2765 } 2766 } 2767 } 2768 } 2769 2770 return numFailed; 2771 } 2772 2773 bool verifyTextureResult (tcu::TestContext& testCtx, 2774 const tcu::ConstPixelBufferAccess& result, 2775 const tcu::TextureCubeArrayView& src, 2776 const float* texCoord, 2777 const ReferenceParams& sampleParams, 2778 const tcu::LookupPrecision& lookupPrec, 2779 const tcu::IVec4& coordBits, 2780 const tcu::LodPrecision& lodPrec, 2781 const tcu::PixelFormat& pixelFormat) 2782 { 2783 tcu::TestLog& log = testCtx.getLog(); 2784 tcu::Surface reference (result.getWidth(), result.getHeight()); 2785 tcu::Surface errorMask (result.getWidth(), result.getHeight()); 2786 int numFailedPixels; 2787 2788 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask); 2789 2790 sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams); 2791 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, coordBits, lodPrec, testCtx.getWatchDog()); 2792 2793 if (numFailedPixels > 0) 2794 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage; 2795 2796 log << TestLog::ImageSet("VerifyResult", "Verification result") 2797 << TestLog::Image("Rendered", "Rendered image", result); 2798 2799 if (numFailedPixels > 0) 2800 { 2801 log << TestLog::Image("Reference", "Ideal reference image", reference) 2802 << TestLog::Image("ErrorMask", "Error mask", errorMask); 2803 } 2804 2805 log << TestLog::EndImageSet; 2806 2807 return numFailedPixels == 0; 2808 } 2809 2810 // Shadow lookup verification 2811 2812 int computeTextureCompareDiff (const tcu::ConstPixelBufferAccess& result, 2813 const tcu::ConstPixelBufferAccess& reference, 2814 const tcu::PixelBufferAccess& errorMask, 2815 const tcu::Texture2DView& src, 2816 const float* texCoord, 2817 const ReferenceParams& sampleParams, 2818 const tcu::TexComparePrecision& comparePrec, 2819 const tcu::LodPrecision& lodPrec, 2820 const tcu::Vec3& nonShadowThreshold) 2821 { 2822 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); 2823 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); 2824 2825 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[2+0], texCoord[4+0], texCoord[6+0]); 2826 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[2+1], texCoord[4+1], texCoord[6+1]); 2827 2828 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); 2829 const float dstW = float(dstSize.x()); 2830 const float dstH = float(dstSize.y()); 2831 const tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight()); 2832 2833 // Coordinates and lod per triangle. 2834 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 2835 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 2836 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) }; 2837 2838 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); 2839 2840 int numFailed = 0; 2841 2842 const tcu::Vec2 lodOffsets[] = 2843 { 2844 tcu::Vec2(-1, 0), 2845 tcu::Vec2(+1, 0), 2846 tcu::Vec2( 0, -1), 2847 tcu::Vec2( 0, +1), 2848 }; 2849 2850 tcu::clear(errorMask, tcu::RGBA::green.toVec()); 2851 2852 for (int py = 0; py < result.getHeight(); py++) 2853 { 2854 for (int px = 0; px < result.getWidth(); px++) 2855 { 2856 const tcu::Vec4 resPix = result.getPixel(px, py); 2857 const tcu::Vec4 refPix = reference.getPixel(px, py); 2858 2859 // Other channels should trivially match to reference. 2860 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(refPix.swizzle(1,2,3) - resPix.swizzle(1,2,3)), nonShadowThreshold))) 2861 { 2862 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py); 2863 numFailed += 1; 2864 continue; 2865 } 2866 2867 // Reference result is known to be a valid result, we can 2868 // skip verification if thes results are equal 2869 if (resPix.x() != refPix.x()) 2870 { 2871 const float wx = (float)px + 0.5f; 2872 const float wy = (float)py + 0.5f; 2873 const float nx = wx / dstW; 2874 const float ny = wy / dstH; 2875 2876 const int triNdx = nx + ny >= 1.0f ? 1 : 0; 2877 const float triWx = triNdx ? dstW - wx : wx; 2878 const float triWy = triNdx ? dstH - wy : wy; 2879 const float triNx = triNdx ? 1.0f - nx : nx; 2880 const float triNy = triNdx ? 1.0f - ny : ny; 2881 2882 const tcu::Vec2 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), 2883 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy)); 2884 const tcu::Vec2 coordDx = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), 2885 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize.asFloat(); 2886 const tcu::Vec2 coordDy = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), 2887 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize.asFloat(); 2888 2889 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDy.x(), coordDy.y(), lodPrec); 2890 2891 // Compute lod bounds across lodOffsets range. 2892 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) 2893 { 2894 const float wxo = triWx + lodOffsets[lodOffsNdx].x(); 2895 const float wyo = triWy + lodOffsets[lodOffsNdx].y(); 2896 const float nxo = wxo/dstW; 2897 const float nyo = wyo/dstH; 2898 2899 const tcu::Vec2 coordDxo = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), 2900 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize.asFloat(); 2901 const tcu::Vec2 coordDyo = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), 2902 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize.asFloat(); 2903 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDyo.x(), coordDyo.y(), lodPrec); 2904 2905 lodBounds.x() = de::min(lodBounds.x(), lodO.x()); 2906 lodBounds.y() = de::max(lodBounds.y(), lodO.y()); 2907 } 2908 2909 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); 2910 const bool isOk = tcu::isTexCompareResultValid(src, sampleParams.sampler, comparePrec, coord, clampedLod, sampleParams.ref, resPix.x()); 2911 2912 if (!isOk) 2913 { 2914 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py); 2915 numFailed += 1; 2916 } 2917 } 2918 } 2919 } 2920 2921 return numFailed; 2922 } 2923 2924 int computeTextureCompareDiff (const tcu::ConstPixelBufferAccess& result, 2925 const tcu::ConstPixelBufferAccess& reference, 2926 const tcu::PixelBufferAccess& errorMask, 2927 const tcu::TextureCubeView& src, 2928 const float* texCoord, 2929 const ReferenceParams& sampleParams, 2930 const tcu::TexComparePrecision& comparePrec, 2931 const tcu::LodPrecision& lodPrec, 2932 const tcu::Vec3& nonShadowThreshold) 2933 { 2934 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); 2935 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); 2936 2937 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]); 2938 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]); 2939 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]); 2940 2941 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); 2942 const float dstW = float(dstSize.x()); 2943 const float dstH = float(dstSize.y()); 2944 const int srcSize = src.getSize(); 2945 2946 // Coordinates per triangle. 2947 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 2948 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 2949 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) }; 2950 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) }; 2951 2952 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); 2953 2954 int numFailed = 0; 2955 2956 const tcu::Vec2 lodOffsets[] = 2957 { 2958 tcu::Vec2(-1, 0), 2959 tcu::Vec2(+1, 0), 2960 tcu::Vec2( 0, -1), 2961 tcu::Vec2( 0, +1), 2962 }; 2963 2964 tcu::clear(errorMask, tcu::RGBA::green.toVec()); 2965 2966 for (int py = 0; py < result.getHeight(); py++) 2967 { 2968 for (int px = 0; px < result.getWidth(); px++) 2969 { 2970 const tcu::Vec4 resPix = result.getPixel(px, py); 2971 const tcu::Vec4 refPix = reference.getPixel(px, py); 2972 2973 // Other channels should trivially match to reference. 2974 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(refPix.swizzle(1,2,3) - resPix.swizzle(1,2,3)), nonShadowThreshold))) 2975 { 2976 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py); 2977 numFailed += 1; 2978 continue; 2979 } 2980 2981 // Reference result is known to be a valid result, we can 2982 // skip verification if thes results are equal 2983 if (resPix.x() != refPix.x()) 2984 { 2985 const float wx = (float)px + 0.5f; 2986 const float wy = (float)py + 0.5f; 2987 const float nx = wx / dstW; 2988 const float ny = wy / dstH; 2989 2990 const int triNdx = nx + ny >= 1.0f ? 1 : 0; 2991 const float triWx = triNdx ? dstW - wx : wx; 2992 const float triWy = triNdx ? dstH - wy : wy; 2993 const float triNx = triNdx ? 1.0f - nx : nx; 2994 const float triNy = triNdx ? 1.0f - ny : ny; 2995 2996 const tcu::Vec3 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), 2997 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy), 2998 projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy)); 2999 const tcu::Vec3 coordDx (triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), 3000 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy), 3001 triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)); 3002 const tcu::Vec3 coordDy (triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), 3003 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx), 3004 triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)); 3005 3006 tcu::Vec2 lodBounds = tcu::computeCubeLodBoundsFromDerivates(coord, coordDx, coordDy, srcSize, lodPrec); 3007 3008 // Compute lod bounds across lodOffsets range. 3009 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) 3010 { 3011 const float wxo = triWx + lodOffsets[lodOffsNdx].x(); 3012 const float wyo = triWy + lodOffsets[lodOffsNdx].y(); 3013 const float nxo = wxo/dstW; 3014 const float nyo = wyo/dstH; 3015 3016 const tcu::Vec3 coordO (projectedTriInterpolate(triS[triNdx], triW[triNdx], nxo, nyo), 3017 projectedTriInterpolate(triT[triNdx], triW[triNdx], nxo, nyo), 3018 projectedTriInterpolate(triR[triNdx], triW[triNdx], nxo, nyo)); 3019 const tcu::Vec3 coordDxo (triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), 3020 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo), 3021 triDerivateX(triR[triNdx], triW[triNdx], wxo, dstW, nyo)); 3022 const tcu::Vec3 coordDyo (triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), 3023 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo), 3024 triDerivateY(triR[triNdx], triW[triNdx], wyo, dstH, nxo)); 3025 const tcu::Vec2 lodO = tcu::computeCubeLodBoundsFromDerivates(coordO, coordDxo, coordDyo, srcSize, lodPrec); 3026 3027 lodBounds.x() = de::min(lodBounds.x(), lodO.x()); 3028 lodBounds.y() = de::max(lodBounds.y(), lodO.y()); 3029 } 3030 3031 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); 3032 const bool isOk = tcu::isTexCompareResultValid(src, sampleParams.sampler, comparePrec, coord, clampedLod, sampleParams.ref, resPix.x()); 3033 3034 if (!isOk) 3035 { 3036 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py); 3037 numFailed += 1; 3038 } 3039 } 3040 } 3041 } 3042 3043 return numFailed; 3044 } 3045 3046 int computeTextureCompareDiff (const tcu::ConstPixelBufferAccess& result, 3047 const tcu::ConstPixelBufferAccess& reference, 3048 const tcu::PixelBufferAccess& errorMask, 3049 const tcu::Texture2DArrayView& src, 3050 const float* texCoord, 3051 const ReferenceParams& sampleParams, 3052 const tcu::TexComparePrecision& comparePrec, 3053 const tcu::LodPrecision& lodPrec, 3054 const tcu::Vec3& nonShadowThreshold) 3055 { 3056 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); 3057 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight()); 3058 3059 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]); 3060 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]); 3061 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]); 3062 3063 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight()); 3064 const float dstW = float(dstSize.x()); 3065 const float dstH = float(dstSize.y()); 3066 const tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight()); 3067 3068 // Coordinates and lod per triangle. 3069 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) }; 3070 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) }; 3071 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) }; 3072 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) }; 3073 3074 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f); 3075 3076 int numFailed = 0; 3077 3078 const tcu::Vec2 lodOffsets[] = 3079 { 3080 tcu::Vec2(-1, 0), 3081 tcu::Vec2(+1, 0), 3082 tcu::Vec2( 0, -1), 3083 tcu::Vec2( 0, +1), 3084 }; 3085 3086 tcu::clear(errorMask, tcu::RGBA::green.toVec()); 3087 3088 for (int py = 0; py < result.getHeight(); py++) 3089 { 3090 for (int px = 0; px < result.getWidth(); px++) 3091 { 3092 const tcu::Vec4 resPix = result.getPixel(px, py); 3093 const tcu::Vec4 refPix = reference.getPixel(px, py); 3094 3095 // Other channels should trivially match to reference. 3096 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(refPix.swizzle(1,2,3) - resPix.swizzle(1,2,3)), nonShadowThreshold))) 3097 { 3098 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py); 3099 numFailed += 1; 3100 continue; 3101 } 3102 3103 // Reference result is known to be a valid result, we can 3104 // skip verification if thes results are equal 3105 if (resPix.x() != refPix.x()) 3106 { 3107 const float wx = (float)px + 0.5f; 3108 const float wy = (float)py + 0.5f; 3109 const float nx = wx / dstW; 3110 const float ny = wy / dstH; 3111 3112 const int triNdx = nx + ny >= 1.0f ? 1 : 0; 3113 const float triWx = triNdx ? dstW - wx : wx; 3114 const float triWy = triNdx ? dstH - wy : wy; 3115 const float triNx = triNdx ? 1.0f - nx : nx; 3116 const float triNy = triNdx ? 1.0f - ny : ny; 3117 3118 const tcu::Vec3 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy), 3119 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy), 3120 projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy)); 3121 const tcu::Vec2 coordDx = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy), 3122 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize.asFloat(); 3123 const tcu::Vec2 coordDy = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx), 3124 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize.asFloat(); 3125 3126 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDy.x(), coordDy.y(), lodPrec); 3127 3128 // Compute lod bounds across lodOffsets range. 3129 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++) 3130 { 3131 const float wxo = triWx + lodOffsets[lodOffsNdx].x(); 3132 const float wyo = triWy + lodOffsets[lodOffsNdx].y(); 3133 const float nxo = wxo/dstW; 3134 const float nyo = wyo/dstH; 3135 3136 const tcu::Vec2 coordDxo = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo), 3137 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize.asFloat(); 3138 const tcu::Vec2 coordDyo = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo), 3139 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize.asFloat(); 3140 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDyo.x(), coordDyo.y(), lodPrec); 3141 3142 lodBounds.x() = de::min(lodBounds.x(), lodO.x()); 3143 lodBounds.y() = de::max(lodBounds.y(), lodO.y()); 3144 } 3145 3146 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec); 3147 const bool isOk = tcu::isTexCompareResultValid(src, sampleParams.sampler, comparePrec, coord, clampedLod, sampleParams.ref, resPix.x()); 3148 3149 if (!isOk) 3150 { 3151 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py); 3152 numFailed += 1; 3153 } 3154 } 3155 } 3156 } 3157 3158 return numFailed; 3159 } 3160 3161 // Mipmap generation comparison. 3162 3163 static int compareGenMipmapBilinear (const tcu::ConstPixelBufferAccess& dst, const tcu::ConstPixelBufferAccess& src, const tcu::PixelBufferAccess& errorMask, const GenMipmapPrecision& precision) 3164 { 3165 DE_ASSERT(dst.getDepth() == 1 && src.getDepth() == 1); // \todo [2013-10-29 pyry] 3D textures. 3166 3167 const float dstW = float(dst.getWidth()); 3168 const float dstH = float(dst.getHeight()); 3169 const float srcW = float(src.getWidth()); 3170 const float srcH = float(src.getHeight()); 3171 int numFailed = 0; 3172 3173 // Translation to lookup verification parameters. 3174 const tcu::Sampler sampler (tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, 3175 tcu::Sampler::LINEAR, tcu::Sampler::LINEAR, 0.0f, false /* non-normalized coords */); 3176 tcu::LookupPrecision lookupPrec; 3177 3178 lookupPrec.colorThreshold = precision.colorThreshold; 3179 lookupPrec.colorMask = precision.colorMask; 3180 lookupPrec.coordBits = tcu::IVec3(22); 3181 lookupPrec.uvwBits = precision.filterBits; 3182 3183 for (int y = 0; y < dst.getHeight(); y++) 3184 for (int x = 0; x < dst.getWidth(); x++) 3185 { 3186 const tcu::Vec4 result = dst.getPixel(x, y); 3187 const float cx = (float(x)+0.5f) / dstW * srcW; 3188 const float cy = (float(y)+0.5f) / dstH * srcH; 3189 const bool isOk = tcu::isLinearSampleResultValid(src, sampler, lookupPrec, tcu::Vec2(cx, cy), 0, result); 3190 3191 errorMask.setPixel(isOk ? tcu::RGBA::green.toVec() : tcu::RGBA::red.toVec(), x, y); 3192 if (!isOk) 3193 numFailed += 1; 3194 } 3195 3196 return numFailed; 3197 } 3198 3199 static int compareGenMipmapBox (const tcu::ConstPixelBufferAccess& dst, const tcu::ConstPixelBufferAccess& src, const tcu::PixelBufferAccess& errorMask, const GenMipmapPrecision& precision) 3200 { 3201 DE_ASSERT(dst.getDepth() == 1 && src.getDepth() == 1); // \todo [2013-10-29 pyry] 3D textures. 3202 3203 const float dstW = float(dst.getWidth()); 3204 const float dstH = float(dst.getHeight()); 3205 const float srcW = float(src.getWidth()); 3206 const float srcH = float(src.getHeight()); 3207 int numFailed = 0; 3208 3209 // Translation to lookup verification parameters. 3210 const tcu::Sampler sampler (tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, 3211 tcu::Sampler::LINEAR, tcu::Sampler::LINEAR, 0.0f, false /* non-normalized coords */); 3212 tcu::LookupPrecision lookupPrec; 3213 3214 lookupPrec.colorThreshold = precision.colorThreshold; 3215 lookupPrec.colorMask = precision.colorMask; 3216 lookupPrec.coordBits = tcu::IVec3(22); 3217 lookupPrec.uvwBits = precision.filterBits; 3218 3219 for (int y = 0; y < dst.getHeight(); y++) 3220 for (int x = 0; x < dst.getWidth(); x++) 3221 { 3222 const tcu::Vec4 result = dst.getPixel(x, y); 3223 const float cx = deFloatFloor(float(x) / dstW * srcW) + 1.0f; 3224 const float cy = deFloatFloor(float(y) / dstH * srcH) + 1.0f; 3225 const bool isOk = tcu::isLinearSampleResultValid(src, sampler, lookupPrec, tcu::Vec2(cx, cy), 0, result); 3226 3227 errorMask.setPixel(isOk ? tcu::RGBA::green.toVec() : tcu::RGBA::red.toVec(), x, y); 3228 if (!isOk) 3229 numFailed += 1; 3230 } 3231 3232 return numFailed; 3233 } 3234 3235 static int compareGenMipmapVeryLenient (const tcu::ConstPixelBufferAccess& dst, const tcu::ConstPixelBufferAccess& src, const tcu::PixelBufferAccess& errorMask, const GenMipmapPrecision& precision) 3236 { 3237 DE_ASSERT(dst.getDepth() == 1 && src.getDepth() == 1); // \todo [2013-10-29 pyry] 3D textures. 3238 DE_UNREF(precision); 3239 3240 const float dstW = float(dst.getWidth()); 3241 const float dstH = float(dst.getHeight()); 3242 const float srcW = float(src.getWidth()); 3243 const float srcH = float(src.getHeight()); 3244 int numFailed = 0; 3245 3246 for (int y = 0; y < dst.getHeight(); y++) 3247 for (int x = 0; x < dst.getWidth(); x++) 3248 { 3249 const tcu::Vec4 result = dst.getPixel(x, y); 3250 const int minX = deFloorFloatToInt32(float(x-0.5f) / dstW * srcW); 3251 const int minY = deFloorFloatToInt32(float(y-0.5f) / dstH * srcH); 3252 const int maxX = deCeilFloatToInt32(float(x+1.5f) / dstW * srcW); 3253 const int maxY = deCeilFloatToInt32(float(y+1.5f) / dstH * srcH); 3254 tcu::Vec4 minVal, maxVal; 3255 bool isOk; 3256 3257 DE_ASSERT(minX < maxX && minY < maxY); 3258 3259 for (int ky = minY; ky <= maxY; ky++) 3260 { 3261 for (int kx = minX; kx <= maxX; kx++) 3262 { 3263 const int sx = de::clamp(kx, 0, src.getWidth()-1); 3264 const int sy = de::clamp(ky, 0, src.getHeight()-1); 3265 const tcu::Vec4 sample = src.getPixel(sx, sy); 3266 3267 if (ky == minY && kx == minX) 3268 { 3269 minVal = sample; 3270 maxVal = sample; 3271 } 3272 else 3273 { 3274 minVal = min(sample, minVal); 3275 maxVal = max(sample, maxVal); 3276 } 3277 } 3278 } 3279 3280 isOk = boolAll(logicalAnd(lessThanEqual(minVal, result), lessThanEqual(result, maxVal))); 3281 3282 errorMask.setPixel(isOk ? tcu::RGBA::green.toVec() : tcu::RGBA::red.toVec(), x, y); 3283 if (!isOk) 3284 numFailed += 1; 3285 } 3286 3287 return numFailed; 3288 } 3289 3290 qpTestResult compareGenMipmapResult (tcu::TestLog& log, const tcu::Texture2D& resultTexture, const tcu::Texture2D& level0Reference, const GenMipmapPrecision& precision) 3291 { 3292 qpTestResult result = QP_TEST_RESULT_PASS; 3293 3294 // Special comparison for level 0. 3295 { 3296 const tcu::Vec4 threshold = select(precision.colorThreshold, tcu::Vec4(1.0f), precision.colorMask); 3297 const bool level0Ok = tcu::floatThresholdCompare(log, "Level0", "Level 0", level0Reference.getLevel(0), resultTexture.getLevel(0), threshold, tcu::COMPARE_LOG_RESULT); 3298 3299 if (!level0Ok) 3300 { 3301 log << TestLog::Message << "ERROR: Level 0 comparison failed!" << TestLog::EndMessage; 3302 result = QP_TEST_RESULT_FAIL; 3303 } 3304 } 3305 3306 for (int levelNdx = 1; levelNdx < resultTexture.getNumLevels(); levelNdx++) 3307 { 3308 const tcu::ConstPixelBufferAccess src = resultTexture.getLevel(levelNdx-1); 3309 const tcu::ConstPixelBufferAccess dst = resultTexture.getLevel(levelNdx); 3310 tcu::Surface errorMask (dst.getWidth(), dst.getHeight()); 3311 bool levelOk = false; 3312 3313 // Try different comparisons in quality order. 3314 3315 if (!levelOk) 3316 { 3317 const int numFailed = compareGenMipmapBilinear(dst, src, errorMask.getAccess(), precision); 3318 if (numFailed == 0) 3319 levelOk = true; 3320 else 3321 log << TestLog::Message << "WARNING: Level " << levelNdx << " comparison to bilinear method failed, found " << numFailed << " invalid pixels." << TestLog::EndMessage; 3322 } 3323 3324 if (!levelOk) 3325 { 3326 const int numFailed = compareGenMipmapBox(dst, src, errorMask.getAccess(), precision); 3327 if (numFailed == 0) 3328 levelOk = true; 3329 else 3330 log << TestLog::Message << "WARNING: Level " << levelNdx << " comparison to box method failed, found " << numFailed << " invalid pixels." << TestLog::EndMessage; 3331 } 3332 3333 // At this point all high-quality methods have been used. 3334 if (!levelOk && result == QP_TEST_RESULT_PASS) 3335 result = QP_TEST_RESULT_QUALITY_WARNING; 3336 3337 if (!levelOk) 3338 { 3339 const int numFailed = compareGenMipmapVeryLenient(dst, src, errorMask.getAccess(), precision); 3340 if (numFailed == 0) 3341 levelOk = true; 3342 else 3343 log << TestLog::Message << "ERROR: Level " << levelNdx << " appears to contain " << numFailed << " completely wrong pixels, failing case!" << TestLog::EndMessage; 3344 } 3345 3346 if (!levelOk) 3347 result = QP_TEST_RESULT_FAIL; 3348 3349 log << TestLog::ImageSet(string("Level") + de::toString(levelNdx), string("Level ") + de::toString(levelNdx) + " result") 3350 << TestLog::Image("Result", "Result", dst); 3351 3352 if (!levelOk) 3353 log << TestLog::Image("ErrorMask", "Error mask", errorMask); 3354 3355 log << TestLog::EndImageSet; 3356 } 3357 3358 return result; 3359 } 3360 3361 qpTestResult compareGenMipmapResult (tcu::TestLog& log, const tcu::TextureCube& resultTexture, const tcu::TextureCube& level0Reference, const GenMipmapPrecision& precision) 3362 { 3363 qpTestResult result = QP_TEST_RESULT_PASS; 3364 3365 static const char* s_faceNames[] = { "-X", "+X", "-Y", "+Y", "-Z", "+Z" }; 3366 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_faceNames) == tcu::CUBEFACE_LAST); 3367 3368 // Special comparison for level 0. 3369 for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++) 3370 { 3371 const tcu::CubeFace face = tcu::CubeFace(faceNdx); 3372 const tcu::Vec4 threshold = select(precision.colorThreshold, tcu::Vec4(1.0f), precision.colorMask); 3373 const bool level0Ok = tcu::floatThresholdCompare(log, 3374 ("Level0Face" + de::toString(faceNdx)).c_str(), 3375 (string("Level 0, face ") + s_faceNames[face]).c_str(), 3376 level0Reference.getLevelFace(0, face), 3377 resultTexture.getLevelFace(0, face), 3378 threshold, tcu::COMPARE_LOG_RESULT); 3379 3380 if (!level0Ok) 3381 { 3382 log << TestLog::Message << "ERROR: Level 0, face " << s_faceNames[face] << " comparison failed!" << TestLog::EndMessage; 3383 result = QP_TEST_RESULT_FAIL; 3384 } 3385 } 3386 3387 for (int levelNdx = 1; levelNdx < resultTexture.getNumLevels(); levelNdx++) 3388 { 3389 for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++) 3390 { 3391 const tcu::CubeFace face = tcu::CubeFace(faceNdx); 3392 const char* faceName = s_faceNames[face]; 3393 const tcu::ConstPixelBufferAccess src = resultTexture.getLevelFace(levelNdx-1, face); 3394 const tcu::ConstPixelBufferAccess dst = resultTexture.getLevelFace(levelNdx, face); 3395 tcu::Surface errorMask (dst.getWidth(), dst.getHeight()); 3396 bool levelOk = false; 3397 3398 // Try different comparisons in quality order. 3399 3400 if (!levelOk) 3401 { 3402 const int numFailed = compareGenMipmapBilinear(dst, src, errorMask.getAccess(), precision); 3403 if (numFailed == 0) 3404 levelOk = true; 3405 else 3406 log << TestLog::Message << "WARNING: Level " << levelNdx << ", face " << faceName << " comparison to bilinear method failed, found " << numFailed << " invalid pixels." << TestLog::EndMessage; 3407 } 3408 3409 if (!levelOk) 3410 { 3411 const int numFailed = compareGenMipmapBox(dst, src, errorMask.getAccess(), precision); 3412 if (numFailed == 0) 3413 levelOk = true; 3414 else 3415 log << TestLog::Message << "WARNING: Level " << levelNdx << ", face " << faceName <<" comparison to box method failed, found " << numFailed << " invalid pixels." << TestLog::EndMessage; 3416 } 3417 3418 // At this point all high-quality methods have been used. 3419 if (!levelOk && result == QP_TEST_RESULT_PASS) 3420 result = QP_TEST_RESULT_QUALITY_WARNING; 3421 3422 if (!levelOk) 3423 { 3424 const int numFailed = compareGenMipmapVeryLenient(dst, src, errorMask.getAccess(), precision); 3425 if (numFailed == 0) 3426 levelOk = true; 3427 else 3428 log << TestLog::Message << "ERROR: Level " << levelNdx << ", face " << faceName << " appears to contain " << numFailed << " completely wrong pixels, failing case!" << TestLog::EndMessage; 3429 } 3430 3431 if (!levelOk) 3432 result = QP_TEST_RESULT_FAIL; 3433 3434 log << TestLog::ImageSet(string("Level") + de::toString(levelNdx) + "Face" + de::toString(faceNdx), string("Level ") + de::toString(levelNdx) + ", face " + string(faceName) + " result") 3435 << TestLog::Image("Result", "Result", dst); 3436 3437 if (!levelOk) 3438 log << TestLog::Image("ErrorMask", "Error mask", errorMask); 3439 3440 log << TestLog::EndImageSet; 3441 } 3442 } 3443 3444 return result; 3445 } 3446 3447 // Logging utilities. 3448 3449 std::ostream& operator<< (std::ostream& str, const LogGradientFmt& fmt) 3450 { 3451 return str << "(R: " << fmt.valueMin->x() << " -> " << fmt.valueMax->x() << ", " 3452 << "G: " << fmt.valueMin->y() << " -> " << fmt.valueMax->y() << ", " 3453 << "B: " << fmt.valueMin->z() << " -> " << fmt.valueMax->z() << ", " 3454 << "A: " << fmt.valueMin->w() << " -> " << fmt.valueMax->w() << ")"; 3455 } 3456 3457 } // TextureTestUtil 3458 } // gls 3459 } // deqp 3460