| /* |
| * Mesa 3-D graphics library |
| * Version: 6.3 |
| * |
| * Copyright (C) 1999-2005 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 framebuffers and renderbuffers. |
| * Also, routines for reading/writing renderbuffer data as ubytes, |
| * ushorts, uints, etc. |
| */ |
| |
| |
| #include "glheader.h" |
| #include "imports.h" |
| #include "context.h" |
| #include "mtypes.h" |
| #include "fbobject.h" |
| #include "framebuffer.h" |
| #include "renderbuffer.h" |
| |
| |
| |
| /** |
| * Compute/set the _DepthMax field for the given framebuffer. |
| * This value depends on the Z buffer resolution. |
| */ |
| static void |
| compute_depth_max(struct gl_framebuffer *fb) |
| { |
| if (fb->Visual.depthBits == 0) { |
| /* Special case. Even if we don't have a depth buffer we need |
| * good values for DepthMax for Z vertex transformation purposes |
| * and for per-fragment fog computation. |
| */ |
| fb->_DepthMax = (1 << 16) - 1; |
| } |
| else if (fb->Visual.depthBits < 32) { |
| fb->_DepthMax = (1 << fb->Visual.depthBits) - 1; |
| } |
| else { |
| /* Special case since shift values greater than or equal to the |
| * number of bits in the left hand expression's type are undefined. |
| */ |
| fb->_DepthMax = 0xffffffff; |
| } |
| fb->_DepthMaxF = (GLfloat) fb->_DepthMax; |
| fb->_MRD = 1.0; /* Minimum resolvable depth value, for polygon offset */ |
| } |
| |
| |
| /** |
| * Create and initialize a gl_framebuffer object. |
| * This is intended for creating _window_system_ framebuffers, not generic |
| * framebuffer objects ala GL_EXT_framebuffer_object. |
| * |
| * \sa _mesa_new_framebuffer |
| */ |
| struct gl_framebuffer * |
| _mesa_create_framebuffer(const GLvisual *visual) |
| { |
| struct gl_framebuffer *fb = CALLOC_STRUCT(gl_framebuffer); |
| assert(visual); |
| if (fb) { |
| _mesa_initialize_framebuffer(fb, visual); |
| } |
| return fb; |
| } |
| |
| |
| /** |
| * Allocate a new gl_framebuffer object. |
| * This is the default function for ctx->Driver.NewFramebuffer(). |
| * This is for allocating user-created framebuffers, not window-system |
| * framebuffers! |
| * \sa _mesa_create_framebuffer |
| */ |
| struct gl_framebuffer * |
| _mesa_new_framebuffer(GLcontext *ctx, GLuint name) |
| { |
| struct gl_framebuffer *fb; |
| assert(name != 0); |
| fb = CALLOC_STRUCT(gl_framebuffer); |
| if (fb) { |
| fb->Name = name; |
| fb->RefCount = 1; |
| fb->Delete = _mesa_destroy_framebuffer; |
| fb->ColorDrawBuffer[0] = GL_COLOR_ATTACHMENT0_EXT; |
| fb->_ColorDrawBufferMask[0] = BUFFER_BIT_COLOR0; |
| fb->ColorReadBuffer = GL_COLOR_ATTACHMENT0_EXT; |
| fb->_ColorReadBufferMask = BUFFER_BIT_COLOR0; |
| fb->Delete = _mesa_destroy_framebuffer; |
| } |
| return fb; |
| } |
| |
| |
| /** |
| * Initialize a gl_framebuffer object. |
| * \sa _mesa_create_framebuffer |
| */ |
| void |
| _mesa_initialize_framebuffer(struct gl_framebuffer *fb, const GLvisual *visual) |
| { |
| assert(fb); |
| assert(visual); |
| |
| _mesa_bzero(fb, sizeof(struct gl_framebuffer)); |
| |
| /* save the visual */ |
| fb->Visual = *visual; |
| |
| /* Init glRead/DrawBuffer state */ |
| if (visual->doubleBufferMode) { |
| fb->ColorDrawBuffer[0] = GL_BACK; |
| fb->_ColorDrawBufferMask[0] = BUFFER_BIT_BACK_LEFT; |
| fb->ColorReadBuffer = GL_BACK; |
| fb->_ColorReadBufferMask = BUFFER_BIT_BACK_LEFT; |
| } |
| else { |
| fb->ColorDrawBuffer[0] = GL_FRONT; |
| fb->_ColorDrawBufferMask[0] = BUFFER_BIT_FRONT_LEFT; |
| fb->ColorReadBuffer = GL_FRONT; |
| fb->_ColorReadBufferMask = BUFFER_BIT_FRONT_LEFT; |
| } |
| |
| fb->Delete = _mesa_destroy_framebuffer; |
| |
| compute_depth_max(fb); |
| } |
| |
| |
| /** |
| * 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 |
| _mesa_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) { |
| if (fb->Visual.rgbMode) { |
| assert(fb->Visual.redBits == fb->Visual.greenBits); |
| assert(fb->Visual.redBits == fb->Visual.blueBits); |
| _mesa_add_color_renderbuffers(NULL, fb, |
| fb->Visual.redBits, |
| fb->Visual.alphaBits, |
| frontLeft, backLeft, |
| frontRight, backRight); |
| } |
| else { |
| _mesa_add_color_index_renderbuffers(NULL, fb, |
| fb->Visual.indexBits, |
| frontLeft, backLeft, |
| frontRight, backRight); |
| } |
| } |
| |
| if (depth) { |
| assert(fb->Visual.depthBits > 0); |
| _mesa_add_depth_renderbuffer(NULL, fb, fb->Visual.depthBits); |
| } |
| |
| if (stencil) { |
| assert(fb->Visual.stencilBits > 0); |
| _mesa_add_stencil_renderbuffer(NULL, fb, fb->Visual.stencilBits); |
| } |
| |
| if (accum) { |
| assert(fb->Visual.rgbMode); |
| assert(fb->Visual.accumRedBits > 0); |
| assert(fb->Visual.accumGreenBits > 0); |
| assert(fb->Visual.accumBlueBits > 0); |
| _mesa_add_accum_renderbuffer(NULL, fb, |
| fb->Visual.accumRedBits, |
| fb->Visual.accumGreenBits, |
| fb->Visual.accumBlueBits, |
| fb->Visual.accumAlphaBits); |
| } |
| |
| if (aux) { |
| assert(fb->Visual.rgbMode); |
| assert(fb->Visual.numAuxBuffers > 0); |
| _mesa_add_aux_renderbuffers(NULL, fb, fb->Visual.redBits, |
| fb->Visual.numAuxBuffers); |
| } |
| |
| #if 1 |
| if (alpha) { |
| assert(fb->Visual.rgbMode); |
| assert(fb->Visual.alphaBits > 0); |
| _mesa_add_alpha_renderbuffers(NULL, fb, fb->Visual.alphaBits, |
| frontLeft, backLeft, |
| frontRight, backRight); |
| } |
| #endif |
| |
| #if 0 |
| if (multisample) { |
| /* maybe someday */ |
| } |
| #endif |
| } |
| |
| |
| /** |
| * Deallocate buffer and everything attached to it. |
| */ |
| void |
| _mesa_destroy_framebuffer(struct gl_framebuffer *buffer) |
| { |
| if (buffer) { |
| _mesa_free_framebuffer_data(buffer); |
| FREE(buffer); |
| } |
| } |
| |
| |
| /** |
| * Free all the data hanging off the given gl_framebuffer, but don't free |
| * the gl_framebuffer object itself. |
| */ |
| void |
| _mesa_free_framebuffer_data(struct gl_framebuffer *fb) |
| { |
| GLuint i; |
| |
| assert(fb); |
| |
| for (i = 0; i < BUFFER_COUNT; i++) { |
| struct gl_renderbuffer_attachment *att = &fb->Attachment[i]; |
| if (att->Type == GL_RENDERBUFFER_EXT && att->Renderbuffer) { |
| struct gl_renderbuffer *rb = att->Renderbuffer; |
| rb->RefCount--; |
| if (rb->RefCount == 0) { |
| rb->Delete(rb); |
| } |
| } |
| att->Type = GL_NONE; |
| att->Renderbuffer = NULL; |
| } |
| } |
| |
| |
| /** |
| * Resize the given framebuffer's renderbuffers to the new width and height. |
| * This should only be used for window-system framebuffers, not |
| * user-created renderbuffers (i.e. made with GL_EXT_framebuffer_object). |
| * This will typically be called via ctx->Driver.ResizeBuffers() |
| */ |
| void |
| _mesa_resize_framebuffer(GLcontext *ctx, struct gl_framebuffer *fb, |
| GLuint width, GLuint height) |
| { |
| GLuint i; |
| |
| /* For window system framebuffers, Name is zero */ |
| assert(fb->Name == 0); |
| |
| for (i = 0; i < BUFFER_COUNT; i++) { |
| struct gl_renderbuffer_attachment *att = &fb->Attachment[i]; |
| if (att->Type == GL_RENDERBUFFER_EXT && att->Renderbuffer) { |
| struct gl_renderbuffer *rb = att->Renderbuffer; |
| /* only resize if size is changing */ |
| if (rb->Width != width || rb->Height != height) { |
| if (rb->AllocStorage(ctx, rb, rb->InternalFormat, width, height)) { |
| rb->Width = width; |
| rb->Height = height; |
| } |
| else { |
| _mesa_error(ctx, GL_OUT_OF_MEMORY, "Resizing framebuffer"); |
| } |
| } |
| } |
| } |
| |
| fb->Width = width; |
| fb->Height = height; |
| } |
| |
| |
| /** |
| * Examine all the framebuffer's renderbuffers to update the Width/Height |
| * fields of the framebuffer. If we have renderbuffers with different |
| * sizes, set the framebuffer's width and height to zero. |
| * Note: this is only intended for user-created framebuffers, not |
| * window-system framebuffes. |
| */ |
| static void |
| update_framebuffer_size(struct gl_framebuffer *fb) |
| { |
| GLboolean haveSize = GL_FALSE; |
| GLuint i; |
| |
| /* user-created framebuffers only */ |
| assert(fb->Name); |
| |
| for (i = 0; i < BUFFER_COUNT; i++) { |
| struct gl_renderbuffer_attachment *att = &fb->Attachment[i]; |
| const struct gl_renderbuffer *rb = att->Renderbuffer; |
| if (rb) { |
| if (haveSize) { |
| if (rb->Width != fb->Width && rb->Height != fb->Height) { |
| /* size mismatch! */ |
| fb->Width = 0; |
| fb->Height = 0; |
| return; |
| } |
| } |
| else { |
| fb->Width = rb->Width; |
| fb->Height = rb->Height; |
| haveSize = GL_TRUE; |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * Update the context's current drawing buffer's Xmin, Xmax, Ymin, Ymax fields. |
| * These values are computed from the buffer's width and height and |
| * the scissor box, if it's enabled. |
| * \param ctx the GL context. |
| */ |
| void |
| _mesa_update_draw_buffer_bounds(GLcontext *ctx) |
| { |
| struct gl_framebuffer *buffer = ctx->DrawBuffer; |
| |
| if (buffer->Name) { |
| /* user-created framebuffer size depends on the renderbuffers */ |
| update_framebuffer_size(buffer); |
| } |
| |
| buffer->_Xmin = 0; |
| buffer->_Ymin = 0; |
| buffer->_Xmax = buffer->Width; |
| buffer->_Ymax = buffer->Height; |
| |
| if (ctx->Scissor.Enabled) { |
| if (ctx->Scissor.X > buffer->_Xmin) { |
| buffer->_Xmin = ctx->Scissor.X; |
| } |
| if (ctx->Scissor.Y > buffer->_Ymin) { |
| buffer->_Ymin = ctx->Scissor.Y; |
| } |
| if (ctx->Scissor.X + ctx->Scissor.Width < buffer->_Xmax) { |
| buffer->_Xmax = ctx->Scissor.X + ctx->Scissor.Width; |
| } |
| if (ctx->Scissor.Y + ctx->Scissor.Height < buffer->_Ymax) { |
| buffer->_Ymax = ctx->Scissor.Y + ctx->Scissor.Height; |
| } |
| /* finally, check for empty region */ |
| if (buffer->_Xmin > buffer->_Xmax) { |
| buffer->_Xmin = buffer->_Xmax; |
| } |
| if (buffer->_Ymin > buffer->_Ymax) { |
| buffer->_Ymin = buffer->_Ymax; |
| } |
| } |
| |
| ASSERT(buffer->_Xmin <= buffer->_Xmax); |
| ASSERT(buffer->_Ymin <= buffer->_Ymax); |
| } |
| |
| |
| /** |
| * The glGet queries of the framebuffer red/green/blue size, stencil size, |
| * etc. are satisfied by the fields of ctx->DrawBuffer->Visual. These can |
| * change depending on the renderbuffer bindings. This function update's |
| * the given framebuffer's Visual from the current renderbuffer bindings. |
| * This is only intended for user-created framebuffers. |
| */ |
| void |
| _mesa_update_framebuffer_visual(struct gl_framebuffer *fb) |
| { |
| assert(fb->Name != 0); |
| |
| _mesa_bzero(&fb->Visual, sizeof(fb->Visual)); |
| fb->Visual.rgbMode = GL_TRUE; |
| |
| if (fb->Attachment[BUFFER_FRONT_LEFT].Renderbuffer) { |
| fb->Visual.redBits |
| = fb->Attachment[BUFFER_FRONT_LEFT].Renderbuffer->ComponentSizes[0]; |
| fb->Visual.greenBits |
| = fb->Attachment[BUFFER_FRONT_LEFT].Renderbuffer->ComponentSizes[1]; |
| fb->Visual.blueBits |
| = fb->Attachment[BUFFER_FRONT_LEFT].Renderbuffer->ComponentSizes[2]; |
| fb->Visual.alphaBits |
| = fb->Attachment[BUFFER_FRONT_LEFT].Renderbuffer->ComponentSizes[3]; |
| fb->Visual.rgbBits |
| = fb->Visual.redBits + fb->Visual.greenBits + fb->Visual.blueBits; |
| fb->Visual.floatMode = GL_FALSE; |
| } |
| |
| if (fb->Attachment[BUFFER_DEPTH].Renderbuffer) { |
| fb->Visual.haveDepthBuffer = GL_TRUE; |
| fb->Visual.depthBits |
| = fb->Attachment[BUFFER_DEPTH].Renderbuffer->ComponentSizes[0]; |
| } |
| |
| if (fb->Attachment[BUFFER_STENCIL].Renderbuffer) { |
| fb->Visual.haveStencilBuffer = GL_TRUE; |
| fb->Visual.stencilBits |
| = fb->Attachment[BUFFER_STENCIL].Renderbuffer->ComponentSizes[0]; |
| } |
| |
| compute_depth_max(fb); |
| } |
| |
| |
| /** |
| * Given a framebuffer and a buffer bit (like BUFFER_BIT_FRONT_LEFT), return |
| * the corresponding renderbuffer. |
| */ |
| static struct gl_renderbuffer * |
| get_renderbuffer(struct gl_framebuffer *fb, GLuint bufferBit) |
| { |
| GLuint index; |
| for (index = 0; index < BUFFER_COUNT; index++) { |
| if ((1 << index) == bufferBit) { |
| return fb->Attachment[index].Renderbuffer; |
| } |
| } |
| _mesa_problem(NULL, "Bad bufferBit in get_renderbuffer"); |
| return NULL; |
| } |
| |
| |
| /** |
| * Update state related to the current draw/read framebuffers. |
| * If the current framebuffer is user-created, make sure it's complete. |
| */ |
| void |
| _mesa_update_framebuffer(GLcontext *ctx) |
| { |
| struct gl_framebuffer *fb = ctx->DrawBuffer; |
| GLuint output; |
| |
| /* Completeness only matters for user-created framebuffers */ |
| if (fb->Name != 0) |
| _mesa_test_framebuffer_completeness(ctx, fb); |
| |
| /* |
| * Update the list of drawing renderbuffer pointers. |
| * Later, when we're rendering we'll loop from 0 to _NumColorDrawBuffers |
| * writing colors. We have a loop because glDrawBuffer(GL_FRONT_AND_BACK) |
| * can specify writing to two or four color buffers. |
| */ |
| for (output = 0; output < ctx->Const.MaxDrawBuffers; output++) { |
| GLuint bufferMask = fb->_ColorDrawBufferMask[output]; |
| GLuint count = 0; |
| GLuint bufferBit; |
| /* for each bit that's set in the bufferMask... */ |
| for (bufferBit = 1; bufferMask; bufferBit <<= 1) { |
| if (bufferBit & bufferMask) { |
| struct gl_renderbuffer *rb = get_renderbuffer(fb, bufferBit); |
| if (rb) { |
| fb->_ColorDrawBuffers[output][count] = rb; |
| fb->_ColorDrawBit[output][count] = bufferBit; |
| count++; |
| } |
| else { |
| _mesa_warning(ctx, "DrawBuffer names a missing buffer!"); |
| } |
| bufferMask &= ~bufferBit; |
| } |
| } |
| fb->_NumColorDrawBuffers[output] = count; |
| } |
| |
| /* |
| * Update the read renderbuffer pointer. |
| * Unlike the DrawBuffer, we can only read from one (or zero) color buffers. |
| */ |
| if (fb->_ColorReadBufferMask == 0x0) |
| fb->_ColorReadBuffer = NULL; /* legal! */ |
| else |
| fb->_ColorReadBuffer = get_renderbuffer(fb, fb->_ColorReadBufferMask); |
| |
| compute_depth_max(fb); |
| } |