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