1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL (ES) 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 Common object lifetime tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "glsLifetimeTests.hpp"
25
26#include "deString.h"
27#include "deRandom.hpp"
28#include "deSTLUtil.hpp"
29#include "deStringUtil.hpp"
30#include "tcuRGBA.hpp"
31#include "tcuImageCompare.hpp"
32#include "tcuRenderTarget.hpp"
33#include "tcuStringTemplate.hpp"
34#include "tcuTestLog.hpp"
35#include "gluDrawUtil.hpp"
36#include "gluObjectWrapper.hpp"
37#include "gluPixelTransfer.hpp"
38#include "gluShaderProgram.hpp"
39#include "gluDefs.hpp"
40#include "glwFunctions.hpp"
41
42#include <vector>
43#include <map>
44#include <algorithm>
45#include <sstream>
46
47namespace deqp
48{
49namespace gls
50{
51namespace LifetimeTests
52{
53namespace details
54{
55
56using std::map;
57using std::string;
58using std::ostringstream;
59using de::Random;
60using tcu::RenderTarget;
61using tcu::RGBA;
62using tcu::StringTemplate;
63using tcu::TestCase;
64typedef TestCase::IterateResult IterateResult;
65using tcu::TestLog;
66using tcu::ScopedLogSection;
67using glu::Program;
68using glu::Shader;
69using glu::Framebuffer;
70using glu::SHADERTYPE_VERTEX;
71using glu::SHADERTYPE_FRAGMENT;
72using namespace glw;
73
74enum { VIEWPORT_SIZE = 128, FRAMEBUFFER_SIZE = 128 };
75
76GLint getInteger (ContextWrapper& gl, GLenum queryParam)
77{
78	GLint ret = 0;
79	GLU_CHECK_CALL_ERROR(
80		gl.glGetIntegerv(queryParam, &ret),
81		gl.glGetError());
82	gl.log() << TestLog::Message << "// Single integer output: " << ret << TestLog::EndMessage;
83	return ret;
84}
85
86#define GLSL100_SRC(BODY) ("#version 100\n" #BODY "\n")
87
88static const char* const s_vertexShaderSrc = GLSL100_SRC(
89	attribute vec2 pos;
90	void main()
91	{
92		gl_Position = vec4(pos.xy, 0.0, 1.0);
93	}
94	);
95
96static const char* const s_fragmentShaderSrc = GLSL100_SRC(
97	void main()
98	{
99		gl_FragColor = vec4(1.0);
100	}
101	);
102
103class CheckedShader : public Shader
104{
105public:
106	CheckedShader (const RenderContext& renderCtx, glu::ShaderType type, const string& src)
107		: Shader (renderCtx, type)
108	{
109		const char* const srcStr = src.c_str();
110		setSources(1, &srcStr, DE_NULL);
111		compile();
112		TCU_CHECK(getCompileStatus());
113	}
114};
115
116class CheckedProgram : public Program
117{
118public:
119	CheckedProgram	(const RenderContext& renderCtx, GLuint vtxShader, GLuint fragShader)
120		: Program	(renderCtx)
121	{
122		attachShader(vtxShader);
123		attachShader(fragShader);
124		link();
125		TCU_CHECK(getLinkStatus());
126	}
127};
128
129ContextWrapper::ContextWrapper (const Context& ctx)
130	: CallLogWrapper	(ctx.gl(), ctx.log())
131	, m_ctx				(ctx)
132{
133	enableLogging(true);
134}
135
136void SimpleBinder::bind (GLuint name)
137{
138	(this->*m_bindFunc)(m_bindTarget, name);
139}
140
141GLuint SimpleBinder::getBinding (void)
142{
143	return getInteger(*this, m_bindingParam);
144}
145
146GLuint SimpleType::gen (void)
147{
148	GLuint ret;
149	(this->*m_genFunc)(1, &ret);
150	return ret;
151}
152
153class VertexArrayBinder : public SimpleBinder
154{
155public:
156						VertexArrayBinder	(Context& ctx)
157							: SimpleBinder	(ctx, 0, GL_NONE, GL_VERTEX_ARRAY_BINDING, true) {}
158	void				bind				(GLuint name) { glBindVertexArray(name); }
159};
160
161class QueryBinder : public Binder
162{
163public:
164						QueryBinder		(Context& ctx) : Binder(ctx) {}
165	void				bind			(GLuint name)
166	{
167		if (name != 0)
168			glBeginQuery(GL_ANY_SAMPLES_PASSED, name);
169		else
170			glEndQuery(GL_ANY_SAMPLES_PASSED);
171	}
172	GLuint				getBinding		(void) { return 0; }
173};
174
175bool ProgramType::isDeleteFlagged (GLuint name)
176{
177	GLint deleteFlagged = 0;
178	glGetProgramiv(name, GL_DELETE_STATUS, &deleteFlagged);
179	return deleteFlagged != 0;
180}
181
182bool ShaderType::isDeleteFlagged (GLuint name)
183{
184	GLint deleteFlagged = 0;
185	glGetShaderiv(name, GL_DELETE_STATUS, &deleteFlagged);
186	return deleteFlagged != 0;
187}
188
189void setupFbo (const Context& ctx, GLuint seed, GLuint fbo)
190{
191	const Functions& gl = ctx.getRenderContext().getFunctions();
192
193	GLU_CHECK_CALL_ERROR(gl.bindFramebuffer(GL_FRAMEBUFFER, fbo),
194						 gl.getError());
195
196	if (seed == 0)
197	{
198		gl.clearColor(0.0, 0.0, 0.0, 1.0);
199		GLU_CHECK_CALL_ERROR(gl.clear(GL_COLOR_BUFFER_BIT), gl.getError());
200	}
201	else
202	{
203		Random			rnd		(seed);
204		const GLsizei	width	= rnd.getInt(0, FRAMEBUFFER_SIZE);
205		const GLsizei	height	= rnd.getInt(0, FRAMEBUFFER_SIZE);
206		const GLint		x		= rnd.getInt(0, FRAMEBUFFER_SIZE - width);
207		const GLint		y		= rnd.getInt(0, FRAMEBUFFER_SIZE - height);
208		const GLfloat	r1		= rnd.getFloat();
209		const GLfloat	g1		= rnd.getFloat();
210		const GLfloat	b1		= rnd.getFloat();
211		const GLfloat	a1		= rnd.getFloat();
212		const GLfloat	r2		= rnd.getFloat();
213		const GLfloat	g2		= rnd.getFloat();
214		const GLfloat	b2		= rnd.getFloat();
215		const GLfloat	a2		= rnd.getFloat();
216
217		GLU_CHECK_CALL_ERROR(gl.clearColor(r1, g1, b1, a1), gl.getError());
218		GLU_CHECK_CALL_ERROR(gl.clear(GL_COLOR_BUFFER_BIT), gl.getError());
219		gl.scissor(x, y, width, height);
220		gl.enable(GL_SCISSOR_TEST);
221		gl.clearColor(r2, g2, b2, a2);
222		gl.clear(GL_COLOR_BUFFER_BIT);
223		gl.disable(GL_SCISSOR_TEST);
224	}
225
226	gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
227	GLU_CHECK_ERROR(gl.getError());
228}
229
230void drawFbo (const Context& ctx, GLuint fbo, Surface& dst)
231{
232	const RenderContext& renderCtx = ctx.getRenderContext();
233	const Functions& gl = renderCtx.getFunctions();
234
235	GLU_CHECK_CALL_ERROR(
236		gl.bindFramebuffer(GL_FRAMEBUFFER, fbo),
237		gl.getError());
238
239	dst.setSize(FRAMEBUFFER_SIZE, FRAMEBUFFER_SIZE);
240	glu::readPixels(renderCtx, 0, 0, dst.getAccess());
241	GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels from framebuffer");
242
243	GLU_CHECK_CALL_ERROR(
244		gl.bindFramebuffer(GL_FRAMEBUFFER, 0),
245		gl.getError());
246}
247
248GLuint getFboAttachment (const Functions& gl, GLuint fbo, GLenum requiredType)
249{
250	GLint type = 0, name = 0;
251	gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
252	GLU_CHECK_CALL_ERROR(
253		gl.getFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
254											   GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE,
255											   &type),
256		gl.getError());
257	GLU_CHECK_CALL_ERROR(
258		gl.getFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
259											   GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
260											   &name),
261		gl.getError());
262	gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
263	GLU_CHECK_ERROR(gl.getError());
264
265	GLuint ret = GLenum(type) == requiredType ? name : 0;
266	return ret;
267}
268
269void FboAttacher::initAttachment (GLuint seed, GLuint element)
270{
271	Binder& binder = *getElementType().binder();
272	Framebuffer fbo(getRenderContext());
273
274	enableLogging(false);
275
276	binder.enableLogging(false);
277	binder.bind(element);
278	initStorage();
279	binder.bind(0);
280	binder.enableLogging(true);
281
282	attach(element, *fbo);
283	setupFbo(getContext(), seed, *fbo);
284	detach(element, *fbo);
285
286	enableLogging(true);
287
288	log() << TestLog::Message
289		  << "// Drew to " << getElementType().getName() << " " << element
290		  << " with seed " << seed << "."
291		  << TestLog::EndMessage;
292}
293
294void FboInputAttacher::drawContainer (GLuint fbo, Surface& dst)
295{
296	drawFbo(getContext(), fbo, dst);
297	log() << TestLog::Message
298		  << "// Read pixels from framebuffer " << fbo << " to output image."
299		  << TestLog::EndMessage;
300}
301
302void FboOutputAttacher::setupContainer (GLuint seed, GLuint fbo)
303{
304	setupFbo(getContext(), seed, fbo);
305	log() << TestLog::Message
306		  << "// Drew to framebuffer " << fbo << " with seed " << seed << "."
307		  << TestLog::EndMessage;
308}
309
310void FboOutputAttacher::drawAttachment (GLuint element, Surface& dst)
311{
312	Framebuffer fbo(getRenderContext());
313	m_attacher.enableLogging(false);
314	m_attacher.attach(element, *fbo);
315	drawFbo(getContext(), *fbo, dst);
316	m_attacher.detach(element, *fbo);
317	m_attacher.enableLogging(true);
318	log() << TestLog::Message
319		  << "// Read pixels from " << m_attacher.getElementType().getName() << " " << element
320		  << " to output image."
321		  << TestLog::EndMessage;
322	GLU_CHECK_ERROR(gl().getError());
323}
324
325void TextureFboAttacher::attach (GLuint texture, GLuint fbo)
326{
327	GLU_CHECK_CALL_ERROR(
328		glBindFramebuffer(GL_FRAMEBUFFER, fbo),
329		gl().getError());
330	GLU_CHECK_CALL_ERROR(
331		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
332								  GL_TEXTURE_2D, texture, 0),
333		gl().getError());
334	GLU_CHECK_CALL_ERROR(
335		glBindFramebuffer(GL_FRAMEBUFFER, 0),
336		gl().getError());
337}
338
339void TextureFboAttacher::detach (GLuint texture, GLuint fbo)
340{
341	DE_UNREF(texture);
342	GLU_CHECK_CALL_ERROR(
343		glBindFramebuffer(GL_FRAMEBUFFER, fbo),
344		gl().getError());
345	GLU_CHECK_CALL_ERROR(
346		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0),
347		gl().getError());
348	GLU_CHECK_CALL_ERROR(
349		glBindFramebuffer(GL_FRAMEBUFFER, 0),
350		gl().getError());
351}
352
353GLuint TextureFboAttacher::getAttachment (GLuint fbo)
354{
355	return getFboAttachment(gl(), fbo, GL_TEXTURE);
356}
357
358void TextureFboAttacher::initStorage (void)
359{
360	GLU_CHECK_CALL_ERROR(
361		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, FRAMEBUFFER_SIZE, FRAMEBUFFER_SIZE, 0,
362					 GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, DE_NULL),
363		gl().getError());
364}
365
366void RboFboAttacher::initStorage (void)
367{
368	GLU_CHECK_CALL_ERROR(
369		glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, FRAMEBUFFER_SIZE, FRAMEBUFFER_SIZE),
370		gl().getError());
371}
372
373void RboFboAttacher::attach (GLuint rbo, GLuint fbo)
374{
375	GLU_CHECK_CALL_ERROR(
376		glBindFramebuffer(GL_FRAMEBUFFER, fbo),
377		gl().getError());
378	GLU_CHECK_CALL_ERROR(
379		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo),
380		gl().getError());
381	GLU_CHECK_CALL_ERROR(
382		glBindFramebuffer(GL_FRAMEBUFFER, 0),
383		gl().getError());
384}
385
386void RboFboAttacher::detach (GLuint rbo, GLuint fbo)
387{
388	DE_UNREF(rbo);
389	GLU_CHECK_CALL_ERROR(
390		glBindFramebuffer(GL_FRAMEBUFFER, fbo),
391		gl().getError());
392	GLU_CHECK_CALL_ERROR(
393		glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0),
394		gl().getError());
395	GLU_CHECK_CALL_ERROR(
396		glBindFramebuffer(GL_FRAMEBUFFER, 0),
397		gl().getError());
398}
399
400GLuint RboFboAttacher::getAttachment (GLuint fbo)
401{
402	return getFboAttachment(gl(), fbo, GL_RENDERBUFFER);
403}
404
405static const char* const s_fragmentShaderTemplate = GLSL100_SRC(
406	void main()
407	{
408		gl_FragColor = vec4(${RED}, ${GREEN}, ${BLUE}, 1.0);
409	}
410	);
411
412void ShaderProgramAttacher::initAttachment (GLuint seed, GLuint shader)
413{
414	using					de::insert;
415	using					de::floatToString;
416
417	Random					rnd(seed);
418	map<string, string>		params;
419	const StringTemplate	sourceTmpl	(s_fragmentShaderTemplate);
420
421	insert(params, "RED",	floatToString(rnd.getFloat(), 4));
422	insert(params, "GREEN",	floatToString(rnd.getFloat(), 4));
423	insert(params, "BLUE",	floatToString(rnd.getFloat(), 4));
424
425	{
426		const string			source		= sourceTmpl.specialize(params);
427		const char* const		sourceStr	= source.c_str();
428
429		GLU_CHECK_CALL_ERROR(glShaderSource(shader, 1, &sourceStr, DE_NULL), gl().getError());
430		GLU_CHECK_CALL_ERROR(glCompileShader(shader), gl().getError());
431
432		{
433			GLint compileStatus = 0;
434			gl().getShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
435			TCU_CHECK_MSG(compileStatus != 0, sourceStr);
436		}
437	}
438}
439
440void ShaderProgramAttacher::attach (GLuint shader, GLuint program)
441{
442	GLU_CHECK_CALL_ERROR(
443		glAttachShader(program, shader),
444		gl().getError());
445}
446
447void ShaderProgramAttacher::detach (GLuint shader, GLuint program)
448{
449	GLU_CHECK_CALL_ERROR(
450		glDetachShader(program, shader),
451		gl().getError());
452}
453
454GLuint ShaderProgramAttacher::getAttachment (GLuint program)
455{
456	GLuint			shaders[2]	= { 0, 0 };
457	const GLsizei	shadersLen	= DE_LENGTH_OF_ARRAY(shaders);
458	GLsizei			numShaders	= 0;
459	GLuint			ret			= 0;
460
461	gl().getAttachedShaders(program, shadersLen, &numShaders, shaders);
462
463	// There should ever be at most one attached shader in normal use, but if
464	// something is wrong, the temporary vertex shader might not have been
465	// detached properly, so let's find the fragment shader explicitly.
466	for (int ndx = 0; ndx < de::min<GLsizei>(shadersLen, numShaders); ++ndx)
467	{
468		GLint shaderType = GL_NONE;
469		gl().getShaderiv(shaders[ndx], GL_SHADER_TYPE, &shaderType);
470
471		if (shaderType == GL_FRAGMENT_SHADER)
472		{
473			ret = shaders[ndx];
474			break;
475		}
476	}
477
478	return ret;
479}
480
481void setViewport (const RenderContext& renderCtx, const Rectangle& rect)
482{
483	renderCtx.getFunctions().viewport(rect.x, rect.y, rect.width, rect.height);
484}
485
486void readRectangle (const RenderContext& renderCtx, const Rectangle& rect, Surface& dst)
487{
488	dst.setSize(rect.width, rect.height);
489	glu::readPixels(renderCtx, rect.x, rect.y, dst.getAccess());
490}
491
492Rectangle randomViewport (const RenderContext& ctx, GLint maxWidth, GLint maxHeight,
493						  Random& rnd)
494{
495	const RenderTarget&	target	= ctx.getRenderTarget();
496	const GLint			width	= de::min(target.getWidth(), maxWidth);
497	const GLint			xOff	= rnd.getInt(0, width - maxWidth);
498	const GLint			height	= de::min(target.getHeight(), maxHeight);
499	const GLint			yOff	= rnd.getInt(0, height - maxHeight);
500
501	return Rectangle(xOff, yOff, width, height);
502}
503
504void ShaderProgramInputAttacher::drawContainer (GLuint program, Surface& dst)
505{
506	static const float	s_vertices[6]	= { -1.0, 0.0, 1.0, 1.0, 0.0, -1.0 };
507	Random				rnd				(program);
508	CheckedShader		vtxShader		(getRenderContext(),
509										 SHADERTYPE_VERTEX, s_vertexShaderSrc);
510	const Rectangle		viewport		= randomViewport(getRenderContext(),
511														 VIEWPORT_SIZE, VIEWPORT_SIZE, rnd);
512
513	gl().attachShader(program, vtxShader.getShader());
514	gl().linkProgram(program);
515
516	{
517		GLint linkStatus = 0;
518		gl().getProgramiv(program, GL_LINK_STATUS, &linkStatus);
519		TCU_CHECK(linkStatus != 0);
520	}
521
522	log() << TestLog::Message
523		  << "// Attached a temporary vertex shader and linked program " << program
524		  << TestLog::EndMessage;
525
526	setViewport(getRenderContext(), viewport);
527	log() << TestLog::Message << "// Positioned viewport randomly" << TestLog::EndMessage;
528
529	glUseProgram(program);
530	{
531		GLint posLoc = gl().getAttribLocation(program, "pos");
532		TCU_CHECK(posLoc >= 0);
533
534		gl().enableVertexAttribArray(posLoc);
535
536		gl().clearColor(0, 0, 0, 1);
537		gl().clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
538		gl().vertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 0, s_vertices);
539		gl().drawArrays(GL_TRIANGLES, 0, 3);
540
541		gl().disableVertexAttribArray(posLoc);
542		log () << TestLog::Message << "// Drew a fixed triangle" << TestLog::EndMessage;
543	}
544	glUseProgram(0);
545
546	readRectangle(getRenderContext(), viewport, dst);
547	log() << TestLog::Message << "// Copied viewport to output image" << TestLog::EndMessage;
548
549	gl().detachShader(program, vtxShader.getShader());
550	log() << TestLog::Message << "// Removed temporary vertex shader" << TestLog::EndMessage;
551}
552
553ES2Types::ES2Types (const Context& ctx)
554	: Types			(ctx)
555	, m_bufferBind	(ctx, &CallLogWrapper::glBindBuffer,
556					 GL_ARRAY_BUFFER, GL_ARRAY_BUFFER_BINDING)
557	, m_bufferType	(ctx, "buffer", &CallLogWrapper::glGenBuffers,
558					 &CallLogWrapper::glDeleteBuffers,
559					 &CallLogWrapper::glIsBuffer, &m_bufferBind)
560	, m_textureBind	(ctx, &CallLogWrapper::glBindTexture, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D)
561	, m_textureType	(ctx, "texture", &CallLogWrapper::glGenTextures,
562					 &CallLogWrapper::glDeleteTextures,
563					 &CallLogWrapper::glIsTexture, &m_textureBind)
564	, m_rboBind		(ctx, &CallLogWrapper::glBindRenderbuffer,
565					 GL_RENDERBUFFER, GL_RENDERBUFFER_BINDING)
566	, m_rboType		(ctx, "renderbuffer",
567					 &CallLogWrapper::glGenRenderbuffers,
568					 &CallLogWrapper::glDeleteRenderbuffers,
569					 &CallLogWrapper::glIsRenderbuffer, &m_rboBind)
570	, m_fboBind		(ctx, &CallLogWrapper::glBindFramebuffer,
571					 GL_FRAMEBUFFER, GL_FRAMEBUFFER_BINDING)
572	, m_fboType		(ctx, "framebuffer",
573					 &CallLogWrapper::glGenFramebuffers,
574					 &CallLogWrapper::glDeleteFramebuffers,
575					 &CallLogWrapper::glIsFramebuffer, &m_fboBind)
576	, m_shaderType	(ctx)
577	, m_programType	(ctx)
578	, m_texFboAtt	(ctx, m_textureType, m_fboType)
579	, m_texFboInAtt	(m_texFboAtt)
580	, m_texFboOutAtt(m_texFboAtt)
581	, m_rboFboAtt	(ctx, m_rboType, m_fboType)
582	, m_rboFboInAtt	(m_rboFboAtt)
583	, m_rboFboOutAtt(m_rboFboAtt)
584	, m_shaderAtt	(ctx, m_shaderType, m_programType)
585	, m_shaderInAtt	(m_shaderAtt)
586{
587	Type* const types[] =
588	{
589		&m_bufferType, &m_textureType, &m_rboType, &m_fboType, &m_shaderType, &m_programType
590	};
591	m_types.insert(m_types.end(), DE_ARRAY_BEGIN(types), DE_ARRAY_END(types));
592
593	m_attachers.push_back(&m_texFboAtt);
594	m_attachers.push_back(&m_rboFboAtt);
595	m_attachers.push_back(&m_shaderAtt);
596
597	m_inAttachers.push_back(&m_texFboInAtt);
598	m_inAttachers.push_back(&m_rboFboInAtt);
599	m_inAttachers.push_back(&m_shaderInAtt);
600
601	m_outAttachers.push_back(&m_texFboOutAtt);
602	m_outAttachers.push_back(&m_rboFboOutAtt);
603}
604
605class Name
606{
607public:
608				Name		(Type& type) : m_type(type), m_name(type.gen()) {}
609				Name		(Type& type, GLuint name) : m_type(type), m_name(name) {}
610				~Name		(void) { m_type.release(m_name); }
611	GLuint		operator*	(void) const { return m_name; }
612
613private:
614	Type&			m_type;
615	const GLuint	m_name;
616};
617
618class ResultCollector
619{
620public:
621					ResultCollector		(TestContext& testCtx);
622	bool			check				(bool cond, const char* msg);
623	void			fail				(const char* msg);
624	void			warn				(const char* msg);
625					~ResultCollector	(void);
626
627private:
628	void			addResult			(qpTestResult result, const char* msg);
629
630	TestContext&	m_testCtx;
631	TestLog&		m_log;
632	qpTestResult	m_result;
633	const char*		m_message;
634};
635
636ResultCollector::ResultCollector (TestContext& testCtx)
637	: m_testCtx		(testCtx)
638	, m_log			(testCtx.getLog())
639	, m_result		(QP_TEST_RESULT_PASS)
640	, m_message		("Pass")
641{
642}
643
644bool ResultCollector::check (bool cond, const char* msg)
645{
646	if (!cond)
647		fail(msg);
648	return cond;
649}
650
651void ResultCollector::addResult (qpTestResult result, const char* msg)
652{
653	m_log << TestLog::Message << "// Fail: " << msg << TestLog::EndMessage;
654	if (m_result == QP_TEST_RESULT_PASS)
655	{
656		m_result = result;
657		m_message = msg;
658	}
659	else
660	{
661		if (result == QP_TEST_RESULT_FAIL)
662			m_result = result;
663		m_message = "Multiple problems, see log for details";
664	}
665}
666
667void ResultCollector::fail (const char* msg)
668{
669	addResult(QP_TEST_RESULT_FAIL, msg);
670}
671
672void ResultCollector::warn (const char* msg)
673{
674	addResult(QP_TEST_RESULT_QUALITY_WARNING, msg);
675}
676
677ResultCollector::~ResultCollector (void)
678{
679	m_testCtx.setTestResult(m_result, m_message);
680}
681
682class TestBase : public TestCase, protected CallLogWrapper
683{
684protected:
685							TestBase			(const char*	name,
686												 const char*	description,
687												 const Context&	ctx);
688
689	// Copy ContextWrapper since MI (except for CallLogWrapper) is a no-no.
690	const Context&			getContext			(void) const { return m_ctx; }
691	const RenderContext&	getRenderContext	(void) const { return m_ctx.getRenderContext(); }
692	const Functions&		gl					(void) const { return m_ctx.gl(); }
693	TestLog&				log					(void) const { return m_ctx.log(); }
694	void					init				(void);
695
696	Context					m_ctx;
697	Random					m_rnd;
698};
699
700TestBase::TestBase (const char* name, const char* description, const Context& ctx)
701	: TestCase			(ctx.getTestContext(), name, description)
702	, CallLogWrapper	(ctx.gl(), ctx.log())
703	, m_ctx				(ctx)
704	, m_rnd				(deStringHash(name))
705{
706	enableLogging(true);
707}
708
709void TestBase::init (void)
710{
711	m_rnd = Random(deStringHash(getName()));
712}
713
714class LifeTest : public TestBase
715{
716public:
717	typedef void			(LifeTest::*TestFunction)	(void);
718
719							LifeTest					(const char*	name,
720														 const char*	description,
721														 Type&			type,
722														 TestFunction 	test)
723								: TestBase		(name, description, type.getContext())
724								, m_type		(type)
725								, m_test		(test) {}
726
727	IterateResult			iterate						(void);
728
729	void					testGen						(void);
730	void					testDelete					(void);
731	void					testBind					(void);
732	void					testDeleteBound				(void);
733	void					testBindNoGen				(void);
734	void					testDeleteUsed				(void);
735
736private:
737	Binder&					binder						(void) { return *m_type.binder(); }
738
739	Type&					m_type;
740	TestFunction			m_test;
741};
742
743IterateResult LifeTest::iterate (void)
744{
745	(this->*m_test)();
746	return STOP;
747}
748
749void LifeTest::testGen (void)
750{
751	ResultCollector	errors	(getTestContext());
752	Name			name	(m_type);
753
754	if (m_type.genCreates())
755		errors.check(m_type.exists(*name), "Gen* should have created an object, but didn't");
756	else
757		errors.check(!m_type.exists(*name), "Gen* should not have created an object, but did");
758}
759
760void LifeTest::testDelete (void)
761{
762	ResultCollector	errors	(getTestContext());
763	GLuint			name	= m_type.gen();
764
765	m_type.release(name);
766	errors.check(!m_type.exists(name), "Object still exists after deletion");
767}
768
769void LifeTest::testBind (void)
770{
771	ResultCollector	errors	(getTestContext());
772	Name			name	(m_type);
773
774	binder().bind(*name);
775	GLU_EXPECT_NO_ERROR(gl().getError(), "Bind failed");
776	errors.check(m_type.exists(*name), "Object does not exist after binding");
777	binder().bind(0);
778}
779
780void LifeTest::testDeleteBound (void)
781{
782	const GLuint	id		= m_type.gen();
783	ResultCollector	errors	(getTestContext());
784
785	binder().bind(id);
786	m_type.release(id);
787
788	if (m_type.nameLingers())
789	{
790		errors.check(gl().getError() == GL_NO_ERROR, "Deleting bound object failed");
791		errors.check(binder().getBinding() == id,
792					 "Deleting bound object did not retain binding");
793		errors.check(m_type.exists(id),
794					 "Deleting bound object made its name invalid");
795		errors.check(m_type.isDeleteFlagged(id),
796					 "Deleting bound object did not flag the object for deletion");
797		binder().bind(0);
798	}
799	else
800	{
801		errors.check(gl().getError() == GL_NO_ERROR, "Deleting bound object failed");
802		errors.check(binder().getBinding() == 0,
803					 "Deleting bound object did not remove binding");
804		errors.check(!m_type.exists(id),
805					 "Deleting bound object did not make its name invalid");
806		binder().bind(0);
807	}
808
809	errors.check(binder().getBinding() == 0, "Unbinding didn't remove binding");
810	errors.check(!m_type.exists(id), "Name is still valid after deleting and unbinding");
811}
812
813void LifeTest::testBindNoGen (void)
814{
815	ResultCollector	errors	(getTestContext());
816	const GLuint	id		= m_rnd.getUint32();
817
818	if (!errors.check(!m_type.exists(id), "Randomly chosen identifier already exists"))
819		return;
820
821	Name			name	(m_type, id);
822	binder().bind(*name);
823
824	if (binder().genRequired())
825	{
826		errors.check(glGetError() == GL_INVALID_OPERATION,
827					 "Did not fail when binding a name not generated by Gen* call");
828		errors.check(!m_type.exists(*name),
829					 "Bind* created an object for a name not generated by a Gen* call");
830	}
831	else
832	{
833		errors.check(glGetError() == GL_NO_ERROR,
834					 "Failed when binding a name not generated by Gen* call");
835		errors.check(m_type.exists(*name),
836					 "Object was not created by the Bind* call");
837	}
838}
839
840void LifeTest::testDeleteUsed (void)
841{
842	ResultCollector	errors(getTestContext());
843	GLuint			programId = 0;
844
845	{
846		CheckedShader	vtxShader	(getRenderContext(),
847									 SHADERTYPE_VERTEX, s_vertexShaderSrc);
848		CheckedShader	fragShader	(getRenderContext(),
849									 SHADERTYPE_FRAGMENT, s_fragmentShaderSrc);
850		CheckedProgram	program		(getRenderContext(),
851									 vtxShader.getShader(), fragShader.getShader());
852
853		programId = program.getProgram();
854
855		log() << TestLog::Message << "// Created and linked program " << programId
856			  << TestLog::EndMessage;
857		GLU_CHECK_CALL_ERROR(glUseProgram(programId), gl().getError());
858
859		log() << TestLog::Message << "// Deleted program " << programId
860			  << TestLog::EndMessage;
861	}
862	TCU_CHECK(glIsProgram(programId));
863	{
864		GLint deleteFlagged = 0;
865		glGetProgramiv(programId, GL_DELETE_STATUS, &deleteFlagged);
866		errors.check(deleteFlagged != 0, "Program object was not flagged as deleted");
867	}
868	GLU_CHECK_CALL_ERROR(glUseProgram(0), gl().getError());
869	errors.check(!gl().isProgram(programId),
870				 "Deleted program name still valid after being made non-current");
871}
872
873class AttachmentTest : public TestBase
874{
875public:
876	typedef void			(AttachmentTest::*TestFunction)	(void);
877							AttachmentTest					(const char*	name,
878															 const char*	description,
879															 Attacher&		attacher,
880															 TestFunction	test)
881								: TestBase		(name, description, attacher.getContext())
882								, m_attacher	(attacher)
883								, m_test		(test) {}
884	IterateResult			iterate							(void);
885
886	void					testDeletedNames				(void);
887	void					testDeletedBinding				(void);
888	void					testDeletedReattach				(void);
889
890private:
891	Attacher&				m_attacher;
892	const TestFunction		m_test;
893};
894
895IterateResult AttachmentTest::iterate (void)
896{
897	(this->*m_test)();
898	return STOP;
899}
900
901GLuint getAttachment (Attacher& attacher, GLuint container)
902{
903	const GLuint queriedAttachment = attacher.getAttachment(container);
904	attacher.log() << TestLog::Message
905				   << "// Result of query for " << attacher.getElementType().getName()
906				   << " attached to " << attacher.getContainerType().getName() << " "
907				   << container << ": " << queriedAttachment << "."
908				   << TestLog::EndMessage;
909	return queriedAttachment;
910}
911
912void AttachmentTest::testDeletedNames (void)
913{
914	Type&			elemType		= m_attacher.getElementType();
915	Type&			containerType	= m_attacher.getContainerType();
916	Name			container		(containerType);
917	ResultCollector	errors			(getTestContext());
918	GLuint			elementId		= 0;
919
920	{
921		Name element(elemType);
922		elementId = *element;
923		m_attacher.initAttachment(0, *element);
924		m_attacher.attach(*element, *container);
925		errors.check(getAttachment(m_attacher, *container) == elementId,
926					 "Attachment name not returned by query even before deletion.");
927	}
928
929	// "Such a container or other context may continue using the object, and
930	// may still contain state identifying its name as being currently bound"
931	//
932	// We here interpret "may" to mean that whenever the container has a
933	// deleted object attached to it, a query will return that object's former
934	// name.
935	errors.check(getAttachment(m_attacher, *container) == elementId,
936				 "Attachment name not returned by query after attachment was deleted.");
937
938	if (elemType.nameLingers())
939		errors.check(elemType.exists(elementId),
940					 "Attached object name no longer valid after deletion.");
941	else
942		errors.check(!elemType.exists(elementId),
943					 "Attached object name still valid after deletion.");
944
945	m_attacher.detach(elementId, *container);
946	errors.check(getAttachment(m_attacher, *container) == 0,
947				 "Attachment name returned by query even after detachment.");
948	errors.check(!elemType.exists(elementId),
949				 "Deleted attached object name still usable after detachment.");
950};
951
952class InputAttachmentTest : public TestBase
953{
954public:
955					InputAttachmentTest	(const char*	name,
956										 const char*	description,
957										 InputAttacher&	inputAttacher)
958						: TestBase			(name, description, inputAttacher.getContext())
959						, m_inputAttacher	(inputAttacher) {}
960
961	IterateResult	iterate				(void);
962
963private:
964	InputAttacher&	m_inputAttacher;
965};
966
967GLuint replaceName (Type& type, GLuint oldName, TestLog& log)
968{
969	const Binder* const	binder		= type.binder();
970	const bool			genRequired	= binder == DE_NULL || binder->genRequired();
971
972	if (genRequired)
973		return type.gen();
974
975	log << TestLog::Message
976		<< "// Type does not require Gen* for binding, reusing old id " << oldName << "."
977		<< TestLog::EndMessage;
978
979	return oldName;
980}
981
982IterateResult InputAttachmentTest::iterate (void)
983{
984	Attacher&		attacher		= m_inputAttacher.getAttacher();
985	Type&			containerType	= attacher.getContainerType();
986	Type&			elementType		= attacher.getElementType();
987	Name			container		(containerType);
988	GLuint			elementId		= 0;
989	const GLuint	refSeed			= m_rnd.getUint32();
990	const GLuint	newSeed			= m_rnd.getUint32();
991	ResultCollector	errors			(getTestContext());
992
993	Surface			refSurface;		// Surface from drawing with refSeed-seeded attachment
994	Surface			delSurface;		// Surface from drawing with deleted refSeed attachment
995	Surface			newSurface;		// Surface from drawing with newSeed-seeded attachment
996
997	log() << TestLog::Message
998		  << "Testing if writing to a newly created object modifies a deleted attachment"
999		  << TestLog::EndMessage;
1000
1001	{
1002		ScopedLogSection	section	(log(),
1003									 "Write to original", "Writing to an original attachment");
1004		const Name			element	(elementType);
1005
1006		elementId = *element;
1007		attacher.initAttachment(refSeed, elementId);
1008		attacher.attach(elementId, *container);
1009		m_inputAttacher.drawContainer(*container, refSurface);
1010		// element gets deleted here
1011		log() << TestLog::Message << "// Deleting attachment";
1012	}
1013	{
1014		ScopedLogSection section	(log(), "Write to new",
1015									 "Writing to a new attachment after deleting the original");
1016		const GLuint	newId		= replaceName(elementType, elementId, log());
1017		const Name		newElement	(elementType, newId);
1018
1019		attacher.initAttachment(newSeed, newId);
1020
1021		m_inputAttacher.drawContainer(*container, delSurface);
1022		attacher.detach(elementId, *container);
1023
1024		attacher.attach(newId, *container);
1025		m_inputAttacher.drawContainer(*container, newSurface);
1026		attacher.detach(newId, *container);
1027	}
1028	{
1029		const bool surfacesMatch = tcu::pixelThresholdCompare(
1030			log(), "Reading from deleted",
1031			"Comparison result from reading from a container with a deleted attachment "
1032			"before and after writing to a fresh object.",
1033			refSurface, delSurface, RGBA(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT);
1034
1035		errors.check(
1036			surfacesMatch,
1037			"Writing to a fresh object modified the container with a deleted attachment.");
1038
1039		if (!surfacesMatch)
1040			log() << TestLog::Image("New attachment",
1041									"Container state after attached to the fresh object",
1042									newSurface);
1043	}
1044
1045	return STOP;
1046}
1047
1048class OutputAttachmentTest : public TestBase
1049{
1050public:
1051						OutputAttachmentTest			(const char*		name,
1052														 const char*		description,
1053														 OutputAttacher&	outputAttacher)
1054							: TestBase			(name, description,
1055												 outputAttacher.getContext())
1056							, m_outputAttacher	(outputAttacher) {}
1057	IterateResult		iterate							(void);
1058
1059private:
1060	OutputAttacher&		m_outputAttacher;
1061};
1062
1063IterateResult OutputAttachmentTest::iterate (void)
1064{
1065	Attacher&		attacher		= m_outputAttacher.getAttacher();
1066	Type&			containerType	= attacher.getContainerType();
1067	Type&			elementType		= attacher.getElementType();
1068	Name			container		(containerType);
1069	GLuint			elementId		= 0;
1070	const GLuint	refSeed			= m_rnd.getUint32();
1071	const GLuint	newSeed			= m_rnd.getUint32();
1072	ResultCollector	errors			(getTestContext());
1073	Surface			refSurface;		// Surface drawn from attachment to refSeed container
1074	Surface			newSurface;		// Surface drawn from attachment to newSeed container
1075	Surface			delSurface;		// Like newSurface, after writing to a deleted attachment
1076
1077	log() << TestLog::Message
1078		  << "Testing if writing to a container with a deleted attachment "
1079		  << "modifies a newly created object"
1080		  << TestLog::EndMessage;
1081
1082	{
1083		ScopedLogSection	section	(log(), "Write to existing",
1084									 "Writing to a container with an existing attachment");
1085		const Name			element	(elementType);
1086
1087		elementId = *element;
1088		attacher.initAttachment(0, elementId);
1089		attacher.attach(elementId, *container);
1090
1091		// For reference purposes, make note of what refSeed looks like.
1092		m_outputAttacher.setupContainer(refSeed, *container);
1093		m_outputAttacher.drawAttachment(elementId, refSurface);
1094	}
1095	{
1096		ScopedLogSection	section		(log(), "Write to deleted",
1097										 "Writing to a container after deletion of attachment");
1098		const GLuint		newId		= replaceName(elementType, elementId, log());
1099		const Name			newElement	(elementType, newId);
1100
1101		log() << TestLog::Message
1102			  << "Creating a new object " << newId
1103			  << TestLog::EndMessage;
1104
1105		log() << TestLog::Message
1106			  << "Recording state of new object before writing to container"
1107			  << TestLog::EndMessage;
1108		attacher.initAttachment(newSeed, newId);
1109		m_outputAttacher.drawAttachment(newId, newSurface);
1110
1111		log() << TestLog::Message
1112			  << "Writing to container"
1113			  << TestLog::EndMessage;
1114
1115		// Now re-write refSeed to the container.
1116		m_outputAttacher.setupContainer(refSeed, *container);
1117		// Does it affect the newly created attachment object?
1118		m_outputAttacher.drawAttachment(newId, delSurface);
1119	}
1120	attacher.detach(elementId, *container);
1121
1122	const bool surfacesMatch = tcu::pixelThresholdCompare(
1123		log(), "Writing to deleted",
1124		"Comparison result from reading from a fresh object before and after "
1125		"writing to a container with a deleted attachment",
1126		newSurface, delSurface, RGBA(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT);
1127
1128	errors.check(surfacesMatch,
1129				 "Writing to container with deleted attachment modified a new object.");
1130
1131	if (!surfacesMatch)
1132		log() << TestLog::Image(
1133			"Original attachment",
1134			"Result of container modification on original attachment before deletion.",
1135			refSurface);
1136	return STOP;
1137};
1138
1139struct LifeTestSpec
1140{
1141	const char*				name;
1142	LifeTest::TestFunction	func;
1143	bool					needBind;
1144};
1145
1146MovePtr<TestCaseGroup> createLifeTestGroup (TestContext& testCtx,
1147											const LifeTestSpec& spec,
1148											const vector<Type*>& types)
1149{
1150	MovePtr<TestCaseGroup> group(new TestCaseGroup(testCtx, spec.name, spec.name));
1151
1152	for (vector<Type*>::const_iterator it = types.begin(); it != types.end(); ++it)
1153	{
1154		Type& type = **it;
1155		const char* name = type.getName();
1156		if (!spec.needBind || type.binder() != DE_NULL)
1157			group->addChild(new LifeTest(name, name, type, spec.func));
1158	}
1159
1160	return group;
1161}
1162
1163static const LifeTestSpec s_lifeTests[] =
1164{
1165	{ "gen",			&LifeTest::testGen,			false	},
1166	{ "delete",			&LifeTest::testDelete,		false	},
1167	{ "bind",			&LifeTest::testBind,		true	},
1168	{ "delete_bound",	&LifeTest::testDeleteBound,	true	},
1169	{ "bind_no_gen",	&LifeTest::testBindNoGen,	true	},
1170};
1171
1172string attacherName (Attacher& attacher)
1173{
1174	ostringstream os;
1175	os << attacher.getElementType().getName() << "_" <<  attacher.getContainerType().getName();
1176	return os.str();
1177}
1178
1179void addTestCases (TestCaseGroup& group, Types& types)
1180{
1181	TestContext& testCtx = types.getTestContext();
1182
1183	for (const LifeTestSpec* it = DE_ARRAY_BEGIN(s_lifeTests);
1184		 it != DE_ARRAY_END(s_lifeTests); ++it)
1185		group.addChild(createLifeTestGroup(testCtx, *it, types.getTypes()).release());
1186
1187	{
1188		TestCaseGroup* const delUsedGroup =
1189			new TestCaseGroup(testCtx, "delete_used", "Delete current program");
1190		group.addChild(delUsedGroup);
1191
1192		delUsedGroup->addChild(
1193			new LifeTest("program", "program", types.getProgramType(),
1194						 &LifeTest::testDeleteUsed));
1195	}
1196
1197	{
1198		TestCaseGroup* const	attGroup	= new TestCaseGroup(
1199			testCtx, "attach", "Attachment tests");
1200		group.addChild(attGroup);
1201
1202		{
1203			TestCaseGroup* const	nameGroup	= new TestCaseGroup(
1204				testCtx, "deleted_name", "Name of deleted attachment");
1205			attGroup->addChild(nameGroup);
1206
1207			const vector<Attacher*>& atts = types.getAttachers();
1208			for (vector<Attacher*>::const_iterator it = atts.begin(); it != atts.end(); ++it)
1209			{
1210				const string name = attacherName(**it);
1211				nameGroup->addChild(new AttachmentTest(name.c_str(), name.c_str(), **it,
1212													   &AttachmentTest::testDeletedNames));
1213			}
1214		}
1215		{
1216			TestCaseGroup* inputGroup = new TestCaseGroup(
1217				testCtx, "deleted_input", "Input from deleted attachment");
1218			attGroup->addChild(inputGroup);
1219
1220			const vector<InputAttacher*>& inAtts = types.getInputAttachers();
1221			for (vector<InputAttacher*>::const_iterator it = inAtts.begin();
1222				 it != inAtts.end(); ++it)
1223			{
1224				const string name = attacherName((*it)->getAttacher());
1225				inputGroup->addChild(new InputAttachmentTest(name.c_str(), name.c_str(), **it));
1226			}
1227		}
1228		{
1229			TestCaseGroup* outputGroup = new TestCaseGroup(
1230				testCtx, "deleted_output", "Output to deleted attachment");
1231			attGroup->addChild(outputGroup);
1232
1233			const vector<OutputAttacher*>& outAtts = types.getOutputAttachers();
1234			for (vector<OutputAttacher*>::const_iterator it = outAtts.begin();
1235				 it != outAtts.end(); ++it)
1236			{
1237				string name = attacherName((*it)->getAttacher());
1238				outputGroup->addChild(new OutputAttachmentTest(name.c_str(), name.c_str(),
1239															   **it));
1240			}
1241		}
1242	}
1243}
1244
1245} // details
1246} // LifetimeTests
1247} // gls
1248} // deqp
1249