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