1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.0 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 Object lifetime tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es3fLifetimeTests.hpp"
25
26#include "deRandom.hpp"
27#include "deUniquePtr.hpp"
28#include "tcuRenderTarget.hpp"
29#include "tcuSurface.hpp"
30#include "gluDrawUtil.hpp"
31#include "gluObjectWrapper.hpp"
32#include "gluPixelTransfer.hpp"
33#include "gluShaderProgram.hpp"
34#include "glsLifetimeTests.hpp"
35#include "glwEnums.hpp"
36#include "glwFunctions.hpp"
37
38#include <vector>
39
40namespace deqp
41{
42namespace gles3
43{
44namespace Functional
45{
46namespace
47{
48
49using std::vector;
50using de::MovePtr;
51using de::Random;
52using tcu::RenderTarget;
53using tcu::Surface;
54using tcu::TestContext;
55using tcu::TestLog;
56using glu::CallLogWrapper;
57using glu::RenderContext;
58using glu::ProgramSources;
59using glu::VertexArray;
60using glu::Buffer;
61namespace lt = gls::LifetimeTests;
62using namespace lt;
63using namespace glw;
64typedef TestCase::IterateResult IterateResult;
65
66enum { VIEWPORT_SIZE = 128 };
67
68class ScaleProgram : public glu::ShaderProgram
69{
70public:
71							ScaleProgram	(lt::Context& ctx);
72	void 					draw			(GLuint vao, GLfloat scale, bool tf, Surface* dst);
73	void					setPos			(GLuint buffer, GLuint vao);
74
75private:
76	ProgramSources			getSources 		(void);
77
78	const RenderContext&	m_renderCtx;
79	GLint					m_scaleLoc;
80	GLint					m_posLoc;
81};
82
83enum { NUM_COMPONENTS = 4, NUM_VERTICES = 3 };
84
85ScaleProgram::ScaleProgram (lt::Context& ctx)
86	: glu::ShaderProgram	(ctx.getRenderContext(), getSources())
87	, m_renderCtx			(ctx.getRenderContext())
88{
89	const Functions& gl = m_renderCtx.getFunctions();
90	TCU_CHECK(isOk());
91	m_scaleLoc = gl.getUniformLocation(getProgram(), "scale");
92	m_posLoc = gl.getAttribLocation(getProgram(), "pos");
93}
94
95#define GLSL(VERSION, BODY) ("#version " #VERSION "\n" #BODY "\n")
96
97static const char* const s_vertexShaderSrc = GLSL(
98	100,
99	attribute vec4 pos;
100	uniform float scale;
101	void main ()
102	{
103		gl_Position = vec4(scale * pos.xy, pos.zw);
104	}
105	);
106
107static const char* const s_fragmentShaderSrc = GLSL(
108	100,
109	void main ()
110	{
111		gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0);
112	}
113	);
114
115ProgramSources ScaleProgram::getSources (void)
116{
117	using namespace glu;
118	ProgramSources sources;
119	sources << VertexSource(s_vertexShaderSrc)
120			<< FragmentSource(s_fragmentShaderSrc)
121			<< TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS)
122			<< TransformFeedbackVarying("gl_Position");
123	return sources;
124}
125
126void ScaleProgram::draw (GLuint vao, GLfloat scale, bool tf, Surface* dst)
127{
128	const Functions&	gl			= m_renderCtx.getFunctions();
129	de::Random			rnd			(vao);
130	Rectangle			viewport	= randomViewport(m_renderCtx,
131													 VIEWPORT_SIZE, VIEWPORT_SIZE, rnd);
132	setViewport(m_renderCtx, viewport);
133	gl.clearColor(0, 0, 0, 1);
134	gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
135
136	gl.bindVertexArray(vao);
137	gl.enableVertexAttribArray(m_posLoc);
138	GLU_CHECK_CALL_ERROR(gl.useProgram(getProgram()),
139						 gl.getError());
140
141	gl.uniform1f(m_scaleLoc, scale);
142
143	if (tf)
144		gl.beginTransformFeedback(GL_TRIANGLES);
145	GLU_CHECK_CALL_ERROR(gl.drawArrays(GL_TRIANGLES, 0, 3), gl.getError());
146	if (tf)
147		gl.endTransformFeedback();
148
149	if (dst != DE_NULL)
150		readRectangle(m_renderCtx, viewport, *dst);
151
152	gl.bindVertexArray(0);
153}
154
155void ScaleProgram::setPos (GLuint buffer, GLuint vao)
156{
157	const Functions& gl = m_renderCtx.getFunctions();
158
159	gl.bindBuffer(GL_ARRAY_BUFFER, buffer);
160	gl.bindVertexArray(vao);
161	GLU_CHECK_CALL_ERROR(
162		gl.vertexAttribPointer(m_posLoc, NUM_COMPONENTS, GL_FLOAT, false, 0, DE_NULL),
163		gl.getError());
164	gl.bindVertexArray(0);
165	gl.bindBuffer(GL_ARRAY_BUFFER, 0);
166	GLU_CHECK_ERROR(gl.getError());
167}
168
169class VertexArrayBinder : public SimpleBinder
170{
171public:
172						VertexArrayBinder	(lt::Context& ctx)
173							: SimpleBinder	(ctx, 0, GL_NONE, GL_VERTEX_ARRAY_BINDING, true) {}
174	void				bind			(GLuint name) { glBindVertexArray(name); }
175};
176
177class SamplerBinder : public Binder
178{
179public:
180						SamplerBinder	(lt::Context& ctx) : Binder(ctx) {}
181	void				bind			(GLuint name) { glBindSampler(0, name); }
182	GLuint				getBinding		(void)
183	{
184		GLint arr[32] = {};
185		glGetIntegerv(GL_SAMPLER_BINDING, arr);
186		log() << TestLog::Message << "// First output integer: " << arr[0]
187			  << TestLog::EndMessage;
188		return arr[0];
189	}
190	bool				genRequired		(void) const { return true; }
191};
192
193class QueryBinder : public Binder
194{
195public:
196						QueryBinder		(lt::Context& ctx) : Binder(ctx) {}
197	void				bind			(GLuint name)
198	{
199		if (name != 0)
200			glBeginQuery(GL_ANY_SAMPLES_PASSED, name);
201		else
202			glEndQuery(GL_ANY_SAMPLES_PASSED);
203	}
204	GLuint				getBinding		(void) { return 0; }
205};
206
207class BufferVAOAttacher : public Attacher
208{
209public:
210						BufferVAOAttacher	(lt::Context& ctx, Type& elementType,
211											 Type& varrType, ScaleProgram& program)
212							: Attacher		(ctx, elementType, varrType)
213							, m_program		(program) {}
214	void				initAttachment		(GLuint seed, GLuint element);
215	void				attach				(GLuint element, GLuint container);
216	void				detach				(GLuint element, GLuint container);
217	bool				canAttachDeleted	(void) const { return false; }
218	ScaleProgram&		getProgram			(void) { return m_program; }
219	GLuint				getAttachment		(GLuint container);
220
221private:
222	ScaleProgram&		m_program;
223};
224
225static const GLfloat s_varrData[NUM_VERTICES * NUM_COMPONENTS] =
226{
227	-1.0,  0.0, 0.0, 1.0,
228	 1.0,  1.0, 0.0, 1.0,
229	 0.0, -1.0, 0.0, 1.0
230};
231
232void initBuffer (const Functions& gl, GLuint seed, GLenum usage, GLuint buffer)
233{
234	gl.bindBuffer(GL_ARRAY_BUFFER, buffer);
235	if (seed == 0)
236		gl.bufferData(GL_ARRAY_BUFFER, sizeof(s_varrData), s_varrData, usage);
237	else
238	{
239		Random	rnd	(seed);
240		GLfloat data[DE_LENGTH_OF_ARRAY(s_varrData)];
241
242		for (int ndx = 0; ndx < NUM_VERTICES; ndx++)
243		{
244			GLfloat* vertex = &data[ndx * NUM_COMPONENTS];
245			vertex[0] = 2.0f * (rnd.getFloat() - 0.5f);
246			vertex[1] = 2.0f * (rnd.getFloat() - 0.5f);
247			DE_STATIC_ASSERT(NUM_COMPONENTS == 4);
248			vertex[2] = 0.0f;
249			vertex[3] = 1.0f;
250		}
251		gl.bufferData(GL_ARRAY_BUFFER, sizeof(data), data, usage);
252	}
253	gl.bindBuffer(GL_ARRAY_BUFFER, 0);
254	GLU_CHECK_ERROR(gl.getError());
255}
256
257void BufferVAOAttacher::initAttachment (GLuint seed, GLuint buffer)
258{
259	initBuffer(gl(), seed, GL_STATIC_DRAW, buffer);
260	log() << TestLog::Message << "// Initialized buffer " << buffer << " from seed " << seed
261		  << TestLog::EndMessage;
262}
263
264void BufferVAOAttacher::attach (GLuint buffer, GLuint vao)
265{
266	m_program.setPos(buffer, vao);
267	log() << TestLog::Message
268		  << "// Set the `pos` attribute in VAO " << vao << " to buffer " << buffer
269		  << TestLog::EndMessage;
270}
271
272void BufferVAOAttacher::detach (GLuint buffer, GLuint varr)
273{
274	DE_UNREF(buffer);
275	attach(0, varr);
276}
277
278GLuint BufferVAOAttacher::getAttachment (GLuint varr)
279{
280	GLint name = 0;
281	gl().bindVertexArray(varr);
282	gl().getVertexAttribiv(0, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &name);
283	gl().bindVertexArray(0);
284	GLU_CHECK_ERROR(gl().getError());
285	return GLuint(name);
286}
287
288class BufferVAOInputAttacher : public InputAttacher
289{
290public:
291						BufferVAOInputAttacher	(BufferVAOAttacher& attacher)
292							: InputAttacher		(attacher)
293							, m_program			(attacher.getProgram()) {}
294	void				drawContainer			(GLuint container, Surface& dst);
295
296private:
297	ScaleProgram&		m_program;
298};
299
300void BufferVAOInputAttacher::drawContainer (GLuint vao, Surface& dst)
301{
302	m_program.draw(vao, 1.0, false, &dst);
303	log() << TestLog::Message << "// Drew an output image with VAO " << vao
304		  << TestLog::EndMessage;
305};
306
307class BufferTfAttacher : public Attacher
308{
309public:
310				BufferTfAttacher	(lt::Context& ctx, Type& bufferType, Type& tfType)
311					: Attacher		(ctx, bufferType, tfType) {}
312	void		initAttachment		(GLuint seed, GLuint element);
313	void		attach				(GLuint buffer, GLuint tf);
314	void		detach				(GLuint buffer, GLuint tf);
315	bool		canAttachDeleted	(void) const { return false; }
316	GLuint		getAttachment		(GLuint tf);
317};
318
319void BufferTfAttacher::initAttachment (GLuint seed, GLuint buffer)
320{
321	initBuffer(gl(), seed, GL_DYNAMIC_READ, buffer);
322	log() << TestLog::Message << "// Initialized buffer " << buffer << " from seed " << seed
323		  << TestLog::EndMessage;
324}
325
326void BufferTfAttacher::attach (GLuint buffer, GLuint tf)
327{
328	glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf);
329	glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buffer);
330	glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
331	GLU_CHECK_ERROR(gl().getError());
332}
333
334void BufferTfAttacher::detach (GLuint buffer, GLuint tf)
335{
336	DE_UNREF(buffer);
337	glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf);
338	glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
339	glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
340	GLU_CHECK_ERROR(gl().getError());
341}
342
343GLuint BufferTfAttacher::getAttachment (GLuint tf)
344{
345	GLint ret = 0;
346	gl().bindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf);
347	gl().getIntegeri_v(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, 0, &ret);
348	gl().bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
349	GLU_CHECK_ERROR(gl().getError());
350	return GLuint(ret);
351}
352
353class BufferTfOutputAttacher : public OutputAttacher
354{
355public:
356				BufferTfOutputAttacher	(BufferTfAttacher&	attacher, ScaleProgram& program)
357					: OutputAttacher	(attacher)
358					, m_program			(program) {}
359	void		setupContainer		(GLuint seed, GLuint container);
360	void		drawAttachment		(GLuint attachment, Surface& dst);
361
362private:
363	ScaleProgram&	m_program;
364};
365
366void BufferTfOutputAttacher::drawAttachment (GLuint buffer, Surface& dst)
367{
368	VertexArray vao(getRenderContext());
369
370	m_program.setPos(buffer, *vao);
371	m_program.draw(*vao, 1.0, false, &dst);
372	log() << TestLog::Message
373		  << "// Drew output image with vertices from buffer " << buffer
374		  << TestLog::EndMessage;
375	GLU_CHECK_ERROR(gl().getError());
376}
377
378void BufferTfOutputAttacher::setupContainer (GLuint seed, GLuint tf)
379{
380	Buffer		posBuf	(getRenderContext());
381	VertexArray	vao		(getRenderContext());
382
383	initBuffer(gl(), seed, GL_STATIC_DRAW, *posBuf);
384	m_program.setPos(*posBuf, *vao);
385
386	glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf);
387	m_program.draw(*vao, -1.0, true, DE_NULL);
388	log() << TestLog::Message
389		  << "// Drew an image with seed " << seed << " with transform feedback to " << tf
390		  << TestLog::EndMessage;
391	glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
392	GLU_CHECK_ERROR(gl().getError());
393}
394
395class ES3Types : public ES2Types
396{
397public:
398							ES3Types		(lt::Context& ctx);
399private:
400	ScaleProgram			m_program;
401	QueryBinder				m_queryBind;
402	SimpleType				m_queryType;
403	SimpleBinder			m_tfBind;
404	SimpleType				m_tfType;
405	VertexArrayBinder		m_varrBind;
406	SimpleType				m_varrType;
407	SamplerBinder			m_samplerBind;
408	SimpleType				m_samplerType;
409	BufferVAOAttacher		m_bufVarrAtt;
410	BufferVAOInputAttacher	m_bufVarrInAtt;
411	BufferTfAttacher		m_bufTfAtt;
412	BufferTfOutputAttacher	m_bufTfOutAtt;
413};
414
415ES3Types::ES3Types (lt::Context& ctx)
416	: ES2Types		(ctx)
417	, m_program		(ctx)
418	, m_queryBind	(ctx)
419	, m_queryType	(ctx, "query", &CallLogWrapper::glGenQueries,
420					 &CallLogWrapper::glDeleteQueries,
421					 &CallLogWrapper::glIsQuery, &m_queryBind)
422	, m_tfBind		(ctx, &CallLogWrapper::glBindTransformFeedback, GL_TRANSFORM_FEEDBACK,
423					 GL_TRANSFORM_FEEDBACK_BINDING, true)
424	, m_tfType		(ctx, "transform_feedback", &CallLogWrapper::glGenTransformFeedbacks,
425					 &CallLogWrapper::glDeleteTransformFeedbacks,
426					 &CallLogWrapper::glIsTransformFeedback, &m_tfBind)
427	, m_varrBind	(ctx)
428	, m_varrType	(ctx, "vertex_array", &CallLogWrapper::glGenVertexArrays,
429					 &CallLogWrapper::glDeleteVertexArrays,
430					 &CallLogWrapper::glIsVertexArray, &m_varrBind)
431	, m_samplerBind	(ctx)
432	, m_samplerType	(ctx, "sampler", &CallLogWrapper::glGenSamplers,
433					 &CallLogWrapper::glDeleteSamplers,
434					 &CallLogWrapper::glIsSampler, &m_samplerBind, true)
435	, m_bufVarrAtt	(ctx, m_bufferType, m_varrType, m_program)
436	, m_bufVarrInAtt(m_bufVarrAtt)
437	, m_bufTfAtt	(ctx, m_bufferType, m_tfType)
438	, m_bufTfOutAtt	(m_bufTfAtt, m_program)
439{
440	Type* types[] = { &m_queryType, &m_tfType, &m_varrType, &m_samplerType };
441	m_types.insert(m_types.end(), DE_ARRAY_BEGIN(types), DE_ARRAY_END(types));
442
443	m_attachers.push_back(&m_bufVarrAtt);
444	m_attachers.push_back(&m_bufTfAtt);
445
446	m_inAttachers.push_back(&m_bufVarrInAtt);
447	m_outAttachers.push_back(&m_bufTfOutAtt);
448}
449
450class TfDeleteActiveTest : public TestCase, private CallLogWrapper
451{
452	public:
453						TfDeleteActiveTest	(gles3::Context& context,
454											 const char* name, const char* description);
455	IterateResult		iterate				(void);
456};
457
458TfDeleteActiveTest::TfDeleteActiveTest (gles3::Context& context,
459										const char* name, const char* description)
460	: TestCase			(context, name, description)
461	, CallLogWrapper	(context.getRenderContext().getFunctions(),
462						 context.getTestContext().getLog())
463{
464	enableLogging(true);
465}
466
467class ScopedTransformFeedbackFeedback
468{
469public:
470							ScopedTransformFeedbackFeedback		(glu::CallLogWrapper& gl, GLenum type);
471							~ScopedTransformFeedbackFeedback	(void);
472
473private:
474	glu::CallLogWrapper&	m_gl;
475};
476
477ScopedTransformFeedbackFeedback::ScopedTransformFeedbackFeedback (glu::CallLogWrapper& gl, GLenum type)
478	: m_gl(gl)
479{
480	m_gl.glBeginTransformFeedback(type);
481	GLU_EXPECT_NO_ERROR(m_gl.glGetError(), "glBeginTransformFeedback");
482}
483
484ScopedTransformFeedbackFeedback::~ScopedTransformFeedbackFeedback (void)
485{
486	m_gl.glEndTransformFeedback();
487}
488
489IterateResult TfDeleteActiveTest::iterate (void)
490{
491	static const char* const s_xfbVertexSource =	"#version 300 es\n"
492													"void main ()\n"
493													"{\n"
494													"	gl_Position = vec4(float(gl_VertexID) / 2.0, float(gl_VertexID % 2) / 2.0, 0.0, 1.0);\n"
495													"}\n";
496	static const char* const s_xfbFragmentSource =	"#version 300 es\n"
497													"layout(location=0) out mediump vec4 dEQP_FragColor;\n"
498													"void main ()\n"
499													"{\n"
500													"	dEQP_FragColor = vec4(1.0, 1.0, 0.0, 1.0);\n"
501													"}\n";
502
503	glu::Buffer			buf			(m_context.getRenderContext());
504	GLuint				tf			= 0;
505	glu::ShaderProgram	program		(m_context.getRenderContext(),
506									 glu::ProgramSources()
507										<< glu::VertexSource(s_xfbVertexSource)
508										<< glu::FragmentSource(s_xfbFragmentSource)
509										<< glu::TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS)
510										<< glu::TransformFeedbackVarying("gl_Position"));
511
512	if (!program.isOk())
513	{
514		m_testCtx.getLog() << program;
515		throw tcu::TestError("failed to build program");
516	}
517
518	try
519	{
520		GLU_CHECK_CALL(glUseProgram(program.getProgram()));
521		GLU_CHECK_CALL(glGenTransformFeedbacks(1, &tf));
522		GLU_CHECK_CALL(glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, tf));
523		GLU_CHECK_CALL(glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, *buf));
524		GLU_CHECK_CALL(glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 3 * sizeof(glw::GLfloat[4]), DE_NULL, GL_DYNAMIC_COPY));
525
526		{
527			ScopedTransformFeedbackFeedback xfb(static_cast<glu::CallLogWrapper&>(*this), GL_TRIANGLES);
528
529			glDeleteTransformFeedbacks(1, &tf);
530			{
531				GLenum err = glGetError();
532				if (err != GL_INVALID_OPERATION)
533					getTestContext().setTestResult(
534						QP_TEST_RESULT_FAIL,
535						"Deleting active transform feedback did not produce GL_INVALID_OPERATION");
536				else
537					getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass");
538			}
539		}
540		GLU_CHECK(); // ScopedTransformFeedbackFeedback::dtor might modify error state
541
542		GLU_CHECK_CALL(glDeleteTransformFeedbacks(1, &tf));
543	}
544	catch (const glu::Error&)
545	{
546		glDeleteTransformFeedbacks(1, &tf);
547		throw;
548	}
549
550	return STOP;
551}
552
553class TestGroup : public TestCaseGroup
554{
555public:
556							TestGroup		(gles3::Context& context)
557								: TestCaseGroup	(context, "lifetime", "Object lifetime tests")
558							{}
559	void					init			(void);
560private:
561	MovePtr<Types>			m_types;
562};
563
564void TestGroup::init (void)
565{
566	gles3::Context&	ctx		= getContext();
567	lt::Context		ltCtx	(ctx.getRenderContext(), ctx.getTestContext());
568
569	m_types	= MovePtr<Types>(new ES3Types(ltCtx));
570
571	addTestCases(*this, *m_types);
572
573	TestCaseGroup* deleteActiveGroup =
574		new TestCaseGroup(ctx, "delete_active", "Delete active object");
575	addChild(deleteActiveGroup);
576	deleteActiveGroup->addChild(
577		new TfDeleteActiveTest(ctx, "transform_feedback", "Transform Feedback"));
578}
579
580} // anonymous
581
582TestCaseGroup* createLifetimeTests (Context& context)
583{
584	return new TestGroup(context);
585}
586
587} // Functional
588} // gles3
589} // deqp
590