/* * 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 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 "depthstencil.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->ColorDrawBuffer[0] = GL_COLOR_ATTACHMENT0_EXT; fb->_ColorDrawBufferMask[0] = BUFFER_BIT_COLOR0; fb->ColorReadBuffer = GL_COLOR_ATTACHMENT0_EXT; fb->_ColorReadBufferIndex = BUFFER_COLOR0; fb->Delete = _mesa_destroy_framebuffer; } return fb; } /** * Initialize a gl_framebuffer object. Typically used to initialize * window system-created framebuffers, not user-created framebuffers. * \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->_ColorReadBufferIndex = BUFFER_BACK_LEFT; } else { fb->ColorDrawBuffer[0] = GL_FRONT; fb->_ColorDrawBufferMask[0] = BUFFER_BIT_FRONT_LEFT; fb->ColorReadBuffer = GL_FRONT; fb->_ColorReadBufferIndex = BUFFER_FRONT_LEFT; } fb->Delete = _mesa_destroy_framebuffer; fb->_Status = GL_FRAMEBUFFER_COMPLETE_EXT; compute_depth_max(fb); } /** * Deallocate buffer and everything attached to it. * Typically called via the gl_framebuffer->Delete() method. */ void _mesa_destroy_framebuffer(struct gl_framebuffer *fb) { if (fb) { _mesa_free_framebuffer_data(fb); _mesa_free(fb); } } /** * 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->Renderbuffer) { struct gl_renderbuffer *rb = att->Renderbuffer; rb->RefCount--; if (rb->RefCount == 0) { rb->Delete(rb); } } att->Type = GL_NONE; att->Renderbuffer = NULL; } if (fb->_DepthBuffer) { struct gl_renderbuffer *rb = fb->_DepthBuffer; rb->RefCount--; if (rb->RefCount <= 0) { rb->Delete(rb); } fb->_DepthBuffer = NULL; } if (fb->_StencilBuffer) { struct gl_renderbuffer *rb = fb->_StencilBuffer; rb->RefCount--; if (rb->RefCount <= 0) { rb->Delete(rb); } fb->_StencilBuffer = 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() or directly * from a device driver. * * \note it's possible for ctx to be null since a window can be resized * without a currently bound rendering context. */ 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; /* to update scissor / window bounds */ if (ctx) ctx->NewState |= _NEW_BUFFERS; } /** * 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 updates * the given framebuffer's Visual from the current renderbuffer bindings. * This is only intended for user-created framebuffers. * * Also note: ctx->DrawBuffer->Visual.depthBits might not equal * ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer.DepthBits. * The former one is used to convert floating point depth values into * integer Z values. */ void _mesa_update_framebuffer_visual(struct gl_framebuffer *fb) { GLuint i; assert(fb->Name != 0); _mesa_bzero(&fb->Visual, sizeof(fb->Visual)); fb->Visual.rgbMode = GL_TRUE; /* assume this */ /* find first RGB or CI renderbuffer */ for (i = 0; i < BUFFER_COUNT; i++) { if (fb->Attachment[i].Renderbuffer) { const struct gl_renderbuffer *rb = fb->Attachment[i].Renderbuffer; if (rb->_BaseFormat == GL_RGBA || rb->_BaseFormat == GL_RGB) { fb->Visual.redBits = rb->RedBits; fb->Visual.greenBits = rb->GreenBits; fb->Visual.blueBits = rb->BlueBits; fb->Visual.alphaBits = rb->AlphaBits; fb->Visual.rgbBits = fb->Visual.redBits + fb->Visual.greenBits + fb->Visual.blueBits; fb->Visual.floatMode = GL_FALSE; break; } else if (rb->_BaseFormat == GL_COLOR_INDEX) { fb->Visual.indexBits = rb->IndexBits; fb->Visual.rgbMode = GL_FALSE; break; } } } if (fb->Attachment[BUFFER_DEPTH].Renderbuffer) { fb->Visual.haveDepthBuffer = GL_TRUE; fb->Visual.depthBits = fb->Attachment[BUFFER_DEPTH].Renderbuffer->DepthBits; } if (fb->Attachment[BUFFER_STENCIL].Renderbuffer) { fb->Visual.haveStencilBuffer = GL_TRUE; fb->Visual.stencilBits = fb->Attachment[BUFFER_STENCIL].Renderbuffer->StencilBits; } compute_depth_max(fb); } /** * Helper function for _mesa_update_framebuffer(). * Set the actual depth renderbuffer for the given framebuffer. * Take care of reference counts, etc. */ static void set_depth_renderbuffer(struct gl_framebuffer *fb, struct gl_renderbuffer *rb) { if (fb->_DepthBuffer) { fb->_DepthBuffer->RefCount--; if (fb->_DepthBuffer->RefCount <= 0) { fb->_DepthBuffer->Delete(fb->_DepthBuffer); } } fb->_DepthBuffer = rb; if (rb) rb->RefCount++; } /** * \sa set_depth_renderbuffer. */ static void set_stencil_renderbuffer(struct gl_framebuffer *fb, struct gl_renderbuffer *rb) { if (fb->_StencilBuffer) { fb->_StencilBuffer->RefCount--; if (fb->_StencilBuffer->RefCount <= 0) { fb->_StencilBuffer->Delete(fb->_StencilBuffer); } } fb->_StencilBuffer = rb; if (rb) rb->RefCount++; } /** * Update state related to the current draw/read framebuffers. * Specifically, update these framebuffer fields: * _ColorDrawBuffers * _NumColorDrawBuffers * _ColorReadBuffer * _DepthBuffer * _StencilBuffer * If the current framebuffer is user-created, make sure it's complete. * The following functions can effect this state: glReadBuffer, * glDrawBuffer, glDrawBuffersARB, glFramebufferRenderbufferEXT, * glRenderbufferStorageEXT. */ 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); _mesa_update_framebuffer_visual(fb); } /* * Update the list of color drawing renderbuffer pointers. * Later, when we're rendering we'll loop from 0 to _NumColorDrawBuffers * writing colors. We need the inner loop here because * glDrawBuffer(GL_FRONT_AND_BACK) can specify writing to two or four * color buffers (for example). */ for (output = 0; output < ctx->Const.MaxDrawBuffers; output++) { GLbitfield bufferMask = fb->_ColorDrawBufferMask[output]; GLuint count = 0; GLuint i; for (i = 0; bufferMask && i < BUFFER_COUNT; i++) { const GLuint bufferBit = 1 << i; if (bufferBit & bufferMask) { struct gl_renderbuffer *rb = fb->Attachment[i].Renderbuffer; if (rb) { fb->_ColorDrawBuffers[output][count] = rb; count++; } else { /*_mesa_warning(ctx, "DrawBuffer names a missing buffer!\n");*/ } bufferMask &= ~bufferBit; } } fb->_NumColorDrawBuffers[output] = count; } /* * Update the color read renderbuffer pointer. * Unlike the DrawBuffer, we can only read from one (or zero) color buffers. */ if (fb->_ColorReadBufferIndex == -1) { fb->_ColorReadBuffer = NULL; /* legal! */ } else { ASSERT(fb->_ColorReadBufferIndex >= 0); ASSERT(fb->_ColorReadBufferIndex < BUFFER_COUNT); fb->_ColorReadBuffer = fb->Attachment[fb->_ColorReadBufferIndex].Renderbuffer; } /* * Deal with GL_DEPTH_STENCIL renderbuffer(s) attached to the depth * and/or stencil attachment points. If either of the DEPTH or STENCIL * renderbuffer attachments are GL_DEPTH_STENCIL buffers, we need to set * up depth/stencil renderbuffer wrappers. */ { struct gl_renderbuffer *depthRb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; struct gl_renderbuffer *stencilRb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; if (depthRb && depthRb->_BaseFormat == GL_DEPTH_STENCIL_EXT) { /* The attached depth buffer is a GL_DEPTH_STENCIL renderbuffer */ if (!fb->_DepthBuffer || fb->_DepthBuffer->Wrapped != depthRb) { /* need to update wrapper */ struct gl_renderbuffer *wrapper = _mesa_new_z24_renderbuffer_wrapper(ctx, depthRb); set_depth_renderbuffer(fb, wrapper); assert(fb->_DepthBuffer->Wrapped == depthRb); } } else { /* depthRb may be null */ set_depth_renderbuffer(fb, depthRb); } if (stencilRb && stencilRb->_BaseFormat == GL_DEPTH_STENCIL_EXT) { /* The attached stencil buffer is a GL_DEPTH_STENCIL renderbuffer */ if (!fb->_StencilBuffer || fb->_StencilBuffer->Wrapped != stencilRb) { /* need to update wrapper */ struct gl_renderbuffer *wrapper = _mesa_new_s8_renderbuffer_wrapper(ctx, stencilRb); set_stencil_renderbuffer(fb, wrapper); assert(fb->_StencilBuffer->Wrapped == stencilRb); } } else { /* stencilRb may be null */ set_stencil_renderbuffer(fb, stencilRb); } } compute_depth_max(fb); } /** * Check if the renderbuffer for a read operation (glReadPixels, glCopyPixels, * glCopyTex[Sub]Image, etc. exists. * \param format a basic image format such as GL_RGB, GL_RGBA, GL_ALPHA, * GL_DEPTH_COMPONENT, etc. or GL_COLOR, GL_DEPTH, GL_STENCIL. * \return GL_TRUE if buffer exists, GL_FALSE otherwise */ GLboolean _mesa_source_buffer_exists(GLcontext *ctx, GLenum format) { const struct gl_renderbuffer_attachment *att = ctx->ReadBuffer->Attachment; if (ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { return GL_FALSE; } switch (format) { case GL_COLOR: case GL_RED: case GL_GREEN: case GL_BLUE: case GL_ALPHA: case GL_LUMINANCE: case GL_LUMINANCE_ALPHA: case GL_INTENSITY: case GL_RGB: case GL_BGR: case GL_RGBA: case GL_BGRA: case GL_ABGR_EXT: case GL_COLOR_INDEX: if (ctx->ReadBuffer->_ColorReadBuffer == NULL) { return GL_FALSE; } break; case GL_DEPTH: case GL_DEPTH_COMPONENT: if (!att[BUFFER_DEPTH].Renderbuffer) { return GL_FALSE; } ASSERT(att[BUFFER_DEPTH].Renderbuffer->DepthBits > 0); break; case GL_STENCIL: case GL_STENCIL_INDEX: if (!att[BUFFER_STENCIL].Renderbuffer) { return GL_FALSE; } ASSERT(att[BUFFER_STENCIL].Renderbuffer->StencilBits > 0); break; case GL_DEPTH_STENCIL_EXT: if (!att[BUFFER_DEPTH].Renderbuffer || !att[BUFFER_STENCIL].Renderbuffer) { return GL_FALSE; } ASSERT(att[BUFFER_DEPTH].Renderbuffer->DepthBits > 0); ASSERT(att[BUFFER_STENCIL].Renderbuffer->StencilBits > 0); break; default: _mesa_problem(ctx, "Unexpected format 0x%x in _mesa_source_buffer_exists", format); return GL_FALSE; } /* OK */ return GL_TRUE; } /** * As above, but for drawing operations. * XXX code do some code merging w/ above function. */ GLboolean _mesa_dest_buffer_exists(GLcontext *ctx, GLenum format) { const struct gl_renderbuffer_attachment *att = ctx->ReadBuffer->Attachment; if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { return GL_FALSE; } switch (format) { case GL_COLOR: case GL_RED: case GL_GREEN: case GL_BLUE: case GL_ALPHA: case GL_LUMINANCE: case GL_LUMINANCE_ALPHA: case GL_INTENSITY: case GL_RGB: case GL_BGR: case GL_RGBA: case GL_BGRA: case GL_ABGR_EXT: case GL_COLOR_INDEX: /* nothing special */ break; case GL_DEPTH: case GL_DEPTH_COMPONENT: if (!att[BUFFER_DEPTH].Renderbuffer) { return GL_FALSE; } ASSERT(att[BUFFER_DEPTH].Renderbuffer->DepthBits > 0); break; case GL_STENCIL: case GL_STENCIL_INDEX: if (!att[BUFFER_STENCIL].Renderbuffer) { return GL_FALSE; } ASSERT(att[BUFFER_STENCIL].Renderbuffer->StencilBits > 0); break; case GL_DEPTH_STENCIL_EXT: if (!att[BUFFER_DEPTH].Renderbuffer || !att[BUFFER_STENCIL].Renderbuffer) { return GL_FALSE; } ASSERT(att[BUFFER_DEPTH].Renderbuffer->DepthBits > 0); ASSERT(att[BUFFER_STENCIL].Renderbuffer->StencilBits > 0); break; default: _mesa_problem(ctx, "Unexpected format 0x%x in _mesa_source_buffer_exists", format); return GL_FALSE; } /* OK */ return GL_TRUE; }