[cogl/wip/for-cairo-cogl: 5/6] offscreen: Adds support for offscreen multisampling



commit dc8c096514d2e74d2bc4174e089a8b9910906d43
Author: Robert Bragg <robert linux intel com>
Date:   Tue Aug 23 13:55:12 2011 +0100

    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.

 cogl/cogl-ext-functions.h       |   30 ++-
 cogl/cogl-framebuffer-private.h |   20 +-
 cogl/cogl-framebuffer.c         |  536 +++++++++++++++++++++++----------------
 cogl/cogl-framebuffer.h         |  118 +++++++++
 4 files changed, 470 insertions(+), 234 deletions(-)
---
diff --git a/cogl/cogl-ext-functions.h b/cogl/cogl-ext-functions.h
index 35832d1..4a85f12 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 98a13ea..a13f2fb 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 f3a03da..b70ff40 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 616cfbd..e2fe35f 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 */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]