Merge "Allow alternative form for refract()." into nyc-dev am: f492a25
[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
258         if (GLenum(type) != requiredType || GLenum(type) == GL_NONE)
259                 return 0;
260
261         GLU_CHECK_CALL_ERROR(
262                 gl.getFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
263                                                                                            GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
264                                                                                            &name),
265                 gl.getError());
266         gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
267         GLU_CHECK_ERROR(gl.getError());
268
269         return name;
270 }
271
272 void FboAttacher::initAttachment (GLuint seed, GLuint element)
273 {
274         Binder& binder = *getElementType().binder();
275         Framebuffer fbo(getRenderContext());
276
277         enableLogging(false);
278
279         binder.enableLogging(false);
280         binder.bind(element);
281         initStorage();
282         binder.bind(0);
283         binder.enableLogging(true);
284
285         attach(element, *fbo);
286         setupFbo(getContext(), seed, *fbo);
287         detach(element, *fbo);
288
289         enableLogging(true);
290
291         log() << TestLog::Message
292                   << "// Drew to " << getElementType().getName() << " " << element
293                   << " with seed " << seed << "."
294                   << TestLog::EndMessage;
295 }
296
297 void FboInputAttacher::drawContainer (GLuint fbo, Surface& dst)
298 {
299         drawFbo(getContext(), fbo, dst);
300         log() << TestLog::Message
301                   << "// Read pixels from framebuffer " << fbo << " to output image."
302                   << TestLog::EndMessage;
303 }
304
305 void FboOutputAttacher::setupContainer (GLuint seed, GLuint fbo)
306 {
307         setupFbo(getContext(), seed, fbo);
308         log() << TestLog::Message
309                   << "// Drew to framebuffer " << fbo << " with seed " << seed << "."
310                   << TestLog::EndMessage;
311 }
312
313 void FboOutputAttacher::drawAttachment (GLuint element, Surface& dst)
314 {
315         Framebuffer fbo(getRenderContext());
316         m_attacher.enableLogging(false);
317         m_attacher.attach(element, *fbo);
318         drawFbo(getContext(), *fbo, dst);
319         m_attacher.detach(element, *fbo);
320         m_attacher.enableLogging(true);
321         log() << TestLog::Message
322                   << "// Read pixels from " << m_attacher.getElementType().getName() << " " << element
323                   << " to output image."
324                   << TestLog::EndMessage;
325         GLU_CHECK_ERROR(gl().getError());
326 }
327
328 void TextureFboAttacher::attach (GLuint texture, GLuint fbo)
329 {
330         GLU_CHECK_CALL_ERROR(
331                 glBindFramebuffer(GL_FRAMEBUFFER, fbo),
332                 gl().getError());
333         GLU_CHECK_CALL_ERROR(
334                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
335                                                                   GL_TEXTURE_2D, texture, 0),
336                 gl().getError());
337         GLU_CHECK_CALL_ERROR(
338                 glBindFramebuffer(GL_FRAMEBUFFER, 0),
339                 gl().getError());
340 }
341
342 void TextureFboAttacher::detach (GLuint texture, GLuint fbo)
343 {
344         DE_UNREF(texture);
345         GLU_CHECK_CALL_ERROR(
346                 glBindFramebuffer(GL_FRAMEBUFFER, fbo),
347                 gl().getError());
348         GLU_CHECK_CALL_ERROR(
349                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0),
350                 gl().getError());
351         GLU_CHECK_CALL_ERROR(
352                 glBindFramebuffer(GL_FRAMEBUFFER, 0),
353                 gl().getError());
354 }
355
356 GLuint TextureFboAttacher::getAttachment (GLuint fbo)
357 {
358         return getFboAttachment(gl(), fbo, GL_TEXTURE);
359 }
360
361 void TextureFboAttacher::initStorage (void)
362 {
363         GLU_CHECK_CALL_ERROR(
364                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, FRAMEBUFFER_SIZE, FRAMEBUFFER_SIZE, 0,
365                                          GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, DE_NULL),
366                 gl().getError());
367 }
368
369 void RboFboAttacher::initStorage (void)
370 {
371         GLU_CHECK_CALL_ERROR(
372                 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, FRAMEBUFFER_SIZE, FRAMEBUFFER_SIZE),
373                 gl().getError());
374 }
375
376 void RboFboAttacher::attach (GLuint rbo, GLuint fbo)
377 {
378         GLU_CHECK_CALL_ERROR(
379                 glBindFramebuffer(GL_FRAMEBUFFER, fbo),
380                 gl().getError());
381         GLU_CHECK_CALL_ERROR(
382                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo),
383                 gl().getError());
384         GLU_CHECK_CALL_ERROR(
385                 glBindFramebuffer(GL_FRAMEBUFFER, 0),
386                 gl().getError());
387 }
388
389 void RboFboAttacher::detach (GLuint rbo, GLuint fbo)
390 {
391         DE_UNREF(rbo);
392         GLU_CHECK_CALL_ERROR(
393                 glBindFramebuffer(GL_FRAMEBUFFER, fbo),
394                 gl().getError());
395         GLU_CHECK_CALL_ERROR(
396                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0),
397                 gl().getError());
398         GLU_CHECK_CALL_ERROR(
399                 glBindFramebuffer(GL_FRAMEBUFFER, 0),
400                 gl().getError());
401 }
402
403 GLuint RboFboAttacher::getAttachment (GLuint fbo)
404 {
405         return getFboAttachment(gl(), fbo, GL_RENDERBUFFER);
406 }
407
408 static const char* const s_fragmentShaderTemplate = GLSL100_SRC(
409         void main()
410         {
411                 gl_FragColor = vec4(${RED}, ${GREEN}, ${BLUE}, 1.0);
412         }
413         );
414
415 void ShaderProgramAttacher::initAttachment (GLuint seed, GLuint shader)
416 {
417         using                                   de::insert;
418         using                                   de::floatToString;
419
420         Random                                  rnd(seed);
421         map<string, string>             params;
422         const StringTemplate    sourceTmpl      (s_fragmentShaderTemplate);
423
424         insert(params, "RED",   floatToString(rnd.getFloat(), 4));
425         insert(params, "GREEN", floatToString(rnd.getFloat(), 4));
426         insert(params, "BLUE",  floatToString(rnd.getFloat(), 4));
427
428         {
429                 const string                    source          = sourceTmpl.specialize(params);
430                 const char* const               sourceStr       = source.c_str();
431
432                 GLU_CHECK_CALL_ERROR(glShaderSource(shader, 1, &sourceStr, DE_NULL), gl().getError());
433                 GLU_CHECK_CALL_ERROR(glCompileShader(shader), gl().getError());
434
435                 {
436                         GLint compileStatus = 0;
437                         gl().getShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
438                         TCU_CHECK_MSG(compileStatus != 0, sourceStr);
439                 }
440         }
441 }
442
443 void ShaderProgramAttacher::attach (GLuint shader, GLuint program)
444 {
445         GLU_CHECK_CALL_ERROR(
446                 glAttachShader(program, shader),
447                 gl().getError());
448 }
449
450 void ShaderProgramAttacher::detach (GLuint shader, GLuint program)
451 {
452         GLU_CHECK_CALL_ERROR(
453                 glDetachShader(program, shader),
454                 gl().getError());
455 }
456
457 GLuint ShaderProgramAttacher::getAttachment (GLuint program)
458 {
459         GLuint                  shaders[2]      = { 0, 0 };
460         const GLsizei   shadersLen      = DE_LENGTH_OF_ARRAY(shaders);
461         GLsizei                 numShaders      = 0;
462         GLuint                  ret                     = 0;
463
464         gl().getAttachedShaders(program, shadersLen, &numShaders, shaders);
465
466         // There should ever be at most one attached shader in normal use, but if
467         // something is wrong, the temporary vertex shader might not have been
468         // detached properly, so let's find the fragment shader explicitly.
469         for (int ndx = 0; ndx < de::min<GLsizei>(shadersLen, numShaders); ++ndx)
470         {
471                 GLint shaderType = GL_NONE;
472                 gl().getShaderiv(shaders[ndx], GL_SHADER_TYPE, &shaderType);
473
474                 if (shaderType == GL_FRAGMENT_SHADER)
475                 {
476                         ret = shaders[ndx];
477                         break;
478                 }
479         }
480
481         return ret;
482 }
483
484 void setViewport (const RenderContext& renderCtx, const Rectangle& rect)
485 {
486         renderCtx.getFunctions().viewport(rect.x, rect.y, rect.width, rect.height);
487 }
488
489 void readRectangle (const RenderContext& renderCtx, const Rectangle& rect, Surface& dst)
490 {
491         dst.setSize(rect.width, rect.height);
492         glu::readPixels(renderCtx, rect.x, rect.y, dst.getAccess());
493 }
494
495 Rectangle randomViewport (const RenderContext& ctx, GLint maxWidth, GLint maxHeight,
496                                                   Random& rnd)
497 {
498         const RenderTarget&     target  = ctx.getRenderTarget();
499         const GLint                     width   = de::min(target.getWidth(), maxWidth);
500         const GLint                     xOff    = rnd.getInt(0, target.getWidth() - width);
501         const GLint                     height  = de::min(target.getHeight(), maxHeight);
502         const GLint                     yOff    = rnd.getInt(0, target.getHeight() - height);
503
504         return Rectangle(xOff, yOff, width, height);
505 }
506
507 void ShaderProgramInputAttacher::drawContainer (GLuint program, Surface& dst)
508 {
509         static const float      s_vertices[6]   = { -1.0, 0.0, 1.0, 1.0, 0.0, -1.0 };
510         Random                          rnd                             (program);
511         CheckedShader           vtxShader               (getRenderContext(),
512                                                                                  SHADERTYPE_VERTEX, s_vertexShaderSrc);
513         const Rectangle         viewport                = randomViewport(getRenderContext(),
514                                                                                                                  VIEWPORT_SIZE, VIEWPORT_SIZE, rnd);
515
516         gl().attachShader(program, vtxShader.getShader());
517         gl().linkProgram(program);
518
519         {
520                 GLint linkStatus = 0;
521                 gl().getProgramiv(program, GL_LINK_STATUS, &linkStatus);
522                 TCU_CHECK(linkStatus != 0);
523         }
524
525         log() << TestLog::Message
526                   << "// Attached a temporary vertex shader and linked program " << program
527                   << TestLog::EndMessage;
528
529         setViewport(getRenderContext(), viewport);
530         log() << TestLog::Message << "// Positioned viewport randomly" << TestLog::EndMessage;
531
532         glUseProgram(program);
533         {
534                 GLint posLoc = gl().getAttribLocation(program, "pos");
535                 TCU_CHECK(posLoc >= 0);
536
537                 gl().enableVertexAttribArray(posLoc);
538
539                 gl().clearColor(0, 0, 0, 1);
540                 gl().clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
541                 gl().vertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 0, s_vertices);
542                 gl().drawArrays(GL_TRIANGLES, 0, 3);
543
544                 gl().disableVertexAttribArray(posLoc);
545                 log () << TestLog::Message << "// Drew a fixed triangle" << TestLog::EndMessage;
546         }
547         glUseProgram(0);
548
549         readRectangle(getRenderContext(), viewport, dst);
550         log() << TestLog::Message << "// Copied viewport to output image" << TestLog::EndMessage;
551
552         gl().detachShader(program, vtxShader.getShader());
553         log() << TestLog::Message << "// Removed temporary vertex shader" << TestLog::EndMessage;
554 }
555
556 ES2Types::ES2Types (const Context& ctx)
557         : Types                 (ctx)
558         , m_bufferBind  (ctx, &CallLogWrapper::glBindBuffer,
559                                          GL_ARRAY_BUFFER, GL_ARRAY_BUFFER_BINDING)
560         , m_bufferType  (ctx, "buffer", &CallLogWrapper::glGenBuffers,
561                                          &CallLogWrapper::glDeleteBuffers,
562                                          &CallLogWrapper::glIsBuffer, &m_bufferBind)
563         , m_textureBind (ctx, &CallLogWrapper::glBindTexture, GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D)
564         , m_textureType (ctx, "texture", &CallLogWrapper::glGenTextures,
565                                          &CallLogWrapper::glDeleteTextures,
566                                          &CallLogWrapper::glIsTexture, &m_textureBind)
567         , m_rboBind             (ctx, &CallLogWrapper::glBindRenderbuffer,
568                                          GL_RENDERBUFFER, GL_RENDERBUFFER_BINDING)
569         , m_rboType             (ctx, "renderbuffer",
570                                          &CallLogWrapper::glGenRenderbuffers,
571                                          &CallLogWrapper::glDeleteRenderbuffers,
572                                          &CallLogWrapper::glIsRenderbuffer, &m_rboBind)
573         , m_fboBind             (ctx, &CallLogWrapper::glBindFramebuffer,
574                                          GL_FRAMEBUFFER, GL_FRAMEBUFFER_BINDING)
575         , m_fboType             (ctx, "framebuffer",
576                                          &CallLogWrapper::glGenFramebuffers,
577                                          &CallLogWrapper::glDeleteFramebuffers,
578                                          &CallLogWrapper::glIsFramebuffer, &m_fboBind)
579         , m_shaderType  (ctx)
580         , m_programType (ctx)
581         , m_texFboAtt   (ctx, m_textureType, m_fboType)
582         , m_texFboInAtt (m_texFboAtt)
583         , m_texFboOutAtt(m_texFboAtt)
584         , m_rboFboAtt   (ctx, m_rboType, m_fboType)
585         , m_rboFboInAtt (m_rboFboAtt)
586         , m_rboFboOutAtt(m_rboFboAtt)
587         , m_shaderAtt   (ctx, m_shaderType, m_programType)
588         , m_shaderInAtt (m_shaderAtt)
589 {
590         Type* const types[] =
591         {
592                 &m_bufferType, &m_textureType, &m_rboType, &m_fboType, &m_shaderType, &m_programType
593         };
594         m_types.insert(m_types.end(), DE_ARRAY_BEGIN(types), DE_ARRAY_END(types));
595
596         m_attachers.push_back(&m_texFboAtt);
597         m_attachers.push_back(&m_rboFboAtt);
598         m_attachers.push_back(&m_shaderAtt);
599
600         m_inAttachers.push_back(&m_texFboInAtt);
601         m_inAttachers.push_back(&m_rboFboInAtt);
602         m_inAttachers.push_back(&m_shaderInAtt);
603
604         m_outAttachers.push_back(&m_texFboOutAtt);
605         m_outAttachers.push_back(&m_rboFboOutAtt);
606 }
607
608 class Name
609 {
610 public:
611                                 Name            (Type& type) : m_type(type), m_name(type.gen()) {}
612                                 Name            (Type& type, GLuint name) : m_type(type), m_name(name) {}
613                                 ~Name           (void) { m_type.release(m_name); }
614         GLuint          operator*       (void) const { return m_name; }
615
616 private:
617         Type&                   m_type;
618         const GLuint    m_name;
619 };
620
621 class ResultCollector
622 {
623 public:
624                                         ResultCollector         (TestContext& testCtx);
625         bool                    check                           (bool cond, const char* msg);
626         void                    fail                            (const char* msg);
627         void                    warn                            (const char* msg);
628                                         ~ResultCollector        (void);
629
630 private:
631         void                    addResult                       (qpTestResult result, const char* msg);
632
633         TestContext&    m_testCtx;
634         TestLog&                m_log;
635         qpTestResult    m_result;
636         const char*             m_message;
637 };
638
639 ResultCollector::ResultCollector (TestContext& testCtx)
640         : m_testCtx             (testCtx)
641         , m_log                 (testCtx.getLog())
642         , m_result              (QP_TEST_RESULT_PASS)
643         , m_message             ("Pass")
644 {
645 }
646
647 bool ResultCollector::check (bool cond, const char* msg)
648 {
649         if (!cond)
650                 fail(msg);
651         return cond;
652 }
653
654 void ResultCollector::addResult (qpTestResult result, const char* msg)
655 {
656         m_log << TestLog::Message << "// Fail: " << msg << TestLog::EndMessage;
657         if (m_result == QP_TEST_RESULT_PASS)
658         {
659                 m_result = result;
660                 m_message = msg;
661         }
662         else
663         {
664                 if (result == QP_TEST_RESULT_FAIL)
665                         m_result = result;
666                 m_message = "Multiple problems, see log for details";
667         }
668 }
669
670 void ResultCollector::fail (const char* msg)
671 {
672         addResult(QP_TEST_RESULT_FAIL, msg);
673 }
674
675 void ResultCollector::warn (const char* msg)
676 {
677         addResult(QP_TEST_RESULT_QUALITY_WARNING, msg);
678 }
679
680 ResultCollector::~ResultCollector (void)
681 {
682         m_testCtx.setTestResult(m_result, m_message);
683 }
684
685 class TestBase : public TestCase, protected CallLogWrapper
686 {
687 protected:
688                                                         TestBase                        (const char*    name,
689                                                                                                  const char*    description,
690                                                                                                  const Context& ctx);
691
692         // Copy ContextWrapper since MI (except for CallLogWrapper) is a no-no.
693         const Context&                  getContext                      (void) const { return m_ctx; }
694         const RenderContext&    getRenderContext        (void) const { return m_ctx.getRenderContext(); }
695         const Functions&                gl                                      (void) const { return m_ctx.gl(); }
696         TestLog&                                log                                     (void) const { return m_ctx.log(); }
697         void                                    init                            (void);
698
699         Context                                 m_ctx;
700         Random                                  m_rnd;
701 };
702
703 TestBase::TestBase (const char* name, const char* description, const Context& ctx)
704         : TestCase                      (ctx.getTestContext(), name, description)
705         , CallLogWrapper        (ctx.gl(), ctx.log())
706         , m_ctx                         (ctx)
707         , m_rnd                         (deStringHash(name))
708 {
709         enableLogging(true);
710 }
711
712 void TestBase::init (void)
713 {
714         m_rnd = Random(deStringHash(getName()));
715 }
716
717 class LifeTest : public TestBase
718 {
719 public:
720         typedef void                    (LifeTest::*TestFunction)       (void);
721
722                                                         LifeTest                                        (const char*    name,
723                                                                                                                  const char*    description,
724                                                                                                                  Type&                  type,
725                                                                                                                  TestFunction   test)
726                                                                 : TestBase              (name, description, type.getContext())
727                                                                 , m_type                (type)
728                                                                 , m_test                (test) {}
729
730         IterateResult                   iterate                                         (void);
731
732         void                                    testGen                                         (void);
733         void                                    testDelete                                      (void);
734         void                                    testBind                                        (void);
735         void                                    testDeleteBound                         (void);
736         void                                    testBindNoGen                           (void);
737         void                                    testDeleteUsed                          (void);
738
739 private:
740         Binder&                                 binder                                          (void) { return *m_type.binder(); }
741
742         Type&                                   m_type;
743         TestFunction                    m_test;
744 };
745
746 IterateResult LifeTest::iterate (void)
747 {
748         (this->*m_test)();
749         return STOP;
750 }
751
752 void LifeTest::testGen (void)
753 {
754         ResultCollector errors  (getTestContext());
755         Name                    name    (m_type);
756
757         if (m_type.genCreates())
758                 errors.check(m_type.exists(*name), "Gen* should have created an object, but didn't");
759         else
760                 errors.check(!m_type.exists(*name), "Gen* should not have created an object, but did");
761 }
762
763 void LifeTest::testDelete (void)
764 {
765         ResultCollector errors  (getTestContext());
766         GLuint                  name    = m_type.gen();
767
768         m_type.release(name);
769         errors.check(!m_type.exists(name), "Object still exists after deletion");
770 }
771
772 void LifeTest::testBind (void)
773 {
774         ResultCollector errors  (getTestContext());
775         Name                    name    (m_type);
776
777         binder().bind(*name);
778         GLU_EXPECT_NO_ERROR(gl().getError(), "Bind failed");
779         errors.check(m_type.exists(*name), "Object does not exist after binding");
780         binder().bind(0);
781 }
782
783 void LifeTest::testDeleteBound (void)
784 {
785         const GLuint    id              = m_type.gen();
786         ResultCollector errors  (getTestContext());
787
788         binder().bind(id);
789         m_type.release(id);
790
791         if (m_type.nameLingers())
792         {
793                 errors.check(gl().getError() == GL_NO_ERROR, "Deleting bound object failed");
794                 errors.check(binder().getBinding() == id,
795                                          "Deleting bound object did not retain binding");
796                 errors.check(m_type.exists(id),
797                                          "Deleting bound object made its name invalid");
798                 errors.check(m_type.isDeleteFlagged(id),
799                                          "Deleting bound object did not flag the object for deletion");
800                 binder().bind(0);
801         }
802         else
803         {
804                 errors.check(gl().getError() == GL_NO_ERROR, "Deleting bound object failed");
805                 errors.check(binder().getBinding() == 0,
806                                          "Deleting bound object did not remove binding");
807                 errors.check(!m_type.exists(id),
808                                          "Deleting bound object did not make its name invalid");
809                 binder().bind(0);
810         }
811
812         errors.check(binder().getBinding() == 0, "Unbinding didn't remove binding");
813         errors.check(!m_type.exists(id), "Name is still valid after deleting and unbinding");
814 }
815
816 void LifeTest::testBindNoGen (void)
817 {
818         ResultCollector errors  (getTestContext());
819         const GLuint    id              = m_rnd.getUint32();
820
821         if (!errors.check(!m_type.exists(id), "Randomly chosen identifier already exists"))
822                 return;
823
824         Name                    name    (m_type, id);
825         binder().bind(*name);
826
827         if (binder().genRequired())
828         {
829                 errors.check(glGetError() == GL_INVALID_OPERATION,
830                                          "Did not fail when binding a name not generated by Gen* call");
831                 errors.check(!m_type.exists(*name),
832                                          "Bind* created an object for a name not generated by a Gen* call");
833         }
834         else
835         {
836                 errors.check(glGetError() == GL_NO_ERROR,
837                                          "Failed when binding a name not generated by Gen* call");
838                 errors.check(m_type.exists(*name),
839                                          "Object was not created by the Bind* call");
840         }
841 }
842
843 void LifeTest::testDeleteUsed (void)
844 {
845         ResultCollector errors(getTestContext());
846         GLuint                  programId = 0;
847
848         {
849                 CheckedShader   vtxShader       (getRenderContext(),
850                                                                          SHADERTYPE_VERTEX, s_vertexShaderSrc);
851                 CheckedShader   fragShader      (getRenderContext(),
852                                                                          SHADERTYPE_FRAGMENT, s_fragmentShaderSrc);
853                 CheckedProgram  program         (getRenderContext(),
854                                                                          vtxShader.getShader(), fragShader.getShader());
855
856                 programId = program.getProgram();
857
858                 log() << TestLog::Message << "// Created and linked program " << programId
859                           << TestLog::EndMessage;
860                 GLU_CHECK_CALL_ERROR(glUseProgram(programId), gl().getError());
861
862                 log() << TestLog::Message << "// Deleted program " << programId
863                           << TestLog::EndMessage;
864         }
865         TCU_CHECK(glIsProgram(programId));
866         {
867                 GLint deleteFlagged = 0;
868                 glGetProgramiv(programId, GL_DELETE_STATUS, &deleteFlagged);
869                 errors.check(deleteFlagged != 0, "Program object was not flagged as deleted");
870         }
871         GLU_CHECK_CALL_ERROR(glUseProgram(0), gl().getError());
872         errors.check(!gl().isProgram(programId),
873                                  "Deleted program name still valid after being made non-current");
874 }
875
876 class AttachmentTest : public TestBase
877 {
878 public:
879         typedef void                    (AttachmentTest::*TestFunction) (void);
880                                                         AttachmentTest                                  (const char*    name,
881                                                                                                                          const char*    description,
882                                                                                                                          Attacher&              attacher,
883                                                                                                                          TestFunction   test)
884                                                                 : TestBase              (name, description, attacher.getContext())
885                                                                 , m_attacher    (attacher)
886                                                                 , m_test                (test) {}
887         IterateResult                   iterate                                                 (void);
888
889         void                                    testDeletedNames                                (void);
890         void                                    testDeletedBinding                              (void);
891         void                                    testDeletedReattach                             (void);
892
893 private:
894         Attacher&                               m_attacher;
895         const TestFunction              m_test;
896 };
897
898 IterateResult AttachmentTest::iterate (void)
899 {
900         (this->*m_test)();
901         return STOP;
902 }
903
904 GLuint getAttachment (Attacher& attacher, GLuint container)
905 {
906         const GLuint queriedAttachment = attacher.getAttachment(container);
907         attacher.log() << TestLog::Message
908                                    << "// Result of query for " << attacher.getElementType().getName()
909                                    << " attached to " << attacher.getContainerType().getName() << " "
910                                    << container << ": " << queriedAttachment << "."
911                                    << TestLog::EndMessage;
912         return queriedAttachment;
913 }
914
915 void AttachmentTest::testDeletedNames (void)
916 {
917         Type&                   elemType                = m_attacher.getElementType();
918         Type&                   containerType   = m_attacher.getContainerType();
919         Name                    container               (containerType);
920         ResultCollector errors                  (getTestContext());
921         GLuint                  elementId               = 0;
922
923         {
924                 Name element(elemType);
925                 elementId = *element;
926                 m_attacher.initAttachment(0, *element);
927                 m_attacher.attach(*element, *container);
928                 errors.check(getAttachment(m_attacher, *container) == elementId,
929                                          "Attachment name not returned by query even before deletion.");
930         }
931
932         // "Such a container or other context may continue using the object, and
933         // may still contain state identifying its name as being currently bound"
934         //
935         // We here interpret "may" to mean that whenever the container has a
936         // deleted object attached to it, a query will return that object's former
937         // name.
938         errors.check(getAttachment(m_attacher, *container) == elementId,
939                                  "Attachment name not returned by query after attachment was deleted.");
940
941         if (elemType.nameLingers())
942                 errors.check(elemType.exists(elementId),
943                                          "Attached object name no longer valid after deletion.");
944         else
945                 errors.check(!elemType.exists(elementId),
946                                          "Attached object name still valid after deletion.");
947
948         m_attacher.detach(elementId, *container);
949         errors.check(getAttachment(m_attacher, *container) == 0,
950                                  "Attachment name returned by query even after detachment.");
951         errors.check(!elemType.exists(elementId),
952                                  "Deleted attached object name still usable after detachment.");
953 };
954
955 class InputAttachmentTest : public TestBase
956 {
957 public:
958                                         InputAttachmentTest     (const char*    name,
959                                                                                  const char*    description,
960                                                                                  InputAttacher& inputAttacher)
961                                                 : TestBase                      (name, description, inputAttacher.getContext())
962                                                 , m_inputAttacher       (inputAttacher) {}
963
964         IterateResult   iterate                         (void);
965
966 private:
967         InputAttacher&  m_inputAttacher;
968 };
969
970 GLuint replaceName (Type& type, GLuint oldName, TestLog& log)
971 {
972         const Binder* const     binder          = type.binder();
973         const bool                      genRequired     = binder == DE_NULL || binder->genRequired();
974
975         if (genRequired)
976                 return type.gen();
977
978         log << TestLog::Message
979                 << "// Type does not require Gen* for binding, reusing old id " << oldName << "."
980                 << TestLog::EndMessage;
981
982         return oldName;
983 }
984
985 IterateResult InputAttachmentTest::iterate (void)
986 {
987         Attacher&               attacher                = m_inputAttacher.getAttacher();
988         Type&                   containerType   = attacher.getContainerType();
989         Type&                   elementType             = attacher.getElementType();
990         Name                    container               (containerType);
991         GLuint                  elementId               = 0;
992         const GLuint    refSeed                 = m_rnd.getUint32();
993         const GLuint    newSeed                 = m_rnd.getUint32();
994         ResultCollector errors                  (getTestContext());
995
996         Surface                 refSurface;             // Surface from drawing with refSeed-seeded attachment
997         Surface                 delSurface;             // Surface from drawing with deleted refSeed attachment
998         Surface                 newSurface;             // Surface from drawing with newSeed-seeded attachment
999
1000         log() << TestLog::Message
1001                   << "Testing if writing to a newly created object modifies a deleted attachment"
1002                   << TestLog::EndMessage;
1003
1004         {
1005                 ScopedLogSection        section (log(),
1006                                                                          "Write to original", "Writing to an original attachment");
1007                 const Name                      element (elementType);
1008
1009                 elementId = *element;
1010                 attacher.initAttachment(refSeed, elementId);
1011                 attacher.attach(elementId, *container);
1012                 m_inputAttacher.drawContainer(*container, refSurface);
1013                 // element gets deleted here
1014                 log() << TestLog::Message << "// Deleting attachment";
1015         }
1016         {
1017                 ScopedLogSection section        (log(), "Write to new",
1018                                                                          "Writing to a new attachment after deleting the original");
1019                 const GLuint    newId           = replaceName(elementType, elementId, log());
1020                 const Name              newElement      (elementType, newId);
1021
1022                 attacher.initAttachment(newSeed, newId);
1023
1024                 m_inputAttacher.drawContainer(*container, delSurface);
1025                 attacher.detach(elementId, *container);
1026
1027                 attacher.attach(newId, *container);
1028                 m_inputAttacher.drawContainer(*container, newSurface);
1029                 attacher.detach(newId, *container);
1030         }
1031         {
1032                 const bool surfacesMatch = tcu::pixelThresholdCompare(
1033                         log(), "Reading from deleted",
1034                         "Comparison result from reading from a container with a deleted attachment "
1035                         "before and after writing to a fresh object.",
1036                         refSurface, delSurface, RGBA(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT);
1037
1038                 errors.check(
1039                         surfacesMatch,
1040                         "Writing to a fresh object modified the container with a deleted attachment.");
1041
1042                 if (!surfacesMatch)
1043                         log() << TestLog::Image("New attachment",
1044                                                                         "Container state after attached to the fresh object",
1045                                                                         newSurface);
1046         }
1047
1048         return STOP;
1049 }
1050
1051 class OutputAttachmentTest : public TestBase
1052 {
1053 public:
1054                                                 OutputAttachmentTest                    (const char*            name,
1055                                                                                                                  const char*            description,
1056                                                                                                                  OutputAttacher&        outputAttacher)
1057                                                         : TestBase                      (name, description,
1058                                                                                                  outputAttacher.getContext())
1059                                                         , m_outputAttacher      (outputAttacher) {}
1060         IterateResult           iterate                                                 (void);
1061
1062 private:
1063         OutputAttacher&         m_outputAttacher;
1064 };
1065
1066 IterateResult OutputAttachmentTest::iterate (void)
1067 {
1068         Attacher&               attacher                = m_outputAttacher.getAttacher();
1069         Type&                   containerType   = attacher.getContainerType();
1070         Type&                   elementType             = attacher.getElementType();
1071         Name                    container               (containerType);
1072         GLuint                  elementId               = 0;
1073         const GLuint    refSeed                 = m_rnd.getUint32();
1074         const GLuint    newSeed                 = m_rnd.getUint32();
1075         ResultCollector errors                  (getTestContext());
1076         Surface                 refSurface;             // Surface drawn from attachment to refSeed container
1077         Surface                 newSurface;             // Surface drawn from attachment to newSeed container
1078         Surface                 delSurface;             // Like newSurface, after writing to a deleted attachment
1079
1080         log() << TestLog::Message
1081                   << "Testing if writing to a container with a deleted attachment "
1082                   << "modifies a newly created object"
1083                   << TestLog::EndMessage;
1084
1085         {
1086                 ScopedLogSection        section (log(), "Write to existing",
1087                                                                          "Writing to a container with an existing attachment");
1088                 const Name                      element (elementType);
1089
1090                 elementId = *element;
1091                 attacher.initAttachment(0, elementId);
1092                 attacher.attach(elementId, *container);
1093
1094                 // For reference purposes, make note of what refSeed looks like.
1095                 m_outputAttacher.setupContainer(refSeed, *container);
1096                 m_outputAttacher.drawAttachment(elementId, refSurface);
1097         }
1098         {
1099                 ScopedLogSection        section         (log(), "Write to deleted",
1100                                                                                  "Writing to a container after deletion of attachment");
1101                 const GLuint            newId           = replaceName(elementType, elementId, log());
1102                 const Name                      newElement      (elementType, newId);
1103
1104                 log() << TestLog::Message
1105                           << "Creating a new object " << newId
1106                           << TestLog::EndMessage;
1107
1108                 log() << TestLog::Message
1109                           << "Recording state of new object before writing to container"
1110                           << TestLog::EndMessage;
1111                 attacher.initAttachment(newSeed, newId);
1112                 m_outputAttacher.drawAttachment(newId, newSurface);
1113
1114                 log() << TestLog::Message
1115                           << "Writing to container"
1116                           << TestLog::EndMessage;
1117
1118                 // Now re-write refSeed to the container.
1119                 m_outputAttacher.setupContainer(refSeed, *container);
1120                 // Does it affect the newly created attachment object?
1121                 m_outputAttacher.drawAttachment(newId, delSurface);
1122         }
1123         attacher.detach(elementId, *container);
1124
1125         const bool surfacesMatch = tcu::pixelThresholdCompare(
1126                 log(), "Writing to deleted",
1127                 "Comparison result from reading from a fresh object before and after "
1128                 "writing to a container with a deleted attachment",
1129                 newSurface, delSurface, RGBA(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT);
1130
1131         errors.check(surfacesMatch,
1132                                  "Writing to container with deleted attachment modified a new object.");
1133
1134         if (!surfacesMatch)
1135                 log() << TestLog::Image(
1136                         "Original attachment",
1137                         "Result of container modification on original attachment before deletion.",
1138                         refSurface);
1139         return STOP;
1140 };
1141
1142 struct LifeTestSpec
1143 {
1144         const char*                             name;
1145         LifeTest::TestFunction  func;
1146         bool                                    needBind;
1147 };
1148
1149 MovePtr<TestCaseGroup> createLifeTestGroup (TestContext& testCtx,
1150                                                                                         const LifeTestSpec& spec,
1151                                                                                         const vector<Type*>& types)
1152 {
1153         MovePtr<TestCaseGroup> group(new TestCaseGroup(testCtx, spec.name, spec.name));
1154
1155         for (vector<Type*>::const_iterator it = types.begin(); it != types.end(); ++it)
1156         {
1157                 Type& type = **it;
1158                 const char* name = type.getName();
1159                 if (!spec.needBind || type.binder() != DE_NULL)
1160                         group->addChild(new LifeTest(name, name, type, spec.func));
1161         }
1162
1163         return group;
1164 }
1165
1166 static const LifeTestSpec s_lifeTests[] =
1167 {
1168         { "gen",                        &LifeTest::testGen,                     false   },
1169         { "delete",                     &LifeTest::testDelete,          false   },
1170         { "bind",                       &LifeTest::testBind,            true    },
1171         { "delete_bound",       &LifeTest::testDeleteBound,     true    },
1172         { "bind_no_gen",        &LifeTest::testBindNoGen,       true    },
1173 };
1174
1175 string attacherName (Attacher& attacher)
1176 {
1177         ostringstream os;
1178         os << attacher.getElementType().getName() << "_" <<  attacher.getContainerType().getName();
1179         return os.str();
1180 }
1181
1182 void addTestCases (TestCaseGroup& group, Types& types)
1183 {
1184         TestContext& testCtx = types.getTestContext();
1185
1186         for (const LifeTestSpec* it = DE_ARRAY_BEGIN(s_lifeTests);
1187                  it != DE_ARRAY_END(s_lifeTests); ++it)
1188                 group.addChild(createLifeTestGroup(testCtx, *it, types.getTypes()).release());
1189
1190         {
1191                 TestCaseGroup* const delUsedGroup =
1192                         new TestCaseGroup(testCtx, "delete_used", "Delete current program");
1193                 group.addChild(delUsedGroup);
1194
1195                 delUsedGroup->addChild(
1196                         new LifeTest("program", "program", types.getProgramType(),
1197                                                  &LifeTest::testDeleteUsed));
1198         }
1199
1200         {
1201                 TestCaseGroup* const    attGroup        = new TestCaseGroup(
1202                         testCtx, "attach", "Attachment tests");
1203                 group.addChild(attGroup);
1204
1205                 {
1206                         TestCaseGroup* const    nameGroup       = new TestCaseGroup(
1207                                 testCtx, "deleted_name", "Name of deleted attachment");
1208                         attGroup->addChild(nameGroup);
1209
1210                         const vector<Attacher*>& atts = types.getAttachers();
1211                         for (vector<Attacher*>::const_iterator it = atts.begin(); it != atts.end(); ++it)
1212                         {
1213                                 const string name = attacherName(**it);
1214                                 nameGroup->addChild(new AttachmentTest(name.c_str(), name.c_str(), **it,
1215                                                                                                            &AttachmentTest::testDeletedNames));
1216                         }
1217                 }
1218                 {
1219                         TestCaseGroup* inputGroup = new TestCaseGroup(
1220                                 testCtx, "deleted_input", "Input from deleted attachment");
1221                         attGroup->addChild(inputGroup);
1222
1223                         const vector<InputAttacher*>& inAtts = types.getInputAttachers();
1224                         for (vector<InputAttacher*>::const_iterator it = inAtts.begin();
1225                                  it != inAtts.end(); ++it)
1226                         {
1227                                 const string name = attacherName((*it)->getAttacher());
1228                                 inputGroup->addChild(new InputAttachmentTest(name.c_str(), name.c_str(), **it));
1229                         }
1230                 }
1231                 {
1232                         TestCaseGroup* outputGroup = new TestCaseGroup(
1233                                 testCtx, "deleted_output", "Output to deleted attachment");
1234                         attGroup->addChild(outputGroup);
1235
1236                         const vector<OutputAttacher*>& outAtts = types.getOutputAttachers();
1237                         for (vector<OutputAttacher*>::const_iterator it = outAtts.begin();
1238                                  it != outAtts.end(); ++it)
1239                         {
1240                                 string name = attacherName((*it)->getAttacher());
1241                                 outputGroup->addChild(new OutputAttachmentTest(name.c_str(), name.c_str(),
1242                                                                                                                            **it));
1243                         }
1244                 }
1245         }
1246 }
1247
1248 } // details
1249 } // LifetimeTests
1250 } // gls
1251 } // deqp