teglRenderTests.cpp revision 8e814ce14475b71be9d3e17db2f1d1c6a3fcc59f
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 613 void init (void); 614 615private: 616 virtual void executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts); 617 618 glw::Functions m_gl; 619}; 620 621// SingleThreadColorClearCase 622 623SingleThreadRenderCase::SingleThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi) 624 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi) 625{ 626} 627 628void SingleThreadRenderCase::init (void) 629{ 630 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0)); 631} 632 633void SingleThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts) 634{ 635 const Library& egl = m_eglTestCtx.getLibrary(); 636 const int width = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH); 637 const int height = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT); 638 const int numContexts = (int)contexts.size(); 639 const int drawsPerCtx = 2; 640 const int numIters = 2; 641 const float threshold = 0.02f; 642 643 const tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config); 644 const int depthBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE); 645 const int stencilBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE); 646 const int numSamples = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES); 647 648 TestLog& log = m_testCtx.getLog(); 649 650 tcu::Surface refFrame (width, height); 651 tcu::Surface frame (width, height); 652 653 de::Random rnd (deStringHash(getName()) ^ deInt32Hash(numContexts)); 654 vector<ProgramSp> programs (contexts.size()); 655 vector<DrawPrimitiveOp> drawOps; 656 657 // Log basic information about config. 658 log << TestLog::Message << "EGL_RED_SIZE = " << pixelFmt.redBits << TestLog::EndMessage; 659 log << TestLog::Message << "EGL_GREEN_SIZE = " << pixelFmt.greenBits << TestLog::EndMessage; 660 log << TestLog::Message << "EGL_BLUE_SIZE = " << pixelFmt.blueBits << TestLog::EndMessage; 661 log << TestLog::Message << "EGL_ALPHA_SIZE = " << pixelFmt.alphaBits << TestLog::EndMessage; 662 log << TestLog::Message << "EGL_DEPTH_SIZE = " << depthBits << TestLog::EndMessage; 663 log << TestLog::Message << "EGL_STENCIL_SIZE = " << stencilBits << TestLog::EndMessage; 664 log << TestLog::Message << "EGL_SAMPLES = " << numSamples << TestLog::EndMessage; 665 666 // Generate draw ops. 667 drawOps.resize(numContexts*drawsPerCtx*numIters); 668 for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp) 669 randomizeDrawOp(rnd, *drawOp); 670 671 // Create and setup programs per context 672 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++) 673 { 674 EGLint api = contexts[ctxNdx].first; 675 EGLContext context = contexts[ctxNdx].second; 676 677 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 678 679 programs[ctxNdx] = ProgramSp(createProgram(m_gl, api)); 680 programs[ctxNdx]->setup(); 681 } 682 683 // Clear to black using first context. 684 { 685 EGLint api = contexts[0].first; 686 EGLContext context = contexts[0].second; 687 688 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 689 690 clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL); 691 } 692 693 // Render. 694 for (int iterNdx = 0; iterNdx < numIters; iterNdx++) 695 { 696 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++) 697 { 698 EGLint api = contexts[ctxNdx].first; 699 EGLContext context = contexts[ctxNdx].second; 700 701 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 702 703 for (int drawNdx = 0; drawNdx < drawsPerCtx; drawNdx++) 704 { 705 const DrawPrimitiveOp& drawOp = drawOps[iterNdx*numContexts*drawsPerCtx + ctxNdx*drawsPerCtx + drawNdx]; 706 draw(m_gl, api, *programs[ctxNdx], drawOp); 707 } 708 } 709 } 710 711 // Read pixels using first context. \todo [pyry] Randomize? 712 { 713 EGLint api = contexts[0].first; 714 EGLContext context = contexts[0].second; 715 716 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 717 718 readPixels(m_gl, api, frame); 719 } 720 721 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); 722 723 // Render reference. 724 // \note Reference image is always generated using single-sampling. 725 renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1); 726 727 // Compare images 728 { 729 bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT); 730 731 if (!imagesOk) 732 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 733 } 734} 735 736// MultiThreadRenderCase 737 738class MultiThreadRenderCase : public MultiContextRenderCase 739{ 740public: 741 MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi); 742 743 void init (void); 744 745private: 746 virtual void executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts); 747 748 glw::Functions m_gl; 749}; 750 751class RenderTestThread; 752 753typedef de::SharedPtr<RenderTestThread> RenderTestThreadSp; 754typedef de::SharedPtr<de::Semaphore> SemaphoreSp; 755 756struct DrawOpPacket 757{ 758 DrawOpPacket (void) 759 : drawOps (DE_NULL) 760 , numOps (0) 761 { 762 } 763 764 const DrawPrimitiveOp* drawOps; 765 int numOps; 766 SemaphoreSp wait; 767 SemaphoreSp signal; 768}; 769 770class RenderTestThread : public de::Thread 771{ 772public: 773 RenderTestThread (const Library& egl, EGLDisplay display, EGLSurface surface, EGLContext context, EGLint api, const glw::Functions& gl, const Program& program, const std::vector<DrawOpPacket>& packets) 774 : m_egl (egl) 775 , m_display (display) 776 , m_surface (surface) 777 , m_context (context) 778 , m_api (api) 779 , m_gl (gl) 780 , m_program (program) 781 , m_packets (packets) 782 { 783 } 784 785 void run (void) 786 { 787 for (std::vector<DrawOpPacket>::const_iterator packetIter = m_packets.begin(); packetIter != m_packets.end(); packetIter++) 788 { 789 // Wait until it is our turn. 790 packetIter->wait->decrement(); 791 792 // Acquire context. 793 EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, m_surface, m_surface, m_context)); 794 795 // Execute rendering. 796 for (int ndx = 0; ndx < packetIter->numOps; ndx++) 797 draw(m_gl, m_api, m_program, packetIter->drawOps[ndx]); 798 799 // Release context. 800 EGLU_CHECK_CALL(m_egl, makeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); 801 802 // Signal completion. 803 packetIter->signal->increment(); 804 } 805 } 806 807private: 808 const Library& m_egl; 809 EGLDisplay m_display; 810 EGLSurface m_surface; 811 EGLContext m_context; 812 EGLint m_api; 813 const glw::Functions& m_gl; 814 const Program& m_program; 815 const std::vector<DrawOpPacket>& m_packets; 816}; 817 818MultiThreadRenderCase::MultiThreadRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const eglu::FilterList& filters, int numContextsPerApi) 819 : MultiContextRenderCase(eglTestCtx, name, description, api, surfaceType, filters, numContextsPerApi) 820{ 821} 822 823void MultiThreadRenderCase::init (void) 824{ 825 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0)); 826} 827 828void MultiThreadRenderCase::executeForContexts (EGLDisplay display, EGLSurface surface, const Config& config, const std::vector<std::pair<EGLint, EGLContext> >& contexts) 829{ 830 const Library& egl = m_eglTestCtx.getLibrary(); 831 const int width = eglu::querySurfaceInt(egl, display, surface, EGL_WIDTH); 832 const int height = eglu::querySurfaceInt(egl, display, surface, EGL_HEIGHT); 833 const int numContexts = (int)contexts.size(); 834 const int opsPerPacket = 2; 835 const int packetsPerThread = 2; 836 const int numThreads = numContexts; 837 const int numPackets = numThreads * packetsPerThread; 838 const float threshold = 0.02f; 839 840 const tcu::PixelFormat pixelFmt = getPixelFormat(egl, display, config.config); 841 const int depthBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_DEPTH_SIZE); 842 const int stencilBits = eglu::getConfigAttribInt(egl, display, config.config, EGL_STENCIL_SIZE); 843 const int numSamples = eglu::getConfigAttribInt(egl, display, config.config, EGL_SAMPLES); 844 845 TestLog& log = m_testCtx.getLog(); 846 847 tcu::Surface refFrame (width, height); 848 tcu::Surface frame (width, height); 849 850 de::Random rnd (deStringHash(getName()) ^ deInt32Hash(numContexts)); 851 852 // Resources that need cleanup 853 vector<ProgramSp> programs (numContexts); 854 vector<SemaphoreSp> semaphores (numPackets+1); 855 vector<DrawPrimitiveOp> drawOps (numPackets*opsPerPacket); 856 vector<vector<DrawOpPacket> > packets (numThreads); 857 vector<RenderTestThreadSp> threads (numThreads); 858 859 // Log basic information about config. 860 log << TestLog::Message << "EGL_RED_SIZE = " << pixelFmt.redBits << TestLog::EndMessage; 861 log << TestLog::Message << "EGL_GREEN_SIZE = " << pixelFmt.greenBits << TestLog::EndMessage; 862 log << TestLog::Message << "EGL_BLUE_SIZE = " << pixelFmt.blueBits << TestLog::EndMessage; 863 log << TestLog::Message << "EGL_ALPHA_SIZE = " << pixelFmt.alphaBits << TestLog::EndMessage; 864 log << TestLog::Message << "EGL_DEPTH_SIZE = " << depthBits << TestLog::EndMessage; 865 log << TestLog::Message << "EGL_STENCIL_SIZE = " << stencilBits << TestLog::EndMessage; 866 log << TestLog::Message << "EGL_SAMPLES = " << numSamples << TestLog::EndMessage; 867 868 // Initialize semaphores. 869 for (vector<SemaphoreSp>::iterator sem = semaphores.begin(); sem != semaphores.end(); ++sem) 870 *sem = SemaphoreSp(new de::Semaphore(0)); 871 872 // Create draw ops. 873 for (vector<DrawPrimitiveOp>::iterator drawOp = drawOps.begin(); drawOp != drawOps.end(); ++drawOp) 874 randomizeDrawOp(rnd, *drawOp); 875 876 // Create packets. 877 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++) 878 { 879 packets[threadNdx].resize(packetsPerThread); 880 881 for (int packetNdx = 0; packetNdx < packetsPerThread; packetNdx++) 882 { 883 DrawOpPacket& packet = packets[threadNdx][packetNdx]; 884 885 // Threads take turns with packets. 886 packet.wait = semaphores[packetNdx*numThreads + threadNdx]; 887 packet.signal = semaphores[packetNdx*numThreads + threadNdx + 1]; 888 packet.numOps = opsPerPacket; 889 packet.drawOps = &drawOps[(packetNdx*numThreads + threadNdx)*opsPerPacket]; 890 } 891 } 892 893 // Create and setup programs per context 894 for (int ctxNdx = 0; ctxNdx < numContexts; ctxNdx++) 895 { 896 EGLint api = contexts[ctxNdx].first; 897 EGLContext context = contexts[ctxNdx].second; 898 899 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 900 901 programs[ctxNdx] = ProgramSp(createProgram(m_gl, api)); 902 programs[ctxNdx]->setup(); 903 904 // Release context 905 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); 906 } 907 908 // Clear to black using first context. 909 { 910 EGLint api = contexts[0].first; 911 EGLContext context = contexts[0].second; 912 913 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 914 915 clear(m_gl, api, CLEAR_COLOR, CLEAR_DEPTH, CLEAR_STENCIL); 916 917 // Release context 918 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); 919 } 920 921 // Create and launch threads (actual rendering starts once first semaphore is signaled). 922 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++) 923 { 924 threads[threadNdx] = RenderTestThreadSp(new RenderTestThread(egl, display, surface, contexts[threadNdx].second, contexts[threadNdx].first, m_gl, *programs[threadNdx], packets[threadNdx])); 925 threads[threadNdx]->start(); 926 } 927 928 // Signal start and wait until complete. 929 semaphores.front()->increment(); 930 semaphores.back()->decrement(); 931 932 // Read pixels using first context. \todo [pyry] Randomize? 933 { 934 EGLint api = contexts[0].first; 935 EGLContext context = contexts[0].second; 936 937 EGLU_CHECK_CALL(egl, makeCurrent(display, surface, surface, context)); 938 939 readPixels(m_gl, api, frame); 940 } 941 942 EGLU_CHECK_CALL(egl, makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); 943 944 // Join threads. 945 for (int threadNdx = 0; threadNdx < numThreads; threadNdx++) 946 threads[threadNdx]->join(); 947 948 // Render reference. 949 renderReference(refFrame.getAccess(), drawOps, pixelFmt, depthBits, stencilBits, 1); 950 951 // Compare images 952 { 953 bool imagesOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, frame, threshold, tcu::COMPARE_LOG_RESULT); 954 955 if (!imagesOk) 956 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 957 } 958} 959 960RenderTests::RenderTests (EglTestContext& eglTestCtx) 961 : TestCaseGroup(eglTestCtx, "render", "Basic rendering with different client APIs") 962{ 963} 964 965RenderTests::~RenderTests (void) 966{ 967} 968 969struct RenderGroupSpec 970{ 971 const char* name; 972 const char* desc; 973 EGLint apiBits; 974 eglu::ConfigFilter baseFilter; 975 int numContextsPerApi; 976}; 977 978template <deUint32 Bits> 979static bool renderable (const eglu::CandidateConfig& c) 980{ 981 return (c.renderableType() & Bits) == Bits; 982} 983 984template <class RenderClass> 985static void createRenderGroups (EglTestContext& eglTestCtx, tcu::TestCaseGroup* group, const RenderGroupSpec* first, const RenderGroupSpec* last) 986{ 987 for (const RenderGroupSpec* groupIter = first; groupIter != last; groupIter++) 988 { 989 tcu::TestCaseGroup* configGroup = new tcu::TestCaseGroup(eglTestCtx.getTestContext(), groupIter->name, groupIter->desc); 990 group->addChild(configGroup); 991 992 vector<RenderFilterList> filterLists; 993 eglu::FilterList baseFilters; 994 baseFilters << groupIter->baseFilter; 995 getDefaultRenderFilterLists(filterLists, baseFilters); 996 997 for (vector<RenderFilterList>::const_iterator listIter = filterLists.begin(); listIter != filterLists.end(); listIter++) 998 configGroup->addChild(new RenderClass(eglTestCtx, listIter->getName(), "", groupIter->apiBits, listIter->getSurfaceTypeMask(), *listIter, groupIter->numContextsPerApi)); 999 } 1000} 1001 1002void RenderTests::init (void) 1003{ 1004 static const RenderGroupSpec singleContextCases[] = 1005 { 1006 { 1007 "gles2", 1008 "Primitive rendering using GLES2", 1009 EGL_OPENGL_ES2_BIT, 1010 renderable<EGL_OPENGL_ES2_BIT>, 1011 1 1012 }, 1013 { 1014 "gles3", 1015 "Primitive rendering using GLES3", 1016 EGL_OPENGL_ES3_BIT, 1017 renderable<EGL_OPENGL_ES3_BIT>, 1018 1 1019 }, 1020 }; 1021 1022 static const RenderGroupSpec multiContextCases[] = 1023 { 1024 { 1025 "gles2", 1026 "Primitive rendering using multiple GLES2 contexts to shared surface", 1027 EGL_OPENGL_ES2_BIT, 1028 renderable<EGL_OPENGL_ES2_BIT>, 1029 3 1030 }, 1031 { 1032 "gles3", 1033 "Primitive rendering using multiple GLES3 contexts to shared surface", 1034 EGL_OPENGL_ES3_BIT, 1035 renderable<EGL_OPENGL_ES3_BIT>, 1036 3 1037 }, 1038 { 1039 "gles2_gles3", 1040 "Primitive rendering using multiple APIs to shared surface", 1041 EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT, 1042 renderable<EGL_OPENGL_ES2_BIT|EGL_OPENGL_ES3_BIT>, 1043 1 1044 }, 1045 }; 1046 1047 tcu::TestCaseGroup* singleContextGroup = new tcu::TestCaseGroup(m_testCtx, "single_context", "Single-context rendering"); 1048 addChild(singleContextGroup); 1049 createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, singleContextGroup, &singleContextCases[0], &singleContextCases[DE_LENGTH_OF_ARRAY(singleContextCases)]); 1050 1051 tcu::TestCaseGroup* multiContextGroup = new tcu::TestCaseGroup(m_testCtx, "multi_context", "Multi-context rendering with shared surface"); 1052 addChild(multiContextGroup); 1053 createRenderGroups<SingleThreadRenderCase>(m_eglTestCtx, multiContextGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]); 1054 1055 tcu::TestCaseGroup* multiThreadGroup = new tcu::TestCaseGroup(m_testCtx, "multi_thread", "Multi-thread rendering with shared surface"); 1056 addChild(multiThreadGroup); 1057 createRenderGroups<MultiThreadRenderCase>(m_eglTestCtx, multiThreadGroup, &multiContextCases[0], &multiContextCases[DE_LENGTH_OF_ARRAY(multiContextCases)]); 1058} 1059 1060} // egl 1061} // deqp 1062