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)
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())));
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 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
825 // \note Reference image is always generated using single-sampling.
826 renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1);
830 bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
833 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
837 // MultiThreadRenderCase
839 class MultiThreadRenderCase : public MultiContextRenderCase
842 MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi);
847 virtual void executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts);
852 class RenderTestThread;
854 typedef de::SharedPtr<RenderTestThread> RenderTestThreadSp;
855 typedef de::SharedPtr<de::Semaphore> SemaphoreSp;
865 const DrawPrimitiveOp* drawOps;
871 class RenderTestThread : public de::Thread
874 RenderTestThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const glw::Functions& gl, const Program& program, const std::vector<DrawOpPacket>& packets)
876 , m_display (display)
877 , m_surface (surface)
878 , m_context (context)
881 , m_program (program)
882 , m_packets (packets)
888 for (std::vector<DrawOpPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++)
890 // Wait until it is our turn.
891 packetIter->wait->decrement();
894 EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, m_surface, m_surface, m_context));
896 // Execute rendering.
897 for (int ndx = 0; ndx < packetIter->numOps; ndx++)
898 draw(m_gl, m_api, m_program, packetIter->drawOps[ndx]);
903 EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
905 // Signal completion.
906 packetIter->signal->increment();
908 m_egl.releaseThread();
912 const Library& m_egl;
913 EGLDisplay m_display;
914 EGLSurface m_surface;
915 EGLContext m_context;
917 const glw::Functions& m_gl;
918 const Program& m_program;
919 const std::vector<DrawOpPacket>& m_packets;
922 MultiThreadRenderCase::MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi)
923 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi)
927 void MultiThreadRenderCase::init (void)
929 MultiContextRenderCase::init();
930 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
933 void MultiThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts)
935 const Library& egl = m_eglTestCtx.getLibrary();
936 const int width = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH);
937 const int height = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT);
938 const int numContexts = (int)contexts.size();
939 const int opsPerPacket = 2;
940 const int packetsPerThread = 2;
941 const int numThreads = numContexts;
942 const int numPackets = numThreads * packetsPerThread;
943 const tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config);
944 const float threshold = getColorThreshold(pixelFmt);
946 const int depthBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE);
947 const int stencilBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE);
948 const int numSamples = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES);
950 TestLog& log = m_testCtx.getLog();
952 tcu::Surface refFrame (width, height);
953 tcu::Surface frame (width, height);
955 de::Random rnd (deStringHash(getName()) ^ deInt32Hash(numContexts));
957 // Resources that need cleanup
958 vector<ProgramSp> programs (numContexts);
959 vector<SemaphoreSp> semaphores (numPackets+1);
960 vector<DrawPrimitiveOp> drawOps (numPackets*opsPerPacket);
961 vector<vector<DrawOpPacket> > packets (numThreads);
962 vector<RenderTestThreadSp> threads (numThreads);
964 // Log basic information about config.
965 log << TestLog::Message << "EGL_RED_SIZE = " << pixelFmt.redBits << TestLog::EndMessage;
966 log << TestLog::Message << "EGL_GREEN_SIZE = " << pixelFmt.greenBits << TestLog::EndMessage;
967 log << TestLog::Message << "EGL_BLUE_SIZE = " << pixelFmt.blueBits << TestLog::EndMessage;
968 log << TestLog::Message << "EGL_ALPHA_SIZE = " << pixelFmt.alphaBits << TestLog::EndMessage;
969 log << TestLog::Message << "EGL_DEPTH_SIZE = " << depthBits << TestLog::EndMessage;
970 log << TestLog::Message << "EGL_STENCIL_SIZE = " << stencilBits << TestLog::EndMessage;
971 log << TestLog::Message << "EGL_SAMPLES = " << numSamples << TestLog::EndMessage;
973 // Initialize semaphores.
974 for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem)
975 *sem = SemaphoreSp(new de::Semaphore(0));
978 for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp)
979 randomizeDrawOp(rnd, *drawOp, (pixelFmt.alphaBits == 1));
982 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
984 packets[threadNdx].resize(packetsPerThread);
986 for (int packetNdx = 0; packetNdx < packetsPerThread; packetNdx++)
988 DrawOpPacket& packet = packets[threadNdx][packetNdx];
990 // Threads take turns with packets.
991 packet.wait = semaphores[packetNdx*numThreads + threadNdx];
992 packet.signal = semaphores[packetNdx*numThreads + threadNdx + 1];
993 packet.numOps = opsPerPacket;
994 packet.drawOps = &drawOps[(packetNdx*numThreads + threadNdx)*opsPerPacket];
998 // Create and setup programs per context
999 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++)
1001 EGLint api = contexts[ctxNdx].first;
1002 EGLContext context = contexts[ctxNdx].second;
1004 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1006 programs[ctxNdx] = ProgramSp(createProgram(m_gl, api));
1007 programs[ctxNdx]->setup();
1010 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1013 // Clear to black using first context.
1015 EGLint api = contexts[0].first;
1016 EGLContext context = contexts[0].second;
1018 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1020 clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL);
1024 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1027 // Create and launch threads (actual rendering starts once first semaphore is signaled).
1028 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
1030 threads[threadNdx] = RenderTestThreadSp(new RenderTestThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, m_gl, *programs[threadNdx], packets[threadNdx]));
1031 threads[threadNdx]->start();
1034 // Signal start and wait until complete.
1035 semaphores.front()->increment();
1036 semaphores.back()->decrement();
1038 // Read pixels using first context. \todo [pyry] Randomize?
1040 EGLint api = contexts[0].first;
1041 EGLContext context = contexts[0].second;
1043 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context));
1045 readPixels(m_gl, api, frame);
1048 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1051 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++)
1052 threads[threadNdx]->join();
1054 // Render reference.
1055 renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1);
1059 bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT);
1062 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
1066 RenderTests::RenderTests (EglTestContext& eglTestCtx)
1067 : TestCaseGroup(eglTestCtx, "render", "Basic rendering with different client APIs")
1071 RenderTests::~RenderTests (void)
1075 struct RenderGroupSpec
1080 eglu::ConfigFilter baseFilter;
1081 int numContextsPerApi;
1084 template <deUint32 Bits>
1085 static bool renderable (const eglu::CandidateConfig& c)
1087 return (c.renderableType() & Bits) == Bits;
1090 template <class RenderClass>
1091 static void createRenderGroups (EglTestContext& eglTestCtx, tcu::TestCaseGroup* group, const RenderGroupSpec* first, const RenderGroupSpec* last)
1093 for (const RenderGroupSpec* groupIter = first; groupIter != last; groupIter++)
1095 tcu::TestCaseGroup* configGroup = new tcu::TestCaseGroup(eglTestCtx.getTestContext(), groupIter->name, groupIter->desc);
1096 group->addChild(configGroup);
1098 vector<RenderFilterList> filterLists;
1099 eglu::FilterList baseFilters;
1100 baseFilters << groupIter->baseFilter;
1101 getDefaultRenderFilterLists(filterLists, baseFilters);
1103 for (vector<RenderFilterList>::const_iterator listIter = filterLists.begin(); listIter != filterLists.end(); listIter++)
1104 configGroup->addChild(new RenderClass(eglTestCtx, listIter->getName(), "", groupIter->apiBits, listIter->getSurfaceTypeMask(), *listIter, groupIter->numContextsPerApi));
1108 void RenderTests::init (void)
1110 static const RenderGroupSpec singleContextCases[] =
1114 "Primitive rendering using GLES2",
1116 renderable<EGL_OPENGL_ES2_BIT>,
1121 "Primitive rendering using GLES3",
1123 renderable<EGL_OPENGL_ES3_BIT>,
1128 static const RenderGroupSpec multiContextCases[] =
1132 "Primitive rendering using multiple GLES2 contexts to shared surface",
1134 renderable<EGL_OPENGL_ES2_BIT>,
1139 "Primitive rendering using multiple GLES3 contexts to shared surface",
1141 renderable<EGL_OPENGL_ES3_BIT>,
1146 "Primitive rendering using multiple APIs to shared surface",
1147 EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT,
1148 renderable<EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT>,
1153 tcu::TestCaseGroup* singleContextGroup = new tcu::TestCaseGroup(m_testCtx, "single_context", "Single-context rendering");
1154 addChild(singleContextGroup);
1155 createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, singleContextGroup, &singleContextCases[0], &singleContextCases[DE_LENGTH_OF_ARRAY(singleContextCases)]);
1157 tcu::TestCaseGroup* multiContextGroup = new tcu::TestCaseGroup(m_testCtx, "multi_context", "Multi-context rendering with shared surface");
1158 addChild(multiContextGroup);
1159 createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, multiContextGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);
1161 tcu::TestCaseGroup* multiThreadGroup = new tcu::TestCaseGroup(m_testCtx, "multi_thread", "Multi-thread rendering with shared surface");
1162 addChild(multiThreadGroup);
1163 createRenderGroups<MultiThreadRenderCase>(m_eglTestCtx, multiThreadGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]);