mirror of
https://github.com/brl/mutter.git
synced 2025-04-22 09:59:38 +00:00
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_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. Reviewed-by: Neil Roberts <neil@linux.intel.com>
This commit is contained in:
parent
a74c81ada3
commit
dd7b16faf2
@ -333,18 +333,6 @@ COGL_EXT_FUNCTION (void, glBlitFramebuffer,
|
|||||||
GLenum filter))
|
GLenum filter))
|
||||||
COGL_EXT_END ()
|
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 */
|
/* ARB_fragment_program */
|
||||||
COGL_EXT_BEGIN (arbfp, 255, 255,
|
COGL_EXT_BEGIN (arbfp, 255, 255,
|
||||||
0, /* not in either GLES */
|
0, /* not in either GLES */
|
||||||
@ -660,3 +648,21 @@ COGL_EXT_FUNCTION (void, glDiscardFramebuffer,
|
|||||||
const GLenum *attachments))
|
const GLenum *attachments))
|
||||||
COGL_EXT_END ()
|
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 ()
|
||||||
|
@ -54,6 +54,12 @@ typedef struct
|
|||||||
int samples_per_pixel;
|
int samples_per_pixel;
|
||||||
} CoglFramebufferConfig;
|
} CoglFramebufferConfig;
|
||||||
|
|
||||||
|
/* Flags to pass to _cogl_offscreen_new_to_texture_full */
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL = 1
|
||||||
|
} CoglOffscreenFlags;
|
||||||
|
|
||||||
struct _CoglFramebuffer
|
struct _CoglFramebuffer
|
||||||
{
|
{
|
||||||
CoglObject _parent;
|
CoglObject _parent;
|
||||||
@ -121,13 +127,15 @@ typedef struct _CoglOffscreen
|
|||||||
GSList *renderbuffers;
|
GSList *renderbuffers;
|
||||||
|
|
||||||
CoglTexture *texture;
|
CoglTexture *texture;
|
||||||
} CoglOffscreen;
|
int texture_level;
|
||||||
|
int texture_level_width;
|
||||||
|
int texture_level_height;
|
||||||
|
|
||||||
/* Flags to pass to _cogl_offscreen_new_to_texture_full */
|
/* FIXME: _cogl_offscreen_new_to_texture_full should be made to use
|
||||||
typedef enum
|
* fb->config to configure if we want a depth or stencil buffer so
|
||||||
{
|
* we can get rid of these flags */
|
||||||
COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL = 1
|
CoglOffscreenFlags create_flags;
|
||||||
} CoglOffscreenFlags;
|
} CoglOffscreen;
|
||||||
|
|
||||||
#define COGL_OFFSCREEN(X) ((CoglOffscreen *)(X))
|
#define COGL_OFFSCREEN(X) ((CoglOffscreen *)(X))
|
||||||
|
|
||||||
|
@ -120,6 +120,12 @@ COGL_OBJECT_DEFINE_DEPRECATED_REF_COUNTING (offscreen);
|
|||||||
* abstract class manually.
|
* abstract class manually.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
GQuark
|
||||||
|
cogl_framebuffer_error_quark (void)
|
||||||
|
{
|
||||||
|
return g_quark_from_static_string ("cogl-framebuffer-error-quark");
|
||||||
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
_cogl_is_framebuffer (void *object)
|
_cogl_is_framebuffer (void *object)
|
||||||
{
|
{
|
||||||
@ -681,144 +687,17 @@ _cogl_framebuffer_init_bits (CoglFramebuffer *framebuffer)
|
|||||||
framebuffer->dirty_bitmasks = FALSE;
|
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
|
CoglHandle
|
||||||
_cogl_offscreen_new_to_texture_full (CoglTexture *texture,
|
_cogl_offscreen_new_to_texture_full (CoglTexture *texture,
|
||||||
CoglOffscreenFlags create_flags,
|
CoglOffscreenFlags create_flags,
|
||||||
unsigned int level)
|
unsigned int level)
|
||||||
{
|
{
|
||||||
CoglOffscreen *offscreen;
|
CoglOffscreen *offscreen;
|
||||||
static TryFBOFlags flags;
|
CoglFramebuffer *fb;
|
||||||
static gboolean have_working_flags = FALSE;
|
int level_width;
|
||||||
unsigned int i;
|
int level_height;
|
||||||
CoglFramebufferTryFBOData data;
|
int i;
|
||||||
gboolean fbo_created;
|
CoglHandle ret;
|
||||||
|
|
||||||
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
|
_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))
|
if (cogl_texture_is_sliced (texture))
|
||||||
return COGL_INVALID_HANDLE;
|
return COGL_INVALID_HANDLE;
|
||||||
|
|
||||||
data.texture = texture;
|
|
||||||
data.level = level;
|
|
||||||
|
|
||||||
/* Calculate the size of the texture at this mipmap level to ensure
|
/* Calculate the size of the texture at this mipmap level to ensure
|
||||||
that it's a valid level */
|
that it's a valid level */
|
||||||
data.level_width = cogl_texture_get_width (texture);
|
level_width = cogl_texture_get_width (texture);
|
||||||
data.level_height = cogl_texture_get_height (texture);
|
level_height = cogl_texture_get_height (texture);
|
||||||
|
|
||||||
for (i = 0; i < level; i++)
|
for (i = 0; i < level; i++)
|
||||||
{
|
{
|
||||||
/* If neither dimension can be further divided then the level is
|
/* If neither dimension can be further divided then the level is
|
||||||
invalid */
|
invalid */
|
||||||
if (data.level_width == 1 && data.level_height == 1)
|
if (level_width == 1 && level_height == 1)
|
||||||
|
{
|
||||||
|
g_warning ("Invalid texture level passed to "
|
||||||
|
"_cogl_offscreen_new_to_texture_full");
|
||||||
return COGL_INVALID_HANDLE;
|
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
|
if (level_width > 1)
|
||||||
* a texture as a renderbuffer with mipmap filtering enabled while the
|
level_width >>= 1;
|
||||||
* mipmaps have not been uploaded should result in an incomplete framebuffer
|
if (level_height > 1)
|
||||||
* object. (different drivers make different decisions)
|
level_height >>= 1;
|
||||||
*
|
}
|
||||||
* 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 = 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))
|
fb = COGL_FRAMEBUFFER (offscreen);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fbo_created)
|
|
||||||
{
|
|
||||||
CoglOffscreen *ret;
|
|
||||||
CoglFramebuffer *fb = COGL_FRAMEBUFFER (offscreen);
|
|
||||||
|
|
||||||
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
|
|
||||||
|
|
||||||
_cogl_framebuffer_init (fb,
|
_cogl_framebuffer_init (fb,
|
||||||
ctx,
|
ctx,
|
||||||
COGL_FRAMEBUFFER_TYPE_OFFSCREEN,
|
COGL_FRAMEBUFFER_TYPE_OFFSCREEN,
|
||||||
cogl_texture_get_format (texture),
|
cogl_texture_get_format (texture),
|
||||||
data.level_width,
|
level_width,
|
||||||
data.level_height);
|
level_height);
|
||||||
|
|
||||||
cogl_object_ref (offscreen->texture);
|
|
||||||
|
|
||||||
ret = _cogl_offscreen_object_new (offscreen);
|
ret = _cogl_offscreen_object_new (offscreen);
|
||||||
_cogl_texture_associate_framebuffer (texture, COGL_FRAMEBUFFER (ret));
|
|
||||||
|
|
||||||
fb->allocated = TRUE;
|
_cogl_texture_associate_framebuffer (texture, fb);
|
||||||
|
|
||||||
return ret;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CoglHandle
|
CoglHandle
|
||||||
@ -1028,6 +858,221 @@ cogl_onscreen_new (CoglContext *ctx, int width, int height)
|
|||||||
return _cogl_onscreen_object_new (onscreen);
|
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
|
gboolean
|
||||||
cogl_framebuffer_allocate (CoglFramebuffer *framebuffer,
|
cogl_framebuffer_allocate (CoglFramebuffer *framebuffer,
|
||||||
GError **error)
|
GError **error)
|
||||||
@ -1038,13 +1083,16 @@ cogl_framebuffer_allocate (CoglFramebuffer *framebuffer,
|
|||||||
if (framebuffer->allocated)
|
if (framebuffer->allocated)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
/* XXX: with the current cogl_offscreen_new_to_texture() API the
|
if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
|
||||||
* framebuffer is implicitly allocated before returning. */
|
{
|
||||||
g_return_val_if_fail (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN,
|
|
||||||
TRUE);
|
|
||||||
|
|
||||||
if (!winsys->onscreen_init (onscreen, error))
|
if (!winsys->onscreen_init (onscreen, error))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!_cogl_offscreen_allocate (COGL_OFFSCREEN (framebuffer), error))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
framebuffer->allocated = TRUE;
|
framebuffer->allocated = TRUE;
|
||||||
|
|
||||||
@ -1591,6 +1639,61 @@ cogl_framebuffer_get_color_format (CoglFramebuffer *framebuffer)
|
|||||||
return framebuffer->format;
|
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 *
|
CoglContext *
|
||||||
cogl_framebuffer_get_context (CoglFramebuffer *framebuffer)
|
cogl_framebuffer_get_context (CoglFramebuffer *framebuffer)
|
||||||
{
|
{
|
||||||
|
@ -277,6 +277,128 @@ cogl_framebuffer_set_color_mask (CoglFramebuffer *framebuffer,
|
|||||||
CoglPixelFormat
|
CoglPixelFormat
|
||||||
cogl_framebuffer_get_color_format (CoglFramebuffer *framebuffer);
|
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.
|
||||||
|
*
|
||||||
|
* <note>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.</note>
|
||||||
|
*
|
||||||
|
* <note>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.</note>
|
||||||
|
*
|
||||||
|
* 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
|
#define cogl_framebuffer_get_context cogl_framebuffer_get_context_EXP
|
||||||
/**
|
/**
|
||||||
* @framebuffer: A #CoglFramebuffer
|
* @framebuffer: A #CoglFramebuffer
|
||||||
@ -598,6 +720,19 @@ cogl_get_draw_framebuffer (void);
|
|||||||
|
|
||||||
#endif /* COGL_ENABLE_EXPERIMENTAL_API */
|
#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
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __COGL_FRAMEBUFFER_H */
|
#endif /* __COGL_FRAMEBUFFER_H */
|
||||||
|
@ -202,7 +202,7 @@ _cogl_gl_update_features (CoglContext *context,
|
|||||||
if (context->glBlitFramebuffer)
|
if (context->glBlitFramebuffer)
|
||||||
flags |= COGL_FEATURE_OFFSCREEN_BLIT;
|
flags |= COGL_FEATURE_OFFSCREEN_BLIT;
|
||||||
|
|
||||||
if (context->glRenderbufferStorageMultisample)
|
if (context->glRenderbufferStorageMultisampleIMG)
|
||||||
flags |= COGL_FEATURE_OFFSCREEN_MULTISAMPLE;
|
flags |= COGL_FEATURE_OFFSCREEN_MULTISAMPLE;
|
||||||
|
|
||||||
if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 1) ||
|
if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 1) ||
|
||||||
|
Loading…
x
Reference in New Issue
Block a user