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
getInteger(ContextWrapper & gl,GLenum queryParam)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:
CheckedShader(const RenderContext & renderCtx,glu::ShaderType type,const string & src)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:
CheckedProgram(const RenderContext & renderCtx,GLuint vtxShader,GLuint fragShader)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
ContextWrapper(const Context & ctx)129 ContextWrapper::ContextWrapper (const Context& ctx)
130 : CallLogWrapper (ctx.gl(), ctx.log())
131 , m_ctx (ctx)
132 {
133 enableLogging(true);
134 }
135
bind(GLuint name)136 void SimpleBinder::bind (GLuint name)
137 {
138 (this->*m_bindFunc)(m_bindTarget, name);
139 }
140
getBinding(void)141 GLuint SimpleBinder::getBinding (void)
142 {
143 return getInteger(*this, m_bindingParam);
144 }
145
gen(void)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:
VertexArrayBinder(Context & ctx)156 VertexArrayBinder (Context& ctx)
157 : SimpleBinder (ctx, 0, GL_NONE, GL_VERTEX_ARRAY_BINDING, true) {}
bind(GLuint name)158 void bind (GLuint name) { glBindVertexArray(name); }
159 };
160
161 class QueryBinder : public Binder
162 {
163 public:
QueryBinder(Context & ctx)164 QueryBinder (Context& ctx) : Binder(ctx) {}
bind(GLuint name)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 }
getBinding(void)172 GLuint getBinding (void) { return 0; }
173 };
174
isDeleteFlagged(GLuint name)175 bool ProgramType::isDeleteFlagged (GLuint name)
176 {
177 GLint deleteFlagged = 0;
178 glGetProgramiv(name, GL_DELETE_STATUS, &deleteFlagged);
179 return deleteFlagged != 0;
180 }
181
isDeleteFlagged(GLuint name)182 bool ShaderType::isDeleteFlagged (GLuint name)
183 {
184 GLint deleteFlagged = 0;
185 glGetShaderiv(name, GL_DELETE_STATUS, &deleteFlagged);
186 return deleteFlagged != 0;
187 }
188
setupFbo(const Context & ctx,GLuint seed,GLuint fbo)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
drawFbo(const Context & ctx,GLuint fbo,Surface & dst)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
getFboAttachment(const Functions & gl,GLuint fbo,GLenum requiredType)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
initAttachment(GLuint seed,GLuint element)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
drawContainer(GLuint fbo,Surface & dst)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
setupContainer(GLuint seed,GLuint fbo)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
drawAttachment(GLuint element,Surface & dst)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
attach(GLuint texture,GLuint fbo)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
detach(GLuint texture,GLuint fbo)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
getAttachment(GLuint fbo)356 GLuint TextureFboAttacher::getAttachment (GLuint fbo)
357 {
358 return getFboAttachment(gl(), fbo, GL_TEXTURE);
359 }
360
initStorage(void)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
initStorage(void)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
attach(GLuint rbo,GLuint fbo)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
detach(GLuint rbo,GLuint fbo)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
getAttachment(GLuint fbo)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
initAttachment(GLuint seed,GLuint shader)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
attach(GLuint shader,GLuint program)443 void ShaderProgramAttacher::attach (GLuint shader, GLuint program)
444 {
445 GLU_CHECK_CALL_ERROR(
446 glAttachShader(program, shader),
447 gl().getError());
448 }
449
detach(GLuint shader,GLuint program)450 void ShaderProgramAttacher::detach (GLuint shader, GLuint program)
451 {
452 GLU_CHECK_CALL_ERROR(
453 glDetachShader(program, shader),
454 gl().getError());
455 }
456
getAttachment(GLuint program)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
setViewport(const RenderContext & renderCtx,const Rectangle & rect)484 void setViewport (const RenderContext& renderCtx, const Rectangle& rect)
485 {
486 renderCtx.getFunctions().viewport(rect.x, rect.y, rect.width, rect.height);
487 }
488
readRectangle(const RenderContext & renderCtx,const Rectangle & rect,Surface & dst)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
randomViewport(const RenderContext & ctx,GLint maxWidth,GLint maxHeight,Random & rnd)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
drawContainer(GLuint program,Surface & dst)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
ES2Types(const Context & ctx)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:
Name(Type & type)611 Name (Type& type) : m_type(type), m_name(type.gen()) {}
Name(Type & type,GLuint name)612 Name (Type& type, GLuint name) : m_type(type), m_name(name) {}
~Name(void)613 ~Name (void) { m_type.release(m_name); }
operator *(void) const614 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
ResultCollector(TestContext & testCtx)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
check(bool cond,const char * msg)647 bool ResultCollector::check (bool cond, const char* msg)
648 {
649 if (!cond)
650 fail(msg);
651 return cond;
652 }
653
addResult(qpTestResult result,const char * msg)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
fail(const char * msg)670 void ResultCollector::fail (const char* msg)
671 {
672 addResult(QP_TEST_RESULT_FAIL, msg);
673 }
674
warn(const char * msg)675 void ResultCollector::warn (const char* msg)
676 {
677 addResult(QP_TEST_RESULT_QUALITY_WARNING, msg);
678 }
679
~ResultCollector(void)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.
getContext(void) const693 const Context& getContext (void) const { return m_ctx; }
getRenderContext(void) const694 const RenderContext& getRenderContext (void) const { return m_ctx.getRenderContext(); }
gl(void) const695 const Functions& gl (void) const { return m_ctx.gl(); }
log(void) const696 TestLog& log (void) const { return m_ctx.log(); }
697 void init (void);
698
699 Context m_ctx;
700 Random m_rnd;
701 };
702
TestBase(const char * name,const char * description,const Context & ctx)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
init(void)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
LifeTest(const char * name,const char * description,Type & type,TestFunction test)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:
binder(void)740 Binder& binder (void) { return *m_type.binder(); }
741
742 Type& m_type;
743 TestFunction m_test;
744 };
745
iterate(void)746 IterateResult LifeTest::iterate (void)
747 {
748 (this->*m_test)();
749 return STOP;
750 }
751
testGen(void)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
testDelete(void)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
testBind(void)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
testDeleteBound(void)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
testBindNoGen(void)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
testDeleteUsed(void)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);
AttachmentTest(const char * name,const char * description,Attacher & attacher,TestFunction test)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
iterate(void)898 IterateResult AttachmentTest::iterate (void)
899 {
900 (this->*m_test)();
901 return STOP;
902 }
903
getAttachment(Attacher & attacher,GLuint container)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
testDeletedNames(void)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:
InputAttachmentTest(const char * name,const char * description,InputAttacher & inputAttacher)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
replaceName(Type & type,GLuint oldName,TestLog & log)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
iterate(void)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:
OutputAttachmentTest(const char * name,const char * description,OutputAttacher & outputAttacher)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
iterate(void)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
createLifeTestGroup(TestContext & testCtx,const LifeTestSpec & spec,const vector<Type * > & types)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
attacherName(Attacher & attacher)1175 string attacherName (Attacher& attacher)
1176 {
1177 ostringstream os;
1178 os << attacher.getElementType().getName() << "_" << attacher.getContainerType().getName();
1179 return os.str();
1180 }
1181
addTestCases(TestCaseGroup & group,Types & types)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
1252