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