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