Merge "Add support for EGL_EXT_pixel_format_float" into nougat-cts-dev
[platform/upstream/VK-GL-CTS.git] / modules / egl / teglRenderTests.cpp
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program EGL 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 Rendering tests for different config and api combinations.
22  * \todo [2013-03-19 pyry] GLES1 and VG support.
23  *//*--------------------------------------------------------------------*/
24
25 #include "teglRenderTests.hpp"
26 #include "teglRenderCase.hpp"
27
28 #include "tcuRenderTarget.hpp"
29 #include "tcuTestLog.hpp"
30 #include "tcuImageCompare.hpp"
31 #include "tcuTextureUtil.hpp"
32 #include "tcuSurface.hpp"
33
34 #include "egluDefs.hpp"
35 #include "egluUtil.hpp"
36
37 #include "eglwLibrary.hpp"
38 #include "eglwEnums.hpp"
39
40 #include "gluShaderProgram.hpp"
41
42 #include "glwFunctions.hpp"
43 #include "glwEnums.hpp"
44
45 #include "deRandom.hpp"
46 #include "deSharedPtr.hpp"
47 #include "deSemaphore.hpp"
48 #include "deThread.hpp"
49 #include "deString.h"
50
51 #include "rrRenderer.hpp"
52 #include "rrFragmentOperations.hpp"
53
54 #include <algorithm>
55 #include <iterator>
56 #include <memory>
57 #include <set>
58
59 namespace deqp
60 {
61 namespace egl
62 {
63
64 using std::string;
65 using std::vector;
66 using std::set;
67
68 using tcu::Vec4;
69
70 using tcu::TestLog;
71
72 using namespace glw;
73 using namespace eglw;
74
75 static const tcu::Vec4  CLEAR_COLOR             = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
76 static const float              CLEAR_DEPTH             = 1.0f;
77 static const int                CLEAR_STENCIL   = 0;
78
79 namespace
80 {
81
82 enum PrimitiveType
83 {
84         PRIMITIVETYPE_TRIANGLE = 0,     //!< Triangles, requires 3 coordinates per primitive
85 //      PRIMITIVETYPE_POINT,            //!< Points, requires 1 coordinate per primitive (w is used as size)
86 //      PRIMITIVETYPE_LINE,                     //!< Lines, requires 2 coordinates per primitive
87
88         PRIMITIVETYPE_LAST
89 };
90
91 enum BlendMode
92 {
93         BLENDMODE_NONE = 0,                     //!< No blending
94         BLENDMODE_ADDITIVE,                     //!< Blending with ONE, ONE
95         BLENDMODE_SRC_OVER,                     //!< Blending with SRC_ALPHA, ONE_MINUS_SRC_ALPHA
96
97         BLENDMODE_LAST
98 };
99
100 enum DepthMode
101 {
102         DEPTHMODE_NONE = 0,                     //!< No depth test or depth writes
103         DEPTHMODE_LESS,                         //!< Depth test with less & depth write
104
105         DEPTHMODE_LAST
106 };
107
108 enum StencilMode
109 {
110         STENCILMODE_NONE = 0,           //!< No stencil test or write
111         STENCILMODE_LEQUAL_INC,         //!< Stencil test with LEQUAL, increment on pass
112
113         STENCILMODE_LAST
114 };
115
116 struct DrawPrimitiveOp
117 {
118         PrimitiveType   type;
119         int                             count;
120         vector<Vec4>    positions;
121         vector<Vec4>    colors;
122         BlendMode               blend;
123         DepthMode               depth;
124         StencilMode             stencil;
125         int                             stencilRef;
126 };
127
128 static bool isANarrowScreenSpaceTriangle (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec4& p2)
129 {
130         // to clip space
131         const tcu::Vec2 csp0                            = p0.swizzle(0, 1) / p0.w();
132         const tcu::Vec2 csp1                            = p1.swizzle(0, 1) / p1.w();
133         const tcu::Vec2 csp2                            = p2.swizzle(0, 1) / p2.w();
134
135         const tcu::Vec2 e01                                     = (csp1 - csp0);
136         const tcu::Vec2 e02                                     = (csp2 - csp0);
137
138         const float             minimumVisibleArea      = 0.4f; // must cover at least 10% of the surface
139         const float             visibleArea                     = de::abs(e01.x() * e02.y() - e02.x() * e01.y()) * 0.5f;
140
141         return visibleArea < minimumVisibleArea;
142 }
143
144 void randomizeDrawOp (de::Random& rnd, DrawPrimitiveOp& drawOp)
145 {
146         const int       minStencilRef   = 0;
147         const int       maxStencilRef   = 8;
148         const int       minPrimitives   = 2;
149         const int       maxPrimitives   = 4;
150
151         const float     maxTriOffset    = 1.0f;
152         const float     minDepth                = -1.0f; // \todo [pyry] Reference doesn't support Z clipping yet
153         const float     maxDepth                = 1.0f;
154
155         const float     minRGB                  = 0.2f;
156         const float     maxRGB                  = 0.9f;
157         const float     minAlpha                = 0.3f;
158         const float     maxAlpha                = 1.0f;
159
160         drawOp.type                     = (PrimitiveType)rnd.getInt(0, PRIMITIVETYPE_LAST-1);
161         drawOp.count            = rnd.getInt(minPrimitives, maxPrimitives);
162         drawOp.blend            = (BlendMode)rnd.getInt(0, BLENDMODE_LAST-1);
163         drawOp.depth            = (DepthMode)rnd.getInt(0, DEPTHMODE_LAST-1);
164         drawOp.stencil          = (StencilMode)rnd.getInt(0, STENCILMODE_LAST-1);
165         drawOp.stencilRef       = rnd.getInt(minStencilRef, maxStencilRef);
166
167         if (drawOp.type == PRIMITIVETYPE_TRIANGLE)
168         {
169                 drawOp.positions.resize(drawOp.count*3);
170                 drawOp.colors.resize(drawOp.count*3);
171
172                 for (int triNdx = 0; triNdx < drawOp.count; triNdx++)
173                 {
174                         const float             cx              = rnd.getFloat(-1.0f, 1.0f);
175                         const float             cy              = rnd.getFloat(-1.0f, 1.0f);
176
177                         for (int coordNdx = 0; coordNdx < 3; coordNdx++)
178                         {
179                                 tcu::Vec4&      position        = drawOp.positions[triNdx*3 + coordNdx];
180                                 tcu::Vec4&      color           = drawOp.colors[triNdx*3 + coordNdx];
181
182                                 position.x()    = cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
183                                 position.y()    = cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
184                                 position.z()    = rnd.getFloat(minDepth, maxDepth);
185                                 position.w()    = 1.0f;
186
187                                 color.x()               = rnd.getFloat(minRGB, maxRGB);
188                                 color.y()               = rnd.getFloat(minRGB, maxRGB);
189                                 color.z()               = rnd.getFloat(minRGB, maxRGB);
190                                 color.w()               = rnd.getFloat(minAlpha, maxAlpha);
191                         }
192
193                         // avoid generating narrow triangles
194                         {
195                                 const int       maxAttempts     = 40;
196                                 int                     numAttempts     = 0;
197                                 tcu::Vec4&      p0                      = drawOp.positions[triNdx*3 + 0];
198                                 tcu::Vec4&      p1                      = drawOp.positions[triNdx*3 + 1];
199                                 tcu::Vec4&      p2                      = drawOp.positions[triNdx*3 + 2];
200
201                                 while (isANarrowScreenSpaceTriangle(p0, p1, p2))
202                                 {
203                                         p1.x()  = cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
204                                         p1.y()  = cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
205                                         p1.z()  = rnd.getFloat(minDepth, maxDepth);
206                                         p1.w()  = 1.0f;
207
208                                         p2.x()  = cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
209                                         p2.y()  = cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
210                                         p2.z()  = rnd.getFloat(minDepth, maxDepth);
211                                         p2.w()  = 1.0f;
212
213                                         if (++numAttempts > maxAttempts)
214                                         {
215                                                 DE_ASSERT(false);
216                                                 break;
217                                         }
218                                 }
219                         }
220                 }
221         }
222         else
223                 DE_ASSERT(false);
224 }
225
226 // Reference rendering code
227
228 class ReferenceShader : public rr::VertexShader, public rr::FragmentShader
229 {
230 public:
231         enum
232         {
233                 VaryingLoc_Color = 0
234         };
235
236         ReferenceShader ()
237                 : rr::VertexShader      (2, 1)          // color and pos in => color out
238                 , rr::FragmentShader(1, 1)              // color in => color out
239         {
240                 this->rr::VertexShader::m_inputs[0].type                = rr::GENERICVECTYPE_FLOAT;
241                 this->rr::VertexShader::m_inputs[1].type                = rr::GENERICVECTYPE_FLOAT;
242
243                 this->rr::VertexShader::m_outputs[0].type               = rr::GENERICVECTYPE_FLOAT;
244                 this->rr::VertexShader::m_outputs[0].flatshade  = false;
245
246                 this->rr::FragmentShader::m_inputs[0].type              = rr::GENERICVECTYPE_FLOAT;
247                 this->rr::FragmentShader::m_inputs[0].flatshade = false;
248
249                 this->rr::FragmentShader::m_outputs[0].type             = rr::GENERICVECTYPE_FLOAT;
250         }
251
252         void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const
253         {
254                 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
255                 {
256                         const int positionAttrLoc = 0;
257                         const int colorAttrLoc = 1;
258
259                         rr::VertexPacket& packet = *packets[packetNdx];
260
261                         // Transform to position
262                         packet.position = rr::readVertexAttribFloat(inputs[positionAttrLoc], packet.instanceNdx, packet.vertexNdx);
263
264                         // Pass color to FS
265                         packet.outputs[VaryingLoc_Color] = rr::readVertexAttribFloat(inputs[colorAttrLoc], packet.instanceNdx, packet.vertexNdx);
266                 }
267         }
268
269         void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
270         {
271                 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
272                 {
273                         rr::FragmentPacket& packet = packets[packetNdx];
274
275                         for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
276                                 rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, rr::readVarying<float>(packet, context, VaryingLoc_Color, fragNdx));
277                 }
278         }
279 };
280
281 void toReferenceRenderState (rr::RenderState& state, const DrawPrimitiveOp& drawOp)
282 {
283         state.cullMode  = rr::CULLMODE_NONE;
284
285         if (drawOp.blend != BLENDMODE_NONE)
286         {
287                 state.fragOps.blendMode = rr::BLENDMODE_STANDARD;
288
289                 switch (drawOp.blend)
290                 {
291                         case BLENDMODE_ADDITIVE:
292                                 state.fragOps.blendRGBState.srcFunc             = rr::BLENDFUNC_ONE;
293                                 state.fragOps.blendRGBState.dstFunc             = rr::BLENDFUNC_ONE;
294                                 state.fragOps.blendRGBState.equation    = rr::BLENDEQUATION_ADD;
295                                 state.fragOps.blendAState                               = state.fragOps.blendRGBState;
296                                 break;
297
298                         case BLENDMODE_SRC_OVER:
299                                 state.fragOps.blendRGBState.srcFunc             = rr::BLENDFUNC_SRC_ALPHA;
300                                 state.fragOps.blendRGBState.dstFunc             = rr::BLENDFUNC_ONE_MINUS_SRC_ALPHA;
301                                 state.fragOps.blendRGBState.equation    = rr::BLENDEQUATION_ADD;
302                                 state.fragOps.blendAState                               = state.fragOps.blendRGBState;
303                                 break;
304
305                         default:
306                                 DE_ASSERT(false);
307                 }
308         }
309
310         if (drawOp.depth != DEPTHMODE_NONE)
311         {
312                 state.fragOps.depthTestEnabled = true;
313
314                 DE_ASSERT(drawOp.depth == DEPTHMODE_LESS);
315                 state.fragOps.depthFunc = rr::TESTFUNC_LESS;
316         }
317
318         if (drawOp.stencil != STENCILMODE_NONE)
319         {
320                 state.fragOps.stencilTestEnabled = true;
321
322                 DE_ASSERT(drawOp.stencil == STENCILMODE_LEQUAL_INC);
323                 state.fragOps.stencilStates[0].func             = rr::TESTFUNC_LEQUAL;
324                 state.fragOps.stencilStates[0].sFail    = rr::STENCILOP_KEEP;
325                 state.fragOps.stencilStates[0].dpFail   = rr::STENCILOP_INCR;
326                 state.fragOps.stencilStates[0].dpPass   = rr::STENCILOP_INCR;
327                 state.fragOps.stencilStates[0].ref              = drawOp.stencilRef;
328                 state.fragOps.stencilStates[1]                  = state.fragOps.stencilStates[0];
329         }
330 }
331
332 tcu::TextureFormat getColorFormat (const tcu::PixelFormat& colorBits)
333 {
334         using tcu::TextureFormat;
335
336         DE_ASSERT(de::inBounds(colorBits.redBits,       0, 0xff) &&
337                           de::inBounds(colorBits.greenBits,     0, 0xff) &&
338                           de::inBounds(colorBits.blueBits,      0, 0xff) &&
339                           de::inBounds(colorBits.alphaBits,     0, 0xff));
340
341 #define PACK_FMT(R, G, B, A) (((R) << 24) | ((G) << 16) | ((B) << 8) | (A))
342
343         // \note [pyry] This may not hold true on some implementations - best effort guess only.
344         switch (PACK_FMT(colorBits.redBits, colorBits.greenBits, colorBits.blueBits, colorBits.alphaBits))
345         {
346                 case PACK_FMT(8,8,8,8):         return TextureFormat(TextureFormat::RGBA,       TextureFormat::UNORM_INT8);
347                 case PACK_FMT(8,8,8,0):         return TextureFormat(TextureFormat::RGB,        TextureFormat::UNORM_INT8);
348                 case PACK_FMT(4,4,4,4):         return TextureFormat(TextureFormat::RGBA,       TextureFormat::UNORM_SHORT_4444);
349                 case PACK_FMT(5,5,5,1):         return TextureFormat(TextureFormat::RGBA,       TextureFormat::UNORM_SHORT_5551);
350                 case PACK_FMT(5,6,5,0):         return TextureFormat(TextureFormat::RGB,        TextureFormat::UNORM_SHORT_565);
351
352                 // \note Defaults to RGBA8
353                 default:                                        return TextureFormat(TextureFormat::RGBA,       TextureFormat::UNORM_INT8);
354         }
355
356 #undef PACK_FMT
357 }
358
359 tcu::TextureFormat getDepthFormat (const int depthBits)
360 {
361         switch (depthBits)
362         {
363                 case 0:         return tcu::TextureFormat();
364                 case 8:         return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT8);
365                 case 16:        return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT16);
366                 case 24:        return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT24);
367                 case 32:
368                 default:        return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT);
369         }
370 }
371
372 tcu::TextureFormat getStencilFormat (int stencilBits)
373 {
374         switch (stencilBits)
375         {
376                 case 0:         return tcu::TextureFormat();
377                 case 8:
378                 default:        return tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT8);
379         }
380 }
381
382 void renderReference (const tcu::PixelBufferAccess& dst, const vector<DrawPrimitiveOp>& drawOps, const tcu::PixelFormat& colorBits, const int depthBits, const int stencilBits, const int numSamples)
383 {
384         const int                                               width                   = dst.getWidth();
385         const int                                               height                  = dst.getHeight();
386
387         tcu::TextureLevel                               colorBuffer;
388         tcu::TextureLevel                               depthBuffer;
389         tcu::TextureLevel                               stencilBuffer;
390
391         rr::Renderer                                    referenceRenderer;
392         rr::VertexAttrib                                attributes[2];
393         const ReferenceShader                   shader;
394
395         attributes[0].type                              = rr::VERTEXATTRIBTYPE_FLOAT;
396         attributes[0].size                              = 4;
397         attributes[0].stride                    = 0;
398         attributes[0].instanceDivisor   = 0;
399
400         attributes[1].type                              = rr::VERTEXATTRIBTYPE_FLOAT;
401         attributes[1].size                              = 4;
402         attributes[1].stride                    = 0;
403         attributes[1].instanceDivisor   = 0;
404
405         // Initialize buffers.
406         colorBuffer.setStorage(getColorFormat(colorBits), numSamples, width, height);
407         rr::clearMultisampleColorBuffer(colorBuffer, CLEAR_COLOR, rr::WindowRectangle(0, 0, width, height));
408
409         if (depthBits > 0)
410         {
411                 depthBuffer.setStorage(getDepthFormat(depthBits), numSamples, width, height);
412                 rr::clearMultisampleDepthBuffer(depthBuffer, CLEAR_DEPTH, rr::WindowRectangle(0, 0, width, height));
413         }
414
415         if (stencilBits > 0)
416         {
417                 stencilBuffer.setStorage(getStencilFormat(stencilBits), numSamples, width, height);
418                 rr::clearMultisampleStencilBuffer(stencilBuffer, CLEAR_STENCIL, rr::WindowRectangle(0, 0, width, height));
419         }
420
421         const rr::RenderTarget renderTarget(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()),
422                                                                                 rr::MultisamplePixelBufferAccess::fromMultisampleAccess(depthBuffer.getAccess()),
423                                                                                 rr::MultisamplePixelBufferAccess::fromMultisampleAccess(stencilBuffer.getAccess()));
424
425         for (vector<DrawPrimitiveOp>::const_iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); drawOp++)
426         {
427                 // Translate state
428                 rr::RenderState renderState((rr::ViewportState)(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess())));
429                 toReferenceRenderState(renderState, *drawOp);
430
431                 DE_ASSERT(drawOp->type == PRIMITIVETYPE_TRIANGLE);
432
433                 attributes[0].pointer = &drawOp->positions[0];
434                 attributes[1].pointer = &drawOp->colors[0];
435
436                 referenceRenderer.draw(
437                         rr::DrawCommand(
438                                 renderState,
439                                 renderTarget,
440                                 rr::Program(static_cast<const rr::VertexShader*>(&shader), static_cast<const rr::FragmentShader*>(&shader)),
441                                 2,
442                                 attributes,
443                                 rr::PrimitiveList(rr::PRIMITIVETYPE_TRIANGLES, drawOp->count * 3, 0)));
444         }
445
446         rr::resolveMultisampleColorBuffer(dst, rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()));
447 }
448
449 // API rendering code
450
451 class Program
452 {
453 public:
454                                         Program                         (void) {}
455         virtual                 ~Program                        (void) {}
456
457         virtual void    setup                           (void) const = DE_NULL;
458 };
459
460 typedef de::SharedPtr<Program> ProgramSp;
461
462 static glu::ProgramSources getProgramSourcesES2 (void)
463 {
464         static const char* s_vertexSrc =
465                 "attribute highp vec4 a_position;\n"
466                 "attribute mediump vec4 a_color;\n"
467                 "varying mediump vec4 v_color;\n"
468                 "void main (void)\n"
469                 "{\n"
470                 "       gl_Position = a_position;\n"
471                 "       v_color = a_color;\n"
472                 "}\n";
473
474         static const char* s_fragmentSrc =
475                 "varying mediump vec4 v_color;\n"
476                 "void main (void)\n"
477                 "{\n"
478                 "       gl_FragColor = v_color;\n"
479                 "}\n";
480
481         return glu::ProgramSources() << glu::VertexSource(s_vertexSrc) << glu::FragmentSource(s_fragmentSrc);
482 }
483
484 class GLES2Program : public Program
485 {
486 public:
487         GLES2Program (const glw::Functions& gl)
488                 : m_gl                          (gl)
489                 , m_program                     (gl, getProgramSourcesES2())
490                 , m_positionLoc         (0)
491                 , m_colorLoc            (0)
492         {
493
494                 m_positionLoc   = m_gl.getAttribLocation(m_program.getProgram(), "a_position");
495                 m_colorLoc              = m_gl.getAttribLocation(m_program.getProgram(), "a_color");
496         }
497
498         ~GLES2Program (void)
499         {
500         }
501
502         void setup (void) const
503         {
504                 m_gl.useProgram(m_program.getProgram());
505                 m_gl.enableVertexAttribArray(m_positionLoc);
506                 m_gl.enableVertexAttribArray(m_colorLoc);
507                 GLU_CHECK_GLW_MSG(m_gl, "Program setup failed");
508         }
509
510         int                                             getPositionLoc          (void) const { return m_positionLoc;    }
511         int                                             getColorLoc                     (void) const { return m_colorLoc;               }
512
513 private:
514         const glw::Functions&   m_gl;
515         glu::ShaderProgram              m_program;
516         int                                             m_positionLoc;
517         int                                             m_colorLoc;
518 };
519
520 void clearGLES2 (const glw::Functions& gl, const tcu::Vec4& color, const float depth, const int stencil)
521 {
522         gl.clearColor(color.x(), color.y(), color.z(), color.w());
523         gl.clearDepthf(depth);
524         gl.clearStencil(stencil);
525         gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
526 }
527
528 void drawGLES2 (const glw::Functions& gl, const Program& program, const DrawPrimitiveOp& drawOp)
529 {
530         const GLES2Program& gles2Program = dynamic_cast<const GLES2Program&>(program);
531
532         switch (drawOp.blend)
533         {
534                 case BLENDMODE_NONE:
535                         gl.disable(GL_BLEND);
536                         break;
537
538                 case BLENDMODE_ADDITIVE:
539                         gl.enable(GL_BLEND);
540                         gl.blendFunc(GL_ONE, GL_ONE);
541                         break;
542
543                 case BLENDMODE_SRC_OVER:
544                         gl.enable(GL_BLEND);
545                         gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
546                         break;
547
548                 default:
549                         DE_ASSERT(false);
550         }
551
552         switch (drawOp.depth)
553         {
554                 case DEPTHMODE_NONE:
555                         gl.disable(GL_DEPTH_TEST);
556                         break;
557
558                 case DEPTHMODE_LESS:
559                         gl.enable(GL_DEPTH_TEST);
560                         break;
561
562                 default:
563                         DE_ASSERT(false);
564         }
565
566         switch (drawOp.stencil)
567         {
568                 case STENCILMODE_NONE:
569                         gl.disable(GL_STENCIL_TEST);
570                         break;
571
572                 case STENCILMODE_LEQUAL_INC:
573                         gl.enable(GL_STENCIL_TEST);
574                         gl.stencilFunc(GL_LEQUAL, drawOp.stencilRef, ~0u);
575                         gl.stencilOp(GL_KEEP, GL_INCR, GL_INCR);
576                         break;
577
578                 default:
579                         DE_ASSERT(false);
580         }
581
582         gl.disable(GL_DITHER);
583
584         gl.vertexAttribPointer(gles2Program.getPositionLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.positions[0]);
585         gl.vertexAttribPointer(gles2Program.getColorLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.colors[0]);
586
587         DE_ASSERT(drawOp.type == PRIMITIVETYPE_TRIANGLE);
588         gl.drawArrays(GL_TRIANGLES, 0, drawOp.count*3);
589 }
590
591 static void readPixelsGLES2 (const glw::Functions& gl, tcu::Surface& dst)
592 {
593         gl.readPixels(0, 0, dst.getWidth(), dst.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, dst.getAccess().getDataPtr());
594 }
595
596 Program* createProgram (const glw::Functions& gl, EGLint api)
597 {
598         switch (api)
599         {
600                 case EGL_OPENGL_ES2_BIT:                return new GLES2Program(gl);
601                 case EGL_OPENGL_ES3_BIT_KHR:    return new GLES2Program(gl);
602                 default:
603                         throw tcu::NotSupportedError("Unsupported API");
604         }
605 }
606
607 void draw (const glw::Functions& gl, EGLint api, const Program& program, const DrawPrimitiveOp& drawOp)
608 {
609         switch (api)
610         {
611                 case EGL_OPENGL_ES2_BIT:                drawGLES2(gl, program, drawOp);         break;
612                 case EGL_OPENGL_ES3_BIT_KHR:    drawGLES2(gl, program, drawOp);         break;
613                 default:
614                         throw tcu::NotSupportedError("Unsupported API");
615         }
616 }
617
618 void clear (const glw::Functions& gl, EGLint api, const tcu::Vec4& color, const float depth, const int stencil)
619 {
620         switch (api)
621         {
622                 case EGL_OPENGL_ES2_BIT:                clearGLES2(gl, color, depth, stencil);          break;
623                 case EGL_OPENGL_ES3_BIT_KHR:    clearGLES2(gl, color, depth, stencil);          break;
624                 default:
625                         throw tcu::NotSupportedError("Unsupported API");
626         }
627 }
628
629 static void readPixels (const glw::Functions& gl, EGLint api, tcu::Surface& dst)
630 {
631         switch (api)
632         {
633                 case EGL_OPENGL_ES2_BIT:                readPixelsGLES2(gl, dst);               break;
634                 case EGL_OPENGL_ES3_BIT_KHR:    readPixelsGLES2(gl, dst);               break;
635                 default:
636                         throw tcu::NotSupportedError("Unsupported API");
637         }
638 }
639
640 static void finish (const glw::Functions& gl, EGLint api)
641 {
642         switch (api)
643         {
644                 case EGL_OPENGL_ES2_BIT:
645                 case EGL_OPENGL_ES3_BIT_KHR:
646                         gl.finish();
647                         break;
648
649                 default:
650                         throw tcu::NotSupportedError("Unsupported API");
651         }
652 }
653
654 tcu::PixelFormat getPixelFormat (const Library& egl, EGLDisplay display, EGLConfig config)
655 {
656         tcu::PixelFormat fmt;
657         fmt.redBits             = eglu::getConfigAttribInt(egl, display, config, EGL_RED_SIZE);
658         fmt.greenBits   = eglu::getConfigAttribInt(egl, display, config, EGL_GREEN_SIZE);
659         fmt.blueBits    = eglu::getConfigAttribInt(egl, display, config, EGL_BLUE_SIZE);
660         fmt.alphaBits   = eglu::getConfigAttribInt(egl, display, config, EGL_ALPHA_SIZE);
661         return fmt;
662 }
663
664 } // anonymous
665
666 // SingleThreadRenderCase
667
668 class SingleThreadRenderCase : public MultiContextRenderCase
669 {
670 public:
671                                                 SingleThreadRenderCase          (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi);
672
673         void                            init                                            (void);
674
675 private:
676         virtual void            executeForContexts                      (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts);
677
678         glw::Functions          m_gl;
679 };
680
681 // SingleThreadColorClearCase
682
683 SingleThreadRenderCase::SingleThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
684         : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
685 {
686 }
687
688 void SingleThreadRenderCase::init (void)
689 {
690         MultiContextRenderCase::init();
691         m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
692 }
693
694 void SingleThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
695 {
696         const Library&                  egl                     = m_eglTestCtx.getLibrary();
697         const int                               width           = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
698         const int                               height          = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
699         const int                               numContexts     = (int)contexts.size();
700         const int                               drawsPerCtx     = 2;
701         const int                               numIters        = 2;
702         const float                             threshold       = 0.02f;
703
704         const tcu::PixelFormat  pixelFmt        = getPixelFormat(egl, display, config.config);
705         const int                               depthBits       = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
706         const int                               stencilBits     = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
707         const int                               numSamples      = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
708
709         TestLog&                                log                     = m_testCtx.getLog();
710
711         tcu::Surface                    refFrame        (width, height);
712         tcu::Surface                    frame           (width, height);
713
714         de::Random                              rnd                     (deStringHash(getName()) ^ deInt32Hash(numContexts));
715         vector<ProgramSp>               programs        (contexts.size());
716         vector<DrawPrimitiveOp> drawOps;
717
718         // Log basic information about config.
719         log << TestLog::Message << "EGL_RED_SIZE = "            << pixelFmt.redBits << TestLog::EndMessage;
720         log << TestLog::Message << "EGL_GREEN_SIZE = "          << pixelFmt.greenBits << TestLog::EndMessage;
721         log << TestLog::Message << "EGL_BLUE_SIZE = "           << pixelFmt.blueBits << TestLog::EndMessage;
722         log << TestLog::Message << "EGL_ALPHA_SIZE = "          << pixelFmt.alphaBits << TestLog::EndMessage;
723         log << TestLog::Message << "EGL_DEPTH_SIZE = "          << depthBits << TestLog::EndMessage;
724         log << TestLog::Message << "EGL_STENCIL_SIZE = "        << stencilBits << TestLog::EndMessage;
725         log << TestLog::Message << "EGL_SAMPLES = "                     << numSamples << TestLog::EndMessage;
726
727         // Generate draw ops.
728         drawOps.resize(numContexts*drawsPerCtx*numIters);
729         for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
730                 randomizeDrawOp(rnd, *drawOp);
731
732         // Create and setup programs per context
733         for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
734         {
735                 EGLint          api                     = contexts[ctxNdx].first;
736                 EGLContext      context         = contexts[ctxNdx].second;
737
738                 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
739
740                 programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
741                 programs[ctxNdx]->setup();
742         }
743
744         // Clear to black using first context.
745         {
746                 EGLint          api                     = contexts[0].first;
747                 EGLContext      context         = contexts[0].second;
748
749                 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
750
751                 clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
752                 finish(m_gl, api);
753         }
754
755         // Render.
756         for (int iterNdx = 0; iterNdx < numIters; iterNdx++)
757         {
758                 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
759                 {
760                         EGLint          api                     = contexts[ctxNdx].first;
761                         EGLContext      context         = contexts[ctxNdx].second;
762
763                         EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
764
765                         for (int drawNdx = 0; drawNdx < drawsPerCtx; drawNdx++)
766                         {
767                                 const DrawPrimitiveOp& drawOp = drawOps[iterNdx*numContexts*drawsPerCtx + ctxNdx*drawsPerCtx + drawNdx];
768                                 draw(m_gl, api, *programs[ctxNdx], drawOp);
769                         }
770
771                         finish(m_gl, api);
772                 }
773         }
774
775         // Read pixels using first context. \todo [pyry] Randomize?
776         {
777                 EGLint          api             = contexts[0].first;
778                 EGLContext      context = contexts[0].second;
779
780                 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
781
782                 readPixels(m_gl, api, frame);
783         }
784
785         EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
786
787         // Render reference.
788         // \note Reference image is always generated using single-sampling.
789         renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1);
790
791         // Compare images
792         {
793                 bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
794
795                 if (!imagesOk)
796                         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
797         }
798 }
799
800 // MultiThreadRenderCase
801
802 class MultiThreadRenderCase : public MultiContextRenderCase
803 {
804 public:
805                                                 MultiThreadRenderCase           (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi);
806
807         void                            init                                            (void);
808
809 private:
810         virtual void            executeForContexts                      (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts);
811
812         glw::Functions          m_gl;
813 };
814
815 class RenderTestThread;
816
817 typedef de::SharedPtr<RenderTestThread> RenderTestThreadSp;
818 typedef de::SharedPtr<de::Semaphore>    SemaphoreSp;
819
820 struct DrawOpPacket
821 {
822         DrawOpPacket (void)
823                 : drawOps       (DE_NULL)
824                 , numOps        (0)
825         {
826         }
827
828         const DrawPrimitiveOp*  drawOps;
829         int                                             numOps;
830         SemaphoreSp                             wait;
831         SemaphoreSp                             signal;
832 };
833
834 class RenderTestThread : public de::Thread
835 {
836 public:
837         RenderTestThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const glw::Functions& gl, const Program& program, const std::vector<DrawOpPacket>& packets)
838                 : m_egl         (egl)
839                 , m_display     (display)
840                 , m_surface     (surface)
841                 , m_context     (context)
842                 , m_api         (api)
843                 , m_gl          (gl)
844                 , m_program     (program)
845                 , m_packets     (packets)
846         {
847         }
848
849         void run (void)
850         {
851                 for (std::vector<DrawOpPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++)
852                 {
853                         // Wait until it is our turn.
854                         packetIter->wait->decrement();
855
856                         // Acquire context.
857                         EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, m_surface, m_surface, m_context));
858
859                         // Execute rendering.
860                         for (int ndx = 0; ndx < packetIter->numOps; ndx++)
861                                 draw(m_gl, m_api, m_program, packetIter->drawOps[ndx]);
862
863                         finish(m_gl, m_api);
864
865                         // Release context.
866                         EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
867
868                         // Signal completion.
869                         packetIter->signal->increment();
870                 }
871         }
872
873 private:
874         const Library&                                          m_egl;
875         EGLDisplay                                                      m_display;
876         EGLSurface                                                      m_surface;
877         EGLContext                                                      m_context;
878         EGLint                                                          m_api;
879         const glw::Functions&                           m_gl;
880         const Program&                                          m_program;
881         const std::vector<DrawOpPacket>&        m_packets;
882 };
883
884 MultiThreadRenderCase::MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
885         : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
886 {
887 }
888
889 void MultiThreadRenderCase::init (void)
890 {
891         MultiContextRenderCase::init();
892         m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
893 }
894
895 void MultiThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
896 {
897         const Library&                  egl                                     = m_eglTestCtx.getLibrary();
898         const int                               width                           = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
899         const int                               height                          = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
900         const int                               numContexts                     = (int)contexts.size();
901         const int                               opsPerPacket            = 2;
902         const int                               packetsPerThread        = 2;
903         const int                               numThreads                      = numContexts;
904         const int                               numPackets                      = numThreads * packetsPerThread;
905         const float                             threshold                       = 0.02f;
906
907         const tcu::PixelFormat  pixelFmt                        = getPixelFormat(egl, display, config.config);
908         const int                               depthBits                       = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
909         const int                               stencilBits                     = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
910         const int                               numSamples                      = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
911
912         TestLog&                                log                                     = m_testCtx.getLog();
913
914         tcu::Surface                    refFrame                        (width, height);
915         tcu::Surface                    frame                           (width, height);
916
917         de::Random                              rnd                                     (deStringHash(getName()) ^ deInt32Hash(numContexts));
918
919         // Resources that need cleanup
920         vector<ProgramSp>                               programs        (numContexts);
921         vector<SemaphoreSp>                             semaphores      (numPackets+1);
922         vector<DrawPrimitiveOp>                 drawOps         (numPackets*opsPerPacket);
923         vector<vector<DrawOpPacket> >   packets         (numThreads);
924         vector<RenderTestThreadSp>              threads         (numThreads);
925
926         // Log basic information about config.
927         log << TestLog::Message << "EGL_RED_SIZE = "            << pixelFmt.redBits << TestLog::EndMessage;
928         log << TestLog::Message << "EGL_GREEN_SIZE = "          << pixelFmt.greenBits << TestLog::EndMessage;
929         log << TestLog::Message << "EGL_BLUE_SIZE = "           << pixelFmt.blueBits << TestLog::EndMessage;
930         log << TestLog::Message << "EGL_ALPHA_SIZE = "          << pixelFmt.alphaBits << TestLog::EndMessage;
931         log << TestLog::Message << "EGL_DEPTH_SIZE = "          << depthBits << TestLog::EndMessage;
932         log << TestLog::Message << "EGL_STENCIL_SIZE = "        << stencilBits << TestLog::EndMessage;
933         log << TestLog::Message << "EGL_SAMPLES = "                     << numSamples << TestLog::EndMessage;
934
935         // Initialize semaphores.
936         for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
937                 *sem = SemaphoreSp(new de::Semaphore(0));
938
939         // Create draw ops.
940         for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
941                 randomizeDrawOp(rnd, *drawOp);
942
943         // Create packets.
944         for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
945         {
946                 packets[threadNdx].resize(packetsPerThread);
947
948                 for (int packetNdx = 0; packetNdx < packetsPerThread; packetNdx++)
949                 {
950                         DrawOpPacket& packet = packets[threadNdx][packetNdx];
951
952                         // Threads take turns with packets.
953                         packet.wait             = semaphores[packetNdx*numThreads + threadNdx];
954                         packet.signal   = semaphores[packetNdx*numThreads + threadNdx + 1];
955                         packet.numOps   = opsPerPacket;
956                         packet.drawOps  = &drawOps[(packetNdx*numThreads + threadNdx)*opsPerPacket];
957                 }
958         }
959
960         // Create and setup programs per context
961         for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
962         {
963                 EGLint          api                     = contexts[ctxNdx].first;
964                 EGLContext      context         = contexts[ctxNdx].second;
965
966                 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
967
968                 programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
969                 programs[ctxNdx]->setup();
970
971                 // Release context
972                 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
973         }
974
975         // Clear to black using first context.
976         {
977                 EGLint          api                     = contexts[0].first;
978                 EGLContext      context         = contexts[0].second;
979
980                 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
981
982                 clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
983                 finish(m_gl, api);
984
985                 // Release context
986                 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
987         }
988
989         // Create and launch threads (actual rendering starts once first semaphore is signaled).
990         for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
991         {
992                 threads[threadNdx] = RenderTestThreadSp(new RenderTestThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, m_gl, *programs[threadNdx], packets[threadNdx]));
993                 threads[threadNdx]->start();
994         }
995
996         // Signal start and wait until complete.
997         semaphores.front()->increment();
998         semaphores.back()->decrement();
999
1000         // Read pixels using first context. \todo [pyry] Randomize?
1001         {
1002                 EGLint          api             = contexts[0].first;
1003                 EGLContext      context = contexts[0].second;
1004
1005                 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1006
1007                 readPixels(m_gl, api, frame);
1008         }
1009
1010         EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1011
1012         // Join threads.
1013         for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
1014                 threads[threadNdx]->join();
1015
1016         // Render reference.
1017         renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1);
1018
1019         // Compare images
1020         {
1021                 bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
1022
1023                 if (!imagesOk)
1024                         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
1025         }
1026 }
1027
1028 RenderTests::RenderTests (EglTestContext& eglTestCtx)
1029         : TestCaseGroup(eglTestCtx, "render", "Basic rendering with different client APIs")
1030 {
1031 }
1032
1033 RenderTests::~RenderTests (void)
1034 {
1035 }
1036
1037 struct RenderGroupSpec
1038 {
1039         const char*                     name;
1040         const char*                     desc;
1041         EGLint                          apiBits;
1042         eglu::ConfigFilter      baseFilter;
1043         int                                     numContextsPerApi;
1044 };
1045
1046 template <deUint32 Bits>
1047 static bool renderable (const eglu::CandidateConfig& c)
1048 {
1049         return (c.renderableType() & Bits) == Bits;
1050 }
1051
1052 template <class RenderClass>
1053 static void createRenderGroups (EglTestContext& eglTestCtx, tcu::TestCaseGroup* group, const RenderGroupSpec* first, const RenderGroupSpec* last)
1054 {
1055         for (const RenderGroupSpec* groupIter = first; groupIter != last; groupIter++)
1056         {
1057                 tcu::TestCaseGroup* configGroup = new tcu::TestCaseGroup(eglTestCtx.getTestContext(), groupIter->name, groupIter->desc);
1058                 group->addChild(configGroup);
1059
1060                 vector<RenderFilterList>        filterLists;
1061                 eglu::FilterList                        baseFilters;
1062                 baseFilters << groupIter->baseFilter;
1063                 getDefaultRenderFilterLists(filterLists, baseFilters);
1064
1065                 for (vector<RenderFilterList>::const_iterator listIter = filterLists.begin(); listIter != filterLists.end(); listIter++)
1066                         configGroup->addChild(new RenderClass(eglTestCtx, listIter->getName(), "", groupIter->apiBits, listIter->getSurfaceTypeMask(), *listIter, groupIter->numContextsPerApi));
1067         }
1068 }
1069
1070 void RenderTests::init (void)
1071 {
1072         static const RenderGroupSpec singleContextCases[] =
1073         {
1074                 {
1075                         "gles2",
1076                         "Primitive rendering using GLES2",
1077                         EGL_OPENGL_ES2_BIT,
1078                         renderable<EGL_OPENGL_ES2_BIT>,
1079                         1
1080                 },
1081                 {
1082                         "gles3",
1083                         "Primitive rendering using GLES3",
1084                         EGL_OPENGL_ES3_BIT,
1085                         renderable<EGL_OPENGL_ES3_BIT>,
1086                         1
1087                 },
1088         };
1089
1090         static const RenderGroupSpec multiContextCases[] =
1091         {
1092                 {
1093                         "gles2",
1094                         "Primitive rendering using multiple GLES2 contexts to shared surface",
1095                         EGL_OPENGL_ES2_BIT,
1096                         renderable<EGL_OPENGL_ES2_BIT>,
1097                         3
1098                 },
1099                 {
1100                         "gles3",
1101                         "Primitive rendering using multiple GLES3 contexts to shared surface",
1102                         EGL_OPENGL_ES3_BIT,
1103                         renderable<EGL_OPENGL_ES3_BIT>,
1104                         3
1105                 },
1106                 {
1107                         "gles2_gles3",
1108                         "Primitive rendering using multiple APIs to shared surface",
1109                         EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT,
1110                         renderable<EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT>,
1111                         1
1112                 },
1113         };
1114
1115         tcu::TestCaseGroup* singleContextGroup = new tcu::TestCaseGroup(m_testCtx, "single_context", "Single-context rendering");
1116         addChild(singleContextGroup);
1117         createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, singleContextGroup, &singleContextCases[0], &singleContextCases[DE_LENGTH_OF_ARRAY(singleContextCases)]);
1118
1119         tcu::TestCaseGroup* multiContextGroup = new tcu::TestCaseGroup(m_testCtx, "multi_context", "Multi-context rendering with shared surface");
1120         addChild(multiContextGroup);
1121         createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, multiContextGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);
1122
1123         tcu::TestCaseGroup* multiThreadGroup = new tcu::TestCaseGroup(m_testCtx, "multi_thread", "Multi-thread rendering with shared surface");
1124         addChild(multiThreadGroup);
1125         createRenderGroups<MultiThreadRenderCase>(m_eglTestCtx, multiThreadGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);
1126 }
1127
1128 } // egl
1129 } // deqp