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