1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program EGL Module
3 * ---------------------------------------
5 * Copyright 2014 The Android Open Source Project
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Rendering tests for different config and api combinations.
22 * \todo [2013-03-19 pyry] GLES1 and VG support.
23 *//*--------------------------------------------------------------------*/
25 #include "teglRenderTests.hpp"
26 #include "teglRenderCase.hpp"
28 #include "tcuRenderTarget.hpp"
29 #include "tcuTestLog.hpp"
30 #include "tcuImageCompare.hpp"
31 #include "tcuTextureUtil.hpp"
32 #include "tcuSurface.hpp"
34 #include "egluDefs.hpp"
35 #include "egluUtil.hpp"
37 #include "eglwLibrary.hpp"
38 #include "eglwEnums.hpp"
40 #include "gluShaderProgram.hpp"
42 #include "glwFunctions.hpp"
43 #include "glwEnums.hpp"
45 #include "deRandom.hpp"
46 #include "deSharedPtr.hpp"
47 #include "deSemaphore.hpp"
48 #include "deThread.hpp"
51 #include "rrRenderer.hpp"
52 #include "rrFragmentOperations.hpp"
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;
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
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
102 DEPTHMODE_NONE = 0, //!< No depth test or depth writes
103 DEPTHMODE_LESS, //!< Depth test with less & depth write
110 STENCILMODE_NONE = 0, //!< No stencil test or write
111 STENCILMODE_LEQUAL_INC, //!< Stencil test with LEQUAL, increment on pass
116 struct DrawPrimitiveOp
120 vector<Vec4> positions;
128 static bool isANarrowScreenSpaceTriangle (const tcu::Vec4& p0, const tcu::Vec4& p1, const tcu::Vec4& p2)
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();
135 const tcu::Vec2 e01 = (csp1 - csp0);
136 const tcu::Vec2 e02 = (csp2 - csp0);
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;
141 return visibleArea < minimumVisibleArea;
144 void randomizeDrawOp (de::Random& rnd, DrawPrimitiveOp& drawOp, const bool alphaZeroOrOne)
146 const int minStencilRef = 0;
147 const int maxStencilRef = 8;
148 const int minPrimitives = 2;
149 const int maxPrimitives = 4;
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;
155 const float minRGB = 0.2f;
156 const float maxRGB = 0.9f;
157 const float minAlpha = 0.3f;
158 const float maxAlpha = 1.0f;
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);
167 if (drawOp.type == PRIMITIVETYPE_TRIANGLE)
169 drawOp.positions.resize(drawOp.count*3);
170 drawOp.colors.resize(drawOp.count*3);
172 for (int triNdx = 0; triNdx < drawOp.count; triNdx++)
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;
178 for (int coordNdx = 0; coordNdx < 3; coordNdx++)
180 tcu::Vec4& position = drawOp.positions[triNdx*3 + coordNdx];
181 tcu::Vec4& color = drawOp.colors[triNdx*3 + coordNdx];
183 position.x() = cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
184 position.y() = cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
185 position.z() = rnd.getFloat(minDepth, maxDepth);
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);
195 color.w() = flatAlpha;
199 // avoid generating narrow triangles
201 const int maxAttempts = 100;
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];
207 while (isANarrowScreenSpaceTriangle(p0, p1, p2))
209 p1.x() = cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
210 p1.y() = cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
211 p1.z() = rnd.getFloat(minDepth, maxDepth);
214 p2.x() = cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
215 p2.y() = cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
216 p2.z() = rnd.getFloat(minDepth, maxDepth);
219 if (++numAttempts > maxAttempts)
232 // Reference rendering code
234 class ReferenceShader : public rr::VertexShader, public rr::FragmentShader
243 : rr::VertexShader (2, 1) // color and pos in => color out
244 , rr::FragmentShader(1, 1) // color in => color out
246 this->rr::VertexShader::m_inputs[0].type = rr::GENERICVECTYPE_FLOAT;
247 this->rr::VertexShader::m_inputs[1].type = rr::GENERICVECTYPE_FLOAT;
249 this->rr::VertexShader::m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
250 this->rr::VertexShader::m_outputs[0].flatshade = false;
252 this->rr::FragmentShader::m_inputs[0].type = rr::GENERICVECTYPE_FLOAT;
253 this->rr::FragmentShader::m_inputs[0].flatshade = false;
255 this->rr::FragmentShader::m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
258 void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const
260 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
262 const int positionAttrLoc = 0;
263 const int colorAttrLoc = 1;
265 rr::VertexPacket& packet = *packets[packetNdx];
267 // Transform to position
268 packet.position = rr::readVertexAttribFloat(inputs[positionAttrLoc], packet.instanceNdx, packet.vertexNdx);
271 packet.outputs[VaryingLoc_Color] = rr::readVertexAttribFloat(inputs[colorAttrLoc], packet.instanceNdx, packet.vertexNdx);
275 void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
277 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
279 rr::FragmentPacket& packet = packets[packetNdx];
281 for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
282 rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, rr::readVarying<float>(packet, context, VaryingLoc_Color, fragNdx));
287 void toReferenceRenderState (rr::RenderState& state, const DrawPrimitiveOp& drawOp)
289 state.cullMode = rr::CULLMODE_NONE;
291 if (drawOp.blend != BLENDMODE_NONE)
293 state.fragOps.blendMode = rr::BLENDMODE_STANDARD;
295 switch (drawOp.blend)
297 case BLENDMODE_ADDITIVE:
298 state.fragOps.blendRGBState.srcFunc = rr::BLENDFUNC_ONE;
299 state.fragOps.blendRGBState.dstFunc = rr::BLENDFUNC_ONE;
300 state.fragOps.blendRGBState.equation = rr::BLENDEQUATION_ADD;
301 state.fragOps.blendAState = state.fragOps.blendRGBState;
304 case BLENDMODE_SRC_OVER:
305 state.fragOps.blendRGBState.srcFunc = rr::BLENDFUNC_SRC_ALPHA;
306 state.fragOps.blendRGBState.dstFunc = rr::BLENDFUNC_ONE_MINUS_SRC_ALPHA;
307 state.fragOps.blendRGBState.equation = rr::BLENDEQUATION_ADD;
308 state.fragOps.blendAState = state.fragOps.blendRGBState;
316 if (drawOp.depth != DEPTHMODE_NONE)
318 state.fragOps.depthTestEnabled = true;
320 DE_ASSERT(drawOp.depth == DEPTHMODE_LESS);
321 state.fragOps.depthFunc = rr::TESTFUNC_LESS;
324 if (drawOp.stencil != STENCILMODE_NONE)
326 state.fragOps.stencilTestEnabled = true;
328 DE_ASSERT(drawOp.stencil == STENCILMODE_LEQUAL_INC);
329 state.fragOps.stencilStates[0].func = rr::TESTFUNC_LEQUAL;
330 state.fragOps.stencilStates[0].sFail = rr::STENCILOP_KEEP;
331 state.fragOps.stencilStates[0].dpFail = rr::STENCILOP_INCR;
332 state.fragOps.stencilStates[0].dpPass = rr::STENCILOP_INCR;
333 state.fragOps.stencilStates[0].ref = drawOp.stencilRef;
334 state.fragOps.stencilStates[1] = state.fragOps.stencilStates[0];
338 tcu::TextureFormat getColorFormat (const tcu::PixelFormat& colorBits)
340 using tcu::TextureFormat;
342 DE_ASSERT(de::inBounds(colorBits.redBits, 0, 0xff) &&
343 de::inBounds(colorBits.greenBits, 0, 0xff) &&
344 de::inBounds(colorBits.blueBits, 0, 0xff) &&
345 de::inBounds(colorBits.alphaBits, 0, 0xff));
347 #define PACK_FMT(R, G, B, A) (((R) << 24) | ((G) << 16) | ((B) << 8) | (A))
349 // \note [pyry] This may not hold true on some implementations - best effort guess only.
350 switch (PACK_FMT(colorBits.redBits, colorBits.greenBits, colorBits.blueBits, colorBits.alphaBits))
352 case PACK_FMT(8,8,8,8): return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8);
353 case PACK_FMT(8,8,8,0): return TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8);
354 case PACK_FMT(4,4,4,4): return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_SHORT_4444);
355 case PACK_FMT(5,5,5,1): return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_SHORT_5551);
356 case PACK_FMT(5,6,5,0): return TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_SHORT_565);
358 // \note Defaults to RGBA8
359 default: return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8);
366 The getColorThreshold function is used to obtain a
367 threshold usable for the fuzzyCompare function.
369 For 8bit color depths a value of 0.02 should provide
370 a good metric for rejecting images above this level.
371 For other bit depths other thresholds should be selected.
372 Ideally this function would take advantage of the
373 getColorThreshold function provided by the PixelFormat class
374 as this would also allow setting per channel thresholds.
375 However using the PixelFormat provided function can result
376 in too strict thresholds for 8bit bit depths (compared to
377 the current default of 0.02) or too relaxed for lower bit
378 depths if scaled proportionally to the 8bit default.
381 float getColorThreshold (const tcu::PixelFormat& colorBits)
383 if ((colorBits.redBits > 0 && colorBits.redBits < 8) ||
384 (colorBits.greenBits > 0 && colorBits.greenBits < 8) ||
385 (colorBits.blueBits > 0 && colorBits.blueBits < 8) ||
386 (colorBits.alphaBits > 0 && colorBits.alphaBits < 8))
396 tcu::TextureFormat getDepthFormat (const int depthBits)
400 case 0: return tcu::TextureFormat();
401 case 8: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT8);
402 case 16: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT16);
403 case 24: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT24);
405 default: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT);
409 tcu::TextureFormat getStencilFormat (int stencilBits)
413 case 0: return tcu::TextureFormat();
415 default: return tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT8);
419 void 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)
421 const int width = dst.getWidth();
422 const int height = dst.getHeight();
424 tcu::TextureLevel colorBuffer;
425 tcu::TextureLevel depthBuffer;
426 tcu::TextureLevel stencilBuffer;
428 rr::Renderer referenceRenderer;
429 rr::VertexAttrib attributes[2];
430 const ReferenceShader shader;
432 attributes[0].type = rr::VERTEXATTRIBTYPE_FLOAT;
433 attributes[0].size = 4;
434 attributes[0].stride = 0;
435 attributes[0].instanceDivisor = 0;
437 attributes[1].type = rr::VERTEXATTRIBTYPE_FLOAT;
438 attributes[1].size = 4;
439 attributes[1].stride = 0;
440 attributes[1].instanceDivisor = 0;
442 // Initialize buffers.
443 colorBuffer.setStorage(getColorFormat(colorBits), numSamples, width, height);
444 rr::clearMultisampleColorBuffer(colorBuffer, CLEAR_COLOR, rr::WindowRectangle(0, 0, width, height));
448 depthBuffer.setStorage(getDepthFormat(depthBits), numSamples, width, height);
449 rr::clearMultisampleDepthBuffer(depthBuffer, CLEAR_DEPTH, rr::WindowRectangle(0, 0, width, height));
454 stencilBuffer.setStorage(getStencilFormat(stencilBits), numSamples, width, height);
455 rr::clearMultisampleStencilBuffer(stencilBuffer, CLEAR_STENCIL, rr::WindowRectangle(0, 0, width, height));
458 const rr::RenderTarget renderTarget(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()),
459 rr::MultisamplePixelBufferAccess::fromMultisampleAccess(depthBuffer.getAccess()),
460 rr::MultisamplePixelBufferAccess::fromMultisampleAccess(stencilBuffer.getAccess()));
462 for (vector<DrawPrimitiveOp>::const_iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); drawOp++)
465 rr::RenderState renderState((rr::ViewportState)(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess())), subpixelBits);
466 toReferenceRenderState(renderState, *drawOp);
468 DE_ASSERT(drawOp->type == PRIMITIVETYPE_TRIANGLE);
470 attributes[0].pointer = &drawOp->positions[0];
471 attributes[1].pointer = &drawOp->colors[0];
473 referenceRenderer.draw(
477 rr::Program(static_cast<const rr::VertexShader*>(&shader), static_cast<const rr::FragmentShader*>(&shader)),
480 rr::PrimitiveList(rr::PRIMITIVETYPE_TRIANGLES, drawOp->count * 3, 0)));
483 rr::resolveMultisampleColorBuffer(dst, rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()));
486 // API rendering code
492 virtual ~Program (void) {}
494 virtual void setup (void) const = DE_NULL;
497 typedef de::SharedPtr<Program> ProgramSp;
499 static glu::ProgramSources getProgramSourcesES2 (void)
501 static const char* s_vertexSrc =
502 "attribute highp vec4 a_position;\n"
503 "attribute mediump vec4 a_color;\n"
504 "varying mediump vec4 v_color;\n"
507 " gl_Position = a_position;\n"
508 " v_color = a_color;\n"
511 static const char* s_fragmentSrc =
512 "varying mediump vec4 v_color;\n"
515 " gl_FragColor = v_color;\n"
518 return glu::ProgramSources() << glu::VertexSource(s_vertexSrc) << glu::FragmentSource(s_fragmentSrc);
521 class GLES2Program : public Program
524 GLES2Program (const glw::Functions& gl)
526 , m_program (gl, getProgramSourcesES2())
531 m_positionLoc = m_gl.getAttribLocation(m_program.getProgram(), "a_position");
532 m_colorLoc = m_gl.getAttribLocation(m_program.getProgram(), "a_color");
539 void setup (void) const
541 m_gl.useProgram(m_program.getProgram());
542 m_gl.enableVertexAttribArray(m_positionLoc);
543 m_gl.enableVertexAttribArray(m_colorLoc);
544 GLU_CHECK_GLW_MSG(m_gl, "Program setup failed");
547 int getPositionLoc (void) const { return m_positionLoc; }
548 int getColorLoc (void) const { return m_colorLoc; }
551 const glw::Functions& m_gl;
552 glu::ShaderProgram m_program;
557 void clearGLES2 (const glw::Functions& gl, const tcu::Vec4& color, const float depth, const int stencil)
559 gl.clearColor(color.x(), color.y(), color.z(), color.w());
560 gl.clearDepthf(depth);
561 gl.clearStencil(stencil);
562 gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
565 void drawGLES2 (const glw::Functions& gl, const Program& program, const DrawPrimitiveOp& drawOp)
567 const GLES2Program& gles2Program = dynamic_cast<const GLES2Program&>(program);
569 switch (drawOp.blend)
572 gl.disable(GL_BLEND);
575 case BLENDMODE_ADDITIVE:
577 gl.blendFunc(GL_ONE, GL_ONE);
580 case BLENDMODE_SRC_OVER:
582 gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
589 switch (drawOp.depth)
592 gl.disable(GL_DEPTH_TEST);
596 gl.enable(GL_DEPTH_TEST);
603 switch (drawOp.stencil)
605 case STENCILMODE_NONE:
606 gl.disable(GL_STENCIL_TEST);
609 case STENCILMODE_LEQUAL_INC:
610 gl.enable(GL_STENCIL_TEST);
611 gl.stencilFunc(GL_LEQUAL, drawOp.stencilRef, ~0u);
612 gl.stencilOp(GL_KEEP, GL_INCR, GL_INCR);
619 gl.disable(GL_DITHER);
621 gl.vertexAttribPointer(gles2Program.getPositionLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.positions[0]);
622 gl.vertexAttribPointer(gles2Program.getColorLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.colors[0]);
624 DE_ASSERT(drawOp.type == PRIMITIVETYPE_TRIANGLE);
625 gl.drawArrays(GL_TRIANGLES, 0, drawOp.count*3);
628 static void readPixelsGLES2 (const glw::Functions& gl, tcu::Surface& dst)
630 gl.readPixels(0, 0, dst.getWidth(), dst.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, dst.getAccess().getDataPtr());
633 Program* createProgram (const glw::Functions& gl, EGLint api)
637 case EGL_OPENGL_ES2_BIT: return new GLES2Program(gl);
638 case EGL_OPENGL_ES3_BIT_KHR: return new GLES2Program(gl);
640 throw tcu::NotSupportedError("Unsupported API");
644 void draw (const glw::Functions& gl, EGLint api, const Program& program, const DrawPrimitiveOp& drawOp)
648 case EGL_OPENGL_ES2_BIT: drawGLES2(gl, program, drawOp); break;
649 case EGL_OPENGL_ES3_BIT_KHR: drawGLES2(gl, program, drawOp); break;
651 throw tcu::NotSupportedError("Unsupported API");
655 void clear (const glw::Functions& gl, EGLint api, const tcu::Vec4& color, const float depth, const int stencil)
659 case EGL_OPENGL_ES2_BIT: clearGLES2(gl, color, depth, stencil); break;
660 case EGL_OPENGL_ES3_BIT_KHR: clearGLES2(gl, color, depth, stencil); break;
662 throw tcu::NotSupportedError("Unsupported API");
666 static void readPixels (const glw::Functions& gl, EGLint api, tcu::Surface& dst)
670 case EGL_OPENGL_ES2_BIT: readPixelsGLES2(gl, dst); break;
671 case EGL_OPENGL_ES3_BIT_KHR: readPixelsGLES2(gl, dst); break;
673 throw tcu::NotSupportedError("Unsupported API");
677 static void finish (const glw::Functions& gl, EGLint api)
681 case EGL_OPENGL_ES2_BIT:
682 case EGL_OPENGL_ES3_BIT_KHR:
687 throw tcu::NotSupportedError("Unsupported API");
691 tcu::PixelFormat getPixelFormat (const Library& egl, EGLDisplay display, EGLConfig config)
693 tcu::PixelFormat fmt;
694 fmt.redBits = eglu::getConfigAttribInt(egl, display, config, EGL_RED_SIZE);
695 fmt.greenBits = eglu::getConfigAttribInt(egl, display, config, EGL_GREEN_SIZE);
696 fmt.blueBits = eglu::getConfigAttribInt(egl, display, config, EGL_BLUE_SIZE);
697 fmt.alphaBits = eglu::getConfigAttribInt(egl, display, config, EGL_ALPHA_SIZE);
703 // SingleThreadRenderCase
705 class SingleThreadRenderCase : public MultiContextRenderCase
708 SingleThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi);
713 virtual void executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts);
718 // SingleThreadColorClearCase
720 SingleThreadRenderCase::SingleThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
721 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
725 void SingleThreadRenderCase::init (void)
727 MultiContextRenderCase::init();
728 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
731 void SingleThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
733 const Library& egl = m_eglTestCtx.getLibrary();
734 const int width = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
735 const int height = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
736 const int numContexts = (int)contexts.size();
737 const int drawsPerCtx = 2;
738 const int numIters = 2;
739 const tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config);
740 const float threshold = getColorThreshold(pixelFmt);
742 const int depthBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
743 const int stencilBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
744 const int numSamples = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
746 TestLog& log = m_testCtx.getLog();
748 tcu::Surface refFrame (width, height);
749 tcu::Surface frame (width, height);
751 de::Random rnd (deStringHash(getName()) ^ deInt32Hash(numContexts));
752 vector<ProgramSp> programs (contexts.size());
753 vector<DrawPrimitiveOp> drawOps;
755 // Log basic information about config.
756 log << TestLog::Message << "EGL_RED_SIZE = " << pixelFmt.redBits << TestLog::EndMessage;
757 log << TestLog::Message << "EGL_GREEN_SIZE = " << pixelFmt.greenBits << TestLog::EndMessage;
758 log << TestLog::Message << "EGL_BLUE_SIZE = " << pixelFmt.blueBits << TestLog::EndMessage;
759 log << TestLog::Message << "EGL_ALPHA_SIZE = " << pixelFmt.alphaBits << TestLog::EndMessage;
760 log << TestLog::Message << "EGL_DEPTH_SIZE = " << depthBits << TestLog::EndMessage;
761 log << TestLog::Message << "EGL_STENCIL_SIZE = " << stencilBits << TestLog::EndMessage;
762 log << TestLog::Message << "EGL_SAMPLES = " << numSamples << TestLog::EndMessage;
764 // Generate draw ops.
765 drawOps.resize(numContexts*drawsPerCtx*numIters);
766 for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
767 randomizeDrawOp(rnd, *drawOp, (pixelFmt.alphaBits == 1));
769 // Create and setup programs per context
770 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
772 EGLint api = contexts[ctxNdx].first;
773 EGLContext context = contexts[ctxNdx].second;
775 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
777 programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
778 programs[ctxNdx]->setup();
781 // Clear to black using first context.
783 EGLint api = contexts[0].first;
784 EGLContext context = contexts[0].second;
786 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
788 clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
793 for (int iterNdx = 0; iterNdx < numIters; iterNdx++)
795 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
797 EGLint api = contexts[ctxNdx].first;
798 EGLContext context = contexts[ctxNdx].second;
800 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
802 for (int drawNdx = 0; drawNdx < drawsPerCtx; drawNdx++)
804 const DrawPrimitiveOp& drawOp = drawOps[iterNdx*numContexts*drawsPerCtx + ctxNdx*drawsPerCtx + drawNdx];
805 draw(m_gl, api, *programs[ctxNdx], drawOp);
812 // Read pixels using first context. \todo [pyry] Randomize?
814 EGLint api = contexts[0].first;
815 EGLContext context = contexts[0].second;
817 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
819 readPixels(m_gl, api, frame);
822 int subpixelBits = 0;
823 m_gl.getIntegerv(GL_SUBPIXEL_BITS, &subpixelBits);
825 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
828 // \note Reference image is always generated using single-sampling.
829 renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1, subpixelBits);
833 bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
836 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
840 // MultiThreadRenderCase
842 class MultiThreadRenderCase : public MultiContextRenderCase
845 MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi);
850 virtual void executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts);
855 class RenderTestThread;
857 typedef de::SharedPtr<RenderTestThread> RenderTestThreadSp;
858 typedef de::SharedPtr<de::Semaphore> SemaphoreSp;
868 const DrawPrimitiveOp* drawOps;
874 class RenderTestThread : public de::Thread
877 RenderTestThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const glw::Functions& gl, const Program& program, const std::vector<DrawOpPacket>& packets)
879 , m_display (display)
880 , m_surface (surface)
881 , m_context (context)
884 , m_program (program)
885 , m_packets (packets)
891 for (std::vector<DrawOpPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++)
893 // Wait until it is our turn.
894 packetIter->wait->decrement();
897 EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, m_surface, m_surface, m_context));
899 // Execute rendering.
900 for (int ndx = 0; ndx < packetIter->numOps; ndx++)
901 draw(m_gl, m_api, m_program, packetIter->drawOps[ndx]);
906 EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
908 // Signal completion.
909 packetIter->signal->increment();
911 m_egl.releaseThread();
915 const Library& m_egl;
916 EGLDisplay m_display;
917 EGLSurface m_surface;
918 EGLContext m_context;
920 const glw::Functions& m_gl;
921 const Program& m_program;
922 const std::vector<DrawOpPacket>& m_packets;
925 MultiThreadRenderCase::MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
926 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
930 void MultiThreadRenderCase::init (void)
932 MultiContextRenderCase::init();
933 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
936 void MultiThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
938 const Library& egl = m_eglTestCtx.getLibrary();
939 const int width = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
940 const int height = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
941 const int numContexts = (int)contexts.size();
942 const int opsPerPacket = 2;
943 const int packetsPerThread = 2;
944 const int numThreads = numContexts;
945 const int numPackets = numThreads * packetsPerThread;
946 const tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config);
947 const float threshold = getColorThreshold(pixelFmt);
949 const int depthBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
950 const int stencilBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
951 const int numSamples = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
953 TestLog& log = m_testCtx.getLog();
955 tcu::Surface refFrame (width, height);
956 tcu::Surface frame (width, height);
958 de::Random rnd (deStringHash(getName()) ^ deInt32Hash(numContexts));
960 // Resources that need cleanup
961 vector<ProgramSp> programs (numContexts);
962 vector<SemaphoreSp> semaphores (numPackets+1);
963 vector<DrawPrimitiveOp> drawOps (numPackets*opsPerPacket);
964 vector<vector<DrawOpPacket> > packets (numThreads);
965 vector<RenderTestThreadSp> threads (numThreads);
967 // Log basic information about config.
968 log << TestLog::Message << "EGL_RED_SIZE = " << pixelFmt.redBits << TestLog::EndMessage;
969 log << TestLog::Message << "EGL_GREEN_SIZE = " << pixelFmt.greenBits << TestLog::EndMessage;
970 log << TestLog::Message << "EGL_BLUE_SIZE = " << pixelFmt.blueBits << TestLog::EndMessage;
971 log << TestLog::Message << "EGL_ALPHA_SIZE = " << pixelFmt.alphaBits << TestLog::EndMessage;
972 log << TestLog::Message << "EGL_DEPTH_SIZE = " << depthBits << TestLog::EndMessage;
973 log << TestLog::Message << "EGL_STENCIL_SIZE = " << stencilBits << TestLog::EndMessage;
974 log << TestLog::Message << "EGL_SAMPLES = " << numSamples << TestLog::EndMessage;
976 // Initialize semaphores.
977 for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
978 *sem = SemaphoreSp(new de::Semaphore(0));
981 for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
982 randomizeDrawOp(rnd, *drawOp, (pixelFmt.alphaBits == 1));
985 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
987 packets[threadNdx].resize(packetsPerThread);
989 for (int packetNdx = 0; packetNdx < packetsPerThread; packetNdx++)
991 DrawOpPacket& packet = packets[threadNdx][packetNdx];
993 // Threads take turns with packets.
994 packet.wait = semaphores[packetNdx*numThreads + threadNdx];
995 packet.signal = semaphores[packetNdx*numThreads + threadNdx + 1];
996 packet.numOps = opsPerPacket;
997 packet.drawOps = &drawOps[(packetNdx*numThreads + threadNdx)*opsPerPacket];
1001 // Create and setup programs per context
1002 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
1004 EGLint api = contexts[ctxNdx].first;
1005 EGLContext context = contexts[ctxNdx].second;
1007 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1009 programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
1010 programs[ctxNdx]->setup();
1013 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1016 // Clear to black using first context.
1018 EGLint api = contexts[0].first;
1019 EGLContext context = contexts[0].second;
1021 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1023 clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
1027 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1030 // Create and launch threads (actual rendering starts once first semaphore is signaled).
1031 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
1033 threads[threadNdx] = RenderTestThreadSp(new RenderTestThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, m_gl, *programs[threadNdx], packets[threadNdx]));
1034 threads[threadNdx]->start();
1037 // Signal start and wait until complete.
1038 semaphores.front()->increment();
1039 semaphores.back()->decrement();
1041 // Read pixels using first context. \todo [pyry] Randomize?
1043 EGLint api = contexts[0].first;
1044 EGLContext context = contexts[0].second;
1046 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1048 readPixels(m_gl, api, frame);
1051 int subpixelBits = 0;
1052 m_gl.getIntegerv(GL_SUBPIXEL_BITS, &subpixelBits);
1054 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1057 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
1058 threads[threadNdx]->join();
1060 // Render reference.
1061 renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1, subpixelBits);
1065 bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
1068 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
1072 RenderTests::RenderTests (EglTestContext& eglTestCtx)
1073 : TestCaseGroup(eglTestCtx, "render", "Basic rendering with different client APIs")
1077 RenderTests::~RenderTests (void)
1081 struct RenderGroupSpec
1086 eglu::ConfigFilter baseFilter;
1087 int numContextsPerApi;
1090 template <deUint32 Bits>
1091 static bool renderable (const eglu::CandidateConfig& c)
1093 return (c.renderableType() & Bits) == Bits;
1096 template <class RenderClass>
1097 static void createRenderGroups (EglTestContext& eglTestCtx, tcu::TestCaseGroup* group, const RenderGroupSpec* first, const RenderGroupSpec* last)
1099 for (const RenderGroupSpec* groupIter = first; groupIter != last; groupIter++)
1101 tcu::TestCaseGroup* configGroup = new tcu::TestCaseGroup(eglTestCtx.getTestContext(), groupIter->name, groupIter->desc);
1102 group->addChild(configGroup);
1104 vector<RenderFilterList> filterLists;
1105 eglu::FilterList baseFilters;
1106 baseFilters << groupIter->baseFilter;
1107 getDefaultRenderFilterLists(filterLists, baseFilters);
1109 for (vector<RenderFilterList>::const_iterator listIter = filterLists.begin(); listIter != filterLists.end(); listIter++)
1110 configGroup->addChild(new RenderClass(eglTestCtx, listIter->getName(), "", groupIter->apiBits, listIter->getSurfaceTypeMask(), *listIter, groupIter->numContextsPerApi));
1114 void RenderTests::init (void)
1116 static const RenderGroupSpec singleContextCases[] =
1120 "Primitive rendering using GLES2",
1122 renderable<EGL_OPENGL_ES2_BIT>,
1127 "Primitive rendering using GLES3",
1129 renderable<EGL_OPENGL_ES3_BIT>,
1134 static const RenderGroupSpec multiContextCases[] =
1138 "Primitive rendering using multiple GLES2 contexts to shared surface",
1140 renderable<EGL_OPENGL_ES2_BIT>,
1145 "Primitive rendering using multiple GLES3 contexts to shared surface",
1147 renderable<EGL_OPENGL_ES3_BIT>,
1152 "Primitive rendering using multiple APIs to shared surface",
1153 EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT,
1154 renderable<EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT>,
1159 tcu::TestCaseGroup* singleContextGroup = new tcu::TestCaseGroup(m_testCtx, "single_context", "Single-context rendering");
1160 addChild(singleContextGroup);
1161 createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, singleContextGroup, &singleContextCases[0], &singleContextCases[DE_LENGTH_OF_ARRAY(singleContextCases)]);
1163 tcu::TestCaseGroup* multiContextGroup = new tcu::TestCaseGroup(m_testCtx, "multi_context", "Multi-context rendering with shared surface");
1164 addChild(multiContextGroup);
1165 createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, multiContextGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);
1167 tcu::TestCaseGroup* multiThreadGroup = new tcu::TestCaseGroup(m_testCtx, "multi_thread", "Multi-thread rendering with shared surface");
1168 addChild(multiThreadGroup);
1169 createRenderGroups<MultiThreadRenderCase>(m_eglTestCtx, multiThreadGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);