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