os/graphics/m3g/m3gcore11/src/m3g_rendercontext.inl
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 /*
     2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
     3 * All rights reserved.
     4 * This component and the accompanying materials are made available
     5 * under the terms of the License "Eclipse Public License v1.0"
     6 * which accompanies this distribution, and is available
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
     8 *
     9 * Initial Contributors:
    10 * Nokia Corporation - initial contribution.
    11 *
    12 * Contributors:
    13 *
    14 * Description: EGL rendering context management functions
    15 *
    16 */
    17 
    18 
    19 /*!
    20  * \internal
    21  * \file
    22  * \brief EGL rendering context management functions
    23  */
    24 
    25 #if defined(M3G_NGL_CONTEXT_API)
    26 #   error This file is for the OES API only
    27 #endif
    28 
    29 #include <EGL/egl.h>
    30 #include "m3g_image.h"
    31 
    32 /*----------------------------------------------------------------------
    33  * Private functions
    34  *--------------------------------------------------------------------*/
    35 
    36 /*!
    37  * \internal
    38  * \brief Queries for an EGL configuration matching given M3G format
    39  * parameters
    40  */
    41 static EGLConfig m3gQueryEGLConfig(M3Genum format,
    42                                    M3Gbitmask bufferBits,
    43                                    EGLint surfaceBits,
    44                                    M3GNativeBitmap bitmapHandle)
    45 {
    46     struct { int attrib, value; } attribs[10];
    47     int samples;
    48 
    49     /* Determine color depth */
    50     
    51     attribs[0].attrib = EGL_RED_SIZE;
    52     attribs[1].attrib = EGL_GREEN_SIZE;
    53     attribs[2].attrib = EGL_BLUE_SIZE;
    54     attribs[3].attrib = EGL_ALPHA_SIZE;
    55         
    56     switch (format) {
    57     case M3G_RGB4:
    58         attribs[0].value = 4;
    59         attribs[1].value = 4;
    60         attribs[2].value = 4;
    61         attribs[3].value = 0;
    62         break;
    63     case M3G_RGB565:
    64         attribs[0].value = 5;
    65         attribs[1].value = 6;
    66         attribs[2].value = 5;
    67         attribs[3].value = 0;
    68         break;
    69     case M3G_RGB8:
    70     case M3G_BGR8_32:
    71         attribs[0].value = 8;
    72         attribs[1].value = 8;
    73         attribs[2].value = 8;
    74         attribs[3].value = 0;
    75         break;
    76     case M3G_RGBA8:
    77     case M3G_BGRA8:
    78         attribs[0].value = 8;
    79         attribs[1].value = 8;
    80         attribs[2].value = 8;
    81         attribs[3].value = 8;
    82         break;
    83     default:
    84         return NULL;
    85     }
    86 
    87     /* Set up the depth buffer */
    88     
    89     attribs[4].attrib = EGL_DEPTH_SIZE;
    90     attribs[4].value = (bufferBits & M3G_DEPTH_BUFFER_BIT) ? 16 : 0;
    91     
    92     /* Set target surface type mask */
    93     
    94     attribs[5].attrib = EGL_SURFACE_TYPE;
    95     attribs[5].value = surfaceBits;
    96 
    97 
    98     if (bitmapHandle) {
    99         /* This attribute is matched only for pixmap targets */
   100         attribs[6].attrib = EGL_MATCH_NATIVE_PIXMAP;
   101         attribs[6].value = bitmapHandle;
   102 
   103         /* Try to get multisampling if requested */
   104 
   105         attribs[7].attrib = EGL_SAMPLE_BUFFERS;
   106         attribs[8].attrib = EGL_SAMPLES;
   107 
   108         attribs[9].attrib = EGL_NONE;
   109     } else {
   110         /* Try to get multisampling if requested */
   111 
   112         attribs[6].attrib = EGL_SAMPLE_BUFFERS;
   113         attribs[7].attrib = EGL_SAMPLES;
   114 
   115         attribs[8].attrib = EGL_NONE;
   116     }
   117     
   118     
   119     /* Try 4 samples if multisampling enabled, then 2, then 1 */
   120     
   121     samples = (bufferBits & M3G_MULTISAMPLE_BUFFER_BIT) ? 4 : 1;
   122     for ( ; samples > 0; samples >>= 1) {
   123         
   124         if (bitmapHandle) {
   125             if (samples > 1) {
   126                 attribs[7].value = 1;
   127                 attribs[8].value = samples;
   128             }
   129             else {
   130                 attribs[7].value = EGL_FALSE;
   131                 attribs[8].value = 0;
   132             }
   133         } else {
   134             if (samples > 1) {
   135                 attribs[6].value = 1;
   136                 attribs[7].value = samples;
   137             }
   138             else {
   139                 attribs[6].value = EGL_FALSE;
   140                 attribs[7].value = 0;
   141             }
   142         }
   143 
   144         /* Get the first matching config; according to EGL sorting
   145          * rules, this should be an accelerated one if possible */
   146         {
   147             EGLConfig config;
   148             int numConfigs;  
   149             EGLint error;            
   150         
   151             eglChooseConfig(eglGetDisplay(EGL_DEFAULT_DISPLAY),
   152                             (const int *) attribs,
   153                             &config, 1,
   154                             &numConfigs);
   155             
   156             error = eglGetError();
   157             if (error != EGL_SUCCESS) {
   158                 M3G_LOG1(M3G_LOG_FATAL_ERRORS, "eglChooseConfig  failed: %d\n", error);
   159             }
   160             
   161             
   162             M3G_ASSERT(error == EGL_SUCCESS);
   163 
   164             /* If we got a config, return that; otherwise, drop the
   165              * number of multisampling samples and try again, or
   166              * return NULL for no config if we already have zero
   167              * samples */
   168             
   169             if (numConfigs > 0) {
   170                 M3G_LOG1(M3G_LOG_RENDERING, "Selected EGL config #%d\n", config);
   171                 return config;
   172             }
   173 
   174             if (samples == 2) {
   175                 M3G_LOG(M3G_LOG_WARNINGS, "Warning: multisampling not available\n");
   176             }
   177         }
   178     }
   179 
   180     /* No matching configuration found */
   181     
   182     return NULL;
   183 }
   184 
   185 /*!
   186  * \internal
   187  * \brief Initializes EGL
   188  */
   189 static void m3gInitializeEGL(void)
   190 {
   191     M3G_LOG(M3G_LOG_INTERFACE, "Initializing EGL\n");
   192     eglInitialize(eglGetDisplay(EGL_DEFAULT_DISPLAY), NULL, NULL);
   193 }
   194 
   195 /*!
   196  * \internal
   197  * \brief Terminates EGL
   198  */
   199 static void m3gTerminateEGL(void)
   200 {
   201     M3G_LOG(M3G_LOG_INTERFACE, "Shutting down EGL\n");
   202     eglTerminate(eglGetDisplay(EGL_DEFAULT_DISPLAY));
   203 }
   204 
   205 /*!
   206  * \internal
   207  * \brief Creates a new EGL context
   208  */
   209 /*static EGLContext m3gCreateGLContext(M3Genum format,
   210                                      M3Gbitmask bufferBits,
   211                                      M3Gbitmask reqSurfaceBits,
   212                                      EGLContext share,
   213                                      M3Gbitmask *outSurfaceBits)
   214 {
   215     EGLContext ctx;
   216     EGLConfig config;
   217 
   218     M3G_ASSERT((reqSurfaceBits & ~(EGL_PIXMAP_BIT|EGL_PBUFFER_BIT|EGL_WINDOW_BIT)) == 0);
   219     
   220     config = m3gQueryEGLConfig(format, bufferBits, reqSurfaceBits, NULL);
   221     
   222     if (!config || !eglGetConfigAttrib(eglGetDisplay(EGL_DEFAULT_DISPLAY),
   223                                        config,
   224                                        EGL_SURFACE_TYPE,
   225                                        (EGLint *) outSurfaceBits)) {
   226         return NULL;
   227     }
   228     
   229     ctx = eglCreateContext(eglGetDisplay(EGL_DEFAULT_DISPLAY),
   230                            config,
   231                            share,
   232                            NULL);
   233     
   234 #   if defined(M3G_DEBUG)
   235     {
   236         EGLint err = eglGetError();
   237         M3G_ASSERT(err == EGL_SUCCESS || err == EGL_BAD_ALLOC);
   238     }
   239 #   endif
   240 
   241     M3G_LOG1(M3G_LOG_OBJECTS, "New GL context 0x%08X\n", (unsigned) ctx);
   242     return ctx;
   243 }
   244 */
   245 /*!
   246  * \internal
   247  * \brief Deletes an EGL context
   248  */
   249 static void m3gDeleteGLContext(EGLContext ctx)
   250 {
   251     eglDestroyContext(eglGetDisplay(EGL_DEFAULT_DISPLAY), ctx);
   252 #   if defined(M3G_DEBUG)
   253     {
   254         EGLint err = eglGetError();
   255         if (err != EGL_SUCCESS) {
   256             M3G_LOG1(M3G_LOG_FATAL_ERRORS, "EGL error 0x%08X\n", (unsigned) err);
   257         }
   258         M3G_ASSERT(err == EGL_SUCCESS);
   259     }
   260 #   endif
   261     M3G_LOG1(M3G_LOG_OBJECTS, "Destroyed GL context 0x%08X\n",
   262              (unsigned) ctx);
   263 }
   264 
   265     
   266 /*!
   267  * \internal
   268  * \brief Creates a new EGL window surface
   269  */
   270 static EGLSurface m3gCreateWindowSurface(M3Genum format,
   271                                          M3Gbitmask bufferBits,
   272                                          M3GNativeWindow wnd)
   273 {
   274     EGLSurface surf;
   275     EGLConfig config = m3gQueryEGLConfig(format, bufferBits, EGL_WINDOW_BIT, NULL);
   276     
   277     if (!config) {
   278         return NULL;
   279     }
   280 
   281     surf = eglCreateWindowSurface(eglGetDisplay(EGL_DEFAULT_DISPLAY),
   282                                   config,
   283                                   (NativeWindowType) wnd,
   284                                   NULL);
   285 
   286 #   if defined(M3G_DEBUG)
   287     {
   288         EGLint err = eglGetError();
   289         M3G_ASSERT(err == EGL_SUCCESS || err == EGL_BAD_ALLOC);
   290     }
   291 #   endif
   292 
   293     if (surf != EGL_NO_SURFACE) {
   294         M3G_LOG1(M3G_LOG_OBJECTS, "New GL window surface 0x%08X\n",
   295                  (unsigned) surf);
   296         return surf;
   297     }
   298     return NULL;
   299 }
   300 
   301 
   302 /*!
   303  * \internal
   304  * \brief Creates a new EGL pixmap surface
   305  */
   306 static EGLSurface m3gCreateBitmapSurface(M3Genum format,
   307                                          M3Gbitmask bufferBits,
   308                                          M3GNativeBitmap bmp)
   309 {
   310     EGLSurface surf;
   311     EGLConfig config = m3gQueryEGLConfig(format, bufferBits, EGL_PIXMAP_BIT, bmp);
   312     
   313     if (!config) {
   314         return NULL;
   315     }
   316     
   317     surf = eglCreatePixmapSurface(eglGetDisplay(EGL_DEFAULT_DISPLAY),
   318                                   config,
   319                                   (NativePixmapType) bmp,
   320                                   NULL);
   321 
   322 #   if defined(M3G_DEBUG)
   323     {
   324         EGLint err = eglGetError();
   325         M3G_ASSERT(err == EGL_SUCCESS || err == EGL_BAD_ALLOC);
   326     }
   327 #   endif
   328     
   329     if (surf != EGL_NO_SURFACE) {
   330         M3G_LOG1(M3G_LOG_OBJECTS, "New GL pixmap surface 0x%08X\n",
   331                  (unsigned) surf);
   332         return surf;
   333     }
   334     return NULL;
   335 }
   336 
   337 
   338 /*!
   339  * \internal
   340  * \brief Creates a new PBuffer
   341  */
   342 static EGLSurface m3gCreatePBufferSurface(M3Genum format,
   343                                           M3Gbitmask bufferBits,
   344                                           M3Gint width, M3Gint height)
   345 {
   346     EGLSurface surf;
   347     EGLConfig config;
   348     EGLint attrib[5];
   349     
   350     attrib[0] = EGL_WIDTH;
   351     attrib[1] = width;
   352     attrib[2] = EGL_HEIGHT;
   353     attrib[3] = height;
   354     attrib[4] = EGL_NONE;
   355     
   356     config = m3gQueryEGLConfig(format, bufferBits, EGL_PBUFFER_BIT, NULL);
   357     if (!config) {
   358         return NULL;
   359     }
   360 
   361     surf = eglCreatePbufferSurface(eglGetDisplay(EGL_DEFAULT_DISPLAY),
   362                                    config,
   363                                    attrib);
   364 #   if defined(M3G_DEBUG)
   365     {
   366         EGLint err = eglGetError();
   367         M3G_ASSERT(err == EGL_SUCCESS || err == EGL_BAD_ALLOC);
   368     }
   369 #   endif
   370                                               
   371     if (surf != EGL_NO_SURFACE) {
   372         M3G_LOG1(M3G_LOG_OBJECTS, "New GL pbuffer surface 0x%08X\n",
   373                  (unsigned) surf);
   374         return surf;
   375     }
   376     return NULL;
   377 }
   378 
   379 
   380 /*!
   381  * \internal
   382  * \brief Deletes an EGL surface
   383  */
   384 static void m3gDeleteGLSurface(EGLSurface surface)
   385 {
   386     eglDestroySurface(eglGetDisplay(EGL_DEFAULT_DISPLAY), surface);
   387 
   388 #   if defined(M3G_DEBUG)
   389     {
   390         EGLint err = eglGetError();
   391         M3G_ASSERT(err == EGL_SUCCESS);
   392     }
   393 #   endif
   394 
   395     M3G_LOG1(M3G_LOG_OBJECTS, "Destroyed GL surface 0x%08X\n",
   396              (unsigned) surface);
   397 }
   398 
   399 /*!
   400  * \brief Swap buffers on a rendering surface
   401  */
   402 static M3Gbool m3gSwapBuffers(EGLSurface surface)
   403 {
   404     EGLBoolean success = eglSwapBuffers(eglGetDisplay(EGL_DEFAULT_DISPLAY),
   405                                         surface);
   406     
   407 #   if defined(M3G_DEBUG)
   408     EGLint err = eglGetError();
   409     M3G_ASSERT(err == EGL_SUCCESS);
   410 #   endif
   411 
   412     return (M3Gbool) success;
   413 }
   414 
   415 /*!
   416  * \brief Does a sub-blit of a frame buffer blit operation
   417  */
   418 static void m3gBlitFrameBufferPixels2(RenderContext *ctx,
   419                                       M3Gint xOffset, M3Gint yOffset,
   420                                       M3Gint width, M3Gint height,
   421                                       M3GPixelFormat internalFormat,
   422                                       M3Gsizei stride,
   423                                       const M3Gubyte *pixels)
   424 {
   425 #   define MAX_TEMP_TEXTURES    8
   426     GLuint glFormat;
   427     static const int MAX_TILE_SIZE = 256; /* -> 256 KB temp buffer(s) */
   428     static const M3Gbyte tc[8] = { 0, 0, 0, 1, 1, 0, 1, 1 };
   429     GLshort pos[8];
   430     int tileWidth = MAX_TILE_SIZE, tileHeight = MAX_TILE_SIZE;
   431     M3Gbool mustConvert = M3G_FALSE;
   432     M3Gubyte *tempPixels = 0; /* initialize to avoid compiler warnings */
   433     GLuint tempTexObj[MAX_TEMP_TEXTURES];
   434     GLint tempTexCount;
   435         
   436     M3G_VALIDATE_OBJECT(ctx);
   437     M3G_ASSERT_GL;
   438 
   439     /* Analyze source and destination formats for possible conversion */
   440     
   441     glFormat = m3gGetGLFormat(internalFormat);
   442     if (!glFormat) {
   443         M3G_ASSERT(M3G_FALSE);  /* internal format not supported in GL */
   444         return;
   445     }
   446     if (internalFormat == M3G_RGB8_32) {
   447         glFormat = GL_RGBA;
   448     }
   449     if (internalFormat == M3G_BGR8_32 || internalFormat == M3G_ARGB8) {
   450         glFormat = GL_RGBA;
   451         mustConvert = M3G_TRUE;
   452     }
   453 
   454     /* Tweak tile size to avoid using excessive amounts of memory for
   455      * portions outside the blit area */
   456 
   457     M3G_ASSERT((width > 0) && (height > 0));
   458     
   459     while (tileWidth >= width * 2) {
   460         tileWidth >>= 1;
   461         tileHeight <<= 1;
   462     }
   463     while (tileHeight >= height * 2) {
   464         tileHeight >>= 1;
   465     }
   466         
   467     /* Allocate temp memory for conversion or adjust tile size for
   468      * optimal direct download to GL */
   469     
   470     if (mustConvert) {
   471         tempPixels = m3gAllocTemp(M3G_INTERFACE(ctx),
   472                                   tileWidth * tileHeight * 4);
   473         if (!tempPixels) {
   474             return; /* out of memory */
   475         }
   476     }
   477     else {
   478 
   479         /* Attempt to adjust the tile size so that we can copy
   480          * complete scanlines at a time -- this is because OpenGL ES
   481          * is missing PixelStore settings that could be used for
   482          * stride control during image uploading */
   483 
   484         M3Gint maxWidth;
   485         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxWidth);
   486         
   487         while (tileWidth < width &&
   488                tileWidth < maxWidth &&
   489                tileHeight > 1) {
   490             tileWidth <<= 1;
   491             tileHeight >>= 1;
   492         }
   493     }
   494     
   495     /* Load default images into the temp texture objects */
   496     
   497     glActiveTexture(GL_TEXTURE0);
   498     glEnable(GL_TEXTURE_2D);
   499     {
   500         int ti;
   501         tempTexCount = ((width + tileWidth - 1) / tileWidth)
   502             * ((height + tileHeight - 1) / tileHeight);
   503         tempTexCount = m3gMinInt(tempTexCount, MAX_TEMP_TEXTURES);
   504         
   505         glGenTextures(tempTexCount, tempTexObj);
   506         
   507         for (ti = 0; ti < tempTexCount; ++ti) {
   508             glBindTexture(GL_TEXTURE_2D, tempTexObj[ti]);
   509             glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
   510             glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   511             glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   512             M3G_ASSERT_GL;
   513             
   514             glTexImage2D(GL_TEXTURE_2D, 0,
   515                          glFormat,
   516                          tileWidth, tileHeight,
   517                          0,
   518                          glFormat,
   519                          GL_UNSIGNED_BYTE, NULL);
   520 
   521             /* Raise out-of-memory if OpenGL ran out of resources */
   522             {
   523                 GLint err = glGetError();
   524 
   525                 if (err == GL_OUT_OF_MEMORY) {
   526                     m3gRaiseError(M3G_INTERFACE(ctx), M3G_OUT_OF_MEMORY);
   527                     goto CleanUpAndExit;
   528                 }
   529                 else if (err != GL_NO_ERROR) {
   530                     M3G_ASSERT(M3G_FALSE);
   531                 }
   532             }
   533         }
   534     }
   535 
   536     /* Set up texture and vertex coordinate arrays for the image tiles */
   537 
   538     glClientActiveTexture(GL_TEXTURE0);
   539     glTexCoordPointer(2, GL_BYTE, 0, tc);
   540     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
   541     glVertexPointer(2, GL_SHORT, 0, pos);
   542     glEnableClientState(GL_VERTEX_ARRAY);
   543     glMatrixMode(GL_TEXTURE);
   544     glLoadIdentity();
   545     glMatrixMode(GL_MODELVIEW);
   546     M3G_ASSERT_GL;
   547 
   548     /* Load each image tile into a texture and draw */
   549 
   550     {
   551         M3Gint nextTexTile = 0;
   552         M3Gint x, y, bpp;
   553         bpp = m3gBytesPerPixel(internalFormat);
   554         if (stride == 0) {
   555             stride = bpp * width;
   556         }
   557 
   558         for (y = 0; y < height; y += tileHeight) {
   559             for (x = 0; x < width; x += tileWidth) {
   560                 M3Gint w, h;
   561 
   562                 w = M3G_MIN(tileWidth, width - x);
   563                 h = M3G_MIN(tileHeight, height - y);
   564 
   565                 glBindTexture(GL_TEXTURE_2D, tempTexObj[nextTexTile]);
   566                 nextTexTile = (nextTexTile + 1) % MAX_TEMP_TEXTURES;
   567                               
   568                 if (mustConvert) {
   569                     m3gConvertPixelRect(internalFormat,
   570                                         pixels + y * stride + x * bpp,
   571                                         stride,
   572                                         w, h,
   573                                         M3G_RGBA8, tempPixels, w * 4);
   574                     glTexSubImage2D(GL_TEXTURE_2D, 0,
   575                                     0, 0,
   576                                     w, h,
   577                                     GL_RGBA, GL_UNSIGNED_BYTE, tempPixels);
   578                 }
   579                 else {
   580                     if (w*bpp == stride) {
   581                         glTexSubImage2D(GL_TEXTURE_2D, 0,
   582                                         0, 0,
   583                                         w, h,
   584                                         glFormat,
   585                                         GL_UNSIGNED_BYTE,
   586                                         pixels + y * stride + x * bpp);
   587                     }
   588                     else {
   589                         int k;
   590                         for (k = 0; k < h; ++k) {
   591                             glTexSubImage2D(GL_TEXTURE_2D, 0,
   592                                             0, k,
   593                                             w, 1,
   594                                             glFormat,
   595                                             GL_UNSIGNED_BYTE,
   596                                             pixels + (y+k) * stride + x * bpp);
   597                         }
   598                     }
   599                 }
   600                 
   601                 pos[0] = (GLshort)(x + xOffset);
   602                 pos[1] = (GLshort)((height - y) + yOffset);
   603                 pos[2] = pos[0];
   604                 pos[3] = (GLshort)((height - (y + tileHeight)) + yOffset);
   605                 pos[4] = (GLshort)((x + tileWidth) + xOffset);
   606                 pos[5] = pos[1];
   607                 pos[6] = pos[4];
   608                 pos[7] = pos[3];
   609                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
   610             }
   611         }
   612         M3G_ASSERT_GL;
   613     }
   614 
   615     /* Restore required OpenGL state and release resources */
   616 
   617 CleanUpAndExit:
   618     if (mustConvert) {
   619         m3gFreeTemp(M3G_INTERFACE(ctx));
   620     }
   621 
   622     glDeleteTextures(tempTexCount, tempTexObj);
   623         
   624     M3G_ASSERT_GL;
   625     
   626 #   undef MAX_TEMP_TEXTURES
   627 }
   628 
   629 /*----------------------------------------------------------------------
   630  * Internal functions
   631  *--------------------------------------------------------------------*/
   632 
   633 /* The frame buffer should be the first thing locked and the last one
   634  * released, so let's mandate that even though it has no real effect
   635  * with EGL */
   636 #define m3gLockFrameBuffer(ctx)    M3G_ASSERT_NO_LOCK(M3G_INTERFACE(ctx))
   637 #define m3gReleaseFrameBuffer(ctx) M3G_ASSERT_NO_LOCK(M3G_INTERFACE(ctx))
   638 
   639 /*!
   640  * \internal
   641  * \brief Alternate rendering function for two-sided lighting on buggy
   642  * hardware
   643  */
   644 static M3Gbool m3gSplitDrawMesh(RenderContext *ctx,
   645                                 const VertexBuffer *vb,
   646                                 const IndexBuffer *ib,
   647                                 const Appearance *app,
   648                                 const M3GMatrix *modelTransform,
   649                                 M3Gint alphaFactor,
   650                                 M3Gint scope)
   651 {
   652     if (!ctx->inSplitDraw && m3gGetMaterial((M3GAppearance) app) && vb->normals) {
   653         PolygonMode *pm = m3gGetPolygonMode((M3GAppearance) app);
   654         if (pm && pm->enableTwoSidedLighting) {
   655             M3Gint originalCulling = m3gGetCulling(pm);
   656             if (originalCulling != M3G_CULL_BACK) {
   657                 
   658                 /* OK, we must render the back sides separately with
   659                  * flipped normals */
   660                     
   661                 Interface *m3g = M3G_INTERFACE(ctx);
   662                 VertexArray *tempNormals;
   663                 
   664                 M3Gint normalCount = vb->vertexCount;
   665                 M3Gint normalStride = vb->normals->stride;
   666 
   667                 /* Duplicate the normal array */
   668                 
   669                 m3gReleaseFrameBuffer(ctx);
   670                 tempNormals = m3gCloneVertexArray(vb->normals);
   671                 if (!tempNormals) {
   672                     m3gLockFrameBuffer(ctx);
   673                     return M3G_TRUE; /* automatic out-of-memory */
   674                 }
   675 
   676                 /* Flip the signs of the temp normals */
   677 
   678                 if (tempNormals->elementType == GL_BYTE) {
   679                     M3Gbyte *p = (M3Gbyte*) m3gMapObject(m3g, tempNormals->data);
   680                     int i;
   681                     for (i = 0; i < normalCount; ++i) {
   682                         p[0] = (M3Gbyte) -m3gClampInt(p[0], -127, 127);
   683                         p[1] = (M3Gbyte) -m3gClampInt(p[1], -127, 127);
   684                         p[2] = (M3Gbyte) -m3gClampInt(p[2], -127, 127);
   685                         p += normalStride;
   686                     }
   687                 }
   688                 else {
   689                     M3Gshort *p = (M3Gshort*) m3gMapObject(m3g, tempNormals->data);
   690                     int i;
   691                     for (i = 0; i < normalCount; ++i) {
   692                         p[0] = (M3Gshort) -m3gClampInt(p[0], -32767, 32767);
   693                         p[1] = (M3Gshort) -m3gClampInt(p[1], -32767, 32767);
   694                         p[2] = (M3Gshort) -m3gClampInt(p[2], -32767, 32767);
   695                         p += normalStride / 2;
   696                     }
   697                 }
   698                 m3gUnmapObject(m3g, tempNormals->data);
   699                 m3gLockFrameBuffer(ctx);
   700                 
   701                 ctx->inSplitDraw = M3G_TRUE;
   702 
   703                 /* Set culling to front faces only and render with the
   704                  * flipped normals */
   705                 {
   706                     VertexArray *orgNormals = vb->normals;
   707                     ((VertexBuffer*)vb)->normals = tempNormals;
   708                     m3gSetCulling(pm, M3G_CULL_FRONT);
   709                     m3gDrawMesh(ctx,
   710                                 vb, ib, app,
   711                                 modelTransform,
   712                                 alphaFactor, scope);
   713                     ((VertexBuffer*)vb)->normals = orgNormals;
   714                 }
   715 
   716                 /* If no culling was enabled, render the front faces
   717                  * with the original normals */
   718 
   719                 if (originalCulling == M3G_CULL_NONE) {
   720                     m3gSetCulling(pm, M3G_CULL_BACK);
   721                     m3gDrawMesh(ctx,
   722                                 vb, ib, app,
   723                                 modelTransform,
   724                                 alphaFactor, scope);
   725                 }
   726 
   727                 /* Restore original culling and free the temp normals */
   728                     
   729                 m3gSetCulling(pm, originalCulling);
   730                 
   731                 m3gReleaseFrameBuffer(ctx);
   732                 m3gDeleteObject((M3GObject) tempNormals);
   733                 m3gLockFrameBuffer(ctx);
   734 
   735                 ctx->inSplitDraw = M3G_FALSE;
   736                 return M3G_TRUE;
   737             }
   738         }
   739     }
   740     return M3G_FALSE;
   741 }
   742 
   743 /*!
   744  * \internal
   745  * \brief Determines whether a format/mode combination can be directly
   746  * rendered
   747  */
   748 static M3Gbool m3gCanDirectRender(const RenderContext *ctx)
   749 {
   750     M3GPixelFormat format = ctx->target.format;
   751     M3Gbitmask bufferBits = ctx->bufferBits;
   752     M3Gbitmask surfaceType = ctx->target.type;
   753     M3GNativeBitmap bitmapHandle = ctx->target.handle;
   754     int i;
   755 
   756     /* Images always go via pbuffers; EGL surfaces can always be
   757      * rendered to */
   758     
   759     if (surfaceType == SURFACE_IMAGE) {
   760         return M3G_FALSE;
   761     }
   762     if (surfaceType == SURFACE_EGL || surfaceType == SURFACE_WINDOW) {
   763         return M3G_TRUE;
   764     }
   765     
   766     /* First scan the context cache for a matching previously used
   767      * context; this should be faster than querying EGL */
   768     
   769     for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) {
   770         const GLContextRecord *rc = &ctx->glContext[i];
   771         
   772         if ((rc->surfaceTypeBits & surfaceType) == surfaceType
   773             && rc->format == format
   774             && (rc->bufferBits & bufferBits) == bufferBits) {
   775             
   776             return M3G_TRUE;
   777         }
   778     }
   779 
   780     /* No dice; must resort to querying from EGL */
   781 
   782     return (m3gQueryEGLConfig(format, bufferBits, (EGLint) surfaceType, bitmapHandle) != NULL);
   783 }
   784 
   785 /*!
   786  * \internal
   787  * \brief Ensures that a sufficient back buffer exists
   788  *
   789  * Creates a new PBuffer for the back buffer if required.
   790  */
   791 static M3Gbool m3gValidateBackBuffer(RenderContext *ctx)
   792 {
   793     BackBuffer *bbuf = &ctx->backBuffer;
   794     int w = ctx->target.width;
   795     int h = ctx->target.height;
   796 
   797     /* NOTE the EGL specification is fuzzy on eglCopyBuffers when the
   798      * pbuffer is larger than the target, so we require an exact match
   799      * (can be relaxed by #undefining the flag, see m3g_defs.h) */
   800 
   801 #   if defined(M3G_GL_FORCE_PBUFFER_SIZE)
   802     if (bbuf->width != w || bbuf->height != h) {
   803 #   else
   804     if (bbuf->width < w || bbuf->height < h) {
   805 #   endif
   806         
   807         M3G_LOG(M3G_LOG_WARNINGS,
   808                 "Warning (performance): Buffered rendering.\n");
   809         
   810         if (bbuf->glSurface != NULL) {
   811             m3gDeleteGLSurface(bbuf->glSurface);
   812         }
   813         
   814         bbuf->glSurface = m3gCreatePBufferSurface(
   815             M3G_RGBA8,
   816             (M3Gbitmask)(M3G_COLOR_BUFFER_BIT|M3G_DEPTH_BUFFER_BIT|M3G_MULTISAMPLE_BUFFER_BIT),
   817             w, h);
   818         
   819         bbuf->width = w;
   820         bbuf->height = h;
   821         
   822         if (!bbuf->glSurface) {
   823             if (eglGetError() == EGL_BAD_ALLOC) {
   824                 return M3G_FALSE; /* ouf of memory */
   825             }
   826             else {
   827                 M3G_ASSERT(M3G_FALSE);
   828             }
   829         }
   830     }
   831     return M3G_TRUE;
   832 }
   833 
   834 /*!
   835  * \internal
   836  * \brief Increment the rendering time stamp
   837  *
   838  * The time stamp is used to manage the various caches for the
   839  * context, so it needs to be updated often enough for the caches to
   840  * function optimally.
   841  * 
   842  * In the rare case that the time stamp should wrap around(!), we
   843  * reset the time stamps dependent on it to avoid sub-optimal cache
   844  * performance.
   845  */
   846 static void m3gIncrementRenderTimeStamp(RenderContext *ctx)
   847 {
   848     if (++ctx->cacheTimeStamp == 0) {
   849         int i;
   850         for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) {
   851             ctx->glContext[i].lastUseTime = 0;
   852         }
   853         for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
   854             ctx->glSurface[i].lastUseTime = 0;
   855         }
   856     }
   857 }
   858 
   859 /*!
   860  * \internal
   861  * \brief Draws a raw RGB or RGBA pixel rectangle of arbitrary size
   862  * into the frame buffer
   863  *
   864  * The offset only affects the position of the blitted rectangle in
   865  * the frame buffer. The source data is read starting at the given
   866  * pointer.
   867  * 
   868  * \param ctx            the rendering context
   869  * \param xOffset        offset from the left edge of the frame buffer
   870  * \param yOffset        offset from the bottom of the frame buffer
   871  * \param width          width of the rectangle
   872  * \param height         height of the rectangle
   873  * \param internalFormat format of the source pixels
   874  * \param stride         stride of the source data
   875  * \param pixels         pointer to the source pixels in top-to-bottom order
   876  */
   877 static void m3gBlitFrameBufferPixels(RenderContext *ctx,
   878                                      M3Gint xOffset, M3Gint yOffset,
   879                                      M3Gint width, M3Gint height,
   880                                      M3GPixelFormat internalFormat,
   881                                      M3Gsizei stride,
   882                                      const M3Gubyte *pixels)
   883 {
   884     /* Skip this if nothing to copy */
   885 
   886     if (width <= 0 || height <= 0) {
   887         return;
   888     }
   889     
   890     /* Set viewport, projection and modelview to map coordinates to
   891      * pixel boundaries */
   892 
   893     glScissor(xOffset, yOffset, width, height);
   894     glViewport(0, 0, ctx->target.width, ctx->target.height);
   895     glMatrixMode(GL_PROJECTION);
   896     glLoadIdentity();
   897     glOrthox(0, ctx->target.width << 16,
   898              0, ctx->target.height << 16,
   899              -1 << 16, 1 << 16);
   900     glMatrixMode(GL_MODELVIEW);
   901     glLoadIdentity();
   902     
   903     /* Disable any stray state we don't want */
   904 
   905     glDisable(GL_CULL_FACE);
   906     glDisable(GL_BLEND);
   907     glDisable(GL_ALPHA_TEST);
   908     glDisableClientState(GL_NORMAL_ARRAY);
   909     glDisableClientState(GL_COLOR_ARRAY);
   910     glDisable(GL_LIGHTING);
   911     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
   912     glDepthMask(GL_FALSE);
   913     glDepthFunc(GL_ALWAYS);
   914     m3gDisableTextures();
   915     M3G_ASSERT_GL;
   916     
   917     /* Split the large blit operation into smaller chunks that are
   918      * efficiently taken care of using power-of-two textures */
   919     {
   920         const int MAX_BLIT_SIZE = 256; /* should be power of two */
   921     
   922         int xBlits = (width / MAX_BLIT_SIZE) + 1;
   923         int yBlits = (height / MAX_BLIT_SIZE) + 1;
   924 
   925         int xBlit, yBlit;
   926     
   927         for (yBlit = yBlits-1; yBlit >= 0; yBlit--) {
   928             for (xBlit = 0; xBlit < xBlits; ++xBlit) {
   929             
   930                 M3Gint xStart = xOffset + xBlit * MAX_BLIT_SIZE;
   931                 M3Gint yStart = yOffset + yBlit * MAX_BLIT_SIZE;
   932                 M3Gint xSize = m3gMinInt(MAX_BLIT_SIZE, width - (xStart - xOffset));
   933                 M3Gint ySize = m3gMinInt(MAX_BLIT_SIZE, height - (yStart - yOffset));
   934 
   935                 M3Gint srcOffset = (height - (yStart - yOffset + ySize)) * stride + (xStart - xOffset) * m3gBytesPerPixel(ctx->target.format);
   936                 
   937                 m3gBlitFrameBufferPixels2(ctx,
   938                                           xStart, yStart,
   939                                           xSize, ySize,
   940                                           internalFormat,
   941                                           stride,
   942                                           pixels + srcOffset);
   943             }
   944         }
   945     }
   946 }
   947 
   948 /*!
   949  * \internal
   950  * \brief Synchronizes the contents of the back buffer with the target
   951  * buffer
   952  */
   953 static void m3gUpdateBackBuffer(RenderContext *ctx)
   954 {
   955     if (ctx->target.type == SURFACE_IMAGE) {
   956         m3gDrawFrameBufferImage(ctx, (Image *) ctx->target.handle);
   957     }
   958     else if (ctx->target.type == SURFACE_BITMAP || ctx->target.type == SURFACE_MEMORY) {
   959 
   960         M3Gubyte *src;
   961         M3Gsizei stride;
   962 
   963         M3Gint clipWidth = ctx->clip.x1 - ctx->clip.x0;
   964         M3Gint clipHeight = ctx->clip.y1 - ctx->clip.y0;
   965         M3Gint srcOffset;
   966 
   967         if (ctx->target.type == SURFACE_BITMAP) {
   968             /* Obtain a pointer to the native bitmap and copy the data to
   969              * the backbuffer from there */
   970             if (!m3gglLockNativeBitmap((M3GNativeBitmap) ctx->target.handle,
   971                                       &src, &stride)) {
   972                 /* No dice! There's no way that we know of to copy the
   973                  * data between the buffers */
   974                 M3G_ASSERT(M3G_FALSE);
   975             }
   976         } else {
   977             /* Memory target */
   978             src = ctx->target.pixels;
   979             stride = ctx->target.stride;
   980         }
   981 
   982         srcOffset =
   983             (ctx->target.height - ctx->clip.y1) * stride
   984             + ctx->clip.x0 * m3gBytesPerPixel(ctx->target.format);
   985         
   986         m3gBlitFrameBufferPixels(
   987             ctx,
   988             ctx->clip.x0, ctx->clip.y0,
   989             clipWidth, clipHeight,
   990             ctx->target.format,
   991             stride,
   992             src + srcOffset);
   993 
   994         if (ctx->target.type == SURFACE_BITMAP) {
   995             m3gglReleaseNativeBitmap((M3GNativeBitmap) ctx->target.handle);
   996         }
   997     }
   998     else {
   999         /* Buffered rendering is not supported for window and pbuffer
  1000          * targets */
  1001         M3G_ASSERT(M3G_FALSE);
  1002     }
  1003     ctx->backBuffer.contentsValid = M3G_TRUE;
  1004 }
  1005 
  1006 /*!
  1007  * \internal
  1008  * \brief Synchronizes the contents of the target buffer with the back
  1009  * buffer
  1010  */
  1011 static void m3gUpdateTargetBuffer(RenderContext *ctx)
  1012 {
  1013     if (ctx->target.type == SURFACE_IMAGE) {
  1014         m3gCopyFrameBufferImage((Image *) ctx->target.handle);
  1015     }
  1016     else if (ctx->target.type == SURFACE_BITMAP || ctx->target.type == SURFACE_MEMORY) {
  1017         
  1018         M3GPixelFormat format = ctx->target.format;
  1019         M3Gint width = ctx->clip.x1 - ctx->clip.x0;
  1020         M3Gint height = ctx->clip.y1 - ctx->clip.y0;
  1021         M3Gint xOffset = ctx->clip.x0;
  1022         M3Gint yOffset = ctx->clip.y0;
  1023         M3Gint row;
  1024 
  1025         M3Gubyte *dst;
  1026         M3Gsizei stride;
  1027         M3Gubyte *temp;
  1028 
  1029         if (ctx->target.type == SURFACE_BITMAP) {
  1030             /* We must copy the back buffer to a native bitmap: first
  1031              * attempt a fast buffer-to-buffer copy using EGL, but if that
  1032              * fails, obtain a pointer and do the copy ourselves */
  1033 
  1034             /* We can only do the fast copy for the full buffer */
  1035 
  1036             M3Gbool fullClip = (ctx->clip.x0 == 0)
  1037                 && (ctx->clip.y0 <= ctx->target.height - ctx->display.height)
  1038                 && (ctx->clip.x1 >= ctx->display.width)
  1039                 && (ctx->clip.y1 >= ctx->clip.y0 + ctx->display.height);
  1040             
  1041             if (fullClip && eglCopyBuffers(eglGetDisplay(EGL_DEFAULT_DISPLAY),
  1042                                              ctx->backBuffer.glSurface,
  1043                                              (NativePixmapType) ctx->target.handle)) 
  1044             {
  1045                 return;
  1046             }
  1047             
  1048             /* Fast copy failed, try the generic approach */
  1049             if (!m3gglLockNativeBitmap((M3GNativeBitmap) ctx->target.handle,
  1050                                       &dst, &stride)) {
  1051                 /* No dice! There's no way that we know of to copy the
  1052                  * data between the buffers */
  1053                 M3G_ASSERT(M3G_FALSE);
  1054             }
  1055         } else {
  1056             /* Memory target */
  1057             dst = ctx->target.pixels;
  1058             stride = ctx->target.stride;
  1059         }
  1060                 
  1061         /* OK, got the pointer; now, copy a few scanlines at a
  1062          * time, and we can pretty much assume conversion since 
  1063          * the fast method didn't work */
  1064 
  1065 #define READPIXELS_BUFFER_SIZE      16384
  1066 
  1067         if (width > 0 && height > 0) {
  1068 
  1069             M3Gint bufSize = (width * 4 > READPIXELS_BUFFER_SIZE ? width * 4 : READPIXELS_BUFFER_SIZE);
  1070             M3Gint numLinesInBuffer = bufSize/(width * 4);
  1071             M3Gint numLinesRead, line;
  1072             temp = m3gAllocTemp(M3G_INTERFACE(ctx), bufSize);
  1073 
  1074             if (!temp) {
  1075                 return; /* out of memory */
  1076             }
  1077 
  1078             dst += (ctx->target.height - (yOffset + height)) * stride
  1079                 + xOffset * m3gBytesPerPixel(format);
  1080 
  1081             for (row = 0; row < height; row += numLinesRead) {
  1082                 line = numLinesRead = (row + numLinesInBuffer > height) ? (height - row) : numLinesInBuffer;
  1083 
  1084                 glReadPixels(xOffset, 
  1085                     yOffset + height - row - numLinesRead,
  1086                     width, numLinesRead, 
  1087                     GL_RGBA, GL_UNSIGNED_BYTE, 
  1088                     temp);
  1089 
  1090                 while (line-- > 0) {
  1091                     m3gConvertPixels(M3G_RGBA8, &temp[4*line*width], format, dst, width);
  1092                     dst += stride;
  1093                 }
  1094             }
  1095             m3gFreeTemp(M3G_INTERFACE(ctx));
  1096         }
  1097 
  1098         if (ctx->target.type == SURFACE_BITMAP) {
  1099             m3gglReleaseNativeBitmap((M3GNativeBitmap) ctx->target.handle);
  1100         }
  1101 
  1102 #undef READPIXELS_BUFFER_SIZE
  1103 
  1104     } 
  1105     else {
  1106         /* Buffered rendering is not supported for window and pbuffer
  1107          * targets */
  1108         M3G_ASSERT(M3G_FALSE);
  1109     }
  1110 }
  1111 
  1112 /*!
  1113  * \internal
  1114  * \brief Selects a GL context matching a given GL surface and a set
  1115  * of rendering parameters
  1116  *
  1117  * If no existing context matches, a new one is created. Contexts are
  1118  * stored in a fixed-size cache and managed using a LRU policy.
  1119  */
  1120 static EGLContext m3gSelectGLContext(RenderContext *ctx,
  1121                                      M3GPixelFormat format,
  1122                                      M3Gbitmask bufferBits,
  1123                                      M3Gbitmask surfaceTypeBits,
  1124                                      EGLSurface surface)
  1125 {
  1126     int i;
  1127     
  1128     /* Look for a matching cached context and attempt to make it
  1129      * current; on success, update the time in the context record and
  1130      * return the GL context handle */
  1131 
  1132     for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) {
  1133         GLContextRecord *rc = &ctx->glContext[i];
  1134         
  1135         if ((rc->surfaceTypeBits & surfaceTypeBits) == surfaceTypeBits
  1136             && rc->format == format
  1137             && (rc->bufferBits & bufferBits) == bufferBits) {
  1138 
  1139             if (eglMakeCurrent(eglGetDisplay(EGL_DEFAULT_DISPLAY), 
  1140                                surface, surface, rc->handle)) {
  1141                 rc->lastUseTime = ctx->cacheTimeStamp;
  1142                 return rc->handle;
  1143             }
  1144             else {
  1145                 /* NOTE we intentionally clear the error flag, since
  1146                  * the MakeCurrent call above can fail in case of a
  1147                  * context mismatch */
  1148                 eglGetError();
  1149             }
  1150         }
  1151     }
  1152 
  1153     /* No match found, we must create a new context */
  1154     {
  1155         GLContextRecord *lru = &ctx->glContext[0];
  1156         EGLContext shareRc = lru->handle;
  1157         EGLContext glrc;
  1158 
  1159         /* Find the least recently used context entry */
  1160         
  1161         for (i = 1; i < M3G_MAX_GL_CONTEXTS; ++i) {
  1162             GLContextRecord *rc = &ctx->glContext[i];
  1163             if (rc->handle) {
  1164                 shareRc = rc->handle; /* keep this for sharing */
  1165             }
  1166             if (!rc->handle || rc->lastUseTime < lru->lastUseTime) {
  1167                 lru = rc;
  1168             }
  1169         }
  1170 
  1171         /* Create a new GL context, then delete the LRU one. This is
  1172          * done in this order so that we don't lose any shared texture
  1173          * objects when deleting a context. */
  1174 
  1175         //if (surfaceTypeBits == SURFACE_EGL) 
  1176         {
  1177             EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  1178             EGLint configID;
  1179             eglQuerySurface(dpy,
  1180                             surface,//(EGLSurface) ctx->target.handle,
  1181                             EGL_CONFIG_ID,
  1182                             &configID);
  1183             glrc = eglCreateContext(dpy, (EGLConfig) configID, shareRc, NULL);
  1184             //M3G_ASSERT(glrc);
  1185         }
  1186         /*else {
  1187             glrc = m3gCreateGLContext(format,
  1188                                       bufferBits,
  1189                                       surfaceTypeBits,
  1190                                       shareRc,
  1191                                       &lru->surfaceTypeBits);
  1192         }
  1193         */
  1194         if (!glrc) {
  1195             m3gRaiseError(M3G_INTERFACE(ctx), M3G_OUT_OF_MEMORY);
  1196             return NULL;
  1197         }
  1198         if (lru->handle) {
  1199             m3gDeleteGLContext(lru->handle);
  1200         }
  1201         
  1202         /* Store the parameters for the new context and make it
  1203          * current */
  1204         
  1205         lru->handle = glrc;
  1206 		lru->surfaceTypeBits = surfaceTypeBits;
  1207         lru->format = format;
  1208         lru->bufferBits = bufferBits;
  1209         lru->modeBits = ctx->modeBits;
  1210         {
  1211             M3Gbool ok = eglMakeCurrent(eglGetDisplay(EGL_DEFAULT_DISPLAY),
  1212                                         surface, surface, glrc);
  1213             M3G_ASSERT(ok);
  1214             if (!ok) {
  1215                 return NULL;
  1216             }
  1217         }
  1218         lru->lastUseTime = ctx->cacheTimeStamp;
  1219         m3gSetGLDefaults();
  1220         return glrc;
  1221     }
  1222 }
  1223 
  1224 /*!
  1225  * \internal
  1226  * \brief Selects a GL surface suitable for rendering into the current
  1227  * target using the currently set rendering parameters
  1228  *
  1229  * If no existing surface matches, a new one is created. Surfaces are
  1230  * stored in a fixed-size LRU cache.
  1231  */
  1232 static EGLSurface m3gSelectGLSurface(RenderContext *ctx)
  1233 {
  1234     int attempts = 0;
  1235     int i;
  1236 
  1237     /* Quick exit for EGL surfaces */
  1238 
  1239     if (ctx->target.type == SURFACE_EGL) {
  1240         return (EGLSurface) ctx->target.handle;
  1241     }
  1242     
  1243     /* Buffered rendering is handled elsewhere! */
  1244     
  1245     if (ctx->target.buffered) {
  1246         M3G_ASSERT(M3G_FALSE);
  1247         return NULL;
  1248     }
  1249 
  1250     /* Find the first matching surface and return it */
  1251     
  1252     for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
  1253         GLSurfaceRecord *surf = &ctx->glSurface[i];
  1254         
  1255         if ((surf->type == ctx->target.type)
  1256             && (surf->targetHandle == ctx->target.handle)
  1257             && ((ctx->bufferBits & surf->bufferBits) == ctx->bufferBits)
  1258             && (surf->width == ctx->target.width)
  1259             && (surf->height == ctx->target.height)
  1260             && (surf->format == ctx->target.format)
  1261             && (surf->pixels == ctx->target.pixels)) {
  1262             
  1263             surf->lastUseTime = ctx->cacheTimeStamp;
  1264             return surf->handle;
  1265         }
  1266     }
  1267 
  1268     /* No matching surface found; must create a new one. If the cache
  1269      * is fully occupied, or if we run out of memory, one of the
  1270      * existing surfaces is swapped out */
  1271     
  1272     while (attempts <= 1) {
  1273         
  1274         GLSurfaceRecord *lru = &ctx->glSurface[0];
  1275 
  1276         /* Find the first entry without a GL surface handle, or the
  1277          * least recently used one if all are occupied. */
  1278         
  1279         for (i = 1; lru->handle != NULL && i < M3G_MAX_GL_SURFACES; ++i) {
  1280             GLSurfaceRecord *surf = &ctx->glSurface[i];
  1281             if (!surf->handle || surf->lastUseTime < lru->lastUseTime) {
  1282                 lru = surf;
  1283             }
  1284         }
  1285 
  1286         /* Delete the existing surface if we hit an occupied slot */
  1287         
  1288         if (lru->handle) {
  1289             m3gDeleteGLSurface(lru->handle);
  1290         }
  1291 
  1292         /* Create a new surface depending on the type of the current
  1293          * rendering target */
  1294         
  1295         switch (ctx->target.type) {
  1296         case SURFACE_BITMAP:
  1297             lru->handle =
  1298                 m3gCreateBitmapSurface(ctx->target.format,
  1299                                        ctx->bufferBits,
  1300                                        (M3GNativeBitmap) ctx->target.handle);
  1301             break;
  1302         case SURFACE_WINDOW:
  1303             lru->handle =
  1304                 m3gCreateWindowSurface(ctx->target.format,
  1305                                        ctx->bufferBits,
  1306                                        (M3GNativeWindow) ctx->target.handle);
  1307             break;
  1308         default:
  1309             M3G_ASSERT(M3G_FALSE);
  1310             return NULL;
  1311         }
  1312 
  1313         /* Success, return the new surface */
  1314 
  1315         if (lru->handle) {
  1316             lru->type         = ctx->target.type;
  1317             lru->targetHandle = ctx->target.handle;
  1318             lru->bufferBits   = ctx->bufferBits;
  1319             lru->width        = ctx->target.width;
  1320             lru->height       = ctx->target.height;
  1321             lru->format       = ctx->target.format;
  1322             lru->pixels       = ctx->target.pixels;
  1323             lru->lastUseTime  = ctx->cacheTimeStamp;
  1324             return lru->handle;
  1325         }
  1326 
  1327         /* No surface created, likely due to running out of memory;
  1328          * delete all existing surfaces and try again */
  1329         
  1330         if (!lru->handle) {
  1331             for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
  1332                 GLSurfaceRecord *surf = &ctx->glSurface[i];
  1333                 if (surf->handle) {
  1334                     m3gDeleteGLSurface(surf->handle);
  1335                     surf->handle = NULL;
  1336                     surf->type = SURFACE_NONE;
  1337                 }
  1338             }
  1339             ++attempts;
  1340             continue;
  1341         }
  1342     }
  1343 
  1344     /* Couldn't create a new surface; must return with an error */
  1345     
  1346     m3gRaiseError(M3G_INTERFACE(ctx), M3G_OUT_OF_MEMORY);
  1347     return NULL;
  1348 }
  1349 
  1350 
  1351 /*!
  1352  * \internal
  1353  * \brief Deletes all native surfaces for a specific target
  1354  *
  1355  * \param ctx    rendering context
  1356  * \param type   bitmask of the types of surfaces to remove
  1357  * \param handle native target handle of the surfaces to remove
  1358  */
  1359 static void m3gDeleteGLSurfaces(RenderContext *ctx,
  1360                                 M3Gbitmask type,
  1361                                 M3Guint handle)
  1362 {
  1363     int i;
  1364     M3G_VALIDATE_OBJECT(ctx);
  1365     
  1366     for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
  1367         GLSurfaceRecord *surf = &ctx->glSurface[i];
  1368 
  1369         if ((surf->type & type) != 0 && surf->targetHandle == handle) {
  1370             m3gDeleteGLSurface(surf->handle);
  1371             
  1372             surf->type = SURFACE_NONE;
  1373             surf->handle = NULL;
  1374         }
  1375     }
  1376 }
  1377     
  1378 /*!
  1379  * \internal
  1380  * \brief Makes an OpenGL context current to the current rendering target
  1381  */
  1382 static void m3gMakeGLCurrent(RenderContext *ctx)
  1383 {
  1384     eglBindAPI(EGL_OPENGL_ES_API);
  1385 
  1386     if (ctx != NULL) {
  1387         EGLContext eglCtx = NULL;
  1388         if (ctx->target.buffered) {
  1389             eglCtx = m3gSelectGLContext(
  1390                 ctx,
  1391                 M3G_RGBA8,
  1392                 (M3Gbitmask) M3G_COLOR_BUFFER_BIT |
  1393                              M3G_DEPTH_BUFFER_BIT |
  1394                              M3G_MULTISAMPLE_BUFFER_BIT,
  1395                 (M3Gbitmask) EGL_PBUFFER_BIT,
  1396                 ctx->backBuffer.glSurface);
  1397             ctx->target.surface = ctx->backBuffer.glSurface;
  1398         }
  1399         else {
  1400             EGLSurface surface = m3gSelectGLSurface(ctx);
  1401             if (surface) {
  1402                 eglCtx = m3gSelectGLContext(ctx,
  1403                                    ctx->target.format,
  1404                                    ctx->bufferBits,
  1405                                    ctx->target.type,
  1406                                    surface);
  1407                 ctx->target.surface = surface;
  1408             }
  1409         }
  1410         /* Synchronize with native rendering in case we're 
  1411            rendering to a native bitmap (or window) target */
  1412         eglWaitNative(EGL_CORE_NATIVE_ENGINE);
  1413 
  1414         /* Update the current acceleration status */
  1415         
  1416         if (eglCtx) {
  1417             EGLint param;
  1418             eglQueryContext(eglGetCurrentDisplay(),
  1419                             eglCtx, EGL_CONFIG_ID,
  1420                             &param);
  1421             eglGetConfigAttrib(eglGetCurrentDisplay(),
  1422                                (EGLConfig) param, EGL_CONFIG_CAVEAT,
  1423                                &param);
  1424             ctx->accelerated = (param == EGL_NONE);
  1425         }
  1426     }
  1427     else {
  1428         eglMakeCurrent(eglGetDisplay(EGL_DEFAULT_DISPLAY), NULL, NULL, NULL);
  1429     }
  1430 }
  1431 
  1432 
  1433 /*----------------------------------------------------------------------
  1434  * Public API
  1435  *--------------------------------------------------------------------*/
  1436 
  1437 /*!
  1438  * \brief
  1439  */
  1440 void m3gBindBitmapTarget(M3GRenderContext hCtx,
  1441                          M3GNativeBitmap hBitmap)
  1442 {
  1443     M3GPixelFormat format;
  1444     M3Gint width, height, pixels;
  1445     RenderContext *ctx = (RenderContext *) hCtx;
  1446     M3G_VALIDATE_OBJECT(ctx);
  1447     
  1448     M3G_LOG1(M3G_LOG_RENDERING, "Binding bitmap 0x%08X\n", (unsigned) hBitmap);
  1449     
  1450     if (!m3gglGetNativeBitmapParams(hBitmap, &format, &width, &height, &pixels)) {
  1451         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OBJECT);
  1452         return;
  1453     }
  1454 
  1455     if (!m3gBindRenderTarget(ctx,
  1456                              SURFACE_BITMAP,
  1457                              width, height,
  1458                              format,
  1459                              hBitmap)) {
  1460         return; /* appropriate error raised automatically */
  1461     }
  1462 
  1463     /* Set the bitmap target specific parameters */
  1464     
  1465     ctx->target.pixels = (void*)pixels;
  1466 
  1467 }
  1468 
  1469 /*!
  1470  * \brief Binds an external EGL surface as a rendering target
  1471  *
  1472  * \param context the M3G rendering context
  1473  * \param surface an EGLSurface cast to M3GEGLSurface
  1474  */
  1475 M3G_API void m3gBindEGLSurfaceTarget(M3GRenderContext context,
  1476                                      M3GEGLSurface surface)
  1477 {
  1478     RenderContext *ctx = (RenderContext*) context;
  1479     Interface *m3g = M3G_INTERFACE(ctx);
  1480     M3G_VALIDATE_OBJECT(ctx);
  1481 
  1482     M3G_LOG1(M3G_LOG_RENDERING, "Binding EGL surface 0x%08X\n", (unsigned) surface);
  1483     {
  1484         EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  1485         EGLSurface surf = (EGLSurface) surface;
  1486         M3Gint width, height;
  1487         
  1488         if (!(eglQuerySurface(dpy, surf, EGL_WIDTH, &width) &&
  1489               eglQuerySurface(dpy, surf, EGL_HEIGHT, &height))) {
  1490             m3gRaiseError(m3g, M3G_INVALID_OBJECT);
  1491             return;
  1492         }
  1493 
  1494         if (!m3gBindRenderTarget(ctx,
  1495                                  SURFACE_EGL,
  1496                                  width, height,
  1497                                  M3G_RGBA8,
  1498                                  surface)) {
  1499             return; /* error raised automatically */
  1500         }
  1501 
  1502         /* placeholder for target type specific setup */
  1503     }
  1504 }
  1505 
  1506 /*!
  1507  * \brief Binds a new memory rendering target to this rendering
  1508  * context
  1509  *
  1510  * Upon first binding of a specific target, binding the buffer may
  1511  * require auxiliary data to be allocated, depending on the rendering
  1512  * modes set for this context. In that case, the binding will be
  1513  * canceled, and the function will return a non-zero value giving the
  1514  * number of bytes of additional memory that needs to be supplied for
  1515  * binding of that target to succeed. The function must then be called
  1516  * again and a pointer to a sufficient memory block supplied as the \c
  1517  * mem parameter.
  1518  *
  1519  * \param pixels NULL to signal that the frame buffer is accessed
  1520  * using a callback upon rendering time
  1521  */
  1522 /*@access M3GGLContext@*/
  1523 void m3gBindMemoryTarget(M3GRenderContext context,
  1524                          /*@shared@*/ void *pixels,
  1525                          M3Guint width, M3Guint height,
  1526                          M3GPixelFormat format,
  1527                          M3Guint stride,
  1528                          M3Guint userHandle)
  1529 {
  1530     RenderContext *ctx = (RenderContext*) context;
  1531     Interface *m3g = M3G_INTERFACE(ctx);
  1532     M3G_VALIDATE_OBJECT(ctx);
  1533 
  1534     M3G_LOG1(M3G_LOG_RENDERING, "Binding memory buffer 0x%08X\n",
  1535              (unsigned) pixels);
  1536     
  1537     /* Check for bitmap specific errors */
  1538     
  1539     if (width == 0 || height == 0 || stride < width) {
  1540         m3gRaiseError(m3g, M3G_INVALID_VALUE);
  1541         return; 
  1542     }
  1543 
  1544     /* Effect the generic target binding */
  1545     
  1546     if (!m3gBindRenderTarget(ctx,
  1547                              SURFACE_MEMORY,
  1548                              width, height,
  1549                              format,
  1550                              userHandle)) {
  1551         return; /* appropriate error raised automatically */
  1552     }
  1553 
  1554     /* Set the memory target specific parameters */
  1555     
  1556     ctx->target.pixels = pixels;
  1557     ctx->target.stride = stride;
  1558 }
  1559 
  1560 /*!
  1561  * \brief
  1562  */
  1563 M3G_API void m3gBindWindowTarget(M3GRenderContext hCtx,
  1564                                  M3GNativeWindow hWindow)
  1565 {
  1566     M3GPixelFormat format;
  1567     M3Gint width, height;
  1568     RenderContext *ctx = (RenderContext *) hCtx;
  1569     M3G_VALIDATE_OBJECT(ctx);
  1570     
  1571     M3G_LOG1(M3G_LOG_RENDERING, "Binding window 0x%08X\n", (unsigned) hWindow);
  1572     
  1573     if (!m3gglGetNativeWindowParams(hWindow, &format, &width, &height)) {
  1574         m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OBJECT);
  1575         return;
  1576     }
  1577 
  1578     if (!m3gBindRenderTarget(ctx,
  1579                              SURFACE_WINDOW,
  1580                              width, height,
  1581                              format,
  1582                              hWindow)) {
  1583         return; /* appropriate error raised automatically */
  1584     }
  1585 
  1586     /* placeholder for window target specific setup */
  1587 }
  1588 
  1589 /*!
  1590  * \brief Invalidate a previously bound bitmap target
  1591  *
  1592  * This should be called prior to deleting a native bitmap that has
  1593  * been used as an M3G rendering target in the past. This erases the
  1594  * object from any internal caches and ensures it will not be accessed
  1595  * in the future.
  1596  *
  1597  * \param hCtx    M3G rendering context
  1598  * \param hBitmap native handle of the bitmap object
  1599  */
  1600 M3G_API void m3gInvalidateBitmapTarget(M3GRenderContext hCtx,
  1601                                        M3GNativeBitmap hBitmap)
  1602 {
  1603     RenderContext *ctx = (RenderContext *) hCtx;
  1604     M3G_VALIDATE_OBJECT(ctx);
  1605 
  1606     M3G_LOG1(M3G_LOG_RENDERING, "Invalidating bitmap 0x%08X\n",
  1607              (unsigned) hBitmap);
  1608     
  1609     m3gDeleteGLSurfaces(ctx, (M3Gbitmask) SURFACE_BITMAP, (M3Guint) hBitmap);
  1610 }
  1611 
  1612 /*!
  1613  * \brief Invalidate a previously bound window target
  1614  *
  1615  * This should be called prior to deleting a native window that has
  1616  * been used as an M3G rendering target in the past. This erases the
  1617  * object from any internal caches and ensures it will not be accessed
  1618  * in the future.
  1619  *
  1620  * \param hCtx    M3G rendering context
  1621  * \param hWindow native handle of the bitmap object
  1622  */
  1623 M3G_API void m3gInvalidateWindowTarget(M3GRenderContext hCtx,
  1624                                        M3GNativeWindow hWindow)
  1625 {
  1626     RenderContext *ctx = (RenderContext *) hCtx;
  1627     M3G_VALIDATE_OBJECT(ctx);
  1628 
  1629     M3G_LOG1(M3G_LOG_RENDERING, "Invalidating window 0x%08X\n",
  1630              (unsigned) hWindow);
  1631     
  1632     m3gDeleteGLSurfaces(ctx, (M3Gbitmask) SURFACE_WINDOW, (M3Guint) hWindow);
  1633 }
  1634 
  1635 /*!
  1636  * \brief Invalidate a previously bound memorytarget
  1637  *
  1638  * This should be called prior to deleting a memory buffer that has
  1639  * been used as an M3G rendering target in the past. 
  1640  *
  1641  * \param hCtx    M3G rendering context
  1642  * \param pixels  pointer to the memory buffer
  1643  */
  1644 M3G_API void m3gInvalidateMemoryTarget(M3GRenderContext hCtx,
  1645                                        void *pixels)
  1646 {
  1647     RenderContext *ctx = (RenderContext *) hCtx;
  1648     M3G_VALIDATE_OBJECT(ctx);
  1649 
  1650     M3G_LOG1(M3G_LOG_RENDERING, "Invalidating memory target 0x%08X\n",
  1651              (unsigned) pixels);
  1652     
  1653     m3gDeleteGLSurfaces(ctx, (M3Gbitmask) SURFACE_MEMORY, (M3Guint) pixels);
  1654 }