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 
isANarrowScreenSpaceTriangle(const tcu::Vec4 & p0,const tcu::Vec4 & p1,const tcu::Vec4 & p2)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 
randomizeDrawOp(de::Random & rnd,DrawPrimitiveOp & drawOp,const bool alphaZeroOrOne)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 
ReferenceShader()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 
shadeVertices(const rr::VertexAttrib * inputs,rr::VertexPacket * const * packets,const int numPackets) const258 	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 
shadeFragments(rr::FragmentPacket * packets,const int numPackets,const rr::FragmentShadingContext & context) const275 	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 
toReferenceRenderState(rr::RenderState & state,const DrawPrimitiveOp & drawOp)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 
getColorFormat(const tcu::PixelFormat & colorBits)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 
getColorThreshold(const tcu::PixelFormat & colorBits)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 
getDepthFormat(const int depthBits)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 
getStencilFormat(int stencilBits)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 
renderReference(const tcu::PixelBufferAccess & dst,const vector<DrawPrimitiveOp> & drawOps,const tcu::PixelFormat & colorBits,const int depthBits,const int stencilBits,const int numSamples,const int subpixelBits)419 void renderReference (const tcu::PixelBufferAccess& dst, const vector<DrawPrimitiveOp>& drawOps, const tcu::PixelFormat& colorBits, const int depthBits, const int stencilBits, const int numSamples, const int subpixelBits)
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())), subpixelBits);
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:
Program(void)491 					Program				(void) {}
~Program(void)492 	virtual			~Program			(void) {}
493 
494 	virtual void	setup				(void) const = DE_NULL;
495 };
496 
497 typedef de::SharedPtr<Program> ProgramSp;
498 
getProgramSourcesES2(void)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:
GLES2Program(const glw::Functions & gl)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 
~GLES2Program(void)535 	~GLES2Program (void)
536 	{
537 	}
538 
setup(void) const539 	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 
getPositionLoc(void) const547 	int						getPositionLoc		(void) const { return m_positionLoc;	}
getColorLoc(void) const548 	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 
clearGLES2(const glw::Functions & gl,const tcu::Vec4 & color,const float depth,const int stencil)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 
drawGLES2(const glw::Functions & gl,const Program & program,const DrawPrimitiveOp & drawOp)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 
readPixelsGLES2(const glw::Functions & gl,tcu::Surface & dst)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 
createProgram(const glw::Functions & gl,EGLint api)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 
draw(const glw::Functions & gl,EGLint api,const Program & program,const DrawPrimitiveOp & drawOp)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 
clear(const glw::Functions & gl,EGLint api,const tcu::Vec4 & color,const float depth,const int stencil)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 
readPixels(const glw::Functions & gl,EGLint api,tcu::Surface & dst)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 
finish(const glw::Functions & gl,EGLint api)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 
getPixelFormat(const Library & egl,EGLDisplay display,EGLConfig config)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 
SingleThreadRenderCase(EglTestContext & eglTestCtx,const char * name,const char * description,EGLint api,EGLint surfaceType,const eglu::FilterList & filters,int numContextsPerApi)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 
init(void)725 void SingleThreadRenderCase::init (void)
726 {
727 	MultiContextRenderCase::init();
728 	m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
729 }
730 
executeForContexts(EGLDisplay display,EGLSurface surface,const Config & config,const std::vector<std::pair<EGLint,EGLContext>> & contexts)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 	int	subpixelBits = 0;
823 	m_gl.getIntegerv(GL_SUBPIXEL_BITS, &subpixelBits);
824 
825 	EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
826 
827 	// Render reference.
828 	// \note Reference image is always generated using single-sampling.
829 	renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1, subpixelBits);
830 
831 	// Compare images
832 	{
833 		bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
834 
835 		if (!imagesOk)
836 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
837 	}
838 }
839 
840 // MultiThreadRenderCase
841 
842 class MultiThreadRenderCase : public MultiContextRenderCase
843 {
844 public:
845 						MultiThreadRenderCase		(EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi);
846 
847 	void				init						(void);
848 
849 private:
850 	virtual void		executeForContexts			(EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts);
851 
852 	glw::Functions		m_gl;
853 };
854 
855 class RenderTestThread;
856 
857 typedef de::SharedPtr<RenderTestThread>	RenderTestThreadSp;
858 typedef de::SharedPtr<de::Semaphore>	SemaphoreSp;
859 
860 struct DrawOpPacket
861 {
DrawOpPacketdeqp::egl::DrawOpPacket862 	DrawOpPacket (void)
863 		: drawOps	(DE_NULL)
864 		, numOps	(0)
865 	{
866 	}
867 
868 	const DrawPrimitiveOp*	drawOps;
869 	int						numOps;
870 	SemaphoreSp				wait;
871 	SemaphoreSp				signal;
872 };
873 
874 class RenderTestThread : public de::Thread
875 {
876 public:
RenderTestThread(const Library & egl,EGLDisplay display,EGLSurface surface,EGLContext context,EGLint api,const glw::Functions & gl,const Program & program,const std::vector<DrawOpPacket> & packets)877 	RenderTestThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const glw::Functions& gl, const Program& program, const std::vector<DrawOpPacket>& packets)
878 		: m_egl		(egl)
879 		, m_display	(display)
880 		, m_surface	(surface)
881 		, m_context	(context)
882 		, m_api		(api)
883 		, m_gl		(gl)
884 		, m_program	(program)
885 		, m_packets	(packets)
886 	{
887 	}
888 
run(void)889 	void run (void)
890 	{
891 		for (std::vector<DrawOpPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++)
892 		{
893 			// Wait until it is our turn.
894 			packetIter->wait->decrement();
895 
896 			// Acquire context.
897 			EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, m_surface, m_surface, m_context));
898 
899 			// Execute rendering.
900 			for (int ndx = 0; ndx < packetIter->numOps; ndx++)
901 				draw(m_gl, m_api, m_program, packetIter->drawOps[ndx]);
902 
903 			finish(m_gl, m_api);
904 
905 			// Release context.
906 			EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
907 
908 			// Signal completion.
909 			packetIter->signal->increment();
910 		}
911 		m_egl.releaseThread();
912 	}
913 
914 private:
915 	const Library&						m_egl;
916 	EGLDisplay							m_display;
917 	EGLSurface							m_surface;
918 	EGLContext							m_context;
919 	EGLint								m_api;
920 	const glw::Functions&				m_gl;
921 	const Program&						m_program;
922 	const std::vector<DrawOpPacket>&	m_packets;
923 };
924 
MultiThreadRenderCase(EglTestContext & eglTestCtx,const char * name,const char * description,EGLint api,EGLint surfaceType,const eglu::FilterList & filters,int numContextsPerApi)925 MultiThreadRenderCase::MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
926 	: MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
927 {
928 }
929 
init(void)930 void MultiThreadRenderCase::init (void)
931 {
932 	MultiContextRenderCase::init();
933 	m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
934 }
935 
executeForContexts(EGLDisplay display,EGLSurface surface,const Config & config,const std::vector<std::pair<EGLint,EGLContext>> & contexts)936 void MultiThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
937 {
938 	const Library&			egl					= m_eglTestCtx.getLibrary();
939 	const int				width				= eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
940 	const int				height				= eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
941 	const int				numContexts			= (int)contexts.size();
942 	const int				opsPerPacket		= 2;
943 	const int				packetsPerThread	= 2;
944 	const int				numThreads			= numContexts;
945 	const int				numPackets			= numThreads * packetsPerThread;
946 	const tcu::PixelFormat	pixelFmt			= getPixelFormat(egl, display, config.config);
947 	const float				threshold			= getColorThreshold(pixelFmt);
948 
949 	const int				depthBits			= eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
950 	const int				stencilBits			= eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
951 	const int				numSamples			= eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
952 
953 	TestLog&				log					= m_testCtx.getLog();
954 
955 	tcu::Surface			refFrame			(width, height);
956 	tcu::Surface			frame				(width, height);
957 
958 	de::Random				rnd					(deStringHash(getName()) ^ deInt32Hash(numContexts));
959 
960 	// Resources that need cleanup
961 	vector<ProgramSp>				programs	(numContexts);
962 	vector<SemaphoreSp>				semaphores	(numPackets+1);
963 	vector<DrawPrimitiveOp>			drawOps		(numPackets*opsPerPacket);
964 	vector<vector<DrawOpPacket> >	packets		(numThreads);
965 	vector<RenderTestThreadSp>		threads		(numThreads);
966 
967 	// Log basic information about config.
968 	log << TestLog::Message << "EGL_RED_SIZE = "		<< pixelFmt.redBits << TestLog::EndMessage;
969 	log << TestLog::Message << "EGL_GREEN_SIZE = "		<< pixelFmt.greenBits << TestLog::EndMessage;
970 	log << TestLog::Message << "EGL_BLUE_SIZE = "		<< pixelFmt.blueBits << TestLog::EndMessage;
971 	log << TestLog::Message << "EGL_ALPHA_SIZE = "		<< pixelFmt.alphaBits << TestLog::EndMessage;
972 	log << TestLog::Message << "EGL_DEPTH_SIZE = "		<< depthBits << TestLog::EndMessage;
973 	log << TestLog::Message << "EGL_STENCIL_SIZE = "	<< stencilBits << TestLog::EndMessage;
974 	log << TestLog::Message << "EGL_SAMPLES = "			<< numSamples << TestLog::EndMessage;
975 
976 	// Initialize semaphores.
977 	for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
978 		*sem = SemaphoreSp(new de::Semaphore(0));
979 
980 	// Create draw ops.
981 	for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
982 		randomizeDrawOp(rnd, *drawOp, (pixelFmt.alphaBits == 1));
983 
984 	// Create packets.
985 	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
986 	{
987 		packets[threadNdx].resize(packetsPerThread);
988 
989 		for (int packetNdx = 0; packetNdx < packetsPerThread; packetNdx++)
990 		{
991 			DrawOpPacket& packet = packets[threadNdx][packetNdx];
992 
993 			// Threads take turns with packets.
994 			packet.wait		= semaphores[packetNdx*numThreads + threadNdx];
995 			packet.signal	= semaphores[packetNdx*numThreads + threadNdx + 1];
996 			packet.numOps	= opsPerPacket;
997 			packet.drawOps	= &drawOps[(packetNdx*numThreads + threadNdx)*opsPerPacket];
998 		}
999 	}
1000 
1001 	// Create and setup programs per context
1002 	for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
1003 	{
1004 		EGLint		api			= contexts[ctxNdx].first;
1005 		EGLContext	context		= contexts[ctxNdx].second;
1006 
1007 		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1008 
1009 		programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
1010 		programs[ctxNdx]->setup();
1011 
1012 		// Release context
1013 		EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1014 	}
1015 
1016 	// Clear to black using first context.
1017 	{
1018 		EGLint		api			= contexts[0].first;
1019 		EGLContext	context		= contexts[0].second;
1020 
1021 		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1022 
1023 		clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
1024 		finish(m_gl, api);
1025 
1026 		// Release context
1027 		EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1028 	}
1029 
1030 	// Create and launch threads (actual rendering starts once first semaphore is signaled).
1031 	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
1032 	{
1033 		threads[threadNdx] = RenderTestThreadSp(new RenderTestThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, m_gl, *programs[threadNdx], packets[threadNdx]));
1034 		threads[threadNdx]->start();
1035 	}
1036 
1037 	// Signal start and wait until complete.
1038 	semaphores.front()->increment();
1039 	semaphores.back()->decrement();
1040 
1041 	// Read pixels using first context. \todo [pyry] Randomize?
1042 	{
1043 		EGLint		api		= contexts[0].first;
1044 		EGLContext	context	= contexts[0].second;
1045 
1046 		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1047 
1048 		readPixels(m_gl, api, frame);
1049 	}
1050 
1051 	int	subpixelBits = 0;
1052 	m_gl.getIntegerv(GL_SUBPIXEL_BITS, &subpixelBits);
1053 
1054 	EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1055 
1056 	// Join threads.
1057 	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
1058 		threads[threadNdx]->join();
1059 
1060 	// Render reference.
1061 	renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1, subpixelBits);
1062 
1063 	// Compare images
1064 	{
1065 		bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
1066 
1067 		if (!imagesOk)
1068 			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
1069 	}
1070 }
1071 
RenderTests(EglTestContext & eglTestCtx)1072 RenderTests::RenderTests (EglTestContext& eglTestCtx)
1073 	: TestCaseGroup(eglTestCtx, "render", "Basic rendering with different client APIs")
1074 {
1075 }
1076 
~RenderTests(void)1077 RenderTests::~RenderTests (void)
1078 {
1079 }
1080 
1081 struct RenderGroupSpec
1082 {
1083 	const char*			name;
1084 	const char*			desc;
1085 	EGLint				apiBits;
1086 	eglu::ConfigFilter	baseFilter;
1087 	int					numContextsPerApi;
1088 };
1089 
1090 template <deUint32 Bits>
renderable(const eglu::CandidateConfig & c)1091 static bool renderable (const eglu::CandidateConfig& c)
1092 {
1093 	return (c.renderableType() & Bits) == Bits;
1094 }
1095 
1096 template <class RenderClass>
createRenderGroups(EglTestContext & eglTestCtx,tcu::TestCaseGroup * group,const RenderGroupSpec * first,const RenderGroupSpec * last)1097 static void createRenderGroups (EglTestContext& eglTestCtx, tcu::TestCaseGroup* group, const RenderGroupSpec* first, const RenderGroupSpec* last)
1098 {
1099 	for (const RenderGroupSpec* groupIter = first; groupIter != last; groupIter++)
1100 	{
1101 		tcu::TestCaseGroup* configGroup = new tcu::TestCaseGroup(eglTestCtx.getTestContext(), groupIter->name, groupIter->desc);
1102 		group->addChild(configGroup);
1103 
1104 		vector<RenderFilterList>	filterLists;
1105 		eglu::FilterList			baseFilters;
1106 		baseFilters << groupIter->baseFilter;
1107 		getDefaultRenderFilterLists(filterLists, baseFilters);
1108 
1109 		for (vector<RenderFilterList>::const_iterator listIter = filterLists.begin(); listIter != filterLists.end(); listIter++)
1110 			configGroup->addChild(new RenderClass(eglTestCtx, listIter->getName(), "", groupIter->apiBits, listIter->getSurfaceTypeMask(), *listIter, groupIter->numContextsPerApi));
1111 	}
1112 }
1113 
init(void)1114 void RenderTests::init (void)
1115 {
1116 	static const RenderGroupSpec singleContextCases[] =
1117 	{
1118 		{
1119 			"gles2",
1120 			"Primitive rendering using GLES2",
1121 			EGL_OPENGL_ES2_BIT,
1122 			renderable<EGL_OPENGL_ES2_BIT>,
1123 			1
1124 		},
1125 		{
1126 			"gles3",
1127 			"Primitive rendering using GLES3",
1128 			EGL_OPENGL_ES3_BIT,
1129 			renderable<EGL_OPENGL_ES3_BIT>,
1130 			1
1131 		},
1132 	};
1133 
1134 	static const RenderGroupSpec multiContextCases[] =
1135 	{
1136 		{
1137 			"gles2",
1138 			"Primitive rendering using multiple GLES2 contexts to shared surface",
1139 			EGL_OPENGL_ES2_BIT,
1140 			renderable<EGL_OPENGL_ES2_BIT>,
1141 			3
1142 		},
1143 		{
1144 			"gles3",
1145 			"Primitive rendering using multiple GLES3 contexts to shared surface",
1146 			EGL_OPENGL_ES3_BIT,
1147 			renderable<EGL_OPENGL_ES3_BIT>,
1148 			3
1149 		},
1150 		{
1151 			"gles2_gles3",
1152 			"Primitive rendering using multiple APIs to shared surface",
1153 			EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT,
1154 			renderable<EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT>,
1155 			1
1156 		},
1157 	};
1158 
1159 	tcu::TestCaseGroup* singleContextGroup = new tcu::TestCaseGroup(m_testCtx, "single_context", "Single-context rendering");
1160 	addChild(singleContextGroup);
1161 	createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, singleContextGroup, &singleContextCases[0], &singleContextCases[DE_LENGTH_OF_ARRAY(singleContextCases)]);
1162 
1163 	tcu::TestCaseGroup* multiContextGroup = new tcu::TestCaseGroup(m_testCtx, "multi_context", "Multi-context rendering with shared surface");
1164 	addChild(multiContextGroup);
1165 	createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, multiContextGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);
1166 
1167 	tcu::TestCaseGroup* multiThreadGroup = new tcu::TestCaseGroup(m_testCtx, "multi_thread", "Multi-thread rendering with shared surface");
1168 	addChild(multiThreadGroup);
1169 	createRenderGroups<MultiThreadRenderCase>(m_eglTestCtx, multiThreadGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);
1170 }
1171 
1172 } // egl
1173 } // deqp
1174