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