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