#include "glheader.h"
#include "imports.h"
#include "buffers.h"
#include "context.h"
#include "depthstencil.h"
#include "mtypes.h"
#include "fbobject.h"
#include "framebuffer.h"
#include "renderbuffer.h"
#include "texobj.h"
static void
compute_depth_max(struct gl_framebuffer *fb)
{
if (fb->Visual.depthBits == 0) {
fb->_DepthMax = (1 << 16) - 1;
}
else if (fb->Visual.depthBits < 32) {
fb->_DepthMax = (1 << fb->Visual.depthBits) - 1;
}
else {
fb->_DepthMax = 0xffffffff;
}
fb->_DepthMaxF = (GLfloat) fb->_DepthMax;
fb->_MRD = 1.0 / fb->_DepthMaxF;
}
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;
}
struct gl_framebuffer *
_mesa_new_framebuffer(GLcontext *ctx, GLuint name)
{
struct gl_framebuffer *fb;
(void) ctx;
assert(name != 0);
fb = CALLOC_STRUCT(gl_framebuffer);
if (fb) {
fb->Name = name;
fb->RefCount = 1;
fb->_NumColorDrawBuffers = 1;
fb->ColorDrawBuffer[0] = GL_COLOR_ATTACHMENT0_EXT;
fb->_ColorDrawBufferIndexes[0] = BUFFER_COLOR0;
fb->ColorReadBuffer = GL_COLOR_ATTACHMENT0_EXT;
fb->_ColorReadBufferIndex = BUFFER_COLOR0;
fb->Delete = _mesa_destroy_framebuffer;
}
return fb;
}
void
_mesa_initialize_framebuffer(struct gl_framebuffer *fb, const GLvisual *visual)
{
assert(fb);
assert(visual);
_mesa_bzero(fb, sizeof(struct gl_framebuffer));
_glthread_INIT_MUTEX(fb->Mutex);
fb->RefCount = 1;
fb->Visual = *visual;
if (visual->doubleBufferMode) {
fb->_NumColorDrawBuffers = 1;
fb->ColorDrawBuffer[0] = GL_BACK;
fb->_ColorDrawBufferIndexes[0] = BUFFER_BACK_LEFT;
fb->ColorReadBuffer = GL_BACK;
fb->_ColorReadBufferIndex = BUFFER_BACK_LEFT;
}
else {
fb->_NumColorDrawBuffers = 1;
fb->ColorDrawBuffer[0] = GL_FRONT;
fb->_ColorDrawBufferIndexes[0] = BUFFER_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);
}
void
_mesa_destroy_framebuffer(struct gl_framebuffer *fb)
{
if (fb) {
_mesa_free_framebuffer_data(fb);
_mesa_free(fb);
}
}
void
_mesa_free_framebuffer_data(struct gl_framebuffer *fb)
{
GLuint i;
assert(fb);
assert(fb->RefCount == 0);
_glthread_DESTROY_MUTEX(fb->Mutex);
for (i = 0; i < BUFFER_COUNT; i++) {
struct gl_renderbuffer_attachment *att = &fb->Attachment[i];
if (att->Renderbuffer) {
_mesa_reference_renderbuffer(&att->Renderbuffer, NULL);
}
if (att->Texture) {
_mesa_reference_texobj(&att->Texture, NULL);
}
ASSERT(!att->Renderbuffer);
ASSERT(!att->Texture);
att->Type = GL_NONE;
}
_mesa_reference_renderbuffer(&fb->_DepthBuffer, NULL);
_mesa_reference_renderbuffer(&fb->_StencilBuffer, NULL);
}
void
_mesa_reference_framebuffer(struct gl_framebuffer **ptr,
struct gl_framebuffer *fb)
{
assert(ptr);
if (*ptr == fb) {
return;
}
if (*ptr) {
_mesa_unreference_framebuffer(ptr);
}
assert(!*ptr);
assert(fb);
_glthread_LOCK_MUTEX(fb->Mutex);
fb->RefCount++;
_glthread_UNLOCK_MUTEX(fb->Mutex);
*ptr = fb;
}
void
_mesa_unreference_framebuffer(struct gl_framebuffer **fb)
{
assert(fb);
if (*fb) {
GLboolean deleteFlag = GL_FALSE;
_glthread_LOCK_MUTEX((*fb)->Mutex);
ASSERT((*fb)->RefCount > 0);
(*fb)->RefCount--;
deleteFlag = ((*fb)->RefCount == 0);
_glthread_UNLOCK_MUTEX((*fb)->Mutex);
if (deleteFlag)
(*fb)->Delete(*fb);
*fb = NULL;
}
}
void
_mesa_resize_framebuffer(GLcontext *ctx, struct gl_framebuffer *fb,
GLuint width, GLuint height)
{
GLuint i;
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;
if (rb->Width != width || rb->Height != height) {
if (rb->AllocStorage(ctx, rb, rb->InternalFormat, width, height)) {
ASSERT(rb->Width == width);
ASSERT(rb->Height == height);
}
else {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "Resizing framebuffer");
}
}
}
}
if (fb->_DepthBuffer) {
struct gl_renderbuffer *rb = fb->_DepthBuffer;
if (rb->Width != width || rb->Height != height) {
if (!rb->AllocStorage(ctx, rb, rb->InternalFormat, width, height)) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "Resizing framebuffer");
}
}
}
if (fb->_StencilBuffer) {
struct gl_renderbuffer *rb = fb->_StencilBuffer;
if (rb->Width != width || rb->Height != height) {
if (!rb->AllocStorage(ctx, rb, rb->InternalFormat, width, height)) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "Resizing framebuffer");
}
}
}
fb->Width = width;
fb->Height = height;
if (ctx) {
_mesa_update_draw_buffer_bounds(ctx);
ctx->NewState |= _NEW_BUFFERS;
}
}
static void
update_framebuffer_size(struct gl_framebuffer *fb)
{
GLboolean haveSize = GL_FALSE;
GLuint i;
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) {
fb->Width = 0;
fb->Height = 0;
return;
}
}
else {
fb->Width = rb->Width;
fb->Height = rb->Height;
haveSize = GL_TRUE;
}
}
}
}
void
_mesa_update_draw_buffer_bounds(GLcontext *ctx)
{
struct gl_framebuffer *buffer = ctx->DrawBuffer;
if (!buffer)
return;
if (buffer->Name) {
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;
}
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);
}
void
_mesa_update_framebuffer_visual(struct gl_framebuffer *fb)
{
GLuint i;
_mesa_bzero(&fb->Visual, sizeof(fb->Visual));
fb->Visual.rgbMode = GL_TRUE;
#if 0
if (fb->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
return;
}
#endif
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;
}
if (fb->Attachment[BUFFER_ACCUM].Renderbuffer) {
fb->Visual.haveAccumBuffer = GL_TRUE;
fb->Visual.accumRedBits
= fb->Attachment[BUFFER_ACCUM].Renderbuffer->RedBits;
fb->Visual.accumGreenBits
= fb->Attachment[BUFFER_ACCUM].Renderbuffer->GreenBits;
fb->Visual.accumBlueBits
= fb->Attachment[BUFFER_ACCUM].Renderbuffer->BlueBits;
fb->Visual.accumAlphaBits
= fb->Attachment[BUFFER_ACCUM].Renderbuffer->AlphaBits;
}
compute_depth_max(fb);
}
void
_mesa_update_depth_buffer(GLcontext *ctx,
struct gl_framebuffer *fb,
GLuint attIndex)
{
struct gl_renderbuffer *depthRb;
ASSERT(attIndex == BUFFER_DEPTH);
depthRb = fb->Attachment[attIndex].Renderbuffer;
if (depthRb && depthRb->_ActualFormat == GL_DEPTH24_STENCIL8_EXT) {
if (!fb->_DepthBuffer
|| fb->_DepthBuffer->Wrapped != depthRb
|| fb->_DepthBuffer->_BaseFormat != GL_DEPTH_COMPONENT) {
struct gl_renderbuffer *wrapper
= _mesa_new_z24_renderbuffer_wrapper(ctx, depthRb);
_mesa_reference_renderbuffer(&fb->_DepthBuffer, wrapper);
ASSERT(fb->_DepthBuffer->Wrapped == depthRb);
}
}
else {
_mesa_reference_renderbuffer(&fb->_DepthBuffer, depthRb);
}
}
void
_mesa_update_stencil_buffer(GLcontext *ctx,
struct gl_framebuffer *fb,
GLuint attIndex)
{
struct gl_renderbuffer *stencilRb;
ASSERT(attIndex == BUFFER_DEPTH ||
attIndex == BUFFER_STENCIL);
stencilRb = fb->Attachment[attIndex].Renderbuffer;
if (stencilRb && stencilRb->_ActualFormat == GL_DEPTH24_STENCIL8_EXT) {
if (!fb->_StencilBuffer
|| fb->_StencilBuffer->Wrapped != stencilRb
|| fb->_StencilBuffer->_BaseFormat != GL_STENCIL_INDEX) {
struct gl_renderbuffer *wrapper
= _mesa_new_s8_renderbuffer_wrapper(ctx, stencilRb);
_mesa_reference_renderbuffer(&fb->_StencilBuffer, wrapper);
ASSERT(fb->_StencilBuffer->Wrapped == stencilRb);
}
}
else {
_mesa_reference_renderbuffer(&fb->_StencilBuffer, stencilRb);
}
}
static void
update_color_draw_buffers(GLcontext *ctx, struct gl_framebuffer *fb)
{
GLuint output;
fb->_ColorDrawBuffers[0] = NULL;
for (output = 0; output < fb->_NumColorDrawBuffers; output++) {
GLint buf = fb->_ColorDrawBufferIndexes[output];
if (buf >= 0) {
fb->_ColorDrawBuffers[output] = fb->Attachment[buf].Renderbuffer;
}
else {
fb->_ColorDrawBuffers[output] = NULL;
}
}
}
static void
update_color_read_buffer(GLcontext *ctx, struct gl_framebuffer *fb)
{
(void) ctx;
if (fb->_ColorReadBufferIndex == -1 ||
fb->DeletePending ||
fb->Width == 0 ||
fb->Height == 0) {
fb->_ColorReadBuffer = NULL;
}
else {
ASSERT(fb->_ColorReadBufferIndex >= 0);
ASSERT(fb->_ColorReadBufferIndex < BUFFER_COUNT);
fb->_ColorReadBuffer
= fb->Attachment[fb->_ColorReadBufferIndex].Renderbuffer;
}
}
static void
update_framebuffer(GLcontext *ctx, struct gl_framebuffer *fb)
{
if (fb->Name == 0) {
if (fb->ColorDrawBuffer[0] != ctx->Color.DrawBuffer[0]) {
_mesa_drawbuffers(ctx, ctx->Const.MaxDrawBuffers,
ctx->Color.DrawBuffer, NULL);
}
if (fb->ColorReadBuffer != ctx->Pixel.ReadBuffer) {
}
}
else {
_mesa_test_framebuffer_completeness(ctx, fb);
_mesa_update_framebuffer_visual(fb);
}
update_color_draw_buffers(ctx, fb);
update_color_read_buffer(ctx, fb);
_mesa_update_depth_buffer(ctx, fb, BUFFER_DEPTH);
_mesa_update_stencil_buffer(ctx, fb, BUFFER_STENCIL);
compute_depth_max(fb);
}
void
_mesa_update_framebuffer(GLcontext *ctx)
{
struct gl_framebuffer *drawFb = ctx->DrawBuffer;
struct gl_framebuffer *readFb = ctx->ReadBuffer;
update_framebuffer(ctx, drawFb);
if (readFb != drawFb)
update_framebuffer(ctx, readFb);
}
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;
}
return GL_TRUE;
}
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:
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;
}
return GL_TRUE;
}