Home | History | Annotate | Download | only in egl
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program EGL Module
      3  * ---------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Rendering tests for different config and api combinations.
     22  * \todo [2013-03-19 pyry] GLES1 and VG support.
     23  *//*--------------------------------------------------------------------*/
     24 
     25 #include "teglRenderTests.hpp"
     26 #include "teglRenderCase.hpp"
     27 
     28 #include "tcuRenderTarget.hpp"
     29 #include "tcuTestLog.hpp"
     30 #include "tcuImageCompare.hpp"
     31 #include "tcuTextureUtil.hpp"
     32 #include "tcuSurface.hpp"
     33 
     34 #include "egluDefs.hpp"
     35 #include "egluUtil.hpp"
     36 
     37 #include "eglwLibrary.hpp"
     38 #include "eglwEnums.hpp"
     39 
     40 #include "gluShaderProgram.hpp"
     41 
     42 #include "glwFunctions.hpp"
     43 #include "glwEnums.hpp"
     44 
     45 #include "deRandom.hpp"
     46 #include "deSharedPtr.hpp"
     47 #include "deSemaphore.hpp"
     48 #include "deThread.hpp"
     49 #include "deString.h"
     50 
     51 #include "rrRenderer.hpp"
     52 #include "rrFragmentOperations.hpp"
     53 
     54 #include <algorithm>
     55 #include <iterator>
     56 #include <memory>
     57 #include <set>
     58 
     59 namespace deqp
     60 {
     61 namespace egl
     62 {
     63 
     64 using std::string;
     65 using std::vector;
     66 using std::set;
     67 
     68 using tcu::Vec4;
     69 
     70 using tcu::TestLog;
     71 
     72 using namespace glw;
     73 using namespace eglw;
     74 
     75 static const tcu::Vec4	CLEAR_COLOR		= tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
     76 static const float		CLEAR_DEPTH		= 1.0f;
     77 static const int		CLEAR_STENCIL	= 0;
     78 
     79 namespace
     80 {
     81 
     82 enum PrimitiveType
     83 {
     84 	PRIMITIVETYPE_TRIANGLE = 0,	//!< Triangles, requires 3 coordinates per primitive
     85 //	PRIMITIVETYPE_POINT,		//!< Points, requires 1 coordinate per primitive (w is used as size)
     86 //	PRIMITIVETYPE_LINE,			//!< Lines, requires 2 coordinates per primitive
     87 
     88 	PRIMITIVETYPE_LAST
     89 };
     90 
     91 enum BlendMode
     92 {
     93 	BLENDMODE_NONE = 0,			//!< No blending
     94 	BLENDMODE_ADDITIVE,			//!< Blending with ONE, ONE
     95 	BLENDMODE_SRC_OVER,			//!< Blending with SRC_ALPHA, ONE_MINUS_SRC_ALPHA
     96 
     97 	BLENDMODE_LAST
     98 };
     99 
    100 enum DepthMode
    101 {
    102 	DEPTHMODE_NONE = 0,			//!< No depth test or depth writes
    103 	DEPTHMODE_LESS,				//!< Depth test with less & depth write
    104 
    105 	DEPTHMODE_LAST
    106 };
    107 
    108 enum StencilMode
    109 {
    110 	STENCILMODE_NONE = 0,		//!< No stencil test or write
    111 	STENCILMODE_LEQUAL_INC,		//!< Stencil test with LEQUAL, increment on pass
    112 
    113 	STENCILMODE_LAST
    114 };
    115 
    116 struct DrawPrimitiveOp
    117 {
    118 	PrimitiveType	type;
    119 	int				count;
    120 	vector<Vec4>	positions;
    121 	vector<Vec4>	colors;
    122 	BlendMode		blend;
    123 	DepthMode		depth;
    124 	StencilMode		stencil;
    125 	int				stencilRef;
    126 };
    127 
    128 static bool isANarrowScreenSpaceTriangle (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec4& p2)
    129 {
    130 	// to clip space
    131 	const tcu::Vec2	csp0				= p0.swizzle(0, 1) / p0.w();
    132 	const tcu::Vec2	csp1				= p1.swizzle(0, 1) / p1.w();
    133 	const tcu::Vec2	csp2				= p2.swizzle(0, 1) / p2.w();
    134 
    135 	const tcu::Vec2	e01					= (csp1 - csp0);
    136 	const tcu::Vec2	e02					= (csp2 - csp0);
    137 
    138 	const float		minimumVisibleArea	= 0.4f; // must cover at least 10% of the surface
    139 	const float		visibleArea			= de::abs(e01.x() * e02.y() - e02.x() * e01.y()) * 0.5f;
    140 
    141 	return visibleArea < minimumVisibleArea;
    142 }
    143 
    144 void randomizeDrawOp (de::Random& rnd, DrawPrimitiveOp& drawOp, const bool alphaZeroOrOne)
    145 {
    146 	const int	minStencilRef	= 0;
    147 	const int	maxStencilRef	= 8;
    148 	const int	minPrimitives	= 2;
    149 	const int	maxPrimitives	= 4;
    150 
    151 	const float	maxTriOffset	= 1.0f;
    152 	const float	minDepth		= -1.0f; // \todo [pyry] Reference doesn't support Z clipping yet
    153 	const float	maxDepth		= 1.0f;
    154 
    155 	const float	minRGB			= 0.2f;
    156 	const float	maxRGB			= 0.9f;
    157 	const float	minAlpha		= 0.3f;
    158 	const float	maxAlpha		= 1.0f;
    159 
    160 	drawOp.type			= (PrimitiveType)rnd.getInt(0, PRIMITIVETYPE_LAST-1);
    161 	drawOp.count		= rnd.getInt(minPrimitives, maxPrimitives);
    162 	drawOp.blend		= (BlendMode)rnd.getInt(0, BLENDMODE_LAST-1);
    163 	drawOp.depth		= (DepthMode)rnd.getInt(0, DEPTHMODE_LAST-1);
    164 	drawOp.stencil		= (StencilMode)rnd.getInt(0, STENCILMODE_LAST-1);
    165 	drawOp.stencilRef	= rnd.getInt(minStencilRef, maxStencilRef);
    166 
    167 	if (drawOp.type == PRIMITIVETYPE_TRIANGLE)
    168 	{
    169 		drawOp.positions.resize(drawOp.count*3);
    170 		drawOp.colors.resize(drawOp.count*3);
    171 
    172 		for (int triNdx = 0; triNdx < drawOp.count; triNdx++)
    173 		{
    174 			const float		cx		= rnd.getFloat(-1.0f, 1.0f);
    175 			const float		cy		= rnd.getFloat(-1.0f, 1.0f);
    176 			const float		flatAlpha = (rnd.getFloat(minAlpha, maxAlpha) > 0.5f) ? 1.0f : 0.0f;
    177 
    178 			for (int coordNdx = 0; coordNdx < 3; coordNdx++)
    179 			{
    180 				tcu::Vec4&	position	= drawOp.positions[triNdx*3 + coordNdx];
    181 				tcu::Vec4&	color		= drawOp.colors[triNdx*3 + coordNdx];
    182 
    183 				position.x()	= cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
    184 				position.y()	= cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
    185 				position.z()	= rnd.getFloat(minDepth, maxDepth);
    186 				position.w()	= 1.0f;
    187 
    188 				color.x()		= rnd.getFloat(minRGB, maxRGB);
    189 				color.y()		= rnd.getFloat(minRGB, maxRGB);
    190 				color.z()		= rnd.getFloat(minRGB, maxRGB);
    191 				color.w()		= rnd.getFloat(minAlpha, maxAlpha);
    192 
    193 				if (alphaZeroOrOne)
    194 				{
    195 					color.w()	= flatAlpha;
    196 				}
    197 			}
    198 
    199 			// avoid generating narrow triangles
    200 			{
    201 				const int	maxAttempts	= 100;
    202 				int			numAttempts	= 0;
    203 				tcu::Vec4&	p0			= drawOp.positions[triNdx*3 + 0];
    204 				tcu::Vec4&	p1			= drawOp.positions[triNdx*3 + 1];
    205 				tcu::Vec4&	p2			= drawOp.positions[triNdx*3 + 2];
    206 
    207 				while (isANarrowScreenSpaceTriangle(p0, p1, p2))
    208 				{
    209 					p1.x()	= cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
    210 					p1.y()	= cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
    211 					p1.z()	= rnd.getFloat(minDepth, maxDepth);
    212 					p1.w()	= 1.0f;
    213 
    214 					p2.x()	= cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
    215 					p2.y()	= cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
    216 					p2.z()	= rnd.getFloat(minDepth, maxDepth);
    217 					p2.w()	= 1.0f;
    218 
    219 					if (++numAttempts > maxAttempts)
    220 					{
    221 						DE_ASSERT(false);
    222 						break;
    223 					}
    224 				}
    225 			}
    226 		}
    227 	}
    228 	else
    229 		DE_ASSERT(false);
    230 }
    231 
    232 // Reference rendering code
    233 
    234 class ReferenceShader : public rr::VertexShader, public rr::FragmentShader
    235 {
    236 public:
    237 	enum
    238 	{
    239 		VaryingLoc_Color = 0
    240 	};
    241 
    242 	ReferenceShader ()
    243 		: rr::VertexShader	(2, 1)		// color and pos in => color out
    244 		, rr::FragmentShader(1, 1)		// color in => color out
    245 	{
    246 		this->rr::VertexShader::m_inputs[0].type		= rr::GENERICVECTYPE_FLOAT;
    247 		this->rr::VertexShader::m_inputs[1].type		= rr::GENERICVECTYPE_FLOAT;
    248 
    249 		this->rr::VertexShader::m_outputs[0].type		= rr::GENERICVECTYPE_FLOAT;
    250 		this->rr::VertexShader::m_outputs[0].flatshade	= false;
    251 
    252 		this->rr::FragmentShader::m_inputs[0].type		= rr::GENERICVECTYPE_FLOAT;
    253 		this->rr::FragmentShader::m_inputs[0].flatshade	= false;
    254 
    255 		this->rr::FragmentShader::m_outputs[0].type		= rr::GENERICVECTYPE_FLOAT;
    256 	}
    257 
    258 	void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const
    259 	{
    260 		for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
    261 		{
    262 			const int positionAttrLoc = 0;
    263 			const int colorAttrLoc = 1;
    264 
    265 			rr::VertexPacket& packet = *packets[packetNdx];
    266 
    267 			// Transform to position
    268 			packet.position = rr::readVertexAttribFloat(inputs[positionAttrLoc], packet.instanceNdx, packet.vertexNdx);
    269 
    270 			// Pass color to FS
    271 			packet.outputs[VaryingLoc_Color] = rr::readVertexAttribFloat(inputs[colorAttrLoc], packet.instanceNdx, packet.vertexNdx);
    272 		}
    273 	}
    274 
    275 	void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
    276 	{
    277 		for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
    278 		{
    279 			rr::FragmentPacket& packet = packets[packetNdx];
    280 
    281 			for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
    282 				rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, rr::readVarying<float>(packet, context, VaryingLoc_Color, fragNdx));
    283 		}
    284 	}
    285 };
    286 
    287 void toReferenceRenderState (rr::RenderState& state, const DrawPrimitiveOp& drawOp)
    288 {
    289 	state.cullMode	= rr::CULLMODE_NONE;
    290 
    291 	if (drawOp.blend != BLENDMODE_NONE)
    292 	{
    293 		state.fragOps.blendMode = rr::BLENDMODE_STANDARD;
    294 
    295 		switch (drawOp.blend)
    296 		{
    297 			case BLENDMODE_ADDITIVE:
    298 				state.fragOps.blendRGBState.srcFunc		= rr::BLENDFUNC_ONE;
    299 				state.fragOps.blendRGBState.dstFunc		= rr::BLENDFUNC_ONE;
    300 				state.fragOps.blendRGBState.equation	= rr::BLENDEQUATION_ADD;
    301 				state.fragOps.blendAState				= state.fragOps.blendRGBState;
    302 				break;
    303 
    304 			case BLENDMODE_SRC_OVER:
    305 				state.fragOps.blendRGBState.srcFunc		= rr::BLENDFUNC_SRC_ALPHA;
    306 				state.fragOps.blendRGBState.dstFunc		= rr::BLENDFUNC_ONE_MINUS_SRC_ALPHA;
    307 				state.fragOps.blendRGBState.equation	= rr::BLENDEQUATION_ADD;
    308 				state.fragOps.blendAState				= state.fragOps.blendRGBState;
    309 				break;
    310 
    311 			default:
    312 				DE_ASSERT(false);
    313 		}
    314 	}
    315 
    316 	if (drawOp.depth != DEPTHMODE_NONE)
    317 	{
    318 		state.fragOps.depthTestEnabled = true;
    319 
    320 		DE_ASSERT(drawOp.depth == DEPTHMODE_LESS);
    321 		state.fragOps.depthFunc = rr::TESTFUNC_LESS;
    322 	}
    323 
    324 	if (drawOp.stencil != STENCILMODE_NONE)
    325 	{
    326 		state.fragOps.stencilTestEnabled = true;
    327 
    328 		DE_ASSERT(drawOp.stencil == STENCILMODE_LEQUAL_INC);
    329 		state.fragOps.stencilStates[0].func		= rr::TESTFUNC_LEQUAL;
    330 		state.fragOps.stencilStates[0].sFail	= rr::STENCILOP_KEEP;
    331 		state.fragOps.stencilStates[0].dpFail	= rr::STENCILOP_INCR;
    332 		state.fragOps.stencilStates[0].dpPass	= rr::STENCILOP_INCR;
    333 		state.fragOps.stencilStates[0].ref		= drawOp.stencilRef;
    334 		state.fragOps.stencilStates[1]			= state.fragOps.stencilStates[0];
    335 	}
    336 }
    337 
    338 tcu::TextureFormat getColorFormat (const tcu::PixelFormat& colorBits)
    339 {
    340 	using tcu::TextureFormat;
    341 
    342 	DE_ASSERT(de::inBounds(colorBits.redBits,	0, 0xff) &&
    343 			  de::inBounds(colorBits.greenBits,	0, 0xff) &&
    344 			  de::inBounds(colorBits.blueBits,	0, 0xff) &&
    345 			  de::inBounds(colorBits.alphaBits,	0, 0xff));
    346 
    347 #define PACK_FMT(R, G, B, A) (((R) << 24) | ((G) << 16) | ((B) << 8) | (A))
    348 
    349 	// \note [pyry] This may not hold true on some implementations - best effort guess only.
    350 	switch (PACK_FMT(colorBits.redBits, colorBits.greenBits, colorBits.blueBits, colorBits.alphaBits))
    351 	{
    352 		case PACK_FMT(8,8,8,8):		return TextureFormat(TextureFormat::RGBA,	TextureFormat::UNORM_INT8);
    353 		case PACK_FMT(8,8,8,0):		return TextureFormat(TextureFormat::RGB,	TextureFormat::UNORM_INT8);
    354 		case PACK_FMT(4,4,4,4):		return TextureFormat(TextureFormat::RGBA,	TextureFormat::UNORM_SHORT_4444);
    355 		case PACK_FMT(5,5,5,1):		return TextureFormat(TextureFormat::RGBA,	TextureFormat::UNORM_SHORT_5551);
    356 		case PACK_FMT(5,6,5,0):		return TextureFormat(TextureFormat::RGB,	TextureFormat::UNORM_SHORT_565);
    357 
    358 		// \note Defaults to RGBA8
    359 		default:					return TextureFormat(TextureFormat::RGBA,	TextureFormat::UNORM_INT8);
    360 	}
    361 
    362 #undef PACK_FMT
    363 }
    364 
    365 /*
    366 The getColorThreshold function is used to obtain a
    367 threshold usable for the fuzzyCompare function.
    368 
    369 For 8bit color depths a value of 0.02 should provide
    370 a good metric for rejecting images above this level.
    371 For other bit depths other thresholds should be selected.
    372 Ideally this function would take advantage of the
    373 getColorThreshold function provided by the PixelFormat class
    374 as this would also allow setting per channel thresholds.
    375 However using the PixelFormat provided function can result
    376 in too strict thresholds for 8bit bit depths (compared to
    377 the current default of 0.02) or too relaxed for lower bit
    378 depths if scaled proportionally to the 8bit default.
    379 */
    380 
    381 float getColorThreshold (const tcu::PixelFormat& colorBits)
    382 {
    383 	if ((colorBits.redBits > 0 && colorBits.redBits < 8) ||
    384 		(colorBits.greenBits > 0 && colorBits.greenBits < 8) ||
    385 		(colorBits.blueBits > 0 && colorBits.blueBits < 8) ||
    386 		(colorBits.alphaBits > 0 && colorBits.alphaBits < 8))
    387 	{
    388 		return 0.05f;
    389 	}
    390 	else
    391 	{
    392 		return 0.02f;
    393 	}
    394 }
    395 
    396 tcu::TextureFormat getDepthFormat (const int depthBits)
    397 {
    398 	switch (depthBits)
    399 	{
    400 		case 0:		return tcu::TextureFormat();
    401 		case 8:		return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT8);
    402 		case 16:	return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT16);
    403 		case 24:	return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT24);
    404 		case 32:
    405 		default:	return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT);
    406 	}
    407 }
    408 
    409 tcu::TextureFormat getStencilFormat (int stencilBits)
    410 {
    411 	switch (stencilBits)
    412 	{
    413 		case 0:		return tcu::TextureFormat();
    414 		case 8:
    415 		default:	return tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT8);
    416 	}
    417 }
    418 
    419 void renderReference (const tcu::PixelBufferAccess& dst, const vector<DrawPrimitiveOp>& drawOps, const tcu::PixelFormat& colorBits, const int depthBits, const int stencilBits, const int numSamples)
    420 {
    421 	const int						width			= dst.getWidth();
    422 	const int						height			= dst.getHeight();
    423 
    424 	tcu::TextureLevel				colorBuffer;
    425 	tcu::TextureLevel				depthBuffer;
    426 	tcu::TextureLevel				stencilBuffer;
    427 
    428 	rr::Renderer					referenceRenderer;
    429 	rr::VertexAttrib				attributes[2];
    430 	const ReferenceShader			shader;
    431 
    432 	attributes[0].type				= rr::VERTEXATTRIBTYPE_FLOAT;
    433 	attributes[0].size				= 4;
    434 	attributes[0].stride			= 0;
    435 	attributes[0].instanceDivisor	= 0;
    436 
    437 	attributes[1].type				= rr::VERTEXATTRIBTYPE_FLOAT;
    438 	attributes[1].size				= 4;
    439 	attributes[1].stride			= 0;
    440 	attributes[1].instanceDivisor	= 0;
    441 
    442 	// Initialize buffers.
    443 	colorBuffer.setStorage(getColorFormat(colorBits), numSamples, width, height);
    444 	rr::clearMultisampleColorBuffer(colorBuffer, CLEAR_COLOR, rr::WindowRectangle(0, 0, width, height));
    445 
    446 	if (depthBits > 0)
    447 	{
    448 		depthBuffer.setStorage(getDepthFormat(depthBits), numSamples, width, height);
    449 		rr::clearMultisampleDepthBuffer(depthBuffer, CLEAR_DEPTH, rr::WindowRectangle(0, 0, width, height));
    450 	}
    451 
    452 	if (stencilBits > 0)
    453 	{
    454 		stencilBuffer.setStorage(getStencilFormat(stencilBits), numSamples, width, height);
    455 		rr::clearMultisampleStencilBuffer(stencilBuffer, CLEAR_STENCIL, rr::WindowRectangle(0, 0, width, height));
    456 	}
    457 
    458 	const rr::RenderTarget renderTarget(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()),
    459 										rr::MultisamplePixelBufferAccess::fromMultisampleAccess(depthBuffer.getAccess()),
    460 										rr::MultisamplePixelBufferAccess::fromMultisampleAccess(stencilBuffer.getAccess()));
    461 
    462 	for (vector<DrawPrimitiveOp>::const_iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); drawOp++)
    463 	{
    464 		// Translate state
    465 		rr::RenderState renderState((rr::ViewportState)(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess())));
    466 		toReferenceRenderState(renderState, *drawOp);
    467 
    468 		DE_ASSERT(drawOp->type == PRIMITIVETYPE_TRIANGLE);
    469 
    470 		attributes[0].pointer = &drawOp->positions[0];
    471 		attributes[1].pointer = &drawOp->colors[0];
    472 
    473 		referenceRenderer.draw(
    474 			rr::DrawCommand(
    475 				renderState,
    476 				renderTarget,
    477 				rr::Program(static_cast<const rr::VertexShader*>(&shader), static_cast<const rr::FragmentShader*>(&shader)),
    478 				2,
    479 				attributes,
    480 				rr::PrimitiveList(rr::PRIMITIVETYPE_TRIANGLES, drawOp->count * 3, 0)));
    481 	}
    482 
    483 	rr::resolveMultisampleColorBuffer(dst, rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()));
    484 }
    485 
    486 // API rendering code
    487 
    488 class Program
    489 {
    490 public:
    491 					Program				(void) {}
    492 	virtual			~Program			(void) {}
    493 
    494 	virtual void	setup				(void) const = DE_NULL;
    495 };
    496 
    497 typedef de::SharedPtr<Program> ProgramSp;
    498 
    499 static glu::ProgramSources getProgramSourcesES2 (void)
    500 {
    501 	static const char* s_vertexSrc =
    502 		"attribute highp vec4 a_position;\n"
    503 		"attribute mediump vec4 a_color;\n"
    504 		"varying mediump vec4 v_color;\n"
    505 		"void main (void)\n"
    506 		"{\n"
    507 		"	gl_Position = a_position;\n"
    508 		"	v_color = a_color;\n"
    509 		"}\n";
    510 
    511 	static const char* s_fragmentSrc =
    512 		"varying mediump vec4 v_color;\n"
    513 		"void main (void)\n"
    514 		"{\n"
    515 		"	gl_FragColor = v_color;\n"
    516 		"}\n";
    517 
    518 	return glu::ProgramSources() << glu::VertexSource(s_vertexSrc) << glu::FragmentSource(s_fragmentSrc);
    519 }
    520 
    521 class GLES2Program : public Program
    522 {
    523 public:
    524 	GLES2Program (const glw::Functions& gl)
    525 		: m_gl				(gl)
    526 		, m_program			(gl, getProgramSourcesES2())
    527 		, m_positionLoc		(0)
    528 		, m_colorLoc		(0)
    529 	{
    530 
    531 		m_positionLoc	= m_gl.getAttribLocation(m_program.getProgram(), "a_position");
    532 		m_colorLoc		= m_gl.getAttribLocation(m_program.getProgram(), "a_color");
    533 	}
    534 
    535 	~GLES2Program (void)
    536 	{
    537 	}
    538 
    539 	void setup (void) const
    540 	{
    541 		m_gl.useProgram(m_program.getProgram());
    542 		m_gl.enableVertexAttribArray(m_positionLoc);
    543 		m_gl.enableVertexAttribArray(m_colorLoc);
    544 		GLU_CHECK_GLW_MSG(m_gl, "Program setup failed");
    545 	}
    546 
    547 	int						getPositionLoc		(void) const { return m_positionLoc;	}
    548 	int						getColorLoc			(void) const { return m_colorLoc;		}
    549 
    550 private:
    551 	const glw::Functions&	m_gl;
    552 	glu::ShaderProgram		m_program;
    553 	int						m_positionLoc;
    554 	int						m_colorLoc;
    555 };
    556 
    557 void clearGLES2 (const glw::Functions& gl, const tcu::Vec4& color, const float depth, const int stencil)
    558 {
    559 	gl.clearColor(color.x(), color.y(), color.z(), color.w());
    560 	gl.clearDepthf(depth);
    561 	gl.clearStencil(stencil);
    562 	gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
    563 }
    564 
    565 void drawGLES2 (const glw::Functions& gl, const Program& program, const DrawPrimitiveOp& drawOp)
    566 {
    567 	const GLES2Program& gles2Program = dynamic_cast<const GLES2Program&>(program);
    568 
    569 	switch (drawOp.blend)
    570 	{
    571 		case BLENDMODE_NONE:
    572 			gl.disable(GL_BLEND);
    573 			break;
    574 
    575 		case BLENDMODE_ADDITIVE:
    576 			gl.enable(GL_BLEND);
    577 			gl.blendFunc(GL_ONE, GL_ONE);
    578 			break;
    579 
    580 		case BLENDMODE_SRC_OVER:
    581 			gl.enable(GL_BLEND);
    582 			gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    583 			break;
    584 
    585 		default:
    586 			DE_ASSERT(false);
    587 	}
    588 
    589 	switch (drawOp.depth)
    590 	{
    591 		case DEPTHMODE_NONE:
    592 			gl.disable(GL_DEPTH_TEST);
    593 			break;
    594 
    595 		case DEPTHMODE_LESS:
    596 			gl.enable(GL_DEPTH_TEST);
    597 			break;
    598 
    599 		default:
    600 			DE_ASSERT(false);
    601 	}
    602 
    603 	switch (drawOp.stencil)
    604 	{
    605 		case STENCILMODE_NONE:
    606 			gl.disable(GL_STENCIL_TEST);
    607 			break;
    608 
    609 		case STENCILMODE_LEQUAL_INC:
    610 			gl.enable(GL_STENCIL_TEST);
    611 			gl.stencilFunc(GL_LEQUAL, drawOp.stencilRef, ~0u);
    612 			gl.stencilOp(GL_KEEP, GL_INCR, GL_INCR);
    613 			break;
    614 
    615 		default:
    616 			DE_ASSERT(false);
    617 	}
    618 
    619 	gl.disable(GL_DITHER);
    620 
    621 	gl.vertexAttribPointer(gles2Program.getPositionLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.positions[0]);
    622 	gl.vertexAttribPointer(gles2Program.getColorLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.colors[0]);
    623 
    624 	DE_ASSERT(drawOp.type == PRIMITIVETYPE_TRIANGLE);
    625 	gl.drawArrays(GL_TRIANGLES, 0, drawOp.count*3);
    626 }
    627 
    628 static void readPixelsGLES2 (const glw::Functions& gl, tcu::Surface& dst)
    629 {
    630 	gl.readPixels(0, 0, dst.getWidth(), dst.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, dst.getAccess().getDataPtr());
    631 }
    632 
    633 Program* createProgram (const glw::Functions& gl, EGLint api)
    634 {
    635 	switch (api)
    636 	{
    637 		case EGL_OPENGL_ES2_BIT:		return new GLES2Program(gl);
    638 		case EGL_OPENGL_ES3_BIT_KHR:	return new GLES2Program(gl);
    639 		default:
    640 			throw tcu::NotSupportedError("Unsupported API");
    641 	}
    642 }
    643 
    644 void draw (const glw::Functions& gl, EGLint api, const Program& program, const DrawPrimitiveOp& drawOp)
    645 {
    646 	switch (api)
    647 	{
    648 		case EGL_OPENGL_ES2_BIT:		drawGLES2(gl, program, drawOp);		break;
    649 		case EGL_OPENGL_ES3_BIT_KHR:	drawGLES2(gl, program, drawOp);		break;
    650 		default:
    651 			throw tcu::NotSupportedError("Unsupported API");
    652 	}
    653 }
    654 
    655 void clear (const glw::Functions& gl, EGLint api, const tcu::Vec4& color, const float depth, const int stencil)
    656 {
    657 	switch (api)
    658 	{
    659 		case EGL_OPENGL_ES2_BIT:		clearGLES2(gl, color, depth, stencil);		break;
    660 		case EGL_OPENGL_ES3_BIT_KHR:	clearGLES2(gl, color, depth, stencil);		break;
    661 		default:
    662 			throw tcu::NotSupportedError("Unsupported API");
    663 	}
    664 }
    665 
    666 static void readPixels (const glw::Functions& gl, EGLint api, tcu::Surface& dst)
    667 {
    668 	switch (api)
    669 	{
    670 		case EGL_OPENGL_ES2_BIT:		readPixelsGLES2(gl, dst);		break;
    671 		case EGL_OPENGL_ES3_BIT_KHR:	readPixelsGLES2(gl, dst);		break;
    672 		default:
    673 			throw tcu::NotSupportedError("Unsupported API");
    674 	}
    675 }
    676 
    677 static void finish (const glw::Functions& gl, EGLint api)
    678 {
    679 	switch (api)
    680 	{
    681 		case EGL_OPENGL_ES2_BIT:
    682 		case EGL_OPENGL_ES3_BIT_KHR:
    683 			gl.finish();
    684 			break;
    685 
    686 		default:
    687 			throw tcu::NotSupportedError("Unsupported API");
    688 	}
    689 }
    690 
    691 tcu::PixelFormat getPixelFormat (const Library& egl, EGLDisplay display, EGLConfig config)
    692 {
    693 	tcu::PixelFormat fmt;
    694 	fmt.redBits		= eglu::getConfigAttribInt(egl, display, config, EGL_RED_SIZE);
    695 	fmt.greenBits	= eglu::getConfigAttribInt(egl, display, config, EGL_GREEN_SIZE);
    696 	fmt.blueBits	= eglu::getConfigAttribInt(egl, display, config, EGL_BLUE_SIZE);
    697 	fmt.alphaBits	= eglu::getConfigAttribInt(egl, display, config, EGL_ALPHA_SIZE);
    698 	return fmt;
    699 }
    700 
    701 } // anonymous
    702 
    703 // SingleThreadRenderCase
    704 
    705 class SingleThreadRenderCase : public MultiContextRenderCase
    706 {
    707 public:
    708 						SingleThreadRenderCase		(EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi);
    709 
    710 	void				init						(void);
    711 
    712 private:
    713 	virtual void		executeForContexts			(EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts);
    714 
    715 	glw::Functions		m_gl;
    716 };
    717 
    718 // SingleThreadColorClearCase
    719 
    720 SingleThreadRenderCase::SingleThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
    721 	: MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
    722 {
    723 }
    724 
    725 void SingleThreadRenderCase::init (void)
    726 {
    727 	MultiContextRenderCase::init();
    728 	m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
    729 }
    730 
    731 void SingleThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
    732 {
    733 	const Library&			egl			= m_eglTestCtx.getLibrary();
    734 	const int				width		= eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
    735 	const int				height		= eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
    736 	const int				numContexts	= (int)contexts.size();
    737 	const int				drawsPerCtx	= 2;
    738 	const int				numIters	= 2;
    739 	const tcu::PixelFormat	pixelFmt	= getPixelFormat(egl, display, config.config);
    740 	const float				threshold	= getColorThreshold(pixelFmt);
    741 
    742 	const int				depthBits	= eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
    743 	const int				stencilBits	= eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
    744 	const int				numSamples	= eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
    745 
    746 	TestLog&				log			= m_testCtx.getLog();
    747 
    748 	tcu::Surface			refFrame	(width, height);
    749 	tcu::Surface			frame		(width, height);
    750 
    751 	de::Random				rnd			(deStringHash(getName()) ^ deInt32Hash(numContexts));
    752 	vector<ProgramSp>		programs	(contexts.size());
    753 	vector<DrawPrimitiveOp>	drawOps;
    754 
    755 	// Log basic information about config.
    756 	log << TestLog::Message << "EGL_RED_SIZE = "		<< pixelFmt.redBits << TestLog::EndMessage;
    757 	log << TestLog::Message << "EGL_GREEN_SIZE = "		<< pixelFmt.greenBits << TestLog::EndMessage;
    758 	log << TestLog::Message << "EGL_BLUE_SIZE = "		<< pixelFmt.blueBits << TestLog::EndMessage;
    759 	log << TestLog::Message << "EGL_ALPHA_SIZE = "		<< pixelFmt.alphaBits << TestLog::EndMessage;
    760 	log << TestLog::Message << "EGL_DEPTH_SIZE = "		<< depthBits << TestLog::EndMessage;
    761 	log << TestLog::Message << "EGL_STENCIL_SIZE = "	<< stencilBits << TestLog::EndMessage;
    762 	log << TestLog::Message << "EGL_SAMPLES = "			<< numSamples << TestLog::EndMessage;
    763 
    764 	// Generate draw ops.
    765 	drawOps.resize(numContexts*drawsPerCtx*numIters);
    766 	for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
    767 		randomizeDrawOp(rnd, *drawOp, (pixelFmt.alphaBits == 1));
    768 
    769 	// Create and setup programs per context
    770 	for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
    771 	{
    772 		EGLint		api			= contexts[ctxNdx].first;
    773 		EGLContext	context		= contexts[ctxNdx].second;
    774 
    775 		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
    776 
    777 		programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
    778 		programs[ctxNdx]->setup();
    779 	}
    780 
    781 	// Clear to black using first context.
    782 	{
    783 		EGLint		api			= contexts[0].first;
    784 		EGLContext	context		= contexts[0].second;
    785 
    786 		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
    787 
    788 		clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
    789 		finish(m_gl, api);
    790 	}
    791 
    792 	// Render.
    793 	for (int iterNdx = 0; iterNdx < numIters; iterNdx++)
    794 	{
    795 		for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
    796 		{
    797 			EGLint		api			= contexts[ctxNdx].first;
    798 			EGLContext	context		= contexts[ctxNdx].second;
    799 
    800 			EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
    801 
    802 			for (int drawNdx = 0; drawNdx < drawsPerCtx; drawNdx++)
    803 			{
    804 				const DrawPrimitiveOp& drawOp = drawOps[iterNdx*numContexts*drawsPerCtx + ctxNdx*drawsPerCtx + drawNdx];
    805 				draw(m_gl, api, *programs[ctxNdx], drawOp);
    806 			}
    807 
    808 			finish(m_gl, api);
    809 		}
    810 	}
    811 
    812 	// Read pixels using first context. \todo [pyry] Randomize?
    813 	{
    814 		EGLint		api		= contexts[0].first;
    815 		EGLContext	context	= contexts[0].second;
    816 
    817 		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
    818 
    819 		readPixels(m_gl, api, frame);
    820 	}
    821 
    822 	EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
    823 
    824 	// Render reference.
    825 	// \note Reference image is always generated using single-sampling.
    826 	renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1);
    827 
    828 	// Compare images
    829 	{
    830 		bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
    831 
    832 		if (!imagesOk)
    833 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
    834 	}
    835 }
    836 
    837 // MultiThreadRenderCase
    838 
    839 class MultiThreadRenderCase : public MultiContextRenderCase
    840 {
    841 public:
    842 						MultiThreadRenderCase		(EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi);
    843 
    844 	void				init						(void);
    845 
    846 private:
    847 	virtual void		executeForContexts			(EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts);
    848 
    849 	glw::Functions		m_gl;
    850 };
    851 
    852 class RenderTestThread;
    853 
    854 typedef de::SharedPtr<RenderTestThread>	RenderTestThreadSp;
    855 typedef de::SharedPtr<de::Semaphore>	SemaphoreSp;
    856 
    857 struct DrawOpPacket
    858 {
    859 	DrawOpPacket (void)
    860 		: drawOps	(DE_NULL)
    861 		, numOps	(0)
    862 	{
    863 	}
    864 
    865 	const DrawPrimitiveOp*	drawOps;
    866 	int						numOps;
    867 	SemaphoreSp				wait;
    868 	SemaphoreSp				signal;
    869 };
    870 
    871 class RenderTestThread : public de::Thread
    872 {
    873 public:
    874 	RenderTestThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const glw::Functions& gl, const Program& program, const std::vector<DrawOpPacket>& packets)
    875 		: m_egl		(egl)
    876 		, m_display	(display)
    877 		, m_surface	(surface)
    878 		, m_context	(context)
    879 		, m_api		(api)
    880 		, m_gl		(gl)
    881 		, m_program	(program)
    882 		, m_packets	(packets)
    883 	{
    884 	}
    885 
    886 	void run (void)
    887 	{
    888 		for (std::vector<DrawOpPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++)
    889 		{
    890 			// Wait until it is our turn.
    891 			packetIter->wait->decrement();
    892 
    893 			// Acquire context.
    894 			EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, m_surface, m_surface, m_context));
    895 
    896 			// Execute rendering.
    897 			for (int ndx = 0; ndx < packetIter->numOps; ndx++)
    898 				draw(m_gl, m_api, m_program, packetIter->drawOps[ndx]);
    899 
    900 			finish(m_gl, m_api);
    901 
    902 			// Release context.
    903 			EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
    904 
    905 			// Signal completion.
    906 			packetIter->signal->increment();
    907 		}
    908 		m_egl.releaseThread();
    909 	}
    910 
    911 private:
    912 	const Library&						m_egl;
    913 	EGLDisplay							m_display;
    914 	EGLSurface							m_surface;
    915 	EGLContext							m_context;
    916 	EGLint								m_api;
    917 	const glw::Functions&				m_gl;
    918 	const Program&						m_program;
    919 	const std::vector<DrawOpPacket>&	m_packets;
    920 };
    921 
    922 MultiThreadRenderCase::MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
    923 	: MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
    924 {
    925 }
    926 
    927 void MultiThreadRenderCase::init (void)
    928 {
    929 	MultiContextRenderCase::init();
    930 	m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
    931 }
    932 
    933 void MultiThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
    934 {
    935 	const Library&			egl					= m_eglTestCtx.getLibrary();
    936 	const int				width				= eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
    937 	const int				height				= eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
    938 	const int				numContexts			= (int)contexts.size();
    939 	const int				opsPerPacket		= 2;
    940 	const int				packetsPerThread	= 2;
    941 	const int				numThreads			= numContexts;
    942 	const int				numPackets			= numThreads * packetsPerThread;
    943 	const tcu::PixelFormat	pixelFmt			= getPixelFormat(egl, display, config.config);
    944 	const float				threshold			= getColorThreshold(pixelFmt);
    945 
    946 	const int				depthBits			= eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
    947 	const int				stencilBits			= eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
    948 	const int				numSamples			= eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
    949 
    950 	TestLog&				log					= m_testCtx.getLog();
    951 
    952 	tcu::Surface			refFrame			(width, height);
    953 	tcu::Surface			frame				(width, height);
    954 
    955 	de::Random				rnd					(deStringHash(getName()) ^ deInt32Hash(numContexts));
    956 
    957 	// Resources that need cleanup
    958 	vector<ProgramSp>				programs	(numContexts);
    959 	vector<SemaphoreSp>				semaphores	(numPackets+1);
    960 	vector<DrawPrimitiveOp>			drawOps		(numPackets*opsPerPacket);
    961 	vector<vector<DrawOpPacket> >	packets		(numThreads);
    962 	vector<RenderTestThreadSp>		threads		(numThreads);
    963 
    964 	// Log basic information about config.
    965 	log << TestLog::Message << "EGL_RED_SIZE = "		<< pixelFmt.redBits << TestLog::EndMessage;
    966 	log << TestLog::Message << "EGL_GREEN_SIZE = "		<< pixelFmt.greenBits << TestLog::EndMessage;
    967 	log << TestLog::Message << "EGL_BLUE_SIZE = "		<< pixelFmt.blueBits << TestLog::EndMessage;
    968 	log << TestLog::Message << "EGL_ALPHA_SIZE = "		<< pixelFmt.alphaBits << TestLog::EndMessage;
    969 	log << TestLog::Message << "EGL_DEPTH_SIZE = "		<< depthBits << TestLog::EndMessage;
    970 	log << TestLog::Message << "EGL_STENCIL_SIZE = "	<< stencilBits << TestLog::EndMessage;
    971 	log << TestLog::Message << "EGL_SAMPLES = "			<< numSamples << TestLog::EndMessage;
    972 
    973 	// Initialize semaphores.
    974 	for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
    975 		*sem = SemaphoreSp(new de::Semaphore(0));
    976 
    977 	// Create draw ops.
    978 	for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
    979 		randomizeDrawOp(rnd, *drawOp, (pixelFmt.alphaBits == 1));
    980 
    981 	// Create packets.
    982 	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
    983 	{
    984 		packets[threadNdx].resize(packetsPerThread);
    985 
    986 		for (int packetNdx = 0; packetNdx < packetsPerThread; packetNdx++)
    987 		{
    988 			DrawOpPacket& packet = packets[threadNdx][packetNdx];
    989 
    990 			// Threads take turns with packets.
    991 			packet.wait		= semaphores[packetNdx*numThreads + threadNdx];
    992 			packet.signal	= semaphores[packetNdx*numThreads + threadNdx + 1];
    993 			packet.numOps	= opsPerPacket;
    994 			packet.drawOps	= &drawOps[(packetNdx*numThreads + threadNdx)*opsPerPacket];
    995 		}
    996 	}
    997 
    998 	// Create and setup programs per context
    999 	for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
   1000 	{
   1001 		EGLint		api			= contexts[ctxNdx].first;
   1002 		EGLContext	context		= contexts[ctxNdx].second;
   1003 
   1004 		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
   1005 
   1006 		programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
   1007 		programs[ctxNdx]->setup();
   1008 
   1009 		// Release context
   1010 		EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
   1011 	}
   1012 
   1013 	// Clear to black using first context.
   1014 	{
   1015 		EGLint		api			= contexts[0].first;
   1016 		EGLContext	context		= contexts[0].second;
   1017 
   1018 		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
   1019 
   1020 		clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
   1021 		finish(m_gl, api);
   1022 
   1023 		// Release context
   1024 		EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
   1025 	}
   1026 
   1027 	// Create and launch threads (actual rendering starts once first semaphore is signaled).
   1028 	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
   1029 	{
   1030 		threads[threadNdx] = RenderTestThreadSp(new RenderTestThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, m_gl, *programs[threadNdx], packets[threadNdx]));
   1031 		threads[threadNdx]->start();
   1032 	}
   1033 
   1034 	// Signal start and wait until complete.
   1035 	semaphores.front()->increment();
   1036 	semaphores.back()->decrement();
   1037 
   1038 	// Read pixels using first context. \todo [pyry] Randomize?
   1039 	{
   1040 		EGLint		api		= contexts[0].first;
   1041 		EGLContext	context	= contexts[0].second;
   1042 
   1043 		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
   1044 
   1045 		readPixels(m_gl, api, frame);
   1046 	}
   1047 
   1048 	EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
   1049 
   1050 	// Join threads.
   1051 	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
   1052 		threads[threadNdx]->join();
   1053 
   1054 	// Render reference.
   1055 	renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1);
   1056 
   1057 	// Compare images
   1058 	{
   1059 		bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
   1060 
   1061 		if (!imagesOk)
   1062 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
   1063 	}
   1064 }
   1065 
   1066 RenderTests::RenderTests (EglTestContext& eglTestCtx)
   1067 	: TestCaseGroup(eglTestCtx, "render", "Basic rendering with different client APIs")
   1068 {
   1069 }
   1070 
   1071 RenderTests::~RenderTests (void)
   1072 {
   1073 }
   1074 
   1075 struct RenderGroupSpec
   1076 {
   1077 	const char*			name;
   1078 	const char*			desc;
   1079 	EGLint				apiBits;
   1080 	eglu::ConfigFilter	baseFilter;
   1081 	int					numContextsPerApi;
   1082 };
   1083 
   1084 template <deUint32 Bits>
   1085 static bool renderable (const eglu::CandidateConfig& c)
   1086 {
   1087 	return (c.renderableType() & Bits) == Bits;
   1088 }
   1089 
   1090 template <class RenderClass>
   1091 static void createRenderGroups (EglTestContext& eglTestCtx, tcu::TestCaseGroup* group, const RenderGroupSpec* first, const RenderGroupSpec* last)
   1092 {
   1093 	for (const RenderGroupSpec* groupIter = first; groupIter != last; groupIter++)
   1094 	{
   1095 		tcu::TestCaseGroup* configGroup = new tcu::TestCaseGroup(eglTestCtx.getTestContext(), groupIter->name, groupIter->desc);
   1096 		group->addChild(configGroup);
   1097 
   1098 		vector<RenderFilterList>	filterLists;
   1099 		eglu::FilterList			baseFilters;
   1100 		baseFilters << groupIter->baseFilter;
   1101 		getDefaultRenderFilterLists(filterLists, baseFilters);
   1102 
   1103 		for (vector<RenderFilterList>::const_iterator listIter = filterLists.begin(); listIter != filterLists.end(); listIter++)
   1104 			configGroup->addChild(new RenderClass(eglTestCtx, listIter->getName(), "", groupIter->apiBits, listIter->getSurfaceTypeMask(), *listIter, groupIter->numContextsPerApi));
   1105 	}
   1106 }
   1107 
   1108 void RenderTests::init (void)
   1109 {
   1110 	static const RenderGroupSpec singleContextCases[] =
   1111 	{
   1112 		{
   1113 			"gles2",
   1114 			"Primitive rendering using GLES2",
   1115 			EGL_OPENGL_ES2_BIT,
   1116 			renderable<EGL_OPENGL_ES2_BIT>,
   1117 			1
   1118 		},
   1119 		{
   1120 			"gles3",
   1121 			"Primitive rendering using GLES3",
   1122 			EGL_OPENGL_ES3_BIT,
   1123 			renderable<EGL_OPENGL_ES3_BIT>,
   1124 			1
   1125 		},
   1126 	};
   1127 
   1128 	static const RenderGroupSpec multiContextCases[] =
   1129 	{
   1130 		{
   1131 			"gles2",
   1132 			"Primitive rendering using multiple GLES2 contexts to shared surface",
   1133 			EGL_OPENGL_ES2_BIT,
   1134 			renderable<EGL_OPENGL_ES2_BIT>,
   1135 			3
   1136 		},
   1137 		{
   1138 			"gles3",
   1139 			"Primitive rendering using multiple GLES3 contexts to shared surface",
   1140 			EGL_OPENGL_ES3_BIT,
   1141 			renderable<EGL_OPENGL_ES3_BIT>,
   1142 			3
   1143 		},
   1144 		{
   1145 			"gles2_gles3",
   1146 			"Primitive rendering using multiple APIs to shared surface",
   1147 			EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT,
   1148 			renderable<EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT>,
   1149 			1
   1150 		},
   1151 	};
   1152 
   1153 	tcu::TestCaseGroup* singleContextGroup = new tcu::TestCaseGroup(m_testCtx, "single_context", "Single-context rendering");
   1154 	addChild(singleContextGroup);
   1155 	createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, singleContextGroup, &singleContextCases[0], &singleContextCases[DE_LENGTH_OF_ARRAY(singleContextCases)]);
   1156 
   1157 	tcu::TestCaseGroup* multiContextGroup = new tcu::TestCaseGroup(m_testCtx, "multi_context", "Multi-context rendering with shared surface");
   1158 	addChild(multiContextGroup);
   1159 	createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, multiContextGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);
   1160 
   1161 	tcu::TestCaseGroup* multiThreadGroup = new tcu::TestCaseGroup(m_testCtx, "multi_thread", "Multi-thread rendering with shared surface");
   1162 	addChild(multiThreadGroup);
   1163 	createRenderGroups<MultiThreadRenderCase>(m_eglTestCtx, multiThreadGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);
   1164 }
   1165 
   1166 } // egl
   1167 } // deqp
   1168