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)
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);
177 for (int coordNdx = 0; coordNdx < 3; coordNdx++)
179 tcu::Vec4& position = drawOp.positions[triNdx*3 + coordNdx];
180 tcu::Vec4& color = drawOp.colors[triNdx*3 + coordNdx];
182 position.x() = cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
183 position.y() = cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
184 position.z() = rnd.getFloat(minDepth, maxDepth);
187 color.x() = rnd.getFloat(minRGB, maxRGB);
188 color.y() = rnd.getFloat(minRGB, maxRGB);
189 color.z() = rnd.getFloat(minRGB, maxRGB);
190 color.w() = rnd.getFloat(minAlpha, maxAlpha);
193 // avoid generating narrow triangles
195 const int maxAttempts = 40;
197 tcu::Vec4& p0 = drawOp.positions[triNdx*3 + 0];
198 tcu::Vec4& p1 = drawOp.positions[triNdx*3 + 1];
199 tcu::Vec4& p2 = drawOp.positions[triNdx*3 + 2];
201 while (isANarrowScreenSpaceTriangle(p0, p1, p2))
203 p1.x() = cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
204 p1.y() = cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
205 p1.z() = rnd.getFloat(minDepth, maxDepth);
208 p2.x() = cx + rnd.getFloat(-maxTriOffset, maxTriOffset);
209 p2.y() = cy + rnd.getFloat(-maxTriOffset, maxTriOffset);
210 p2.z() = rnd.getFloat(minDepth, maxDepth);
213 if (++numAttempts > maxAttempts)
226 // Reference rendering code
228 class ReferenceShader : public rr::VertexShader, public rr::FragmentShader
237 : rr::VertexShader (2, 1) // color and pos in => color out
238 , rr::FragmentShader(1, 1) // color in => color out
240 this->rr::VertexShader::m_inputs[0].type = rr::GENERICVECTYPE_FLOAT;
241 this->rr::VertexShader::m_inputs[1].type = rr::GENERICVECTYPE_FLOAT;
243 this->rr::VertexShader::m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
244 this->rr::VertexShader::m_outputs[0].flatshade = false;
246 this->rr::FragmentShader::m_inputs[0].type = rr::GENERICVECTYPE_FLOAT;
247 this->rr::FragmentShader::m_inputs[0].flatshade = false;
249 this->rr::FragmentShader::m_outputs[0].type = rr::GENERICVECTYPE_FLOAT;
252 void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const
254 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
256 const int positionAttrLoc = 0;
257 const int colorAttrLoc = 1;
259 rr::VertexPacket& packet = *packets[packetNdx];
261 // Transform to position
262 packet.position = rr::readVertexAttribFloat(inputs[positionAttrLoc], packet.instanceNdx, packet.vertexNdx);
265 packet.outputs[VaryingLoc_Color] = rr::readVertexAttribFloat(inputs[colorAttrLoc], packet.instanceNdx, packet.vertexNdx);
269 void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
271 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
273 rr::FragmentPacket& packet = packets[packetNdx];
275 for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
276 rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, rr::readVarying<float>(packet, context, VaryingLoc_Color, fragNdx));
281 void toReferenceRenderState (rr::RenderState& state, const DrawPrimitiveOp& drawOp)
283 state.cullMode = rr::CULLMODE_NONE;
285 if (drawOp.blend != BLENDMODE_NONE)
287 state.fragOps.blendMode = rr::BLENDMODE_STANDARD;
289 switch (drawOp.blend)
291 case BLENDMODE_ADDITIVE:
292 state.fragOps.blendRGBState.srcFunc = rr::BLENDFUNC_ONE;
293 state.fragOps.blendRGBState.dstFunc = rr::BLENDFUNC_ONE;
294 state.fragOps.blendRGBState.equation = rr::BLENDEQUATION_ADD;
295 state.fragOps.blendAState = state.fragOps.blendRGBState;
298 case BLENDMODE_SRC_OVER:
299 state.fragOps.blendRGBState.srcFunc = rr::BLENDFUNC_SRC_ALPHA;
300 state.fragOps.blendRGBState.dstFunc = rr::BLENDFUNC_ONE_MINUS_SRC_ALPHA;
301 state.fragOps.blendRGBState.equation = rr::BLENDEQUATION_ADD;
302 state.fragOps.blendAState = state.fragOps.blendRGBState;
310 if (drawOp.depth != DEPTHMODE_NONE)
312 state.fragOps.depthTestEnabled = true;
314 DE_ASSERT(drawOp.depth == DEPTHMODE_LESS);
315 state.fragOps.depthFunc = rr::TESTFUNC_LESS;
318 if (drawOp.stencil != STENCILMODE_NONE)
320 state.fragOps.stencilTestEnabled = true;
322 DE_ASSERT(drawOp.stencil == STENCILMODE_LEQUAL_INC);
323 state.fragOps.stencilStates[0].func = rr::TESTFUNC_LEQUAL;
324 state.fragOps.stencilStates[0].sFail = rr::STENCILOP_KEEP;
325 state.fragOps.stencilStates[0].dpFail = rr::STENCILOP_INCR;
326 state.fragOps.stencilStates[0].dpPass = rr::STENCILOP_INCR;
327 state.fragOps.stencilStates[0].ref = drawOp.stencilRef;
328 state.fragOps.stencilStates[1] = state.fragOps.stencilStates[0];
332 tcu::TextureFormat getColorFormat (const tcu::PixelFormat& colorBits)
334 using tcu::TextureFormat;
336 DE_ASSERT(de::inBounds(colorBits.redBits, 0, 0xff) &&
337 de::inBounds(colorBits.greenBits, 0, 0xff) &&
338 de::inBounds(colorBits.blueBits, 0, 0xff) &&
339 de::inBounds(colorBits.alphaBits, 0, 0xff));
341 #define PACK_FMT(R, G, B, A) (((R) << 24) | ((G) << 16) | ((B) << 8) | (A))
343 // \note [pyry] This may not hold true on some implementations - best effort guess only.
344 switch (PACK_FMT(colorBits.redBits, colorBits.greenBits, colorBits.blueBits, colorBits.alphaBits))
346 case PACK_FMT(8,8,8,8): return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8);
347 case PACK_FMT(8,8,8,0): return TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8);
348 case PACK_FMT(4,4,4,4): return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_SHORT_4444);
349 case PACK_FMT(5,5,5,1): return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_SHORT_5551);
350 case PACK_FMT(5,6,5,0): return TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_SHORT_565);
352 // \note Defaults to RGBA8
353 default: return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8);
360 The getColorThreshold function is used to obtain a
361 threshold usable for the fuzzyCompare function.
363 For 8bit color depths a value of 0.02 should provide
364 a good metric for rejecting images above this level.
365 For other bit depths other thresholds should be selected.
366 Ideally this function would take advantage of the
367 getColorThreshold function provided by the PixelFormat class
368 as this would also allow setting per channel thresholds.
369 However using the PixelFormat provided function can result
370 in too strict thresholds for 8bit bit depths (compared to
371 the current default of 0.02) or too relaxed for lower bit
372 depths if scaled proportionally to the 8bit default.
375 float getColorThreshold (const tcu::PixelFormat& colorBits)
377 if ((colorBits.redBits > 0 && colorBits.redBits < 8) ||
378 (colorBits.greenBits > 0 && colorBits.greenBits < 8) ||
379 (colorBits.blueBits > 0 && colorBits.blueBits < 8) ||
380 (colorBits.alphaBits > 0 && colorBits.alphaBits < 8))
390 tcu::TextureFormat getDepthFormat (const int depthBits)
394 case 0: return tcu::TextureFormat();
395 case 8: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT8);
396 case 16: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT16);
397 case 24: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT24);
399 default: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT);
403 tcu::TextureFormat getStencilFormat (int stencilBits)
407 case 0: return tcu::TextureFormat();
409 default: return tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT8);
413 void renderReference (const tcu::PixelBufferAccess& dst, const vector<DrawPrimitiveOp>& drawOps, const tcu::PixelFormat& colorBits, const int depthBits, const int stencilBits, const int numSamples)
415 const int width = dst.getWidth();
416 const int height = dst.getHeight();
418 tcu::TextureLevel colorBuffer;
419 tcu::TextureLevel depthBuffer;
420 tcu::TextureLevel stencilBuffer;
422 rr::Renderer referenceRenderer;
423 rr::VertexAttrib attributes[2];
424 const ReferenceShader shader;
426 attributes[0].type = rr::VERTEXATTRIBTYPE_FLOAT;
427 attributes[0].size = 4;
428 attributes[0].stride = 0;
429 attributes[0].instanceDivisor = 0;
431 attributes[1].type = rr::VERTEXATTRIBTYPE_FLOAT;
432 attributes[1].size = 4;
433 attributes[1].stride = 0;
434 attributes[1].instanceDivisor = 0;
436 // Initialize buffers.
437 colorBuffer.setStorage(getColorFormat(colorBits), numSamples, width, height);
438 rr::clearMultisampleColorBuffer(colorBuffer, CLEAR_COLOR, rr::WindowRectangle(0, 0, width, height));
442 depthBuffer.setStorage(getDepthFormat(depthBits), numSamples, width, height);
443 rr::clearMultisampleDepthBuffer(depthBuffer, CLEAR_DEPTH, rr::WindowRectangle(0, 0, width, height));
448 stencilBuffer.setStorage(getStencilFormat(stencilBits), numSamples, width, height);
449 rr::clearMultisampleStencilBuffer(stencilBuffer, CLEAR_STENCIL, rr::WindowRectangle(0, 0, width, height));
452 const rr::RenderTarget renderTarget(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()),
453 rr::MultisamplePixelBufferAccess::fromMultisampleAccess(depthBuffer.getAccess()),
454 rr::MultisamplePixelBufferAccess::fromMultisampleAccess(stencilBuffer.getAccess()));
456 for (vector<DrawPrimitiveOp>::const_iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); drawOp++)
459 rr::RenderState renderState((rr::ViewportState)(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess())));
460 toReferenceRenderState(renderState, *drawOp);
462 DE_ASSERT(drawOp->type == PRIMITIVETYPE_TRIANGLE);
464 attributes[0].pointer = &drawOp->positions[0];
465 attributes[1].pointer = &drawOp->colors[0];
467 referenceRenderer.draw(
471 rr::Program(static_cast<const rr::VertexShader*>(&shader), static_cast<const rr::FragmentShader*>(&shader)),
474 rr::PrimitiveList(rr::PRIMITIVETYPE_TRIANGLES, drawOp->count * 3, 0)));
477 rr::resolveMultisampleColorBuffer(dst, rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()));
480 // API rendering code
486 virtual ~Program (void) {}
488 virtual void setup (void) const = DE_NULL;
491 typedef de::SharedPtr<Program> ProgramSp;
493 static glu::ProgramSources getProgramSourcesES2 (void)
495 static const char* s_vertexSrc =
496 "attribute highp vec4 a_position;\n"
497 "attribute mediump vec4 a_color;\n"
498 "varying mediump vec4 v_color;\n"
501 " gl_Position = a_position;\n"
502 " v_color = a_color;\n"
505 static const char* s_fragmentSrc =
506 "varying mediump vec4 v_color;\n"
509 " gl_FragColor = v_color;\n"
512 return glu::ProgramSources() << glu::VertexSource(s_vertexSrc) << glu::FragmentSource(s_fragmentSrc);
515 class GLES2Program : public Program
518 GLES2Program (const glw::Functions& gl)
520 , m_program (gl, getProgramSourcesES2())
525 m_positionLoc = m_gl.getAttribLocation(m_program.getProgram(), "a_position");
526 m_colorLoc = m_gl.getAttribLocation(m_program.getProgram(), "a_color");
533 void setup (void) const
535 m_gl.useProgram(m_program.getProgram());
536 m_gl.enableVertexAttribArray(m_positionLoc);
537 m_gl.enableVertexAttribArray(m_colorLoc);
538 GLU_CHECK_GLW_MSG(m_gl, "Program setup failed");
541 int getPositionLoc (void) const { return m_positionLoc; }
542 int getColorLoc (void) const { return m_colorLoc; }
545 const glw::Functions& m_gl;
546 glu::ShaderProgram m_program;
551 void clearGLES2 (const glw::Functions& gl, const tcu::Vec4& color, const float depth, const int stencil)
553 gl.clearColor(color.x(), color.y(), color.z(), color.w());
554 gl.clearDepthf(depth);
555 gl.clearStencil(stencil);
556 gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
559 void drawGLES2 (const glw::Functions& gl, const Program& program, const DrawPrimitiveOp& drawOp)
561 const GLES2Program& gles2Program = dynamic_cast<const GLES2Program&>(program);
563 switch (drawOp.blend)
566 gl.disable(GL_BLEND);
569 case BLENDMODE_ADDITIVE:
571 gl.blendFunc(GL_ONE, GL_ONE);
574 case BLENDMODE_SRC_OVER:
576 gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
583 switch (drawOp.depth)
586 gl.disable(GL_DEPTH_TEST);
590 gl.enable(GL_DEPTH_TEST);
597 switch (drawOp.stencil)
599 case STENCILMODE_NONE:
600 gl.disable(GL_STENCIL_TEST);
603 case STENCILMODE_LEQUAL_INC:
604 gl.enable(GL_STENCIL_TEST);
605 gl.stencilFunc(GL_LEQUAL, drawOp.stencilRef, ~0u);
606 gl.stencilOp(GL_KEEP, GL_INCR, GL_INCR);
613 gl.disable(GL_DITHER);
615 gl.vertexAttribPointer(gles2Program.getPositionLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.positions[0]);
616 gl.vertexAttribPointer(gles2Program.getColorLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.colors[0]);
618 DE_ASSERT(drawOp.type == PRIMITIVETYPE_TRIANGLE);
619 gl.drawArrays(GL_TRIANGLES, 0, drawOp.count*3);
622 static void readPixelsGLES2 (const glw::Functions& gl, tcu::Surface& dst)
624 gl.readPixels(0, 0, dst.getWidth(), dst.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, dst.getAccess().getDataPtr());
627 Program* createProgram (const glw::Functions& gl, EGLint api)
631 case EGL_OPENGL_ES2_BIT: return new GLES2Program(gl);
632 case EGL_OPENGL_ES3_BIT_KHR: return new GLES2Program(gl);
634 throw tcu::NotSupportedError("Unsupported API");
638 void draw (const glw::Functions& gl, EGLint api, const Program& program, const DrawPrimitiveOp& drawOp)
642 case EGL_OPENGL_ES2_BIT: drawGLES2(gl, program, drawOp); break;
643 case EGL_OPENGL_ES3_BIT_KHR: drawGLES2(gl, program, drawOp); break;
645 throw tcu::NotSupportedError("Unsupported API");
649 void clear (const glw::Functions& gl, EGLint api, const tcu::Vec4& color, const float depth, const int stencil)
653 case EGL_OPENGL_ES2_BIT: clearGLES2(gl, color, depth, stencil); break;
654 case EGL_OPENGL_ES3_BIT_KHR: clearGLES2(gl, color, depth, stencil); break;
656 throw tcu::NotSupportedError("Unsupported API");
660 static void readPixels (const glw::Functions& gl, EGLint api, tcu::Surface& dst)
664 case EGL_OPENGL_ES2_BIT: readPixelsGLES2(gl, dst); break;
665 case EGL_OPENGL_ES3_BIT_KHR: readPixelsGLES2(gl, dst); break;
667 throw tcu::NotSupportedError("Unsupported API");
671 static void finish (const glw::Functions& gl, EGLint api)
675 case EGL_OPENGL_ES2_BIT:
676 case EGL_OPENGL_ES3_BIT_KHR:
681 throw tcu::NotSupportedError("Unsupported API");
685 tcu::PixelFormat getPixelFormat (const Library& egl, EGLDisplay display, EGLConfig config)
687 tcu::PixelFormat fmt;
688 fmt.redBits = eglu::getConfigAttribInt(egl, display, config, EGL_RED_SIZE);
689 fmt.greenBits = eglu::getConfigAttribInt(egl, display, config, EGL_GREEN_SIZE);
690 fmt.blueBits = eglu::getConfigAttribInt(egl, display, config, EGL_BLUE_SIZE);
691 fmt.alphaBits = eglu::getConfigAttribInt(egl, display, config, EGL_ALPHA_SIZE);
697 // SingleThreadRenderCase
699 class SingleThreadRenderCase : public MultiContextRenderCase
702 SingleThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi);
707 virtual void executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts);
712 // SingleThreadColorClearCase
714 SingleThreadRenderCase::SingleThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
715 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
719 void SingleThreadRenderCase::init (void)
721 MultiContextRenderCase::init();
722 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
725 void SingleThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
727 const Library& egl = m_eglTestCtx.getLibrary();
728 const int width = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
729 const int height = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
730 const int numContexts = (int)contexts.size();
731 const int drawsPerCtx = 2;
732 const int numIters = 2;
733 const tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config);
734 const float threshold = getColorThreshold(pixelFmt);
736 const int depthBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
737 const int stencilBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
738 const int numSamples = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
740 TestLog& log = m_testCtx.getLog();
742 tcu::Surface refFrame (width, height);
743 tcu::Surface frame (width, height);
745 de::Random rnd (deStringHash(getName()) ^ deInt32Hash(numContexts));
746 vector<ProgramSp> programs (contexts.size());
747 vector<DrawPrimitiveOp> drawOps;
749 // Log basic information about config.
750 log << TestLog::Message << "EGL_RED_SIZE = " << pixelFmt.redBits << TestLog::EndMessage;
751 log << TestLog::Message << "EGL_GREEN_SIZE = " << pixelFmt.greenBits << TestLog::EndMessage;
752 log << TestLog::Message << "EGL_BLUE_SIZE = " << pixelFmt.blueBits << TestLog::EndMessage;
753 log << TestLog::Message << "EGL_ALPHA_SIZE = " << pixelFmt.alphaBits << TestLog::EndMessage;
754 log << TestLog::Message << "EGL_DEPTH_SIZE = " << depthBits << TestLog::EndMessage;
755 log << TestLog::Message << "EGL_STENCIL_SIZE = " << stencilBits << TestLog::EndMessage;
756 log << TestLog::Message << "EGL_SAMPLES = " << numSamples << TestLog::EndMessage;
758 // Generate draw ops.
759 drawOps.resize(numContexts*drawsPerCtx*numIters);
760 for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
761 randomizeDrawOp(rnd, *drawOp);
763 // Create and setup programs per context
764 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
766 EGLint api = contexts[ctxNdx].first;
767 EGLContext context = contexts[ctxNdx].second;
769 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
771 programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
772 programs[ctxNdx]->setup();
775 // Clear to black using first context.
777 EGLint api = contexts[0].first;
778 EGLContext context = contexts[0].second;
780 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
782 clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
787 for (int iterNdx = 0; iterNdx < numIters; iterNdx++)
789 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
791 EGLint api = contexts[ctxNdx].first;
792 EGLContext context = contexts[ctxNdx].second;
794 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
796 for (int drawNdx = 0; drawNdx < drawsPerCtx; drawNdx++)
798 const DrawPrimitiveOp& drawOp = drawOps[iterNdx*numContexts*drawsPerCtx + ctxNdx*drawsPerCtx + drawNdx];
799 draw(m_gl, api, *programs[ctxNdx], drawOp);
806 // Read pixels using first context. \todo [pyry] Randomize?
808 EGLint api = contexts[0].first;
809 EGLContext context = contexts[0].second;
811 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
813 readPixels(m_gl, api, frame);
816 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
819 // \note Reference image is always generated using single-sampling.
820 renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1);
824 bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
827 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
831 // MultiThreadRenderCase
833 class MultiThreadRenderCase : public MultiContextRenderCase
836 MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi);
841 virtual void executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts);
846 class RenderTestThread;
848 typedef de::SharedPtr<RenderTestThread> RenderTestThreadSp;
849 typedef de::SharedPtr<de::Semaphore> SemaphoreSp;
859 const DrawPrimitiveOp* drawOps;
865 class RenderTestThread : public de::Thread
868 RenderTestThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const glw::Functions& gl, const Program& program, const std::vector<DrawOpPacket>& packets)
870 , m_display (display)
871 , m_surface (surface)
872 , m_context (context)
875 , m_program (program)
876 , m_packets (packets)
882 for (std::vector<DrawOpPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++)
884 // Wait until it is our turn.
885 packetIter->wait->decrement();
888 EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, m_surface, m_surface, m_context));
890 // Execute rendering.
891 for (int ndx = 0; ndx < packetIter->numOps; ndx++)
892 draw(m_gl, m_api, m_program, packetIter->drawOps[ndx]);
897 EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
899 // Signal completion.
900 packetIter->signal->increment();
902 m_egl.releaseThread();
906 const Library& m_egl;
907 EGLDisplay m_display;
908 EGLSurface m_surface;
909 EGLContext m_context;
911 const glw::Functions& m_gl;
912 const Program& m_program;
913 const std::vector<DrawOpPacket>& m_packets;
916 MultiThreadRenderCase::MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
917 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
921 void MultiThreadRenderCase::init (void)
923 MultiContextRenderCase::init();
924 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
927 void MultiThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
929 const Library& egl = m_eglTestCtx.getLibrary();
930 const int width = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
931 const int height = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
932 const int numContexts = (int)contexts.size();
933 const int opsPerPacket = 2;
934 const int packetsPerThread = 2;
935 const int numThreads = numContexts;
936 const int numPackets = numThreads * packetsPerThread;
937 const tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config);
938 const float threshold = getColorThreshold(pixelFmt);
940 const int depthBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
941 const int stencilBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
942 const int numSamples = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
944 TestLog& log = m_testCtx.getLog();
946 tcu::Surface refFrame (width, height);
947 tcu::Surface frame (width, height);
949 de::Random rnd (deStringHash(getName()) ^ deInt32Hash(numContexts));
951 // Resources that need cleanup
952 vector<ProgramSp> programs (numContexts);
953 vector<SemaphoreSp> semaphores (numPackets+1);
954 vector<DrawPrimitiveOp> drawOps (numPackets*opsPerPacket);
955 vector<vector<DrawOpPacket> > packets (numThreads);
956 vector<RenderTestThreadSp> threads (numThreads);
958 // Log basic information about config.
959 log << TestLog::Message << "EGL_RED_SIZE = " << pixelFmt.redBits << TestLog::EndMessage;
960 log << TestLog::Message << "EGL_GREEN_SIZE = " << pixelFmt.greenBits << TestLog::EndMessage;
961 log << TestLog::Message << "EGL_BLUE_SIZE = " << pixelFmt.blueBits << TestLog::EndMessage;
962 log << TestLog::Message << "EGL_ALPHA_SIZE = " << pixelFmt.alphaBits << TestLog::EndMessage;
963 log << TestLog::Message << "EGL_DEPTH_SIZE = " << depthBits << TestLog::EndMessage;
964 log << TestLog::Message << "EGL_STENCIL_SIZE = " << stencilBits << TestLog::EndMessage;
965 log << TestLog::Message << "EGL_SAMPLES = " << numSamples << TestLog::EndMessage;
967 // Initialize semaphores.
968 for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
969 *sem = SemaphoreSp(new de::Semaphore(0));
972 for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
973 randomizeDrawOp(rnd, *drawOp);
976 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
978 packets[threadNdx].resize(packetsPerThread);
980 for (int packetNdx = 0; packetNdx < packetsPerThread; packetNdx++)
982 DrawOpPacket& packet = packets[threadNdx][packetNdx];
984 // Threads take turns with packets.
985 packet.wait = semaphores[packetNdx*numThreads + threadNdx];
986 packet.signal = semaphores[packetNdx*numThreads + threadNdx + 1];
987 packet.numOps = opsPerPacket;
988 packet.drawOps = &drawOps[(packetNdx*numThreads + threadNdx)*opsPerPacket];
992 // Create and setup programs per context
993 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
995 EGLint api = contexts[ctxNdx].first;
996 EGLContext context = contexts[ctxNdx].second;
998 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1000 programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
1001 programs[ctxNdx]->setup();
1004 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1007 // Clear to black using first context.
1009 EGLint api = contexts[0].first;
1010 EGLContext context = contexts[0].second;
1012 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1014 clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
1018 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1021 // Create and launch threads (actual rendering starts once first semaphore is signaled).
1022 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
1024 threads[threadNdx] = RenderTestThreadSp(new RenderTestThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, m_gl, *programs[threadNdx], packets[threadNdx]));
1025 threads[threadNdx]->start();
1028 // Signal start and wait until complete.
1029 semaphores.front()->increment();
1030 semaphores.back()->decrement();
1032 // Read pixels using first context. \todo [pyry] Randomize?
1034 EGLint api = contexts[0].first;
1035 EGLContext context = contexts[0].second;
1037 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1039 readPixels(m_gl, api, frame);
1042 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1045 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
1046 threads[threadNdx]->join();
1048 // Render reference.
1049 renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1);
1053 bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
1056 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
1060 RenderTests::RenderTests (EglTestContext& eglTestCtx)
1061 : TestCaseGroup(eglTestCtx, "render", "Basic rendering with different client APIs")
1065 RenderTests::~RenderTests (void)
1069 struct RenderGroupSpec
1074 eglu::ConfigFilter baseFilter;
1075 int numContextsPerApi;
1078 template <deUint32 Bits>
1079 static bool renderable (const eglu::CandidateConfig& c)
1081 return (c.renderableType() & Bits) == Bits;
1084 template <class RenderClass>
1085 static void createRenderGroups (EglTestContext& eglTestCtx, tcu::TestCaseGroup* group, const RenderGroupSpec* first, const RenderGroupSpec* last)
1087 for (const RenderGroupSpec* groupIter = first; groupIter != last; groupIter++)
1089 tcu::TestCaseGroup* configGroup = new tcu::TestCaseGroup(eglTestCtx.getTestContext(), groupIter->name, groupIter->desc);
1090 group->addChild(configGroup);
1092 vector<RenderFilterList> filterLists;
1093 eglu::FilterList baseFilters;
1094 baseFilters << groupIter->baseFilter;
1095 getDefaultRenderFilterLists(filterLists, baseFilters);
1097 for (vector<RenderFilterList>::const_iterator listIter = filterLists.begin(); listIter != filterLists.end(); listIter++)
1098 configGroup->addChild(new RenderClass(eglTestCtx, listIter->getName(), "", groupIter->apiBits, listIter->getSurfaceTypeMask(), *listIter, groupIter->numContextsPerApi));
1102 void RenderTests::init (void)
1104 static const RenderGroupSpec singleContextCases[] =
1108 "Primitive rendering using GLES2",
1110 renderable<EGL_OPENGL_ES2_BIT>,
1115 "Primitive rendering using GLES3",
1117 renderable<EGL_OPENGL_ES3_BIT>,
1122 static const RenderGroupSpec multiContextCases[] =
1126 "Primitive rendering using multiple GLES2 contexts to shared surface",
1128 renderable<EGL_OPENGL_ES2_BIT>,
1133 "Primitive rendering using multiple GLES3 contexts to shared surface",
1135 renderable<EGL_OPENGL_ES3_BIT>,
1140 "Primitive rendering using multiple APIs to shared surface",
1141 EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT,
1142 renderable<EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT>,
1147 tcu::TestCaseGroup* singleContextGroup = new tcu::TestCaseGroup(m_testCtx, "single_context", "Single-context rendering");
1148 addChild(singleContextGroup);
1149 createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, singleContextGroup, &singleContextCases[0], &singleContextCases[DE_LENGTH_OF_ARRAY(singleContextCases)]);
1151 tcu::TestCaseGroup* multiContextGroup = new tcu::TestCaseGroup(m_testCtx, "multi_context", "Multi-context rendering with shared surface");
1152 addChild(multiContextGroup);
1153 createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, multiContextGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);
1155 tcu::TestCaseGroup* multiThreadGroup = new tcu::TestCaseGroup(m_testCtx, "multi_thread", "Multi-thread rendering with shared surface");
1156 addChild(multiThreadGroup);
1157 createRenderGroups<MultiThreadRenderCase>(m_eglTestCtx, multiThreadGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);