/* * Mesa 3-D graphics library * Version: 6.5 * * Copyright (C) 1999-2006 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 * BRIAN PAUL 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. */ /** * Functions for allocating/managing software-based renderbuffers. * Also, routines for reading/writing software-based renderbuffer data as * ubytes, ushorts, uints, etc. */ #include "main/glheader.h" #include "main/imports.h" #include "main/context.h" #include "main/fbobject.h" #include "main/formats.h" #include "main/mtypes.h" #include "main/renderbuffer.h" #include "swrast/s_renderbuffer.h" /** * This is a software fallback for the gl_renderbuffer->AllocStorage * function. * Device drivers will typically override this function for the buffers * which it manages (typically color buffers, Z and stencil). * Other buffers (like software accumulation and aux buffers) which the driver * doesn't manage can be handled with this function. * * This one multi-purpose function can allocate stencil, depth, accum, color * or color-index buffers! */ static GLboolean soft_renderbuffer_storage(struct gl_context *ctx, struct gl_renderbuffer *rb, GLenum internalFormat, GLuint width, GLuint height) { switch (internalFormat) { case GL_RGB: case GL_R3_G3_B2: case GL_RGB4: case GL_RGB5: case GL_RGB8: case GL_RGB10: case GL_RGB12: case GL_RGB16: rb->Format = MESA_FORMAT_RGB888; break; case GL_RGBA: case GL_RGBA2: case GL_RGBA4: case GL_RGB5_A1: case GL_RGBA8: #if 1 case GL_RGB10_A2: case GL_RGBA12: #endif if (_mesa_little_endian()) rb->Format = MESA_FORMAT_RGBA8888_REV; else rb->Format = MESA_FORMAT_RGBA8888; break; case GL_RGBA16: case GL_RGBA16_SNORM: /* for accum buffer */ rb->Format = MESA_FORMAT_SIGNED_RGBA_16; break; case GL_STENCIL_INDEX: case GL_STENCIL_INDEX1_EXT: case GL_STENCIL_INDEX4_EXT: case GL_STENCIL_INDEX8_EXT: case GL_STENCIL_INDEX16_EXT: rb->Format = MESA_FORMAT_S8; break; case GL_DEPTH_COMPONENT: case GL_DEPTH_COMPONENT16: rb->Format = MESA_FORMAT_Z16; break; case GL_DEPTH_COMPONENT24: rb->Format = MESA_FORMAT_X8_Z24; break; case GL_DEPTH_COMPONENT32: rb->Format = MESA_FORMAT_Z32; break; case GL_DEPTH_STENCIL_EXT: case GL_DEPTH24_STENCIL8_EXT: rb->Format = MESA_FORMAT_Z24_S8; break; default: /* unsupported format */ return GL_FALSE; } /* free old buffer storage */ if (rb->Buffer) { free(rb->Buffer); rb->Buffer = NULL; } rb->RowStrideBytes = width * _mesa_get_format_bytes(rb->Format); if (width > 0 && height > 0) { /* allocate new buffer storage */ rb->Buffer = malloc(width * height * _mesa_get_format_bytes(rb->Format)); if (rb->Buffer == NULL) { rb->Width = 0; rb->Height = 0; _mesa_error(ctx, GL_OUT_OF_MEMORY, "software renderbuffer allocation (%d x %d x %d)", width, height, _mesa_get_format_bytes(rb->Format)); return GL_FALSE; } } rb->Width = width; rb->Height = height; rb->_BaseFormat = _mesa_base_fbo_format(ctx, internalFormat); if (rb->Name == 0 && internalFormat == GL_RGBA16_SNORM && rb->_BaseFormat == 0) { /* NOTE: This is a special case just for accumulation buffers. * This is a very limited use case- there's no snorm texturing or * rendering going on. */ rb->_BaseFormat = GL_RGBA; } else { /* the internalFormat should have been error checked long ago */ ASSERT(rb->_BaseFormat); } return GL_TRUE; } /** * Called via gl_renderbuffer::Delete() */ static void soft_renderbuffer_delete(struct gl_renderbuffer *rb) { if (rb->Buffer) { free(rb->Buffer); rb->Buffer = NULL; } free(rb); } void _swrast_map_soft_renderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb, GLuint x, GLuint y, GLuint w, GLuint h, GLbitfield mode, GLubyte **out_map, GLint *out_stride) { GLubyte *map = rb->Buffer; int cpp = _mesa_get_format_bytes(rb->Format); int stride = rb->Width * cpp; if (!map) { *out_map = NULL; *out_stride = 0; } map += y * stride; map += x * cpp; *out_map = map; *out_stride = stride; } void _swrast_unmap_soft_renderbuffer(struct gl_context *ctx, struct gl_renderbuffer *rb) { } /** * Allocate a software-based renderbuffer. This is called via the * ctx->Driver.NewRenderbuffer() function when the user creates a new * renderbuffer. * This would not be used for hardware-based renderbuffers. */ struct gl_renderbuffer * _swrast_new_soft_renderbuffer(struct gl_context *ctx, GLuint name) { struct gl_renderbuffer *rb = _mesa_new_renderbuffer(ctx, name); if (rb) { rb->AllocStorage = soft_renderbuffer_storage; rb->Delete = soft_renderbuffer_delete; } return rb; } /** * Add software-based color renderbuffers to the given framebuffer. * This is a helper routine for device drivers when creating a * window system framebuffer (not a user-created render/framebuffer). * Once this function is called, you can basically forget about this * renderbuffer; core Mesa will handle all the buffer management and * rendering! */ static GLboolean add_color_renderbuffers(struct gl_context *ctx, struct gl_framebuffer *fb, GLuint rgbBits, GLuint alphaBits, GLboolean frontLeft, GLboolean backLeft, GLboolean frontRight, GLboolean backRight) { gl_buffer_index b; if (rgbBits > 16 || alphaBits > 16) { _mesa_problem(ctx, "Unsupported bit depth in add_color_renderbuffers"); return GL_FALSE; } assert(MAX_COLOR_ATTACHMENTS >= 4); for (b = BUFFER_FRONT_LEFT; b <= BUFFER_BACK_RIGHT; b++) { struct gl_renderbuffer *rb; if (b == BUFFER_FRONT_LEFT && !frontLeft) continue; else if (b == BUFFER_BACK_LEFT && !backLeft) continue; else if (b == BUFFER_FRONT_RIGHT && !frontRight) continue; else if (b == BUFFER_BACK_RIGHT && !backRight) continue; assert(fb->Attachment[b].Renderbuffer == NULL); rb = _mesa_new_renderbuffer(ctx, 0); if (!rb) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating color buffer"); return GL_FALSE; } rb->InternalFormat = GL_RGBA; rb->AllocStorage = soft_renderbuffer_storage; _mesa_add_renderbuffer(fb, b, rb); } return GL_TRUE; } /** * Add a software-based depth renderbuffer to the given framebuffer. * This is a helper routine for device drivers when creating a * window system framebuffer (not a user-created render/framebuffer). * Once this function is called, you can basically forget about this * renderbuffer; core Mesa will handle all the buffer management and * rendering! */ static GLboolean add_depth_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb, GLuint depthBits) { struct gl_renderbuffer *rb; if (depthBits > 32) { _mesa_problem(ctx, "Unsupported depthBits in add_depth_renderbuffer"); return GL_FALSE; } assert(fb->Attachment[BUFFER_DEPTH].Renderbuffer == NULL); rb = _mesa_new_renderbuffer(ctx, 0); if (!rb) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating depth buffer"); return GL_FALSE; } if (depthBits <= 16) { rb->InternalFormat = GL_DEPTH_COMPONENT16; } else if (depthBits <= 24) { rb->InternalFormat = GL_DEPTH_COMPONENT24; } else { rb->InternalFormat = GL_DEPTH_COMPONENT32; } rb->AllocStorage = soft_renderbuffer_storage; _mesa_add_renderbuffer(fb, BUFFER_DEPTH, rb); return GL_TRUE; } /** * Add a software-based stencil renderbuffer to the given framebuffer. * This is a helper routine for device drivers when creating a * window system framebuffer (not a user-created render/framebuffer). * Once this function is called, you can basically forget about this * renderbuffer; core Mesa will handle all the buffer management and * rendering! */ static GLboolean add_stencil_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb, GLuint stencilBits) { struct gl_renderbuffer *rb; if (stencilBits > 16) { _mesa_problem(ctx, "Unsupported stencilBits in add_stencil_renderbuffer"); return GL_FALSE; } assert(fb->Attachment[BUFFER_STENCIL].Renderbuffer == NULL); rb = _mesa_new_renderbuffer(ctx, 0); if (!rb) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating stencil buffer"); return GL_FALSE; } assert(stencilBits <= 8); rb->InternalFormat = GL_STENCIL_INDEX8; rb->AllocStorage = soft_renderbuffer_storage; _mesa_add_renderbuffer(fb, BUFFER_STENCIL, rb); return GL_TRUE; } static GLboolean add_depth_stencil_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb) { struct gl_renderbuffer *rb; assert(fb->Attachment[BUFFER_DEPTH].Renderbuffer == NULL); assert(fb->Attachment[BUFFER_STENCIL].Renderbuffer == NULL); rb = _mesa_new_renderbuffer(ctx, 0); if (!rb) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating depth+stencil buffer"); return GL_FALSE; } rb->InternalFormat = GL_DEPTH_STENCIL; rb->AllocStorage = soft_renderbuffer_storage; _mesa_add_renderbuffer(fb, BUFFER_DEPTH, rb); _mesa_add_renderbuffer(fb, BUFFER_STENCIL, rb); return GL_TRUE; } /** * Add a software-based accumulation renderbuffer to the given framebuffer. * This is a helper routine for device drivers when creating a * window system framebuffer (not a user-created render/framebuffer). * Once this function is called, you can basically forget about this * renderbuffer; core Mesa will handle all the buffer management and * rendering! */ static GLboolean add_accum_renderbuffer(struct gl_context *ctx, struct gl_framebuffer *fb, GLuint redBits, GLuint greenBits, GLuint blueBits, GLuint alphaBits) { struct gl_renderbuffer *rb; if (redBits > 16 || greenBits > 16 || blueBits > 16 || alphaBits > 16) { _mesa_problem(ctx, "Unsupported accumBits in add_accum_renderbuffer"); return GL_FALSE; } assert(fb->Attachment[BUFFER_ACCUM].Renderbuffer == NULL); rb = _mesa_new_renderbuffer(ctx, 0); if (!rb) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating accum buffer"); return GL_FALSE; } rb->InternalFormat = GL_RGBA16_SNORM; rb->AllocStorage = soft_renderbuffer_storage; _mesa_add_renderbuffer(fb, BUFFER_ACCUM, rb); return GL_TRUE; } /** * Add a software-based aux renderbuffer to the given framebuffer. * This is a helper routine for device drivers when creating a * window system framebuffer (not a user-created render/framebuffer). * Once this function is called, you can basically forget about this * renderbuffer; core Mesa will handle all the buffer management and * rendering! * * NOTE: color-index aux buffers not supported. */ static GLboolean add_aux_renderbuffers(struct gl_context *ctx, struct gl_framebuffer *fb, GLuint colorBits, GLuint numBuffers) { GLuint i; if (colorBits > 16) { _mesa_problem(ctx, "Unsupported colorBits in add_aux_renderbuffers"); return GL_FALSE; } assert(numBuffers <= MAX_AUX_BUFFERS); for (i = 0; i < numBuffers; i++) { struct gl_renderbuffer *rb = _mesa_new_renderbuffer(ctx, 0); assert(fb->Attachment[BUFFER_AUX0 + i].Renderbuffer == NULL); if (!rb) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating aux buffer"); return GL_FALSE; } assert (colorBits <= 8); rb->InternalFormat = GL_RGBA; rb->AllocStorage = soft_renderbuffer_storage; _mesa_add_renderbuffer(fb, BUFFER_AUX0 + i, rb); } return GL_TRUE; } /** * Create/attach software-based renderbuffers to the given framebuffer. * This is a helper routine for device drivers. Drivers can just as well * call the individual _mesa_add_*_renderbuffer() routines directly. */ void _swrast_add_soft_renderbuffers(struct gl_framebuffer *fb, GLboolean color, GLboolean depth, GLboolean stencil, GLboolean accum, GLboolean alpha, GLboolean aux) { GLboolean frontLeft = GL_TRUE; GLboolean backLeft = fb->Visual.doubleBufferMode; GLboolean frontRight = fb->Visual.stereoMode; GLboolean backRight = fb->Visual.stereoMode && fb->Visual.doubleBufferMode; if (color) { assert(fb->Visual.redBits == fb->Visual.greenBits); assert(fb->Visual.redBits == fb->Visual.blueBits); add_color_renderbuffers(NULL, fb, fb->Visual.redBits, fb->Visual.alphaBits, frontLeft, backLeft, frontRight, backRight); } #if 0 /* This is pretty much for debugging purposes only since there's a perf * hit for using combined depth/stencil in swrast. */ if (depth && fb->Visual.depthBits == 24 && stencil && fb->Visual.stencilBits == 8) { /* use combined depth/stencil buffer */ add_depth_stencil_renderbuffer(NULL, fb); } else #else (void) add_depth_stencil_renderbuffer; #endif { if (depth) { assert(fb->Visual.depthBits > 0); add_depth_renderbuffer(NULL, fb, fb->Visual.depthBits); } if (stencil) { assert(fb->Visual.stencilBits > 0); add_stencil_renderbuffer(NULL, fb, fb->Visual.stencilBits); } } if (accum) { assert(fb->Visual.accumRedBits > 0); assert(fb->Visual.accumGreenBits > 0); assert(fb->Visual.accumBlueBits > 0); add_accum_renderbuffer(NULL, fb, fb->Visual.accumRedBits, fb->Visual.accumGreenBits, fb->Visual.accumBlueBits, fb->Visual.accumAlphaBits); } if (aux) { assert(fb->Visual.numAuxBuffers > 0); add_aux_renderbuffers(NULL, fb, fb->Visual.redBits, fb->Visual.numAuxBuffers); } #if 0 if (multisample) { /* maybe someday */ } #endif }