stash: virtual-framebuffer work in progress

TODO:

would it be worth caching the glScissor associated with a framebuffer so
that when we switch between framebuffers (for virtual framebuffers) we
can cheaply restore the correct glScissor so we don't need to hit the
clip_stack_flush code.

If we know the stencil buffer matches the current clip stack, we know
any clip planes remain constant

as we rotate between framebuffers, we are going to want to set a
transient offset viewport but don't want that to mark the clip state as
changed

when drawing a primitive on multiple framebuffers we should always check
what the current scissor is to see if we can trivially avoid drawing to
multiple framebuffers.
This commit is contained in:
Robert Bragg 2011-11-14 18:33:53 +00:00
parent 2469065904
commit a32b7f1db4
8 changed files with 538 additions and 4 deletions

View File

@ -90,6 +90,7 @@ cogl_public_h = \
$(srcdir)/cogl-primitive.h \
$(srcdir)/cogl-clip-state.h \
$(srcdir)/cogl-framebuffer.h \
$(srcdir)/cogl-virtual-framebuffer.h \
$(srcdir)/cogl-onscreen.h \
$(srcdir)/cogl-clutter.h \
$(srcdir)/cogl.h \
@ -309,6 +310,8 @@ cogl_sources_c = \
$(srcdir)/cogl-framebuffer-private.h \
$(srcdir)/cogl-framebuffer.c \
$(srcdir)/cogl-onscreen-private.h \
$(srcdir)/cogl-virtual-framebuffer-private.h \
$(srcdir)/cogl-virtual-framebuffer.c \
$(srcdir)/cogl-onscreen.c \
$(srcdir)/cogl-profile.h \
$(srcdir)/cogl-profile.c \

View File

@ -41,7 +41,8 @@
typedef enum _CoglFramebufferType {
COGL_FRAMEBUFFER_TYPE_ONSCREEN,
COGL_FRAMEBUFFER_TYPE_OFFSCREEN
COGL_FRAMEBUFFER_TYPE_OFFSCREEN,
COGL_FRAMEBUFFER_TYPE_VIRTUAL
} CoglFramebufferType;
typedef struct
@ -115,6 +116,11 @@ struct _CoglFramebuffer
CoglClipState clip_state;
/* The stencil buffer matches what's required for this
* clip stack. NULL means the stencil buffer contents
* are undefined. */
CoglClipStack *clip_stack_of_current_stencil;
gboolean dirty_bitmasks;
int red_bits;
int blue_bits;

View File

@ -35,6 +35,8 @@
#include "cogl-util.h"
#include "cogl-texture-private.h"
#include "cogl-framebuffer-private.h"
#include "cogl-virtual-framebuffer-private.h"
#include "cogl-virtual-framebuffer.h"
#include "cogl-onscreen-template-private.h"
#include "cogl-clip-stack.h"
#include "cogl-journal-private.h"
@ -137,7 +139,8 @@ _cogl_is_framebuffer (void *object)
return FALSE;
return obj->klass->type == _cogl_object_onscreen_get_type ()
|| obj->klass->type == _cogl_object_offscreen_get_type ();
|| obj->klass->type == _cogl_object_offscreen_get_type ()
|| obj->klass->type == _cogl_object_virtual_framebuffer_get_type ();
}
void
@ -171,6 +174,7 @@ _cogl_framebuffer_init (CoglFramebuffer *framebuffer,
/* Initialise the clip stack */
_cogl_clip_state_init (&framebuffer->clip_state);
framebuffer->clip_stack_of_current_stencil = NULL;
framebuffer->journal = _cogl_journal_new ();
@ -413,6 +417,15 @@ cogl_framebuffer_clear4f (CoglFramebuffer *framebuffer,
_cogl_framebuffer_clear_without_flush4f (framebuffer, buffers,
red, green, blue, alpha);
/* We are clobbering the stencil state so we can no longer assume it
* will match any clip_stack.
*
* Note: although NULL is a valid clip_stack value that's ok because
* in that case it doesn't matter what the stencil buffer contains.
*/
if (buffers & COGL_BUFFER_BIT_STENCIL)
framebuffer->clip_stack_of_current_stencil = NULL;
/* This is a debugging variable used to visually display the quad
* batches from the journal. It is reset here to increase the
* chances of getting the same colours for each frame during an
@ -532,6 +545,7 @@ cogl_framebuffer_set_viewport (CoglFramebuffer *framebuffer,
framebuffer->viewport_width = width;
framebuffer->viewport_height = height;
#warning "XXX: I think we also need to dirty the clip state here too"
if (framebuffer->context->current_draw_buffer == framebuffer)
framebuffer->context->current_draw_buffer_changes |=
COGL_FRAMEBUFFER_STATE_VIEWPORT;
@ -1327,6 +1341,8 @@ bind_gl_framebuffer (CoglContext *ctx,
GLenum target,
CoglFramebuffer *framebuffer)
{
_COGL_RETURN_IF_FAIL (!cogl_is_virtual_framebuffer (framebuffer));
if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN)
GE (ctx, glBindFramebuffer (target,
COGL_OFFSCREEN (framebuffer)->fbo_handle));
@ -1513,6 +1529,8 @@ _cogl_framebuffer_flush_clip_state (CoglFramebuffer *framebuffer)
{
CoglClipStack *stack = _cogl_clip_state_get_stack (&framebuffer->clip_state);
_cogl_clip_stack_flush (stack, framebuffer);
#warning "should we take a reference here?"
framebuffer->clip_stack_of_current_stencil = stack;
}
static void
@ -1601,7 +1619,8 @@ _cogl_framebuffer_flush_state (CoglFramebuffer *draw_buffer,
unsigned long differences;
int bit;
_COGL_RETURN_IF_FAIL (!cogl_is_virtual_framebuffer (framebuffer));
_COGL_RETURN_IF_FAIL (!cogl_is_virtual_framebuffer (draw_buffer));
_COGL_RETURN_IF_FAIL (!cogl_is_virtual_framebuffer (read_buffer));
/* We can assume that any state that has changed for the current
* framebuffer is different to the currently flushed value. */

View File

@ -0,0 +1,289 @@
/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2011 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, see <http://www.gnu.org/licenses/>.
*
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cogl-util.h"
#include "cogl-framebuffer-private.h"
#include "cogl-virtual-framebuffer-private.h"
#include "cogl-virtual-framebuffer.h"
#include "cogl-context-private.h"
#include "cogl-object-private.h"
#include <glib.h>
static void
_cogl_virtual_framebuffer_free (CoglVirtualFramebuffer *virtual_framebuffer);
COGL_OBJECT_DEFINE (VirtualFramebuffer, virtual_framebuffer);
CoglVirtualFramebuffer *
cogl_virtual_framebuffer_new (CoglContext *context,
int width,
int height)
{
CoglVirtualFramebuffer *virtual_framebuffer =
g_new0 (CoglVirtualFramebuffer, 1);
_cogl_framebuffer_init (COGL_FRAMEBUFFER (virtual_framebuffer),
context,
COGL_FRAMEBUFFER_TYPE_VIRTUAL,
COGL_PIXEL_FORMAT_RGBA_8888, /* arbitrary */
width,
height);
COGL_FRAMEBUFFER (virtual_framebuffer)->allocated = TRUE;
return _cogl_virtual_framebuffer_object_new (virtual_framebuffer);
}
void
cogl_virtual_framebuffer_add_slice (CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice,
int sample_x,
int sample_y,
int sample_width,
int sample_height,
int virtual_x,
int virtual_y)
{
CoglFramebufferSlice *new_slice = g_new0 (CoglFramebufferSlice, 1);
new_slice->framebuffer = cogl_object_ref (slice);
new_slice->sample_region[0] = sample_x;
new_slice->sample_region[1] = sample_y;
if (sample_width == -1)
sample_width = cogl_framebuffer_get_width (slice);
new_slice->sample_region[2] = sample_width;
if (sample_height == -1)
sample_height = cogl_framebuffer_get_height (slice);
new_slice->sample_region[3] = sample_height;
new_slice->virtual_x = virtual_x;
new_slice->virtual_y = virtual_y;
if (virtual_framebuffer->slices == NULL)
{
CoglFramebuffer *fb = COGL_FRAMEBUFFER (virtual_framebuffer);
fb->format = cogl_framebuffer_get_color_format (fb);
}
virtual_framebuffer->slices =
g_list_prepend (virtual_framebuffer->slices, new_slice);
}
static void
_cogl_framebuffer_slice_free (CoglFramebufferSlice *slice)
{
cogl_object_unref (slice->framebuffer);
g_free (slice);
}
static void
_cogl_virtual_framebuffer_free (CoglVirtualFramebuffer *virtual_framebuffer)
{
CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (virtual_framebuffer);
GList *l;
for (l = virtual_framebuffer->slices; l; l = l->next)
_cogl_framebuffer_slice_free (l->data);
g_list_free (virtual_framebuffer->slices);
/* Chain up to parent */
_cogl_framebuffer_free (framebuffer);
g_free (virtual_framebuffer);
}
static CoglFramebufferSlice *
lookup_slice (CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice)
{
GList *l;
for (l = virtual_framebuffer->slices; l; l = l->next)
{
CoglFramebufferSlice *current_slice = l->data;
if (current_slice->framebuffer == slice)
return current_slice;
}
return NULL;
}
void
cogl_virtual_framebuffer_remove_slice (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice)
{
GList *l;
for (l = virtual_framebuffer->slices; l; l = l->next)
{
CoglFramebufferSlice *current_slice = l->data;
if (current_slice->framebuffer == slice)
{
_cogl_framebuffer_slice_free (current_slice);
virtual_framebuffer->slices =
g_list_remove_list (virtual_framebuffer->slices, l);
return;
}
}
g_warn_if_reached ();
}
void
cogl_virtual_framebuffer_move_slice (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice,
int virtual_x,
int virtual_y)
{
CoglFramebufferSlice *match = lookup_slice (virtual_framebuffer, slice);
_COGL_RETURN_IF_FAIL (match);
/* XXX: do we need to flush anything here? */
match->virtual_x = virtual_x;
match->virtual_y = virtual_y;
}
void
cogl_virtual_framebuffer_set_slice_sample_region (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice,
int x,
int y,
int width,
int height)
{
CoglFramebufferSlice *match = lookup_slice (virtual_framebuffer, slice);
_COGL_RETURN_IF_FAIL (match);
/* XXX: do we need to flush anything here? */
match->sample_region[0] = x;
match->sample_region[1] = y;
if (width == -1)
width = cogl_framebuffer_get_width (slice);
match->sample_region[2] = width;
if (height == -1)
height = cogl_framebuffer_get_height (slice);
match->sample_region[3] = height;
}
int
cogl_virtual_framebuffer_get_slice_sample_x (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice)
{
CoglFramebufferSlice *match = lookup_slice (virtual_framebuffer, slice);
_COGL_RETURN_VAL_IF_FAIL (match, 0);
/* XXX: do we need to flush anything here? */
return match->sample_region[0];
}
int
cogl_virtual_framebuffer_get_slice_sample_y (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice)
{
CoglFramebufferSlice *match = lookup_slice (virtual_framebuffer, slice);
_COGL_RETURN_VAL_IF_FAIL (match, 0);
/* XXX: do we need to flush anything here? */
return match->sample_region[1];
}
int
cogl_virtual_framebuffer_get_slice_sample_width (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice)
{
CoglFramebufferSlice *match = lookup_slice (virtual_framebuffer, slice);
_COGL_RETURN_VAL_IF_FAIL (match, 0);
/* XXX: do we need to flush anything here? */
return match->sample_region[2];
}
int
cogl_virtual_framebuffer_get_slice_sample_height (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice)
{
CoglFramebufferSlice *match = lookup_slice (virtual_framebuffer, slice);
_COGL_RETURN_VAL_IF_FAIL (match, 0);
/* XXX: do we need to flush anything here? */
return match->sample_region[3];
}
void
cogl_virtual_framebuffer_set_size (CoglVirtualFramebuffer *virtual_framebuffer,
int width,
int height)
{
CoglFramebuffer *fb = COGL_FRAMEBUFFER (virtual_framebuffer);
/* XXX: do we need to flush anything here? */
fb->width = width;
fb->height = height;
}
void
_cogl_virtual_framebuffer_foreach (CoglVirtualFramebuffer *virtual_framebuffer,
CoglVirtialFramebufferCallback callback,
void *user_data)
{
GList *l;
gboolean cont = TRUE;
for (l = virtual_framebuffer->slices; l && cont; l = l->next)
{
CoglFramebufferSlice *slice = l->data;
cont = callback (virtual_framebuffer,
slice->framebuffer,
slice->sample_region,
slice->virtual_x,
slice->virtual_y,
user_data);
}
}

View File

@ -0,0 +1,118 @@
/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2011 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, see
* <http://www.gnu.org/licenses/>.
*
*
* Authors:
* Robert Bragg <robert@linux.intel.com>
*/
#if !defined(__COGL_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
#error "Only <cogl/cogl.h> can be included directly."
#endif
#ifndef _COGL_VIRTUAL_FRAMEBUFFER_H_
#define _COGL_VIRTUAL_FRAMEBUFFER_H_
#include <cogl/cogl-context.h>
#include <cogl/cogl-framebuffer.h>
#include <glib.h>
G_BEGIN_DECLS
typedef struct _CoglVirtualFramebuffer CoglVirtualFramebuffer;
#define COGL_VIRTUAL_FRAMEBUFFER(X) ((CoglVirtualFramebuffer *)(X))
#define cogl_is_virtual_framebuffer cogl_is_virtual_framebuffer_EXP
/**
* cogl_is_virtual_framebuffer:
* @object: A pointer to a #CoglObject
*
* Gets whether the given object implements the virtual framebuffer
* interface.
*
* Return value: %TRUE if object implements the
* #CoglVirtualFramebuffer interface %FALSE otherwise.
*
* Since: 1.10
* Stability: Unstable
*/
gboolean
cogl_is_virtual_framebuffer (void *object);
CoglVirtualFramebuffer *
cogl_virtual_framebuffer_new (CoglContext *context,
int width,
int height);
void
cogl_virtual_framebuffer_add_slice (CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice,
int sample_x,
int sample_y,
int sample_width,
int sample_height,
int virtual_x,
int virtual_y);
void
cogl_virtual_framebuffer_remove_slice (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice);
void
cogl_virtual_framebuffer_move_slice (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice,
int virtual_x,
int virtual_y);
void
cogl_virtual_framebuffer_set_slice_sample_region (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice,
int x,
int y,
int width,
int height);
int
cogl_virtual_framebuffer_get_slice_sample_x (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice);
int
cogl_virtual_framebuffer_get_slice_sample_y (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice);
int
cogl_virtual_framebuffer_get_slice_sample_width (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice);
int
cogl_virtual_framebuffer_get_slice_sample_height (
CoglVirtualFramebuffer *virtual_framebuffer,
CoglFramebuffer *slice);
void
cogl_virtual_framebuffer_set_size (CoglVirtualFramebuffer *virtual_framebuffer,
int width,
int height);
G_END_DECLS
#endif /* _COGL_VIRTUAL_FRAMEBUFFER_H_ */

View File

@ -95,6 +95,7 @@ typedef struct _CoglFramebuffer CoglFramebuffer;
#include <cogl/cogl-pipeline-state.h>
#include <cogl/cogl-pipeline-layer-state.h>
#include <cogl/cogl-framebuffer.h>
#include <cogl/cogl-virtual-framebuffer.h>
#include <cogl/cogl-onscreen.h>
#if defined (COGL_HAS_EGL_PLATFORM_WAYLAND_SUPPORT)
#include <cogl/cogl-wayland-renderer.h>

View File

@ -18,7 +18,7 @@ common_ldadd = \
$(COGL_DEP_LIBS) \
$(top_builddir)/cogl/libcogl.la
programs = cogl-hello cogl-info cogl-msaa
programs = cogl-hello cogl-info cogl-msaa cogl-virtual-framebuffer
examples_datadir = $(pkgdatadir)/examples-data
examples_data_DATA =
@ -28,6 +28,8 @@ cogl_info_SOURCES = cogl-info.c
cogl_info_LDADD = $(common_ldadd)
cogl_msaa_SOURCES = cogl-msaa.c
cogl_msaa_LDADD = $(common_ldadd)
cogl_virtual_framebuffer_SOURCES = cogl-virtual-framebuffer.c
cogl_virtual_framebuffer_LDADD = $(common_ldadd)
if BUILD_COGL_PANGO
programs += cogl-crate

View File

@ -0,0 +1,96 @@
#include <cogl/cogl.h>
#include <glib.h>
#include <stdio.h>
CoglColor black;
#define TEX_WIDTH 300
#define TEX_HEIGHT 220
int
main (int argc, char **argv)
{
CoglContext *ctx;
CoglOnscreen *onscreen;
CoglTexture2D *textures[4];
CoglHandle offscreens[4];
CoglVirtualFramebuffer *vfb;
CoglFramebuffer *fb;
GError *error = NULL;
CoglVertexP2C4 triangle_vertices[] = {
{0, 0.7, 0xff, 0x00, 0x00, 0x80},
{-0.7, -0.7, 0x00, 0xff, 0x00, 0xff},
{0.7, -0.7, 0x00, 0x00, 0xff, 0xff}
};
CoglPrimitive *triangle;
int i;
ctx = cogl_context_new (NULL, &error);
if (!ctx) {
fprintf (stderr, "Failed to create context: %s\n", error->message);
return 1;
}
for (i = 0; i < 4; i++)
{
textures[i] = cogl_texture_2d_new_with_size (ctx,
300, 220,
COGL_PIXEL_FORMAT_RGBA_8888,
&error);
if (!textures[i])
{
fprintf (stderr, "Failed to allocated texture: %s\n", error->message);
return 1;
}
offscreens[i] =
cogl_offscreen_new_to_texture (COGL_TEXTURE (textures[i]));
}
vfb = cogl_virtual_framebuffer_new (ctx, 640, 480);
cogl_virtual_framebuffer_add_slice (vfb, offscreens[0],
0, 0, -1, -1,
10, 10);
cogl_virtual_framebuffer_add_slice (vfb, offscreens[1],
0, 0, -1, -1,
330, 10);
cogl_virtual_framebuffer_add_slice (vfb, offscreens[2],
0, 0, -1, -1,
10, 250);
cogl_virtual_framebuffer_add_slice (vfb, offscreens[3],
0, 0, -1, -1,
330, 250);
onscreen = cogl_onscreen_new (ctx, 640, 480);
/* Eventually there will be an implicit allocate on first use so this
* will become optional... */
fb = COGL_FRAMEBUFFER (onscreen);
if (!cogl_framebuffer_allocate (fb, &error)) {
fprintf (stderr, "Failed to allocate framebuffer: %s\n", error->message);
return 1;
}
cogl_onscreen_show (onscreen);
cogl_push_framebuffer (COGL_FRAMEBUFFER (vfb));
triangle = cogl_primitive_new_p2c4 (COGL_VERTICES_MODE_TRIANGLES,
3, triangle_vertices);
for (;;) {
cogl_clear (&black, COGL_BUFFER_BIT_COLOR);
cogl_primitive_draw (triangle);
/* Now draw the slices of the virtual framebuffer onscreen */
cogl_set_source_texture (COGL_TEXTURE (textures[0]));
cogl_rectangle (10, 10, 10 + TEX_WIDTH, 10 + TEX_HEIGHT);
cogl_set_source_texture (COGL_TEXTURE (textures[1]));
cogl_rectangle (330, 10, 330 + TEX_WIDTH, 10 + TEX_HEIGHT);
cogl_set_source_texture (COGL_TEXTURE (textures[2]));
cogl_rectangle (10, 250, 10 + TEX_WIDTH, 250 + TEX_HEIGHT);
cogl_set_source_texture (COGL_TEXTURE (textures[3]));
cogl_rectangle (330, 250, 330 + TEX_WIDTH, 250 + TEX_HEIGHT);
cogl_framebuffer_swap_buffers (fb);
}
return 0;
}