/* * Mesa 3-D graphics library * * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /* * Off-Screen Mesa rendering / Rendering into client memory space * * Note on thread safety: this driver is thread safe. All * functions are reentrant. The notion of current context is * managed by the core _mesa_make_current() and _mesa_get_current_context() * functions. Those functions are thread-safe. */ #include <stdio.h> #include "main/glheader.h" #include "GL/osmesa.h" #include "main/api_exec.h" #include "main/context.h" #include "main/extensions.h" #include "main/formats.h" #include "main/framebuffer.h" #include "main/imports.h" #include "main/macros.h" #include "main/mipmap.h" #include "main/mtypes.h" #include "main/renderbuffer.h" #include "main/version.h" #include "main/vtxfmt.h" #include "swrast/swrast.h" #include "swrast_setup/swrast_setup.h" #include "swrast/s_context.h" #include "swrast/s_lines.h" #include "swrast/s_renderbuffer.h" #include "swrast/s_triangle.h" #include "tnl/tnl.h" #include "tnl/t_context.h" #include "tnl/t_pipeline.h" #include "drivers/common/driverfuncs.h" #include "drivers/common/meta.h" #include "vbo/vbo.h" #define OSMESA_RENDERBUFFER_CLASS 0x053 /** * OSMesa rendering context, derived from core Mesa struct gl_context. */ struct osmesa_context { struct gl_context mesa; /*< Base class - this must be first */ struct gl_config *gl_visual; /*< Describes the buffers */ struct swrast_renderbuffer *srb; /*< The user's colorbuffer */ struct gl_framebuffer *gl_buffer; /*< The framebuffer, containing user's rb */ GLenum format; /*< User-specified context format */ GLint userRowLength; /*< user-specified number of pixels per row */ GLint rInd, gInd, bInd, aInd;/*< index offsets for RGBA formats */ GLvoid *rowaddr[SWRAST_MAX_HEIGHT]; /*< address of first pixel in each image row */ GLboolean yup; /*< TRUE -> Y increases upward */ /*< FALSE -> Y increases downward */ GLenum DataType; }; static inline OSMesaContext OSMESA_CONTEXT(struct gl_context *ctx) { /* Just cast, since we're using structure containment */ return (OSMesaContext) ctx; } /**********************************************************************/ /*** Private Device Driver Functions ***/ /**********************************************************************/ static const GLubyte * get_string( struct gl_context *ctx, GLenum name ) { (void) ctx; switch (name) { case GL_RENDERER: #if CHAN_BITS == 32 return (const GLubyte *) "Mesa OffScreen32"; #elif CHAN_BITS == 16 return (const GLubyte *) "Mesa OffScreen16"; #else return (const GLubyte *) "Mesa OffScreen"; #endif default: return NULL; } } static void osmesa_update_state( struct gl_context *ctx, GLuint new_state ) { /* easy - just propogate */ _swrast_InvalidateState( ctx, new_state ); _swsetup_InvalidateState( ctx, new_state ); _tnl_InvalidateState( ctx, new_state ); _vbo_InvalidateState( ctx, new_state ); } /** * Macros for optimized line/triangle rendering. * Only for 8-bit channel, RGBA, BGRA, ARGB formats. */ #define PACK_RGBA(DST, R, G, B, A) \ do { \ (DST)[osmesa->rInd] = R; \ (DST)[osmesa->gInd] = G; \ (DST)[osmesa->bInd] = B; \ (DST)[osmesa->aInd] = A; \ } while (0) #define PIXELADDR4(X,Y) ((GLchan *) osmesa->rowaddr[Y] + 4 * (X)) /** * Draw a flat-shaded, RGB line into an osmesa buffer. */ #define NAME flat_rgba_line #define CLIP_HACK 1 #define SETUP_CODE \ const OSMesaContext osmesa = OSMESA_CONTEXT(ctx); \ const GLchan *color = vert1->color; #define PLOT(X, Y) \ do { \ GLchan *p = PIXELADDR4(X, Y); \ PACK_RGBA(p, color[0], color[1], color[2], color[3]); \ } while (0) #include "swrast/s_linetemp.h" /** * Draw a flat-shaded, Z-less, RGB line into an osmesa buffer. */ #define NAME flat_rgba_z_line #define CLIP_HACK 1 #define INTERP_Z 1 #define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE #define SETUP_CODE \ const OSMesaContext osmesa = OSMESA_CONTEXT(ctx); \ const GLchan *color = vert1->color; #define PLOT(X, Y) \ do { \ if (Z < *zPtr) { \ GLchan *p = PIXELADDR4(X, Y); \ PACK_RGBA(p, color[RCOMP], color[GCOMP], \ color[BCOMP], color[ACOMP]); \ *zPtr = Z; \ } \ } while (0) #include "swrast/s_linetemp.h" /** * Analyze context state to see if we can provide a fast line drawing * function. Otherwise, return NULL. */ static swrast_line_func osmesa_choose_line_function( struct gl_context *ctx ) { const OSMesaContext osmesa = OSMESA_CONTEXT(ctx); const SWcontext *swrast = SWRAST_CONTEXT(ctx); if (ctx->DrawBuffer && ctx->DrawBuffer->Visual.redBits == 32) { /* the special-case line functions in this file don't work * for float color channels. */ return NULL; } if (ctx->RenderMode != GL_RENDER || ctx->Line.SmoothFlag || ctx->Texture._MaxEnabledTexImageUnit == -1 || ctx->Light.ShadeModel != GL_FLAT || ctx->Line.Width != 1.0F || ctx->Line.StippleFlag || ctx->Line.SmoothFlag) { return NULL; } if (osmesa->format != OSMESA_RGBA && osmesa->format != OSMESA_BGRA && osmesa->format != OSMESA_ARGB) { return NULL; } if (swrast->_RasterMask == DEPTH_BIT && ctx->Depth.Func == GL_LESS && ctx->Depth.Mask == GL_TRUE && ctx->Visual.depthBits == DEFAULT_SOFTWARE_DEPTH_BITS) { return flat_rgba_z_line; } if (swrast->_RasterMask == 0) { return flat_rgba_line; } return (swrast_line_func) NULL; } /**********************************************************************/ /***** Optimized triangle rendering *****/ /**********************************************************************/ /* * Smooth-shaded, z-less triangle, RGBA color. */ #define NAME smooth_rgba_z_triangle #define INTERP_Z 1 #define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE #define INTERP_RGB 1 #define INTERP_ALPHA 1 #define SETUP_CODE \ const OSMesaContext osmesa = OSMESA_CONTEXT(ctx); #define RENDER_SPAN( span ) { \ GLuint i; \ GLchan *img = PIXELADDR4(span.x, span.y); \ for (i = 0; i < span.end; i++, img += 4) { \ const GLuint z = FixedToDepth(span.z); \ if (z < zRow[i]) { \ PACK_RGBA(img, FixedToChan(span.red), \ FixedToChan(span.green), FixedToChan(span.blue), \ FixedToChan(span.alpha)); \ zRow[i] = z; \ } \ span.red += span.redStep; \ span.green += span.greenStep; \ span.blue += span.blueStep; \ span.alpha += span.alphaStep; \ span.z += span.zStep; \ } \ } #include "swrast/s_tritemp.h" /* * Flat-shaded, z-less triangle, RGBA color. */ #define NAME flat_rgba_z_triangle #define INTERP_Z 1 #define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE #define SETUP_CODE \ const OSMesaContext osmesa = OSMESA_CONTEXT(ctx); \ GLuint pixel; \ PACK_RGBA((GLchan *) &pixel, v2->color[0], v2->color[1], \ v2->color[2], v2->color[3]); #define RENDER_SPAN( span ) { \ GLuint i; \ GLuint *img = (GLuint *) PIXELADDR4(span.x, span.y); \ for (i = 0; i < span.end; i++) { \ const GLuint z = FixedToDepth(span.z); \ if (z < zRow[i]) { \ img[i] = pixel; \ zRow[i] = z; \ } \ span.z += span.zStep; \ } \ } #include "swrast/s_tritemp.h" /** * Return pointer to an optimized triangle function if possible. */ static swrast_tri_func osmesa_choose_triangle_function( struct gl_context *ctx ) { const OSMesaContext osmesa = OSMESA_CONTEXT(ctx); const SWcontext *swrast = SWRAST_CONTEXT(ctx); if (ctx->DrawBuffer && ctx->DrawBuffer->Visual.redBits == 32) { /* the special-case triangle functions in this file don't work * for float color channels. */ return NULL; } if (ctx->RenderMode != GL_RENDER || ctx->Polygon.SmoothFlag || ctx->Polygon.StippleFlag || ctx->Texture._MaxEnabledTexImageUnit != -1) { return NULL; } if (osmesa->format != OSMESA_RGBA && osmesa->format != OSMESA_BGRA && osmesa->format != OSMESA_ARGB) { return NULL; } if (ctx->Polygon.CullFlag && ctx->Polygon.CullFaceMode == GL_FRONT_AND_BACK) { return NULL; } if (swrast->_RasterMask == DEPTH_BIT && ctx->Depth.Func == GL_LESS && ctx->Depth.Mask == GL_TRUE && ctx->Visual.depthBits == DEFAULT_SOFTWARE_DEPTH_BITS) { if (ctx->Light.ShadeModel == GL_SMOOTH) { return smooth_rgba_z_triangle; } else { return flat_rgba_z_triangle; } } return NULL; } /* Override for the swrast triangle-selection function. Try to use one * of our internal triangle functions, otherwise fall back to the * standard swrast functions. */ static void osmesa_choose_triangle( struct gl_context *ctx ) { SWcontext *swrast = SWRAST_CONTEXT(ctx); swrast->Triangle = osmesa_choose_triangle_function( ctx ); if (!swrast->Triangle) _swrast_choose_triangle( ctx ); } static void osmesa_choose_line( struct gl_context *ctx ) { SWcontext *swrast = SWRAST_CONTEXT(ctx); swrast->Line = osmesa_choose_line_function( ctx ); if (!swrast->Line) _swrast_choose_line( ctx ); } /** * Recompute the values of the context's rowaddr array. */ static void compute_row_addresses( OSMesaContext osmesa ) { GLint bytesPerRow, i; GLubyte *origin = (GLubyte *) osmesa->srb->Buffer; GLint rowlength; /* in pixels */ GLint height = osmesa->srb->Base.Height; if (osmesa->userRowLength) rowlength = osmesa->userRowLength; else rowlength = osmesa->srb->Base.Width; bytesPerRow = rowlength * _mesa_get_format_bytes(osmesa->srb->Base.Format); if (osmesa->yup) { /* Y=0 is bottom line of window */ for (i = 0; i < height; i++) { osmesa->rowaddr[i] = (GLvoid *) ((GLubyte *) origin + i * bytesPerRow); } } else { /* Y=0 is top line of window */ for (i = 0; i < height; i++) { GLint j = height - i - 1; osmesa->rowaddr[i] = (GLvoid *) ((GLubyte *) origin + j * bytesPerRow); } } } /** * Don't use _mesa_delete_renderbuffer since we can't free rb->Buffer. */ static void osmesa_delete_renderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb) { _mesa_delete_renderbuffer(ctx, rb); } /** * Allocate renderbuffer storage. We don't actually allocate any storage * since we're using a user-provided buffer. * Just set up all the gl_renderbuffer methods. */ static GLboolean osmesa_renderbuffer_storage(struct gl_context *ctx, struct gl_renderbuffer *rb, GLenum internalFormat, GLuint width, GLuint height) { const OSMesaContext osmesa = OSMESA_CONTEXT(ctx); /* Note: we can ignoring internalFormat for "window-system" renderbuffers */ (void) internalFormat; /* Given the user-provided format and type, figure out which MESA_FORMAT_x * to use. * XXX There aren't Mesa formats for all the possible combinations here! * XXX Specifically, there's only RGBA-order 16-bit/channel and float * XXX formats. * XXX The 8-bit/channel formats should all be OK. */ if (osmesa->format == OSMESA_RGBA) { if (osmesa->DataType == GL_UNSIGNED_BYTE) { if (_mesa_little_endian()) rb->Format = MESA_FORMAT_R8G8B8A8_UNORM; else rb->Format = MESA_FORMAT_A8B8G8R8_UNORM; } else if (osmesa->DataType == GL_UNSIGNED_SHORT) { rb->Format = MESA_FORMAT_RGBA_UNORM16; } else { rb->Format = MESA_FORMAT_RGBA_FLOAT32; } } else if (osmesa->format == OSMESA_BGRA) { if (osmesa->DataType == GL_UNSIGNED_BYTE) { if (_mesa_little_endian()) rb->Format = MESA_FORMAT_B8G8R8A8_UNORM; else rb->Format = MESA_FORMAT_A8R8G8B8_UNORM; } else if (osmesa->DataType == GL_UNSIGNED_SHORT) { _mesa_warning(ctx, "Unsupported OSMesa format BGRA/GLushort"); rb->Format = MESA_FORMAT_RGBA_UNORM16; /* not exactly right */ } else { _mesa_warning(ctx, "Unsupported OSMesa format BGRA/GLfloat"); rb->Format = MESA_FORMAT_RGBA_FLOAT32; /* not exactly right */ } } else if (osmesa->format == OSMESA_ARGB) { if (osmesa->DataType == GL_UNSIGNED_BYTE) { if (_mesa_little_endian()) rb->Format = MESA_FORMAT_A8R8G8B8_UNORM; else rb->Format = MESA_FORMAT_B8G8R8A8_UNORM; } else if (osmesa->DataType == GL_UNSIGNED_SHORT) { _mesa_warning(ctx, "Unsupported OSMesa format ARGB/GLushort"); rb->Format = MESA_FORMAT_RGBA_UNORM16; /* not exactly right */ } else { _mesa_warning(ctx, "Unsupported OSMesa format ARGB/GLfloat"); rb->Format = MESA_FORMAT_RGBA_FLOAT32; /* not exactly right */ } } else if (osmesa->format == OSMESA_RGB) { if (osmesa->DataType == GL_UNSIGNED_BYTE) { rb->Format = MESA_FORMAT_BGR_UNORM8; } else if (osmesa->DataType == GL_UNSIGNED_SHORT) { _mesa_warning(ctx, "Unsupported OSMesa format RGB/GLushort"); rb->Format = MESA_FORMAT_RGBA_UNORM16; /* not exactly right */ } else { _mesa_warning(ctx, "Unsupported OSMesa format RGB/GLfloat"); rb->Format = MESA_FORMAT_RGBA_FLOAT32; /* not exactly right */ } } else if (osmesa->format == OSMESA_BGR) { if (osmesa->DataType == GL_UNSIGNED_BYTE) { rb->Format = MESA_FORMAT_RGB_UNORM8; } else if (osmesa->DataType == GL_UNSIGNED_SHORT) { _mesa_warning(ctx, "Unsupported OSMesa format BGR/GLushort"); rb->Format = MESA_FORMAT_RGBA_UNORM16; /* not exactly right */ } else { _mesa_warning(ctx, "Unsupported OSMesa format BGR/GLfloat"); rb->Format = MESA_FORMAT_RGBA_FLOAT32; /* not exactly right */ } } else if (osmesa->format == OSMESA_RGB_565) { assert(osmesa->DataType == GL_UNSIGNED_BYTE); rb->Format = MESA_FORMAT_B5G6R5_UNORM; } else { _mesa_problem(ctx, "bad pixel format in osmesa renderbuffer_storage"); } rb->Width = width; rb->Height = height; compute_row_addresses( osmesa ); return GL_TRUE; } /** * Allocate a new renderbuffer to describe the user-provided color buffer. */ static struct swrast_renderbuffer * new_osmesa_renderbuffer(struct gl_context *ctx, GLenum format, GLenum type) { const GLuint name = 0; struct swrast_renderbuffer *srb = CALLOC_STRUCT(swrast_renderbuffer); if (srb) { _mesa_init_renderbuffer(&srb->Base, name); srb->Base.ClassID = OSMESA_RENDERBUFFER_CLASS; srb->Base.RefCount = 1; srb->Base.Delete = osmesa_delete_renderbuffer; srb->Base.AllocStorage = osmesa_renderbuffer_storage; srb->Base.InternalFormat = GL_RGBA; srb->Base._BaseFormat = GL_RGBA; return srb; } return NULL; } static void osmesa_MapRenderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb, GLuint x, GLuint y, GLuint w, GLuint h, GLbitfield mode, GLubyte **mapOut, GLint *rowStrideOut) { const OSMesaContext osmesa = OSMESA_CONTEXT(ctx); if (rb->ClassID == OSMESA_RENDERBUFFER_CLASS) { /* this is an OSMesa renderbuffer which wraps user memory */ struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); const GLuint bpp = _mesa_get_format_bytes(rb->Format); GLint rowStride; /* in bytes */ if (osmesa->userRowLength) rowStride = osmesa->userRowLength * bpp; else rowStride = rb->Width * bpp; if (!osmesa->yup) { /* Y=0 is top line of window */ y = rb->Height - y - 1; *rowStrideOut = -rowStride; } else { *rowStrideOut = rowStride; } *mapOut = (GLubyte *) srb->Buffer + y * rowStride + x * bpp; } else { _swrast_map_soft_renderbuffer(ctx, rb, x, y, w, h, mode, mapOut, rowStrideOut); } } static void osmesa_UnmapRenderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb) { if (rb->ClassID == OSMESA_RENDERBUFFER_CLASS) { /* no-op */ } else { _swrast_unmap_soft_renderbuffer(ctx, rb); } } /**********************************************************************/ /***** Public Functions *****/ /**********************************************************************/ /** * Create an Off-Screen Mesa rendering context. The only attribute needed is * an RGBA vs Color-Index mode flag. * * Input: format - Must be GL_RGBA * sharelist - specifies another OSMesaContext with which to share * display lists. NULL indicates no sharing. * Return: an OSMesaContext or 0 if error */ GLAPI OSMesaContext GLAPIENTRY OSMesaCreateContext( GLenum format, OSMesaContext sharelist ) { return OSMesaCreateContextExt(format, DEFAULT_SOFTWARE_DEPTH_BITS, 8, 0, sharelist); } /** * New in Mesa 3.5 * * Create context and specify size of ancillary buffers. */ GLAPI OSMesaContext GLAPIENTRY OSMesaCreateContextExt( GLenum format, GLint depthBits, GLint stencilBits, GLint accumBits, OSMesaContext sharelist ) { int attribs[100], n = 0; attribs[n++] = OSMESA_FORMAT; attribs[n++] = format; attribs[n++] = OSMESA_DEPTH_BITS; attribs[n++] = depthBits; attribs[n++] = OSMESA_STENCIL_BITS; attribs[n++] = stencilBits; attribs[n++] = OSMESA_ACCUM_BITS; attribs[n++] = accumBits; attribs[n++] = 0; return OSMesaCreateContextAttribs(attribs, sharelist); } /** * New in Mesa 11.2 * * Create context with attribute list. */ GLAPI OSMesaContext GLAPIENTRY OSMesaCreateContextAttribs(const int *attribList, OSMesaContext sharelist) { OSMesaContext osmesa; struct dd_function_table functions; GLint rind, gind, bind, aind; GLint redBits = 0, greenBits = 0, blueBits = 0, alphaBits =0; GLenum format = OSMESA_RGBA; GLint depthBits = 0, stencilBits = 0, accumBits = 0; int profile = OSMESA_COMPAT_PROFILE, version_major = 1, version_minor = 0; gl_api api_profile = API_OPENGL_COMPAT; int i; for (i = 0; attribList[i]; i += 2) { switch (attribList[i]) { case OSMESA_FORMAT: format = attribList[i+1]; switch (format) { case OSMESA_COLOR_INDEX: case OSMESA_RGBA: case OSMESA_BGRA: case OSMESA_ARGB: case OSMESA_RGB: case OSMESA_BGR: case OSMESA_RGB_565: /* legal */ break; default: return NULL; } break; case OSMESA_DEPTH_BITS: depthBits = attribList[i+1]; if (depthBits < 0) return NULL; break; case OSMESA_STENCIL_BITS: stencilBits = attribList[i+1]; if (stencilBits < 0) return NULL; break; case OSMESA_ACCUM_BITS: accumBits = attribList[i+1]; if (accumBits < 0) return NULL; break; case OSMESA_PROFILE: profile = attribList[i+1]; if (profile == OSMESA_COMPAT_PROFILE) api_profile = API_OPENGL_COMPAT; else if (profile == OSMESA_CORE_PROFILE) api_profile = API_OPENGL_CORE; else return NULL; break; case OSMESA_CONTEXT_MAJOR_VERSION: version_major = attribList[i+1]; if (version_major < 1) return NULL; break; case OSMESA_CONTEXT_MINOR_VERSION: version_minor = attribList[i+1]; if (version_minor < 0) return NULL; break; case 0: /* end of list */ break; default: fprintf(stderr, "Bad attribute in OSMesaCreateContextAttribs()\n"); return NULL; } } rind = gind = bind = aind = 0; if (format==OSMESA_RGBA) { redBits = CHAN_BITS; greenBits = CHAN_BITS; blueBits = CHAN_BITS; alphaBits = CHAN_BITS; rind = 0; gind = 1; bind = 2; aind = 3; } else if (format==OSMESA_BGRA) { redBits = CHAN_BITS; greenBits = CHAN_BITS; blueBits = CHAN_BITS; alphaBits = CHAN_BITS; bind = 0; gind = 1; rind = 2; aind = 3; } else if (format==OSMESA_ARGB) { redBits = CHAN_BITS; greenBits = CHAN_BITS; blueBits = CHAN_BITS; alphaBits = CHAN_BITS; aind = 0; rind = 1; gind = 2; bind = 3; } else if (format==OSMESA_RGB) { redBits = CHAN_BITS; greenBits = CHAN_BITS; blueBits = CHAN_BITS; alphaBits = 0; rind = 0; gind = 1; bind = 2; } else if (format==OSMESA_BGR) { redBits = CHAN_BITS; greenBits = CHAN_BITS; blueBits = CHAN_BITS; alphaBits = 0; rind = 2; gind = 1; bind = 0; } #if CHAN_TYPE == GL_UNSIGNED_BYTE else if (format==OSMESA_RGB_565) { redBits = 5; greenBits = 6; blueBits = 5; alphaBits = 0; rind = 0; /* not used */ gind = 0; bind = 0; } #endif else { return NULL; } osmesa = (OSMesaContext) CALLOC_STRUCT(osmesa_context); if (osmesa) { osmesa->gl_visual = _mesa_create_visual( GL_FALSE, /* double buffer */ GL_FALSE, /* stereo */ redBits, greenBits, blueBits, alphaBits, depthBits, stencilBits, accumBits, accumBits, accumBits, alphaBits ? accumBits : 0, 1 /* num samples */ ); if (!osmesa->gl_visual) { free(osmesa); return NULL; } /* Initialize device driver function table */ _mesa_init_driver_functions(&functions); /* override with our functions */ functions.GetString = get_string; functions.UpdateState = osmesa_update_state; if (!_mesa_initialize_context(&osmesa->mesa, api_profile, osmesa->gl_visual, sharelist ? &sharelist->mesa : (struct gl_context *) NULL, &functions)) { _mesa_destroy_visual( osmesa->gl_visual ); free(osmesa); return NULL; } _mesa_enable_sw_extensions(&(osmesa->mesa)); osmesa->gl_buffer = _mesa_create_framebuffer(osmesa->gl_visual); if (!osmesa->gl_buffer) { _mesa_destroy_visual( osmesa->gl_visual ); _mesa_free_context_data( &osmesa->mesa ); free(osmesa); return NULL; } /* Create depth/stencil/accum buffers. We'll create the color * buffer later in OSMesaMakeCurrent(). */ _swrast_add_soft_renderbuffers(osmesa->gl_buffer, GL_FALSE, /* color */ osmesa->gl_visual->haveDepthBuffer, osmesa->gl_visual->haveStencilBuffer, osmesa->gl_visual->haveAccumBuffer, GL_FALSE, /* alpha */ GL_FALSE /* aux */ ); osmesa->format = format; osmesa->userRowLength = 0; osmesa->yup = GL_TRUE; osmesa->rInd = rind; osmesa->gInd = gind; osmesa->bInd = bind; osmesa->aInd = aind; _mesa_meta_init(&osmesa->mesa); /* Initialize the software rasterizer and helper modules. */ { struct gl_context *ctx = &osmesa->mesa; SWcontext *swrast; TNLcontext *tnl; if (!_swrast_CreateContext( ctx ) || !_vbo_CreateContext( ctx ) || !_tnl_CreateContext( ctx ) || !_swsetup_CreateContext( ctx )) { _mesa_destroy_visual(osmesa->gl_visual); _mesa_free_context_data(ctx); free(osmesa); return NULL; } _swsetup_Wakeup( ctx ); /* use default TCL pipeline */ tnl = TNL_CONTEXT(ctx); tnl->Driver.RunPipeline = _tnl_run_pipeline; ctx->Driver.MapRenderbuffer = osmesa_MapRenderbuffer; ctx->Driver.UnmapRenderbuffer = osmesa_UnmapRenderbuffer; ctx->Driver.GenerateMipmap = _mesa_generate_mipmap; /* Extend the software rasterizer with our optimized line and triangle * drawing functions. */ swrast = SWRAST_CONTEXT( ctx ); swrast->choose_line = osmesa_choose_line; swrast->choose_triangle = osmesa_choose_triangle; _mesa_compute_version(ctx); if (ctx->Version < version_major * 10 + version_minor) { _mesa_destroy_visual(osmesa->gl_visual); _mesa_free_context_data(ctx); free(osmesa); return NULL; } /* Exec table initialization requires the version to be computed */ _mesa_initialize_dispatch_tables(ctx); _mesa_initialize_vbo_vtxfmt(ctx); } } return osmesa; } /** * Destroy an Off-Screen Mesa rendering context. * * \param osmesa the context to destroy */ GLAPI void GLAPIENTRY OSMesaDestroyContext( OSMesaContext osmesa ) { if (osmesa) { if (osmesa->srb) _mesa_reference_renderbuffer((struct gl_renderbuffer **) &osmesa->srb, NULL); _mesa_meta_free( &osmesa->mesa ); _swsetup_DestroyContext( &osmesa->mesa ); _tnl_DestroyContext( &osmesa->mesa ); _vbo_DestroyContext( &osmesa->mesa ); _swrast_DestroyContext( &osmesa->mesa ); _mesa_destroy_visual( osmesa->gl_visual ); _mesa_reference_framebuffer( &osmesa->gl_buffer, NULL ); _mesa_free_context_data( &osmesa->mesa ); free( osmesa ); } } /** * Bind an OSMesaContext to an image buffer. The image buffer is just a * block of memory which the client provides. Its size must be at least * as large as width*height*sizeof(type). Its address should be a multiple * of 4 if using RGBA mode. * * Image data is stored in the order of glDrawPixels: row-major order * with the lower-left image pixel stored in the first array position * (ie. bottom-to-top). * * If the context's viewport hasn't been initialized yet, it will now be * initialized to (0,0,width,height). * * If both the context and the buffer are null, the current context will be * unbound. * * Input: osmesa - the rendering context * buffer - the image buffer memory * type - data type for pixel components * Normally, only GL_UNSIGNED_BYTE and GL_UNSIGNED_SHORT_5_6_5 * are supported. But if Mesa's been compiled with CHAN_BITS==16 * then type may be GL_UNSIGNED_SHORT or GL_UNSIGNED_BYTE. And if * Mesa's been build with CHAN_BITS==32 then type may be GL_FLOAT, * GL_UNSIGNED_SHORT or GL_UNSIGNED_BYTE. * width, height - size of image buffer in pixels, at least 1 * Return: GL_TRUE if success, GL_FALSE if error because of invalid osmesa, * invalid buffer address, invalid type, width<1, height<1, * width>internal limit or height>internal limit. */ GLAPI GLboolean GLAPIENTRY OSMesaMakeCurrent( OSMesaContext osmesa, void *buffer, GLenum type, GLsizei width, GLsizei height ) { if (!osmesa && !buffer) { return _mesa_make_current(NULL, NULL, NULL); } if (!osmesa || !buffer || width < 1 || height < 1 || width > SWRAST_MAX_WIDTH || height > SWRAST_MAX_HEIGHT) { return GL_FALSE; } if (osmesa->format == OSMESA_RGB_565 && type != GL_UNSIGNED_SHORT_5_6_5) { return GL_FALSE; } #if 0 if (!(type == GL_UNSIGNED_BYTE || (type == GL_UNSIGNED_SHORT && CHAN_BITS >= 16) || (type == GL_FLOAT && CHAN_BITS == 32))) { /* i.e. is sizeof(type) * 8 > CHAN_BITS? */ return GL_FALSE; } #endif osmesa_update_state( &osmesa->mesa, 0 ); /* Call this periodically to detect when the user has begun using * GL rendering from multiple threads. */ _glapi_check_multithread(); /* Create a front/left color buffer which wraps the user-provided buffer. * There is no back color buffer. * If the user tries to use a 8, 16 or 32-bit/channel buffer that * doesn't match what Mesa was compiled for (CHAN_BITS) the * _mesa_add_renderbuffer() function will create a "wrapper" renderbuffer * that converts rendering from CHAN_BITS to the user-requested channel * size. */ if (!osmesa->srb) { osmesa->srb = new_osmesa_renderbuffer(&osmesa->mesa, osmesa->format, type); _mesa_remove_renderbuffer(osmesa->gl_buffer, BUFFER_FRONT_LEFT); _mesa_add_renderbuffer(osmesa->gl_buffer, BUFFER_FRONT_LEFT, &osmesa->srb->Base); assert(osmesa->srb->Base.RefCount == 2); } osmesa->DataType = type; /* Set renderbuffer fields. Set width/height = 0 to force * osmesa_renderbuffer_storage() being called by _mesa_resize_framebuffer() */ osmesa->srb->Buffer = buffer; osmesa->srb->Base.Width = osmesa->srb->Base.Height = 0; /* Set the framebuffer's size. This causes the * osmesa_renderbuffer_storage() function to get called. */ _mesa_resize_framebuffer(&osmesa->mesa, osmesa->gl_buffer, width, height); _mesa_make_current( &osmesa->mesa, osmesa->gl_buffer, osmesa->gl_buffer ); /* Remove renderbuffer attachment, then re-add. This installs the * renderbuffer adaptor/wrapper if needed (for bpp conversion). */ _mesa_remove_renderbuffer(osmesa->gl_buffer, BUFFER_FRONT_LEFT); _mesa_add_renderbuffer(osmesa->gl_buffer, BUFFER_FRONT_LEFT, &osmesa->srb->Base); /* this updates the visual's red/green/blue/alphaBits fields */ _mesa_update_framebuffer_visual(&osmesa->mesa, osmesa->gl_buffer); /* update the framebuffer size */ _mesa_resize_framebuffer(&osmesa->mesa, osmesa->gl_buffer, width, height); return GL_TRUE; } GLAPI OSMesaContext GLAPIENTRY OSMesaGetCurrentContext( void ) { struct gl_context *ctx = _mesa_get_current_context(); if (ctx) return (OSMesaContext) ctx; else return NULL; } GLAPI void GLAPIENTRY OSMesaPixelStore( GLint pname, GLint value ) { OSMesaContext osmesa = OSMesaGetCurrentContext(); switch (pname) { case OSMESA_ROW_LENGTH: if (value<0) { _mesa_error( &osmesa->mesa, GL_INVALID_VALUE, "OSMesaPixelStore(value)" ); return; } osmesa->userRowLength = value; break; case OSMESA_Y_UP: osmesa->yup = value ? GL_TRUE : GL_FALSE; break; default: _mesa_error( &osmesa->mesa, GL_INVALID_ENUM, "OSMesaPixelStore(pname)" ); return; } compute_row_addresses( osmesa ); } GLAPI void GLAPIENTRY OSMesaGetIntegerv( GLint pname, GLint *value ) { OSMesaContext osmesa = OSMesaGetCurrentContext(); switch (pname) { case OSMESA_WIDTH: if (osmesa->gl_buffer) *value = osmesa->gl_buffer->Width; else *value = 0; return; case OSMESA_HEIGHT: if (osmesa->gl_buffer) *value = osmesa->gl_buffer->Height; else *value = 0; return; case OSMESA_FORMAT: *value = osmesa->format; return; case OSMESA_TYPE: /* current color buffer's data type */ *value = osmesa->DataType; return; case OSMESA_ROW_LENGTH: *value = osmesa->userRowLength; return; case OSMESA_Y_UP: *value = osmesa->yup; return; case OSMESA_MAX_WIDTH: *value = SWRAST_MAX_WIDTH; return; case OSMESA_MAX_HEIGHT: *value = SWRAST_MAX_HEIGHT; return; default: _mesa_error(&osmesa->mesa, GL_INVALID_ENUM, "OSMesaGetIntergerv(pname)"); return; } } /** * Return the depth buffer associated with an OSMesa context. * Input: c - the OSMesa context * Output: width, height - size of buffer in pixels * bytesPerValue - bytes per depth value (2 or 4) * buffer - pointer to depth buffer values * Return: GL_TRUE or GL_FALSE to indicate success or failure. */ GLAPI GLboolean GLAPIENTRY OSMesaGetDepthBuffer( OSMesaContext c, GLint *width, GLint *height, GLint *bytesPerValue, void **buffer ) { struct swrast_renderbuffer *srb = NULL; if (c->gl_buffer) srb = swrast_renderbuffer(c->gl_buffer-> Attachment[BUFFER_DEPTH].Renderbuffer); if (!srb || !srb->Buffer) { *width = 0; *height = 0; *bytesPerValue = 0; *buffer = 0; return GL_FALSE; } else { *width = srb->Base.Width; *height = srb->Base.Height; if (c->gl_visual->depthBits <= 16) *bytesPerValue = sizeof(GLushort); else *bytesPerValue = sizeof(GLuint); *buffer = (void *) srb->Buffer; return GL_TRUE; } } /** * Return the color buffer associated with an OSMesa context. * Input: c - the OSMesa context * Output: width, height - size of buffer in pixels * format - the pixel format (OSMESA_FORMAT) * buffer - pointer to color buffer values * Return: GL_TRUE or GL_FALSE to indicate success or failure. */ GLAPI GLboolean GLAPIENTRY OSMesaGetColorBuffer( OSMesaContext osmesa, GLint *width, GLint *height, GLint *format, void **buffer ) { if (osmesa->srb && osmesa->srb->Buffer) { *width = osmesa->srb->Base.Width; *height = osmesa->srb->Base.Height; *format = osmesa->format; *buffer = (void *) osmesa->srb->Buffer; return GL_TRUE; } else { *width = 0; *height = 0; *format = 0; *buffer = 0; return GL_FALSE; } } struct name_function { const char *Name; OSMESAproc Function; }; static struct name_function functions[] = { { "OSMesaCreateContext", (OSMESAproc) OSMesaCreateContext }, { "OSMesaCreateContextExt", (OSMESAproc) OSMesaCreateContextExt }, { "OSMesaCreateContextAttribs", (OSMESAproc) OSMesaCreateContextAttribs }, { "OSMesaDestroyContext", (OSMESAproc) OSMesaDestroyContext }, { "OSMesaMakeCurrent", (OSMESAproc) OSMesaMakeCurrent }, { "OSMesaGetCurrentContext", (OSMESAproc) OSMesaGetCurrentContext }, { "OSMesaPixelStore", (OSMESAproc) OSMesaPixelStore }, { "OSMesaGetIntegerv", (OSMESAproc) OSMesaGetIntegerv }, { "OSMesaGetDepthBuffer", (OSMESAproc) OSMesaGetDepthBuffer }, { "OSMesaGetColorBuffer", (OSMESAproc) OSMesaGetColorBuffer }, { "OSMesaGetProcAddress", (OSMESAproc) OSMesaGetProcAddress }, { "OSMesaColorClamp", (OSMESAproc) OSMesaColorClamp }, { "OSMesaPostprocess", (OSMESAproc) OSMesaPostprocess }, { NULL, NULL } }; GLAPI OSMESAproc GLAPIENTRY OSMesaGetProcAddress( const char *funcName ) { int i; for (i = 0; functions[i].Name; i++) { if (strcmp(functions[i].Name, funcName) == 0) return functions[i].Function; } return _glapi_get_proc_address(funcName); } GLAPI void GLAPIENTRY OSMesaColorClamp(GLboolean enable) { OSMesaContext osmesa = OSMesaGetCurrentContext(); if (enable == GL_TRUE) { osmesa->mesa.Color.ClampFragmentColor = GL_TRUE; } else { osmesa->mesa.Color.ClampFragmentColor = GL_FIXED_ONLY_ARB; } } GLAPI void GLAPIENTRY OSMesaPostprocess(OSMesaContext osmesa, const char *filter, unsigned enable_value) { fprintf(stderr, "OSMesaPostProcess() is only available with gallium drivers\n"); } /** * When GLX_INDIRECT_RENDERING is defined, some symbols are missing in * libglapi.a. We need to define them here. */ #ifdef GLX_INDIRECT_RENDERING #define GL_GLEXT_PROTOTYPES #include "GL/gl.h" #include "glapi/glapi.h" #include "glapi/glapitable.h" #if defined(USE_MGL_NAMESPACE) #define NAME(func) mgl##func #else #define NAME(func) gl##func #endif #define DISPATCH(FUNC, ARGS, MESSAGE) \ GET_DISPATCH()->FUNC ARGS #define RETURN_DISPATCH(FUNC, ARGS, MESSAGE) \ return GET_DISPATCH()->FUNC ARGS /* skip normal ones */ #define _GLAPI_SKIP_NORMAL_ENTRY_POINTS #include "glapi/glapitemp.h" #endif /* GLX_INDIRECT_RENDERING */