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