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