summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Bragg <robert@linux.intel.com>2011-08-23 13:55:12 +0100
committerRobert Bragg <robert@linux.intel.com>2011-09-21 22:03:09 +0100
commitdc8c096514d2e74d2bc4174e089a8b9910906d43 (patch)
treeb3d69398d9d89dd7fe93cc604ac1d9a4018708f8
parentba8e4857b81a27f1dee61b7ea8dbf449fd9cf799 (diff)
downloadcogl-dc8c096514d2e74d2bc4174e089a8b9910906d43.tar.gz
offscreen: Adds support for offscreen multisampling
This adds support for multisample rendering to offscreen framebuffers. After an offscreen framebuffer is first instantiated using cogl_offscreen_new_to_texture() it is then possible to use cogl_framebuffer_set_point_samples_per_pixel() to request multisampling before the framebuffer is allocated. This also adds cogl_framebuffer_resolve_samples() for explicitly resolving point samples into pixels. Even though we currently only support the IMG_multisampled_render_to_texture extension which doesn't require an explicit resolve, the plan is to also support the EXT_framebuffer_multisample extension which uses the framebuffer_blit extension to issue an explicit resolve.
-rw-r--r--cogl/cogl-ext-functions.h30
-rw-r--r--cogl/cogl-framebuffer-private.h20
-rw-r--r--cogl/cogl-framebuffer.c536
-rw-r--r--cogl/cogl-framebuffer.h118
4 files changed, 470 insertions, 234 deletions
diff --git a/cogl/cogl-ext-functions.h b/cogl/cogl-ext-functions.h
index 35832d1c..4a85f129 100644
--- a/cogl/cogl-ext-functions.h
+++ b/cogl/cogl-ext-functions.h
@@ -333,18 +333,6 @@ COGL_EXT_FUNCTION (void, glBlitFramebuffer,
GLenum filter))
COGL_EXT_END ()
-COGL_EXT_BEGIN (offscreen_multisample, 255, 255,
- 0, /* not in either GLES */
- "EXT\0",
- "framebuffer_multisample\0")
-COGL_EXT_FUNCTION (void, glRenderbufferStorageMultisample,
- (GLenum target,
- GLsizei samples,
- GLenum internalformat,
- GLsizei width,
- GLsizei height))
-COGL_EXT_END ()
-
/* ARB_fragment_program */
COGL_EXT_BEGIN (arbfp, 255, 255,
0, /* not in either GLES */
@@ -660,3 +648,21 @@ COGL_EXT_FUNCTION (void, glDiscardFramebuffer,
const GLenum *attachments))
COGL_EXT_END ()
+COGL_EXT_BEGIN (multisampled_render_to_texture, 255, 255,
+ 0, /* not in either GLES */
+ "IMG\0",
+ "multisampled_render_to_texture\0")
+COGL_EXT_FUNCTION (void, glRenderbufferStorageMultisample,
+ (GLenum target,
+ GLsizei samples,
+ GLenum internal_format,
+ GLsizei width,
+ GLsizei height))
+COGL_EXT_FUNCTION (void, glFramebufferTexture2DMultisample,
+ (GLenum target,
+ GLenum attachment,
+ GLenum textarget,
+ GLuint texture,
+ GLint level,
+ GLsizei samples))
+COGL_EXT_END ()
diff --git a/cogl/cogl-framebuffer-private.h b/cogl/cogl-framebuffer-private.h
index 98a13ea0..a13f2fb6 100644
--- a/cogl/cogl-framebuffer-private.h
+++ b/cogl/cogl-framebuffer-private.h
@@ -54,6 +54,12 @@ typedef struct
int point_samples_per_pixel;
} CoglFramebufferConfig;
+/* Flags to pass to _cogl_offscreen_new_to_texture_full */
+typedef enum
+{
+ COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL = 1
+} CoglOffscreenFlags;
+
struct _CoglFramebuffer
{
CoglObject _parent;
@@ -127,14 +133,16 @@ typedef struct _CoglOffscreen
GLuint resolve_fbo_handle;
CoglTexture *texture;
+ int texture_level;
+ int texture_level_width;
+ int texture_level_height;
+
+ /* FIXME: _cogl_offscreen_new_to_texture_full should be made to use
+ * fb->config to configure if we want a depth or stencil buffer so
+ * we can get rid of these flags */
+ CoglOffscreenFlags create_flags;
} CoglOffscreen;
-/* Flags to pass to _cogl_offscreen_new_to_texture_full */
-typedef enum
-{
- COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL = 1
-} CoglOffscreenFlags;
-
#define COGL_OFFSCREEN(X) ((CoglOffscreen *)(X))
struct _CoglOnscreen
diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c
index f3a03da4..b70ff400 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -120,6 +120,12 @@ COGL_OBJECT_DEFINE_DEPRECATED_REF_COUNTING (offscreen);
* abstract class manually.
*/
+GQuark
+cogl_framebuffer_error_quark (void)
+{
+ return g_quark_from_static_string ("cogl-framebuffer-error-quark");
+}
+
gboolean
_cogl_is_framebuffer (void *object)
{
@@ -681,144 +687,17 @@ _cogl_framebuffer_init_bits (CoglFramebuffer *framebuffer)
framebuffer->dirty_bitmasks = FALSE;
}
-typedef struct
-{
- CoglTexture *texture;
- unsigned int level;
- unsigned int level_width;
- unsigned int level_height;
-} CoglFramebufferTryFBOData;
-
-static gboolean
-try_creating_fbo (CoglOffscreen *offscreen,
- TryFBOFlags flags,
- CoglFramebufferTryFBOData *data)
-{
- GLuint gl_depth_stencil_handle;
- GLuint gl_depth_handle;
- GLuint gl_stencil_handle;
- GLuint tex_gl_handle;
- GLenum tex_gl_target;
- GLuint fbo_gl_handle;
- GLenum status;
-
- _COGL_GET_CONTEXT (ctx, FALSE);
-
- if (!cogl_texture_get_gl_texture (data->texture,
- &tex_gl_handle, &tex_gl_target))
- return FALSE;
-
- if (tex_gl_target != GL_TEXTURE_2D
-#ifdef HAVE_COGL_GL
- && tex_gl_target != GL_TEXTURE_RECTANGLE_ARB
-#endif
- )
- return FALSE;
-
- /* We are about to generate and bind a new fbo, so we pretend to
- * change framebuffer state so that the old framebuffer will be
- * rebound again before drawing. */
- ctx->dirty_bound_framebuffer = 1;
-
- /* Generate framebuffer */
- ctx->glGenFramebuffers (1, &fbo_gl_handle);
- GE (ctx, glBindFramebuffer (GL_FRAMEBUFFER, fbo_gl_handle));
- offscreen->fbo_handle = fbo_gl_handle;
-
- GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- tex_gl_target, tex_gl_handle, data->level));
-
- if (flags & _TRY_DEPTH_STENCIL)
- {
- /* Create a renderbuffer for depth and stenciling */
- GE (ctx, glGenRenderbuffers (1, &gl_depth_stencil_handle));
- GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_stencil_handle));
- GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_STENCIL,
- data->level_width,
- data->level_height));
- GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, 0));
- GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER,
- GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER,
- gl_depth_stencil_handle));
- GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER,
- GL_DEPTH_ATTACHMENT,
- GL_RENDERBUFFER,
- gl_depth_stencil_handle));
- offscreen->renderbuffers =
- g_slist_prepend (offscreen->renderbuffers,
- GUINT_TO_POINTER (gl_depth_stencil_handle));
- }
-
- if (flags & _TRY_DEPTH)
- {
- GE (ctx, glGenRenderbuffers (1, &gl_depth_handle));
- GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_handle));
- /* For now we just ask for GL_DEPTH_COMPONENT16 since this is all that's
- * available under GLES */
- GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
- data->level_width,
- data->level_height));
- GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, 0));
- GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER,
- GL_DEPTH_ATTACHMENT,
- GL_RENDERBUFFER, gl_depth_handle));
- offscreen->renderbuffers =
- g_slist_prepend (offscreen->renderbuffers,
- GUINT_TO_POINTER (gl_depth_handle));
- }
-
- if (flags & _TRY_STENCIL)
- {
- GE (ctx, glGenRenderbuffers (1, &gl_stencil_handle));
- GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle));
- GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, GL_STENCIL_INDEX8,
- data->level_width,
- data->level_height));
- GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, 0));
- GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER,
- GL_STENCIL_ATTACHMENT,
- GL_RENDERBUFFER, gl_stencil_handle));
- offscreen->renderbuffers =
- g_slist_prepend (offscreen->renderbuffers,
- GUINT_TO_POINTER (gl_stencil_handle));
- }
-
- /* Make sure it's complete */
- status = ctx->glCheckFramebufferStatus (GL_FRAMEBUFFER);
-
- if (status != GL_FRAMEBUFFER_COMPLETE)
- {
- GSList *l;
-
- GE (ctx, glDeleteFramebuffers (1, &fbo_gl_handle));
-
- for (l = offscreen->renderbuffers; l; l = l->next)
- {
- GLuint renderbuffer = GPOINTER_TO_UINT (l->data);
- GE (ctx, glDeleteRenderbuffers (1, &renderbuffer));
- }
-
- g_slist_free (offscreen->renderbuffers);
- offscreen->renderbuffers = NULL;
-
- return FALSE;
- }
-
- return TRUE;
-}
-
CoglHandle
_cogl_offscreen_new_to_texture_full (CoglTexture *texture,
CoglOffscreenFlags create_flags,
unsigned int level)
{
- CoglOffscreen *offscreen;
- static TryFBOFlags flags;
- static gboolean have_working_flags = FALSE;
- unsigned int i;
- CoglFramebufferTryFBOData data;
- gboolean fbo_created;
+ CoglOffscreen *offscreen;
+ CoglFramebuffer *fb;
+ int level_width;
+ int level_height;
+ int i;
+ CoglHandle ret;
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
@@ -833,98 +712,49 @@ _cogl_offscreen_new_to_texture_full (CoglTexture *texture,
if (cogl_texture_is_sliced (texture))
return COGL_INVALID_HANDLE;
- data.texture = texture;
- data.level = level;
-
/* Calculate the size of the texture at this mipmap level to ensure
that it's a valid level */
- data.level_width = cogl_texture_get_width (texture);
- data.level_height = cogl_texture_get_height (texture);
+ level_width = cogl_texture_get_width (texture);
+ level_height = cogl_texture_get_height (texture);
for (i = 0; i < level; i++)
{
/* If neither dimension can be further divided then the level is
invalid */
- if (data.level_width == 1 && data.level_height == 1)
- return COGL_INVALID_HANDLE;
-
- if (data.level_width > 1)
- data.level_width >>= 1;
- if (data.level_height > 1)
- data.level_height >>= 1;
- }
-
- /* 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 CoglPipeline.
- */
- _cogl_texture_set_filters (texture, GL_NEAREST, GL_NEAREST);
-
- offscreen = g_new0 (CoglOffscreen, 1);
- offscreen->texture = texture;
-
- if ((create_flags & COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL))
- fbo_created = try_creating_fbo (offscreen, 0, &data);
- else
- {
- if ((have_working_flags &&
- try_creating_fbo (offscreen, flags, &data)) ||
-#ifdef HAVE_COGL_GL
- (ctx->driver == COGL_DRIVER_GL &&
- try_creating_fbo (offscreen, flags = _TRY_DEPTH_STENCIL, &data)) ||
-#endif
- try_creating_fbo (offscreen, flags = _TRY_DEPTH | _TRY_STENCIL,
- &data) ||
- try_creating_fbo (offscreen, flags = _TRY_STENCIL, &data) ||
- try_creating_fbo (offscreen, flags = _TRY_DEPTH, &data) ||
- try_creating_fbo (offscreen, flags = 0, &data))
+ if (level_width == 1 && level_height == 1)
{
- /* Record that the last set of flags succeeded so that we can
- try that set first next time */
- have_working_flags = TRUE;
- fbo_created = TRUE;
+ g_warning ("Invalid texture level passed to "
+ "_cogl_offscreen_new_to_texture_full");
+ return COGL_INVALID_HANDLE;
}
- else
- fbo_created = FALSE;
- }
-
- if (fbo_created)
- {
- CoglOffscreen *ret;
- CoglFramebuffer *fb = COGL_FRAMEBUFFER (offscreen);
- _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
+ if (level_width > 1)
+ level_width >>= 1;
+ if (level_height > 1)
+ level_height >>= 1;
+ }
- _cogl_framebuffer_init (fb,
- ctx,
- COGL_FRAMEBUFFER_TYPE_OFFSCREEN,
- cogl_texture_get_format (texture),
- data.level_width,
- data.level_height);
+ offscreen = g_new0 (CoglOffscreen, 1);
+ offscreen->texture = cogl_object_ref (texture);
+ offscreen->texture_level = level;
+ offscreen->texture_level_width = level_width;
+ offscreen->texture_level_height = level_height;
+ offscreen->create_flags = create_flags;
- cogl_object_ref (offscreen->texture);
+ fb = COGL_FRAMEBUFFER (offscreen);
- ret = _cogl_offscreen_object_new (offscreen);
- _cogl_texture_associate_framebuffer (texture, COGL_FRAMEBUFFER (ret));
+ _cogl_framebuffer_init (fb,
+ ctx,
+ COGL_FRAMEBUFFER_TYPE_OFFSCREEN,
+ cogl_texture_get_format (texture),
+ level_width,
+ level_height);
- fb->allocated = TRUE;
+ ret = _cogl_offscreen_object_new (offscreen);
- return ret;
- }
- else
- {
- g_free (offscreen);
- /* XXX: This API should probably have been defined to take a GError */
- g_warning ("%s: Failed to create an OpenGL framebuffer", G_STRLOC);
+ _cogl_texture_associate_framebuffer (texture, fb);
- return COGL_INVALID_HANDLE;
- }
+ return ret;
}
CoglHandle
@@ -1028,6 +858,221 @@ cogl_onscreen_new (CoglContext *ctx, int width, int height)
return _cogl_onscreen_object_new (onscreen);
}
+static gboolean
+try_creating_fbo (CoglOffscreen *offscreen,
+ TryFBOFlags flags)
+{
+ CoglFramebuffer *fb = COGL_FRAMEBUFFER (offscreen);
+ CoglContext *ctx = fb->context;
+ GLuint gl_depth_stencil_handle;
+ GLuint gl_depth_handle;
+ GLuint gl_stencil_handle;
+ GLuint tex_gl_handle;
+ GLenum tex_gl_target;
+ GLuint fbo_gl_handle;
+ GLenum status;
+ int n_samples;
+ int height;
+ int width;
+
+ if (!cogl_texture_get_gl_texture (offscreen->texture,
+ &tex_gl_handle, &tex_gl_target))
+ return FALSE;
+
+ if (tex_gl_target != GL_TEXTURE_2D
+#ifdef HAVE_COGL_GL
+ && tex_gl_target != GL_TEXTURE_RECTANGLE_ARB
+#endif
+ )
+ return FALSE;
+
+ if (fb->config.point_samples_per_pixel)
+ {
+ if (!ctx->glFramebufferTexture2DMultisample)
+ return FALSE;
+ n_samples = fb->config.point_samples_per_pixel;
+ }
+ else
+ n_samples = 0;
+
+ width = offscreen->texture_level_width;
+ height = offscreen->texture_level_height;
+
+ /* We are about to generate and bind a new fbo, so we pretend to
+ * change framebuffer state so that the old framebuffer will be
+ * rebound again before drawing. */
+ ctx->dirty_bound_framebuffer = 1;
+
+ /* Generate framebuffer */
+ ctx->glGenFramebuffers (1, &fbo_gl_handle);
+ GE (ctx, glBindFramebuffer (GL_FRAMEBUFFER, fbo_gl_handle));
+ offscreen->fbo_handle = fbo_gl_handle;
+
+ if (n_samples)
+ {
+ GE (ctx, glFramebufferTexture2DMultisample (GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ tex_gl_target, tex_gl_handle,
+ n_samples,
+ offscreen->texture_level));
+ }
+ else
+ GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ tex_gl_target, tex_gl_handle,
+ offscreen->texture_level));
+
+ if (flags & _TRY_DEPTH_STENCIL)
+ {
+ /* Create a renderbuffer for depth and stenciling */
+ GE (ctx, glGenRenderbuffers (1, &gl_depth_stencil_handle));
+ GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_stencil_handle));
+ if (n_samples)
+ GE (ctx, glRenderbufferStorageMultisample (GL_RENDERBUFFER,
+ n_samples,
+ GL_DEPTH_STENCIL,
+ width, height));
+ else
+ GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_STENCIL,
+ width, height));
+ GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, 0));
+ GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER,
+ GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER,
+ gl_depth_stencil_handle));
+ GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER,
+ GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER,
+ gl_depth_stencil_handle));
+ offscreen->renderbuffers =
+ g_slist_prepend (offscreen->renderbuffers,
+ GUINT_TO_POINTER (gl_depth_stencil_handle));
+ }
+
+ if (flags & _TRY_DEPTH)
+ {
+ GE (ctx, glGenRenderbuffers (1, &gl_depth_handle));
+ GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_handle));
+ /* For now we just ask for GL_DEPTH_COMPONENT16 since this is all that's
+ * available under GLES */
+ if (n_samples)
+ GE (ctx, glRenderbufferStorageMultisample (GL_RENDERBUFFER,
+ n_samples,
+ GL_DEPTH_COMPONENT16,
+ width, height));
+ else
+ GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
+ width, height));
+ GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, 0));
+ GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER,
+ GL_DEPTH_ATTACHMENT,
+ GL_RENDERBUFFER, gl_depth_handle));
+ offscreen->renderbuffers =
+ g_slist_prepend (offscreen->renderbuffers,
+ GUINT_TO_POINTER (gl_depth_handle));
+ }
+
+ if (flags & _TRY_STENCIL)
+ {
+ GE (ctx, glGenRenderbuffers (1, &gl_stencil_handle));
+ GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle));
+ if (n_samples)
+ GE (ctx, glRenderbufferStorageMultisample (GL_RENDERBUFFER,
+ n_samples,
+ GL_STENCIL_INDEX8,
+ width, height));
+ else
+ GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, GL_STENCIL_INDEX8,
+ width, height));
+ GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, 0));
+ GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER,
+ GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, gl_stencil_handle));
+ offscreen->renderbuffers =
+ g_slist_prepend (offscreen->renderbuffers,
+ GUINT_TO_POINTER (gl_stencil_handle));
+ }
+
+ /* Make sure it's complete */
+ status = ctx->glCheckFramebufferStatus (GL_FRAMEBUFFER);
+
+ if (status != GL_FRAMEBUFFER_COMPLETE)
+ {
+ GSList *l;
+
+ GE (ctx, glDeleteFramebuffers (1, &fbo_gl_handle));
+
+ for (l = offscreen->renderbuffers; l; l = l->next)
+ {
+ GLuint renderbuffer = GPOINTER_TO_UINT (l->data);
+ GE (ctx, glDeleteRenderbuffers (1, &renderbuffer));
+ }
+
+ g_slist_free (offscreen->renderbuffers);
+ offscreen->renderbuffers = NULL;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+_cogl_offscreen_allocate (CoglOffscreen *offscreen,
+ GError **error)
+{
+ CoglFramebuffer *fb = COGL_FRAMEBUFFER (offscreen);
+ CoglContext *ctx = fb->context;
+ static TryFBOFlags flags;
+ static gboolean have_working_flags = FALSE;
+ gboolean fbo_created;
+
+ /* 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 CoglPipeline.
+ */
+ _cogl_texture_set_filters (offscreen->texture, GL_NEAREST, GL_NEAREST);
+
+ if ((offscreen->create_flags & COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL))
+ fbo_created = try_creating_fbo (offscreen, 0);
+ else
+ {
+ if ((have_working_flags &&
+ try_creating_fbo (offscreen, flags)) ||
+#ifdef HAVE_COGL_GL
+ (ctx->driver == COGL_DRIVER_GL &&
+ try_creating_fbo (offscreen, flags = _TRY_DEPTH_STENCIL)) ||
+#endif
+ try_creating_fbo (offscreen, flags = _TRY_DEPTH | _TRY_STENCIL) ||
+ try_creating_fbo (offscreen, flags = _TRY_STENCIL) ||
+ try_creating_fbo (offscreen, flags = _TRY_DEPTH) ||
+ try_creating_fbo (offscreen, flags = 0))
+ {
+ /* Record that the last set of flags succeeded so that we can
+ try that set first next time */
+ have_working_flags = TRUE;
+ fbo_created = TRUE;
+ }
+ else
+ fbo_created = FALSE;
+ }
+
+ if (!fbo_created)
+ {
+ g_set_error (error, COGL_FRAMEBUFFER_ERROR,
+ COGL_FRAMEBUFFER_ERROR_ALLOCATE,
+ "Failed to create an OpenGL framebuffer object");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
gboolean
cogl_framebuffer_allocate (CoglFramebuffer *framebuffer,
GError **error)
@@ -1038,13 +1083,16 @@ cogl_framebuffer_allocate (CoglFramebuffer *framebuffer,
if (framebuffer->allocated)
return TRUE;
- /* XXX: with the current cogl_offscreen_new_to_texture() API the
- * framebuffer is implicitly allocated before returning. */
- g_return_val_if_fail (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN,
- TRUE);
-
- if (!winsys->onscreen_init (onscreen, error))
- return FALSE;
+ if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
+ {
+ if (!winsys->onscreen_init (onscreen, error))
+ return FALSE;
+ }
+ else
+ {
+ if (!_cogl_offscreen_allocate (COGL_OFFSCREEN (framebuffer), error))
+ return FALSE;
+ }
framebuffer->allocated = TRUE;
@@ -1591,6 +1639,62 @@ cogl_framebuffer_get_color_format (CoglFramebuffer *framebuffer)
return framebuffer->format;
}
+void
+cogl_framebuffer_set_point_samples_per_pixel (CoglFramebuffer *framebuffer,
+ int samples_per_pixel)
+{
+ g_return_if_fail (!framebuffer->allocated);
+
+ framebuffer->config.point_samples_per_pixel = samples_per_pixel;
+}
+
+void
+cogl_framebuffer_resolve_samples (CoglFramebuffer *framebuffer)
+{
+ cogl_framebuffer_resolve_samples_region (framebuffer,
+ 0, 0,
+ framebuffer->width,
+ framebuffer->height);
+
+
+ /* TODO: Make this happen implicitly when the resolve texture next gets used
+ * as a source, either via cogl_texture_get_data(), via cogl_read_pixels() or
+ * if used as a source for rendering. We would also implicitly resolve if
+ * necessary before freeing a CoglFramebuffer.
+ *
+ * This API should still be kept but it will be optional, only necessary
+ * if the user wants to explicitly control when the resolve happens e.g.
+ * to ensure it's done in advance of it being used as a source.
+ *
+ * Every texture should have a CoglFramebuffer *needs_resolve member
+ * internally. When the texture gets validated before being used as a source
+ * we should first check the needs_resolve pointer and if set we'll
+ * automatically call cogl_framebuffer_resolve_samples ().
+ *
+ * Calling cogl_framebuffer_resolve_samples() or
+ * cogl_framebuffer_resolve_samples_region() should reset the textures
+ * needs_resolve pointer to NULL.
+ *
+ * Rendering anything to a framebuffer will cause the corresponding
+ * texture's ->needs_resolve pointer to be set.
+ */
+}
+
+void
+cogl_framebuffer_resolve_samples_region (CoglFramebuffer *framebuffer,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ /* NOP for now since we don't support EXT_framebuffer_multisample yet which
+ * requires an explicit resolve. */
+
+ /* FIXME: We need to put some explicit assertions into Cogl to make sure
+ * that developers don't forget to use this API, because otherwise they
+ * will end up with non-portable code. */
+}
+
CoglContext *
cogl_framebuffer_get_context (CoglFramebuffer *framebuffer)
{
diff --git a/cogl/cogl-framebuffer.h b/cogl/cogl-framebuffer.h
index 616cfbde..e2fe35f1 100644
--- a/cogl/cogl-framebuffer.h
+++ b/cogl/cogl-framebuffer.h
@@ -277,6 +277,111 @@ cogl_framebuffer_set_color_mask (CoglFramebuffer *framebuffer,
CoglPixelFormat
cogl_framebuffer_get_color_format (CoglFramebuffer *framebuffer);
+#define cogl_framebuffer_set_point_samples_per_pixel \
+ cogl_framebuffer_set_point_samples_per_pixel_EXP
+/**
+ * cogl_framebuffer_set_point_samples_per_pixel:
+ * @framebuffer: A #CoglFramebuffer framebuffer
+ * @n: The minimum number of samples per pixel
+ *
+ * Requires that when rendering to @framebuffer then @n point samples
+ * should be made per pixel which will all contribute to the final
+ * resolved color for that pixel. The idea is that the hardware aims
+ * to get quality similar to what you would get if you rendered
+ * everything twice as big (for 4 samples per pixel) and then scaled
+ * that image back down with filtering. It can effectively remove the
+ * jagged edges of polygons and should be more efficient than if you
+ * were to manually render at a higher resolution and downscale
+ * because the hardware is often able to take some shortcuts. For
+ * example the GPU may only calculate a single texture sample for all
+ * points of a single pixel, and for tile based architectures all the
+ * extra sample data (such as depth and stencil samples) may be
+ * handled on-chip and so avoid increased demand on system memory
+ * bandwidth.
+ *
+ * By default sampling is not based on point samples but rather by
+ * considering the whole rectangular area of the current pixel, so an
+ * @n value of %1 is not equivalent to the default behaviour. A value
+ * of %0 can be used to explicitly request non point based sampling.
+ *
+ * <note>It's important that you call
+ * cogl_framebuffer_resolve_samples() at the end of each frame when
+ * point sample rendering (also known as multisample rendering) has
+ * been enabled, because some GPUs do not implicitly resolve the
+ * samples into the final color buffer during rendering.</note>
+ *
+ * Since: 1.8
+ * Stability: unstable
+ */
+void
+cogl_framebuffer_set_point_samples_per_pixel (CoglFramebuffer *framebuffer,
+ int samples_per_pixel);
+
+#define cogl_framebuffer_resolve_samples \
+ cogl_framebuffer_resolve_samples_EXP
+/**
+ * cogl_framebuffer_resolve_samples:
+ * @framebuffer: A #CoglFramebuffer framebuffer
+ *
+ * When point sample rendering (also known as multisample rendering)
+ * has been enabled via cogl_framebuffer_set_point_samples_per_pixel()
+ * then you are required to call this function (or
+ * cogl_framebuffer_resolve_samples_region()) to guarantee that that
+ * the samples will be resolved and written out to the final color
+ * buffer.
+ *
+ * Some GPUs will implicitly resolve the point samples during
+ * rendering and so this function is effectively a nop, but with other
+ * architectures it is desirable to defer the resolve step until the
+ * end of the frame.
+ *
+ * If you are performing incremental updates to a framebuffer you
+ * should consider using cogl_framebuffer_resolve_samples_region()
+ * instead to avoid resolving redundant pixels.
+ *
+ * Since: 1.8
+ * Stability: unstable
+ */
+void
+cogl_framebuffer_resolve_samples (CoglFramebuffer *framebuffer);
+
+#define cogl_framebuffer_resolve_samples_region \
+ cogl_framebuffer_resolve_samples_region_EXP
+/**
+ * cogl_framebuffer_resolve_samples_region:
+ * @framebuffer: A #CoglFramebuffer framebuffer
+ * @x: top-left x coordinate of region to resolve
+ * @y: top-left y coordinate of region to resolve
+ * @width: width of region to resolve
+ * @height: height of region to resolve
+ *
+ * When point sample rendering (also known as multisample rendering)
+ * has been enabled via cogl_framebuffer_set_point_samples_per_pixel()
+ * then you are required to call this function (or
+ * cogl_framebuffer_resolve_samples()) to guarantee that that the
+ * samples corresponding to a given region will be resolved and
+ * written out to the final color buffer.
+ *
+ * Some GPUs will implicitly resolve the point samples during
+ * rendering and so this function is effectively a nop, but with other
+ * architectures it is desirable to defer the resolve step until the
+ * end of the frame.
+ *
+ * Because some GPUs implicitly resolve point samples this function
+ * only guarantees that at-least the region specified will be resolved
+ * and if you have rendered to a larger region then it's possible that
+ * other samples may be implicitly resolved.
+ *
+ * Since: 1.8
+ * Stability: unstable
+ */
+void
+cogl_framebuffer_resolve_samples_region (CoglFramebuffer *framebuffer,
+ int x,
+ int y,
+ int width,
+ int height);
+
#define cogl_framebuffer_get_context cogl_framebuffer_get_context_EXP
/**
* @framebuffer: A #CoglFramebuffer
@@ -598,6 +703,19 @@ cogl_get_draw_framebuffer (void);
#endif /* COGL_ENABLE_EXPERIMENTAL_API */
+/* XXX: Note these are defined outside the COGL_ENABLE_EXPERIMENTAL_API guard since
+ * otherwise the glib-mkenums stuff will get upset. */
+
+#define cogl_framebuffer_error_quark cogl_framebuffer_error_quark_EXP
+GQuark
+cogl_framebuffer_error_quark (void);
+
+#define COGL_FRAMEBUFFER_ERROR (cogl_framebuffer_error_quark ())
+
+typedef enum { /*< prefix=COGL_FRAMEBUFFER_ERROR >*/
+ COGL_FRAMEBUFFER_ERROR_ALLOCATE
+} CoglFramebufferError;
+
G_END_DECLS
#endif /* __COGL_FRAMEBUFFER_H */