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 
getInteger(ContextWrapper & gl,GLenum queryParam)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:
CheckedShader(const RenderContext & renderCtx,glu::ShaderType type,const string & src)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:
CheckedProgram(const RenderContext & renderCtx,GLuint vtxShader,GLuint fragShader)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 
ContextWrapper(const Context & ctx)131 ContextWrapper::ContextWrapper (const Context& ctx)
132 	: CallLogWrapper	(ctx.gl(), ctx.log())
133 	, m_ctx				(ctx)
134 {
135 	enableLogging(true);
136 }
137 
bind(GLuint name)138 void SimpleBinder::bind (GLuint name)
139 {
140 	(this->*m_bindFunc)(m_bindTarget, name);
141 }
142 
getBinding(void)143 GLuint SimpleBinder::getBinding (void)
144 {
145 	return getInteger(*this, m_bindingParam);
146 }
147 
gen(void)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:
VertexArrayBinder(Context & ctx)158 						VertexArrayBinder	(Context& ctx)
159 							: SimpleBinder	(ctx, 0, GL_NONE, GL_VERTEX_ARRAY_BINDING, true) {}
bind(GLuint name)160 	void				bind				(GLuint name) { glBindVertexArray(name); }
161 };
162 
163 class QueryBinder : public Binder
164 {
165 public:
QueryBinder(Context & ctx)166 						QueryBinder		(Context& ctx) : Binder(ctx) {}
bind(GLuint name)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 	}
getBinding(void)174 	GLuint				getBinding		(void) { return 0; }
175 };
176 
isDeleteFlagged(GLuint name)177 bool ProgramType::isDeleteFlagged (GLuint name)
178 {
179 	GLint deleteFlagged = 0;
180 	glGetProgramiv(name, GL_DELETE_STATUS, &deleteFlagged);
181 	return deleteFlagged != 0;
182 }
183 
isDeleteFlagged(GLuint name)184 bool ShaderType::isDeleteFlagged (GLuint name)
185 {
186 	GLint deleteFlagged = 0;
187 	glGetShaderiv(name, GL_DELETE_STATUS, &deleteFlagged);
188 	return deleteFlagged != 0;
189 }
190 
setupFbo(const Context & ctx,GLuint seed,GLuint fbo)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 
drawFbo(const Context & ctx,GLuint fbo,Surface & dst)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 
getFboAttachment(const Functions & gl,GLuint fbo,GLenum requiredType)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 
initAttachment(GLuint seed,GLuint element)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 
drawContainer(GLuint fbo,Surface & dst)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 
setupContainer(GLuint seed,GLuint fbo)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 
drawAttachment(GLuint element,Surface & dst)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 
attach(GLuint texture,GLuint fbo)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 
detach(GLuint texture,GLuint fbo)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 
getAttachment(GLuint fbo)358 GLuint TextureFboAttacher::getAttachment (GLuint fbo)
359 {
360 	return getFboAttachment(gl(), fbo, GL_TEXTURE);
361 }
362 
isTextureFormatColorRenderable(const glu::RenderContext & renderCtx,const glu::TransferFormat & format)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 
getRenderableColorTextureFormat(const glu::RenderContext & renderCtx)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 
initStorage(void)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 
isRenderbufferFormatColorRenderable(const glu::RenderContext & renderCtx,const deUint32 format)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 
getRenderableColorRenderbufferFormat(const glu::RenderContext & renderCtx)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 
initStorage(void)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 
attach(GLuint rbo,GLuint fbo)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 
detach(GLuint rbo,GLuint fbo)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 
getAttachment(GLuint fbo)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 
initAttachment(GLuint seed,GLuint shader)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 
attach(GLuint shader,GLuint program)605 void ShaderProgramAttacher::attach (GLuint shader, GLuint program)
606 {
607 	GLU_CHECK_CALL_ERROR(
608 		glAttachShader(program, shader),
609 		gl().getError());
610 }
611 
detach(GLuint shader,GLuint program)612 void ShaderProgramAttacher::detach (GLuint shader, GLuint program)
613 {
614 	GLU_CHECK_CALL_ERROR(
615 		glDetachShader(program, shader),
616 		gl().getError());
617 }
618 
getAttachment(GLuint program)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 
setViewport(const RenderContext & renderCtx,const Rectangle & rect)646 void setViewport (const RenderContext& renderCtx, const Rectangle& rect)
647 {
648 	renderCtx.getFunctions().viewport(rect.x, rect.y, rect.width, rect.height);
649 }
650 
readRectangle(const RenderContext & renderCtx,const Rectangle & rect,Surface & dst)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 
randomViewport(const RenderContext & ctx,GLint maxWidth,GLint maxHeight,Random & rnd)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 
drawContainer(GLuint program,Surface & dst)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 
ES2Types(const Context & ctx)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:
Name(Type & type)773 				Name		(Type& type) : m_type(type), m_name(type.gen()) {}
Name(Type & type,GLuint name)774 				Name		(Type& type, GLuint name) : m_type(type), m_name(name) {}
~Name(void)775 				~Name		(void) { m_type.release(m_name); }
operator *(void) const776 	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 
ResultCollector(TestContext & testCtx)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 
check(bool cond,const char * msg)809 bool ResultCollector::check (bool cond, const char* msg)
810 {
811 	if (!cond)
812 		fail(msg);
813 	return cond;
814 }
815 
addResult(qpTestResult result,const char * msg)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 
fail(const char * msg)832 void ResultCollector::fail (const char* msg)
833 {
834 	addResult(QP_TEST_RESULT_FAIL, msg);
835 }
836 
warn(const char * msg)837 void ResultCollector::warn (const char* msg)
838 {
839 	addResult(QP_TEST_RESULT_QUALITY_WARNING, msg);
840 }
841 
~ResultCollector(void)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.
getContext(void) const855 	const Context&			getContext			(void) const { return m_ctx; }
getRenderContext(void) const856 	const RenderContext&	getRenderContext	(void) const { return m_ctx.getRenderContext(); }
gl(void) const857 	const Functions&		gl					(void) const { return m_ctx.gl(); }
log(void) const858 	TestLog&				log					(void) const { return m_ctx.log(); }
859 	void					init				(void);
860 
861 	Context					m_ctx;
862 	Random					m_rnd;
863 };
864 
TestBase(const char * name,const char * description,const Context & ctx)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 
init(void)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 
LifeTest(const char * name,const char * description,Type & type,TestFunction test)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:
binder(void)902 	Binder&					binder						(void) { return *m_type.binder(); }
903 
904 	Type&					m_type;
905 	TestFunction			m_test;
906 };
907 
iterate(void)908 IterateResult LifeTest::iterate (void)
909 {
910 	(this->*m_test)();
911 	return STOP;
912 }
913 
testGen(void)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 
testDelete(void)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 
testBind(void)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 
testDeleteBound(void)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 
testBindNoGen(void)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 
testDeleteUsed(void)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);
AttachmentTest(const char * name,const char * description,Attacher & attacher,TestFunction test)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 
iterate(void)1060 IterateResult AttachmentTest::iterate (void)
1061 {
1062 	(this->*m_test)();
1063 	return STOP;
1064 }
1065 
getAttachment(Attacher & attacher,GLuint container)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 
testDeletedNames(void)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:
InputAttachmentTest(const char * name,const char * description,InputAttacher & inputAttacher)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 
replaceName(Type & type,GLuint oldName,TestLog & log)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 
iterate(void)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:
OutputAttachmentTest(const char * name,const char * description,OutputAttacher & outputAttacher)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 
iterate(void)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 
createLifeTestGroup(TestContext & testCtx,const LifeTestSpec & spec,const vector<Type * > & types)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 
attacherName(Attacher & attacher)1337 string attacherName (Attacher& attacher)
1338 {
1339 	ostringstream os;
1340 	os << attacher.getElementType().getName() << "_" <<  attacher.getContainerType().getName();
1341 	return os.str();
1342 }
1343 
addTestCases(TestCaseGroup & group,Types & types)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