teglRenderTests.cpp revision 3c67e4f0ec73f9c30c6b2ed2adfbfe7faaf576a4
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
59namespace deqp
60{
61namespace egl
62{
63
64using std::string;
65using std::vector;
66using std::set;
67
68using tcu::Vec4;
69
70using tcu::TestLog;
71
72using namespace glw;
73using namespace eglw;
74
75static const tcu::Vec4	CLEAR_COLOR		= tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
76static const float		CLEAR_DEPTH		= 1.0f;
77static const int		CLEAR_STENCIL	= 0;
78
79namespace
80{
81
82enum 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
91enum 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
100enum 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
108enum 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
116struct 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
128void randomizeDrawOp (de::Random& rnd, DrawPrimitiveOp& drawOp)
129{
130	const int	minStencilRef	= 0;
131	const int	maxStencilRef	= 8;
132	const int	minPrimitives	= 2;
133	const int	maxPrimitives	= 4;
134
135	const float	maxTriOffset	= 1.0f;
136	const float	minDepth		= -1.0f; // \todo [pyry] Reference doesn't support Z clipping yet
137	const float	maxDepth		= 1.0f;
138
139	const float	minRGB			= 0.2f;
140	const float	maxRGB			= 0.9f;
141	const float	minAlpha		= 0.3f;
142	const float	maxAlpha		= 1.0f;
143
144	drawOp.type			= (PrimitiveType)rnd.getInt(0, PRIMITIVETYPE_LAST-1);
145	drawOp.count		= rnd.getInt(minPrimitives, maxPrimitives);
146	drawOp.blend		= (BlendMode)rnd.getInt(0, BLENDMODE_LAST-1);
147	drawOp.depth		= (DepthMode)rnd.getInt(0, DEPTHMODE_LAST-1);
148	drawOp.stencil		= (StencilMode)rnd.getInt(0, STENCILMODE_LAST-1);
149	drawOp.stencilRef	= rnd.getInt(minStencilRef, maxStencilRef);
150
151	if (drawOp.type == PRIMITIVETYPE_TRIANGLE)
152	{
153		drawOp.positions.resize(drawOp.count*3);
154		drawOp.colors.resize(drawOp.count*3);
155
156		for (int triNdx = 0; triNdx < drawOp.count; triNdx++)
157		{
158			const float		cx		= rnd.getFloat(-1.0f, 1.0f);
159			const float		cy		= rnd.getFloat(-1.0f, 1.0f);
160
161			for (int coordNdx = 0; coordNdx < 3; coordNdx++)
162			{
163				tcu::Vec4&	position	= drawOp.positions[triNdx*3 + coordNdx];
164				tcu::Vec4&	color		= drawOp.colors[triNdx*3 + coordNdx];
165
166				position.x()	= cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
167				position.y()	= cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
168				position.z()	= rnd.getFloat(minDepth, maxDepth);
169				position.w()	= 1.0f;
170
171				color.x()		= rnd.getFloat(minRGB, maxRGB);
172				color.y()		= rnd.getFloat(minRGB, maxRGB);
173				color.z()		= rnd.getFloat(minRGB, maxRGB);
174				color.w()		= rnd.getFloat(minAlpha, maxAlpha);
175			}
176		}
177	}
178	else
179		DE_ASSERT(false);
180}
181
182// Reference rendering code
183
184class ReferenceShader : public rr::VertexShader, public rr::FragmentShader
185{
186public:
187	enum
188	{
189		VaryingLoc_Color = 0
190	};
191
192	ReferenceShader ()
193		: rr::VertexShader	(2, 1)		// color and pos in => color out
194		, rr::FragmentShader(1, 1)		// color in => color out
195	{
196		this->rr::VertexShader::m_inputs[0].type		= rr::GENERICVECTYPE_FLOAT;
197		this->rr::VertexShader::m_inputs[1].type		= rr::GENERICVECTYPE_FLOAT;
198
199		this->rr::VertexShader::m_outputs[0].type		= rr::GENERICVECTYPE_FLOAT;
200		this->rr::VertexShader::m_outputs[0].flatshade	= false;
201
202		this->rr::FragmentShader::m_inputs[0].type		= rr::GENERICVECTYPE_FLOAT;
203		this->rr::FragmentShader::m_inputs[0].flatshade	= false;
204
205		this->rr::FragmentShader::m_outputs[0].type		= rr::GENERICVECTYPE_FLOAT;
206	}
207
208	void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const
209	{
210		for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
211		{
212			const int positionAttrLoc = 0;
213			const int colorAttrLoc = 1;
214
215			rr::VertexPacket& packet = *packets[packetNdx];
216
217			// Transform to position
218			packet.position = rr::readVertexAttribFloat(inputs[positionAttrLoc], packet.instanceNdx, packet.vertexNdx);
219
220			// Pass color to FS
221			packet.outputs[VaryingLoc_Color] = rr::readVertexAttribFloat(inputs[colorAttrLoc], packet.instanceNdx, packet.vertexNdx);
222		}
223	}
224
225	void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
226	{
227		for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
228		{
229			rr::FragmentPacket& packet = packets[packetNdx];
230
231			for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
232				rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, rr::readVarying<float>(packet, context, VaryingLoc_Color, fragNdx));
233		}
234	}
235};
236
237void toReferenceRenderState (rr::RenderState& state, const DrawPrimitiveOp& drawOp)
238{
239	state.cullMode	= rr::CULLMODE_NONE;
240
241	if (drawOp.blend != BLENDMODE_NONE)
242	{
243		state.fragOps.blendMode = rr::BLENDMODE_STANDARD;
244
245		switch (drawOp.blend)
246		{
247			case BLENDMODE_ADDITIVE:
248				state.fragOps.blendRGBState.srcFunc		= rr::BLENDFUNC_ONE;
249				state.fragOps.blendRGBState.dstFunc		= rr::BLENDFUNC_ONE;
250				state.fragOps.blendRGBState.equation	= rr::BLENDEQUATION_ADD;
251				state.fragOps.blendAState				= state.fragOps.blendRGBState;
252				break;
253
254			case BLENDMODE_SRC_OVER:
255				state.fragOps.blendRGBState.srcFunc		= rr::BLENDFUNC_SRC_ALPHA;
256				state.fragOps.blendRGBState.dstFunc		= rr::BLENDFUNC_ONE_MINUS_SRC_ALPHA;
257				state.fragOps.blendRGBState.equation	= rr::BLENDEQUATION_ADD;
258				state.fragOps.blendAState				= state.fragOps.blendRGBState;
259				break;
260
261			default:
262				DE_ASSERT(false);
263		}
264	}
265
266	if (drawOp.depth != DEPTHMODE_NONE)
267	{
268		state.fragOps.depthTestEnabled = true;
269
270		DE_ASSERT(drawOp.depth == DEPTHMODE_LESS);
271		state.fragOps.depthFunc = rr::TESTFUNC_LESS;
272	}
273
274	if (drawOp.stencil != STENCILMODE_NONE)
275	{
276		state.fragOps.stencilTestEnabled = true;
277
278		DE_ASSERT(drawOp.stencil == STENCILMODE_LEQUAL_INC);
279		state.fragOps.stencilStates[0].func		= rr::TESTFUNC_LEQUAL;
280		state.fragOps.stencilStates[0].sFail	= rr::STENCILOP_KEEP;
281		state.fragOps.stencilStates[0].dpFail	= rr::STENCILOP_INCR;
282		state.fragOps.stencilStates[0].dpPass	= rr::STENCILOP_INCR;
283		state.fragOps.stencilStates[0].ref		= drawOp.stencilRef;
284		state.fragOps.stencilStates[1]			= state.fragOps.stencilStates[0];
285	}
286}
287
288tcu::TextureFormat getColorFormat (const tcu::PixelFormat& colorBits)
289{
290	using tcu::TextureFormat;
291
292	DE_ASSERT(de::inBounds(colorBits.redBits,	0, 0xff) &&
293			  de::inBounds(colorBits.greenBits,	0, 0xff) &&
294			  de::inBounds(colorBits.blueBits,	0, 0xff) &&
295			  de::inBounds(colorBits.alphaBits,	0, 0xff));
296
297#define PACK_FMT(R, G, B, A) (((R) << 24) | ((G) << 16) | ((B) << 8) | (A))
298
299	// \note [pyry] This may not hold true on some implementations - best effort guess only.
300	switch (PACK_FMT(colorBits.redBits, colorBits.greenBits, colorBits.blueBits, colorBits.alphaBits))
301	{
302		case PACK_FMT(8,8,8,8):		return TextureFormat(TextureFormat::RGBA,	TextureFormat::UNORM_INT8);
303		case PACK_FMT(8,8,8,0):		return TextureFormat(TextureFormat::RGB,	TextureFormat::UNORM_INT8);
304		case PACK_FMT(4,4,4,4):		return TextureFormat(TextureFormat::RGBA,	TextureFormat::UNORM_SHORT_4444);
305		case PACK_FMT(5,5,5,1):		return TextureFormat(TextureFormat::RGBA,	TextureFormat::UNORM_SHORT_5551);
306		case PACK_FMT(5,6,5,0):		return TextureFormat(TextureFormat::RGB,	TextureFormat::UNORM_SHORT_565);
307
308		// \note Defaults to RGBA8
309		default:					return TextureFormat(TextureFormat::RGBA,	TextureFormat::UNORM_INT8);
310	}
311
312#undef PACK_FMT
313}
314
315tcu::TextureFormat getDepthFormat (const int depthBits)
316{
317	switch (depthBits)
318	{
319		case 0:		return tcu::TextureFormat();
320		case 8:		return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT8);
321		case 16:	return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT16);
322		case 24:	return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNSIGNED_INT_24_8);
323		case 32:
324		default:	return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT);
325	}
326}
327
328tcu::TextureFormat getStencilFormat (int stencilBits)
329{
330	switch (stencilBits)
331	{
332		case 0:		return tcu::TextureFormat();
333		case 8:
334		default:	return tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT8);
335	}
336}
337
338void renderReference (const tcu::PixelBufferAccess& dst, const vector<DrawPrimitiveOp>& drawOps, const tcu::PixelFormat& colorBits, const int depthBits, const int stencilBits, const int numSamples)
339{
340	const int						width			= dst.getWidth();
341	const int						height			= dst.getHeight();
342
343	tcu::TextureLevel				colorBuffer;
344	tcu::TextureLevel				depthBuffer;
345	tcu::TextureLevel				stencilBuffer;
346
347	rr::Renderer					referenceRenderer;
348	rr::VertexAttrib				attributes[2];
349	const ReferenceShader			shader;
350
351	attributes[0].type				= rr::VERTEXATTRIBTYPE_FLOAT;
352	attributes[0].size				= 4;
353	attributes[0].stride			= 0;
354	attributes[0].instanceDivisor	= 0;
355
356	attributes[1].type				= rr::VERTEXATTRIBTYPE_FLOAT;
357	attributes[1].size				= 4;
358	attributes[1].stride			= 0;
359	attributes[1].instanceDivisor	= 0;
360
361	// Initialize buffers.
362	colorBuffer.setStorage(getColorFormat(colorBits), numSamples, width, height);
363	rr::clearMultisampleColorBuffer(colorBuffer, CLEAR_COLOR, rr::WindowRectangle(0, 0, width, height));
364
365	if (depthBits > 0)
366	{
367		depthBuffer.setStorage(getDepthFormat(depthBits), numSamples, width, height);
368		rr::clearMultisampleDepthBuffer(depthBuffer, CLEAR_DEPTH, rr::WindowRectangle(0, 0, width, height));
369	}
370
371	if (stencilBits > 0)
372	{
373		stencilBuffer.setStorage(getStencilFormat(stencilBits), numSamples, width, height);
374		rr::clearMultisampleStencilBuffer(stencilBuffer, CLEAR_STENCIL, rr::WindowRectangle(0, 0, width, height));
375	}
376
377	const rr::RenderTarget renderTarget(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()),
378										rr::MultisamplePixelBufferAccess::fromMultisampleAccess(depthBuffer.getAccess()),
379										rr::MultisamplePixelBufferAccess::fromMultisampleAccess(stencilBuffer.getAccess()));
380
381	for (vector<DrawPrimitiveOp>::const_iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); drawOp++)
382	{
383		// Translate state
384		rr::RenderState renderState((rr::ViewportState)(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess())));
385		toReferenceRenderState(renderState, *drawOp);
386
387		DE_ASSERT(drawOp->type == PRIMITIVETYPE_TRIANGLE);
388
389		attributes[0].pointer = &drawOp->positions[0];
390		attributes[1].pointer = &drawOp->colors[0];
391
392		referenceRenderer.draw(
393			rr::DrawCommand(
394				renderState,
395				renderTarget,
396				rr::Program(static_cast<const rr::VertexShader*>(&shader), static_cast<const rr::FragmentShader*>(&shader)),
397				2,
398				attributes,
399				rr::PrimitiveList(rr::PRIMITIVETYPE_TRIANGLES, drawOp->count * 3, 0)));
400	}
401
402	rr::resolveMultisampleColorBuffer(dst, rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()));
403}
404
405// API rendering code
406
407class Program
408{
409public:
410					Program				(void) {}
411	virtual			~Program			(void) {}
412
413	virtual void	setup				(void) const = DE_NULL;
414};
415
416typedef de::SharedPtr<Program> ProgramSp;
417
418static glu::ProgramSources getProgramSourcesES2 (void)
419{
420	static const char* s_vertexSrc =
421		"attribute highp vec4 a_position;\n"
422		"attribute mediump vec4 a_color;\n"
423		"varying mediump vec4 v_color;\n"
424		"void main (void)\n"
425		"{\n"
426		"	gl_Position = a_position;\n"
427		"	v_color = a_color;\n"
428		"}\n";
429
430	static const char* s_fragmentSrc =
431		"varying mediump vec4 v_color;\n"
432		"void main (void)\n"
433		"{\n"
434		"	gl_FragColor = v_color;\n"
435		"}\n";
436
437	return glu::ProgramSources() << glu::VertexSource(s_vertexSrc) << glu::FragmentSource(s_fragmentSrc);
438}
439
440class GLES2Program : public Program
441{
442public:
443	GLES2Program (const glw::Functions& gl)
444		: m_gl				(gl)
445		, m_program			(gl, getProgramSourcesES2())
446		, m_positionLoc		(0)
447		, m_colorLoc		(0)
448	{
449
450		m_positionLoc	= m_gl.getAttribLocation(m_program.getProgram(), "a_position");
451		m_colorLoc		= m_gl.getAttribLocation(m_program.getProgram(), "a_color");
452	}
453
454	~GLES2Program (void)
455	{
456	}
457
458	void setup (void) const
459	{
460		m_gl.useProgram(m_program.getProgram());
461		m_gl.enableVertexAttribArray(m_positionLoc);
462		m_gl.enableVertexAttribArray(m_colorLoc);
463		GLU_CHECK_GLW_MSG(m_gl, "Program setup failed");
464	}
465
466	int						getPositionLoc		(void) const { return m_positionLoc;	}
467	int						getColorLoc			(void) const { return m_colorLoc;		}
468
469private:
470	const glw::Functions&	m_gl;
471	glu::ShaderProgram		m_program;
472	int						m_positionLoc;
473	int						m_colorLoc;
474};
475
476void clearGLES2 (const glw::Functions& gl, const tcu::Vec4& color, const float depth, const int stencil)
477{
478	gl.clearColor(color.x(), color.y(), color.z(), color.w());
479	gl.clearDepthf(depth);
480	gl.clearStencil(stencil);
481	gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
482}
483
484void drawGLES2 (const glw::Functions& gl, const Program& program, const DrawPrimitiveOp& drawOp)
485{
486	const GLES2Program& gles2Program = dynamic_cast<const GLES2Program&>(program);
487
488	switch (drawOp.blend)
489	{
490		case BLENDMODE_NONE:
491			gl.disable(GL_BLEND);
492			break;
493
494		case BLENDMODE_ADDITIVE:
495			gl.enable(GL_BLEND);
496			gl.blendFunc(GL_ONE, GL_ONE);
497			break;
498
499		case BLENDMODE_SRC_OVER:
500			gl.enable(GL_BLEND);
501			gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
502			break;
503
504		default:
505			DE_ASSERT(false);
506	}
507
508	switch (drawOp.depth)
509	{
510		case DEPTHMODE_NONE:
511			gl.disable(GL_DEPTH_TEST);
512			break;
513
514		case DEPTHMODE_LESS:
515			gl.enable(GL_DEPTH_TEST);
516			break;
517
518		default:
519			DE_ASSERT(false);
520	}
521
522	switch (drawOp.stencil)
523	{
524		case STENCILMODE_NONE:
525			gl.disable(GL_STENCIL_TEST);
526			break;
527
528		case STENCILMODE_LEQUAL_INC:
529			gl.enable(GL_STENCIL_TEST);
530			gl.stencilFunc(GL_LEQUAL, drawOp.stencilRef, ~0u);
531			gl.stencilOp(GL_KEEP, GL_INCR, GL_INCR);
532			break;
533
534		default:
535			DE_ASSERT(false);
536	}
537
538	gl.vertexAttribPointer(gles2Program.getPositionLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.positions[0]);
539	gl.vertexAttribPointer(gles2Program.getColorLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.colors[0]);
540
541	DE_ASSERT(drawOp.type == PRIMITIVETYPE_TRIANGLE);
542	gl.drawArrays(GL_TRIANGLES, 0, drawOp.count*3);
543}
544
545static void readPixelsGLES2 (const glw::Functions& gl, tcu::Surface& dst)
546{
547	gl.readPixels(0, 0, dst.getWidth(), dst.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, dst.getAccess().getDataPtr());
548}
549
550Program* createProgram (const glw::Functions& gl, EGLint api)
551{
552	switch (api)
553	{
554		case EGL_OPENGL_ES2_BIT:		return new GLES2Program(gl);
555		case EGL_OPENGL_ES3_BIT_KHR:	return new GLES2Program(gl);
556		default:
557			throw tcu::NotSupportedError("Unsupported API");
558	}
559}
560
561void draw (const glw::Functions& gl, EGLint api, const Program& program, const DrawPrimitiveOp& drawOp)
562{
563	switch (api)
564	{
565		case EGL_OPENGL_ES2_BIT:		drawGLES2(gl, program, drawOp);		break;
566		case EGL_OPENGL_ES3_BIT_KHR:	drawGLES2(gl, program, drawOp);		break;
567		default:
568			throw tcu::NotSupportedError("Unsupported API");
569	}
570}
571
572void clear (const glw::Functions& gl, EGLint api, const tcu::Vec4& color, const float depth, const int stencil)
573{
574	switch (api)
575	{
576		case EGL_OPENGL_ES2_BIT:		clearGLES2(gl, color, depth, stencil);		break;
577		case EGL_OPENGL_ES3_BIT_KHR:	clearGLES2(gl, color, depth, stencil);		break;
578		default:
579			throw tcu::NotSupportedError("Unsupported API");
580	}
581}
582
583static void readPixels (const glw::Functions& gl, EGLint api, tcu::Surface& dst)
584{
585	switch (api)
586	{
587		case EGL_OPENGL_ES2_BIT:		readPixelsGLES2(gl, dst);		break;
588		case EGL_OPENGL_ES3_BIT_KHR:	readPixelsGLES2(gl, dst);		break;
589		default:
590			throw tcu::NotSupportedError("Unsupported API");
591	}
592}
593
594tcu::PixelFormat getPixelFormat (const Library& egl, EGLDisplay display, EGLConfig config)
595{
596	tcu::PixelFormat fmt;
597	fmt.redBits		= eglu::getConfigAttribInt(egl, display, config, EGL_RED_SIZE);
598	fmt.greenBits	= eglu::getConfigAttribInt(egl, display, config, EGL_GREEN_SIZE);
599	fmt.blueBits	= eglu::getConfigAttribInt(egl, display, config, EGL_BLUE_SIZE);
600	fmt.alphaBits	= eglu::getConfigAttribInt(egl, display, config, EGL_ALPHA_SIZE);
601	return fmt;
602}
603
604} // anonymous
605
606// SingleThreadRenderCase
607
608class SingleThreadRenderCase : public MultiContextRenderCase
609{
610public:
611						SingleThreadRenderCase		(EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi);
612
613private:
614	virtual void		executeForContexts			(EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts);
615
616	glw::Functions		m_gl;
617};
618
619// SingleThreadColorClearCase
620
621SingleThreadRenderCase::SingleThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
622	: MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
623{
624	m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
625}
626
627void SingleThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
628{
629	const Library&			egl			= m_eglTestCtx.getLibrary();
630	const int				width		= eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
631	const int				height		= eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
632	const int				numContexts	= (int)contexts.size();
633	const int				drawsPerCtx	= 2;
634	const int				numIters	= 2;
635	const float				threshold	= 0.02f;
636
637	const tcu::PixelFormat	pixelFmt	= getPixelFormat(egl, display, config.config);
638	const int				depthBits	= eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
639	const int				stencilBits	= eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
640	const int				numSamples	= eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
641
642	TestLog&				log			= m_testCtx.getLog();
643
644	tcu::Surface			refFrame	(width, height);
645	tcu::Surface			frame		(width, height);
646
647	de::Random				rnd			(deStringHash(getName()) ^ deInt32Hash(numContexts));
648	vector<ProgramSp>		programs	(contexts.size());
649	vector<DrawPrimitiveOp>	drawOps;
650
651	// Log basic information about config.
652	log << TestLog::Message << "EGL_RED_SIZE = "		<< pixelFmt.redBits << TestLog::EndMessage;
653	log << TestLog::Message << "EGL_GREEN_SIZE = "		<< pixelFmt.greenBits << TestLog::EndMessage;
654	log << TestLog::Message << "EGL_BLUE_SIZE = "		<< pixelFmt.blueBits << TestLog::EndMessage;
655	log << TestLog::Message << "EGL_ALPHA_SIZE = "		<< pixelFmt.alphaBits << TestLog::EndMessage;
656	log << TestLog::Message << "EGL_DEPTH_SIZE = "		<< depthBits << TestLog::EndMessage;
657	log << TestLog::Message << "EGL_STENCIL_SIZE = "	<< stencilBits << TestLog::EndMessage;
658	log << TestLog::Message << "EGL_SAMPLES = "			<< numSamples << TestLog::EndMessage;
659
660	// Generate draw ops.
661	drawOps.resize(numContexts*drawsPerCtx*numIters);
662	for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
663		randomizeDrawOp(rnd, *drawOp);
664
665	// Create and setup programs per context
666	for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
667	{
668		EGLint		api			= contexts[ctxNdx].first;
669		EGLContext	context		= contexts[ctxNdx].second;
670
671		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
672
673		programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
674		programs[ctxNdx]->setup();
675	}
676
677	// Clear to black using first context.
678	{
679		EGLint		api			= contexts[0].first;
680		EGLContext	context		= contexts[0].second;
681
682		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
683
684		clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
685	}
686
687	// Render.
688	for (int iterNdx = 0; iterNdx < numIters; iterNdx++)
689	{
690		for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
691		{
692			EGLint		api			= contexts[ctxNdx].first;
693			EGLContext	context		= contexts[ctxNdx].second;
694
695			EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
696
697			for (int drawNdx = 0; drawNdx < drawsPerCtx; drawNdx++)
698			{
699				const DrawPrimitiveOp& drawOp = drawOps[iterNdx*numContexts*drawsPerCtx + ctxNdx*drawsPerCtx + drawNdx];
700				draw(m_gl, api, *programs[ctxNdx], drawOp);
701			}
702		}
703	}
704
705	// Read pixels using first context. \todo [pyry] Randomize?
706	{
707		EGLint		api		= contexts[0].first;
708		EGLContext	context	= contexts[0].second;
709
710		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
711
712		readPixels(m_gl, api, frame);
713	}
714
715	// Render reference.
716	// \note Reference image is always generated using single-sampling.
717	renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1);
718
719	// Compare images
720	{
721		bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
722
723		if (!imagesOk)
724			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
725	}
726}
727
728// MultiThreadRenderCase
729
730class MultiThreadRenderCase : public MultiContextRenderCase
731{
732public:
733						MultiThreadRenderCase		(EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi);
734
735private:
736	virtual void		executeForContexts			(EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts);
737
738	glw::Functions		m_gl;
739};
740
741class RenderTestThread;
742
743typedef de::SharedPtr<RenderTestThread>	RenderTestThreadSp;
744typedef de::SharedPtr<de::Semaphore>	SemaphoreSp;
745
746struct DrawOpPacket
747{
748	DrawOpPacket (void)
749		: drawOps	(DE_NULL)
750		, numOps	(0)
751	{
752	}
753
754	const DrawPrimitiveOp*	drawOps;
755	int						numOps;
756	SemaphoreSp				wait;
757	SemaphoreSp				signal;
758};
759
760class RenderTestThread : public de::Thread
761{
762public:
763	RenderTestThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const glw::Functions& gl, const Program& program, const std::vector<DrawOpPacket>& packets)
764		: m_egl		(egl)
765		, m_display	(display)
766		, m_surface	(surface)
767		, m_context	(context)
768		, m_api		(api)
769		, m_gl		(gl)
770		, m_program	(program)
771		, m_packets	(packets)
772	{
773	}
774
775	void run (void)
776	{
777		for (std::vector<DrawOpPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++)
778		{
779			// Wait until it is our turn.
780			packetIter->wait->decrement();
781
782			// Acquire context.
783			EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, m_surface, m_surface, m_context));
784
785			// Execute rendering.
786			for (int ndx = 0; ndx < packetIter->numOps; ndx++)
787				draw(m_gl, m_api, m_program, packetIter->drawOps[ndx]);
788
789			// Release context.
790			EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
791
792			// Signal completion.
793			packetIter->signal->increment();
794		}
795	}
796
797private:
798	const Library&						m_egl;
799	EGLDisplay							m_display;
800	EGLSurface							m_surface;
801	EGLContext							m_context;
802	EGLint								m_api;
803	const glw::Functions&				m_gl;
804	const Program&						m_program;
805	const std::vector<DrawOpPacket>&	m_packets;
806};
807
808MultiThreadRenderCase::MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
809	: MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
810{
811	m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
812}
813
814void MultiThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
815{
816	const Library&			egl					= m_eglTestCtx.getLibrary();
817	const int				width				= eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
818	const int				height				= eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
819	const int				numContexts			= (int)contexts.size();
820	const int				opsPerPacket		= 2;
821	const int				packetsPerThread	= 2;
822	const int				numThreads			= numContexts;
823	const int				numPackets			= numThreads * packetsPerThread;
824	const float				threshold			= 0.02f;
825
826	const tcu::PixelFormat	pixelFmt			= getPixelFormat(egl, display, config.config);
827	const int				depthBits			= eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
828	const int				stencilBits			= eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
829	const int				numSamples			= eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
830
831	TestLog&				log					= m_testCtx.getLog();
832
833	tcu::Surface			refFrame			(width, height);
834	tcu::Surface			frame				(width, height);
835
836	de::Random				rnd					(deStringHash(getName()) ^ deInt32Hash(numContexts));
837
838	// Resources that need cleanup
839	vector<ProgramSp>				programs	(numContexts);
840	vector<SemaphoreSp>				semaphores	(numPackets+1);
841	vector<DrawPrimitiveOp>			drawOps		(numPackets*opsPerPacket);
842	vector<vector<DrawOpPacket> >	packets		(numThreads);
843	vector<RenderTestThreadSp>		threads		(numThreads);
844
845	// Log basic information about config.
846	log << TestLog::Message << "EGL_RED_SIZE = "		<< pixelFmt.redBits << TestLog::EndMessage;
847	log << TestLog::Message << "EGL_GREEN_SIZE = "		<< pixelFmt.greenBits << TestLog::EndMessage;
848	log << TestLog::Message << "EGL_BLUE_SIZE = "		<< pixelFmt.blueBits << TestLog::EndMessage;
849	log << TestLog::Message << "EGL_ALPHA_SIZE = "		<< pixelFmt.alphaBits << TestLog::EndMessage;
850	log << TestLog::Message << "EGL_DEPTH_SIZE = "		<< depthBits << TestLog::EndMessage;
851	log << TestLog::Message << "EGL_STENCIL_SIZE = "	<< stencilBits << TestLog::EndMessage;
852	log << TestLog::Message << "EGL_SAMPLES = "			<< numSamples << TestLog::EndMessage;
853
854	// Initialize semaphores.
855	for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
856		*sem = SemaphoreSp(new de::Semaphore(0));
857
858	// Create draw ops.
859	for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
860		randomizeDrawOp(rnd, *drawOp);
861
862	// Create packets.
863	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
864	{
865		packets[threadNdx].resize(packetsPerThread);
866
867		for (int packetNdx = 0; packetNdx < packetsPerThread; packetNdx++)
868		{
869			DrawOpPacket& packet = packets[threadNdx][packetNdx];
870
871			// Threads take turns with packets.
872			packet.wait		= semaphores[packetNdx*numThreads + threadNdx];
873			packet.signal	= semaphores[packetNdx*numThreads + threadNdx + 1];
874			packet.numOps	= opsPerPacket;
875			packet.drawOps	= &drawOps[(packetNdx*numThreads + threadNdx)*opsPerPacket];
876		}
877	}
878
879	// Create and setup programs per context
880	for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
881	{
882		EGLint		api			= contexts[ctxNdx].first;
883		EGLContext	context		= contexts[ctxNdx].second;
884
885		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
886
887		programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
888		programs[ctxNdx]->setup();
889
890		// Release context
891		EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
892	}
893
894	// Clear to black using first context.
895	{
896		EGLint		api			= contexts[0].first;
897		EGLContext	context		= contexts[0].second;
898
899		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
900
901		clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
902
903		// Release context
904		EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
905	}
906
907	// Create and launch threads (actual rendering starts once first semaphore is signaled).
908	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
909	{
910		threads[threadNdx] = RenderTestThreadSp(new RenderTestThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, m_gl, *programs[threadNdx], packets[threadNdx]));
911		threads[threadNdx]->start();
912	}
913
914	// Signal start and wait until complete.
915	semaphores.front()->increment();
916	semaphores.back()->decrement();
917
918	// Read pixels using first context. \todo [pyry] Randomize?
919	{
920		EGLint		api		= contexts[0].first;
921		EGLContext	context	= contexts[0].second;
922
923		EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
924
925		readPixels(m_gl, api, frame);
926	}
927
928	// Join threads.
929	for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
930		threads[threadNdx]->join();
931
932	// Render reference.
933	renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1);
934
935	// Compare images
936	{
937		bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
938
939		if (!imagesOk)
940			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
941	}
942}
943
944RenderTests::RenderTests (EglTestContext& eglTestCtx)
945	: TestCaseGroup(eglTestCtx, "render", "Basic rendering with different client APIs")
946{
947}
948
949RenderTests::~RenderTests (void)
950{
951}
952
953struct RenderGroupSpec
954{
955	const char*			name;
956	const char*			desc;
957	EGLint				apiBits;
958	eglu::ConfigFilter	baseFilter;
959	int					numContextsPerApi;
960};
961
962template <deUint32 Bits>
963static bool renderable (const eglu::CandidateConfig& c)
964{
965	return (c.renderableType() & Bits) == Bits;
966}
967
968template <class RenderClass>
969static void createRenderGroups (EglTestContext& eglTestCtx, tcu::TestCaseGroup* group, const RenderGroupSpec* first, const RenderGroupSpec* last)
970{
971	for (const RenderGroupSpec* groupIter = first; groupIter != last; groupIter++)
972	{
973		tcu::TestCaseGroup* configGroup = new tcu::TestCaseGroup(eglTestCtx.getTestContext(), groupIter->name, groupIter->desc);
974		group->addChild(configGroup);
975
976		vector<RenderFilterList>	filterLists;
977		eglu::FilterList			baseFilters;
978		baseFilters << groupIter->baseFilter;
979		getDefaultRenderFilterLists(filterLists, baseFilters);
980
981		for (vector<RenderFilterList>::const_iterator listIter = filterLists.begin(); listIter != filterLists.end(); listIter++)
982			configGroup->addChild(new RenderClass(eglTestCtx, listIter->getName(), "", groupIter->apiBits, listIter->getSurfaceTypeMask(), *listIter, groupIter->numContextsPerApi));
983	}
984}
985
986void RenderTests::init (void)
987{
988	static const RenderGroupSpec singleContextCases[] =
989	{
990		{
991			"gles2",
992			"Primitive rendering using GLES2",
993			EGL_OPENGL_ES2_BIT,
994			renderable<EGL_OPENGL_ES2_BIT>,
995			1
996		},
997		{
998			"gles3",
999			"Primitive rendering using GLES3",
1000			EGL_OPENGL_ES3_BIT,
1001			renderable<EGL_OPENGL_ES3_BIT>,
1002			1
1003		},
1004	};
1005
1006	static const RenderGroupSpec multiContextCases[] =
1007	{
1008		{
1009			"gles2",
1010			"Primitive rendering using multiple GLES2 contexts to shared surface",
1011			EGL_OPENGL_ES2_BIT,
1012			renderable<EGL_OPENGL_ES2_BIT>,
1013			3
1014		},
1015		{
1016			"gles3",
1017			"Primitive rendering using multiple GLES3 contexts to shared surface",
1018			EGL_OPENGL_ES3_BIT,
1019			renderable<EGL_OPENGL_ES3_BIT>,
1020			3
1021		},
1022		{
1023			"gles2_gles3",
1024			"Primitive rendering using multiple APIs to shared surface",
1025			EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT,
1026			renderable<EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT>,
1027			1
1028		},
1029	};
1030
1031	tcu::TestCaseGroup* singleContextGroup = new tcu::TestCaseGroup(m_testCtx, "single_context", "Single-context rendering");
1032	addChild(singleContextGroup);
1033	createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, singleContextGroup, &singleContextCases[0], &singleContextCases[DE_LENGTH_OF_ARRAY(singleContextCases)]);
1034
1035	tcu::TestCaseGroup* multiContextGroup = new tcu::TestCaseGroup(m_testCtx, "multi_context", "Multi-context rendering with shared surface");
1036	addChild(multiContextGroup);
1037	createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, multiContextGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);
1038
1039	tcu::TestCaseGroup* multiThreadGroup = new tcu::TestCaseGroup(m_testCtx, "multi_thread", "Multi-thread rendering with shared surface");
1040	addChild(multiThreadGroup);
1041	createRenderGroups<MultiThreadRenderCase>(m_eglTestCtx, multiThreadGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);
1042}
1043
1044} // egl
1045} // deqp
1046