xref: /aosp_15_r20/external/deqp/modules/egl/teglRenderTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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::set;
65 using std::string;
66 using std::vector;
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 
isANarrowScreenSpaceTriangle(const tcu::Vec4 & p0,const tcu::Vec4 & p1,const tcu::Vec4 & p2)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 
randomizeDrawOp(de::Random & rnd,DrawPrimitiveOp & drawOp,const bool alphaZeroOrOne)144 void randomizeDrawOp(de::Random &rnd, DrawPrimitiveOp &drawOp, const bool alphaZeroOrOne)
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             const float flatAlpha = (rnd.getFloat(minAlpha, maxAlpha) > 0.5f) ? 1.0f : 0.0f;
177 
178             for (int coordNdx = 0; coordNdx < 3; coordNdx++)
179             {
180                 tcu::Vec4 &position = drawOp.positions[triNdx * 3 + coordNdx];
181                 tcu::Vec4 &color    = drawOp.colors[triNdx * 3 + coordNdx];
182 
183                 position.x() = cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
184                 position.y() = cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
185                 position.z() = rnd.getFloat(minDepth, maxDepth);
186                 position.w() = 1.0f;
187 
188                 color.x() = rnd.getFloat(minRGB, maxRGB);
189                 color.y() = rnd.getFloat(minRGB, maxRGB);
190                 color.z() = rnd.getFloat(minRGB, maxRGB);
191                 color.w() = rnd.getFloat(minAlpha, maxAlpha);
192 
193                 if (alphaZeroOrOne)
194                 {
195                     color.w() = flatAlpha;
196                 }
197             }
198 
199             // avoid generating narrow triangles
200             {
201                 const int maxAttempts = 100;
202                 int numAttempts       = 0;
203                 tcu::Vec4 &p0         = drawOp.positions[triNdx * 3 + 0];
204                 tcu::Vec4 &p1         = drawOp.positions[triNdx * 3 + 1];
205                 tcu::Vec4 &p2         = drawOp.positions[triNdx * 3 + 2];
206 
207                 while (isANarrowScreenSpaceTriangle(p0, p1, p2))
208                 {
209                     p1.x() = cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
210                     p1.y() = cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
211                     p1.z() = rnd.getFloat(minDepth, maxDepth);
212                     p1.w() = 1.0f;
213 
214                     p2.x() = cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
215                     p2.y() = cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
216                     p2.z() = rnd.getFloat(minDepth, maxDepth);
217                     p2.w() = 1.0f;
218 
219                     if (++numAttempts > maxAttempts)
220                     {
221                         DE_ASSERT(false);
222                         break;
223                     }
224                 }
225             }
226         }
227     }
228     else
229         DE_ASSERT(false);
230 }
231 
232 // Reference rendering code
233 
234 class ReferenceShader : public rr::VertexShader, public rr::FragmentShader
235 {
236 public:
237     enum
238     {
239         VaryingLoc_Color = 0
240     };
241 
ReferenceShader()242     ReferenceShader()
243         : rr::VertexShader(2, 1)   // color and pos in => color out
244         , rr::FragmentShader(1, 1) // color in => color out
245     {
246         this->rr::VertexShader::m_inputs[0].type = rr::GENERICVECTYPE_FLOAT;
247         this->rr::VertexShader::m_inputs[1].type = rr::GENERICVECTYPE_FLOAT;
248 
249         this->rr::VertexShader::m_outputs[0].type      = rr::GENERICVECTYPE_FLOAT;
250         this->rr::VertexShader::m_outputs[0].flatshade = false;
251 
252         this->rr::FragmentShader::m_inputs[0].type      = rr::GENERICVECTYPE_FLOAT;
253         this->rr::FragmentShader::m_inputs[0].flatshade = false;
254 
255         this->rr::FragmentShader::m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
256     }
257 
shadeVertices(const rr::VertexAttrib * inputs,rr::VertexPacket * const * packets,const int numPackets) const258     void shadeVertices(const rr::VertexAttrib *inputs, rr::VertexPacket *const *packets, const int numPackets) const
259     {
260         for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
261         {
262             const int positionAttrLoc = 0;
263             const int colorAttrLoc    = 1;
264 
265             rr::VertexPacket &packet = *packets[packetNdx];
266 
267             // Transform to position
268             packet.position = rr::readVertexAttribFloat(inputs[positionAttrLoc], packet.instanceNdx, packet.vertexNdx);
269 
270             // Pass color to FS
271             packet.outputs[VaryingLoc_Color] =
272                 rr::readVertexAttribFloat(inputs[colorAttrLoc], packet.instanceNdx, packet.vertexNdx);
273         }
274     }
275 
shadeFragments(rr::FragmentPacket * packets,const int numPackets,const rr::FragmentShadingContext & context) const276     void shadeFragments(rr::FragmentPacket *packets, const int numPackets,
277                         const rr::FragmentShadingContext &context) const
278     {
279         for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
280         {
281             rr::FragmentPacket &packet = packets[packetNdx];
282 
283             for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
284                 rr::writeFragmentOutput(context, packetNdx, fragNdx, 0,
285                                         rr::readVarying<float>(packet, context, VaryingLoc_Color, fragNdx));
286         }
287     }
288 };
289 
toReferenceRenderState(rr::RenderState & state,const DrawPrimitiveOp & drawOp)290 void toReferenceRenderState(rr::RenderState &state, const DrawPrimitiveOp &drawOp)
291 {
292     state.cullMode = rr::CULLMODE_NONE;
293 
294     if (drawOp.blend != BLENDMODE_NONE)
295     {
296         state.fragOps.blendMode = rr::BLENDMODE_STANDARD;
297 
298         switch (drawOp.blend)
299         {
300         case BLENDMODE_ADDITIVE:
301             state.fragOps.blendRGBState.srcFunc  = rr::BLENDFUNC_ONE;
302             state.fragOps.blendRGBState.dstFunc  = rr::BLENDFUNC_ONE;
303             state.fragOps.blendRGBState.equation = rr::BLENDEQUATION_ADD;
304             state.fragOps.blendAState            = state.fragOps.blendRGBState;
305             break;
306 
307         case BLENDMODE_SRC_OVER:
308             state.fragOps.blendRGBState.srcFunc  = rr::BLENDFUNC_SRC_ALPHA;
309             state.fragOps.blendRGBState.dstFunc  = rr::BLENDFUNC_ONE_MINUS_SRC_ALPHA;
310             state.fragOps.blendRGBState.equation = rr::BLENDEQUATION_ADD;
311             state.fragOps.blendAState            = state.fragOps.blendRGBState;
312             break;
313 
314         default:
315             DE_ASSERT(false);
316         }
317     }
318 
319     if (drawOp.depth != DEPTHMODE_NONE)
320     {
321         state.fragOps.depthTestEnabled = true;
322 
323         DE_ASSERT(drawOp.depth == DEPTHMODE_LESS);
324         state.fragOps.depthFunc = rr::TESTFUNC_LESS;
325     }
326 
327     if (drawOp.stencil != STENCILMODE_NONE)
328     {
329         state.fragOps.stencilTestEnabled = true;
330 
331         DE_ASSERT(drawOp.stencil == STENCILMODE_LEQUAL_INC);
332         state.fragOps.stencilStates[0].func   = rr::TESTFUNC_LEQUAL;
333         state.fragOps.stencilStates[0].sFail  = rr::STENCILOP_KEEP;
334         state.fragOps.stencilStates[0].dpFail = rr::STENCILOP_INCR;
335         state.fragOps.stencilStates[0].dpPass = rr::STENCILOP_INCR;
336         state.fragOps.stencilStates[0].ref    = drawOp.stencilRef;
337         state.fragOps.stencilStates[1]        = state.fragOps.stencilStates[0];
338     }
339 }
340 
getColorFormat(const tcu::PixelFormat & colorBits)341 tcu::TextureFormat getColorFormat(const tcu::PixelFormat &colorBits)
342 {
343     using tcu::TextureFormat;
344 
345     DE_ASSERT(de::inBounds(colorBits.redBits, 0, 0xff) && de::inBounds(colorBits.greenBits, 0, 0xff) &&
346               de::inBounds(colorBits.blueBits, 0, 0xff) && de::inBounds(colorBits.alphaBits, 0, 0xff));
347 
348 #define PACK_FMT(R, G, B, A) (((R) << 24) | ((G) << 16) | ((B) << 8) | (A))
349 
350     // \note [pyry] This may not hold true on some implementations - best effort guess only.
351     switch (PACK_FMT(colorBits.redBits, colorBits.greenBits, colorBits.blueBits, colorBits.alphaBits))
352     {
353     case PACK_FMT(8, 8, 8, 8):
354         return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8);
355     case PACK_FMT(8, 8, 8, 0):
356         return TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8);
357     case PACK_FMT(4, 4, 4, 4):
358         return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_SHORT_4444);
359     case PACK_FMT(5, 5, 5, 1):
360         return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_SHORT_5551);
361     case PACK_FMT(5, 6, 5, 0):
362         return TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_SHORT_565);
363     case PACK_FMT(10, 10, 10, 2):
364         return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT_1010102_REV);
365     case PACK_FMT(10, 10, 10, 0):
366         return TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT_101010);
367     // \note Defaults to RGBA8
368     default:
369         return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8);
370     }
371 
372 #undef PACK_FMT
373 }
374 
375 /*
376 The getColorThreshold function is used to obtain a
377 threshold usable for the fuzzyCompare function.
378 
379 For 8bit color depths a value of 0.02 should provide
380 a good metric for rejecting images above this level.
381 For other bit depths other thresholds should be selected.
382 Ideally this function would take advantage of the
383 getColorThreshold function provided by the PixelFormat class
384 as this would also allow setting per channel thresholds.
385 However using the PixelFormat provided function can result
386 in too strict thresholds for 8bit bit depths (compared to
387 the current default of 0.02) or too relaxed for lower bit
388 depths if scaled proportionally to the 8bit default.
389 */
390 
getColorThreshold(const tcu::PixelFormat & colorBits)391 float getColorThreshold(const tcu::PixelFormat &colorBits)
392 {
393     if ((colorBits.redBits > 0 && colorBits.redBits < 8) || (colorBits.greenBits > 0 && colorBits.greenBits < 8) ||
394         (colorBits.blueBits > 0 && colorBits.blueBits < 8) || (colorBits.alphaBits > 0 && colorBits.alphaBits < 8))
395     {
396         return 0.05f;
397     }
398     else
399     {
400         return 0.02f;
401     }
402 }
403 
getDepthFormat(const int depthBits)404 tcu::TextureFormat getDepthFormat(const int depthBits)
405 {
406     switch (depthBits)
407     {
408     case 0:
409         return tcu::TextureFormat();
410     case 8:
411         return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT8);
412     case 16:
413         return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT16);
414     case 24:
415         return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT24);
416     case 32:
417     default:
418         return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT);
419     }
420 }
421 
getStencilFormat(int stencilBits)422 tcu::TextureFormat getStencilFormat(int stencilBits)
423 {
424     switch (stencilBits)
425     {
426     case 0:
427         return tcu::TextureFormat();
428     case 8:
429     default:
430         return tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT8);
431     }
432 }
433 
renderReference(const tcu::PixelBufferAccess & dst,const vector<DrawPrimitiveOp> & drawOps,const tcu::PixelFormat & colorBits,const int depthBits,const int stencilBits,const int numSamples,const int subpixelBits)434 void renderReference(const tcu::PixelBufferAccess &dst, const vector<DrawPrimitiveOp> &drawOps,
435                      const tcu::PixelFormat &colorBits, const int depthBits, const int stencilBits,
436                      const int numSamples, const int subpixelBits)
437 {
438     const int width  = dst.getWidth();
439     const int height = dst.getHeight();
440 
441     tcu::TextureLevel colorBuffer;
442     tcu::TextureLevel depthBuffer;
443     tcu::TextureLevel stencilBuffer;
444 
445     rr::Renderer referenceRenderer;
446     rr::VertexAttrib attributes[2];
447     const ReferenceShader shader;
448 
449     attributes[0].type            = rr::VERTEXATTRIBTYPE_FLOAT;
450     attributes[0].size            = 4;
451     attributes[0].stride          = 0;
452     attributes[0].instanceDivisor = 0;
453 
454     attributes[1].type            = rr::VERTEXATTRIBTYPE_FLOAT;
455     attributes[1].size            = 4;
456     attributes[1].stride          = 0;
457     attributes[1].instanceDivisor = 0;
458 
459     // Initialize buffers.
460     colorBuffer.setStorage(getColorFormat(colorBits), numSamples, width, height);
461     rr::clearMultisampleColorBuffer(colorBuffer, CLEAR_COLOR, rr::WindowRectangle(0, 0, width, height));
462 
463     if (depthBits > 0)
464     {
465         depthBuffer.setStorage(getDepthFormat(depthBits), numSamples, width, height);
466         rr::clearMultisampleDepthBuffer(depthBuffer, CLEAR_DEPTH, rr::WindowRectangle(0, 0, width, height));
467     }
468 
469     if (stencilBits > 0)
470     {
471         stencilBuffer.setStorage(getStencilFormat(stencilBits), numSamples, width, height);
472         rr::clearMultisampleStencilBuffer(stencilBuffer, CLEAR_STENCIL, rr::WindowRectangle(0, 0, width, height));
473     }
474 
475     const rr::RenderTarget renderTarget(
476         rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()),
477         rr::MultisamplePixelBufferAccess::fromMultisampleAccess(depthBuffer.getAccess()),
478         rr::MultisamplePixelBufferAccess::fromMultisampleAccess(stencilBuffer.getAccess()));
479 
480     for (vector<DrawPrimitiveOp>::const_iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); drawOp++)
481     {
482         // Translate state
483         rr::RenderState renderState(
484             (rr::ViewportState)(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess())),
485             subpixelBits);
486         toReferenceRenderState(renderState, *drawOp);
487 
488         DE_ASSERT(drawOp->type == PRIMITIVETYPE_TRIANGLE);
489 
490         attributes[0].pointer = &drawOp->positions[0];
491         attributes[1].pointer = &drawOp->colors[0];
492 
493         referenceRenderer.draw(rr::DrawCommand(renderState, renderTarget,
494                                                rr::Program(static_cast<const rr::VertexShader *>(&shader),
495                                                            static_cast<const rr::FragmentShader *>(&shader)),
496                                                2, attributes,
497                                                rr::PrimitiveList(rr::PRIMITIVETYPE_TRIANGLES, drawOp->count * 3, 0)));
498     }
499 
500     rr::resolveMultisampleColorBuffer(dst,
501                                       rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()));
502 }
503 
504 // API rendering code
505 
506 class Program
507 {
508 public:
Program(void)509     Program(void)
510     {
511     }
~Program(void)512     virtual ~Program(void)
513     {
514     }
515 
516     virtual void setup(void) const = DE_NULL;
517 };
518 
519 typedef de::SharedPtr<Program> ProgramSp;
520 
getProgramSourcesES2(void)521 static glu::ProgramSources getProgramSourcesES2(void)
522 {
523     static const char *s_vertexSrc = "attribute highp vec4 a_position;\n"
524                                      "attribute mediump vec4 a_color;\n"
525                                      "varying mediump vec4 v_color;\n"
526                                      "void main (void)\n"
527                                      "{\n"
528                                      "    gl_Position = a_position;\n"
529                                      "    v_color = a_color;\n"
530                                      "}\n";
531 
532     static const char *s_fragmentSrc = "varying mediump vec4 v_color;\n"
533                                        "void main (void)\n"
534                                        "{\n"
535                                        "    gl_FragColor = v_color;\n"
536                                        "}\n";
537 
538     return glu::ProgramSources() << glu::VertexSource(s_vertexSrc) << glu::FragmentSource(s_fragmentSrc);
539 }
540 
541 class GLES2Program : public Program
542 {
543 public:
GLES2Program(const glw::Functions & gl)544     GLES2Program(const glw::Functions &gl)
545         : m_gl(gl)
546         , m_program(gl, getProgramSourcesES2())
547         , m_positionLoc(0)
548         , m_colorLoc(0)
549     {
550 
551         m_positionLoc = m_gl.getAttribLocation(m_program.getProgram(), "a_position");
552         m_colorLoc    = m_gl.getAttribLocation(m_program.getProgram(), "a_color");
553     }
554 
~GLES2Program(void)555     ~GLES2Program(void)
556     {
557     }
558 
setup(void) const559     void setup(void) const
560     {
561         m_gl.useProgram(m_program.getProgram());
562         m_gl.enableVertexAttribArray(m_positionLoc);
563         m_gl.enableVertexAttribArray(m_colorLoc);
564         GLU_CHECK_GLW_MSG(m_gl, "Program setup failed");
565     }
566 
getPositionLoc(void) const567     int getPositionLoc(void) const
568     {
569         return m_positionLoc;
570     }
getColorLoc(void) const571     int getColorLoc(void) const
572     {
573         return m_colorLoc;
574     }
575 
576 private:
577     const glw::Functions &m_gl;
578     glu::ShaderProgram m_program;
579     int m_positionLoc;
580     int m_colorLoc;
581 };
582 
clearGLES2(const glw::Functions & gl,const tcu::Vec4 & color,const float depth,const int stencil)583 void clearGLES2(const glw::Functions &gl, const tcu::Vec4 &color, const float depth, const int stencil)
584 {
585     gl.clearColor(color.x(), color.y(), color.z(), color.w());
586     gl.clearDepthf(depth);
587     gl.clearStencil(stencil);
588     gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
589 }
590 
drawGLES2(const glw::Functions & gl,const Program & program,const DrawPrimitiveOp & drawOp)591 void drawGLES2(const glw::Functions &gl, const Program &program, const DrawPrimitiveOp &drawOp)
592 {
593     const GLES2Program &gles2Program = dynamic_cast<const GLES2Program &>(program);
594 
595     switch (drawOp.blend)
596     {
597     case BLENDMODE_NONE:
598         gl.disable(GL_BLEND);
599         break;
600 
601     case BLENDMODE_ADDITIVE:
602         gl.enable(GL_BLEND);
603         gl.blendFunc(GL_ONE, GL_ONE);
604         break;
605 
606     case BLENDMODE_SRC_OVER:
607         gl.enable(GL_BLEND);
608         gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
609         break;
610 
611     default:
612         DE_ASSERT(false);
613     }
614 
615     switch (drawOp.depth)
616     {
617     case DEPTHMODE_NONE:
618         gl.disable(GL_DEPTH_TEST);
619         break;
620 
621     case DEPTHMODE_LESS:
622         gl.enable(GL_DEPTH_TEST);
623         break;
624 
625     default:
626         DE_ASSERT(false);
627     }
628 
629     switch (drawOp.stencil)
630     {
631     case STENCILMODE_NONE:
632         gl.disable(GL_STENCIL_TEST);
633         break;
634 
635     case STENCILMODE_LEQUAL_INC:
636         gl.enable(GL_STENCIL_TEST);
637         gl.stencilFunc(GL_LEQUAL, drawOp.stencilRef, ~0u);
638         gl.stencilOp(GL_KEEP, GL_INCR, GL_INCR);
639         break;
640 
641     default:
642         DE_ASSERT(false);
643     }
644 
645     gl.disable(GL_DITHER);
646 
647     gl.vertexAttribPointer(gles2Program.getPositionLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.positions[0]);
648     gl.vertexAttribPointer(gles2Program.getColorLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.colors[0]);
649 
650     DE_ASSERT(drawOp.type == PRIMITIVETYPE_TRIANGLE);
651     gl.drawArrays(GL_TRIANGLES, 0, drawOp.count * 3);
652 }
653 
readPixelsGLES2(const glw::Functions & gl,tcu::Surface & dst)654 static void readPixelsGLES2(const glw::Functions &gl, tcu::Surface &dst)
655 {
656     gl.readPixels(0, 0, dst.getWidth(), dst.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, dst.getAccess().getDataPtr());
657 }
658 
createProgram(const glw::Functions & gl,EGLint api)659 Program *createProgram(const glw::Functions &gl, EGLint api)
660 {
661     switch (api)
662     {
663     case EGL_OPENGL_ES2_BIT:
664         return new GLES2Program(gl);
665     case EGL_OPENGL_ES3_BIT_KHR:
666         return new GLES2Program(gl);
667     default:
668         throw tcu::NotSupportedError("Unsupported API");
669     }
670 }
671 
draw(const glw::Functions & gl,EGLint api,const Program & program,const DrawPrimitiveOp & drawOp)672 void draw(const glw::Functions &gl, EGLint api, const Program &program, const DrawPrimitiveOp &drawOp)
673 {
674     switch (api)
675     {
676     case EGL_OPENGL_ES2_BIT:
677         drawGLES2(gl, program, drawOp);
678         break;
679     case EGL_OPENGL_ES3_BIT_KHR:
680         drawGLES2(gl, program, drawOp);
681         break;
682     default:
683         throw tcu::NotSupportedError("Unsupported API");
684     }
685 }
686 
clear(const glw::Functions & gl,EGLint api,const tcu::Vec4 & color,const float depth,const int stencil)687 void clear(const glw::Functions &gl, EGLint api, const tcu::Vec4 &color, const float depth, const int stencil)
688 {
689     switch (api)
690     {
691     case EGL_OPENGL_ES2_BIT:
692         clearGLES2(gl, color, depth, stencil);
693         break;
694     case EGL_OPENGL_ES3_BIT_KHR:
695         clearGLES2(gl, color, depth, stencil);
696         break;
697     default:
698         throw tcu::NotSupportedError("Unsupported API");
699     }
700 }
701 
readPixels(const glw::Functions & gl,EGLint api,tcu::Surface & dst)702 static void readPixels(const glw::Functions &gl, EGLint api, tcu::Surface &dst)
703 {
704     switch (api)
705     {
706     case EGL_OPENGL_ES2_BIT:
707         readPixelsGLES2(gl, dst);
708         break;
709     case EGL_OPENGL_ES3_BIT_KHR:
710         readPixelsGLES2(gl, dst);
711         break;
712     default:
713         throw tcu::NotSupportedError("Unsupported API");
714     }
715 }
716 
finish(const glw::Functions & gl,EGLint api)717 static void finish(const glw::Functions &gl, EGLint api)
718 {
719     switch (api)
720     {
721     case EGL_OPENGL_ES2_BIT:
722     case EGL_OPENGL_ES3_BIT_KHR:
723         gl.finish();
724         break;
725 
726     default:
727         throw tcu::NotSupportedError("Unsupported API");
728     }
729 }
730 
getPixelFormat(const Library & egl,EGLDisplay display,EGLConfig config)731 tcu::PixelFormat getPixelFormat(const Library &egl, EGLDisplay display, EGLConfig config)
732 {
733     tcu::PixelFormat fmt;
734     fmt.redBits   = eglu::getConfigAttribInt(egl, display, config, EGL_RED_SIZE);
735     fmt.greenBits = eglu::getConfigAttribInt(egl, display, config, EGL_GREEN_SIZE);
736     fmt.blueBits  = eglu::getConfigAttribInt(egl, display, config, EGL_BLUE_SIZE);
737     fmt.alphaBits = eglu::getConfigAttribInt(egl, display, config, EGL_ALPHA_SIZE);
738     return fmt;
739 }
740 
741 } // namespace
742 
743 // SingleThreadRenderCase
744 
745 class SingleThreadRenderCase : public MultiContextRenderCase
746 {
747 public:
748     SingleThreadRenderCase(EglTestContext &eglTestCtx, const char *name, const char *description, EGLint api,
749                            EGLint surfaceType, const eglu::FilterList &filters, int numContextsPerApi);
750 
751     void init(void);
752 
753 private:
754     virtual void executeForContexts(EGLDisplay display, EGLSurface surface, const Config &config,
755                                     const std::vector<std::pair<EGLint, EGLContext>> &contexts);
756 
757     glw::Functions m_gl;
758 };
759 
760 // SingleThreadColorClearCase
761 
SingleThreadRenderCase(EglTestContext & eglTestCtx,const char * name,const char * description,EGLint api,EGLint surfaceType,const eglu::FilterList & filters,int numContextsPerApi)762 SingleThreadRenderCase::SingleThreadRenderCase(EglTestContext &eglTestCtx, const char *name, const char *description,
763                                                EGLint api, EGLint surfaceType, const eglu::FilterList &filters,
764                                                int numContextsPerApi)
765     : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
766 {
767 }
768 
init(void)769 void SingleThreadRenderCase::init(void)
770 {
771     MultiContextRenderCase::init();
772     m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2, 0));
773 }
774 
executeForContexts(EGLDisplay display,EGLSurface surface,const Config & config,const std::vector<std::pair<EGLint,EGLContext>> & contexts)775 void SingleThreadRenderCase::executeForContexts(EGLDisplay display, EGLSurface surface, const Config &config,
776                                                 const std::vector<std::pair<EGLint, EGLContext>> &contexts)
777 {
778     const Library &egl              = m_eglTestCtx.getLibrary();
779     const int width                 = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
780     const int height                = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
781     const int numContexts           = (int)contexts.size();
782     const int drawsPerCtx           = 2;
783     const int numIters              = 2;
784     const tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config);
785     const float threshold           = getColorThreshold(pixelFmt);
786 
787     const int depthBits   = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
788     const int stencilBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
789     const int numSamples  = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
790 
791     TestLog &log = m_testCtx.getLog();
792 
793     tcu::Surface refFrame(width, height);
794     tcu::Surface frame(width, height);
795 
796     de::Random rnd(deStringHash(getName()) ^ deInt32Hash(numContexts));
797     vector<ProgramSp> programs(contexts.size());
798     vector<DrawPrimitiveOp> drawOps;
799 
800     // Log basic information about config.
801     log << TestLog::Message << "EGL_RED_SIZE = " << pixelFmt.redBits << TestLog::EndMessage;
802     log << TestLog::Message << "EGL_GREEN_SIZE = " << pixelFmt.greenBits << TestLog::EndMessage;
803     log << TestLog::Message << "EGL_BLUE_SIZE = " << pixelFmt.blueBits << TestLog::EndMessage;
804     log << TestLog::Message << "EGL_ALPHA_SIZE = " << pixelFmt.alphaBits << TestLog::EndMessage;
805     log << TestLog::Message << "EGL_DEPTH_SIZE = " << depthBits << TestLog::EndMessage;
806     log << TestLog::Message << "EGL_STENCIL_SIZE = " << stencilBits << TestLog::EndMessage;
807     log << TestLog::Message << "EGL_SAMPLES = " << numSamples << TestLog::EndMessage;
808 
809     // Generate draw ops.
810     drawOps.resize(numContexts * drawsPerCtx * numIters);
811     for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
812     {
813         // The randomized draws force us to use the fuzzyCompare algorithm to check for possible rasterization differences between
814         // our rasterizer and the one used by the implementation. However, fuzzyCompare only takes a single float as the error
815         // threshold. As per the comments in the code, that threshold doesn't have a direct equivalent to a difference in color
816         // values. Instead, it's related to an accumulated quadratic error. Also according to these comments, this works well for
817         // rgba8 (the only case it was originally designed for), and it can also be made to work for other formats in which all
818         // used channels have a roughly similar number of bits.
819         //
820         // However, in some of these tests we will run this code using formats such as rgb10a2, in which the alpha can only have
821         // values of 0, 0.33, 0.66 and 1.0 while other channels have much more precission. Any small difference in the rounding
822         // applied by CTS and the implementation could result in wildly different alpha values that make using the single floating
823         // point threshold risky, because we can't separate the alpha errors from the rgb errors. If we increase the threshold to
824         // a point which is acceptable for alpha errors, the kind of errors we allow in the colors would be huge.
825         //
826         // For this reason, with 2 bits for alpha we restrict ourselves to alpha values of 1.0 and 0.0, as if alphabits was 1.
827         randomizeDrawOp(rnd, *drawOp, (pixelFmt.alphaBits <= 2));
828     }
829 
830     // Create and setup programs per context
831     for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
832     {
833         EGLint api         = contexts[ctxNdx].first;
834         EGLContext context = contexts[ctxNdx].second;
835 
836         EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
837 
838         programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
839         programs[ctxNdx]->setup();
840     }
841 
842     // Clear to black using first context.
843     {
844         EGLint api         = contexts[0].first;
845         EGLContext context = contexts[0].second;
846 
847         EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
848 
849         clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
850         finish(m_gl, api);
851     }
852 
853     // Render.
854     for (int iterNdx = 0; iterNdx < numIters; iterNdx++)
855     {
856         for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
857         {
858             EGLint api         = contexts[ctxNdx].first;
859             EGLContext context = contexts[ctxNdx].second;
860 
861             EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
862 
863             for (int drawNdx = 0; drawNdx < drawsPerCtx; drawNdx++)
864             {
865                 const DrawPrimitiveOp &drawOp =
866                     drawOps[iterNdx * numContexts * drawsPerCtx + ctxNdx * drawsPerCtx + drawNdx];
867                 draw(m_gl, api, *programs[ctxNdx], drawOp);
868             }
869 
870             finish(m_gl, api);
871         }
872     }
873 
874     // Read pixels using first context. \todo [pyry] Randomize?
875     {
876         EGLint api         = contexts[0].first;
877         EGLContext context = contexts[0].second;
878 
879         EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
880 
881         readPixels(m_gl, api, frame);
882     }
883 
884     int subpixelBits = 0;
885     m_gl.getIntegerv(GL_SUBPIXEL_BITS, &subpixelBits);
886 
887     EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
888 
889     // Render reference.
890     // \note Reference image is always generated using single-sampling.
891     renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1, subpixelBits);
892 
893     // Compare images
894     {
895         bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame,
896                                           threshold, tcu::COMPARE_LOG_RESULT);
897 
898         if (!imagesOk)
899             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
900     }
901 }
902 
903 // MultiThreadRenderCase
904 
905 class MultiThreadRenderCase : public MultiContextRenderCase
906 {
907 public:
908     MultiThreadRenderCase(EglTestContext &eglTestCtx, const char *name, const char *description, EGLint api,
909                           EGLint surfaceType, const eglu::FilterList &filters, int numContextsPerApi);
910 
911     void init(void);
912 
913 private:
914     virtual void executeForContexts(EGLDisplay display, EGLSurface surface, const Config &config,
915                                     const std::vector<std::pair<EGLint, EGLContext>> &contexts);
916 
917     glw::Functions m_gl;
918 };
919 
920 class RenderTestThread;
921 
922 typedef de::SharedPtr<RenderTestThread> RenderTestThreadSp;
923 typedef de::SharedPtr<de::Semaphore> SemaphoreSp;
924 
925 struct DrawOpPacket
926 {
DrawOpPacketdeqp::egl::DrawOpPacket927     DrawOpPacket(void) : drawOps(DE_NULL), numOps(0)
928     {
929     }
930 
931     const DrawPrimitiveOp *drawOps;
932     int numOps;
933     SemaphoreSp wait;
934     SemaphoreSp signal;
935 };
936 
937 class RenderTestThread : public de::Thread
938 {
939 public:
RenderTestThread(const Library & egl,EGLDisplay display,EGLSurface surface,EGLContext context,EGLint api,const glw::Functions & gl,const Program & program,const std::vector<DrawOpPacket> & packets)940     RenderTestThread(const Library &egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api,
941                      const glw::Functions &gl, const Program &program, const std::vector<DrawOpPacket> &packets)
942         : m_egl(egl)
943         , m_display(display)
944         , m_surface(surface)
945         , m_context(context)
946         , m_api(api)
947         , m_gl(gl)
948         , m_program(program)
949         , m_packets(packets)
950     {
951     }
952 
run(void)953     void run(void)
954     {
955         for (std::vector<DrawOpPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end();
956              packetIter++)
957         {
958             // Wait until it is our turn.
959             packetIter->wait->decrement();
960 
961             // Acquire context.
962             EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, m_surface, m_surface, m_context));
963 
964             // Execute rendering.
965             for (int ndx = 0; ndx < packetIter->numOps; ndx++)
966                 draw(m_gl, m_api, m_program, packetIter->drawOps[ndx]);
967 
968             finish(m_gl, m_api);
969 
970             // Release context.
971             EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
972 
973             // Signal completion.
974             packetIter->signal->increment();
975         }
976         m_egl.releaseThread();
977     }
978 
979 private:
980     const Library &m_egl;
981     EGLDisplay m_display;
982     EGLSurface m_surface;
983     EGLContext m_context;
984     EGLint m_api;
985     const glw::Functions &m_gl;
986     const Program &m_program;
987     const std::vector<DrawOpPacket> &m_packets;
988 };
989 
MultiThreadRenderCase(EglTestContext & eglTestCtx,const char * name,const char * description,EGLint api,EGLint surfaceType,const eglu::FilterList & filters,int numContextsPerApi)990 MultiThreadRenderCase::MultiThreadRenderCase(EglTestContext &eglTestCtx, const char *name, const char *description,
991                                              EGLint api, EGLint surfaceType, const eglu::FilterList &filters,
992                                              int numContextsPerApi)
993     : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
994 {
995 }
996 
init(void)997 void MultiThreadRenderCase::init(void)
998 {
999     MultiContextRenderCase::init();
1000     m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2, 0));
1001 }
1002 
executeForContexts(EGLDisplay display,EGLSurface surface,const Config & config,const std::vector<std::pair<EGLint,EGLContext>> & contexts)1003 void MultiThreadRenderCase::executeForContexts(EGLDisplay display, EGLSurface surface, const Config &config,
1004                                                const std::vector<std::pair<EGLint, EGLContext>> &contexts)
1005 {
1006     const Library &egl              = m_eglTestCtx.getLibrary();
1007     const int width                 = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
1008     const int height                = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
1009     const int numContexts           = (int)contexts.size();
1010     const int opsPerPacket          = 2;
1011     const int packetsPerThread      = 2;
1012     const int numThreads            = numContexts;
1013     const int numPackets            = numThreads * packetsPerThread;
1014     const tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config);
1015     const float threshold           = getColorThreshold(pixelFmt);
1016 
1017     const int depthBits   = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
1018     const int stencilBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
1019     const int numSamples  = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
1020 
1021     TestLog &log = m_testCtx.getLog();
1022 
1023     tcu::Surface refFrame(width, height);
1024     tcu::Surface frame(width, height);
1025 
1026     de::Random rnd(deStringHash(getName()) ^ deInt32Hash(numContexts));
1027 
1028     // Resources that need cleanup
1029     vector<ProgramSp> programs(numContexts);
1030     vector<SemaphoreSp> semaphores(numPackets + 1);
1031     vector<DrawPrimitiveOp> drawOps(numPackets * opsPerPacket);
1032     vector<vector<DrawOpPacket>> packets(numThreads);
1033     vector<RenderTestThreadSp> threads(numThreads);
1034 
1035     // Log basic information about config.
1036     log << TestLog::Message << "EGL_RED_SIZE = " << pixelFmt.redBits << TestLog::EndMessage;
1037     log << TestLog::Message << "EGL_GREEN_SIZE = " << pixelFmt.greenBits << TestLog::EndMessage;
1038     log << TestLog::Message << "EGL_BLUE_SIZE = " << pixelFmt.blueBits << TestLog::EndMessage;
1039     log << TestLog::Message << "EGL_ALPHA_SIZE = " << pixelFmt.alphaBits << TestLog::EndMessage;
1040     log << TestLog::Message << "EGL_DEPTH_SIZE = " << depthBits << TestLog::EndMessage;
1041     log << TestLog::Message << "EGL_STENCIL_SIZE = " << stencilBits << TestLog::EndMessage;
1042     log << TestLog::Message << "EGL_SAMPLES = " << numSamples << TestLog::EndMessage;
1043 
1044     // Initialize semaphores.
1045     for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
1046         *sem = SemaphoreSp(new de::Semaphore(0));
1047 
1048     // Create draw ops.
1049     for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
1050     {
1051         // The randomized draws force us to use the fuzzyCompare algorithm to check for possible rasterization differences between
1052         // our rasterizer and the one used by the implementation. However, fuzzyCompare only takes a single float as the error
1053         // threshold. As per the comments in the code, that threshold doesn't have a direct equivalent to a difference in color
1054         // values. Instead, it's related to an accumulated quadratic error. Also according to these comments, this works well for
1055         // rgba8 (the only case it was originally designed for), and it can also be made to work for other formats in which all
1056         // used channels have a roughly similar number of bits.
1057         //
1058         // However, in some of these tests we will run this code using formats such as rgb10a2, in which the alpha can only have
1059         // values of 0, 0.33, 0.66 and 1.0 while other channels have much more precission. Any small difference in the rounding
1060         // applied by CTS and the implementation could result in wildly different alpha values that make using the single floating
1061         // point threshold risky, because we can't separate the alpha errors from the rgb errors. If we increase the threshold to
1062         // a point which is acceptable for alpha errors, the kind of errors we allow in the colors would be huge.
1063         //
1064         // For this reason, with 2 bits for alpha we restrict ourselves to alpha values of 1.0 and 0.0, as if alphabits was 1.
1065         randomizeDrawOp(rnd, *drawOp, (pixelFmt.alphaBits <= 2));
1066     }
1067 
1068     // Create packets.
1069     for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
1070     {
1071         packets[threadNdx].resize(packetsPerThread);
1072 
1073         for (int packetNdx = 0; packetNdx < packetsPerThread; packetNdx++)
1074         {
1075             DrawOpPacket &packet = packets[threadNdx][packetNdx];
1076 
1077             // Threads take turns with packets.
1078             packet.wait    = semaphores[packetNdx * numThreads + threadNdx];
1079             packet.signal  = semaphores[packetNdx * numThreads + threadNdx + 1];
1080             packet.numOps  = opsPerPacket;
1081             packet.drawOps = &drawOps[(packetNdx * numThreads + threadNdx) * opsPerPacket];
1082         }
1083     }
1084 
1085     // Create and setup programs per context
1086     for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
1087     {
1088         EGLint api         = contexts[ctxNdx].first;
1089         EGLContext context = contexts[ctxNdx].second;
1090 
1091         EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1092 
1093         programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
1094         programs[ctxNdx]->setup();
1095 
1096         // Release context
1097         EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1098     }
1099 
1100     // Clear to black using first context.
1101     {
1102         EGLint api         = contexts[0].first;
1103         EGLContext context = contexts[0].second;
1104 
1105         EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1106 
1107         clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
1108         finish(m_gl, api);
1109 
1110         // Release context
1111         EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1112     }
1113 
1114     // Create and launch threads (actual rendering starts once first semaphore is signaled).
1115     for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
1116     {
1117         threads[threadNdx] = RenderTestThreadSp(new RenderTestThread(egl, display, surface, contexts[threadNdx].second,
1118                                                                      contexts[threadNdx].first, m_gl,
1119                                                                      *programs[threadNdx], packets[threadNdx]));
1120         threads[threadNdx]->start();
1121     }
1122 
1123     // Signal start and wait until complete.
1124     semaphores.front()->increment();
1125     semaphores.back()->decrement();
1126 
1127     // Read pixels using first context. \todo [pyry] Randomize?
1128     {
1129         EGLint api         = contexts[0].first;
1130         EGLContext context = contexts[0].second;
1131 
1132         EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1133 
1134         readPixels(m_gl, api, frame);
1135     }
1136 
1137     int subpixelBits = 0;
1138     m_gl.getIntegerv(GL_SUBPIXEL_BITS, &subpixelBits);
1139 
1140     EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1141 
1142     // Join threads.
1143     for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
1144         threads[threadNdx]->join();
1145 
1146     // Render reference.
1147     renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1, subpixelBits);
1148 
1149     // Compare images
1150     {
1151         bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame,
1152                                           threshold, tcu::COMPARE_LOG_RESULT);
1153 
1154         if (!imagesOk)
1155             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
1156     }
1157 }
1158 
RenderTests(EglTestContext & eglTestCtx)1159 RenderTests::RenderTests(EglTestContext &eglTestCtx)
1160     : TestCaseGroup(eglTestCtx, "render", "Basic rendering with different client APIs")
1161 {
1162 }
1163 
~RenderTests(void)1164 RenderTests::~RenderTests(void)
1165 {
1166 }
1167 
1168 struct RenderGroupSpec
1169 {
1170     const char *name;
1171     const char *desc;
1172     EGLint apiBits;
1173     eglu::ConfigFilter baseFilter;
1174     int numContextsPerApi;
1175 };
1176 
1177 template <uint32_t Bits>
renderable(const eglu::CandidateConfig & c)1178 static bool renderable(const eglu::CandidateConfig &c)
1179 {
1180     return (c.renderableType() & Bits) == Bits;
1181 }
1182 
1183 template <class RenderClass>
createRenderGroups(EglTestContext & eglTestCtx,tcu::TestCaseGroup * group,const RenderGroupSpec * first,const RenderGroupSpec * last)1184 static void createRenderGroups(EglTestContext &eglTestCtx, tcu::TestCaseGroup *group, const RenderGroupSpec *first,
1185                                const RenderGroupSpec *last)
1186 {
1187     for (const RenderGroupSpec *groupIter = first; groupIter != last; groupIter++)
1188     {
1189         tcu::TestCaseGroup *configGroup =
1190             new tcu::TestCaseGroup(eglTestCtx.getTestContext(), groupIter->name, groupIter->desc);
1191         group->addChild(configGroup);
1192 
1193         vector<RenderFilterList> filterLists;
1194         eglu::FilterList baseFilters;
1195         baseFilters << groupIter->baseFilter;
1196         getDefaultRenderFilterLists(filterLists, baseFilters);
1197 
1198         for (vector<RenderFilterList>::const_iterator listIter = filterLists.begin(); listIter != filterLists.end();
1199              listIter++)
1200             configGroup->addChild(new RenderClass(eglTestCtx, listIter->getName(), "", groupIter->apiBits,
1201                                                   listIter->getSurfaceTypeMask(), *listIter,
1202                                                   groupIter->numContextsPerApi));
1203     }
1204 }
1205 
init(void)1206 void RenderTests::init(void)
1207 {
1208     static const RenderGroupSpec singleContextCases[] = {
1209         {"gles2", "Primitive rendering using GLES2", EGL_OPENGL_ES2_BIT, renderable<EGL_OPENGL_ES2_BIT>, 1},
1210         {"gles3", "Primitive rendering using GLES3", EGL_OPENGL_ES3_BIT, renderable<EGL_OPENGL_ES3_BIT>, 1},
1211     };
1212 
1213     static const RenderGroupSpec multiContextCases[] = {
1214         {"gles2", "Primitive rendering using multiple GLES2 contexts to shared surface", EGL_OPENGL_ES2_BIT,
1215          renderable<EGL_OPENGL_ES2_BIT>, 3},
1216         {"gles3", "Primitive rendering using multiple GLES3 contexts to shared surface", EGL_OPENGL_ES3_BIT,
1217          renderable<EGL_OPENGL_ES3_BIT>, 3},
1218         {"gles2_gles3", "Primitive rendering using multiple APIs to shared surface",
1219          EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT, renderable<EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT>, 1},
1220     };
1221 
1222     tcu::TestCaseGroup *singleContextGroup =
1223         new tcu::TestCaseGroup(m_testCtx, "single_context", "Single-context rendering");
1224     addChild(singleContextGroup);
1225     createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, singleContextGroup, &singleContextCases[0],
1226                                                &singleContextCases[DE_LENGTH_OF_ARRAY(singleContextCases)]);
1227 
1228     tcu::TestCaseGroup *multiContextGroup =
1229         new tcu::TestCaseGroup(m_testCtx, "multi_context", "Multi-context rendering with shared surface");
1230     addChild(multiContextGroup);
1231     createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, multiContextGroup, &multiContextCases[0],
1232                                                &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);
1233 
1234     tcu::TestCaseGroup *multiThreadGroup =
1235         new tcu::TestCaseGroup(m_testCtx, "multi_thread", "Multi-thread rendering with shared surface");
1236     addChild(multiThreadGroup);
1237     createRenderGroups<MultiThreadRenderCase>(m_eglTestCtx, multiThreadGroup, &multiContextCases[0],
1238                                               &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);
1239 }
1240 
1241 } // namespace egl
1242 } // namespace deqp
1243