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