Home | History | Annotate | Download | only in glshared
      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 
     49 namespace deqp
     50 {
     51 namespace gls
     52 {
     53 namespace LifetimeTests
     54 {
     55 namespace details
     56 {
     57 
     58 using std::map;
     59 using std::string;
     60 using std::ostringstream;
     61 using de::Random;
     62 using tcu::RenderTarget;
     63 using tcu::RGBA;
     64 using tcu::StringTemplate;
     65 using tcu::TestCase;
     66 typedef TestCase::IterateResult IterateResult;
     67 using tcu::TestLog;
     68 using tcu::ScopedLogSection;
     69 using glu::Program;
     70 using glu::Shader;
     71 using glu::Framebuffer;
     72 using glu::SHADERTYPE_VERTEX;
     73 using glu::SHADERTYPE_FRAGMENT;
     74 using namespace glw;
     75 
     76 enum { VIEWPORT_SIZE = 128, FRAMEBUFFER_SIZE = 128 };
     77 
     78 GLint 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 
     90 static 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 
     98 static const char* const s_fragmentShaderSrc = GLSL100_SRC(
     99 	void main()
    100 	{
    101 		gl_FragColor = vec4(1.0);
    102 	}
    103 	);
    104 
    105 class CheckedShader : public Shader
    106 {
    107 public:
    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 
    118 class CheckedProgram : public Program
    119 {
    120 public:
    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 
    131 ContextWrapper::ContextWrapper (const Context& ctx)
    132 	: CallLogWrapper	(ctx.gl(), ctx.log())
    133 	, m_ctx				(ctx)
    134 {
    135 	enableLogging(true);
    136 }
    137 
    138 void SimpleBinder::bind (GLuint name)
    139 {
    140 	(this->*m_bindFunc)(m_bindTarget, name);
    141 }
    142 
    143 GLuint SimpleBinder::getBinding (void)
    144 {
    145 	return getInteger(*this, m_bindingParam);
    146 }
    147 
    148 GLuint SimpleType::gen (void)
    149 {
    150 	GLuint ret;
    151 	(this->*m_genFunc)(1, &ret);
    152 	return ret;
    153 }
    154 
    155 class VertexArrayBinder : public SimpleBinder
    156 {
    157 public:
    158 						VertexArrayBinder	(Context& ctx)
    159 							: SimpleBinder	(ctx, 0, GL_NONE, GL_VERTEX_ARRAY_BINDING, true) {}
    160 	void				bind				(GLuint name) { glBindVertexArray(name); }
    161 };
    162 
    163 class QueryBinder : public Binder
    164 {
    165 public:
    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 
    177 bool ProgramType::isDeleteFlagged (GLuint name)
    178 {
    179 	GLint deleteFlagged = 0;
    180 	glGetProgramiv(name, GL_DELETE_STATUS, &deleteFlagged);
    181 	return deleteFlagged != 0;
    182 }
    183 
    184 bool ShaderType::isDeleteFlagged (GLuint name)
    185 {
    186 	GLint deleteFlagged = 0;
    187 	glGetShaderiv(name, GL_DELETE_STATUS, &deleteFlagged);
    188 	return deleteFlagged != 0;
    189 }
    190 
    191 void 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 
    232 void 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 
    250 GLuint 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 
    274 void 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 
    299 void 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 
    307 void 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 
    315 void 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 
    330 void 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 
    344 void 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 
    358 GLuint TextureFboAttacher::getAttachment (GLuint fbo)
    359 {
    360 	return getFboAttachment(gl(), fbo, GL_TEXTURE);
    361 }
    362 
    363 static 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 
    415 static 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 
    439 void 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 
    452 static 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 
    503 static 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 
    526 void 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 
    538 void 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 
    551 void 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 
    565 GLuint RboFboAttacher::getAttachment (GLuint fbo)
    566 {
    567 	return getFboAttachment(gl(), fbo, GL_RENDERBUFFER);
    568 }
    569 
    570 static const char* const s_fragmentShaderTemplate = GLSL100_SRC(
    571 	void main()
    572 	{
    573 		gl_FragColor = vec4(${RED}, ${GREEN}, ${BLUE}, 1.0);
    574 	}
    575 	);
    576 
    577 void 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 
    605 void ShaderProgramAttacher::attach (GLuint shader, GLuint program)
    606 {
    607 	GLU_CHECK_CALL_ERROR(
    608 		glAttachShader(program, shader),
    609 		gl().getError());
    610 }
    611 
    612 void ShaderProgramAttacher::detach (GLuint shader, GLuint program)
    613 {
    614 	GLU_CHECK_CALL_ERROR(
    615 		glDetachShader(program, shader),
    616 		gl().getError());
    617 }
    618 
    619 GLuint 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 
    646 void setViewport (const RenderContext& renderCtx, const Rectangle& rect)
    647 {
    648 	renderCtx.getFunctions().viewport(rect.x, rect.y, rect.width, rect.height);
    649 }
    650 
    651 void 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 
    657 Rectangle 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 
    669 void 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 
    718 ES2Types::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 
    770 class Name
    771 {
    772 public:
    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 
    778 private:
    779 	Type&			m_type;
    780 	const GLuint	m_name;
    781 };
    782 
    783 class ResultCollector
    784 {
    785 public:
    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 
    792 private:
    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 
    801 ResultCollector::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 
    809 bool ResultCollector::check (bool cond, const char* msg)
    810 {
    811 	if (!cond)
    812 		fail(msg);
    813 	return cond;
    814 }
    815 
    816 void 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 
    832 void ResultCollector::fail (const char* msg)
    833 {
    834 	addResult(QP_TEST_RESULT_FAIL, msg);
    835 }
    836 
    837 void ResultCollector::warn (const char* msg)
    838 {
    839 	addResult(QP_TEST_RESULT_QUALITY_WARNING, msg);
    840 }
    841 
    842 ResultCollector::~ResultCollector (void)
    843 {
    844 	m_testCtx.setTestResult(m_result, m_message);
    845 }
    846 
    847 class TestBase : public TestCase, protected CallLogWrapper
    848 {
    849 protected:
    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 
    865 TestBase::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 
    874 void TestBase::init (void)
    875 {
    876 	m_rnd = Random(deStringHash(getName()));
    877 }
    878 
    879 class LifeTest : public TestBase
    880 {
    881 public:
    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 
    901 private:
    902 	Binder&					binder						(void) { return *m_type.binder(); }
    903 
    904 	Type&					m_type;
    905 	TestFunction			m_test;
    906 };
    907 
    908 IterateResult LifeTest::iterate (void)
    909 {
    910 	(this->*m_test)();
    911 	return STOP;
    912 }
    913 
    914 void 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 
    925 void 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 
    934 void 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 
    945 void 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 
    978 void 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 
   1005 void 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 
   1038 class AttachmentTest : public TestBase
   1039 {
   1040 public:
   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 
   1055 private:
   1056 	Attacher&				m_attacher;
   1057 	const TestFunction		m_test;
   1058 };
   1059 
   1060 IterateResult AttachmentTest::iterate (void)
   1061 {
   1062 	(this->*m_test)();
   1063 	return STOP;
   1064 }
   1065 
   1066 GLuint 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 
   1077 void 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 
   1117 class InputAttachmentTest : public TestBase
   1118 {
   1119 public:
   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 
   1128 private:
   1129 	InputAttacher&	m_inputAttacher;
   1130 };
   1131 
   1132 GLuint 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 
   1147 IterateResult 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 
   1213 class OutputAttachmentTest : public TestBase
   1214 {
   1215 public:
   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 
   1224 private:
   1225 	OutputAttacher&		m_outputAttacher;
   1226 };
   1227 
   1228 IterateResult 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 
   1304 struct LifeTestSpec
   1305 {
   1306 	const char*				name;
   1307 	LifeTest::TestFunction	func;
   1308 	bool					needBind;
   1309 };
   1310 
   1311 MovePtr<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 
   1328 static 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 
   1337 string attacherName (Attacher& attacher)
   1338 {
   1339 	ostringstream os;
   1340 	os << attacher.getElementType().getName() << "_" <<  attacher.getContainerType().getName();
   1341 	return os.str();
   1342 }
   1343 
   1344 void 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