First public contribution.
2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
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".
9 * Initial Contributors:
10 * Nokia Corporation - initial contribution.
14 * Description: EGL rendering context management functions
22 * \brief EGL rendering context management functions
25 #if defined(M3G_NGL_CONTEXT_API)
26 # error This file is for the OES API only
30 #include "m3g_image.h"
32 /*----------------------------------------------------------------------
34 *--------------------------------------------------------------------*/
38 * \brief Queries for an EGL configuration matching given M3G format
41 static EGLConfig m3gQueryEGLConfig(M3Genum format,
42 M3Gbitmask bufferBits,
44 M3GNativeBitmap bitmapHandle)
46 struct { int attrib, value; } attribs[10];
49 /* Determine color depth */
51 attribs[0].attrib = EGL_RED_SIZE;
52 attribs[1].attrib = EGL_GREEN_SIZE;
53 attribs[2].attrib = EGL_BLUE_SIZE;
54 attribs[3].attrib = EGL_ALPHA_SIZE;
87 /* Set up the depth buffer */
89 attribs[4].attrib = EGL_DEPTH_SIZE;
90 attribs[4].value = (bufferBits & M3G_DEPTH_BUFFER_BIT) ? 16 : 0;
92 /* Set target surface type mask */
94 attribs[5].attrib = EGL_SURFACE_TYPE;
95 attribs[5].value = surfaceBits;
99 /* This attribute is matched only for pixmap targets */
100 attribs[6].attrib = EGL_MATCH_NATIVE_PIXMAP;
101 attribs[6].value = bitmapHandle;
103 /* Try to get multisampling if requested */
105 attribs[7].attrib = EGL_SAMPLE_BUFFERS;
106 attribs[8].attrib = EGL_SAMPLES;
108 attribs[9].attrib = EGL_NONE;
110 /* Try to get multisampling if requested */
112 attribs[6].attrib = EGL_SAMPLE_BUFFERS;
113 attribs[7].attrib = EGL_SAMPLES;
115 attribs[8].attrib = EGL_NONE;
119 /* Try 4 samples if multisampling enabled, then 2, then 1 */
121 samples = (bufferBits & M3G_MULTISAMPLE_BUFFER_BIT) ? 4 : 1;
122 for ( ; samples > 0; samples >>= 1) {
126 attribs[7].value = 1;
127 attribs[8].value = samples;
130 attribs[7].value = EGL_FALSE;
131 attribs[8].value = 0;
135 attribs[6].value = 1;
136 attribs[7].value = samples;
139 attribs[6].value = EGL_FALSE;
140 attribs[7].value = 0;
144 /* Get the first matching config; according to EGL sorting
145 * rules, this should be an accelerated one if possible */
151 eglChooseConfig(eglGetDisplay(EGL_DEFAULT_DISPLAY),
152 (const int *) attribs,
156 error = eglGetError();
157 if (error != EGL_SUCCESS) {
158 M3G_LOG1(M3G_LOG_FATAL_ERRORS, "eglChooseConfig failed: %d\n", error);
162 M3G_ASSERT(error == EGL_SUCCESS);
164 /* If we got a config, return that; otherwise, drop the
165 * number of multisampling samples and try again, or
166 * return NULL for no config if we already have zero
169 if (numConfigs > 0) {
170 M3G_LOG1(M3G_LOG_RENDERING, "Selected EGL config #%d\n", config);
175 M3G_LOG(M3G_LOG_WARNINGS, "Warning: multisampling not available\n");
180 /* No matching configuration found */
187 * \brief Initializes EGL
189 static void m3gInitializeEGL(void)
191 M3G_LOG(M3G_LOG_INTERFACE, "Initializing EGL\n");
192 eglInitialize(eglGetDisplay(EGL_DEFAULT_DISPLAY), NULL, NULL);
197 * \brief Terminates EGL
199 static void m3gTerminateEGL(void)
201 M3G_LOG(M3G_LOG_INTERFACE, "Shutting down EGL\n");
202 eglTerminate(eglGetDisplay(EGL_DEFAULT_DISPLAY));
207 * \brief Creates a new EGL context
209 /*static EGLContext m3gCreateGLContext(M3Genum format,
210 M3Gbitmask bufferBits,
211 M3Gbitmask reqSurfaceBits,
213 M3Gbitmask *outSurfaceBits)
218 M3G_ASSERT((reqSurfaceBits & ~(EGL_PIXMAP_BIT|EGL_PBUFFER_BIT|EGL_WINDOW_BIT)) == 0);
220 config = m3gQueryEGLConfig(format, bufferBits, reqSurfaceBits, NULL);
222 if (!config || !eglGetConfigAttrib(eglGetDisplay(EGL_DEFAULT_DISPLAY),
225 (EGLint *) outSurfaceBits)) {
229 ctx = eglCreateContext(eglGetDisplay(EGL_DEFAULT_DISPLAY),
234 # if defined(M3G_DEBUG)
236 EGLint err = eglGetError();
237 M3G_ASSERT(err == EGL_SUCCESS || err == EGL_BAD_ALLOC);
241 M3G_LOG1(M3G_LOG_OBJECTS, "New GL context 0x%08X\n", (unsigned) ctx);
247 * \brief Deletes an EGL context
249 static void m3gDeleteGLContext(EGLContext ctx)
251 eglDestroyContext(eglGetDisplay(EGL_DEFAULT_DISPLAY), ctx);
252 # if defined(M3G_DEBUG)
254 EGLint err = eglGetError();
255 if (err != EGL_SUCCESS) {
256 M3G_LOG1(M3G_LOG_FATAL_ERRORS, "EGL error 0x%08X\n", (unsigned) err);
258 M3G_ASSERT(err == EGL_SUCCESS);
261 M3G_LOG1(M3G_LOG_OBJECTS, "Destroyed GL context 0x%08X\n",
268 * \brief Creates a new EGL window surface
270 static EGLSurface m3gCreateWindowSurface(M3Genum format,
271 M3Gbitmask bufferBits,
275 EGLConfig config = m3gQueryEGLConfig(format, bufferBits, EGL_WINDOW_BIT, NULL);
281 surf = eglCreateWindowSurface(eglGetDisplay(EGL_DEFAULT_DISPLAY),
283 (NativeWindowType) wnd,
286 # if defined(M3G_DEBUG)
288 EGLint err = eglGetError();
289 M3G_ASSERT(err == EGL_SUCCESS || err == EGL_BAD_ALLOC);
293 if (surf != EGL_NO_SURFACE) {
294 M3G_LOG1(M3G_LOG_OBJECTS, "New GL window surface 0x%08X\n",
304 * \brief Creates a new EGL pixmap surface
306 static EGLSurface m3gCreateBitmapSurface(M3Genum format,
307 M3Gbitmask bufferBits,
311 EGLConfig config = m3gQueryEGLConfig(format, bufferBits, EGL_PIXMAP_BIT, bmp);
317 surf = eglCreatePixmapSurface(eglGetDisplay(EGL_DEFAULT_DISPLAY),
319 (NativePixmapType) bmp,
322 # if defined(M3G_DEBUG)
324 EGLint err = eglGetError();
325 M3G_ASSERT(err == EGL_SUCCESS || err == EGL_BAD_ALLOC);
329 if (surf != EGL_NO_SURFACE) {
330 M3G_LOG1(M3G_LOG_OBJECTS, "New GL pixmap surface 0x%08X\n",
340 * \brief Creates a new PBuffer
342 static EGLSurface m3gCreatePBufferSurface(M3Genum format,
343 M3Gbitmask bufferBits,
344 M3Gint width, M3Gint height)
350 attrib[0] = EGL_WIDTH;
352 attrib[2] = EGL_HEIGHT;
354 attrib[4] = EGL_NONE;
356 config = m3gQueryEGLConfig(format, bufferBits, EGL_PBUFFER_BIT, NULL);
361 surf = eglCreatePbufferSurface(eglGetDisplay(EGL_DEFAULT_DISPLAY),
364 # if defined(M3G_DEBUG)
366 EGLint err = eglGetError();
367 M3G_ASSERT(err == EGL_SUCCESS || err == EGL_BAD_ALLOC);
371 if (surf != EGL_NO_SURFACE) {
372 M3G_LOG1(M3G_LOG_OBJECTS, "New GL pbuffer surface 0x%08X\n",
382 * \brief Deletes an EGL surface
384 static void m3gDeleteGLSurface(EGLSurface surface)
386 eglDestroySurface(eglGetDisplay(EGL_DEFAULT_DISPLAY), surface);
388 # if defined(M3G_DEBUG)
390 EGLint err = eglGetError();
391 M3G_ASSERT(err == EGL_SUCCESS);
395 M3G_LOG1(M3G_LOG_OBJECTS, "Destroyed GL surface 0x%08X\n",
400 * \brief Swap buffers on a rendering surface
402 static M3Gbool m3gSwapBuffers(EGLSurface surface)
404 EGLBoolean success = eglSwapBuffers(eglGetDisplay(EGL_DEFAULT_DISPLAY),
407 # if defined(M3G_DEBUG)
408 EGLint err = eglGetError();
409 M3G_ASSERT(err == EGL_SUCCESS);
412 return (M3Gbool) success;
416 * \brief Does a sub-blit of a frame buffer blit operation
418 static void m3gBlitFrameBufferPixels2(RenderContext *ctx,
419 M3Gint xOffset, M3Gint yOffset,
420 M3Gint width, M3Gint height,
421 M3GPixelFormat internalFormat,
423 const M3Gubyte *pixels)
425 # define MAX_TEMP_TEXTURES 8
427 static const int MAX_TILE_SIZE = 256; /* -> 256 KB temp buffer(s) */
428 static const M3Gbyte tc[8] = { 0, 0, 0, 1, 1, 0, 1, 1 };
430 int tileWidth = MAX_TILE_SIZE, tileHeight = MAX_TILE_SIZE;
431 M3Gbool mustConvert = M3G_FALSE;
432 M3Gubyte *tempPixels = 0; /* initialize to avoid compiler warnings */
433 GLuint tempTexObj[MAX_TEMP_TEXTURES];
436 M3G_VALIDATE_OBJECT(ctx);
439 /* Analyze source and destination formats for possible conversion */
441 glFormat = m3gGetGLFormat(internalFormat);
443 M3G_ASSERT(M3G_FALSE); /* internal format not supported in GL */
446 if (internalFormat == M3G_RGB8_32) {
449 if (internalFormat == M3G_BGR8_32 || internalFormat == M3G_ARGB8) {
451 mustConvert = M3G_TRUE;
454 /* Tweak tile size to avoid using excessive amounts of memory for
455 * portions outside the blit area */
457 M3G_ASSERT((width > 0) && (height > 0));
459 while (tileWidth >= width * 2) {
463 while (tileHeight >= height * 2) {
467 /* Allocate temp memory for conversion or adjust tile size for
468 * optimal direct download to GL */
471 tempPixels = m3gAllocTemp(M3G_INTERFACE(ctx),
472 tileWidth * tileHeight * 4);
474 return; /* out of memory */
479 /* Attempt to adjust the tile size so that we can copy
480 * complete scanlines at a time -- this is because OpenGL ES
481 * is missing PixelStore settings that could be used for
482 * stride control during image uploading */
485 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxWidth);
487 while (tileWidth < width &&
488 tileWidth < maxWidth &&
495 /* Load default images into the temp texture objects */
497 glActiveTexture(GL_TEXTURE0);
498 glEnable(GL_TEXTURE_2D);
501 tempTexCount = ((width + tileWidth - 1) / tileWidth)
502 * ((height + tileHeight - 1) / tileHeight);
503 tempTexCount = m3gMinInt(tempTexCount, MAX_TEMP_TEXTURES);
505 glGenTextures(tempTexCount, tempTexObj);
507 for (ti = 0; ti < tempTexCount; ++ti) {
508 glBindTexture(GL_TEXTURE_2D, tempTexObj[ti]);
509 glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
510 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
511 glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
514 glTexImage2D(GL_TEXTURE_2D, 0,
516 tileWidth, tileHeight,
519 GL_UNSIGNED_BYTE, NULL);
521 /* Raise out-of-memory if OpenGL ran out of resources */
523 GLint err = glGetError();
525 if (err == GL_OUT_OF_MEMORY) {
526 m3gRaiseError(M3G_INTERFACE(ctx), M3G_OUT_OF_MEMORY);
529 else if (err != GL_NO_ERROR) {
530 M3G_ASSERT(M3G_FALSE);
536 /* Set up texture and vertex coordinate arrays for the image tiles */
538 glClientActiveTexture(GL_TEXTURE0);
539 glTexCoordPointer(2, GL_BYTE, 0, tc);
540 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
541 glVertexPointer(2, GL_SHORT, 0, pos);
542 glEnableClientState(GL_VERTEX_ARRAY);
543 glMatrixMode(GL_TEXTURE);
545 glMatrixMode(GL_MODELVIEW);
548 /* Load each image tile into a texture and draw */
551 M3Gint nextTexTile = 0;
553 bpp = m3gBytesPerPixel(internalFormat);
555 stride = bpp * width;
558 for (y = 0; y < height; y += tileHeight) {
559 for (x = 0; x < width; x += tileWidth) {
562 w = M3G_MIN(tileWidth, width - x);
563 h = M3G_MIN(tileHeight, height - y);
565 glBindTexture(GL_TEXTURE_2D, tempTexObj[nextTexTile]);
566 nextTexTile = (nextTexTile + 1) % MAX_TEMP_TEXTURES;
569 m3gConvertPixelRect(internalFormat,
570 pixels + y * stride + x * bpp,
573 M3G_RGBA8, tempPixels, w * 4);
574 glTexSubImage2D(GL_TEXTURE_2D, 0,
577 GL_RGBA, GL_UNSIGNED_BYTE, tempPixels);
580 if (w*bpp == stride) {
581 glTexSubImage2D(GL_TEXTURE_2D, 0,
586 pixels + y * stride + x * bpp);
590 for (k = 0; k < h; ++k) {
591 glTexSubImage2D(GL_TEXTURE_2D, 0,
596 pixels + (y+k) * stride + x * bpp);
601 pos[0] = (GLshort)(x + xOffset);
602 pos[1] = (GLshort)((height - y) + yOffset);
604 pos[3] = (GLshort)((height - (y + tileHeight)) + yOffset);
605 pos[4] = (GLshort)((x + tileWidth) + xOffset);
609 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
615 /* Restore required OpenGL state and release resources */
619 m3gFreeTemp(M3G_INTERFACE(ctx));
622 glDeleteTextures(tempTexCount, tempTexObj);
626 # undef MAX_TEMP_TEXTURES
629 /*----------------------------------------------------------------------
631 *--------------------------------------------------------------------*/
633 /* The frame buffer should be the first thing locked and the last one
634 * released, so let's mandate that even though it has no real effect
636 #define m3gLockFrameBuffer(ctx) M3G_ASSERT_NO_LOCK(M3G_INTERFACE(ctx))
637 #define m3gReleaseFrameBuffer(ctx) M3G_ASSERT_NO_LOCK(M3G_INTERFACE(ctx))
641 * \brief Alternate rendering function for two-sided lighting on buggy
644 static M3Gbool m3gSplitDrawMesh(RenderContext *ctx,
645 const VertexBuffer *vb,
646 const IndexBuffer *ib,
647 const Appearance *app,
648 const M3GMatrix *modelTransform,
652 if (!ctx->inSplitDraw && m3gGetMaterial((M3GAppearance) app) && vb->normals) {
653 PolygonMode *pm = m3gGetPolygonMode((M3GAppearance) app);
654 if (pm && pm->enableTwoSidedLighting) {
655 M3Gint originalCulling = m3gGetCulling(pm);
656 if (originalCulling != M3G_CULL_BACK) {
658 /* OK, we must render the back sides separately with
661 Interface *m3g = M3G_INTERFACE(ctx);
662 VertexArray *tempNormals;
664 M3Gint normalCount = vb->vertexCount;
665 M3Gint normalStride = vb->normals->stride;
667 /* Duplicate the normal array */
669 m3gReleaseFrameBuffer(ctx);
670 tempNormals = m3gCloneVertexArray(vb->normals);
672 m3gLockFrameBuffer(ctx);
673 return M3G_TRUE; /* automatic out-of-memory */
676 /* Flip the signs of the temp normals */
678 if (tempNormals->elementType == GL_BYTE) {
679 M3Gbyte *p = (M3Gbyte*) m3gMapObject(m3g, tempNormals->data);
681 for (i = 0; i < normalCount; ++i) {
682 p[0] = (M3Gbyte) -m3gClampInt(p[0], -127, 127);
683 p[1] = (M3Gbyte) -m3gClampInt(p[1], -127, 127);
684 p[2] = (M3Gbyte) -m3gClampInt(p[2], -127, 127);
689 M3Gshort *p = (M3Gshort*) m3gMapObject(m3g, tempNormals->data);
691 for (i = 0; i < normalCount; ++i) {
692 p[0] = (M3Gshort) -m3gClampInt(p[0], -32767, 32767);
693 p[1] = (M3Gshort) -m3gClampInt(p[1], -32767, 32767);
694 p[2] = (M3Gshort) -m3gClampInt(p[2], -32767, 32767);
695 p += normalStride / 2;
698 m3gUnmapObject(m3g, tempNormals->data);
699 m3gLockFrameBuffer(ctx);
701 ctx->inSplitDraw = M3G_TRUE;
703 /* Set culling to front faces only and render with the
706 VertexArray *orgNormals = vb->normals;
707 ((VertexBuffer*)vb)->normals = tempNormals;
708 m3gSetCulling(pm, M3G_CULL_FRONT);
713 ((VertexBuffer*)vb)->normals = orgNormals;
716 /* If no culling was enabled, render the front faces
717 * with the original normals */
719 if (originalCulling == M3G_CULL_NONE) {
720 m3gSetCulling(pm, M3G_CULL_BACK);
727 /* Restore original culling and free the temp normals */
729 m3gSetCulling(pm, originalCulling);
731 m3gReleaseFrameBuffer(ctx);
732 m3gDeleteObject((M3GObject) tempNormals);
733 m3gLockFrameBuffer(ctx);
735 ctx->inSplitDraw = M3G_FALSE;
745 * \brief Determines whether a format/mode combination can be directly
748 static M3Gbool m3gCanDirectRender(const RenderContext *ctx)
750 M3GPixelFormat format = ctx->target.format;
751 M3Gbitmask bufferBits = ctx->bufferBits;
752 M3Gbitmask surfaceType = ctx->target.type;
753 M3GNativeBitmap bitmapHandle = ctx->target.handle;
756 /* Images always go via pbuffers; EGL surfaces can always be
759 if (surfaceType == SURFACE_IMAGE) {
762 if (surfaceType == SURFACE_EGL || surfaceType == SURFACE_WINDOW) {
766 /* First scan the context cache for a matching previously used
767 * context; this should be faster than querying EGL */
769 for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) {
770 const GLContextRecord *rc = &ctx->glContext[i];
772 if ((rc->surfaceTypeBits & surfaceType) == surfaceType
773 && rc->format == format
774 && (rc->bufferBits & bufferBits) == bufferBits) {
780 /* No dice; must resort to querying from EGL */
782 return (m3gQueryEGLConfig(format, bufferBits, (EGLint) surfaceType, bitmapHandle) != NULL);
787 * \brief Ensures that a sufficient back buffer exists
789 * Creates a new PBuffer for the back buffer if required.
791 static M3Gbool m3gValidateBackBuffer(RenderContext *ctx)
793 BackBuffer *bbuf = &ctx->backBuffer;
794 int w = ctx->target.width;
795 int h = ctx->target.height;
797 /* NOTE the EGL specification is fuzzy on eglCopyBuffers when the
798 * pbuffer is larger than the target, so we require an exact match
799 * (can be relaxed by #undefining the flag, see m3g_defs.h) */
801 # if defined(M3G_GL_FORCE_PBUFFER_SIZE)
802 if (bbuf->width != w || bbuf->height != h) {
804 if (bbuf->width < w || bbuf->height < h) {
807 M3G_LOG(M3G_LOG_WARNINGS,
808 "Warning (performance): Buffered rendering.\n");
810 if (bbuf->glSurface != NULL) {
811 m3gDeleteGLSurface(bbuf->glSurface);
814 bbuf->glSurface = m3gCreatePBufferSurface(
816 (M3Gbitmask)(M3G_COLOR_BUFFER_BIT|M3G_DEPTH_BUFFER_BIT|M3G_MULTISAMPLE_BUFFER_BIT),
822 if (!bbuf->glSurface) {
823 if (eglGetError() == EGL_BAD_ALLOC) {
824 return M3G_FALSE; /* ouf of memory */
827 M3G_ASSERT(M3G_FALSE);
836 * \brief Increment the rendering time stamp
838 * The time stamp is used to manage the various caches for the
839 * context, so it needs to be updated often enough for the caches to
840 * function optimally.
842 * In the rare case that the time stamp should wrap around(!), we
843 * reset the time stamps dependent on it to avoid sub-optimal cache
846 static void m3gIncrementRenderTimeStamp(RenderContext *ctx)
848 if (++ctx->cacheTimeStamp == 0) {
850 for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) {
851 ctx->glContext[i].lastUseTime = 0;
853 for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
854 ctx->glSurface[i].lastUseTime = 0;
861 * \brief Draws a raw RGB or RGBA pixel rectangle of arbitrary size
862 * into the frame buffer
864 * The offset only affects the position of the blitted rectangle in
865 * the frame buffer. The source data is read starting at the given
868 * \param ctx the rendering context
869 * \param xOffset offset from the left edge of the frame buffer
870 * \param yOffset offset from the bottom of the frame buffer
871 * \param width width of the rectangle
872 * \param height height of the rectangle
873 * \param internalFormat format of the source pixels
874 * \param stride stride of the source data
875 * \param pixels pointer to the source pixels in top-to-bottom order
877 static void m3gBlitFrameBufferPixels(RenderContext *ctx,
878 M3Gint xOffset, M3Gint yOffset,
879 M3Gint width, M3Gint height,
880 M3GPixelFormat internalFormat,
882 const M3Gubyte *pixels)
884 /* Skip this if nothing to copy */
886 if (width <= 0 || height <= 0) {
890 /* Set viewport, projection and modelview to map coordinates to
891 * pixel boundaries */
893 glScissor(xOffset, yOffset, width, height);
894 glViewport(0, 0, ctx->target.width, ctx->target.height);
895 glMatrixMode(GL_PROJECTION);
897 glOrthox(0, ctx->target.width << 16,
898 0, ctx->target.height << 16,
900 glMatrixMode(GL_MODELVIEW);
903 /* Disable any stray state we don't want */
905 glDisable(GL_CULL_FACE);
907 glDisable(GL_ALPHA_TEST);
908 glDisableClientState(GL_NORMAL_ARRAY);
909 glDisableClientState(GL_COLOR_ARRAY);
910 glDisable(GL_LIGHTING);
911 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
912 glDepthMask(GL_FALSE);
913 glDepthFunc(GL_ALWAYS);
914 m3gDisableTextures();
917 /* Split the large blit operation into smaller chunks that are
918 * efficiently taken care of using power-of-two textures */
920 const int MAX_BLIT_SIZE = 256; /* should be power of two */
922 int xBlits = (width / MAX_BLIT_SIZE) + 1;
923 int yBlits = (height / MAX_BLIT_SIZE) + 1;
927 for (yBlit = yBlits-1; yBlit >= 0; yBlit--) {
928 for (xBlit = 0; xBlit < xBlits; ++xBlit) {
930 M3Gint xStart = xOffset + xBlit * MAX_BLIT_SIZE;
931 M3Gint yStart = yOffset + yBlit * MAX_BLIT_SIZE;
932 M3Gint xSize = m3gMinInt(MAX_BLIT_SIZE, width - (xStart - xOffset));
933 M3Gint ySize = m3gMinInt(MAX_BLIT_SIZE, height - (yStart - yOffset));
935 M3Gint srcOffset = (height - (yStart - yOffset + ySize)) * stride + (xStart - xOffset) * m3gBytesPerPixel(ctx->target.format);
937 m3gBlitFrameBufferPixels2(ctx,
950 * \brief Synchronizes the contents of the back buffer with the target
953 static void m3gUpdateBackBuffer(RenderContext *ctx)
955 if (ctx->target.type == SURFACE_IMAGE) {
956 m3gDrawFrameBufferImage(ctx, (Image *) ctx->target.handle);
958 else if (ctx->target.type == SURFACE_BITMAP || ctx->target.type == SURFACE_MEMORY) {
963 M3Gint clipWidth = ctx->clip.x1 - ctx->clip.x0;
964 M3Gint clipHeight = ctx->clip.y1 - ctx->clip.y0;
967 if (ctx->target.type == SURFACE_BITMAP) {
968 /* Obtain a pointer to the native bitmap and copy the data to
969 * the backbuffer from there */
970 if (!m3gglLockNativeBitmap((M3GNativeBitmap) ctx->target.handle,
972 /* No dice! There's no way that we know of to copy the
973 * data between the buffers */
974 M3G_ASSERT(M3G_FALSE);
978 src = ctx->target.pixels;
979 stride = ctx->target.stride;
983 (ctx->target.height - ctx->clip.y1) * stride
984 + ctx->clip.x0 * m3gBytesPerPixel(ctx->target.format);
986 m3gBlitFrameBufferPixels(
988 ctx->clip.x0, ctx->clip.y0,
989 clipWidth, clipHeight,
994 if (ctx->target.type == SURFACE_BITMAP) {
995 m3gglReleaseNativeBitmap((M3GNativeBitmap) ctx->target.handle);
999 /* Buffered rendering is not supported for window and pbuffer
1001 M3G_ASSERT(M3G_FALSE);
1003 ctx->backBuffer.contentsValid = M3G_TRUE;
1008 * \brief Synchronizes the contents of the target buffer with the back
1011 static void m3gUpdateTargetBuffer(RenderContext *ctx)
1013 if (ctx->target.type == SURFACE_IMAGE) {
1014 m3gCopyFrameBufferImage((Image *) ctx->target.handle);
1016 else if (ctx->target.type == SURFACE_BITMAP || ctx->target.type == SURFACE_MEMORY) {
1018 M3GPixelFormat format = ctx->target.format;
1019 M3Gint width = ctx->clip.x1 - ctx->clip.x0;
1020 M3Gint height = ctx->clip.y1 - ctx->clip.y0;
1021 M3Gint xOffset = ctx->clip.x0;
1022 M3Gint yOffset = ctx->clip.y0;
1029 if (ctx->target.type == SURFACE_BITMAP) {
1030 /* We must copy the back buffer to a native bitmap: first
1031 * attempt a fast buffer-to-buffer copy using EGL, but if that
1032 * fails, obtain a pointer and do the copy ourselves */
1034 /* We can only do the fast copy for the full buffer */
1036 M3Gbool fullClip = (ctx->clip.x0 == 0)
1037 && (ctx->clip.y0 <= ctx->target.height - ctx->display.height)
1038 && (ctx->clip.x1 >= ctx->display.width)
1039 && (ctx->clip.y1 >= ctx->clip.y0 + ctx->display.height);
1041 if (fullClip && eglCopyBuffers(eglGetDisplay(EGL_DEFAULT_DISPLAY),
1042 ctx->backBuffer.glSurface,
1043 (NativePixmapType) ctx->target.handle))
1048 /* Fast copy failed, try the generic approach */
1049 if (!m3gglLockNativeBitmap((M3GNativeBitmap) ctx->target.handle,
1051 /* No dice! There's no way that we know of to copy the
1052 * data between the buffers */
1053 M3G_ASSERT(M3G_FALSE);
1057 dst = ctx->target.pixels;
1058 stride = ctx->target.stride;
1061 /* OK, got the pointer; now, copy a few scanlines at a
1062 * time, and we can pretty much assume conversion since
1063 * the fast method didn't work */
1065 #define READPIXELS_BUFFER_SIZE 16384
1067 if (width > 0 && height > 0) {
1069 M3Gint bufSize = (width * 4 > READPIXELS_BUFFER_SIZE ? width * 4 : READPIXELS_BUFFER_SIZE);
1070 M3Gint numLinesInBuffer = bufSize/(width * 4);
1071 M3Gint numLinesRead, line;
1072 temp = m3gAllocTemp(M3G_INTERFACE(ctx), bufSize);
1075 return; /* out of memory */
1078 dst += (ctx->target.height - (yOffset + height)) * stride
1079 + xOffset * m3gBytesPerPixel(format);
1081 for (row = 0; row < height; row += numLinesRead) {
1082 line = numLinesRead = (row + numLinesInBuffer > height) ? (height - row) : numLinesInBuffer;
1084 glReadPixels(xOffset,
1085 yOffset + height - row - numLinesRead,
1086 width, numLinesRead,
1087 GL_RGBA, GL_UNSIGNED_BYTE,
1090 while (line-- > 0) {
1091 m3gConvertPixels(M3G_RGBA8, &temp[4*line*width], format, dst, width);
1095 m3gFreeTemp(M3G_INTERFACE(ctx));
1098 if (ctx->target.type == SURFACE_BITMAP) {
1099 m3gglReleaseNativeBitmap((M3GNativeBitmap) ctx->target.handle);
1102 #undef READPIXELS_BUFFER_SIZE
1106 /* Buffered rendering is not supported for window and pbuffer
1108 M3G_ASSERT(M3G_FALSE);
1114 * \brief Selects a GL context matching a given GL surface and a set
1115 * of rendering parameters
1117 * If no existing context matches, a new one is created. Contexts are
1118 * stored in a fixed-size cache and managed using a LRU policy.
1120 static EGLContext m3gSelectGLContext(RenderContext *ctx,
1121 M3GPixelFormat format,
1122 M3Gbitmask bufferBits,
1123 M3Gbitmask surfaceTypeBits,
1128 /* Look for a matching cached context and attempt to make it
1129 * current; on success, update the time in the context record and
1130 * return the GL context handle */
1132 for (i = 0; i < M3G_MAX_GL_CONTEXTS; ++i) {
1133 GLContextRecord *rc = &ctx->glContext[i];
1135 if ((rc->surfaceTypeBits & surfaceTypeBits) == surfaceTypeBits
1136 && rc->format == format
1137 && (rc->bufferBits & bufferBits) == bufferBits) {
1139 if (eglMakeCurrent(eglGetDisplay(EGL_DEFAULT_DISPLAY),
1140 surface, surface, rc->handle)) {
1141 rc->lastUseTime = ctx->cacheTimeStamp;
1145 /* NOTE we intentionally clear the error flag, since
1146 * the MakeCurrent call above can fail in case of a
1147 * context mismatch */
1153 /* No match found, we must create a new context */
1155 GLContextRecord *lru = &ctx->glContext[0];
1156 EGLContext shareRc = lru->handle;
1159 /* Find the least recently used context entry */
1161 for (i = 1; i < M3G_MAX_GL_CONTEXTS; ++i) {
1162 GLContextRecord *rc = &ctx->glContext[i];
1164 shareRc = rc->handle; /* keep this for sharing */
1166 if (!rc->handle || rc->lastUseTime < lru->lastUseTime) {
1171 /* Create a new GL context, then delete the LRU one. This is
1172 * done in this order so that we don't lose any shared texture
1173 * objects when deleting a context. */
1175 //if (surfaceTypeBits == SURFACE_EGL)
1177 EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
1179 eglQuerySurface(dpy,
1180 surface,//(EGLSurface) ctx->target.handle,
1183 glrc = eglCreateContext(dpy, (EGLConfig) configID, shareRc, NULL);
1187 glrc = m3gCreateGLContext(format,
1191 &lru->surfaceTypeBits);
1195 m3gRaiseError(M3G_INTERFACE(ctx), M3G_OUT_OF_MEMORY);
1199 m3gDeleteGLContext(lru->handle);
1202 /* Store the parameters for the new context and make it
1206 lru->surfaceTypeBits = surfaceTypeBits;
1207 lru->format = format;
1208 lru->bufferBits = bufferBits;
1209 lru->modeBits = ctx->modeBits;
1211 M3Gbool ok = eglMakeCurrent(eglGetDisplay(EGL_DEFAULT_DISPLAY),
1212 surface, surface, glrc);
1218 lru->lastUseTime = ctx->cacheTimeStamp;
1226 * \brief Selects a GL surface suitable for rendering into the current
1227 * target using the currently set rendering parameters
1229 * If no existing surface matches, a new one is created. Surfaces are
1230 * stored in a fixed-size LRU cache.
1232 static EGLSurface m3gSelectGLSurface(RenderContext *ctx)
1237 /* Quick exit for EGL surfaces */
1239 if (ctx->target.type == SURFACE_EGL) {
1240 return (EGLSurface) ctx->target.handle;
1243 /* Buffered rendering is handled elsewhere! */
1245 if (ctx->target.buffered) {
1246 M3G_ASSERT(M3G_FALSE);
1250 /* Find the first matching surface and return it */
1252 for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
1253 GLSurfaceRecord *surf = &ctx->glSurface[i];
1255 if ((surf->type == ctx->target.type)
1256 && (surf->targetHandle == ctx->target.handle)
1257 && ((ctx->bufferBits & surf->bufferBits) == ctx->bufferBits)
1258 && (surf->width == ctx->target.width)
1259 && (surf->height == ctx->target.height)
1260 && (surf->format == ctx->target.format)
1261 && (surf->pixels == ctx->target.pixels)) {
1263 surf->lastUseTime = ctx->cacheTimeStamp;
1264 return surf->handle;
1268 /* No matching surface found; must create a new one. If the cache
1269 * is fully occupied, or if we run out of memory, one of the
1270 * existing surfaces is swapped out */
1272 while (attempts <= 1) {
1274 GLSurfaceRecord *lru = &ctx->glSurface[0];
1276 /* Find the first entry without a GL surface handle, or the
1277 * least recently used one if all are occupied. */
1279 for (i = 1; lru->handle != NULL && i < M3G_MAX_GL_SURFACES; ++i) {
1280 GLSurfaceRecord *surf = &ctx->glSurface[i];
1281 if (!surf->handle || surf->lastUseTime < lru->lastUseTime) {
1286 /* Delete the existing surface if we hit an occupied slot */
1289 m3gDeleteGLSurface(lru->handle);
1292 /* Create a new surface depending on the type of the current
1293 * rendering target */
1295 switch (ctx->target.type) {
1296 case SURFACE_BITMAP:
1298 m3gCreateBitmapSurface(ctx->target.format,
1300 (M3GNativeBitmap) ctx->target.handle);
1302 case SURFACE_WINDOW:
1304 m3gCreateWindowSurface(ctx->target.format,
1306 (M3GNativeWindow) ctx->target.handle);
1309 M3G_ASSERT(M3G_FALSE);
1313 /* Success, return the new surface */
1316 lru->type = ctx->target.type;
1317 lru->targetHandle = ctx->target.handle;
1318 lru->bufferBits = ctx->bufferBits;
1319 lru->width = ctx->target.width;
1320 lru->height = ctx->target.height;
1321 lru->format = ctx->target.format;
1322 lru->pixels = ctx->target.pixels;
1323 lru->lastUseTime = ctx->cacheTimeStamp;
1327 /* No surface created, likely due to running out of memory;
1328 * delete all existing surfaces and try again */
1331 for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
1332 GLSurfaceRecord *surf = &ctx->glSurface[i];
1334 m3gDeleteGLSurface(surf->handle);
1335 surf->handle = NULL;
1336 surf->type = SURFACE_NONE;
1344 /* Couldn't create a new surface; must return with an error */
1346 m3gRaiseError(M3G_INTERFACE(ctx), M3G_OUT_OF_MEMORY);
1353 * \brief Deletes all native surfaces for a specific target
1355 * \param ctx rendering context
1356 * \param type bitmask of the types of surfaces to remove
1357 * \param handle native target handle of the surfaces to remove
1359 static void m3gDeleteGLSurfaces(RenderContext *ctx,
1364 M3G_VALIDATE_OBJECT(ctx);
1366 for (i = 0; i < M3G_MAX_GL_SURFACES; ++i) {
1367 GLSurfaceRecord *surf = &ctx->glSurface[i];
1369 if ((surf->type & type) != 0 && surf->targetHandle == handle) {
1370 m3gDeleteGLSurface(surf->handle);
1372 surf->type = SURFACE_NONE;
1373 surf->handle = NULL;
1380 * \brief Makes an OpenGL context current to the current rendering target
1382 static void m3gMakeGLCurrent(RenderContext *ctx)
1384 eglBindAPI(EGL_OPENGL_ES_API);
1387 EGLContext eglCtx = NULL;
1388 if (ctx->target.buffered) {
1389 eglCtx = m3gSelectGLContext(
1392 (M3Gbitmask) M3G_COLOR_BUFFER_BIT |
1393 M3G_DEPTH_BUFFER_BIT |
1394 M3G_MULTISAMPLE_BUFFER_BIT,
1395 (M3Gbitmask) EGL_PBUFFER_BIT,
1396 ctx->backBuffer.glSurface);
1397 ctx->target.surface = ctx->backBuffer.glSurface;
1400 EGLSurface surface = m3gSelectGLSurface(ctx);
1402 eglCtx = m3gSelectGLContext(ctx,
1407 ctx->target.surface = surface;
1410 /* Synchronize with native rendering in case we're
1411 rendering to a native bitmap (or window) target */
1412 eglWaitNative(EGL_CORE_NATIVE_ENGINE);
1414 /* Update the current acceleration status */
1418 eglQueryContext(eglGetCurrentDisplay(),
1419 eglCtx, EGL_CONFIG_ID,
1421 eglGetConfigAttrib(eglGetCurrentDisplay(),
1422 (EGLConfig) param, EGL_CONFIG_CAVEAT,
1424 ctx->accelerated = (param == EGL_NONE);
1428 eglMakeCurrent(eglGetDisplay(EGL_DEFAULT_DISPLAY), NULL, NULL, NULL);
1433 /*----------------------------------------------------------------------
1435 *--------------------------------------------------------------------*/
1440 void m3gBindBitmapTarget(M3GRenderContext hCtx,
1441 M3GNativeBitmap hBitmap)
1443 M3GPixelFormat format;
1444 M3Gint width, height, pixels;
1445 RenderContext *ctx = (RenderContext *) hCtx;
1446 M3G_VALIDATE_OBJECT(ctx);
1448 M3G_LOG1(M3G_LOG_RENDERING, "Binding bitmap 0x%08X\n", (unsigned) hBitmap);
1450 if (!m3gglGetNativeBitmapParams(hBitmap, &format, &width, &height, &pixels)) {
1451 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OBJECT);
1455 if (!m3gBindRenderTarget(ctx,
1460 return; /* appropriate error raised automatically */
1463 /* Set the bitmap target specific parameters */
1465 ctx->target.pixels = (void*)pixels;
1470 * \brief Binds an external EGL surface as a rendering target
1472 * \param context the M3G rendering context
1473 * \param surface an EGLSurface cast to M3GEGLSurface
1475 M3G_API void m3gBindEGLSurfaceTarget(M3GRenderContext context,
1476 M3GEGLSurface surface)
1478 RenderContext *ctx = (RenderContext*) context;
1479 Interface *m3g = M3G_INTERFACE(ctx);
1480 M3G_VALIDATE_OBJECT(ctx);
1482 M3G_LOG1(M3G_LOG_RENDERING, "Binding EGL surface 0x%08X\n", (unsigned) surface);
1484 EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
1485 EGLSurface surf = (EGLSurface) surface;
1486 M3Gint width, height;
1488 if (!(eglQuerySurface(dpy, surf, EGL_WIDTH, &width) &&
1489 eglQuerySurface(dpy, surf, EGL_HEIGHT, &height))) {
1490 m3gRaiseError(m3g, M3G_INVALID_OBJECT);
1494 if (!m3gBindRenderTarget(ctx,
1499 return; /* error raised automatically */
1502 /* placeholder for target type specific setup */
1507 * \brief Binds a new memory rendering target to this rendering
1510 * Upon first binding of a specific target, binding the buffer may
1511 * require auxiliary data to be allocated, depending on the rendering
1512 * modes set for this context. In that case, the binding will be
1513 * canceled, and the function will return a non-zero value giving the
1514 * number of bytes of additional memory that needs to be supplied for
1515 * binding of that target to succeed. The function must then be called
1516 * again and a pointer to a sufficient memory block supplied as the \c
1519 * \param pixels NULL to signal that the frame buffer is accessed
1520 * using a callback upon rendering time
1522 /*@access M3GGLContext@*/
1523 void m3gBindMemoryTarget(M3GRenderContext context,
1524 /*@shared@*/ void *pixels,
1525 M3Guint width, M3Guint height,
1526 M3GPixelFormat format,
1530 RenderContext *ctx = (RenderContext*) context;
1531 Interface *m3g = M3G_INTERFACE(ctx);
1532 M3G_VALIDATE_OBJECT(ctx);
1534 M3G_LOG1(M3G_LOG_RENDERING, "Binding memory buffer 0x%08X\n",
1537 /* Check for bitmap specific errors */
1539 if (width == 0 || height == 0 || stride < width) {
1540 m3gRaiseError(m3g, M3G_INVALID_VALUE);
1544 /* Effect the generic target binding */
1546 if (!m3gBindRenderTarget(ctx,
1551 return; /* appropriate error raised automatically */
1554 /* Set the memory target specific parameters */
1556 ctx->target.pixels = pixels;
1557 ctx->target.stride = stride;
1563 M3G_API void m3gBindWindowTarget(M3GRenderContext hCtx,
1564 M3GNativeWindow hWindow)
1566 M3GPixelFormat format;
1567 M3Gint width, height;
1568 RenderContext *ctx = (RenderContext *) hCtx;
1569 M3G_VALIDATE_OBJECT(ctx);
1571 M3G_LOG1(M3G_LOG_RENDERING, "Binding window 0x%08X\n", (unsigned) hWindow);
1573 if (!m3gglGetNativeWindowParams(hWindow, &format, &width, &height)) {
1574 m3gRaiseError(M3G_INTERFACE(ctx), M3G_INVALID_OBJECT);
1578 if (!m3gBindRenderTarget(ctx,
1583 return; /* appropriate error raised automatically */
1586 /* placeholder for window target specific setup */
1590 * \brief Invalidate a previously bound bitmap target
1592 * This should be called prior to deleting a native bitmap that has
1593 * been used as an M3G rendering target in the past. This erases the
1594 * object from any internal caches and ensures it will not be accessed
1597 * \param hCtx M3G rendering context
1598 * \param hBitmap native handle of the bitmap object
1600 M3G_API void m3gInvalidateBitmapTarget(M3GRenderContext hCtx,
1601 M3GNativeBitmap hBitmap)
1603 RenderContext *ctx = (RenderContext *) hCtx;
1604 M3G_VALIDATE_OBJECT(ctx);
1606 M3G_LOG1(M3G_LOG_RENDERING, "Invalidating bitmap 0x%08X\n",
1607 (unsigned) hBitmap);
1609 m3gDeleteGLSurfaces(ctx, (M3Gbitmask) SURFACE_BITMAP, (M3Guint) hBitmap);
1613 * \brief Invalidate a previously bound window target
1615 * This should be called prior to deleting a native window that has
1616 * been used as an M3G rendering target in the past. This erases the
1617 * object from any internal caches and ensures it will not be accessed
1620 * \param hCtx M3G rendering context
1621 * \param hWindow native handle of the bitmap object
1623 M3G_API void m3gInvalidateWindowTarget(M3GRenderContext hCtx,
1624 M3GNativeWindow hWindow)
1626 RenderContext *ctx = (RenderContext *) hCtx;
1627 M3G_VALIDATE_OBJECT(ctx);
1629 M3G_LOG1(M3G_LOG_RENDERING, "Invalidating window 0x%08X\n",
1630 (unsigned) hWindow);
1632 m3gDeleteGLSurfaces(ctx, (M3Gbitmask) SURFACE_WINDOW, (M3Guint) hWindow);
1636 * \brief Invalidate a previously bound memorytarget
1638 * This should be called prior to deleting a memory buffer that has
1639 * been used as an M3G rendering target in the past.
1641 * \param hCtx M3G rendering context
1642 * \param pixels pointer to the memory buffer
1644 M3G_API void m3gInvalidateMemoryTarget(M3GRenderContext hCtx,
1647 RenderContext *ctx = (RenderContext *) hCtx;
1648 M3G_VALIDATE_OBJECT(ctx);
1650 M3G_LOG1(M3G_LOG_RENDERING, "Invalidating memory target 0x%08X\n",
1653 m3gDeleteGLSurfaces(ctx, (M3Gbitmask) SURFACE_MEMORY, (M3Guint) pixels);