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