1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL (ES) Module
3 * -----------------------------------------------
5 * Copyright 2014 The Android Open Source Project
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Texture test utilities.
22 *//*--------------------------------------------------------------------*/
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"
50 namespace TextureTestUtil
58 SamplerType getSamplerType (tcu::TextureFormat format)
60 using tcu::TextureFormat;
64 case TextureFormat::SIGNED_INT8:
65 case TextureFormat::SIGNED_INT16:
66 case TextureFormat::SIGNED_INT32:
67 return SAMPLERTYPE_INT;
69 case TextureFormat::UNSIGNED_INT8:
70 case TextureFormat::UNSIGNED_INT32:
71 case TextureFormat::UNSIGNED_INT_1010102_REV:
72 return SAMPLERTYPE_UINT;
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;
80 return SAMPLERTYPE_FLOAT;
84 SamplerType getFetchSamplerType (tcu::TextureFormat format)
86 using tcu::TextureFormat;
90 case TextureFormat::SIGNED_INT8:
91 case TextureFormat::SIGNED_INT16:
92 case TextureFormat::SIGNED_INT32:
93 return SAMPLERTYPE_FETCH_INT;
95 case TextureFormat::UNSIGNED_INT8:
96 case TextureFormat::UNSIGNED_INT32:
97 case TextureFormat::UNSIGNED_INT_1010102_REV:
98 return SAMPLERTYPE_FETCH_UINT;
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;
106 return SAMPLERTYPE_FETCH_FLOAT;
110 static tcu::Texture1DView getSubView (const tcu::Texture1DView& view, int baseLevel, int maxLevel)
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);
118 static tcu::Texture2DView getSubView (const tcu::Texture2DView& view, int baseLevel, int maxLevel)
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);
126 static tcu::TextureCubeView getSubView (const tcu::TextureCubeView& view, int baseLevel, int maxLevel)
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];
133 for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
134 levels[face] = view.getFaceLevels((tcu::CubeFace)face) + clampedBase;
136 return tcu::TextureCubeView(numLevels, levels);
139 static tcu::Texture3DView getSubView (const tcu::Texture3DView& view, int baseLevel, int maxLevel)
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);
147 static tcu::TextureCubeArrayView getSubView (const tcu::TextureCubeArrayView& view, int baseLevel, int maxLevel)
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);
155 inline float linearInterpolate (float t, float minVal, float maxVal)
157 return minVal + (maxVal - minVal) * t;
160 inline tcu::Vec4 linearInterpolate (float t, const tcu::Vec4& a, const tcu::Vec4& b)
162 return a + (b - a) * t;
165 inline float bilinearInterpolate (float x, float y, const tcu::Vec4& quad)
167 float w00 = (1.0f-x)*(1.0f-y);
168 float w01 = (1.0f-x)*y;
169 float w10 = x*(1.0f-y);
171 return quad.x()*w00 + quad.y()*w10 + quad.z()*w01 + quad.w()*w11;
174 inline float triangleInterpolate (float v0, float v1, float v2, float x, float y)
176 return v0 + (v2-v0)*x + (v1-v0)*y;
179 inline float triangleInterpolate (const tcu::Vec3& v, float x, float y)
181 return triangleInterpolate(v.x(), v.y(), v.z(), x, y);
184 inline float triQuadInterpolate (float x, float y, const tcu::Vec4& quad)
186 // \note Top left fill rule.
188 return triangleInterpolate(quad.x(), quad.y(), quad.z(), x, y);
190 return triangleInterpolate(quad.w(), quad.z(), quad.y(), 1.0f-x, 1.0f-y);
193 SurfaceAccess::SurfaceAccess (tcu::Surface& surface, const tcu::PixelFormat& colorFmt, int x, int y, int width, int height)
194 : m_surface (&surface)
195 , m_colorMask (getColorMask(colorFmt))
203 SurfaceAccess::SurfaceAccess (tcu::Surface& surface, const tcu::PixelFormat& colorFmt)
204 : m_surface (&surface)
205 , m_colorMask (getColorMask(colorFmt))
208 , m_width (surface.getWidth())
209 , m_height (surface.getHeight())
213 SurfaceAccess::SurfaceAccess (const SurfaceAccess& parent, int x, int y, int width, int height)
214 : m_surface (parent.m_surface)
215 , m_colorMask (parent.m_colorMask)
216 , m_x (parent.m_x + x)
217 , m_y (parent.m_y + y)
223 // 1D lookup LOD computation.
225 inline float computeLodFromDerivates (LodMode mode, float dudx, float dudy)
230 // \note [mika] Min and max bounds equal to exact with 1D textures
232 case LODMODE_MIN_BOUND:
233 case LODMODE_MAX_BOUND:
234 p = de::max(deFloatAbs(dudx), deFloatAbs(dudy));
241 return deFloatLog2(p);
244 static float computeNonProjectedTriLod (LodMode mode, const tcu::IVec2& dstSize, deInt32 srcSize, const tcu::Vec3& sq)
246 float dux = (sq.z() - sq.x()) * (float)srcSize;
247 float duy = (sq.y() - sq.x()) * (float)srcSize;
248 float dx = (float)dstSize.x();
249 float dy = (float)dstSize.y();
251 return computeLodFromDerivates(mode, dux/dx, duy/dy);
254 // 2D lookup LOD computation.
256 inline float computeLodFromDerivates (LodMode mode, float dudx, float dvdx, float dudy, float dvdy)
262 p = de::max(deFloatSqrt(dudx*dudx + dvdx*dvdx), deFloatSqrt(dudy*dudy + dvdy*dvdy));
265 case LODMODE_MIN_BOUND:
266 case LODMODE_MAX_BOUND:
268 float mu = de::max(deFloatAbs(dudx), deFloatAbs(dudy));
269 float mv = de::max(deFloatAbs(dvdx), deFloatAbs(dvdy));
271 p = mode == LODMODE_MIN_BOUND ? de::max(mu, mv) : mu + mv;
279 return deFloatLog2(p);
282 static float computeNonProjectedTriLod (LodMode mode, const tcu::IVec2& dstSize, const tcu::IVec2& srcSize, const tcu::Vec3& sq, const tcu::Vec3& tq)
284 float dux = (sq.z() - sq.x()) * (float)srcSize.x();
285 float duy = (sq.y() - sq.x()) * (float)srcSize.x();
286 float dvx = (tq.z() - tq.x()) * (float)srcSize.y();
287 float dvy = (tq.y() - tq.x()) * (float)srcSize.y();
288 float dx = (float)dstSize.x();
289 float dy = (float)dstSize.y();
291 return computeLodFromDerivates(mode, dux/dx, dvx/dx, duy/dy, dvy/dy);
294 // 3D lookup LOD computation.
296 inline float computeLodFromDerivates (LodMode mode, float dudx, float dvdx, float dwdx, float dudy, float dvdy, float dwdy)
302 p = de::max(deFloatSqrt(dudx*dudx + dvdx*dvdx + dwdx*dwdx), deFloatSqrt(dudy*dudy + dvdy*dvdy + dwdy*dwdy));
305 case LODMODE_MIN_BOUND:
306 case LODMODE_MAX_BOUND:
308 float mu = de::max(deFloatAbs(dudx), deFloatAbs(dudy));
309 float mv = de::max(deFloatAbs(dvdx), deFloatAbs(dvdy));
310 float mw = de::max(deFloatAbs(dwdx), deFloatAbs(dwdy));
312 p = mode == LODMODE_MIN_BOUND ? de::max(de::max(mu, mv), mw) : (mu + mv + mw);
320 return deFloatLog2(p);
323 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)
325 float dux = (sq.z() - sq.x()) * (float)srcSize.x();
326 float duy = (sq.y() - sq.x()) * (float)srcSize.x();
327 float dvx = (tq.z() - tq.x()) * (float)srcSize.y();
328 float dvy = (tq.y() - tq.x()) * (float)srcSize.y();
329 float dwx = (rq.z() - rq.x()) * (float)srcSize.z();
330 float dwy = (rq.y() - rq.x()) * (float)srcSize.z();
331 float dx = (float)dstSize.x();
332 float dy = (float)dstSize.y();
334 return computeLodFromDerivates(mode, dux/dx, dvx/dx, dwx/dx, duy/dy, dvy/dy, dwy/dy);
337 static inline float projectedTriInterpolate (const tcu::Vec3& s, const tcu::Vec3& w, float nx, float ny)
339 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]);
342 static inline float triDerivateX (const tcu::Vec3& s, const tcu::Vec3& w, float wx, float width, float ny)
344 float d = w[1]*w[2]*(width*(ny - 1.0f) + wx) - w[0]*(w[2]*width*ny + w[1]*wx);
345 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);
348 static inline float triDerivateY (const tcu::Vec3& s, const tcu::Vec3& w, float wy, float height, float nx)
350 float d = w[1]*w[2]*(height*(nx - 1.0f) + wy) - w[0]*(w[1]*height*nx + w[2]*wy);
351 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);
355 static float computeProjectedTriLod (LodMode mode, const tcu::Vec3& u, const tcu::Vec3& projection, float wx, float wy, float width, float height)
357 // Exact derivatives.
358 float dudx = triDerivateX(u, projection, wx, width, wy/height);
359 float dudy = triDerivateY(u, projection, wy, height, wx/width);
361 return computeLodFromDerivates(mode, dudx, dudy);
365 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)
367 // Exact derivatives.
368 float dudx = triDerivateX(u, projection, wx, width, wy/height);
369 float dvdx = triDerivateX(v, projection, wx, width, wy/height);
370 float dudy = triDerivateY(u, projection, wy, height, wx/width);
371 float dvdy = triDerivateY(v, projection, wy, height, wx/width);
373 return computeLodFromDerivates(mode, dudx, dvdx, dudy, dvdy);
377 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)
379 // Exact derivatives.
380 float dudx = triDerivateX(u, projection, wx, width, wy/height);
381 float dvdx = triDerivateX(v, projection, wx, width, wy/height);
382 float dwdx = triDerivateX(w, projection, wx, width, wy/height);
383 float dudy = triDerivateY(u, projection, wy, height, wx/width);
384 float dvdy = triDerivateY(v, projection, wy, height, wx/width);
385 float dwdy = triDerivateY(w, projection, wy, height, wx/width);
387 return computeLodFromDerivates(mode, dudx, dvdx, dwdx, dudy, dvdy, dwdy);
390 static inline tcu::Vec4 execSample (const tcu::Texture1DView& src, const ReferenceParams& params, float s, float lod)
392 if (params.samplerType == SAMPLERTYPE_SHADOW)
393 return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, lod), 0.0, 0.0, 1.0f);
395 return src.sample(params.sampler, s, lod);
398 static inline tcu::Vec4 execSample (const tcu::Texture2DView& src, const ReferenceParams& params, float s, float t, float lod)
400 if (params.samplerType == SAMPLERTYPE_SHADOW)
401 return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, lod), 0.0, 0.0, 1.0f);
403 return src.sample(params.sampler, s, t, lod);
406 static inline tcu::Vec4 execSample (const tcu::TextureCubeView& src, const ReferenceParams& params, float s, float t, float r, float lod)
408 if (params.samplerType == SAMPLERTYPE_SHADOW)
409 return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, r, lod), 0.0, 0.0, 1.0f);
411 return src.sample(params.sampler, s, t, r, lod);
414 static inline tcu::Vec4 execSample (const tcu::Texture2DArrayView& src, const ReferenceParams& params, float s, float t, float r, float lod)
416 if (params.samplerType == SAMPLERTYPE_SHADOW)
417 return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, r, lod), 0.0, 0.0, 1.0f);
419 return src.sample(params.sampler, s, t, r, lod);
422 static inline tcu::Vec4 execSample (const tcu::TextureCubeArrayView& src, const ReferenceParams& params, float s, float t, float r, float q, float lod)
424 if (params.samplerType == SAMPLERTYPE_SHADOW)
425 return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, r, q, lod), 0.0, 0.0, 1.0f);
427 return src.sample(params.sampler, s, t, r, q, lod);
430 static inline tcu::Vec4 execSample (const tcu::Texture1DArrayView& src, const ReferenceParams& params, float s, float t, float lod)
432 if (params.samplerType == SAMPLERTYPE_SHADOW)
433 return tcu::Vec4(src.sampleCompare(params.sampler, params.ref, s, t, lod), 0.0, 0.0, 1.0f);
435 return src.sample(params.sampler, s, t, lod);
438 static void sampleTextureNonProjected (const SurfaceAccess& dst, const tcu::Texture1DView& src, const tcu::Vec4& sq, const ReferenceParams& params)
440 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f;
442 tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight());
443 int srcSize = src.getWidth();
445 // Coordinates and lod per triangle.
446 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
447 float triLod[2] = { de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0]) + lodBias, params.minLod, params.maxLod),
448 de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1]) + lodBias, params.minLod, params.maxLod) };
450 for (int y = 0; y < dst.getHeight(); y++)
452 for (int x = 0; x < dst.getWidth(); x++)
454 float yf = ((float)y + 0.5f) / (float)dst.getHeight();
455 float xf = ((float)x + 0.5f) / (float)dst.getWidth();
457 int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule.
458 float triX = triNdx ? 1.0f-xf : xf;
459 float triY = triNdx ? 1.0f-yf : yf;
461 float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY);
462 float lod = triLod[triNdx];
464 dst.setPixel(execSample(src, params, s, lod) * params.colorScale + params.colorBias, x, y);
469 static void sampleTextureNonProjected (const SurfaceAccess& dst, const tcu::Texture2DView& src, const tcu::Vec4& sq, const tcu::Vec4& tq, const ReferenceParams& params)
471 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f;
473 tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight());
474 tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight());
476 // Coordinates and lod per triangle.
477 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
478 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
479 float triLod[2] = { de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0], triT[0]) + lodBias, params.minLod, params.maxLod),
480 de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1], triT[1]) + lodBias, params.minLod, params.maxLod) };
482 for (int y = 0; y < dst.getHeight(); y++)
484 for (int x = 0; x < dst.getWidth(); x++)
486 float yf = ((float)y + 0.5f) / (float)dst.getHeight();
487 float xf = ((float)x + 0.5f) / (float)dst.getWidth();
489 int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule.
490 float triX = triNdx ? 1.0f-xf : xf;
491 float triY = triNdx ? 1.0f-yf : yf;
493 float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY);
494 float t = triangleInterpolate(triT[triNdx].x(), triT[triNdx].y(), triT[triNdx].z(), triX, triY);
495 float lod = triLod[triNdx];
497 dst.setPixel(execSample(src, params, s, t, lod) * params.colorScale + params.colorBias, x, y);
502 static void sampleTextureProjected (const SurfaceAccess& dst, const tcu::Texture1DView& src, const tcu::Vec4& sq, const ReferenceParams& params)
504 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f;
505 float dstW = (float)dst.getWidth();
506 float dstH = (float)dst.getHeight();
508 tcu::Vec4 uq = sq * (float)src.getWidth();
510 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
511 tcu::Vec3 triU[2] = { uq.swizzle(0, 1, 2), uq.swizzle(3, 2, 1) };
512 tcu::Vec3 triW[2] = { params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1) };
514 for (int py = 0; py < dst.getHeight(); py++)
516 for (int px = 0; px < dst.getWidth(); px++)
518 float wx = (float)px + 0.5f;
519 float wy = (float)py + 0.5f;
520 float nx = wx / dstW;
521 float ny = wy / dstH;
523 int triNdx = nx + ny >= 1.0f ? 1 : 0;
524 float triWx = triNdx ? dstW - wx : wx;
525 float triWy = triNdx ? dstH - wy : wy;
526 float triNx = triNdx ? 1.0f - nx : nx;
527 float triNy = triNdx ? 1.0f - ny : ny;
529 float s = projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy);
530 float lod = computeProjectedTriLod(params.lodMode, triU[triNdx], triW[triNdx], triWx, triWy, (float)dst.getWidth(), (float)dst.getHeight())
533 dst.setPixel(execSample(src, params, s, lod) * params.colorScale + params.colorBias, px, py);
538 static void sampleTextureProjected (const SurfaceAccess& dst, const tcu::Texture2DView& src, const tcu::Vec4& sq, const tcu::Vec4& tq, const ReferenceParams& params)
540 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f;
541 float dstW = (float)dst.getWidth();
542 float dstH = (float)dst.getHeight();
544 tcu::Vec4 uq = sq * (float)src.getWidth();
545 tcu::Vec4 vq = tq * (float)src.getHeight();
547 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
548 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
549 tcu::Vec3 triU[2] = { uq.swizzle(0, 1, 2), uq.swizzle(3, 2, 1) };
550 tcu::Vec3 triV[2] = { vq.swizzle(0, 1, 2), vq.swizzle(3, 2, 1) };
551 tcu::Vec3 triW[2] = { params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1) };
553 for (int py = 0; py < dst.getHeight(); py++)
555 for (int px = 0; px < dst.getWidth(); px++)
557 float wx = (float)px + 0.5f;
558 float wy = (float)py + 0.5f;
559 float nx = wx / dstW;
560 float ny = wy / dstH;
562 int triNdx = nx + ny >= 1.0f ? 1 : 0;
563 float triWx = triNdx ? dstW - wx : wx;
564 float triWy = triNdx ? dstH - wy : wy;
565 float triNx = triNdx ? 1.0f - nx : nx;
566 float triNy = triNdx ? 1.0f - ny : ny;
568 float s = projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy);
569 float t = projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy);
570 float lod = computeProjectedTriLod(params.lodMode, triU[triNdx], triV[triNdx], triW[triNdx], triWx, triWy, (float)dst.getWidth(), (float)dst.getHeight())
573 dst.setPixel(execSample(src, params, s, t, lod) * params.colorScale + params.colorBias, px, py);
578 void sampleTexture (const SurfaceAccess& dst, const tcu::Texture2DView& src, const float* texCoord, const ReferenceParams& params)
580 const tcu::Texture2DView view = getSubView(src, params.baseLevel, params.maxLevel);
581 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[2+0], texCoord[4+0], texCoord[6+0]);
582 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[2+1], texCoord[4+1], texCoord[6+1]);
584 if (params.flags & ReferenceParams::PROJECTED)
585 sampleTextureProjected(dst, view, sq, tq, params);
587 sampleTextureNonProjected(dst, view, sq, tq, params);
590 void sampleTexture (const SurfaceAccess& dst, const tcu::Texture1DView& src, const float* texCoord, const ReferenceParams& params)
592 const tcu::Texture1DView view = getSubView(src, params.baseLevel, params.maxLevel);
593 const tcu::Vec4 sq = tcu::Vec4(texCoord[0], texCoord[1], texCoord[2], texCoord[3]);
595 if (params.flags & ReferenceParams::PROJECTED)
596 sampleTextureProjected(dst, view, sq, params);
598 sampleTextureNonProjected(dst, view, sq, params);
601 static float computeCubeLodFromDerivates (LodMode lodMode, const tcu::Vec3& coord, const tcu::Vec3& coordDx, const tcu::Vec3& coordDy, const int faceSize)
603 const tcu::CubeFace face = tcu::selectCubeFace(coord);
608 // \note Derivate signs don't matter when computing lod
611 case tcu::CUBEFACE_NEGATIVE_X:
612 case tcu::CUBEFACE_POSITIVE_X: maNdx = 0; sNdx = 2; tNdx = 1; break;
613 case tcu::CUBEFACE_NEGATIVE_Y:
614 case tcu::CUBEFACE_POSITIVE_Y: maNdx = 1; sNdx = 0; tNdx = 2; break;
615 case tcu::CUBEFACE_NEGATIVE_Z:
616 case tcu::CUBEFACE_POSITIVE_Z: maNdx = 2; sNdx = 0; tNdx = 1; break;
622 const float sc = coord[sNdx];
623 const float tc = coord[tNdx];
624 const float ma = de::abs(coord[maNdx]);
625 const float scdx = coordDx[sNdx];
626 const float tcdx = coordDx[tNdx];
627 const float madx = de::abs(coordDx[maNdx]);
628 const float scdy = coordDy[sNdx];
629 const float tcdy = coordDy[tNdx];
630 const float mady = de::abs(coordDy[maNdx]);
631 const float dudx = float(faceSize) * 0.5f * (scdx*ma - sc*madx) / (ma*ma);
632 const float dvdx = float(faceSize) * 0.5f * (tcdx*ma - tc*madx) / (ma*ma);
633 const float dudy = float(faceSize) * 0.5f * (scdy*ma - sc*mady) / (ma*ma);
634 const float dvdy = float(faceSize) * 0.5f * (tcdy*ma - tc*mady) / (ma*ma);
636 return computeLodFromDerivates(lodMode, dudx, dvdx, dudy, dvdy);
640 static void sampleTexture (const SurfaceAccess& dst, const tcu::TextureCubeView& src, const tcu::Vec4& sq, const tcu::Vec4& tq, const tcu::Vec4& rq, const ReferenceParams& params)
642 const tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight());
643 const float dstW = float(dstSize.x());
644 const float dstH = float(dstSize.y());
645 const int srcSize = src.getSize();
647 // Coordinates per triangle.
648 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
649 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
650 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) };
651 const tcu::Vec3 triW[2] = { params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1) };
653 const float lodBias ((params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f);
655 for (int py = 0; py < dst.getHeight(); py++)
657 for (int px = 0; px < dst.getWidth(); px++)
659 const float wx = (float)px + 0.5f;
660 const float wy = (float)py + 0.5f;
661 const float nx = wx / dstW;
662 const float ny = wy / dstH;
664 const int triNdx = nx + ny >= 1.0f ? 1 : 0;
665 const float triNx = triNdx ? 1.0f - nx : nx;
666 const float triNy = triNdx ? 1.0f - ny : ny;
668 const tcu::Vec3 coord (triangleInterpolate(triS[triNdx], triNx, triNy),
669 triangleInterpolate(triT[triNdx], triNx, triNy),
670 triangleInterpolate(triR[triNdx], triNx, triNy));
671 const tcu::Vec3 coordDx (triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy),
672 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy),
673 triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy));
674 const tcu::Vec3 coordDy (triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx),
675 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx),
676 triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx));
678 const float lod = de::clamp(computeCubeLodFromDerivates(params.lodMode, coord, coordDx, coordDy, srcSize) + lodBias, params.minLod, params.maxLod);
680 dst.setPixel(execSample(src, params, coord.x(), coord.y(), coord.z(), lod) * params.colorScale + params.colorBias, px, py);
685 void sampleTexture (const SurfaceAccess& dst, const tcu::TextureCubeView& src, const float* texCoord, const ReferenceParams& params)
687 const tcu::TextureCubeView view = getSubView(src, params.baseLevel, params.maxLevel);
688 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]);
689 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]);
690 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]);
692 return sampleTexture(dst, view, sq, tq, rq, params);
695 // \todo [2013-07-17 pyry] Remove this!
696 void sampleTextureMultiFace (const SurfaceAccess& dst, const tcu::TextureCubeView& src, const float* texCoord, const ReferenceParams& params)
698 return sampleTexture(dst, src, texCoord, params);
701 static void sampleTextureNonProjected (const SurfaceAccess& dst, const tcu::Texture2DArrayView& src, const tcu::Vec4& sq, const tcu::Vec4& tq, const tcu::Vec4& rq, const ReferenceParams& params)
703 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f;
705 tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight());
706 tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight());
708 // Coordinates and lod per triangle.
709 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
710 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
711 tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) };
712 float triLod[2] = { computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0], triT[0]) + lodBias,
713 computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1], triT[1]) + lodBias};
715 for (int y = 0; y < dst.getHeight(); y++)
717 for (int x = 0; x < dst.getWidth(); x++)
719 float yf = ((float)y + 0.5f) / (float)dst.getHeight();
720 float xf = ((float)x + 0.5f) / (float)dst.getWidth();
722 int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule.
723 float triX = triNdx ? 1.0f-xf : xf;
724 float triY = triNdx ? 1.0f-yf : yf;
726 float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY);
727 float t = triangleInterpolate(triT[triNdx].x(), triT[triNdx].y(), triT[triNdx].z(), triX, triY);
728 float r = triangleInterpolate(triR[triNdx].x(), triR[triNdx].y(), triR[triNdx].z(), triX, triY);
729 float lod = triLod[triNdx];
731 dst.setPixel(execSample(src, params, s, t, r, lod) * params.colorScale + params.colorBias, x, y);
736 void sampleTexture (const SurfaceAccess& dst, const tcu::Texture2DArrayView& src, const float* texCoord, const ReferenceParams& params)
738 tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]);
739 tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]);
740 tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]);
742 DE_ASSERT(!(params.flags & ReferenceParams::PROJECTED)); // \todo [2012-02-17 pyry] Support projected lookups.
743 sampleTextureNonProjected(dst, src, sq, tq, rq, params);
746 static void sampleTextureNonProjected (const SurfaceAccess& dst, const tcu::Texture1DArrayView& src, const tcu::Vec4& sq, const tcu::Vec4& tq, const ReferenceParams& params)
748 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f;
750 tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight());
751 deInt32 srcSize = src.getWidth();
753 // Coordinates and lod per triangle.
754 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
755 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
756 float triLod[2] = { computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0]) + lodBias,
757 computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1]) + lodBias};
759 for (int y = 0; y < dst.getHeight(); y++)
761 for (int x = 0; x < dst.getWidth(); x++)
763 float yf = ((float)y + 0.5f) / (float)dst.getHeight();
764 float xf = ((float)x + 0.5f) / (float)dst.getWidth();
766 int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule.
767 float triX = triNdx ? 1.0f-xf : xf;
768 float triY = triNdx ? 1.0f-yf : yf;
770 float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY);
771 float t = triangleInterpolate(triT[triNdx].x(), triT[triNdx].y(), triT[triNdx].z(), triX, triY);
772 float lod = triLod[triNdx];
774 dst.setPixel(execSample(src, params, s, t, lod) * params.colorScale + params.colorBias, x, y);
779 void sampleTexture (const SurfaceAccess& dst, const tcu::Texture1DArrayView& src, const float* texCoord, const ReferenceParams& params)
781 tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[2+0], texCoord[4+0], texCoord[6+0]);
782 tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[2+1], texCoord[4+1], texCoord[6+1]);
784 DE_ASSERT(!(params.flags & ReferenceParams::PROJECTED)); // \todo [2014-06-09 mika] Support projected lookups.
785 sampleTextureNonProjected(dst, src, sq, tq, params);
788 static void sampleTextureNonProjected (const SurfaceAccess& dst, const tcu::Texture3DView& src, const tcu::Vec4& sq, const tcu::Vec4& tq, const tcu::Vec4& rq, const ReferenceParams& params)
790 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f;
792 tcu::IVec2 dstSize = tcu::IVec2(dst.getWidth(), dst.getHeight());
793 tcu::IVec3 srcSize = tcu::IVec3(src.getWidth(), src.getHeight(), src.getDepth());
795 // Coordinates and lod per triangle.
796 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
797 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
798 tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) };
799 float triLod[2] = { de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[0], triT[0], triR[0]) + lodBias, params.minLod, params.maxLod),
800 de::clamp(computeNonProjectedTriLod(params.lodMode, dstSize, srcSize, triS[1], triT[1], triR[1]) + lodBias, params.minLod, params.maxLod) };
802 for (int y = 0; y < dst.getHeight(); y++)
804 for (int x = 0; x < dst.getWidth(); x++)
806 float yf = ((float)y + 0.5f) / (float)dst.getHeight();
807 float xf = ((float)x + 0.5f) / (float)dst.getWidth();
809 int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule.
810 float triX = triNdx ? 1.0f-xf : xf;
811 float triY = triNdx ? 1.0f-yf : yf;
813 float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY);
814 float t = triangleInterpolate(triT[triNdx].x(), triT[triNdx].y(), triT[triNdx].z(), triX, triY);
815 float r = triangleInterpolate(triR[triNdx].x(), triR[triNdx].y(), triR[triNdx].z(), triX, triY);
816 float lod = triLod[triNdx];
818 dst.setPixel(src.sample(params.sampler, s, t, r, lod) * params.colorScale + params.colorBias, x, y);
823 static void sampleTextureProjected (const SurfaceAccess& dst, const tcu::Texture3DView& src, const tcu::Vec4& sq, const tcu::Vec4& tq, const tcu::Vec4& rq, const ReferenceParams& params)
825 float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f;
826 float dstW = (float)dst.getWidth();
827 float dstH = (float)dst.getHeight();
829 tcu::Vec4 uq = sq * (float)src.getWidth();
830 tcu::Vec4 vq = tq * (float)src.getHeight();
831 tcu::Vec4 wq = rq * (float)src.getDepth();
833 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
834 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
835 tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) };
836 tcu::Vec3 triU[2] = { uq.swizzle(0, 1, 2), uq.swizzle(3, 2, 1) };
837 tcu::Vec3 triV[2] = { vq.swizzle(0, 1, 2), vq.swizzle(3, 2, 1) };
838 tcu::Vec3 triW[2] = { wq.swizzle(0, 1, 2), wq.swizzle(3, 2, 1) };
839 tcu::Vec3 triP[2] = { params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1) };
841 for (int py = 0; py < dst.getHeight(); py++)
843 for (int px = 0; px < dst.getWidth(); px++)
845 float wx = (float)px + 0.5f;
846 float wy = (float)py + 0.5f;
847 float nx = wx / dstW;
848 float ny = wy / dstH;
850 int triNdx = nx + ny >= 1.0f ? 1 : 0;
851 float triWx = triNdx ? dstW - wx : wx;
852 float triWy = triNdx ? dstH - wy : wy;
853 float triNx = triNdx ? 1.0f - nx : nx;
854 float triNy = triNdx ? 1.0f - ny : ny;
856 float s = projectedTriInterpolate(triS[triNdx], triP[triNdx], triNx, triNy);
857 float t = projectedTriInterpolate(triT[triNdx], triP[triNdx], triNx, triNy);
858 float r = projectedTriInterpolate(triR[triNdx], triP[triNdx], triNx, triNy);
859 float lod = computeProjectedTriLod(params.lodMode, triU[triNdx], triV[triNdx], triW[triNdx], triP[triNdx], triWx, triWy, (float)dst.getWidth(), (float)dst.getHeight())
862 dst.setPixel(src.sample(params.sampler, s, t, r, lod) * params.colorScale + params.colorBias, px, py);
867 void sampleTexture (const SurfaceAccess& dst, const tcu::Texture3DView& src, const float* texCoord, const ReferenceParams& params)
869 const tcu::Texture3DView view = getSubView(src, params.baseLevel, params.maxLevel);
870 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]);
871 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]);
872 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]);
874 if (params.flags & ReferenceParams::PROJECTED)
875 sampleTextureProjected(dst, view, sq, tq, rq, params);
877 sampleTextureNonProjected(dst, view, sq, tq, rq, params);
880 static void sampleTexture (const SurfaceAccess& dst, const tcu::TextureCubeArrayView& src, const tcu::Vec4& sq, const tcu::Vec4& tq, const tcu::Vec4& rq, const tcu::Vec4& qq, const ReferenceParams& params)
882 const float dstW = (float)dst.getWidth();
883 const float dstH = (float)dst.getHeight();
885 // Coordinates per triangle.
886 tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
887 tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
888 tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) };
889 tcu::Vec3 triQ[2] = { qq.swizzle(0, 1, 2), qq.swizzle(3, 2, 1) };
890 const tcu::Vec3 triW[2] = { params.w.swizzle(0, 1, 2), params.w.swizzle(3, 2, 1) };
892 const float lodBias = (params.flags & ReferenceParams::USE_BIAS) ? params.bias : 0.0f;
894 for (int py = 0; py < dst.getHeight(); py++)
896 for (int px = 0; px < dst.getWidth(); px++)
898 const float wx = (float)px + 0.5f;
899 const float wy = (float)py + 0.5f;
900 const float nx = wx / dstW;
901 const float ny = wy / dstH;
903 const int triNdx = nx + ny >= 1.0f ? 1 : 0;
904 const float triNx = triNdx ? 1.0f - nx : nx;
905 const float triNy = triNdx ? 1.0f - ny : ny;
907 const tcu::Vec3 coord (triangleInterpolate(triS[triNdx], triNx, triNy),
908 triangleInterpolate(triT[triNdx], triNx, triNy),
909 triangleInterpolate(triR[triNdx], triNx, triNy));
911 const float coordQ = triangleInterpolate(triQ[triNdx], triNx, triNy);
913 const tcu::Vec3 coordDx (triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy),
914 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy),
915 triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy));
916 const tcu::Vec3 coordDy (triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx),
917 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx),
918 triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx));
920 const float lod = de::clamp(computeCubeLodFromDerivates(params.lodMode, coord, coordDx, coordDy, src.getSize()) + lodBias, params.minLod, params.maxLod);
922 dst.setPixel(execSample(src, params, coord.x(), coord.y(), coord.z(), coordQ, lod) * params.colorScale + params.colorBias, px, py);
927 void sampleTexture (const SurfaceAccess& dst, const tcu::TextureCubeArrayView& src, const float* texCoord, const ReferenceParams& params)
929 tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[4+0], texCoord[8+0], texCoord[12+0]);
930 tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[4+1], texCoord[8+1], texCoord[12+1]);
931 tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[4+2], texCoord[8+2], texCoord[12+2]);
932 tcu::Vec4 qq = tcu::Vec4(texCoord[0+3], texCoord[4+3], texCoord[8+3], texCoord[12+3]);
934 sampleTexture(dst, src, sq, tq, rq, qq, params);
937 void fetchTexture (const SurfaceAccess& dst, const tcu::ConstPixelBufferAccess& src, const float* texCoord, const tcu::Vec4& colorScale, const tcu::Vec4& colorBias)
939 const tcu::Vec4 sq = tcu::Vec4(texCoord[0], texCoord[1], texCoord[2], texCoord[3]);
940 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
942 for (int y = 0; y < dst.getHeight(); y++)
944 for (int x = 0; x < dst.getWidth(); x++)
946 const float yf = ((float)y + 0.5f) / (float)dst.getHeight();
947 const float xf = ((float)x + 0.5f) / (float)dst.getWidth();
949 const int triNdx = xf + yf >= 1.0f ? 1 : 0; // Top left fill rule.
950 const float triX = triNdx ? 1.0f-xf : xf;
951 const float triY = triNdx ? 1.0f-yf : yf;
953 const float s = triangleInterpolate(triS[triNdx].x(), triS[triNdx].y(), triS[triNdx].z(), triX, triY);
955 dst.setPixel(src.getPixel((int)s, 0) * colorScale + colorBias, x, y);
960 void clear (const SurfaceAccess& dst, const tcu::Vec4& color)
962 for (int y = 0; y < dst.getHeight(); y++)
963 for (int x = 0; x < dst.getWidth(); x++)
964 dst.setPixel(color, x, y);
967 bool compareImages (TestLog& log, const tcu::Surface& reference, const tcu::Surface& rendered, tcu::RGBA threshold)
969 return tcu::pixelThresholdCompare(log, "Result", "Image comparison result", reference, rendered, threshold, tcu::COMPARE_LOG_RESULT);
972 bool compareImages (TestLog& log, const char* name, const char* desc, const tcu::Surface& reference, const tcu::Surface& rendered, tcu::RGBA threshold)
974 return tcu::pixelThresholdCompare(log, name, desc, reference, rendered, threshold, tcu::COMPARE_LOG_RESULT);
977 int measureAccuracy (tcu::TestLog& log, const tcu::Surface& reference, const tcu::Surface& rendered, int bestScoreDiff, int worstScoreDiff)
979 return tcu::measurePixelDiffAccuracy(log, "Result", "Image comparison result", reference, rendered, bestScoreDiff, worstScoreDiff, tcu::COMPARE_LOG_EVERYTHING);
982 inline int rangeDiff (int x, int a, int b)
992 inline tcu::RGBA rangeDiff (tcu::RGBA p, tcu::RGBA a, tcu::RGBA b)
994 int rMin = de::min(a.getRed(), b.getRed());
995 int rMax = de::max(a.getRed(), b.getRed());
996 int gMin = de::min(a.getGreen(), b.getGreen());
997 int gMax = de::max(a.getGreen(), b.getGreen());
998 int bMin = de::min(a.getBlue(), b.getBlue());
999 int bMax = de::max(a.getBlue(), b.getBlue());
1000 int aMin = de::min(a.getAlpha(), b.getAlpha());
1001 int aMax = de::max(a.getAlpha(), b.getAlpha());
1003 return tcu::RGBA(rangeDiff(p.getRed(), rMin, rMax),
1004 rangeDiff(p.getGreen(), gMin, gMax),
1005 rangeDiff(p.getBlue(), bMin, bMax),
1006 rangeDiff(p.getAlpha(), aMin, aMax));
1009 inline bool rangeCompare (tcu::RGBA p, tcu::RGBA a, tcu::RGBA b, tcu::RGBA threshold)
1011 tcu::RGBA diff = rangeDiff(p, a, b);
1012 return diff.getRed() <= threshold.getRed() &&
1013 diff.getGreen() <= threshold.getGreen() &&
1014 diff.getBlue() <= threshold.getBlue() &&
1015 diff.getAlpha() <= threshold.getAlpha();
1018 RandomViewport::RandomViewport (const tcu::RenderTarget& renderTarget, int preferredWidth, int preferredHeight, deUint32 seed)
1021 , width (deMin32(preferredWidth, renderTarget.getWidth()))
1022 , height (deMin32(preferredHeight, renderTarget.getHeight()))
1024 de::Random rnd(seed);
1025 x = rnd.getInt(0, renderTarget.getWidth() - width);
1026 y = rnd.getInt(0, renderTarget.getHeight() - height);
1029 ProgramLibrary::ProgramLibrary (const glu::RenderContext& context, tcu::TestLog& log, glu::GLSLVersion glslVersion, glu::Precision texCoordPrecision)
1030 : m_context (context)
1032 , m_glslVersion (glslVersion)
1033 , m_texCoordPrecision (texCoordPrecision)
1037 ProgramLibrary::~ProgramLibrary (void)
1042 void ProgramLibrary::clear (void)
1044 for (map<Program, glu::ShaderProgram*>::iterator i = m_programs.begin(); i != m_programs.end(); i++)
1047 i->second = DE_NULL;
1052 glu::ShaderProgram* ProgramLibrary::getProgram (Program program)
1054 if (m_programs.find(program) != m_programs.end())
1055 return m_programs[program]; // Return from cache.
1057 static const char* vertShaderTemplate =
1059 "${VTX_IN} highp vec4 a_position;\n"
1060 "${VTX_IN} ${PRECISION} ${TEXCOORD_TYPE} a_texCoord;\n"
1061 "${VTX_OUT} ${PRECISION} ${TEXCOORD_TYPE} v_texCoord;\n"
1063 "void main (void)\n"
1065 " gl_Position = a_position;\n"
1066 " v_texCoord = a_texCoord;\n"
1068 static const char* fragShaderTemplate =
1070 "${FRAG_IN} ${PRECISION} ${TEXCOORD_TYPE} v_texCoord;\n"
1071 "uniform ${PRECISION} float u_bias;\n"
1072 "uniform ${PRECISION} float u_ref;\n"
1073 "uniform ${PRECISION} vec4 u_colorScale;\n"
1074 "uniform ${PRECISION} vec4 u_colorBias;\n"
1075 "uniform ${PRECISION} ${SAMPLER_TYPE} u_sampler;\n"
1077 "void main (void)\n"
1079 " ${FRAG_COLOR} = ${LOOKUP} * u_colorScale + u_colorBias;\n"
1082 map<string, string> params;
1084 bool isCube = de::inRange<int>(program, PROGRAM_CUBE_FLOAT, PROGRAM_CUBE_SHADOW_BIAS);
1085 bool isArray = de::inRange<int>(program, PROGRAM_2D_ARRAY_FLOAT, PROGRAM_2D_ARRAY_SHADOW)
1086 || de::inRange<int>(program, PROGRAM_1D_ARRAY_FLOAT, PROGRAM_1D_ARRAY_SHADOW);
1088 bool is1D = de::inRange<int>(program, PROGRAM_1D_FLOAT, PROGRAM_1D_UINT_BIAS)
1089 || de::inRange<int>(program, PROGRAM_1D_ARRAY_FLOAT, PROGRAM_1D_ARRAY_SHADOW)
1090 || de::inRange<int>(program, PROGRAM_BUFFER_FLOAT, PROGRAM_BUFFER_UINT);
1092 bool is2D = de::inRange<int>(program, PROGRAM_2D_FLOAT, PROGRAM_2D_UINT_BIAS)
1093 || de::inRange<int>(program, PROGRAM_2D_ARRAY_FLOAT, PROGRAM_2D_ARRAY_SHADOW);
1095 bool is3D = de::inRange<int>(program, PROGRAM_3D_FLOAT, PROGRAM_3D_UINT_BIAS);
1096 bool isCubeArray = de::inRange<int>(program, PROGRAM_CUBE_ARRAY_FLOAT, PROGRAM_CUBE_ARRAY_SHADOW);
1097 bool isBuffer = de::inRange<int>(program, PROGRAM_BUFFER_FLOAT, PROGRAM_BUFFER_UINT);
1099 if (m_glslVersion == glu::GLSL_VERSION_100_ES)
1101 params["FRAG_HEADER"] = "";
1102 params["VTX_HEADER"] = "";
1103 params["VTX_IN"] = "attribute";
1104 params["VTX_OUT"] = "varying";
1105 params["FRAG_IN"] = "varying";
1106 params["FRAG_COLOR"] = "gl_FragColor";
1108 else if (m_glslVersion == glu::GLSL_VERSION_300_ES || m_glslVersion == glu::GLSL_VERSION_310_ES || m_glslVersion == glu::GLSL_VERSION_330)
1110 const string version = glu::getGLSLVersionDeclaration(m_glslVersion);
1111 const char* ext = DE_NULL;
1113 if (isCubeArray && glu::glslVersionIsES(m_glslVersion))
1114 ext = "GL_EXT_texture_cube_map_array";
1115 else if (isBuffer && glu::glslVersionIsES(m_glslVersion))
1116 ext = "GL_EXT_texture_buffer";
1118 params["FRAG_HEADER"] = version + (ext ? string("\n#extension ") + ext + " : require" : string()) + "\nlayout(location = 0) out mediump vec4 dEQP_FragColor;\n";
1119 params["VTX_HEADER"] = version + "\n";
1120 params["VTX_IN"] = "in";
1121 params["VTX_OUT"] = "out";
1122 params["FRAG_IN"] = "in";
1123 params["FRAG_COLOR"] = "dEQP_FragColor";
1126 DE_ASSERT(!"Unsupported version");
1128 params["PRECISION"] = glu::getPrecisionName(m_texCoordPrecision);
1131 params["TEXCOORD_TYPE"] = "vec4";
1132 else if (isCube || (is2D && isArray) || is3D)
1133 params["TEXCOORD_TYPE"] = "vec3";
1134 else if ((is1D && isArray) || is2D)
1135 params["TEXCOORD_TYPE"] = "vec2";
1137 params["TEXCOORD_TYPE"] = "float";
1139 DE_ASSERT(DE_FALSE);
1141 const char* sampler = DE_NULL;
1142 const char* lookup = DE_NULL;
1144 if (m_glslVersion == glu::GLSL_VERSION_300_ES || m_glslVersion == glu::GLSL_VERSION_310_ES || m_glslVersion == glu::GLSL_VERSION_330)
1148 case PROGRAM_2D_FLOAT: sampler = "sampler2D"; lookup = "texture(u_sampler, v_texCoord)"; break;
1149 case PROGRAM_2D_INT: sampler = "isampler2D"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1150 case PROGRAM_2D_UINT: sampler = "usampler2D"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1151 case PROGRAM_2D_SHADOW: sampler = "sampler2DShadow"; lookup = "vec4(texture(u_sampler, vec3(v_texCoord, u_ref)), 0.0, 0.0, 1.0)"; break;
1152 case PROGRAM_2D_FLOAT_BIAS: sampler = "sampler2D"; lookup = "texture(u_sampler, v_texCoord, u_bias)"; break;
1153 case PROGRAM_2D_INT_BIAS: sampler = "isampler2D"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break;
1154 case PROGRAM_2D_UINT_BIAS: sampler = "usampler2D"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break;
1155 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;
1156 case PROGRAM_1D_FLOAT: sampler = "sampler1D"; lookup = "texture(u_sampler, v_texCoord)"; break;
1157 case PROGRAM_1D_INT: sampler = "isampler1D"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1158 case PROGRAM_1D_UINT: sampler = "usampler1D"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1159 case PROGRAM_1D_SHADOW: sampler = "sampler1DShadow"; lookup = "vec4(texture(u_sampler, vec3(v_texCoord, u_ref)), 0.0, 0.0, 1.0)"; break;
1160 case PROGRAM_1D_FLOAT_BIAS: sampler = "sampler1D"; lookup = "texture(u_sampler, v_texCoord, u_bias)"; break;
1161 case PROGRAM_1D_INT_BIAS: sampler = "isampler1D"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break;
1162 case PROGRAM_1D_UINT_BIAS: sampler = "usampler1D"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break;
1163 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;
1164 case PROGRAM_CUBE_FLOAT: sampler = "samplerCube"; lookup = "texture(u_sampler, v_texCoord)"; break;
1165 case PROGRAM_CUBE_INT: sampler = "isamplerCube"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1166 case PROGRAM_CUBE_UINT: sampler = "usamplerCube"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1167 case PROGRAM_CUBE_SHADOW: sampler = "samplerCubeShadow"; lookup = "vec4(texture(u_sampler, vec4(v_texCoord, u_ref)), 0.0, 0.0, 1.0)"; break;
1168 case PROGRAM_CUBE_FLOAT_BIAS: sampler = "samplerCube"; lookup = "texture(u_sampler, v_texCoord, u_bias)"; break;
1169 case PROGRAM_CUBE_INT_BIAS: sampler = "isamplerCube"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break;
1170 case PROGRAM_CUBE_UINT_BIAS: sampler = "usamplerCube"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break;
1171 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;
1172 case PROGRAM_2D_ARRAY_FLOAT: sampler = "sampler2DArray"; lookup = "texture(u_sampler, v_texCoord)"; break;
1173 case PROGRAM_2D_ARRAY_INT: sampler = "isampler2DArray"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1174 case PROGRAM_2D_ARRAY_UINT: sampler = "usampler2DArray"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1175 case PROGRAM_2D_ARRAY_SHADOW: sampler = "sampler2DArrayShadow"; lookup = "vec4(texture(u_sampler, vec4(v_texCoord, u_ref)), 0.0, 0.0, 1.0)"; break;
1176 case PROGRAM_3D_FLOAT: sampler = "sampler3D"; lookup = "texture(u_sampler, v_texCoord)"; break;
1177 case PROGRAM_3D_INT: sampler = "isampler3D"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1178 case PROGRAM_3D_UINT: sampler =" usampler3D"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1179 case PROGRAM_3D_FLOAT_BIAS: sampler = "sampler3D"; lookup = "texture(u_sampler, v_texCoord, u_bias)"; break;
1180 case PROGRAM_3D_INT_BIAS: sampler = "isampler3D"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break;
1181 case PROGRAM_3D_UINT_BIAS: sampler =" usampler3D"; lookup = "vec4(texture(u_sampler, v_texCoord, u_bias))"; break;
1182 case PROGRAM_CUBE_ARRAY_FLOAT: sampler = "samplerCubeArray"; lookup = "texture(u_sampler, v_texCoord)"; break;
1183 case PROGRAM_CUBE_ARRAY_INT: sampler = "isamplerCubeArray"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1184 case PROGRAM_CUBE_ARRAY_UINT: sampler = "usamplerCubeArray"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1185 case PROGRAM_CUBE_ARRAY_SHADOW: sampler = "samplerCubeArrayShadow"; lookup = "vec4(texture(u_sampler, vec4(v_texCoord, u_ref)), 0.0, 0.0, 1.0)"; break;
1186 case PROGRAM_1D_ARRAY_FLOAT: sampler = "sampler1DArray"; lookup = "texture(u_sampler, v_texCoord)"; break;
1187 case PROGRAM_1D_ARRAY_INT: sampler = "isampler1DArray"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1188 case PROGRAM_1D_ARRAY_UINT: sampler = "usampler1DArray"; lookup = "vec4(texture(u_sampler, v_texCoord))"; break;
1189 case PROGRAM_1D_ARRAY_SHADOW: sampler = "sampler1DArrayShadow"; lookup = "vec4(texture(u_sampler, vec4(v_texCoord, u_ref)), 0.0, 0.0, 1.0)"; break;
1190 case PROGRAM_BUFFER_FLOAT: sampler = "samplerBuffer"; lookup = "texelFetch(u_sampler, int(v_texCoord))"; break;
1191 case PROGRAM_BUFFER_INT: sampler = "isamplerBuffer"; lookup = "vec4(texelFetch(u_sampler, int(v_texCoord)))"; break;
1192 case PROGRAM_BUFFER_UINT: sampler = "usamplerBuffer"; lookup = "vec4(texelFetch(u_sampler, int(v_texCoord)))"; break;
1197 else if (m_glslVersion == glu::GLSL_VERSION_100_ES)
1199 sampler = isCube ? "samplerCube" : "sampler2D";
1203 case PROGRAM_2D_FLOAT: lookup = "texture2D(u_sampler, v_texCoord)"; break;
1204 case PROGRAM_2D_FLOAT_BIAS: lookup = "texture2D(u_sampler, v_texCoord, u_bias)"; break;
1205 case PROGRAM_CUBE_FLOAT: lookup = "textureCube(u_sampler, v_texCoord)"; break;
1206 case PROGRAM_CUBE_FLOAT_BIAS: lookup = "textureCube(u_sampler, v_texCoord, u_bias)"; break;
1212 DE_ASSERT(!"Unsupported version");
1214 params["SAMPLER_TYPE"] = sampler;
1215 params["LOOKUP"] = lookup;
1217 std::string vertSrc = tcu::StringTemplate(vertShaderTemplate).specialize(params);
1218 std::string fragSrc = tcu::StringTemplate(fragShaderTemplate).specialize(params);
1220 glu::ShaderProgram* progObj = new glu::ShaderProgram(m_context, glu::makeVtxFragSources(vertSrc, fragSrc));
1221 if (!progObj->isOk())
1225 TCU_FAIL("Failed to compile shader program");
1230 m_programs[program] = progObj;
1241 TextureRenderer::TextureRenderer (const glu::RenderContext& context, tcu::TestLog& log, glu::GLSLVersion glslVersion, glu::Precision texCoordPrecision)
1242 : m_renderCtx (context)
1244 , m_programLibrary (context, log, glslVersion, texCoordPrecision)
1248 TextureRenderer::~TextureRenderer (void)
1253 void TextureRenderer::clear (void)
1255 m_programLibrary.clear();
1258 void TextureRenderer::renderQuad (int texUnit, const float* texCoord, TextureType texType)
1260 renderQuad(texUnit, texCoord, RenderParams(texType));
1263 void TextureRenderer::renderQuad (int texUnit, const float* texCoord, const RenderParams& params)
1265 const glw::Functions& gl = m_renderCtx.getFunctions();
1266 tcu::Vec4 wCoord = params.flags & RenderParams::PROJECTED ? params.w : tcu::Vec4(1.0f);
1267 bool useBias = !!(params.flags & RenderParams::USE_BIAS);
1268 bool logUniforms = !!(params.flags & RenderParams::LOG_UNIFORMS);
1270 // Render quad with texture.
1273 -1.0f*wCoord.x(), -1.0f*wCoord.x(), 0.0f, wCoord.x(),
1274 -1.0f*wCoord.y(), +1.0f*wCoord.y(), 0.0f, wCoord.y(),
1275 +1.0f*wCoord.z(), -1.0f*wCoord.z(), 0.0f, wCoord.z(),
1276 +1.0f*wCoord.w(), +1.0f*wCoord.w(), 0.0f, wCoord.w()
1278 static const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 };
1280 Program progSpec = PROGRAM_LAST;
1282 if (params.texType == TEXTURETYPE_2D)
1286 switch (params.samplerType)
1288 case SAMPLERTYPE_FLOAT: progSpec = useBias ? PROGRAM_2D_FLOAT_BIAS : PROGRAM_2D_FLOAT; break;
1289 case SAMPLERTYPE_INT: progSpec = useBias ? PROGRAM_2D_INT_BIAS : PROGRAM_2D_INT; break;
1290 case SAMPLERTYPE_UINT: progSpec = useBias ? PROGRAM_2D_UINT_BIAS : PROGRAM_2D_UINT; break;
1291 case SAMPLERTYPE_SHADOW: progSpec = useBias ? PROGRAM_2D_SHADOW_BIAS : PROGRAM_2D_SHADOW; break;
1292 default: DE_ASSERT(false);
1295 else if (params.texType == TEXTURETYPE_1D)
1299 switch (params.samplerType)
1301 case SAMPLERTYPE_FLOAT: progSpec = useBias ? PROGRAM_1D_FLOAT_BIAS : PROGRAM_1D_FLOAT; break;
1302 case SAMPLERTYPE_INT: progSpec = useBias ? PROGRAM_1D_INT_BIAS : PROGRAM_1D_INT; break;
1303 case SAMPLERTYPE_UINT: progSpec = useBias ? PROGRAM_1D_UINT_BIAS : PROGRAM_1D_UINT; break;
1304 case SAMPLERTYPE_SHADOW: progSpec = useBias ? PROGRAM_1D_SHADOW_BIAS : PROGRAM_1D_SHADOW; break;
1305 default: DE_ASSERT(false);
1308 else if (params.texType == TEXTURETYPE_CUBE)
1312 switch (params.samplerType)
1314 case SAMPLERTYPE_FLOAT: progSpec = useBias ? PROGRAM_CUBE_FLOAT_BIAS : PROGRAM_CUBE_FLOAT; break;
1315 case SAMPLERTYPE_INT: progSpec = useBias ? PROGRAM_CUBE_INT_BIAS : PROGRAM_CUBE_INT; break;
1316 case SAMPLERTYPE_UINT: progSpec = useBias ? PROGRAM_CUBE_UINT_BIAS : PROGRAM_CUBE_UINT; break;
1317 case SAMPLERTYPE_SHADOW: progSpec = useBias ? PROGRAM_CUBE_SHADOW_BIAS : PROGRAM_CUBE_SHADOW; break;
1318 default: DE_ASSERT(false);
1321 else if (params.texType == TEXTURETYPE_3D)
1325 switch (params.samplerType)
1327 case SAMPLERTYPE_FLOAT: progSpec = useBias ? PROGRAM_3D_FLOAT_BIAS : PROGRAM_3D_FLOAT; break;
1328 case SAMPLERTYPE_INT: progSpec = useBias ? PROGRAM_3D_INT_BIAS : PROGRAM_3D_INT; break;
1329 case SAMPLERTYPE_UINT: progSpec = useBias ? PROGRAM_3D_UINT_BIAS : PROGRAM_3D_UINT; break;
1330 default: DE_ASSERT(false);
1333 else if (params.texType == TEXTURETYPE_2D_ARRAY)
1335 DE_ASSERT(!useBias); // \todo [2012-02-17 pyry] Support bias.
1339 switch (params.samplerType)
1341 case SAMPLERTYPE_FLOAT: progSpec = PROGRAM_2D_ARRAY_FLOAT; break;
1342 case SAMPLERTYPE_INT: progSpec = PROGRAM_2D_ARRAY_INT; break;
1343 case SAMPLERTYPE_UINT: progSpec = PROGRAM_2D_ARRAY_UINT; break;
1344 case SAMPLERTYPE_SHADOW: progSpec = PROGRAM_2D_ARRAY_SHADOW; break;
1345 default: DE_ASSERT(false);
1348 else if (params.texType == TEXTURETYPE_CUBE_ARRAY)
1350 DE_ASSERT(!useBias);
1354 switch (params.samplerType)
1356 case SAMPLERTYPE_FLOAT: progSpec = PROGRAM_CUBE_ARRAY_FLOAT; break;
1357 case SAMPLERTYPE_INT: progSpec = PROGRAM_CUBE_ARRAY_INT; break;
1358 case SAMPLERTYPE_UINT: progSpec = PROGRAM_CUBE_ARRAY_UINT; break;
1359 case SAMPLERTYPE_SHADOW: progSpec = PROGRAM_CUBE_ARRAY_SHADOW; break;
1360 default: DE_ASSERT(false);
1363 else if (params.texType == TEXTURETYPE_1D_ARRAY)
1365 DE_ASSERT(!useBias); // \todo [2012-02-17 pyry] Support bias.
1369 switch (params.samplerType)
1371 case SAMPLERTYPE_FLOAT: progSpec = PROGRAM_1D_ARRAY_FLOAT; break;
1372 case SAMPLERTYPE_INT: progSpec = PROGRAM_1D_ARRAY_INT; break;
1373 case SAMPLERTYPE_UINT: progSpec = PROGRAM_1D_ARRAY_UINT; break;
1374 case SAMPLERTYPE_SHADOW: progSpec = PROGRAM_1D_ARRAY_SHADOW; break;
1375 default: DE_ASSERT(false);
1378 else if (params.texType == TEXTURETYPE_BUFFER)
1382 switch (params.samplerType)
1384 case SAMPLERTYPE_FETCH_FLOAT: progSpec = PROGRAM_BUFFER_FLOAT; break;
1385 case SAMPLERTYPE_FETCH_INT: progSpec = PROGRAM_BUFFER_INT; break;
1386 case SAMPLERTYPE_FETCH_UINT: progSpec = PROGRAM_BUFFER_UINT; break;
1387 default: DE_ASSERT(false);
1391 DE_ASSERT(DE_FALSE);
1393 glu::ShaderProgram* program = m_programLibrary.getProgram(progSpec);
1395 // \todo [2012-09-26 pyry] Move to ProgramLibrary and log unique programs only(?)
1396 if (params.flags & RenderParams::LOG_PROGRAMS)
1399 GLU_EXPECT_NO_ERROR(gl.getError(), "Set vertex attributes");
1401 // Program and uniforms.
1402 deUint32 prog = program->getProgram();
1403 gl.useProgram(prog);
1405 gl.uniform1i(gl.getUniformLocation(prog, "u_sampler"), texUnit);
1407 m_log << TestLog::Message << "u_sampler = " << texUnit << TestLog::EndMessage;
1411 gl.uniform1f(gl.getUniformLocation(prog, "u_bias"), params.bias);
1413 m_log << TestLog::Message << "u_bias = " << params.bias << TestLog::EndMessage;
1416 if (params.samplerType == SAMPLERTYPE_SHADOW)
1418 gl.uniform1f(gl.getUniformLocation(prog, "u_ref"), params.ref);
1420 m_log << TestLog::Message << "u_ref = " << params.ref << TestLog::EndMessage;
1423 gl.uniform4fv(gl.getUniformLocation(prog, "u_colorScale"), 1, params.colorScale.getPtr());
1424 gl.uniform4fv(gl.getUniformLocation(prog, "u_colorBias"), 1, params.colorBias.getPtr());
1428 m_log << TestLog::Message << "u_colorScale = " << params.colorScale << TestLog::EndMessage;
1429 m_log << TestLog::Message << "u_colorBias = " << params.colorBias << TestLog::EndMessage;
1432 GLU_EXPECT_NO_ERROR(gl.getError(), "Set program state");
1435 const glu::VertexArrayBinding vertexArrays[] =
1437 glu::va::Float("a_position", 4, 4, 0, &position[0]),
1438 glu::va::Float("a_texCoord", numComps, 4, 0, texCoord)
1440 glu::draw(m_renderCtx, prog, DE_LENGTH_OF_ARRAY(vertexArrays), &vertexArrays[0],
1441 glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
1445 void computeQuadTexCoord1D (std::vector<float>& dst, float left, float right)
1455 void computeQuadTexCoord1DArray (std::vector<float>& dst, int layerNdx, float left, float right)
1459 dst[0] = left; dst[1] = (float)layerNdx;
1460 dst[2] = left; dst[3] = (float)layerNdx;
1461 dst[4] = right; dst[5] = (float)layerNdx;
1462 dst[6] = right; dst[7] = (float)layerNdx;
1465 void computeQuadTexCoord2D (std::vector<float>& dst, const tcu::Vec2& bottomLeft, const tcu::Vec2& topRight)
1469 dst[0] = bottomLeft.x(); dst[1] = bottomLeft.y();
1470 dst[2] = bottomLeft.x(); dst[3] = topRight.y();
1471 dst[4] = topRight.x(); dst[5] = bottomLeft.y();
1472 dst[6] = topRight.x(); dst[7] = topRight.y();
1475 void computeQuadTexCoord2DArray (std::vector<float>& dst, int layerNdx, const tcu::Vec2& bottomLeft, const tcu::Vec2& topRight)
1479 dst[0] = bottomLeft.x(); dst[ 1] = bottomLeft.y(); dst[ 2] = (float)layerNdx;
1480 dst[3] = bottomLeft.x(); dst[ 4] = topRight.y(); dst[ 5] = (float)layerNdx;
1481 dst[6] = topRight.x(); dst[ 7] = bottomLeft.y(); dst[ 8] = (float)layerNdx;
1482 dst[9] = topRight.x(); dst[10] = topRight.y(); dst[11] = (float)layerNdx;
1485 void computeQuadTexCoord3D (std::vector<float>& dst, const tcu::Vec3& p0, const tcu::Vec3& p1, const tcu::IVec3& dirSwz)
1487 tcu::Vec3 f0 = tcu::Vec3(0.0f, 0.0f, 0.0f).swizzle(dirSwz[0], dirSwz[1], dirSwz[2]);
1488 tcu::Vec3 f1 = tcu::Vec3(0.0f, 1.0f, 0.0f).swizzle(dirSwz[0], dirSwz[1], dirSwz[2]);
1489 tcu::Vec3 f2 = tcu::Vec3(1.0f, 0.0f, 0.0f).swizzle(dirSwz[0], dirSwz[1], dirSwz[2]);
1490 tcu::Vec3 f3 = tcu::Vec3(1.0f, 1.0f, 0.0f).swizzle(dirSwz[0], dirSwz[1], dirSwz[2]);
1492 tcu::Vec3 v0 = p0 + (p1-p0)*f0;
1493 tcu::Vec3 v1 = p0 + (p1-p0)*f1;
1494 tcu::Vec3 v2 = p0 + (p1-p0)*f2;
1495 tcu::Vec3 v3 = p0 + (p1-p0)*f3;
1499 dst[0] = v0.x(); dst[ 1] = v0.y(); dst[ 2] = v0.z();
1500 dst[3] = v1.x(); dst[ 4] = v1.y(); dst[ 5] = v1.z();
1501 dst[6] = v2.x(); dst[ 7] = v2.y(); dst[ 8] = v2.z();
1502 dst[9] = v3.x(); dst[10] = v3.y(); dst[11] = v3.z();
1505 void computeQuadTexCoordCube (std::vector<float>& dst, tcu::CubeFace face)
1507 static const float texCoordNegX[] =
1510 -1.0f, -1.0f, -1.0f,
1514 static const float texCoordPosX[] =
1521 static const float texCoordNegY[] =
1524 -1.0f, -1.0f, -1.0f,
1528 static const float texCoordPosY[] =
1530 -1.0f, +1.0f, -1.0f,
1535 static const float texCoordNegZ[] =
1542 static const float texCoordPosZ[] =
1545 -1.0f, -1.0f, +1.0f,
1550 const float* texCoord = DE_NULL;
1551 int texCoordSize = DE_LENGTH_OF_ARRAY(texCoordNegX);
1555 case tcu::CUBEFACE_NEGATIVE_X: texCoord = texCoordNegX; break;
1556 case tcu::CUBEFACE_POSITIVE_X: texCoord = texCoordPosX; break;
1557 case tcu::CUBEFACE_NEGATIVE_Y: texCoord = texCoordNegY; break;
1558 case tcu::CUBEFACE_POSITIVE_Y: texCoord = texCoordPosY; break;
1559 case tcu::CUBEFACE_NEGATIVE_Z: texCoord = texCoordNegZ; break;
1560 case tcu::CUBEFACE_POSITIVE_Z: texCoord = texCoordPosZ; break;
1562 DE_ASSERT(DE_FALSE);
1566 dst.resize(texCoordSize);
1567 std::copy(texCoord, texCoord+texCoordSize, dst.begin());
1570 void computeQuadTexCoordCube (std::vector<float>& dst, tcu::CubeFace face, const tcu::Vec2& bottomLeft, const tcu::Vec2& topRight)
1581 case tcu::CUBEFACE_NEGATIVE_X: mRow = 0; sRow = 2; tRow = 1; mSign = -1.0f; tSign = -1.0f; break;
1582 case tcu::CUBEFACE_POSITIVE_X: mRow = 0; sRow = 2; tRow = 1; sSign = -1.0f; tSign = -1.0f; break;
1583 case tcu::CUBEFACE_NEGATIVE_Y: mRow = 1; sRow = 0; tRow = 2; mSign = -1.0f; tSign = -1.0f; break;
1584 case tcu::CUBEFACE_POSITIVE_Y: mRow = 1; sRow = 0; tRow = 2; break;
1585 case tcu::CUBEFACE_NEGATIVE_Z: mRow = 2; sRow = 0; tRow = 1; mSign = -1.0f; sSign = -1.0f; tSign = -1.0f; break;
1586 case tcu::CUBEFACE_POSITIVE_Z: mRow = 2; sRow = 0; tRow = 1; tSign = -1.0f; break;
1588 DE_ASSERT(DE_FALSE);
1594 dst[0+mRow] = mSign;
1595 dst[3+mRow] = mSign;
1596 dst[6+mRow] = mSign;
1597 dst[9+mRow] = mSign;
1599 dst[0+sRow] = sSign * bottomLeft.x();
1600 dst[3+sRow] = sSign * bottomLeft.x();
1601 dst[6+sRow] = sSign * topRight.x();
1602 dst[9+sRow] = sSign * topRight.x();
1604 dst[0+tRow] = tSign * bottomLeft.y();
1605 dst[3+tRow] = tSign * topRight.y();
1606 dst[6+tRow] = tSign * bottomLeft.y();
1607 dst[9+tRow] = tSign * topRight.y();
1610 void computeQuadTexCoordCubeArray (std::vector<float>& dst, tcu::CubeFace face, const tcu::Vec2& bottomLeft, const tcu::Vec2& topRight, const tcu::Vec2& layerRange)
1619 const float l0 = layerRange.x();
1620 const float l1 = layerRange.y();
1624 case tcu::CUBEFACE_NEGATIVE_X: mRow = 0; sRow = 2; tRow = 1; mSign = -1.0f; tSign = -1.0f; break;
1625 case tcu::CUBEFACE_POSITIVE_X: mRow = 0; sRow = 2; tRow = 1; sSign = -1.0f; tSign = -1.0f; break;
1626 case tcu::CUBEFACE_NEGATIVE_Y: mRow = 1; sRow = 0; tRow = 2; mSign = -1.0f; tSign = -1.0f; break;
1627 case tcu::CUBEFACE_POSITIVE_Y: mRow = 1; sRow = 0; tRow = 2; break;
1628 case tcu::CUBEFACE_NEGATIVE_Z: mRow = 2; sRow = 0; tRow = 1; mSign = -1.0f; sSign = -1.0f; tSign = -1.0f; break;
1629 case tcu::CUBEFACE_POSITIVE_Z: mRow = 2; sRow = 0; tRow = 1; tSign = -1.0f; break;
1631 DE_ASSERT(DE_FALSE);
1637 dst[ 0+mRow] = mSign;
1638 dst[ 4+mRow] = mSign;
1639 dst[ 8+mRow] = mSign;
1640 dst[12+mRow] = mSign;
1642 dst[ 0+sRow] = sSign * bottomLeft.x();
1643 dst[ 4+sRow] = sSign * bottomLeft.x();
1644 dst[ 8+sRow] = sSign * topRight.x();
1645 dst[12+sRow] = sSign * topRight.x();
1647 dst[ 0+tRow] = tSign * bottomLeft.y();
1648 dst[ 4+tRow] = tSign * topRight.y();
1649 dst[ 8+tRow] = tSign * bottomLeft.y();
1650 dst[12+tRow] = tSign * topRight.y();
1655 dst[ 4+qRow] = l0*0.5f + l1*0.5f;
1656 dst[ 8+qRow] = l0*0.5f + l1*0.5f;
1668 // Texture result verification
1670 //! Verifies texture lookup results and returns number of failed pixels.
1671 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result,
1672 const tcu::ConstPixelBufferAccess& reference,
1673 const tcu::PixelBufferAccess& errorMask,
1674 const tcu::Texture1DView& baseView,
1675 const float* texCoord,
1676 const ReferenceParams& sampleParams,
1677 const tcu::LookupPrecision& lookupPrec,
1678 const tcu::LodPrecision& lodPrec,
1679 qpWatchDog* watchDog)
1681 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight());
1682 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight());
1684 const tcu::Texture1DView src = getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel);
1686 const tcu::Vec4 sq = tcu::Vec4(texCoord[0], texCoord[1], texCoord[2], texCoord[3]);
1688 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight());
1689 const float dstW = float(dstSize.x());
1690 const float dstH = float(dstSize.y());
1691 const int srcSize = src.getWidth();
1693 // Coordinates and lod per triangle.
1694 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
1695 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) };
1697 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f);
1701 const tcu::Vec2 lodOffsets[] =
1709 tcu::clear(errorMask, tcu::RGBA::green.toVec());
1711 for (int py = 0; py < result.getHeight(); py++)
1713 // Ugly hack, validation can take way too long at the moment.
1715 qpWatchDog_touch(watchDog);
1717 for (int px = 0; px < result.getWidth(); px++)
1719 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
1720 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
1722 // Try comparison to ideal reference first, and if that fails use slower verificator.
1723 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold)))
1725 const float wx = (float)px + 0.5f;
1726 const float wy = (float)py + 0.5f;
1727 const float nx = wx / dstW;
1728 const float ny = wy / dstH;
1730 const int triNdx = nx + ny >= 1.0f ? 1 : 0;
1731 const float triWx = triNdx ? dstW - wx : wx;
1732 const float triWy = triNdx ? dstH - wy : wy;
1733 const float triNx = triNdx ? 1.0f - nx : nx;
1734 const float triNy = triNdx ? 1.0f - ny : ny;
1736 const float coord = projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy);
1737 const float coordDx = triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy) * float(srcSize);
1738 const float coordDy = triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx) * float(srcSize);
1740 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx, coordDy, lodPrec);
1742 // Compute lod bounds across lodOffsets range.
1743 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++)
1745 const float wxo = triWx + lodOffsets[lodOffsNdx].x();
1746 const float wyo = triWy + lodOffsets[lodOffsNdx].y();
1747 const float nxo = wxo/dstW;
1748 const float nyo = wyo/dstH;
1750 const float coordDxo = triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo) * float(srcSize);
1751 const float coordDyo = triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo) * float(srcSize);
1752 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo, coordDyo, lodPrec);
1754 lodBounds.x() = de::min(lodBounds.x(), lodO.x());
1755 lodBounds.y() = de::max(lodBounds.y(), lodO.y());
1758 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec);
1759 const bool isOk = tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix);
1763 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py);
1773 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result,
1774 const tcu::ConstPixelBufferAccess& reference,
1775 const tcu::PixelBufferAccess& errorMask,
1776 const tcu::Texture2DView& baseView,
1777 const float* texCoord,
1778 const ReferenceParams& sampleParams,
1779 const tcu::LookupPrecision& lookupPrec,
1780 const tcu::LodPrecision& lodPrec,
1781 qpWatchDog* watchDog)
1783 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight());
1784 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight());
1786 const tcu::Texture2DView src = getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel);
1788 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[2+0], texCoord[4+0], texCoord[6+0]);
1789 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[2+1], texCoord[4+1], texCoord[6+1]);
1791 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight());
1792 const float dstW = float(dstSize.x());
1793 const float dstH = float(dstSize.y());
1794 const tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight());
1796 // Coordinates and lod per triangle.
1797 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
1798 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
1799 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) };
1801 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f);
1805 const tcu::Vec2 lodOffsets[] =
1813 tcu::clear(errorMask, tcu::RGBA::green.toVec());
1815 for (int py = 0; py < result.getHeight(); py++)
1817 // Ugly hack, validation can take way too long at the moment.
1819 qpWatchDog_touch(watchDog);
1821 for (int px = 0; px < result.getWidth(); px++)
1823 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
1824 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
1826 // Try comparison to ideal reference first, and if that fails use slower verificator.
1827 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold)))
1829 const float wx = (float)px + 0.5f;
1830 const float wy = (float)py + 0.5f;
1831 const float nx = wx / dstW;
1832 const float ny = wy / dstH;
1834 const int triNdx = nx + ny >= 1.0f ? 1 : 0;
1835 const float triWx = triNdx ? dstW - wx : wx;
1836 const float triWy = triNdx ? dstH - wy : wy;
1837 const float triNx = triNdx ? 1.0f - nx : nx;
1838 const float triNy = triNdx ? 1.0f - ny : ny;
1840 const tcu::Vec2 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy),
1841 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy));
1842 const tcu::Vec2 coordDx = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy),
1843 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize.asFloat();
1844 const tcu::Vec2 coordDy = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx),
1845 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize.asFloat();
1847 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDy.x(), coordDy.y(), lodPrec);
1849 // Compute lod bounds across lodOffsets range.
1850 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++)
1852 const float wxo = triWx + lodOffsets[lodOffsNdx].x();
1853 const float wyo = triWy + lodOffsets[lodOffsNdx].y();
1854 const float nxo = wxo/dstW;
1855 const float nyo = wyo/dstH;
1857 const tcu::Vec2 coordDxo = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo),
1858 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize.asFloat();
1859 const tcu::Vec2 coordDyo = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo),
1860 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize.asFloat();
1861 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDyo.x(), coordDyo.y(), lodPrec);
1863 lodBounds.x() = de::min(lodBounds.x(), lodO.x());
1864 lodBounds.y() = de::max(lodBounds.y(), lodO.y());
1867 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec);
1868 const bool isOk = tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix);
1872 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py);
1882 bool verifyTextureResult (tcu::TestContext& testCtx,
1883 const tcu::ConstPixelBufferAccess& result,
1884 const tcu::Texture1DView& src,
1885 const float* texCoord,
1886 const ReferenceParams& sampleParams,
1887 const tcu::LookupPrecision& lookupPrec,
1888 const tcu::LodPrecision& lodPrec,
1889 const tcu::PixelFormat& pixelFormat)
1891 tcu::TestLog& log = testCtx.getLog();
1892 tcu::Surface reference (result.getWidth(), result.getHeight());
1893 tcu::Surface errorMask (result.getWidth(), result.getHeight());
1894 int numFailedPixels;
1896 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask);
1898 sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams);
1899 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog());
1901 if (numFailedPixels > 0)
1902 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
1904 log << TestLog::ImageSet("VerifyResult", "Verification result")
1905 << TestLog::Image("Rendered", "Rendered image", result);
1907 if (numFailedPixels > 0)
1909 log << TestLog::Image("Reference", "Ideal reference image", reference)
1910 << TestLog::Image("ErrorMask", "Error mask", errorMask);
1913 log << TestLog::EndImageSet;
1915 return numFailedPixels == 0;
1918 bool verifyTextureResult (tcu::TestContext& testCtx,
1919 const tcu::ConstPixelBufferAccess& result,
1920 const tcu::Texture2DView& src,
1921 const float* texCoord,
1922 const ReferenceParams& sampleParams,
1923 const tcu::LookupPrecision& lookupPrec,
1924 const tcu::LodPrecision& lodPrec,
1925 const tcu::PixelFormat& pixelFormat)
1927 tcu::TestLog& log = testCtx.getLog();
1928 tcu::Surface reference (result.getWidth(), result.getHeight());
1929 tcu::Surface errorMask (result.getWidth(), result.getHeight());
1930 int numFailedPixels;
1932 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask);
1934 sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams);
1935 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog());
1937 if (numFailedPixels > 0)
1938 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
1940 log << TestLog::ImageSet("VerifyResult", "Verification result")
1941 << TestLog::Image("Rendered", "Rendered image", result);
1943 if (numFailedPixels > 0)
1945 log << TestLog::Image("Reference", "Ideal reference image", reference)
1946 << TestLog::Image("ErrorMask", "Error mask", errorMask);
1949 log << TestLog::EndImageSet;
1951 return numFailedPixels == 0;
1954 //! Verifies texture lookup results and returns number of failed pixels.
1955 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result,
1956 const tcu::ConstPixelBufferAccess& reference,
1957 const tcu::PixelBufferAccess& errorMask,
1958 const tcu::TextureCubeView& baseView,
1959 const float* texCoord,
1960 const ReferenceParams& sampleParams,
1961 const tcu::LookupPrecision& lookupPrec,
1962 const tcu::LodPrecision& lodPrec,
1963 qpWatchDog* watchDog)
1965 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight());
1966 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight());
1968 const tcu::TextureCubeView src = getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel);
1970 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]);
1971 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]);
1972 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]);
1974 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight());
1975 const float dstW = float(dstSize.x());
1976 const float dstH = float(dstSize.y());
1977 const int srcSize = src.getSize();
1979 // Coordinates per triangle.
1980 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
1981 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
1982 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) };
1983 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) };
1985 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f);
1987 const float posEps = 1.0f / float(1<<MIN_SUBPIXEL_BITS);
1991 const tcu::Vec2 lodOffsets[] =
1998 // \note Not strictly allowed by spec, but implementations do this in practice.
2005 tcu::clear(errorMask, tcu::RGBA::green.toVec());
2007 for (int py = 0; py < result.getHeight(); py++)
2009 // Ugly hack, validation can take way too long at the moment.
2011 qpWatchDog_touch(watchDog);
2013 for (int px = 0; px < result.getWidth(); px++)
2015 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
2016 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
2018 // Try comparison to ideal reference first, and if that fails use slower verificator.
2019 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold)))
2021 const float wx = (float)px + 0.5f;
2022 const float wy = (float)py + 0.5f;
2023 const float nx = wx / dstW;
2024 const float ny = wy / dstH;
2026 const bool tri0 = (wx-posEps)/dstW + (wy-posEps)/dstH <= 1.0f;
2027 const bool tri1 = (wx+posEps)/dstW + (wy+posEps)/dstH >= 1.0f;
2031 DE_ASSERT(tri0 || tri1);
2033 // Pixel can belong to either of the triangles if it lies close enough to the edge.
2034 for (int triNdx = (tri0?0:1); triNdx <= (tri1?1:0); triNdx++)
2036 const float triWx = triNdx ? dstW - wx : wx;
2037 const float triWy = triNdx ? dstH - wy : wy;
2038 const float triNx = triNdx ? 1.0f - nx : nx;
2039 const float triNy = triNdx ? 1.0f - ny : ny;
2041 const tcu::Vec3 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy),
2042 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy),
2043 projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy));
2044 const tcu::Vec3 coordDx (triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy),
2045 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy),
2046 triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy));
2047 const tcu::Vec3 coordDy (triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx),
2048 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx),
2049 triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx));
2051 tcu::Vec2 lodBounds = tcu::computeCubeLodBoundsFromDerivates(coord, coordDx, coordDy, srcSize, lodPrec);
2053 // Compute lod bounds across lodOffsets range.
2054 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++)
2056 const float wxo = triWx + lodOffsets[lodOffsNdx].x();
2057 const float wyo = triWy + lodOffsets[lodOffsNdx].y();
2058 const float nxo = wxo/dstW;
2059 const float nyo = wyo/dstH;
2061 const tcu::Vec3 coordO (projectedTriInterpolate(triS[triNdx], triW[triNdx], nxo, nyo),
2062 projectedTriInterpolate(triT[triNdx], triW[triNdx], nxo, nyo),
2063 projectedTriInterpolate(triR[triNdx], triW[triNdx], nxo, nyo));
2064 const tcu::Vec3 coordDxo (triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo),
2065 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo),
2066 triDerivateX(triR[triNdx], triW[triNdx], wxo, dstW, nyo));
2067 const tcu::Vec3 coordDyo (triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo),
2068 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo),
2069 triDerivateY(triR[triNdx], triW[triNdx], wyo, dstH, nxo));
2070 const tcu::Vec2 lodO = tcu::computeCubeLodBoundsFromDerivates(coordO, coordDxo, coordDyo, srcSize, lodPrec);
2072 lodBounds.x() = de::min(lodBounds.x(), lodO.x());
2073 lodBounds.y() = de::max(lodBounds.y(), lodO.y());
2076 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec);
2078 if (tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix))
2087 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py);
2097 bool verifyTextureResult (tcu::TestContext& testCtx,
2098 const tcu::ConstPixelBufferAccess& result,
2099 const tcu::TextureCubeView& src,
2100 const float* texCoord,
2101 const ReferenceParams& sampleParams,
2102 const tcu::LookupPrecision& lookupPrec,
2103 const tcu::LodPrecision& lodPrec,
2104 const tcu::PixelFormat& pixelFormat)
2106 tcu::TestLog& log = testCtx.getLog();
2107 tcu::Surface reference (result.getWidth(), result.getHeight());
2108 tcu::Surface errorMask (result.getWidth(), result.getHeight());
2109 int numFailedPixels;
2111 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask);
2113 sampleTextureMultiFace(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams);
2114 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog());
2116 if (numFailedPixels > 0)
2117 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
2119 log << TestLog::ImageSet("VerifyResult", "Verification result")
2120 << TestLog::Image("Rendered", "Rendered image", result);
2122 if (numFailedPixels > 0)
2124 log << TestLog::Image("Reference", "Ideal reference image", reference)
2125 << TestLog::Image("ErrorMask", "Error mask", errorMask);
2128 log << TestLog::EndImageSet;
2130 return numFailedPixels == 0;
2133 //! Verifies texture lookup results and returns number of failed pixels.
2134 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result,
2135 const tcu::ConstPixelBufferAccess& reference,
2136 const tcu::PixelBufferAccess& errorMask,
2137 const tcu::Texture3DView& baseView,
2138 const float* texCoord,
2139 const ReferenceParams& sampleParams,
2140 const tcu::LookupPrecision& lookupPrec,
2141 const tcu::LodPrecision& lodPrec,
2142 qpWatchDog* watchDog)
2144 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight());
2145 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight());
2147 const tcu::Texture3DView src = getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel);
2149 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]);
2150 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]);
2151 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]);
2153 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight());
2154 const float dstW = float(dstSize.x());
2155 const float dstH = float(dstSize.y());
2156 const tcu::IVec3 srcSize = tcu::IVec3(src.getWidth(), src.getHeight(), src.getDepth());
2158 // Coordinates and lod per triangle.
2159 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
2160 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
2161 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) };
2162 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) };
2164 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f);
2166 const float posEps = 1.0f / float(1<<MIN_SUBPIXEL_BITS);
2170 const tcu::Vec2 lodOffsets[] =
2178 tcu::clear(errorMask, tcu::RGBA::green.toVec());
2180 for (int py = 0; py < result.getHeight(); py++)
2182 // Ugly hack, validation can take way too long at the moment.
2184 qpWatchDog_touch(watchDog);
2186 for (int px = 0; px < result.getWidth(); px++)
2188 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
2189 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
2191 // Try comparison to ideal reference first, and if that fails use slower verificator.
2192 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold)))
2194 const float wx = (float)px + 0.5f;
2195 const float wy = (float)py + 0.5f;
2196 const float nx = wx / dstW;
2197 const float ny = wy / dstH;
2199 const bool tri0 = (wx-posEps)/dstW + (wy-posEps)/dstH <= 1.0f;
2200 const bool tri1 = (wx+posEps)/dstW + (wy+posEps)/dstH >= 1.0f;
2204 DE_ASSERT(tri0 || tri1);
2206 // Pixel can belong to either of the triangles if it lies close enough to the edge.
2207 for (int triNdx = (tri0?0:1); triNdx <= (tri1?1:0); triNdx++)
2209 const float triWx = triNdx ? dstW - wx : wx;
2210 const float triWy = triNdx ? dstH - wy : wy;
2211 const float triNx = triNdx ? 1.0f - nx : nx;
2212 const float triNy = triNdx ? 1.0f - ny : ny;
2214 const tcu::Vec3 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy),
2215 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy),
2216 projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy));
2217 const tcu::Vec3 coordDx = tcu::Vec3(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy),
2218 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy),
2219 triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize.asFloat();
2220 const tcu::Vec3 coordDy = tcu::Vec3(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx),
2221 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx),
2222 triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize.asFloat();
2224 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDx.z(), coordDy.x(), coordDy.y(), coordDy.z(), lodPrec);
2226 // Compute lod bounds across lodOffsets range.
2227 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++)
2229 const float wxo = triWx + lodOffsets[lodOffsNdx].x();
2230 const float wyo = triWy + lodOffsets[lodOffsNdx].y();
2231 const float nxo = wxo/dstW;
2232 const float nyo = wyo/dstH;
2234 const tcu::Vec3 coordDxo = tcu::Vec3(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo),
2235 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo),
2236 triDerivateX(triR[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize.asFloat();
2237 const tcu::Vec3 coordDyo = tcu::Vec3(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo),
2238 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo),
2239 triDerivateY(triR[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize.asFloat();
2240 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDxo.z(), coordDyo.x(), coordDyo.y(), coordDyo.z(), lodPrec);
2242 lodBounds.x() = de::min(lodBounds.x(), lodO.x());
2243 lodBounds.y() = de::max(lodBounds.y(), lodO.y());
2246 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec);
2248 if (tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix))
2257 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py);
2267 bool verifyTextureResult (tcu::TestContext& testCtx,
2268 const tcu::ConstPixelBufferAccess& result,
2269 const tcu::Texture3DView& src,
2270 const float* texCoord,
2271 const ReferenceParams& sampleParams,
2272 const tcu::LookupPrecision& lookupPrec,
2273 const tcu::LodPrecision& lodPrec,
2274 const tcu::PixelFormat& pixelFormat)
2276 tcu::TestLog& log = testCtx.getLog();
2277 tcu::Surface reference (result.getWidth(), result.getHeight());
2278 tcu::Surface errorMask (result.getWidth(), result.getHeight());
2279 int numFailedPixels;
2281 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask);
2283 sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams);
2284 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog());
2286 if (numFailedPixels > 0)
2287 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
2289 log << TestLog::ImageSet("VerifyResult", "Verification result")
2290 << TestLog::Image("Rendered", "Rendered image", result);
2292 if (numFailedPixels > 0)
2294 log << TestLog::Image("Reference", "Ideal reference image", reference)
2295 << TestLog::Image("ErrorMask", "Error mask", errorMask);
2298 log << TestLog::EndImageSet;
2300 return numFailedPixels == 0;
2303 //! Verifies texture lookup results and returns number of failed pixels.
2304 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result,
2305 const tcu::ConstPixelBufferAccess& reference,
2306 const tcu::PixelBufferAccess& errorMask,
2307 const tcu::Texture1DArrayView& src,
2308 const float* texCoord,
2309 const ReferenceParams& sampleParams,
2310 const tcu::LookupPrecision& lookupPrec,
2311 const tcu::LodPrecision& lodPrec,
2312 qpWatchDog* watchDog)
2314 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight());
2315 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight());
2317 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[2+0], texCoord[4+0], texCoord[6+0]);
2318 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[2+1], texCoord[4+1], texCoord[6+1]);
2320 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight());
2321 const float dstW = float(dstSize.x());
2322 const float dstH = float(dstSize.y());
2323 const float srcSize = float(src.getWidth()); // For lod computation, thus #layers is ignored.
2325 // Coordinates and lod per triangle.
2326 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
2327 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
2328 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) };
2330 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f);
2334 const tcu::Vec2 lodOffsets[] =
2342 tcu::clear(errorMask, tcu::RGBA::green.toVec());
2344 for (int py = 0; py < result.getHeight(); py++)
2346 // Ugly hack, validation can take way too long at the moment.
2348 qpWatchDog_touch(watchDog);
2350 for (int px = 0; px < result.getWidth(); px++)
2352 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
2353 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
2355 // Try comparison to ideal reference first, and if that fails use slower verificator.
2356 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold)))
2358 const float wx = (float)px + 0.5f;
2359 const float wy = (float)py + 0.5f;
2360 const float nx = wx / dstW;
2361 const float ny = wy / dstH;
2363 const int triNdx = nx + ny >= 1.0f ? 1 : 0;
2364 const float triWx = triNdx ? dstW - wx : wx;
2365 const float triWy = triNdx ? dstH - wy : wy;
2366 const float triNx = triNdx ? 1.0f - nx : nx;
2367 const float triNy = triNdx ? 1.0f - ny : ny;
2369 const tcu::Vec2 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy),
2370 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy));
2371 const float coordDx = triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy) * srcSize;
2372 const float coordDy = triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx) * srcSize;
2374 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx, coordDy, lodPrec);
2376 // Compute lod bounds across lodOffsets range.
2377 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++)
2379 const float wxo = triWx + lodOffsets[lodOffsNdx].x();
2380 const float wyo = triWy + lodOffsets[lodOffsNdx].y();
2381 const float nxo = wxo/dstW;
2382 const float nyo = wyo/dstH;
2384 const float coordDxo = triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo) * srcSize;
2385 const float coordDyo = triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo) * srcSize;
2386 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo, coordDyo, lodPrec);
2388 lodBounds.x() = de::min(lodBounds.x(), lodO.x());
2389 lodBounds.y() = de::max(lodBounds.y(), lodO.y());
2392 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec);
2393 const bool isOk = tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix);
2397 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py);
2407 //! Verifies texture lookup results and returns number of failed pixels.
2408 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result,
2409 const tcu::ConstPixelBufferAccess& reference,
2410 const tcu::PixelBufferAccess& errorMask,
2411 const tcu::Texture2DArrayView& src,
2412 const float* texCoord,
2413 const ReferenceParams& sampleParams,
2414 const tcu::LookupPrecision& lookupPrec,
2415 const tcu::LodPrecision& lodPrec,
2416 qpWatchDog* watchDog)
2418 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight());
2419 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight());
2421 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]);
2422 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]);
2423 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]);
2425 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight());
2426 const float dstW = float(dstSize.x());
2427 const float dstH = float(dstSize.y());
2428 const tcu::Vec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight()).asFloat(); // For lod computation, thus #layers is ignored.
2430 // Coordinates and lod per triangle.
2431 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
2432 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
2433 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) };
2434 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) };
2436 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f);
2440 const tcu::Vec2 lodOffsets[] =
2448 tcu::clear(errorMask, tcu::RGBA::green.toVec());
2450 for (int py = 0; py < result.getHeight(); py++)
2452 // Ugly hack, validation can take way too long at the moment.
2454 qpWatchDog_touch(watchDog);
2456 for (int px = 0; px < result.getWidth(); px++)
2458 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
2459 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
2461 // Try comparison to ideal reference first, and if that fails use slower verificator.
2462 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold)))
2464 const float wx = (float)px + 0.5f;
2465 const float wy = (float)py + 0.5f;
2466 const float nx = wx / dstW;
2467 const float ny = wy / dstH;
2469 const int triNdx = nx + ny >= 1.0f ? 1 : 0;
2470 const float triWx = triNdx ? dstW - wx : wx;
2471 const float triWy = triNdx ? dstH - wy : wy;
2472 const float triNx = triNdx ? 1.0f - nx : nx;
2473 const float triNy = triNdx ? 1.0f - ny : ny;
2475 const tcu::Vec3 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy),
2476 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy),
2477 projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy));
2478 const tcu::Vec2 coordDx = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy),
2479 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize;
2480 const tcu::Vec2 coordDy = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx),
2481 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize;
2483 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDy.x(), coordDy.y(), lodPrec);
2485 // Compute lod bounds across lodOffsets range.
2486 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++)
2488 const float wxo = triWx + lodOffsets[lodOffsNdx].x();
2489 const float wyo = triWy + lodOffsets[lodOffsNdx].y();
2490 const float nxo = wxo/dstW;
2491 const float nyo = wyo/dstH;
2493 const tcu::Vec2 coordDxo = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo),
2494 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize;
2495 const tcu::Vec2 coordDyo = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo),
2496 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize;
2497 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDyo.x(), coordDyo.y(), lodPrec);
2499 lodBounds.x() = de::min(lodBounds.x(), lodO.x());
2500 lodBounds.y() = de::max(lodBounds.y(), lodO.y());
2503 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec);
2504 const bool isOk = tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coord, clampedLod, resPix);
2508 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py);
2518 bool verifyTextureResult (tcu::TestContext& testCtx,
2519 const tcu::ConstPixelBufferAccess& result,
2520 const tcu::Texture1DArrayView& src,
2521 const float* texCoord,
2522 const ReferenceParams& sampleParams,
2523 const tcu::LookupPrecision& lookupPrec,
2524 const tcu::LodPrecision& lodPrec,
2525 const tcu::PixelFormat& pixelFormat)
2527 tcu::TestLog& log = testCtx.getLog();
2528 tcu::Surface reference (result.getWidth(), result.getHeight());
2529 tcu::Surface errorMask (result.getWidth(), result.getHeight());
2530 int numFailedPixels;
2532 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask);
2534 sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams);
2535 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog());
2537 if (numFailedPixels > 0)
2538 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
2540 log << TestLog::ImageSet("VerifyResult", "Verification result")
2541 << TestLog::Image("Rendered", "Rendered image", result);
2543 if (numFailedPixels > 0)
2545 log << TestLog::Image("Reference", "Ideal reference image", reference)
2546 << TestLog::Image("ErrorMask", "Error mask", errorMask);
2549 log << TestLog::EndImageSet;
2551 return numFailedPixels == 0;
2554 bool verifyTextureResult (tcu::TestContext& testCtx,
2555 const tcu::ConstPixelBufferAccess& result,
2556 const tcu::Texture2DArrayView& src,
2557 const float* texCoord,
2558 const ReferenceParams& sampleParams,
2559 const tcu::LookupPrecision& lookupPrec,
2560 const tcu::LodPrecision& lodPrec,
2561 const tcu::PixelFormat& pixelFormat)
2563 tcu::TestLog& log = testCtx.getLog();
2564 tcu::Surface reference (result.getWidth(), result.getHeight());
2565 tcu::Surface errorMask (result.getWidth(), result.getHeight());
2566 int numFailedPixels;
2568 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask);
2570 sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams);
2571 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, lodPrec, testCtx.getWatchDog());
2573 if (numFailedPixels > 0)
2574 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
2576 log << TestLog::ImageSet("VerifyResult", "Verification result")
2577 << TestLog::Image("Rendered", "Rendered image", result);
2579 if (numFailedPixels > 0)
2581 log << TestLog::Image("Reference", "Ideal reference image", reference)
2582 << TestLog::Image("ErrorMask", "Error mask", errorMask);
2585 log << TestLog::EndImageSet;
2587 return numFailedPixels == 0;
2590 //! Verifies texture lookup results and returns number of failed pixels.
2591 int computeTextureLookupDiff (const tcu::ConstPixelBufferAccess& result,
2592 const tcu::ConstPixelBufferAccess& reference,
2593 const tcu::PixelBufferAccess& errorMask,
2594 const tcu::TextureCubeArrayView& baseView,
2595 const float* texCoord,
2596 const ReferenceParams& sampleParams,
2597 const tcu::LookupPrecision& lookupPrec,
2598 const tcu::IVec4& coordBits,
2599 const tcu::LodPrecision& lodPrec,
2600 qpWatchDog* watchDog)
2602 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight());
2603 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight());
2605 const tcu::TextureCubeArrayView src = getSubView(baseView, sampleParams.baseLevel, sampleParams.maxLevel);
2607 // What is the 'q' in all these names? Also a two char name for something that is in scope for ~120 lines and only used twice each seems excessive
2608 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[4+0], texCoord[8+0], texCoord[12+0]);
2609 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[4+1], texCoord[8+1], texCoord[12+1]);
2610 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[4+2], texCoord[8+2], texCoord[12+2]);
2611 const tcu::Vec4 qq = tcu::Vec4(texCoord[0+3], texCoord[4+3], texCoord[8+3], texCoord[12+3]);
2613 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight());
2614 const float dstW = float(dstSize.x());
2615 const float dstH = float(dstSize.y());
2616 const int srcSize = src.getSize();
2618 // Coordinates per triangle.
2619 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
2620 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
2621 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) };
2622 const tcu::Vec3 triQ[2] = { qq.swizzle(0, 1, 2), qq.swizzle(3, 2, 1) };
2623 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) };
2625 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f);
2627 const float posEps = 1.0f / float((1<<4) + 1); // ES3 requires at least 4 subpixel bits.
2631 const tcu::Vec2 lodOffsets[] =
2638 // \note Not strictly allowed by spec, but implementations do this in practice.
2645 tcu::clear(errorMask, tcu::RGBA::green.toVec());
2647 for (int py = 0; py < result.getHeight(); py++)
2649 // Ugly hack, validation can take way too long at the moment.
2651 qpWatchDog_touch(watchDog);
2653 for (int px = 0; px < result.getWidth(); px++)
2655 const tcu::Vec4 resPix = (result.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
2656 const tcu::Vec4 refPix = (reference.getPixel(px, py) - sampleParams.colorBias) / sampleParams.colorScale;
2658 // Try comparison to ideal reference first, and if that fails use slower verificator.
2659 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(resPix - refPix), lookupPrec.colorThreshold)))
2661 const float wx = (float)px + 0.5f;
2662 const float wy = (float)py + 0.5f;
2663 const float nx = wx / dstW;
2664 const float ny = wy / dstH;
2666 const bool tri0 = nx + ny - posEps <= 1.0f;
2667 const bool tri1 = nx + ny + posEps >= 1.0f;
2671 DE_ASSERT(tri0 || tri1);
2673 // Pixel can belong to either of the triangles if it lies close enough to the edge.
2674 for (int triNdx = (tri0?0:1); triNdx <= (tri1?1:0); triNdx++)
2676 const float triWx = triNdx ? dstW - wx : wx;
2677 const float triWy = triNdx ? dstH - wy : wy;
2678 const float triNx = triNdx ? 1.0f - nx : nx;
2679 const float triNy = triNdx ? 1.0f - ny : ny;
2681 const tcu::Vec4 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy),
2682 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy),
2683 projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy),
2684 projectedTriInterpolate(triQ[triNdx], triW[triNdx], triNx, triNy));
2685 const tcu::Vec3 coordDx (triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy),
2686 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy),
2687 triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy));
2688 const tcu::Vec3 coordDy (triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx),
2689 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx),
2690 triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx));
2692 tcu::Vec2 lodBounds = tcu::computeCubeLodBoundsFromDerivates(coord.toWidth<3>(), coordDx, coordDy, srcSize, lodPrec);
2694 // Compute lod bounds across lodOffsets range.
2695 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++)
2697 const float wxo = triWx + lodOffsets[lodOffsNdx].x();
2698 const float wyo = triWy + lodOffsets[lodOffsNdx].y();
2699 const float nxo = wxo/dstW;
2700 const float nyo = wyo/dstH;
2702 const tcu::Vec3 coordO (projectedTriInterpolate(triS[triNdx], triW[triNdx], nxo, nyo),
2703 projectedTriInterpolate(triT[triNdx], triW[triNdx], nxo, nyo),
2704 projectedTriInterpolate(triR[triNdx], triW[triNdx], nxo, nyo));
2705 const tcu::Vec3 coordDxo (triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo),
2706 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo),
2707 triDerivateX(triR[triNdx], triW[triNdx], wxo, dstW, nyo));
2708 const tcu::Vec3 coordDyo (triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo),
2709 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo),
2710 triDerivateY(triR[triNdx], triW[triNdx], wyo, dstH, nxo));
2711 const tcu::Vec2 lodO = tcu::computeCubeLodBoundsFromDerivates(coordO, coordDxo, coordDyo, srcSize, lodPrec);
2713 lodBounds.x() = de::min(lodBounds.x(), lodO.x());
2714 lodBounds.y() = de::max(lodBounds.y(), lodO.y());
2717 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec);
2719 if (tcu::isLookupResultValid(src, sampleParams.sampler, lookupPrec, coordBits, coord, clampedLod, resPix))
2728 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py);
2738 bool verifyTextureResult (tcu::TestContext& testCtx,
2739 const tcu::ConstPixelBufferAccess& result,
2740 const tcu::TextureCubeArrayView& src,
2741 const float* texCoord,
2742 const ReferenceParams& sampleParams,
2743 const tcu::LookupPrecision& lookupPrec,
2744 const tcu::IVec4& coordBits,
2745 const tcu::LodPrecision& lodPrec,
2746 const tcu::PixelFormat& pixelFormat)
2748 tcu::TestLog& log = testCtx.getLog();
2749 tcu::Surface reference (result.getWidth(), result.getHeight());
2750 tcu::Surface errorMask (result.getWidth(), result.getHeight());
2751 int numFailedPixels;
2753 DE_ASSERT(getCompareMask(pixelFormat) == lookupPrec.colorMask);
2755 sampleTexture(SurfaceAccess(reference, pixelFormat), src, texCoord, sampleParams);
2756 numFailedPixels = computeTextureLookupDiff(result, reference.getAccess(), errorMask.getAccess(), src, texCoord, sampleParams, lookupPrec, coordBits, lodPrec, testCtx.getWatchDog());
2758 if (numFailedPixels > 0)
2759 log << TestLog::Message << "ERROR: Result verification failed, got " << numFailedPixels << " invalid pixels!" << TestLog::EndMessage;
2761 log << TestLog::ImageSet("VerifyResult", "Verification result")
2762 << TestLog::Image("Rendered", "Rendered image", result);
2764 if (numFailedPixels > 0)
2766 log << TestLog::Image("Reference", "Ideal reference image", reference)
2767 << TestLog::Image("ErrorMask", "Error mask", errorMask);
2770 log << TestLog::EndImageSet;
2772 return numFailedPixels == 0;
2775 // Shadow lookup verification
2777 int computeTextureCompareDiff (const tcu::ConstPixelBufferAccess& result,
2778 const tcu::ConstPixelBufferAccess& reference,
2779 const tcu::PixelBufferAccess& errorMask,
2780 const tcu::Texture2DView& src,
2781 const float* texCoord,
2782 const ReferenceParams& sampleParams,
2783 const tcu::TexComparePrecision& comparePrec,
2784 const tcu::LodPrecision& lodPrec,
2785 const tcu::Vec3& nonShadowThreshold)
2787 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight());
2788 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight());
2790 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[2+0], texCoord[4+0], texCoord[6+0]);
2791 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[2+1], texCoord[4+1], texCoord[6+1]);
2793 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight());
2794 const float dstW = float(dstSize.x());
2795 const float dstH = float(dstSize.y());
2796 const tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight());
2798 // Coordinates and lod per triangle.
2799 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
2800 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
2801 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) };
2803 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f);
2807 const tcu::Vec2 lodOffsets[] =
2815 tcu::clear(errorMask, tcu::RGBA::green.toVec());
2817 for (int py = 0; py < result.getHeight(); py++)
2819 for (int px = 0; px < result.getWidth(); px++)
2821 const tcu::Vec4 resPix = result.getPixel(px, py);
2822 const tcu::Vec4 refPix = reference.getPixel(px, py);
2824 // Other channels should trivially match to reference.
2825 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(refPix.swizzle(1,2,3) - resPix.swizzle(1,2,3)), nonShadowThreshold)))
2827 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py);
2833 const float wx = (float)px + 0.5f;
2834 const float wy = (float)py + 0.5f;
2835 const float nx = wx / dstW;
2836 const float ny = wy / dstH;
2838 const int triNdx = nx + ny >= 1.0f ? 1 : 0;
2839 const float triWx = triNdx ? dstW - wx : wx;
2840 const float triWy = triNdx ? dstH - wy : wy;
2841 const float triNx = triNdx ? 1.0f - nx : nx;
2842 const float triNy = triNdx ? 1.0f - ny : ny;
2844 const tcu::Vec2 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy),
2845 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy));
2846 const tcu::Vec2 coordDx = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy),
2847 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize.asFloat();
2848 const tcu::Vec2 coordDy = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx),
2849 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize.asFloat();
2851 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDy.x(), coordDy.y(), lodPrec);
2853 // Compute lod bounds across lodOffsets range.
2854 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++)
2856 const float wxo = triWx + lodOffsets[lodOffsNdx].x();
2857 const float wyo = triWy + lodOffsets[lodOffsNdx].y();
2858 const float nxo = wxo/dstW;
2859 const float nyo = wyo/dstH;
2861 const tcu::Vec2 coordDxo = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo),
2862 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize.asFloat();
2863 const tcu::Vec2 coordDyo = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo),
2864 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize.asFloat();
2865 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDyo.x(), coordDyo.y(), lodPrec);
2867 lodBounds.x() = de::min(lodBounds.x(), lodO.x());
2868 lodBounds.y() = de::max(lodBounds.y(), lodO.y());
2871 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec);
2872 const bool isOk = tcu::isTexCompareResultValid(src, sampleParams.sampler, comparePrec, coord, clampedLod, sampleParams.ref, resPix.x());
2876 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py);
2886 int computeTextureCompareDiff (const tcu::ConstPixelBufferAccess& result,
2887 const tcu::ConstPixelBufferAccess& reference,
2888 const tcu::PixelBufferAccess& errorMask,
2889 const tcu::TextureCubeView& src,
2890 const float* texCoord,
2891 const ReferenceParams& sampleParams,
2892 const tcu::TexComparePrecision& comparePrec,
2893 const tcu::LodPrecision& lodPrec,
2894 const tcu::Vec3& nonShadowThreshold)
2896 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight());
2897 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight());
2899 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]);
2900 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]);
2901 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]);
2903 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight());
2904 const float dstW = float(dstSize.x());
2905 const float dstH = float(dstSize.y());
2906 const int srcSize = src.getSize();
2908 // Coordinates per triangle.
2909 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
2910 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
2911 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) };
2912 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) };
2914 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f);
2918 const tcu::Vec2 lodOffsets[] =
2926 tcu::clear(errorMask, tcu::RGBA::green.toVec());
2928 for (int py = 0; py < result.getHeight(); py++)
2930 for (int px = 0; px < result.getWidth(); px++)
2932 const tcu::Vec4 resPix = result.getPixel(px, py);
2933 const tcu::Vec4 refPix = reference.getPixel(px, py);
2935 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(refPix.swizzle(1,2,3) - resPix.swizzle(1,2,3)), nonShadowThreshold)))
2937 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py);
2943 const float wx = (float)px + 0.5f;
2944 const float wy = (float)py + 0.5f;
2945 const float nx = wx / dstW;
2946 const float ny = wy / dstH;
2948 const int triNdx = nx + ny >= 1.0f ? 1 : 0;
2949 const float triWx = triNdx ? dstW - wx : wx;
2950 const float triWy = triNdx ? dstH - wy : wy;
2951 const float triNx = triNdx ? 1.0f - nx : nx;
2952 const float triNy = triNdx ? 1.0f - ny : ny;
2954 const tcu::Vec3 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy),
2955 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy),
2956 projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy));
2957 const tcu::Vec3 coordDx (triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy),
2958 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy),
2959 triDerivateX(triR[triNdx], triW[triNdx], wx, dstW, triNy));
2960 const tcu::Vec3 coordDy (triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx),
2961 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx),
2962 triDerivateY(triR[triNdx], triW[triNdx], wy, dstH, triNx));
2964 tcu::Vec2 lodBounds = tcu::computeCubeLodBoundsFromDerivates(coord, coordDx, coordDy, srcSize, lodPrec);
2966 // Compute lod bounds across lodOffsets range.
2967 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++)
2969 const float wxo = triWx + lodOffsets[lodOffsNdx].x();
2970 const float wyo = triWy + lodOffsets[lodOffsNdx].y();
2971 const float nxo = wxo/dstW;
2972 const float nyo = wyo/dstH;
2974 const tcu::Vec3 coordO (projectedTriInterpolate(triS[triNdx], triW[triNdx], nxo, nyo),
2975 projectedTriInterpolate(triT[triNdx], triW[triNdx], nxo, nyo),
2976 projectedTriInterpolate(triR[triNdx], triW[triNdx], nxo, nyo));
2977 const tcu::Vec3 coordDxo (triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo),
2978 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo),
2979 triDerivateX(triR[triNdx], triW[triNdx], wxo, dstW, nyo));
2980 const tcu::Vec3 coordDyo (triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo),
2981 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo),
2982 triDerivateY(triR[triNdx], triW[triNdx], wyo, dstH, nxo));
2983 const tcu::Vec2 lodO = tcu::computeCubeLodBoundsFromDerivates(coordO, coordDxo, coordDyo, srcSize, lodPrec);
2985 lodBounds.x() = de::min(lodBounds.x(), lodO.x());
2986 lodBounds.y() = de::max(lodBounds.y(), lodO.y());
2989 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec);
2990 const bool isOk = tcu::isTexCompareResultValid(src, sampleParams.sampler, comparePrec, coord, clampedLod, sampleParams.ref, resPix.x());
2994 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py);
3004 int computeTextureCompareDiff (const tcu::ConstPixelBufferAccess& result,
3005 const tcu::ConstPixelBufferAccess& reference,
3006 const tcu::PixelBufferAccess& errorMask,
3007 const tcu::Texture2DArrayView& src,
3008 const float* texCoord,
3009 const ReferenceParams& sampleParams,
3010 const tcu::TexComparePrecision& comparePrec,
3011 const tcu::LodPrecision& lodPrec,
3012 const tcu::Vec3& nonShadowThreshold)
3014 DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight());
3015 DE_ASSERT(result.getWidth() == errorMask.getWidth() && result.getHeight() == errorMask.getHeight());
3017 const tcu::Vec4 sq = tcu::Vec4(texCoord[0+0], texCoord[3+0], texCoord[6+0], texCoord[9+0]);
3018 const tcu::Vec4 tq = tcu::Vec4(texCoord[0+1], texCoord[3+1], texCoord[6+1], texCoord[9+1]);
3019 const tcu::Vec4 rq = tcu::Vec4(texCoord[0+2], texCoord[3+2], texCoord[6+2], texCoord[9+2]);
3021 const tcu::IVec2 dstSize = tcu::IVec2(result.getWidth(), result.getHeight());
3022 const float dstW = float(dstSize.x());
3023 const float dstH = float(dstSize.y());
3024 const tcu::IVec2 srcSize = tcu::IVec2(src.getWidth(), src.getHeight());
3026 // Coordinates and lod per triangle.
3027 const tcu::Vec3 triS[2] = { sq.swizzle(0, 1, 2), sq.swizzle(3, 2, 1) };
3028 const tcu::Vec3 triT[2] = { tq.swizzle(0, 1, 2), tq.swizzle(3, 2, 1) };
3029 const tcu::Vec3 triR[2] = { rq.swizzle(0, 1, 2), rq.swizzle(3, 2, 1) };
3030 const tcu::Vec3 triW[2] = { sampleParams.w.swizzle(0, 1, 2), sampleParams.w.swizzle(3, 2, 1) };
3032 const tcu::Vec2 lodBias ((sampleParams.flags & ReferenceParams::USE_BIAS) ? sampleParams.bias : 0.0f);
3036 const tcu::Vec2 lodOffsets[] =
3044 tcu::clear(errorMask, tcu::RGBA::green.toVec());
3046 for (int py = 0; py < result.getHeight(); py++)
3048 for (int px = 0; px < result.getWidth(); px++)
3050 const tcu::Vec4 resPix = result.getPixel(px, py);
3051 const tcu::Vec4 refPix = reference.getPixel(px, py);
3053 if (!tcu::boolAll(tcu::lessThanEqual(tcu::abs(refPix.swizzle(1,2,3) - resPix.swizzle(1,2,3)), nonShadowThreshold)))
3055 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py);
3061 const float wx = (float)px + 0.5f;
3062 const float wy = (float)py + 0.5f;
3063 const float nx = wx / dstW;
3064 const float ny = wy / dstH;
3066 const int triNdx = nx + ny >= 1.0f ? 1 : 0;
3067 const float triWx = triNdx ? dstW - wx : wx;
3068 const float triWy = triNdx ? dstH - wy : wy;
3069 const float triNx = triNdx ? 1.0f - nx : nx;
3070 const float triNy = triNdx ? 1.0f - ny : ny;
3072 const tcu::Vec3 coord (projectedTriInterpolate(triS[triNdx], triW[triNdx], triNx, triNy),
3073 projectedTriInterpolate(triT[triNdx], triW[triNdx], triNx, triNy),
3074 projectedTriInterpolate(triR[triNdx], triW[triNdx], triNx, triNy));
3075 const tcu::Vec2 coordDx = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wx, dstW, triNy),
3076 triDerivateX(triT[triNdx], triW[triNdx], wx, dstW, triNy)) * srcSize.asFloat();
3077 const tcu::Vec2 coordDy = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wy, dstH, triNx),
3078 triDerivateY(triT[triNdx], triW[triNdx], wy, dstH, triNx)) * srcSize.asFloat();
3080 tcu::Vec2 lodBounds = tcu::computeLodBoundsFromDerivates(coordDx.x(), coordDx.y(), coordDy.x(), coordDy.y(), lodPrec);
3082 // Compute lod bounds across lodOffsets range.
3083 for (int lodOffsNdx = 0; lodOffsNdx < DE_LENGTH_OF_ARRAY(lodOffsets); lodOffsNdx++)
3085 const float wxo = triWx + lodOffsets[lodOffsNdx].x();
3086 const float wyo = triWy + lodOffsets[lodOffsNdx].y();
3087 const float nxo = wxo/dstW;
3088 const float nyo = wyo/dstH;
3090 const tcu::Vec2 coordDxo = tcu::Vec2(triDerivateX(triS[triNdx], triW[triNdx], wxo, dstW, nyo),
3091 triDerivateX(triT[triNdx], triW[triNdx], wxo, dstW, nyo)) * srcSize.asFloat();
3092 const tcu::Vec2 coordDyo = tcu::Vec2(triDerivateY(triS[triNdx], triW[triNdx], wyo, dstH, nxo),
3093 triDerivateY(triT[triNdx], triW[triNdx], wyo, dstH, nxo)) * srcSize.asFloat();
3094 const tcu::Vec2 lodO = tcu::computeLodBoundsFromDerivates(coordDxo.x(), coordDxo.y(), coordDyo.x(), coordDyo.y(), lodPrec);
3096 lodBounds.x() = de::min(lodBounds.x(), lodO.x());
3097 lodBounds.y() = de::max(lodBounds.y(), lodO.y());
3100 const tcu::Vec2 clampedLod = tcu::clampLodBounds(lodBounds + lodBias, tcu::Vec2(sampleParams.minLod, sampleParams.maxLod), lodPrec);
3101 const bool isOk = tcu::isTexCompareResultValid(src, sampleParams.sampler, comparePrec, coord, clampedLod, sampleParams.ref, resPix.x());
3105 errorMask.setPixel(tcu::RGBA::red.toVec(), px, py);
3115 // Mipmap generation comparison.
3117 static int compareGenMipmapBilinear (const tcu::ConstPixelBufferAccess& dst, const tcu::ConstPixelBufferAccess& src, const tcu::PixelBufferAccess& errorMask, const GenMipmapPrecision& precision)
3119 DE_ASSERT(dst.getDepth() == 1 && src.getDepth() == 1); // \todo [2013-10-29 pyry] 3D textures.
3121 const float dstW = float(dst.getWidth());
3122 const float dstH = float(dst.getHeight());
3123 const float srcW = float(src.getWidth());
3124 const float srcH = float(src.getHeight());
3127 // Translation to lookup verification parameters.
3128 const tcu::Sampler sampler (tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE,
3129 tcu::Sampler::LINEAR, tcu::Sampler::LINEAR, 0.0f, false /* non-normalized coords */);
3130 tcu::LookupPrecision lookupPrec;
3132 lookupPrec.colorThreshold = precision.colorThreshold;
3133 lookupPrec.colorMask = precision.colorMask;
3134 lookupPrec.coordBits = tcu::IVec3(22);
3135 lookupPrec.uvwBits = precision.filterBits;
3137 for (int y = 0; y < dst.getHeight(); y++)
3138 for (int x = 0; x < dst.getWidth(); x++)
3140 const tcu::Vec4 result = dst.getPixel(x, y);
3141 const float cx = (float(x)+0.5f) / dstW * srcW;
3142 const float cy = (float(y)+0.5f) / dstH * srcH;
3143 const bool isOk = tcu::isLinearSampleResultValid(src, sampler, lookupPrec, tcu::Vec2(cx, cy), 0, result);
3145 errorMask.setPixel(isOk ? tcu::RGBA::green.toVec() : tcu::RGBA::red.toVec(), x, y);
3153 static int compareGenMipmapBox (const tcu::ConstPixelBufferAccess& dst, const tcu::ConstPixelBufferAccess& src, const tcu::PixelBufferAccess& errorMask, const GenMipmapPrecision& precision)
3155 DE_ASSERT(dst.getDepth() == 1 && src.getDepth() == 1); // \todo [2013-10-29 pyry] 3D textures.
3157 const float dstW = float(dst.getWidth());
3158 const float dstH = float(dst.getHeight());
3159 const float srcW = float(src.getWidth());
3160 const float srcH = float(src.getHeight());
3163 // Translation to lookup verification parameters.
3164 const tcu::Sampler sampler (tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE,
3165 tcu::Sampler::LINEAR, tcu::Sampler::LINEAR, 0.0f, false /* non-normalized coords */);
3166 tcu::LookupPrecision lookupPrec;
3168 lookupPrec.colorThreshold = precision.colorThreshold;
3169 lookupPrec.colorMask = precision.colorMask;
3170 lookupPrec.coordBits = tcu::IVec3(22);
3171 lookupPrec.uvwBits = precision.filterBits;
3173 for (int y = 0; y < dst.getHeight(); y++)
3174 for (int x = 0; x < dst.getWidth(); x++)
3176 const tcu::Vec4 result = dst.getPixel(x, y);
3177 const float cx = deFloatFloor(float(x) / dstW * srcW) + 1.0f;
3178 const float cy = deFloatFloor(float(y) / dstH * srcH) + 1.0f;
3179 const bool isOk = tcu::isLinearSampleResultValid(src, sampler, lookupPrec, tcu::Vec2(cx, cy), 0, result);
3181 errorMask.setPixel(isOk ? tcu::RGBA::green.toVec() : tcu::RGBA::red.toVec(), x, y);
3189 static int compareGenMipmapVeryLenient (const tcu::ConstPixelBufferAccess& dst, const tcu::ConstPixelBufferAccess& src, const tcu::PixelBufferAccess& errorMask, const GenMipmapPrecision& precision)
3191 DE_ASSERT(dst.getDepth() == 1 && src.getDepth() == 1); // \todo [2013-10-29 pyry] 3D textures.
3192 DE_UNREF(precision);
3194 const float dstW = float(dst.getWidth());
3195 const float dstH = float(dst.getHeight());
3196 const float srcW = float(src.getWidth());
3197 const float srcH = float(src.getHeight());
3200 for (int y = 0; y < dst.getHeight(); y++)
3201 for (int x = 0; x < dst.getWidth(); x++)
3203 const tcu::Vec4 result = dst.getPixel(x, y);
3204 const int minX = deFloorFloatToInt32(float(x-0.5f) / dstW * srcW);
3205 const int minY = deFloorFloatToInt32(float(y-0.5f) / dstH * srcH);
3206 const int maxX = deCeilFloatToInt32(float(x+1.5f) / dstW * srcW);
3207 const int maxY = deCeilFloatToInt32(float(y+1.5f) / dstH * srcH);
3208 tcu::Vec4 minVal, maxVal;
3211 DE_ASSERT(minX < maxX && minY < maxY);
3213 for (int ky = minY; ky <= maxY; ky++)
3215 for (int kx = minX; kx <= maxX; kx++)
3217 const int sx = de::clamp(kx, 0, src.getWidth()-1);
3218 const int sy = de::clamp(ky, 0, src.getHeight()-1);
3219 const tcu::Vec4 sample = src.getPixel(sx, sy);
3221 if (ky == minY && kx == minX)
3228 minVal = min(sample, minVal);
3229 maxVal = max(sample, maxVal);
3234 isOk = boolAll(logicalAnd(lessThanEqual(minVal, result), lessThanEqual(result, maxVal)));
3236 errorMask.setPixel(isOk ? tcu::RGBA::green.toVec() : tcu::RGBA::red.toVec(), x, y);
3244 qpTestResult compareGenMipmapResult (tcu::TestLog& log, const tcu::Texture2D& resultTexture, const tcu::Texture2D& level0Reference, const GenMipmapPrecision& precision)
3246 qpTestResult result = QP_TEST_RESULT_PASS;
3248 // Special comparison for level 0.
3250 const tcu::Vec4 threshold = select(precision.colorThreshold, tcu::Vec4(1.0f), precision.colorMask);
3251 const bool level0Ok = tcu::floatThresholdCompare(log, "Level0", "Level 0", level0Reference.getLevel(0), resultTexture.getLevel(0), threshold, tcu::COMPARE_LOG_RESULT);
3255 log << TestLog::Message << "ERROR: Level 0 comparison failed!" << TestLog::EndMessage;
3256 result = QP_TEST_RESULT_FAIL;
3260 for (int levelNdx = 1; levelNdx < resultTexture.getNumLevels(); levelNdx++)
3262 const tcu::ConstPixelBufferAccess src = resultTexture.getLevel(levelNdx-1);
3263 const tcu::ConstPixelBufferAccess dst = resultTexture.getLevel(levelNdx);
3264 tcu::Surface errorMask (dst.getWidth(), dst.getHeight());
3265 bool levelOk = false;
3267 // Try different comparisons in quality order.
3271 const int numFailed = compareGenMipmapBilinear(dst, src, errorMask.getAccess(), precision);
3275 log << TestLog::Message << "WARNING: Level " << levelNdx << " comparison to bilinear method failed, found " << numFailed << " invalid pixels." << TestLog::EndMessage;
3280 const int numFailed = compareGenMipmapBox(dst, src, errorMask.getAccess(), precision);
3284 log << TestLog::Message << "WARNING: Level " << levelNdx << " comparison to box method failed, found " << numFailed << " invalid pixels." << TestLog::EndMessage;
3287 // At this point all high-quality methods have been used.
3288 if (!levelOk && result == QP_TEST_RESULT_PASS)
3289 result = QP_TEST_RESULT_QUALITY_WARNING;
3293 const int numFailed = compareGenMipmapVeryLenient(dst, src, errorMask.getAccess(), precision);
3297 log << TestLog::Message << "ERROR: Level " << levelNdx << " appears to contain " << numFailed << " completely wrong pixels, failing case!" << TestLog::EndMessage;
3301 result = QP_TEST_RESULT_FAIL;
3303 log << TestLog::ImageSet(string("Level") + de::toString(levelNdx), string("Level ") + de::toString(levelNdx) + " result")
3304 << TestLog::Image("Result", "Result", dst);
3307 log << TestLog::Image("ErrorMask", "Error mask", errorMask);
3309 log << TestLog::EndImageSet;
3315 qpTestResult compareGenMipmapResult (tcu::TestLog& log, const tcu::TextureCube& resultTexture, const tcu::TextureCube& level0Reference, const GenMipmapPrecision& precision)
3317 qpTestResult result = QP_TEST_RESULT_PASS;
3319 static const char* s_faceNames[] = { "-X", "+X", "-Y", "+Y", "-Z", "+Z" };
3320 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_faceNames) == tcu::CUBEFACE_LAST);
3322 // Special comparison for level 0.
3323 for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
3325 const tcu::CubeFace face = tcu::CubeFace(faceNdx);
3326 const tcu::Vec4 threshold = select(precision.colorThreshold, tcu::Vec4(1.0f), precision.colorMask);
3327 const bool level0Ok = tcu::floatThresholdCompare(log,
3328 ("Level0Face" + de::toString(faceNdx)).c_str(),
3329 (string("Level 0, face ") + s_faceNames[face]).c_str(),
3330 level0Reference.getLevelFace(0, face),
3331 resultTexture.getLevelFace(0, face),
3332 threshold, tcu::COMPARE_LOG_RESULT);
3336 log << TestLog::Message << "ERROR: Level 0, face " << s_faceNames[face] << " comparison failed!" << TestLog::EndMessage;
3337 result = QP_TEST_RESULT_FAIL;
3341 for (int levelNdx = 1; levelNdx < resultTexture.getNumLevels(); levelNdx++)
3343 for (int faceNdx = 0; faceNdx < tcu::CUBEFACE_LAST; faceNdx++)
3345 const tcu::CubeFace face = tcu::CubeFace(faceNdx);
3346 const char* faceName = s_faceNames[face];
3347 const tcu::ConstPixelBufferAccess src = resultTexture.getLevelFace(levelNdx-1, face);
3348 const tcu::ConstPixelBufferAccess dst = resultTexture.getLevelFace(levelNdx, face);
3349 tcu::Surface errorMask (dst.getWidth(), dst.getHeight());
3350 bool levelOk = false;
3352 // Try different comparisons in quality order.
3356 const int numFailed = compareGenMipmapBilinear(dst, src, errorMask.getAccess(), precision);
3360 log << TestLog::Message << "WARNING: Level " << levelNdx << ", face " << faceName << " comparison to bilinear method failed, found " << numFailed << " invalid pixels." << TestLog::EndMessage;
3365 const int numFailed = compareGenMipmapBox(dst, src, errorMask.getAccess(), precision);
3369 log << TestLog::Message << "WARNING: Level " << levelNdx << ", face " << faceName <<" comparison to box method failed, found " << numFailed << " invalid pixels." << TestLog::EndMessage;
3372 // At this point all high-quality methods have been used.
3373 if (!levelOk && result == QP_TEST_RESULT_PASS)
3374 result = QP_TEST_RESULT_QUALITY_WARNING;
3378 const int numFailed = compareGenMipmapVeryLenient(dst, src, errorMask.getAccess(), precision);
3382 log << TestLog::Message << "ERROR: Level " << levelNdx << ", face " << faceName << " appears to contain " << numFailed << " completely wrong pixels, failing case!" << TestLog::EndMessage;
3386 result = QP_TEST_RESULT_FAIL;
3388 log << TestLog::ImageSet(string("Level") + de::toString(levelNdx) + "Face" + de::toString(faceNdx), string("Level ") + de::toString(levelNdx) + ", face " + string(faceName) + " result")
3389 << TestLog::Image("Result", "Result", dst);
3392 log << TestLog::Image("ErrorMask", "Error mask", errorMask);
3394 log << TestLog::EndImageSet;
3401 // Logging utilities.
3403 std::ostream& operator<< (std::ostream& str, const LogGradientFmt& fmt)
3405 return str << "(R: " << fmt.valueMin->x() << " -> " << fmt.valueMax->x() << ", "
3406 << "G: " << fmt.valueMin->y() << " -> " << fmt.valueMax->y() << ", "
3407 << "B: " << fmt.valueMin->z() << " -> " << fmt.valueMax->z() << ", "
3408 << "A: " << fmt.valueMin->w() << " -> " << fmt.valueMax->w() << ")";
3411 } // TextureTestUtil