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