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