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