diff --git a/cogl/cogl-ext-functions.h b/cogl/cogl-ext-functions.h index 35832d1ce..ecc8b8a5d 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 (IMG_multisampled_render_to_texture, 255, 255, + 0, /* not in either GLES */ + "\0", + "IMG_multisampled_render_to_texture\0") +COGL_EXT_FUNCTION (void, glRenderbufferStorageMultisampleIMG, + (GLenum target, + GLsizei samples, + GLenum internal_format, + GLsizei width, + GLsizei height)) +COGL_EXT_FUNCTION (void, glFramebufferTexture2DMultisampleIMG, + (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 fb8ddcf58..6b334ce01 100644 --- a/cogl/cogl-framebuffer-private.h +++ b/cogl/cogl-framebuffer-private.h @@ -54,6 +54,12 @@ typedef struct int 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; @@ -121,13 +127,15 @@ typedef struct _CoglOffscreen GSList *renderbuffers; CoglTexture *texture; -} CoglOffscreen; + int texture_level; + int texture_level_width; + int texture_level_height; -/* Flags to pass to _cogl_offscreen_new_to_texture_full */ -typedef enum -{ - COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL = 1 -} CoglOffscreenFlags; + /* 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; #define COGL_OFFSCREEN(X) ((CoglOffscreen *)(X)) diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c index f3a03da46..29d4eb508 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 (level_width == 1 && level_height == 1) + { + g_warning ("Invalid texture level passed to " + "_cogl_offscreen_new_to_texture_full"); + return COGL_INVALID_HANDLE; + } - if (data.level_width > 1) - data.level_width >>= 1; - if (data.level_height > 1) - data.level_height >>= 1; + if (level_width > 1) + level_width >>= 1; + if (level_height > 1) + 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; + 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; - 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)) - { - /* 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; - } + fb = COGL_FRAMEBUFFER (offscreen); - if (fbo_created) - { - CoglOffscreen *ret; - CoglFramebuffer *fb = COGL_FRAMEBUFFER (offscreen); + _cogl_framebuffer_init (fb, + ctx, + COGL_FRAMEBUFFER_TYPE_OFFSCREEN, + cogl_texture_get_format (texture), + level_width, + level_height); - _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + ret = _cogl_offscreen_object_new (offscreen); - _cogl_framebuffer_init (fb, - ctx, - COGL_FRAMEBUFFER_TYPE_OFFSCREEN, - cogl_texture_get_format (texture), - data.level_width, - data.level_height); + _cogl_texture_associate_framebuffer (texture, fb); - cogl_object_ref (offscreen->texture); - - ret = _cogl_offscreen_object_new (offscreen); - _cogl_texture_associate_framebuffer (texture, COGL_FRAMEBUFFER (ret)); - - fb->allocated = TRUE; - - 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); - - 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.samples_per_pixel) + { + if (!ctx->glFramebufferTexture2DMultisampleIMG) + return FALSE; + n_samples = fb->config.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, glFramebufferTexture2DMultisampleIMG (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, glRenderbufferStorageMultisampleIMG (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, glRenderbufferStorageMultisampleIMG (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, glRenderbufferStorageMultisampleIMG (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,61 @@ cogl_framebuffer_get_color_format (CoglFramebuffer *framebuffer) return framebuffer->format; } +void +cogl_framebuffer_set_samples_per_pixel (CoglFramebuffer *framebuffer, + int samples_per_pixel) +{ + g_return_if_fail (!framebuffer->allocated); + + framebuffer->config.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 is 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. + * + * XXX: Note: we only need to address this TODO item when adding support for + * EXT_framebuffer_multisample because currently we only support hardware + * that resolves implicitly anyway. + */ +} + +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. */ +} + CoglContext * cogl_framebuffer_get_context (CoglFramebuffer *framebuffer) { diff --git a/cogl/cogl-framebuffer.h b/cogl/cogl-framebuffer.h index 616cfbdef..ad4d1e4ea 100644 --- a/cogl/cogl-framebuffer.h +++ b/cogl/cogl-framebuffer.h @@ -277,6 +277,128 @@ cogl_framebuffer_set_color_mask (CoglFramebuffer *framebuffer, CoglPixelFormat cogl_framebuffer_get_color_format (CoglFramebuffer *framebuffer); +#define cogl_framebuffer_set_samples_per_pixel \ + cogl_framebuffer_set_samples_per_pixel_EXP +/** + * cogl_framebuffer_set_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 this value is usually set to 0 and that is referred to + * as "single-sample" rendering. A value of 1 or greater is referred + * to as "multisample" rendering. + * + * There are some semantic differences between single-sample + * rendering and multisampling with just 1 point sample such as it + * being redundant to use the cogl_framebuffer_resolve_samples() and + * cogl_framebuffer_resolve_samples_region() apis with single-sample + * rendering. + * + * It's recommended that + * cogl_framebuffer_resolve_samples_region() be explicitly used at the + * end of rendering to a point sample buffer to minimize the number of + * samples that get resolved. By default Cogl will implicitly resolve + * all framebuffer samples but if only a small region of a + * framebuffer has changed this can lead to redundant work being + * done. + * + * Since: 1.8 + * Stability: unstable + */ +void +cogl_framebuffer_set_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_samples_per_pixel() + * then you can optionally call this function (or + * cogl_framebuffer_resolve_samples_region()) to explicitly resolve + * the point samples into values for 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. + * + * Since Cogl will automatically ensure samples are resolved if the + * target color buffer is used as a source this API only needs to be + * used if explicit control is desired - perhaps because you want to + * ensure that the resolve is completed in advance to avoid later + * having to wait for the resolve to complete. + * + * 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_samples_per_pixel() + * then you can optionally call this function (or + * cogl_framebuffer_resolve_samples()) to explicitly resolve the point + * samples into values for 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. + * + * Use of this API is recommended if incremental, small updates to + * a framebuffer are being made because by default Cogl will + * implicitly resolve all the point samples of the framebuffer which + * can result in redundant work if only a small number of samples have + * changed. + * + * 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 +720,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 */ diff --git a/cogl/driver/gl/cogl-gl.c b/cogl/driver/gl/cogl-gl.c index cc0cca16b..3ac772d8b 100644 --- a/cogl/driver/gl/cogl-gl.c +++ b/cogl/driver/gl/cogl-gl.c @@ -202,8 +202,8 @@ _cogl_gl_update_features (CoglContext *context, if (context->glBlitFramebuffer) flags |= COGL_FEATURE_OFFSCREEN_BLIT; - if (context->glRenderbufferStorageMultisample) - flags |= COGL_FEATURE_OFFSCREEN_MULTISAMPLE; + if (context->glRenderbufferStorageMultisampleIMG) + flags |= COGL_FEATURE_OFFSCREEN_MULTISAMPLE; if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 1) || _cogl_check_extension ("GL_EXT_pixel_buffer_object", gl_extensions))