teglRenderTests.cpp revision 3c67e4f0ec73f9c30c6b2ed2adfbfe7faaf576a4
1/*------------------------------------------------------------------------- 2 * drawElements Quality Program EGL Module 3 * --------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 *//*! 20 * \file 21 * \brief Rendering tests for different config and api combinations. 22 * \todo [2013-03-19 pyry] GLES1 and VG support. 23 *//*--------------------------------------------------------------------*/ 24 25#include "teglRenderTests.hpp" 26#include "teglRenderCase.hpp" 27 28#include "tcuRenderTarget.hpp" 29#include "tcuTestLog.hpp" 30#include "tcuImageCompare.hpp" 31#include "tcuTextureUtil.hpp" 32#include "tcuSurface.hpp" 33 34#include "egluDefs.hpp" 35#include "egluUtil.hpp" 36 37#include "eglwLibrary.hpp" 38#include "eglwEnums.hpp" 39 40#include "gluShaderProgram.hpp" 41 42#include "glwFunctions.hpp" 43#include "glwEnums.hpp" 44 45#include "deRandom.hpp" 46#include "deSharedPtr.hpp" 47#include "deSemaphore.hpp" 48#include "deThread.hpp" 49#include "deString.h" 50 51#include "rrRenderer.hpp" 52#include "rrFragmentOperations.hpp" 53 54#include <algorithm> 55#include <iterator> 56#include <memory> 57#include <set> 58 59namespace deqp 60{ 61namespace egl 62{ 63 64using std::string; 65using std::vector; 66using std::set; 67 68using tcu::Vec4; 69 70using tcu::TestLog; 71 72using namespace glw; 73using namespace eglw; 74 75static const tcu::Vec4 CLEAR_COLOR = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f); 76static const float CLEAR_DEPTH = 1.0f; 77static const int CLEAR_STENCIL = 0; 78 79namespace 80{ 81 82enum PrimitiveType 83{ 84 PRIMITIVETYPE_TRIANGLE = 0, //!< Triangles, requires 3 coordinates per primitive 85// PRIMITIVETYPE_POINT, //!< Points, requires 1 coordinate per primitive (w is used as size) 86// PRIMITIVETYPE_LINE, //!< Lines, requires 2 coordinates per primitive 87 88 PRIMITIVETYPE_LAST 89}; 90 91enum BlendMode 92{ 93 BLENDMODE_NONE = 0, //!< No blending 94 BLENDMODE_ADDITIVE, //!< Blending with ONE, ONE 95 BLENDMODE_SRC_OVER, //!< Blending with SRC_ALPHA, ONE_MINUS_SRC_ALPHA 96 97 BLENDMODE_LAST 98}; 99 100enum DepthMode 101{ 102 DEPTHMODE_NONE = 0, //!< No depth test or depth writes 103 DEPTHMODE_LESS, //!< Depth test with less & depth write 104 105 DEPTHMODE_LAST 106}; 107 108enum StencilMode 109{ 110 STENCILMODE_NONE = 0, //!< No stencil test or write 111 STENCILMODE_LEQUAL_INC, //!< Stencil test with LEQUAL, increment on pass 112 113 STENCILMODE_LAST 114}; 115 116struct DrawPrimitiveOp 117{ 118 PrimitiveType type; 119 int count; 120 vector<Vec4> positions; 121 vector<Vec4> colors; 122 BlendMode blend; 123 DepthMode depth; 124 StencilMode stencil; 125 int stencilRef; 126}; 127 128void randomizeDrawOp (de::Random& rnd, DrawPrimitiveOp& drawOp) 129{ 130 const int minStencilRef = 0; 131 const int maxStencilRef = 8; 132 const int minPrimitives = 2; 133 const int maxPrimitives = 4; 134 135 const float maxTriOffset = 1.0f; 136 const float minDepth = -1.0f; // \todo [pyry] Reference doesn't support Z clipping yet 137 const float maxDepth = 1.0f; 138 139 const float minRGB = 0.2f; 140 const float maxRGB = 0.9f; 141 const float minAlpha = 0.3f; 142 const float maxAlpha = 1.0f; 143 144 drawOp.type = (PrimitiveType)rnd.getInt(0, PRIMITIVETYPE_LAST-1); 145 drawOp.count = rnd.getInt(minPrimitives, maxPrimitives); 146 drawOp.blend = (BlendMode)rnd.getInt(0, BLENDMODE_LAST-1); 147 drawOp.depth = (DepthMode)rnd.getInt(0, DEPTHMODE_LAST-1); 148 drawOp.stencil = (StencilMode)rnd.getInt(0, STENCILMODE_LAST-1); 149 drawOp.stencilRef = rnd.getInt(minStencilRef, maxStencilRef); 150 151 if (drawOp.type == PRIMITIVETYPE_TRIANGLE) 152 { 153 drawOp.positions.resize(drawOp.count*3); 154 drawOp.colors.resize(drawOp.count*3); 155 156 for (int triNdx = 0; triNdx < drawOp.count; triNdx++) 157 { 158 const float cx = rnd.getFloat(-1.0f, 1.0f); 159 const float cy = rnd.getFloat(-1.0f, 1.0f); 160 161 for (int coordNdx = 0; coordNdx < 3; coordNdx++) 162 { 163 tcu::Vec4& position = drawOp.positions[triNdx*3 + coordNdx]; 164 tcu::Vec4& color = drawOp.colors[triNdx*3 + coordNdx]; 165 166 position.x() = cx + rnd.getFloat(-maxTriOffset, maxTriOffset); 167 position.y() = cy + rnd.getFloat(-maxTriOffset, maxTriOffset); 168 position.z() = rnd.getFloat(minDepth, maxDepth); 169 position.w() = 1.0f; 170 171 color.x() = rnd.getFloat(minRGB, maxRGB); 172 color.y() = rnd.getFloat(minRGB, maxRGB); 173 color.z() = rnd.getFloat(minRGB, maxRGB); 174 color.w() = rnd.getFloat(minAlpha, maxAlpha); 175 } 176 } 177 } 178 else 179 DE_ASSERT(false); 180} 181 182// Reference rendering code 183 184class ReferenceShader : public rr::VertexShader, public rr::FragmentShader 185{ 186public: 187 enum 188 { 189 VaryingLoc_Color = 0 190 }; 191 192 ReferenceShader () 193 : rr::VertexShader (2, 1) // color and pos in => color out 194 , rr::FragmentShader(1, 1) // color in => color out 195 { 196 this->rr::VertexShader::m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; 197 this->rr::VertexShader::m_inputs[1].type = rr::GENERICVECTYPE_FLOAT; 198 199 this->rr::VertexShader::m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; 200 this->rr::VertexShader::m_outputs[0].flatshade = false; 201 202 this->rr::FragmentShader::m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; 203 this->rr::FragmentShader::m_inputs[0].flatshade = false; 204 205 this->rr::FragmentShader::m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; 206 } 207 208 void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const 209 { 210 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) 211 { 212 const int positionAttrLoc = 0; 213 const int colorAttrLoc = 1; 214 215 rr::VertexPacket& packet = *packets[packetNdx]; 216 217 // Transform to position 218 packet.position = rr::readVertexAttribFloat(inputs[positionAttrLoc], packet.instanceNdx, packet.vertexNdx); 219 220 // Pass color to FS 221 packet.outputs[VaryingLoc_Color] = rr::readVertexAttribFloat(inputs[colorAttrLoc], packet.instanceNdx, packet.vertexNdx); 222 } 223 } 224 225 void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const 226 { 227 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) 228 { 229 rr::FragmentPacket& packet = packets[packetNdx]; 230 231 for (int fragNdx = 0; fragNdx < 4; ++fragNdx) 232 rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, rr::readVarying<float>(packet, context, VaryingLoc_Color, fragNdx)); 233 } 234 } 235}; 236 237void toReferenceRenderState (rr::RenderState& state, const DrawPrimitiveOp& drawOp) 238{ 239 state.cullMode = rr::CULLMODE_NONE; 240 241 if (drawOp.blend != BLENDMODE_NONE) 242 { 243 state.fragOps.blendMode = rr::BLENDMODE_STANDARD; 244 245 switch (drawOp.blend) 246 { 247 case BLENDMODE_ADDITIVE: 248 state.fragOps.blendRGBState.srcFunc = rr::BLENDFUNC_ONE; 249 state.fragOps.blendRGBState.dstFunc = rr::BLENDFUNC_ONE; 250 state.fragOps.blendRGBState.equation = rr::BLENDEQUATION_ADD; 251 state.fragOps.blendAState = state.fragOps.blendRGBState; 252 break; 253 254 case BLENDMODE_SRC_OVER: 255 state.fragOps.blendRGBState.srcFunc = rr::BLENDFUNC_SRC_ALPHA; 256 state.fragOps.blendRGBState.dstFunc = rr::BLENDFUNC_ONE_MINUS_SRC_ALPHA; 257 state.fragOps.blendRGBState.equation = rr::BLENDEQUATION_ADD; 258 state.fragOps.blendAState = state.fragOps.blendRGBState; 259 break; 260 261 default: 262 DE_ASSERT(false); 263 } 264 } 265 266 if (drawOp.depth != DEPTHMODE_NONE) 267 { 268 state.fragOps.depthTestEnabled = true; 269 270 DE_ASSERT(drawOp.depth == DEPTHMODE_LESS); 271 state.fragOps.depthFunc = rr::TESTFUNC_LESS; 272 } 273 274 if (drawOp.stencil != STENCILMODE_NONE) 275 { 276 state.fragOps.stencilTestEnabled = true; 277 278 DE_ASSERT(drawOp.stencil == STENCILMODE_LEQUAL_INC); 279 state.fragOps.stencilStates[0].func = rr::TESTFUNC_LEQUAL; 280 state.fragOps.stencilStates[0].sFail = rr::STENCILOP_KEEP; 281 state.fragOps.stencilStates[0].dpFail = rr::STENCILOP_INCR; 282 state.fragOps.stencilStates[0].dpPass = rr::STENCILOP_INCR; 283 state.fragOps.stencilStates[0].ref = drawOp.stencilRef; 284 state.fragOps.stencilStates[1] = state.fragOps.stencilStates[0]; 285 } 286} 287 288tcu::TextureFormat getColorFormat (const tcu::PixelFormat& colorBits) 289{ 290 using tcu::TextureFormat; 291 292 DE_ASSERT(de::inBounds(colorBits.redBits, 0, 0xff) && 293 de::inBounds(colorBits.greenBits, 0, 0xff) && 294 de::inBounds(colorBits.blueBits, 0, 0xff) && 295 de::inBounds(colorBits.alphaBits, 0, 0xff)); 296 297#define PACK_FMT(R, G, B, A) (((R) << 24) | ((G) << 16) | ((B) << 8) | (A)) 298 299 // \note [pyry] This may not hold true on some implementations - best effort guess only. 300 switch (PACK_FMT(colorBits.redBits, colorBits.greenBits, colorBits.blueBits, colorBits.alphaBits)) 301 { 302 case PACK_FMT(8,8,8,8): return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8); 303 case PACK_FMT(8,8,8,0): return TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8); 304 case PACK_FMT(4,4,4,4): return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_SHORT_4444); 305 case PACK_FMT(5,5,5,1): return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_SHORT_5551); 306 case PACK_FMT(5,6,5,0): return TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_SHORT_565); 307 308 // \note Defaults to RGBA8 309 default: return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8); 310 } 311 312#undef PACK_FMT 313} 314 315tcu::TextureFormat getDepthFormat (const int depthBits) 316{ 317 switch (depthBits) 318 { 319 case 0: return tcu::TextureFormat(); 320 case 8: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT8); 321 case 16: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT16); 322 case 24: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNSIGNED_INT_24_8); 323 case 32: 324 default: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT); 325 } 326} 327 328tcu::TextureFormat getStencilFormat (int stencilBits) 329{ 330 switch (stencilBits) 331 { 332 case 0: return tcu::TextureFormat(); 333 case 8: 334 default: return tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT8); 335 } 336} 337 338void renderReference (const tcu::PixelBufferAccess& dst, const vector<DrawPrimitiveOp>& drawOps, const tcu::PixelFormat& colorBits, const int depthBits, const int stencilBits, const int numSamples) 339{ 340 const int width = dst.getWidth(); 341 const int height = dst.getHeight(); 342 343 tcu::TextureLevel colorBuffer; 344 tcu::TextureLevel depthBuffer; 345 tcu::TextureLevel stencilBuffer; 346 347 rr::Renderer referenceRenderer; 348 rr::VertexAttrib attributes[2]; 349 const ReferenceShader shader; 350 351 attributes[0].type = rr::VERTEXATTRIBTYPE_FLOAT; 352 attributes[0].size = 4; 353 attributes[0].stride = 0; 354 attributes[0].instanceDivisor = 0; 355 356 attributes[1].type = rr::VERTEXATTRIBTYPE_FLOAT; 357 attributes[1].size = 4; 358 attributes[1].stride = 0; 359 attributes[1].instanceDivisor = 0; 360 361 // Initialize buffers. 362 colorBuffer.setStorage(getColorFormat(colorBits), numSamples, width, height); 363 rr::clearMultisampleColorBuffer(colorBuffer, CLEAR_COLOR, rr::WindowRectangle(0, 0, width, height)); 364 365 if (depthBits > 0) 366 { 367 depthBuffer.setStorage(getDepthFormat(depthBits), numSamples, width, height); 368 rr::clearMultisampleDepthBuffer(depthBuffer, CLEAR_DEPTH, rr::WindowRectangle(0, 0, width, height)); 369 } 370 371 if (stencilBits > 0) 372 { 373 stencilBuffer.setStorage(getStencilFormat(stencilBits), numSamples, width, height); 374 rr::clearMultisampleStencilBuffer(stencilBuffer, CLEAR_STENCIL, rr::WindowRectangle(0, 0, width, height)); 375 } 376 377 const rr::RenderTarget renderTarget(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()), 378 rr::MultisamplePixelBufferAccess::fromMultisampleAccess(depthBuffer.getAccess()), 379 rr::MultisamplePixelBufferAccess::fromMultisampleAccess(stencilBuffer.getAccess())); 380 381 for (vector<DrawPrimitiveOp>::const_iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); drawOp++) 382 { 383 // Translate state 384 rr::RenderState renderState((rr::ViewportState)(rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess()))); 385 toReferenceRenderState(renderState, *drawOp); 386 387 DE_ASSERT(drawOp->type == PRIMITIVETYPE_TRIANGLE); 388 389 attributes[0].pointer = &drawOp->positions[0]; 390 attributes[1].pointer = &drawOp->colors[0]; 391 392 referenceRenderer.draw( 393 rr::DrawCommand( 394 renderState, 395 renderTarget, 396 rr::Program(static_cast<const rr::VertexShader*>(&shader), static_cast<const rr::FragmentShader*>(&shader)), 397 2, 398 attributes, 399 rr::PrimitiveList(rr::PRIMITIVETYPE_TRIANGLES, drawOp->count * 3, 0))); 400 } 401 402 rr::resolveMultisampleColorBuffer(dst, rr::MultisamplePixelBufferAccess::fromMultisampleAccess(colorBuffer.getAccess())); 403} 404 405// API rendering code 406 407class Program 408{ 409public: 410 Program (void) {} 411 virtual ~Program (void) {} 412 413 virtual void setup (void) const = DE_NULL; 414}; 415 416typedef de::SharedPtr<Program> ProgramSp; 417 418static glu::ProgramSources getProgramSourcesES2 (void) 419{ 420 static const char* s_vertexSrc = 421 "attribute highp vec4 a_position;\n" 422 "attribute mediump vec4 a_color;\n" 423 "varying mediump vec4 v_color;\n" 424 "void main (void)\n" 425 "{\n" 426 " gl_Position = a_position;\n" 427 " v_color = a_color;\n" 428 "}\n"; 429 430 static const char* s_fragmentSrc = 431 "varying mediump vec4 v_color;\n" 432 "void main (void)\n" 433 "{\n" 434 " gl_FragColor = v_color;\n" 435 "}\n"; 436 437 return glu::ProgramSources() << glu::VertexSource(s_vertexSrc) << glu::FragmentSource(s_fragmentSrc); 438} 439 440class GLES2Program : public Program 441{ 442public: 443 GLES2Program (const glw::Functions& gl) 444 : m_gl (gl) 445 , m_program (gl, getProgramSourcesES2()) 446 , m_positionLoc (0) 447 , m_colorLoc (0) 448 { 449 450 m_positionLoc = m_gl.getAttribLocation(m_program.getProgram(), "a_position"); 451 m_colorLoc = m_gl.getAttribLocation(m_program.getProgram(), "a_color"); 452 } 453 454 ~GLES2Program (void) 455 { 456 } 457 458 void setup (void) const 459 { 460 m_gl.useProgram(m_program.getProgram()); 461 m_gl.enableVertexAttribArray(m_positionLoc); 462 m_gl.enableVertexAttribArray(m_colorLoc); 463 GLU_CHECK_GLW_MSG(m_gl, "Program setup failed"); 464 } 465 466 int getPositionLoc (void) const { return m_positionLoc; } 467 int getColorLoc (void) const { return m_colorLoc; } 468 469private: 470 const glw::Functions& m_gl; 471 glu::ShaderProgram m_program; 472 int m_positionLoc; 473 int m_colorLoc; 474}; 475 476void clearGLES2 (const glw::Functions& gl, const tcu::Vec4& color, const float depth, const int stencil) 477{ 478 gl.clearColor(color.x(), color.y(), color.z(), color.w()); 479 gl.clearDepthf(depth); 480 gl.clearStencil(stencil); 481 gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); 482} 483 484void drawGLES2 (const glw::Functions& gl, const Program& program, const DrawPrimitiveOp& drawOp) 485{ 486 const GLES2Program& gles2Program = dynamic_cast<const GLES2Program&>(program); 487 488 switch (drawOp.blend) 489 { 490 case BLENDMODE_NONE: 491 gl.disable(GL_BLEND); 492 break; 493 494 case BLENDMODE_ADDITIVE: 495 gl.enable(GL_BLEND); 496 gl.blendFunc(GL_ONE, GL_ONE); 497 break; 498 499 case BLENDMODE_SRC_OVER: 500 gl.enable(GL_BLEND); 501 gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 502 break; 503 504 default: 505 DE_ASSERT(false); 506 } 507 508 switch (drawOp.depth) 509 { 510 case DEPTHMODE_NONE: 511 gl.disable(GL_DEPTH_TEST); 512 break; 513 514 case DEPTHMODE_LESS: 515 gl.enable(GL_DEPTH_TEST); 516 break; 517 518 default: 519 DE_ASSERT(false); 520 } 521 522 switch (drawOp.stencil) 523 { 524 case STENCILMODE_NONE: 525 gl.disable(GL_STENCIL_TEST); 526 break; 527 528 case STENCILMODE_LEQUAL_INC: 529 gl.enable(GL_STENCIL_TEST); 530 gl.stencilFunc(GL_LEQUAL, drawOp.stencilRef, ~0u); 531 gl.stencilOp(GL_KEEP, GL_INCR, GL_INCR); 532 break; 533 534 default: 535 DE_ASSERT(false); 536 } 537 538 gl.vertexAttribPointer(gles2Program.getPositionLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.positions[0]); 539 gl.vertexAttribPointer(gles2Program.getColorLoc(), 4, GL_FLOAT, GL_FALSE, 0, &drawOp.colors[0]); 540 541 DE_ASSERT(drawOp.type == PRIMITIVETYPE_TRIANGLE); 542 gl.drawArrays(GL_TRIANGLES, 0, drawOp.count*3); 543} 544 545static void readPixelsGLES2 (const glw::Functions& gl, tcu::Surface& dst) 546{ 547 gl.readPixels(0, 0, dst.getWidth(), dst.getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, dst.getAccess().getDataPtr()); 548} 549 550Program* createProgram (const glw::Functions& gl, EGLint api) 551{ 552 switch (api) 553 { 554 case EGL_OPENGL_ES2_BIT: return new GLES2Program(gl); 555 case EGL_OPENGL_ES3_BIT_KHR: return new GLES2Program(gl); 556 default: 557 throw tcu::NotSupportedError("Unsupported API"); 558 } 559} 560 561void draw (const glw::Functions& gl, EGLint api, const Program& program, const DrawPrimitiveOp& drawOp) 562{ 563 switch (api) 564 { 565 case EGL_OPENGL_ES2_BIT: drawGLES2(gl, program, drawOp); break; 566 case EGL_OPENGL_ES3_BIT_KHR: drawGLES2(gl, program, drawOp); break; 567 default: 568 throw tcu::NotSupportedError("Unsupported API"); 569 } 570} 571 572void clear (const glw::Functions& gl, EGLint api, const tcu::Vec4& color, const float depth, const int stencil) 573{ 574 switch (api) 575 { 576 case EGL_OPENGL_ES2_BIT: clearGLES2(gl, color, depth, stencil); break; 577 case EGL_OPENGL_ES3_BIT_KHR: clearGLES2(gl, color, depth, stencil); break; 578 default: 579 throw tcu::NotSupportedError("Unsupported API"); 580 } 581} 582 583static void readPixels (const glw::Functions& gl, EGLint api, tcu::Surface& dst) 584{ 585 switch (api) 586 { 587 case EGL_OPENGL_ES2_BIT: readPixelsGLES2(gl, dst); break; 588 case EGL_OPENGL_ES3_BIT_KHR: readPixelsGLES2(gl, dst); break; 589 default: 590 throw tcu::NotSupportedError("Unsupported API"); 591 } 592} 593 594tcu::PixelFormat getPixelFormat (const Library& egl, EGLDisplay display, EGLConfig config) 595{ 596 tcu::PixelFormat fmt; 597 fmt.redBits = eglu::getConfigAttribInt(egl, display, config, EGL_RED_SIZE); 598 fmt.greenBits = eglu::getConfigAttribInt(egl, display, config, EGL_GREEN_SIZE); 599 fmt.blueBits = eglu::getConfigAttribInt(egl, display, config, EGL_BLUE_SIZE); 600 fmt.alphaBits = eglu::getConfigAttribInt(egl, display, config, EGL_ALPHA_SIZE); 601 return fmt; 602} 603 604} // anonymous 605 606// SingleThreadRenderCase 607 608class SingleThreadRenderCase : public MultiContextRenderCase 609{ 610public: 611 SingleThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi); 612 613private: 614 virtual void executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts); 615 616 glw::Functions m_gl; 617}; 618 619// SingleThreadColorClearCase 620 621SingleThreadRenderCase::SingleThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi) 622 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi) 623{ 624 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0)); 625} 626 627void SingleThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts) 628{ 629 const Library& egl = m_eglTestCtx.getLibrary(); 630 const int width = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH); 631 const int height = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT); 632 const int numContexts = (int)contexts.size(); 633 const int drawsPerCtx = 2; 634 const int numIters = 2; 635 const float threshold = 0.02f; 636 637 const tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config); 638 const int depthBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE); 639 const int stencilBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE); 640 const int numSamples = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES); 641 642 TestLog& log = m_testCtx.getLog(); 643 644 tcu::Surface refFrame (width, height); 645 tcu::Surface frame (width, height); 646 647 de::Random rnd (deStringHash(getName()) ^ deInt32Hash(numContexts)); 648 vector<ProgramSp> programs (contexts.size()); 649 vector<DrawPrimitiveOp> drawOps; 650 651 // Log basic information about config. 652 log << TestLog::Message << "EGL_RED_SIZE = " << pixelFmt.redBits << TestLog::EndMessage; 653 log << TestLog::Message << "EGL_GREEN_SIZE = " << pixelFmt.greenBits << TestLog::EndMessage; 654 log << TestLog::Message << "EGL_BLUE_SIZE = " << pixelFmt.blueBits << TestLog::EndMessage; 655 log << TestLog::Message << "EGL_ALPHA_SIZE = " << pixelFmt.alphaBits << TestLog::EndMessage; 656 log << TestLog::Message << "EGL_DEPTH_SIZE = " << depthBits << TestLog::EndMessage; 657 log << TestLog::Message << "EGL_STENCIL_SIZE = " << stencilBits << TestLog::EndMessage; 658 log << TestLog::Message << "EGL_SAMPLES = " << numSamples << TestLog::EndMessage; 659 660 // Generate draw ops. 661 drawOps.resize(numContexts*drawsPerCtx*numIters); 662 for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp) 663 randomizeDrawOp(rnd, *drawOp); 664 665 // Create and setup programs per context 666 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++) 667 { 668 EGLint api = contexts[ctxNdx].first; 669 EGLContext context = contexts[ctxNdx].second; 670 671 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 672 673 programs[ctxNdx] = ProgramSp(createProgram(m_gl, api)); 674 programs[ctxNdx]->setup(); 675 } 676 677 // Clear to black using first context. 678 { 679 EGLint api = contexts[0].first; 680 EGLContext context = contexts[0].second; 681 682 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 683 684 clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL); 685 } 686 687 // Render. 688 for (int iterNdx = 0; iterNdx < numIters; iterNdx++) 689 { 690 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++) 691 { 692 EGLint api = contexts[ctxNdx].first; 693 EGLContext context = contexts[ctxNdx].second; 694 695 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 696 697 for (int drawNdx = 0; drawNdx < drawsPerCtx; drawNdx++) 698 { 699 const DrawPrimitiveOp& drawOp = drawOps[iterNdx*numContexts*drawsPerCtx + ctxNdx*drawsPerCtx + drawNdx]; 700 draw(m_gl, api, *programs[ctxNdx], drawOp); 701 } 702 } 703 } 704 705 // Read pixels using first context. \todo [pyry] Randomize? 706 { 707 EGLint api = contexts[0].first; 708 EGLContext context = contexts[0].second; 709 710 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 711 712 readPixels(m_gl, api, frame); 713 } 714 715 // Render reference. 716 // \note Reference image is always generated using single-sampling. 717 renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1); 718 719 // Compare images 720 { 721 bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT); 722 723 if (!imagesOk) 724 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 725 } 726} 727 728// MultiThreadRenderCase 729 730class MultiThreadRenderCase : public MultiContextRenderCase 731{ 732public: 733 MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi); 734 735private: 736 virtual void executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts); 737 738 glw::Functions m_gl; 739}; 740 741class RenderTestThread; 742 743typedef de::SharedPtr<RenderTestThread> RenderTestThreadSp; 744typedef de::SharedPtr<de::Semaphore> SemaphoreSp; 745 746struct DrawOpPacket 747{ 748 DrawOpPacket (void) 749 : drawOps (DE_NULL) 750 , numOps (0) 751 { 752 } 753 754 const DrawPrimitiveOp* drawOps; 755 int numOps; 756 SemaphoreSp wait; 757 SemaphoreSp signal; 758}; 759 760class RenderTestThread : public de::Thread 761{ 762public: 763 RenderTestThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const glw::Functions& gl, const Program& program, const std::vector<DrawOpPacket>& packets) 764 : m_egl (egl) 765 , m_display (display) 766 , m_surface (surface) 767 , m_context (context) 768 , m_api (api) 769 , m_gl (gl) 770 , m_program (program) 771 , m_packets (packets) 772 { 773 } 774 775 void run (void) 776 { 777 for (std::vector<DrawOpPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++) 778 { 779 // Wait until it is our turn. 780 packetIter->wait->decrement(); 781 782 // Acquire context. 783 EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, m_surface, m_surface, m_context)); 784 785 // Execute rendering. 786 for (int ndx = 0; ndx < packetIter->numOps; ndx++) 787 draw(m_gl, m_api, m_program, packetIter->drawOps[ndx]); 788 789 // Release context. 790 EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); 791 792 // Signal completion. 793 packetIter->signal->increment(); 794 } 795 } 796 797private: 798 const Library& m_egl; 799 EGLDisplay m_display; 800 EGLSurface m_surface; 801 EGLContext m_context; 802 EGLint m_api; 803 const glw::Functions& m_gl; 804 const Program& m_program; 805 const std::vector<DrawOpPacket>& m_packets; 806}; 807 808MultiThreadRenderCase::MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi) 809 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi) 810{ 811 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0)); 812} 813 814void MultiThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts) 815{ 816 const Library& egl = m_eglTestCtx.getLibrary(); 817 const int width = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH); 818 const int height = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT); 819 const int numContexts = (int)contexts.size(); 820 const int opsPerPacket = 2; 821 const int packetsPerThread = 2; 822 const int numThreads = numContexts; 823 const int numPackets = numThreads * packetsPerThread; 824 const float threshold = 0.02f; 825 826 const tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config); 827 const int depthBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE); 828 const int stencilBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE); 829 const int numSamples = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES); 830 831 TestLog& log = m_testCtx.getLog(); 832 833 tcu::Surface refFrame (width, height); 834 tcu::Surface frame (width, height); 835 836 de::Random rnd (deStringHash(getName()) ^ deInt32Hash(numContexts)); 837 838 // Resources that need cleanup 839 vector<ProgramSp> programs (numContexts); 840 vector<SemaphoreSp> semaphores (numPackets+1); 841 vector<DrawPrimitiveOp> drawOps (numPackets*opsPerPacket); 842 vector<vector<DrawOpPacket> > packets (numThreads); 843 vector<RenderTestThreadSp> threads (numThreads); 844 845 // Log basic information about config. 846 log << TestLog::Message << "EGL_RED_SIZE = " << pixelFmt.redBits << TestLog::EndMessage; 847 log << TestLog::Message << "EGL_GREEN_SIZE = " << pixelFmt.greenBits << TestLog::EndMessage; 848 log << TestLog::Message << "EGL_BLUE_SIZE = " << pixelFmt.blueBits << TestLog::EndMessage; 849 log << TestLog::Message << "EGL_ALPHA_SIZE = " << pixelFmt.alphaBits << TestLog::EndMessage; 850 log << TestLog::Message << "EGL_DEPTH_SIZE = " << depthBits << TestLog::EndMessage; 851 log << TestLog::Message << "EGL_STENCIL_SIZE = " << stencilBits << TestLog::EndMessage; 852 log << TestLog::Message << "EGL_SAMPLES = " << numSamples << TestLog::EndMessage; 853 854 // Initialize semaphores. 855 for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem) 856 *sem = SemaphoreSp(new de::Semaphore(0)); 857 858 // Create draw ops. 859 for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp) 860 randomizeDrawOp(rnd, *drawOp); 861 862 // Create packets. 863 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++) 864 { 865 packets[threadNdx].resize(packetsPerThread); 866 867 for (int packetNdx = 0; packetNdx < packetsPerThread; packetNdx++) 868 { 869 DrawOpPacket& packet = packets[threadNdx][packetNdx]; 870 871 // Threads take turns with packets. 872 packet.wait = semaphores[packetNdx*numThreads + threadNdx]; 873 packet.signal = semaphores[packetNdx*numThreads + threadNdx + 1]; 874 packet.numOps = opsPerPacket; 875 packet.drawOps = &drawOps[(packetNdx*numThreads + threadNdx)*opsPerPacket]; 876 } 877 } 878 879 // Create and setup programs per context 880 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++) 881 { 882 EGLint api = contexts[ctxNdx].first; 883 EGLContext context = contexts[ctxNdx].second; 884 885 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 886 887 programs[ctxNdx] = ProgramSp(createProgram(m_gl, api)); 888 programs[ctxNdx]->setup(); 889 890 // Release context 891 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); 892 } 893 894 // Clear to black using first context. 895 { 896 EGLint api = contexts[0].first; 897 EGLContext context = contexts[0].second; 898 899 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 900 901 clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL); 902 903 // Release context 904 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); 905 } 906 907 // Create and launch threads (actual rendering starts once first semaphore is signaled). 908 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++) 909 { 910 threads[threadNdx] = RenderTestThreadSp(new RenderTestThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, m_gl, *programs[threadNdx], packets[threadNdx])); 911 threads[threadNdx]->start(); 912 } 913 914 // Signal start and wait until complete. 915 semaphores.front()->increment(); 916 semaphores.back()->decrement(); 917 918 // Read pixels using first context. \todo [pyry] Randomize? 919 { 920 EGLint api = contexts[0].first; 921 EGLContext context = contexts[0].second; 922 923 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 924 925 readPixels(m_gl, api, frame); 926 } 927 928 // Join threads. 929 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++) 930 threads[threadNdx]->join(); 931 932 // Render reference. 933 renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1); 934 935 // Compare images 936 { 937 bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT); 938 939 if (!imagesOk) 940 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 941 } 942} 943 944RenderTests::RenderTests (EglTestContext& eglTestCtx) 945 : TestCaseGroup(eglTestCtx, "render", "Basic rendering with different client APIs") 946{ 947} 948 949RenderTests::~RenderTests (void) 950{ 951} 952 953struct RenderGroupSpec 954{ 955 const char* name; 956 const char* desc; 957 EGLint apiBits; 958 eglu::ConfigFilter baseFilter; 959 int numContextsPerApi; 960}; 961 962template <deUint32 Bits> 963static bool renderable (const eglu::CandidateConfig& c) 964{ 965 return (c.renderableType() & Bits) == Bits; 966} 967 968template <class RenderClass> 969static void createRenderGroups (EglTestContext& eglTestCtx, tcu::TestCaseGroup* group, const RenderGroupSpec* first, const RenderGroupSpec* last) 970{ 971 for (const RenderGroupSpec* groupIter = first; groupIter != last; groupIter++) 972 { 973 tcu::TestCaseGroup* configGroup = new tcu::TestCaseGroup(eglTestCtx.getTestContext(), groupIter->name, groupIter->desc); 974 group->addChild(configGroup); 975 976 vector<RenderFilterList> filterLists; 977 eglu::FilterList baseFilters; 978 baseFilters << groupIter->baseFilter; 979 getDefaultRenderFilterLists(filterLists, baseFilters); 980 981 for (vector<RenderFilterList>::const_iterator listIter = filterLists.begin(); listIter != filterLists.end(); listIter++) 982 configGroup->addChild(new RenderClass(eglTestCtx, listIter->getName(), "", groupIter->apiBits, listIter->getSurfaceTypeMask(), *listIter, groupIter->numContextsPerApi)); 983 } 984} 985 986void RenderTests::init (void) 987{ 988 static const RenderGroupSpec singleContextCases[] = 989 { 990 { 991 "gles2", 992 "Primitive rendering using GLES2", 993 EGL_OPENGL_ES2_BIT, 994 renderable<EGL_OPENGL_ES2_BIT>, 995 1 996 }, 997 { 998 "gles3", 999 "Primitive rendering using GLES3", 1000 EGL_OPENGL_ES3_BIT, 1001 renderable<EGL_OPENGL_ES3_BIT>, 1002 1 1003 }, 1004 }; 1005 1006 static const RenderGroupSpec multiContextCases[] = 1007 { 1008 { 1009 "gles2", 1010 "Primitive rendering using multiple GLES2 contexts to shared surface", 1011 EGL_OPENGL_ES2_BIT, 1012 renderable<EGL_OPENGL_ES2_BIT>, 1013 3 1014 }, 1015 { 1016 "gles3", 1017 "Primitive rendering using multiple GLES3 contexts to shared surface", 1018 EGL_OPENGL_ES3_BIT, 1019 renderable<EGL_OPENGL_ES3_BIT>, 1020 3 1021 }, 1022 { 1023 "gles2_gles3", 1024 "Primitive rendering using multiple APIs to shared surface", 1025 EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT, 1026 renderable<EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT>, 1027 1 1028 }, 1029 }; 1030 1031 tcu::TestCaseGroup* singleContextGroup = new tcu::TestCaseGroup(m_testCtx, "single_context", "Single-context rendering"); 1032 addChild(singleContextGroup); 1033 createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, singleContextGroup, &singleContextCases[0], &singleContextCases[DE_LENGTH_OF_ARRAY(singleContextCases)]); 1034 1035 tcu::TestCaseGroup* multiContextGroup = new tcu::TestCaseGroup(m_testCtx, "multi_context", "Multi-context rendering with shared surface"); 1036 addChild(multiContextGroup); 1037 createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, multiContextGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]); 1038 1039 tcu::TestCaseGroup* multiThreadGroup = new tcu::TestCaseGroup(m_testCtx, "multi_thread", "Multi-thread rendering with shared surface"); 1040 addChild(multiThreadGroup); 1041 createRenderGroups<MultiThreadRenderCase>(m_eglTestCtx, multiThreadGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]); 1042} 1043 1044} // egl 1045} // deqp 1046