/* * Mesa 3-D graphics library * * 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 * 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. */ /** * 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_context.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) { struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); GLuint bpp; 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_BGR_UNORM8; 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_R8G8B8A8_UNORM; else rb->Format = MESA_FORMAT_A8B8G8R8_UNORM; break; case GL_RGBA16: case GL_RGBA16_SNORM: /* for accum buffer */ rb->Format = MESA_FORMAT_RGBA_SNORM16; 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_S_UINT8; break; case GL_DEPTH_COMPONENT: case GL_DEPTH_COMPONENT16: rb->Format = MESA_FORMAT_Z_UNORM16; break; case GL_DEPTH_COMPONENT24: rb->Format = MESA_FORMAT_Z24_UNORM_X8_UINT; break; case GL_DEPTH_COMPONENT32: rb->Format = MESA_FORMAT_Z_UNORM32; break; case GL_DEPTH_STENCIL_EXT: case GL_DEPTH24_STENCIL8_EXT: rb->Format = MESA_FORMAT_S8_UINT_Z24_UNORM; break; default: /* unsupported format */ return GL_FALSE; } bpp = _mesa_get_format_bytes(rb->Format); /* free old buffer storage */ free(srb->Buffer); srb->Buffer = NULL; srb->RowStride = width * bpp; if (width > 0 && height > 0) { /* allocate new buffer storage */ srb->Buffer = malloc(srb->RowStride * height); if (srb->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, bpp); 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_context *ctx, struct gl_renderbuffer *rb) { struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); free(srb->Buffer); srb->Buffer = NULL; _mesa_delete_renderbuffer(ctx, 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) { struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); GLubyte *map = srb->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 swrast_renderbuffer *srb = CALLOC_STRUCT(swrast_renderbuffer); if (srb) { _mesa_init_renderbuffer(&srb->Base, name); srb->Base.AllocStorage = soft_renderbuffer_storage; srb->Base.Delete = soft_renderbuffer_delete; } return &srb->Base; } /** * 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 = ctx->Driver.NewRenderbuffer(ctx, 0); if (!rb) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "Allocating color buffer"); return GL_FALSE; } /* Set refcount to 0 to avoid a leak since the _mesa_add_renderbuffer() * call below will bump the initial refcount. */ rb->RefCount = 0; 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 = _swrast_new_soft_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 = _swrast_new_soft_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 = _swrast_new_soft_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 = _swrast_new_soft_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 = _swrast_new_soft_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 } static void map_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, gl_buffer_index buffer) { struct gl_texture_object *texObj = fb->Attachment[buffer].Texture; struct gl_renderbuffer *rb = fb->Attachment[buffer].Renderbuffer; struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); if (texObj) { /* map texture image (render to texture) */ const GLuint level = fb->Attachment[buffer].TextureLevel; const GLuint face = fb->Attachment[buffer].CubeMapFace; const GLuint slice = fb->Attachment[buffer].Zoffset; struct gl_texture_image *texImage = texObj->Image[face][level]; if (texImage) { ctx->Driver.MapTextureImage(ctx, texImage, slice, 0, 0, texImage->Width, texImage->Height, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, &srb->Map, &srb->RowStride); } } else if (rb) { /* Map ordinary renderbuffer */ ctx->Driver.MapRenderbuffer(ctx, rb, 0, 0, rb->Width, rb->Height, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, &srb->Map, &srb->RowStride); } assert(srb->Map); } static void unmap_attachment(struct gl_context *ctx, struct gl_framebuffer *fb, gl_buffer_index buffer) { struct gl_texture_object *texObj = fb->Attachment[buffer].Texture; struct gl_renderbuffer *rb = fb->Attachment[buffer].Renderbuffer; struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); if (texObj) { /* unmap texture image (render to texture) */ const GLuint level = fb->Attachment[buffer].TextureLevel; const GLuint face = fb->Attachment[buffer].CubeMapFace; const GLuint slice = fb->Attachment[buffer].Zoffset; struct gl_texture_image *texImage = texObj->Image[face][level]; if (texImage) { ctx->Driver.UnmapTextureImage(ctx, texImage, slice); } } else if (rb) { /* unmap ordinary renderbuffer */ ctx->Driver.UnmapRenderbuffer(ctx, rb); } srb->Map = NULL; } /** * Determine what type to use (ubyte vs. float) for span colors for the * given renderbuffer. * See also _swrast_write_rgba_span(). */ static void find_renderbuffer_colortype(struct gl_renderbuffer *rb) { struct swrast_renderbuffer *srb = swrast_renderbuffer(rb); GLuint rbMaxBits = _mesa_get_format_max_bits(rb->Format); GLenum rbDatatype = _mesa_get_format_datatype(rb->Format); if (rbDatatype == GL_UNSIGNED_NORMALIZED && rbMaxBits <= 8) { /* the buffer's values fit in GLubyte values */ srb->ColorType = GL_UNSIGNED_BYTE; } else { /* use floats otherwise */ srb->ColorType = GL_FLOAT; } } /** * Map the renderbuffers we'll use for tri/line/point rendering. */ void _swrast_map_renderbuffers(struct gl_context *ctx) { struct gl_framebuffer *fb = ctx->DrawBuffer; struct gl_renderbuffer *depthRb, *stencilRb; GLuint buf; depthRb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; if (depthRb) { /* map depth buffer */ map_attachment(ctx, fb, BUFFER_DEPTH); } stencilRb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; if (stencilRb && stencilRb != depthRb) { /* map stencil buffer */ map_attachment(ctx, fb, BUFFER_STENCIL); } for (buf = 0; buf < fb->_NumColorDrawBuffers; buf++) { if (fb->_ColorDrawBufferIndexes[buf] >= 0) { map_attachment(ctx, fb, fb->_ColorDrawBufferIndexes[buf]); find_renderbuffer_colortype(fb->_ColorDrawBuffers[buf]); } } } /** * Unmap renderbuffers after rendering. */ void _swrast_unmap_renderbuffers(struct gl_context *ctx) { struct gl_framebuffer *fb = ctx->DrawBuffer; struct gl_renderbuffer *depthRb, *stencilRb; GLuint buf; depthRb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; if (depthRb) { /* map depth buffer */ unmap_attachment(ctx, fb, BUFFER_DEPTH); } stencilRb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; if (stencilRb && stencilRb != depthRb) { /* map stencil buffer */ unmap_attachment(ctx, fb, BUFFER_STENCIL); } for (buf = 0; buf < fb->_NumColorDrawBuffers; buf++) { if (fb->_ColorDrawBufferIndexes[buf] >= 0) { unmap_attachment(ctx, fb, fb->_ColorDrawBufferIndexes[buf]); } } }