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