1
0
Fork 0
mutter-performance-source/cogl/cogl-draw-buffer.c
Robert Bragg e4fe655521 [draw-buffer] Adds cogl_draw_buffer_get_{width,height} API
Simply adds missing API to query the width and height of any Cogl draw buffer.
2009-11-04 03:23:20 +00:00

595 lines
17 KiB
C

/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2007,2008,2009 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cogl.h"
#include "cogl-internal.h"
#include "cogl-context.h"
#include "cogl-handle.h"
#include "cogl-util.h"
#include "cogl-texture-private.h"
#include "cogl-draw-buffer-private.h"
#include "cogl-clip-stack.h"
#ifdef HAVE_COGL_GLES2
#include "../gles/cogl-gles2-wrapper.h"
#else
#define glGenRenderbuffers ctx->drv.pf_glGenRenderbuffers
#define glDeleteRenderbuffers ctx->drv.pf_glDeleteRenderbuffers
#define glBindRenderbuffer ctx->drv.pf_glBindRenderbuffer
#define glRenderbufferStorage ctx->drv.pf_glRenderbufferStorage
#define glGenFramebuffers ctx->drv.pf_glGenFramebuffers
#define glBindFramebuffer ctx->drv.pf_glBindFramebuffer
#define glFramebufferTexture2D ctx->drv.pf_glFramebufferTexture2D
#define glFramebufferRenderbuffer ctx->drv.pf_glFramebufferRenderbuffer
#define glCheckFramebufferStatus ctx->drv.pf_glCheckFramebufferStatus
#define glDeleteFramebuffers ctx->drv.pf_glDeleteFramebuffers
#endif
#ifndef GL_FRAMEBUFFER
#define GL_FRAMEBUFFER 0x8D40
#endif
#ifndef GL_RENDERBUFFER
#define GL_RENDERBUFFER 0x8D41
#endif
#ifndef GL_STENCIL_ATTACHMENT
#define GL_STENCIL_ATTACHMENT 0x8D00
#endif
#ifndef GL_COLOR_ATTACHMENT0
#define GL_COLOR_ATTACHMENT0 0x8CE0
#endif
#ifndef GL_FRAMEBUFFER_COMPLETE
#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
#endif
#ifndef GL_STENCIL_INDEX8
#define GL_STENCIL_INDEX8 0x8D48
#endif
static void _cogl_draw_buffer_free (CoglDrawBuffer *draw_buffer);
static void _cogl_onscreen_free (CoglOnscreen *onscreen);
static void _cogl_offscreen_free (CoglOffscreen *offscreen);
COGL_HANDLE_DEFINE (Onscreen, onscreen);
COGL_HANDLE_DEFINE (Offscreen, offscreen);
/* XXX:
* The CoglHandle macros don't support any form of inheritance, so for
* now we implement the CoglHandle support for the CoglDrawBuffer
* abstract class manually.
*/
gboolean
cogl_is_draw_buffer (CoglHandle handle)
{
CoglHandleObject *obj = (CoglHandleObject *)handle;
if (handle == COGL_INVALID_HANDLE)
return FALSE;
return obj->klass->type == _cogl_handle_onscreen_get_type ()
|| obj->klass->type == _cogl_handle_offscreen_get_type ();
}
static void
_cogl_draw_buffer_init (CoglDrawBuffer *draw_buffer,
CoglDrawBufferType type,
int width,
int height)
{
draw_buffer->type = type;
draw_buffer->width = width;
draw_buffer->height = height;
draw_buffer->viewport_x = 0;
draw_buffer->viewport_y = 0;
draw_buffer->viewport_width = width;
draw_buffer->viewport_height = height;
draw_buffer->modelview_stack = _cogl_matrix_stack_new ();
draw_buffer->projection_stack = _cogl_matrix_stack_new ();
/* Initialise the clip stack */
_cogl_clip_stack_state_init (&draw_buffer->clip_state);
}
void
_cogl_draw_buffer_free (CoglDrawBuffer *draw_buffer)
{
_cogl_clip_stack_state_destroy (&draw_buffer->clip_state);
_cogl_matrix_stack_destroy (draw_buffer->modelview_stack);
draw_buffer->modelview_stack = NULL;
_cogl_matrix_stack_destroy (draw_buffer->projection_stack);
draw_buffer->projection_stack = NULL;
}
int
_cogl_draw_buffer_get_width (CoglHandle handle)
{
CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle);
return draw_buffer->width;
}
int
_cogl_draw_buffer_get_height (CoglHandle handle)
{
CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle);
return draw_buffer->height;
}
CoglClipStackState *
_cogl_draw_buffer_get_clip_state (CoglHandle handle)
{
CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle);
return &draw_buffer->clip_state;
}
void
_cogl_draw_buffer_set_viewport (CoglHandle handle,
int x,
int y,
int width,
int height)
{
CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle);
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (draw_buffer->viewport_x == x &&
draw_buffer->viewport_y == y &&
draw_buffer->viewport_width == width &&
draw_buffer->viewport_height == height)
return;
_cogl_journal_flush ();
draw_buffer->viewport_x = x;
draw_buffer->viewport_y = y;
draw_buffer->viewport_width = width;
draw_buffer->viewport_height = height;
if (_cogl_get_draw_buffer () == draw_buffer)
ctx->dirty_gl_viewport = TRUE;
}
int
_cogl_draw_buffer_get_viewport_x (CoglHandle handle)
{
CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle);
return draw_buffer->viewport_x;
}
int
_cogl_draw_buffer_get_viewport_y (CoglHandle handle)
{
CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle);
return draw_buffer->viewport_y;
}
int
_cogl_draw_buffer_get_viewport_width (CoglHandle handle)
{
CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle);
return draw_buffer->viewport_width;
}
int
_cogl_draw_buffer_get_viewport_height (CoglHandle handle)
{
CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle);
return draw_buffer->viewport_height;
}
void
_cogl_draw_buffer_get_viewport4fv (CoglHandle handle, int *viewport)
{
CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle);
viewport[0] = draw_buffer->viewport_x;
viewport[1] = draw_buffer->viewport_y;
viewport[2] = draw_buffer->viewport_width;
viewport[3] = draw_buffer->viewport_height;
}
CoglMatrixStack *
_cogl_draw_buffer_get_modelview_stack (CoglHandle handle)
{
CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle);
return draw_buffer->modelview_stack;
}
CoglMatrixStack *
_cogl_draw_buffer_get_projection_stack (CoglHandle handle)
{
CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (handle);
return draw_buffer->projection_stack;
}
CoglHandle
cogl_offscreen_new_to_texture (CoglHandle texhandle)
{
CoglOffscreen *offscreen;
int width;
int height;
GLuint tex_gl_handle;
GLenum tex_gl_target;
GLuint fbo_gl_handle;
GLuint gl_stencil_handle;
GLenum status;
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
if (!cogl_features_available (COGL_FEATURE_OFFSCREEN))
return COGL_INVALID_HANDLE;
/* Make texhandle is a valid texture object */
if (!cogl_is_texture (texhandle))
return COGL_INVALID_HANDLE;
/* The texture must not be sliced */
if (cogl_texture_is_sliced (texhandle))
return COGL_INVALID_HANDLE;
/* Pick the single texture slice width, height and GL id */
width = cogl_texture_get_width (texhandle);
height = cogl_texture_get_height (texhandle);
if (!cogl_texture_get_gl_texture (texhandle, &tex_gl_handle, &tex_gl_target))
return COGL_INVALID_HANDLE;
if (tex_gl_target != GL_TEXTURE_2D)
return COGL_INVALID_HANDLE;
/* Create a renderbuffer for stenciling */
GE (glGenRenderbuffers (1, &gl_stencil_handle));
GE (glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle));
GE (glRenderbufferStorage (GL_RENDERBUFFER, GL_STENCIL_INDEX8,
cogl_texture_get_width (texhandle),
cogl_texture_get_height (texhandle)));
GE (glBindRenderbuffer (GL_RENDERBUFFER, 0));
/* We are about to generate and bind a new fbo, so when next flushing the
* journal, we will need to rebind the current draw buffer... */
ctx->dirty_bound_framebuffer = 1;
/* Generate framebuffer */
glGenFramebuffers (1, &fbo_gl_handle);
GE (glBindFramebuffer (GL_FRAMEBUFFER, fbo_gl_handle));
GE (glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
tex_gl_target, tex_gl_handle, 0));
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, gl_stencil_handle));
/* XXX: The framebuffer_object spec isn't clear in defining whether attaching
* a texture as a renderbuffer with mipmap filtering enabled while the
* mipmaps have not been uploaded should result in an incomplete framebuffer
* object. (different drivers make different decisions)
*
* To avoid an error with drivers that do consider this a problem we
* explicitly set non mipmapped filters here. These will later be reset when
* the texture is actually used for rendering according to the filters set on
* the corresponding CoglMaterial.
*/
_cogl_texture_set_filters (texhandle, GL_NEAREST, GL_NEAREST);
/* Make sure it's complete */
status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
/* Stencil renderbuffers aren't always supported. Try again
without the stencil buffer */
GE (glFramebufferRenderbuffer (GL_FRAMEBUFFER,
GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER,
0));
GE (glDeleteRenderbuffers (1, &gl_stencil_handle));
gl_stencil_handle = 0;
status = glCheckFramebufferStatus (GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
/* Still failing, so give up */
GE (glDeleteFramebuffers (1, &fbo_gl_handle));
GE (glBindFramebuffer (GL_FRAMEBUFFER, 0));
return COGL_INVALID_HANDLE;
}
}
offscreen = g_new0 (CoglOffscreen, 1);
_cogl_draw_buffer_init (COGL_DRAW_BUFFER (offscreen),
COGL_DRAW_BUFFER_TYPE_OFFSCREEN,
width,
height);
offscreen->fbo_handle = fbo_gl_handle;
offscreen->gl_stencil_handle = gl_stencil_handle;
/* XXX: Can we get a away with removing this? It wasn't documented, and most
* users of the API are hopefully setting up the modelview from scratch
* anyway */
#if 0
cogl_matrix_translate (&draw_buffer->modelview, -1.0f, -1.0f, 0.0f);
cogl_matrix_scale (&draw_buffer->modelview,
2.0f / draw_buffer->width, 2.0f / draw_buffer->height, 1.0f);
#endif
return _cogl_offscreen_handle_new (offscreen);
}
static void
_cogl_offscreen_free (CoglOffscreen *offscreen)
{
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
/* Chain up to parent */
_cogl_draw_buffer_free (COGL_DRAW_BUFFER (offscreen));
if (offscreen->gl_stencil_handle)
GE (glDeleteRenderbuffers (1, &offscreen->gl_stencil_handle));
GE (glDeleteFramebuffers (1, &offscreen->fbo_handle));
g_free (offscreen);
}
CoglHandle
_cogl_onscreen_new (void)
{
CoglOnscreen *onscreen;
/* XXX: Until we have full winsys support in Cogl then we can't fully
* implement CoglOnscreen draw buffers, since we can't, e.g. keep track of
* the window size. */
onscreen = g_new0 (CoglOnscreen, 1);
_cogl_draw_buffer_init (COGL_DRAW_BUFFER (onscreen),
COGL_DRAW_BUFFER_TYPE_ONSCREEN,
0xdeadbeef, /* width */
0xdeadbeef); /* height */
return _cogl_onscreen_handle_new (onscreen);
}
static void
_cogl_onscreen_free (CoglOnscreen *onscreen)
{
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
/* Chain up to parent */
_cogl_draw_buffer_free (COGL_DRAW_BUFFER (onscreen));
g_free (onscreen);
}
void
_cogl_onscreen_clutter_backend_set_size (int width, int height)
{
CoglDrawBuffer *draw_buffer;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
draw_buffer = COGL_DRAW_BUFFER (ctx->window_buffer);
if (draw_buffer->width == width && draw_buffer->height == height)
return;
draw_buffer->width = width;
draw_buffer->height = height;
/* We'll need to recalculate the GL viewport state derived
* from the Cogl viewport */
ctx->dirty_gl_viewport = 1;
}
GSList *
_cogl_create_draw_buffer_stack (void)
{
GSList *stack = NULL;
CoglDrawBufferStackEntry *entry;
entry = g_slice_new0 (CoglDrawBufferStackEntry);
entry->target = COGL_WINDOW_BUFFER;
entry->draw_buffer = COGL_INVALID_HANDLE;
return g_slist_prepend (stack, entry);
}
void
_cogl_free_draw_buffer_stack (GSList *stack)
{
GSList *l;
for (l = stack; l != NULL; l = l->next)
{
CoglDrawBufferStackEntry *entry = l->data;
CoglDrawBuffer *draw_buffer = COGL_DRAW_BUFFER (entry->draw_buffer);
if (draw_buffer->type == COGL_DRAW_BUFFER_TYPE_OFFSCREEN)
_cogl_offscreen_free (COGL_OFFSCREEN (draw_buffer));
else
_cogl_onscreen_free (COGL_ONSCREEN (draw_buffer));
}
g_slist_free (stack);
}
/* XXX: The target argument is redundant; when we break API, we should
* remove it! */
void
cogl_set_draw_buffer (CoglBufferTarget target, CoglHandle handle)
{
CoglDrawBuffer *draw_buffer = NULL;
CoglDrawBufferStackEntry *entry;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
_cogl_journal_flush ();
g_assert (ctx->draw_buffer_stack != NULL);
entry = ctx->draw_buffer_stack->data;
if (target == COGL_WINDOW_BUFFER)
handle = ctx->window_buffer;
else if (!cogl_is_draw_buffer (handle))
return;
draw_buffer = COGL_DRAW_BUFFER (handle);
if (entry->draw_buffer != draw_buffer)
{
entry->target = target;
ctx->dirty_bound_framebuffer = 1;
ctx->dirty_gl_viewport = 1;
if (draw_buffer != COGL_INVALID_HANDLE)
cogl_handle_ref (draw_buffer);
if (entry->draw_buffer != COGL_INVALID_HANDLE)
cogl_handle_unref (entry->draw_buffer);
entry->draw_buffer = draw_buffer;
/* We've effectively just switched the current modelview and
* projection matrix stacks and clip state so we need to dirty
* them to ensure they get flushed for the next batch of geometry
* we flush */
_cogl_matrix_stack_dirty (draw_buffer->modelview_stack);
_cogl_matrix_stack_dirty (draw_buffer->projection_stack);
_cogl_clip_stack_state_dirty (&draw_buffer->clip_state);
}
}
CoglHandle
_cogl_get_draw_buffer (void)
{
CoglDrawBufferStackEntry *entry;
_COGL_GET_CONTEXT (ctx, NULL);
g_assert (ctx->draw_buffer_stack);
entry = ctx->draw_buffer_stack->data;
return entry->draw_buffer;
}
void
cogl_push_draw_buffer (void)
{
CoglDrawBufferStackEntry *old;
CoglDrawBufferStackEntry *entry;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
entry = g_slice_new0 (CoglDrawBufferStackEntry);
g_assert (ctx->draw_buffer_stack);
old = ctx->draw_buffer_stack->data;
*entry = *old;
cogl_handle_ref (entry->draw_buffer);
ctx->draw_buffer_stack =
g_slist_prepend (ctx->draw_buffer_stack, entry);
}
void
cogl_pop_draw_buffer (void)
{
CoglDrawBufferStackEntry *to_pop;
CoglDrawBufferStackEntry *to_restore;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
g_assert (ctx->draw_buffer_stack != NULL);
to_pop = ctx->draw_buffer_stack->data;
to_restore = ctx->draw_buffer_stack->next->data;
cogl_set_draw_buffer (to_restore->target, to_restore->draw_buffer);
cogl_handle_unref (to_pop->draw_buffer);
ctx->draw_buffer_stack =
g_slist_remove_link (ctx->draw_buffer_stack,
ctx->draw_buffer_stack);
g_slice_free (CoglDrawBufferStackEntry, to_pop);
}
void
_cogl_draw_buffer_flush_state (CoglHandle handle,
CoglDrawBufferFlushFlags flags)
{
CoglDrawBuffer *draw_buffer;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
draw_buffer = COGL_DRAW_BUFFER (handle);
if (cogl_features_available (COGL_FEATURE_OFFSCREEN) &&
ctx->dirty_bound_framebuffer)
{
if (draw_buffer->type == COGL_DRAW_BUFFER_TYPE_OFFSCREEN)
{
GE (glBindFramebuffer (GL_FRAMEBUFFER,
COGL_OFFSCREEN (draw_buffer)->fbo_handle));
}
else
GE (glBindFramebuffer (GL_FRAMEBUFFER, 0));
ctx->dirty_bound_framebuffer = FALSE;
}
if (ctx->dirty_gl_viewport)
{
/* Convert the Cogl viewport y offset to an OpenGL viewport y offset
* (NB: OpenGL defines its window and viewport origins to be bottom
* left, while Cogl defines them to be top left.) */
int gl_viewport_y = draw_buffer->height -
(draw_buffer->viewport_y + draw_buffer->viewport_height);
GE (glViewport (draw_buffer->viewport_x,
gl_viewport_y,
draw_buffer->viewport_width,
draw_buffer->viewport_height));
ctx->dirty_gl_viewport = FALSE;
}
/* XXX: Flushing clip state may trash the modelview and projection
* matrices so we must do it before flushing the matrices...
*/
_cogl_flush_clip_state (&draw_buffer->clip_state);
if (!(flags & COGL_DRAW_BUFFER_FLUSH_SKIP_MODELVIEW))
_cogl_matrix_stack_flush_to_gl (draw_buffer->modelview_stack,
COGL_MATRIX_MODELVIEW);
_cogl_matrix_stack_flush_to_gl (draw_buffer->projection_stack,
COGL_MATRIX_PROJECTION);
}