From 60e1516b1c5ba4ae6632007384e2f9b6d2d13ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 19 Oct 2020 21:19:15 +0200 Subject: [PATCH] cogl/gl-framebuffer: Split up into FBO and back drivers One is for when we're painting to the back buffer (onscreen), and the other when we're painting to an FBO (offscreen). Part-of: --- cogl/cogl/cogl-driver.h | 4 - cogl/cogl/cogl-framebuffer-driver.c | 7 + cogl/cogl/cogl-framebuffer-driver.h | 9 + cogl/cogl/cogl-framebuffer-private.h | 10 +- cogl/cogl/cogl-framebuffer.c | 42 +- cogl/cogl/cogl-offscreen.c | 1 + .../driver/gl/cogl-framebuffer-gl-private.h | 27 +- cogl/cogl/driver/gl/cogl-framebuffer-gl.c | 687 +----------------- .../cogl/driver/gl/cogl-gl-framebuffer-back.c | 239 ++++++ .../cogl/driver/gl/cogl-gl-framebuffer-back.h | 41 ++ cogl/cogl/driver/gl/cogl-gl-framebuffer-fbo.c | 611 ++++++++++++++++ cogl/cogl/driver/gl/cogl-gl-framebuffer-fbo.h | 41 ++ cogl/cogl/driver/gl/cogl-util-gl-private.h | 83 +++ cogl/cogl/driver/gl/cogl-util-gl.c | 58 +- cogl/cogl/driver/gl/gl/cogl-driver-gl.c | 1 - cogl/cogl/driver/gl/gles/cogl-driver-gles.c | 1 - cogl/cogl/driver/nop/cogl-driver-nop.c | 1 - .../driver/nop/cogl-framebuffer-nop-private.h | 4 - cogl/cogl/driver/nop/cogl-framebuffer-nop.c | 7 - cogl/cogl/driver/nop/cogl-nop-framebuffer.c | 13 + cogl/cogl/meson.build | 4 + cogl/cogl/winsys/cogl-onscreen-glx.c | 6 + cogl/cogl/winsys/cogl-onscreen-xlib.c | 6 + src/backends/native/meta-onscreen-native.c | 5 + 24 files changed, 1165 insertions(+), 743 deletions(-) create mode 100644 cogl/cogl/driver/gl/cogl-gl-framebuffer-back.c create mode 100644 cogl/cogl/driver/gl/cogl-gl-framebuffer-back.h create mode 100644 cogl/cogl/driver/gl/cogl-gl-framebuffer-fbo.c create mode 100644 cogl/cogl/driver/gl/cogl-gl-framebuffer-fbo.h diff --git a/cogl/cogl/cogl-driver.h b/cogl/cogl/cogl-driver.h index e2cf98927..5e698debd 100644 --- a/cogl/cogl/cogl-driver.h +++ b/cogl/cogl/cogl-driver.h @@ -93,10 +93,6 @@ struct _CoglDriverVtable float blue, float alpha); - void - (* framebuffer_query_bits) (CoglFramebuffer *framebuffer, - CoglFramebufferBits *bits); - void (* framebuffer_finish) (CoglFramebuffer *framebuffer); diff --git a/cogl/cogl/cogl-framebuffer-driver.c b/cogl/cogl/cogl-framebuffer-driver.c index 1559de0f6..d1bbf82a0 100644 --- a/cogl/cogl/cogl-framebuffer-driver.c +++ b/cogl/cogl/cogl-framebuffer-driver.c @@ -58,6 +58,13 @@ cogl_framebuffer_driver_get_framebuffer (CoglFramebufferDriver *driver) return priv->framebuffer; } +void +cogl_framebuffer_driver_query_bits (CoglFramebufferDriver *driver, + CoglFramebufferBits *bits) +{ + COGL_FRAMEBUFFER_DRIVER_GET_CLASS (driver)->query_bits (driver, bits); +} + static void cogl_framebuffer_driver_get_property (GObject *object, guint prop_id, diff --git a/cogl/cogl/cogl-framebuffer-driver.h b/cogl/cogl/cogl-framebuffer-driver.h index 1ef6ce058..8f06e9e05 100644 --- a/cogl/cogl/cogl-framebuffer-driver.h +++ b/cogl/cogl/cogl-framebuffer-driver.h @@ -30,6 +30,8 @@ #include "cogl-framebuffer.h" +typedef struct _CoglFramebufferBits CoglFramebufferBits; + #define COGL_TYPE_FRAMEBUFFER_DRIVER (cogl_framebuffer_driver_get_type ()) G_DECLARE_DERIVABLE_TYPE (CoglFramebufferDriver, cogl_framebuffer_driver, @@ -39,9 +41,16 @@ G_DECLARE_DERIVABLE_TYPE (CoglFramebufferDriver, struct _CoglFramebufferDriverClass { GObjectClass parent_cleass; + + void (* query_bits) (CoglFramebufferDriver *driver, + CoglFramebufferBits *bits); }; CoglFramebuffer * cogl_framebuffer_driver_get_framebuffer (CoglFramebufferDriver *driver); +void +cogl_framebuffer_driver_query_bits (CoglFramebufferDriver *driver, + CoglFramebufferBits *bits); + #endif /* COGL_FRAMEBUFFER_DRIVER_H */ diff --git a/cogl/cogl/cogl-framebuffer-private.h b/cogl/cogl/cogl-framebuffer-private.h index f00a5bc4b..0c9af2d92 100644 --- a/cogl/cogl/cogl-framebuffer-private.h +++ b/cogl/cogl/cogl-framebuffer-private.h @@ -40,8 +40,16 @@ #include "cogl-attribute-private.h" #include "cogl-clip-stack.h" +typedef enum +{ + COGL_FRAMEBUFFER_DRIVER_TYPE_FBO, + COGL_FRAMEBUFFER_DRIVER_TYPE_BACK, +} CoglFramebufferDriverType; + struct _CoglFramebufferDriverConfig { + CoglFramebufferDriverType type; + gboolean disable_depth_and_stencil; }; @@ -98,7 +106,7 @@ typedef enum COGL_READ_PIXELS_NO_FLIP = 1L << 30 } CoglPrivateReadPixelsFlags; -typedef struct +typedef struct _CoglFramebufferBits { int red; int blue; diff --git a/cogl/cogl/cogl-framebuffer.c b/cogl/cogl/cogl-framebuffer.c index b83c3c19a..4bd4673da 100644 --- a/cogl/cogl/cogl-framebuffer.c +++ b/cogl/cogl/cogl-framebuffer.c @@ -1126,15 +1126,24 @@ cogl_context_flush_framebuffer_state (CoglContext *ctx, state); } -int -cogl_framebuffer_get_red_bits (CoglFramebuffer *framebuffer) +static void +cogl_framebuffer_query_bits (CoglFramebuffer *framebuffer, + CoglFramebufferBits *bits) { CoglFramebufferPrivate *priv = cogl_framebuffer_get_instance_private (framebuffer); - CoglContext *ctx = priv->context; + + g_return_if_fail (priv->driver); + + cogl_framebuffer_driver_query_bits (priv->driver, bits); +} + +int +cogl_framebuffer_get_red_bits (CoglFramebuffer *framebuffer) +{ CoglFramebufferBits bits; - ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); + cogl_framebuffer_query_bits (framebuffer, &bits); return bits.red; } @@ -1142,12 +1151,9 @@ cogl_framebuffer_get_red_bits (CoglFramebuffer *framebuffer) int cogl_framebuffer_get_green_bits (CoglFramebuffer *framebuffer) { - CoglFramebufferPrivate *priv = - cogl_framebuffer_get_instance_private (framebuffer); - CoglContext *ctx = priv->context; CoglFramebufferBits bits; - ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); + cogl_framebuffer_query_bits (framebuffer, &bits); return bits.green; } @@ -1155,12 +1161,9 @@ cogl_framebuffer_get_green_bits (CoglFramebuffer *framebuffer) int cogl_framebuffer_get_blue_bits (CoglFramebuffer *framebuffer) { - CoglFramebufferPrivate *priv = - cogl_framebuffer_get_instance_private (framebuffer); - CoglContext *ctx = priv->context; CoglFramebufferBits bits; - ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); + cogl_framebuffer_query_bits (framebuffer, &bits); return bits.blue; } @@ -1168,12 +1171,9 @@ cogl_framebuffer_get_blue_bits (CoglFramebuffer *framebuffer) int cogl_framebuffer_get_alpha_bits (CoglFramebuffer *framebuffer) { - CoglFramebufferPrivate *priv = - cogl_framebuffer_get_instance_private (framebuffer); - CoglContext *ctx = priv->context; CoglFramebufferBits bits; - ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); + cogl_framebuffer_query_bits (framebuffer, &bits); return bits.alpha; } @@ -1181,12 +1181,9 @@ cogl_framebuffer_get_alpha_bits (CoglFramebuffer *framebuffer) int cogl_framebuffer_get_depth_bits (CoglFramebuffer *framebuffer) { - CoglFramebufferPrivate *priv = - cogl_framebuffer_get_instance_private (framebuffer); - CoglContext *ctx = priv->context; CoglFramebufferBits bits; - ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); + cogl_framebuffer_query_bits (framebuffer, &bits); return bits.depth; } @@ -1194,12 +1191,9 @@ cogl_framebuffer_get_depth_bits (CoglFramebuffer *framebuffer) int _cogl_framebuffer_get_stencil_bits (CoglFramebuffer *framebuffer) { - CoglFramebufferPrivate *priv = - cogl_framebuffer_get_instance_private (framebuffer); - CoglContext *ctx = priv->context; CoglFramebufferBits bits; - ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); + cogl_framebuffer_query_bits (framebuffer, &bits); return bits.stencil; } diff --git a/cogl/cogl/cogl-offscreen.c b/cogl/cogl/cogl-offscreen.c index ae05f7343..a9b25cc79 100644 --- a/cogl/cogl/cogl-offscreen.c +++ b/cogl/cogl/cogl-offscreen.c @@ -56,6 +56,7 @@ _cogl_offscreen_new_with_texture_full (CoglTexture *texture, g_return_val_if_fail (cogl_is_texture (texture), NULL); driver_config = (CoglFramebufferDriverConfig) { + .type = COGL_FRAMEBUFFER_DRIVER_TYPE_FBO, .disable_depth_and_stencil = !!(flags & COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL), }; diff --git a/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h b/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h index 2f96f6f8f..3f0ddb539 100644 --- a/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h +++ b/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h @@ -34,20 +34,22 @@ #ifndef __COGL_FRAMEBUFFER_GL_PRIVATE_H__ #define __COGL_FRAMEBUFFER_GL_PRIVATE_H__ +#include "cogl-attribute-private.h" #include "cogl-framebuffer-driver.h" +#include "cogl-gl-header.h" #define COGL_TYPE_GL_FRAMEBUFFER (cogl_gl_framebuffer_get_type ()) -G_DECLARE_FINAL_TYPE (CoglGlFramebuffer, cogl_gl_framebuffer, - COGL, GL_FRAMEBUFFER, - CoglFramebufferDriver) +G_DECLARE_DERIVABLE_TYPE (CoglGlFramebuffer, cogl_gl_framebuffer, + COGL, GL_FRAMEBUFFER, + CoglFramebufferDriver) -gboolean -_cogl_offscreen_gl_allocate (CoglOffscreen *offscreen, - CoglOffscreenFlags flags, - GError **error); +struct _CoglGlFramebufferClass +{ + CoglFramebufferDriverClass parent_class; -void -_cogl_offscreen_gl_free (CoglOffscreen *offscreen); + void (* bind) (CoglGlFramebuffer *gl_framebuffer, + GLenum target); +}; void _cogl_framebuffer_gl_clear (CoglFramebuffer *framebuffer, @@ -57,10 +59,6 @@ _cogl_framebuffer_gl_clear (CoglFramebuffer *framebuffer, float blue, float alpha); -void -_cogl_framebuffer_gl_query_bits (CoglFramebuffer *framebuffer, - CoglFramebufferBits *bits); - void _cogl_framebuffer_gl_finish (CoglFramebuffer *framebuffer); @@ -72,7 +70,8 @@ _cogl_framebuffer_gl_discard_buffers (CoglFramebuffer *framebuffer, unsigned long buffers); void -_cogl_framebuffer_gl_bind (CoglFramebuffer *framebuffer, GLenum target); +cogl_gl_framebuffer_bind (CoglGlFramebuffer *gl_framebuffer, + GLenum target); void _cogl_framebuffer_gl_draw_attributes (CoglFramebuffer *framebuffer, diff --git a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c index 95e4a39b6..4700e449b 100644 --- a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c +++ b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c @@ -40,116 +40,12 @@ #include "driver/gl/cogl-framebuffer-gl-private.h" #include "driver/gl/cogl-bitmap-gl-private.h" #include "driver/gl/cogl-buffer-gl-private.h" -#include "driver/gl/cogl-texture-gl-private.h" #include #include -#ifndef GL_FRAMEBUFFER -#define GL_FRAMEBUFFER 0x8D40 -#endif -#ifndef GL_RENDERBUFFER -#define GL_RENDERBUFFER 0x8D41 -#endif -#ifndef GL_STENCIL_ATTACHMENT -#define GL_STENCIL_ATTACHMENT 0x8D00 -#endif -#ifndef GL_COLOR_ATTACHMENT0 -#define GL_COLOR_ATTACHMENT0 0x8CE0 -#endif -#ifndef GL_FRAMEBUFFER_COMPLETE -#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 -#endif -#ifndef GL_STENCIL_INDEX8 -#define GL_STENCIL_INDEX8 0x8D48 -#endif -#ifndef GL_DEPTH_STENCIL -#define GL_DEPTH_STENCIL 0x84F9 -#endif -#ifndef GL_DEPTH24_STENCIL8 -#define GL_DEPTH24_STENCIL8 0x88F0 -#endif -#ifndef GL_DEPTH_ATTACHMENT -#define GL_DEPTH_ATTACHMENT 0x8D00 -#endif -#ifndef GL_DEPTH_STENCIL_ATTACHMENT -#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A -#endif -#ifndef GL_DEPTH_COMPONENT16 -#define GL_DEPTH_COMPONENT16 0x81A5 -#endif -#ifndef GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE -#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 -#endif -#ifndef GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE -#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 -#endif -#ifndef GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE -#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 -#endif -#ifndef GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE -#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 -#endif -#ifndef GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE -#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 -#endif -#ifndef GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE -#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 -#endif -#ifndef GL_READ_FRAMEBUFFER -#define GL_READ_FRAMEBUFFER 0x8CA8 -#endif -#ifndef GL_DRAW_FRAMEBUFFER -#define GL_DRAW_FRAMEBUFFER 0x8CA9 -#endif -#ifndef GL_TEXTURE_SAMPLES_IMG -#define GL_TEXTURE_SAMPLES_IMG 0x9136 -#endif -#ifndef GL_PACK_INVERT_MESA -#define GL_PACK_INVERT_MESA 0x8758 -#endif -#ifndef GL_PACK_REVERSE_ROW_ORDER_ANGLE -#define GL_PACK_REVERSE_ROW_ORDER_ANGLE 0x93A4 -#endif -#ifndef GL_BACK_LEFT -#define GL_BACK_LEFT 0x0402 -#endif -#ifndef GL_BACK_RIGHT -#define GL_BACK_RIGHT 0x0403 -#endif - -#ifndef GL_COLOR -#define GL_COLOR 0x1800 -#endif -#ifndef GL_DEPTH -#define GL_DEPTH 0x1801 -#endif -#ifndef GL_STENCIL -#define GL_STENCIL 0x1802 -#endif - -typedef struct _CoglGlFbo -{ - GLuint fbo_handle; - GList *renderbuffers; - int samples_per_pixel; -} CoglGlFbo; - -typedef struct _CoglGlFramebufferPrivate -{ - CoglGlFbo gl_fbo; - - gboolean dirty_bitmasks; - CoglFramebufferBits bits; -} CoglGlFramebufferPrivate; - -struct _CoglGlFramebuffer -{ - CoglFramebufferDriver parent; -}; - -G_DEFINE_TYPE_WITH_PRIVATE (CoglGlFramebuffer, cogl_gl_framebuffer, - COGL_TYPE_FRAMEBUFFER_DRIVER) +G_DEFINE_ABSTRACT_TYPE (CoglGlFramebuffer, cogl_gl_framebuffer, + COGL_TYPE_FRAMEBUFFER_DRIVER) static void _cogl_framebuffer_gl_flush_viewport_state (CoglFramebuffer *framebuffer) @@ -352,464 +248,12 @@ cogl_gl_framebuffer_flush_state_differences (CoglGlFramebuffer *gl_framebuffer, COGL_FLAGS_FOREACH_END; } -static CoglGlFramebuffer * -cogl_gl_framebuffer_from_framebuffer (CoglFramebuffer *framebuffer) -{ - CoglFramebufferDriver *driver = cogl_framebuffer_get_driver (framebuffer); - - g_assert (driver); - - return COGL_GL_FRAMEBUFFER (driver); -} - void -_cogl_framebuffer_gl_bind (CoglFramebuffer *framebuffer, GLenum target) +cogl_gl_framebuffer_bind (CoglGlFramebuffer *gl_framebuffer, + GLenum target) { - CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); - - if (COGL_IS_OFFSCREEN (framebuffer)) - { - CoglGlFramebuffer *gl_framebuffer = - cogl_gl_framebuffer_from_framebuffer (framebuffer); - CoglGlFramebufferPrivate *priv = - cogl_gl_framebuffer_get_instance_private (gl_framebuffer); - - GE (ctx, glBindFramebuffer (target, priv->gl_fbo.fbo_handle)); - } - else - { - const CoglWinsysVtable *winsys = - _cogl_framebuffer_get_winsys (framebuffer); - winsys->onscreen_bind (COGL_ONSCREEN (framebuffer)); - GE (ctx, glBindFramebuffer (target, 0)); - - /* Initialise the glDrawBuffer state the first time the context - * is bound to the default framebuffer. If the winsys is using a - * surfaceless context for the initial make current then the - * default draw buffer will be GL_NONE so we need to correct - * that. We can't do it any earlier because binding GL_BACK when - * there is no default framebuffer won't work */ - if (!ctx->was_bound_to_onscreen) - { - if (ctx->glDrawBuffer) - { - GE (ctx, glDrawBuffer (GL_BACK)); - } - else if (ctx->glDrawBuffers) - { - /* glDrawBuffer isn't available on GLES 3.0 so we need - * to be able to use glDrawBuffers as well. On GLES 2 - * neither is available but the state should always be - * GL_BACK anyway so we don't need to set anything. On - * desktop GL this must be GL_BACK_LEFT instead of - * GL_BACK but as this code path will only be hit for - * GLES we can just use GL_BACK. */ - static const GLenum buffers[] = { GL_BACK }; - - GE (ctx, glDrawBuffers (G_N_ELEMENTS (buffers), buffers)); - } - - ctx->was_bound_to_onscreen = TRUE; - } - } -} - -static GList * -try_creating_renderbuffers (CoglContext *ctx, - int width, - int height, - CoglOffscreenAllocateFlags flags, - int n_samples) -{ - GList *renderbuffers = NULL; - GLuint gl_depth_stencil_handle; - - if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL) - { - GLenum format; - - /* WebGL adds a GL_DEPTH_STENCIL_ATTACHMENT and requires that we - * use the GL_DEPTH_STENCIL format. */ - /* Although GL_OES_packed_depth_stencil is mostly equivalent to - * GL_EXT_packed_depth_stencil, one notable difference is that - * GL_OES_packed_depth_stencil doesn't allow GL_DEPTH_STENCIL to - * be passed as an internal format to glRenderbufferStorage. - */ - if (_cogl_has_private_feature - (ctx, COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL)) - format = GL_DEPTH_STENCIL; - else - { - g_return_val_if_fail ( - _cogl_has_private_feature (ctx, - COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL), - NULL); - format = GL_DEPTH24_STENCIL8; - } - - /* 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, - format, - width, height)); - else - GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, format, - 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)); - renderbuffers = - g_list_prepend (renderbuffers, - GUINT_TO_POINTER (gl_depth_stencil_handle)); - } - - if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH) - { - GLuint gl_depth_handle; - - 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)); - renderbuffers = - g_list_prepend (renderbuffers, GUINT_TO_POINTER (gl_depth_handle)); - } - - if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL) - { - GLuint gl_stencil_handle; - - 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)); - renderbuffers = - g_list_prepend (renderbuffers, GUINT_TO_POINTER (gl_stencil_handle)); - } - - return renderbuffers; -} - -static void -delete_renderbuffers (CoglContext *ctx, GList *renderbuffers) -{ - GList *l; - - for (l = renderbuffers; l; l = l->next) - { - GLuint renderbuffer = GPOINTER_TO_UINT (l->data); - GE (ctx, glDeleteRenderbuffers (1, &renderbuffer)); - } - - g_list_free (renderbuffers); -} - -/* - * NB: This function may be called with a standalone GLES2 context - * bound so we can create a shadow framebuffer that wraps the same - * CoglTexture as the given CoglOffscreen. This function shouldn't - * modify anything in - */ -static gboolean -try_creating_fbo (CoglContext *ctx, - CoglTexture *texture, - int texture_level, - int texture_level_width, - int texture_level_height, - const CoglFramebufferConfig *config, - CoglOffscreenAllocateFlags flags, - CoglGlFbo *gl_fbo) -{ - GLuint tex_gl_handle; - GLenum tex_gl_target; - GLenum status; - int n_samples; - - if (!cogl_texture_get_gl_texture (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 (config->samples_per_pixel) - { - if (!ctx->glFramebufferTexture2DMultisampleIMG) - return FALSE; - n_samples = config->samples_per_pixel; - } - else - n_samples = 0; - - /* 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->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_BIND; - - /* Generate framebuffer */ - ctx->glGenFramebuffers (1, &gl_fbo->fbo_handle); - GE (ctx, glBindFramebuffer (GL_FRAMEBUFFER, gl_fbo->fbo_handle)); - - if (n_samples) - { - GE (ctx, glFramebufferTexture2DMultisampleIMG (GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - tex_gl_target, tex_gl_handle, - n_samples, - texture_level)); - } - else - GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - tex_gl_target, tex_gl_handle, - texture_level)); - - if (flags) - { - gl_fbo->renderbuffers = - try_creating_renderbuffers (ctx, - texture_level_width, - texture_level_height, - flags, - n_samples); - } - - /* Make sure it's complete */ - status = ctx->glCheckFramebufferStatus (GL_FRAMEBUFFER); - - if (status != GL_FRAMEBUFFER_COMPLETE) - { - GE (ctx, glDeleteFramebuffers (1, &gl_fbo->fbo_handle)); - - delete_renderbuffers (ctx, gl_fbo->renderbuffers); - gl_fbo->renderbuffers = NULL; - - return FALSE; - } - - /* Update the real number of samples_per_pixel now that we have a - * complete framebuffer */ - if (n_samples) - { - GLenum attachment = GL_COLOR_ATTACHMENT0; - GLenum pname = GL_TEXTURE_SAMPLES_IMG; - int texture_samples; - - GE( ctx, glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, - attachment, - pname, - &texture_samples) ); - gl_fbo->samples_per_pixel = texture_samples; - } - - return TRUE; -} - -CoglFramebufferDriver * -_cogl_driver_gl_create_framebuffer_driver (CoglContext *context, - CoglFramebuffer *framebuffer, - const CoglFramebufferDriverConfig *driver_config, - GError **error) -{ - CoglOffscreenAllocateFlags allocate_flags; - CoglOffscreen *offscreen; - CoglGlFramebuffer *gl_framebuffer; - CoglGlFramebufferPrivate *priv; - CoglGlFbo *gl_fbo; - const CoglFramebufferConfig *config; - CoglTexture *texture; - int texture_level; - int level_width; - int level_height; - - if (COGL_IS_ONSCREEN (framebuffer)) - { - return g_object_new (COGL_TYPE_GL_FRAMEBUFFER, - "framebuffer", framebuffer, - NULL); - } - - offscreen = COGL_OFFSCREEN (framebuffer); - texture = cogl_offscreen_get_texture (offscreen); - texture_level = cogl_offscreen_get_texture_level (offscreen); - - g_return_val_if_fail (texture_level < _cogl_texture_get_n_levels (texture), - FALSE); - - _cogl_texture_get_level_size (texture, - texture_level, - &level_width, - &level_height, - NULL); - - /* 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_gl_flush_legacy_texobj_filters (texture, - GL_NEAREST, GL_NEAREST); - - config = cogl_framebuffer_get_config (framebuffer); - - gl_framebuffer = g_object_new (COGL_TYPE_GL_FRAMEBUFFER, - "framebuffer", framebuffer, - NULL); - priv = cogl_gl_framebuffer_get_instance_private (gl_framebuffer); - gl_fbo = &priv->gl_fbo; - - if ((driver_config->disable_depth_and_stencil && - try_creating_fbo (context, - texture, - texture_level, - level_width, - level_height, - config, - allocate_flags = 0, - gl_fbo)) || - - (context->have_last_offscreen_allocate_flags && - try_creating_fbo (context, - texture, - texture_level, - level_width, - level_height, - config, - allocate_flags = context->last_offscreen_allocate_flags, - gl_fbo)) || - - ( - /* NB: WebGL introduces a DEPTH_STENCIL_ATTACHMENT and doesn't - * need an extension to handle _FLAG_DEPTH_STENCIL */ - (_cogl_has_private_feature - (context, COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL) || - _cogl_has_private_feature - (context, COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL)) && - try_creating_fbo (context, - texture, - texture_level, - level_width, - level_height, - config, - allocate_flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL, - gl_fbo)) || - - try_creating_fbo (context, - texture, - texture_level, - level_width, - level_height, - config, - allocate_flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH | - COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL, - gl_fbo) || - - try_creating_fbo (context, - texture, - texture_level, - level_width, - level_height, - config, - allocate_flags = COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL, - gl_fbo) || - - try_creating_fbo (context, - texture, - texture_level, - level_width, - level_height, - config, - allocate_flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH, - gl_fbo) || - - try_creating_fbo (context, - texture, - texture_level, - level_width, - level_height, - config, - allocate_flags = 0, - gl_fbo)) - { - cogl_framebuffer_update_samples_per_pixel (framebuffer, - gl_fbo->samples_per_pixel); - - if (!driver_config->disable_depth_and_stencil) - { - /* Record that the last set of flags succeeded so that we can - try that set first next time */ - context->last_offscreen_allocate_flags = allocate_flags; - context->have_last_offscreen_allocate_flags = TRUE; - } - - return COGL_FRAMEBUFFER_DRIVER (gl_framebuffer); - } - else - { - g_object_unref (gl_framebuffer); - g_set_error (error, COGL_FRAMEBUFFER_ERROR, - COGL_FRAMEBUFFER_ERROR_ALLOCATE, - "Failed to create an OpenGL framebuffer object"); - return NULL; - } -} - -static void -cogl_gl_framebuffer_dispose (GObject *object) -{ - CoglGlFramebuffer *gl_framebuffer = COGL_GL_FRAMEBUFFER (object); - CoglGlFramebufferPrivate *priv = - cogl_gl_framebuffer_get_instance_private (gl_framebuffer); - CoglFramebufferDriver *driver = COGL_FRAMEBUFFER_DRIVER (object); - CoglFramebuffer *framebuffer = - cogl_framebuffer_driver_get_framebuffer (driver); - CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); - - G_OBJECT_CLASS (cogl_gl_framebuffer_parent_class)->dispose (object); - - delete_renderbuffers (ctx, priv->gl_fbo.renderbuffers); - - GE (ctx, glDeleteFramebuffers (1, &priv->gl_fbo.fbo_handle)); + COGL_GL_FRAMEBUFFER_GET_CLASS (gl_framebuffer)->bind (gl_framebuffer, + target); } void @@ -857,118 +301,6 @@ _cogl_framebuffer_gl_clear (CoglFramebuffer *framebuffer, GE (ctx, glClear (gl_buffers)); } -static inline void -_cogl_framebuffer_init_bits (CoglFramebuffer *framebuffer) -{ - CoglGlFramebuffer *gl_framebuffer = - cogl_gl_framebuffer_from_framebuffer (framebuffer); - CoglGlFramebufferPrivate *priv = - cogl_gl_framebuffer_get_instance_private (gl_framebuffer); - CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); - - if (!priv->dirty_bitmasks) - return; - - cogl_framebuffer_allocate (framebuffer, NULL); - - cogl_context_flush_framebuffer_state (ctx, - framebuffer, - framebuffer, - COGL_FRAMEBUFFER_STATE_BIND); - -#ifdef HAVE_COGL_GL - if ((ctx->driver == COGL_DRIVER_GL3 && - COGL_IS_ONSCREEN (framebuffer)) || - (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_QUERY_FRAMEBUFFER_BITS) && - COGL_IS_OFFSCREEN (framebuffer))) - { - gboolean is_offscreen = COGL_IS_OFFSCREEN (framebuffer); - const struct { - GLenum attachment, pname; - size_t offset; - } params[] = { - { is_offscreen ? GL_COLOR_ATTACHMENT0 : GL_BACK_LEFT, - GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, - offsetof (CoglFramebufferBits, red) }, - { is_offscreen ? GL_COLOR_ATTACHMENT0 : GL_BACK_LEFT, - GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, - offsetof (CoglFramebufferBits, green) }, - { is_offscreen ? GL_COLOR_ATTACHMENT0 : GL_BACK_LEFT, - GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, - offsetof (CoglFramebufferBits, blue) }, - { is_offscreen ? GL_COLOR_ATTACHMENT0 : GL_BACK_LEFT, - GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, - offsetof (CoglFramebufferBits, alpha) }, - { is_offscreen ? GL_DEPTH_ATTACHMENT : GL_DEPTH, - GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, - offsetof (CoglFramebufferBits, depth) }, - { is_offscreen ? GL_STENCIL_ATTACHMENT : GL_STENCIL, - GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, - offsetof (CoglFramebufferBits, stencil) }, - }; - int i; - - for (i = 0; i < G_N_ELEMENTS (params); i++) - { - int *value = - (int *) ((uint8_t *) &priv->bits + params[i].offset); - GE( ctx, glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, - params[i].attachment, - params[i].pname, - value) ); - } - } - else -#endif /* HAVE_COGL_GL */ - { - GE (ctx, glGetIntegerv (GL_RED_BITS, &priv->bits.red)); - GE (ctx, glGetIntegerv (GL_GREEN_BITS, &priv->bits.green)); - GE (ctx, glGetIntegerv (GL_BLUE_BITS, &priv->bits.blue)); - GE (ctx, glGetIntegerv (GL_ALPHA_BITS, &priv->bits.alpha)); - GE (ctx, glGetIntegerv (GL_DEPTH_BITS, &priv->bits.depth)); - GE (ctx, glGetIntegerv (GL_STENCIL_BITS, &priv->bits.stencil)); - } - - /* If we don't have alpha textures then the alpha bits are actually - * stored in the red component */ - if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) && - COGL_IS_OFFSCREEN (framebuffer) && - cogl_framebuffer_get_internal_format (framebuffer) == COGL_PIXEL_FORMAT_A_8) - { - priv->bits.alpha = priv->bits.red; - priv->bits.red = 0; - } - - COGL_NOTE (OFFSCREEN, - "RGBA/D/S Bits for framebuffer[%p, %s]: %d, %d, %d, %d, %d, %d", - framebuffer, - COGL_IS_OFFSCREEN (framebuffer) ? "offscreen" : "onscreen", - priv->bits.red, - priv->bits.blue, - priv->bits.green, - priv->bits.alpha, - priv->bits.depth, - priv->bits.stencil); - - priv->dirty_bitmasks = FALSE; -} - -void -_cogl_framebuffer_gl_query_bits (CoglFramebuffer *framebuffer, - CoglFramebufferBits *bits) -{ - CoglGlFramebuffer *gl_framebuffer = - cogl_gl_framebuffer_from_framebuffer (framebuffer); - CoglGlFramebufferPrivate *priv = - cogl_gl_framebuffer_get_instance_private (gl_framebuffer); - - _cogl_framebuffer_init_bits (framebuffer); - - /* TODO: cache these in some driver specific location not - * directly as part of CoglFramebuffer. */ - *bits = priv->bits; -} - void _cogl_framebuffer_gl_finish (CoglFramebuffer *framebuffer) { @@ -1365,16 +697,9 @@ EXIT: static void cogl_gl_framebuffer_init (CoglGlFramebuffer *gl_framebuffer) { - CoglGlFramebufferPrivate *priv = - cogl_gl_framebuffer_get_instance_private (gl_framebuffer); - - priv->dirty_bitmasks = TRUE; } static void cogl_gl_framebuffer_class_init (CoglGlFramebufferClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = cogl_gl_framebuffer_dispose; } diff --git a/cogl/cogl/driver/gl/cogl-gl-framebuffer-back.c b/cogl/cogl/driver/gl/cogl-gl-framebuffer-back.c new file mode 100644 index 000000000..8a49d6f80 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-gl-framebuffer-back.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2007,2008,2009,2012 Intel Corporation. + * Copyright (C) 2018 DisplayLink (UK) Ltd. + * Copyright (C) 2020 Red Hat + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include "cogl-config.h" + +#include "driver/gl/cogl-gl-framebuffer-back.h" + +#include + +#include "cogl-context-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-offscreen-private.h" +#include "driver/gl/cogl-util-gl-private.h" + +struct _CoglGlFramebufferBack +{ + CoglGlFramebuffer parent; + + gboolean dirty_bitmasks; + CoglFramebufferBits bits; +}; + +G_DEFINE_TYPE (CoglGlFramebufferBack, cogl_gl_framebuffer_back, + COGL_TYPE_GL_FRAMEBUFFER) + +static gboolean +ensure_bits_initialized (CoglGlFramebufferBack *gl_framebuffer_back) +{ + CoglFramebufferDriver *driver = COGL_FRAMEBUFFER_DRIVER (gl_framebuffer_back); + CoglFramebuffer *framebuffer = + cogl_framebuffer_driver_get_framebuffer (driver); + CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); + CoglFramebufferBits *bits = &gl_framebuffer_back->bits; + g_autoptr (GError) error = NULL; + + if (!gl_framebuffer_back->dirty_bitmasks) + return TRUE; + + cogl_context_flush_framebuffer_state (ctx, + framebuffer, + framebuffer, + COGL_FRAMEBUFFER_STATE_BIND); + +#ifdef HAVE_COGL_GL + if (ctx->driver == COGL_DRIVER_GL3) + { + const struct { + GLenum attachment, pname; + size_t offset; + } params[] = { + { + .attachment = GL_BACK_LEFT, + .pname = GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, + .offset = offsetof (CoglFramebufferBits, red), + }, + { + .attachment = GL_BACK_LEFT, + .pname = GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, + .offset = offsetof (CoglFramebufferBits, green), + }, + { + .attachment = GL_BACK_LEFT, + .pname = GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, + .offset = offsetof (CoglFramebufferBits, blue), + }, + { + .attachment = GL_BACK_LEFT, + .pname = GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, + .offset = offsetof (CoglFramebufferBits, alpha), + }, + { + .attachment = GL_DEPTH, + .pname = GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, + .offset = offsetof (CoglFramebufferBits, depth), + }, + { + .attachment = GL_STENCIL, + .pname = GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, + .offset = offsetof (CoglFramebufferBits, stencil), + }, + }; + int i; + + for (i = 0; i < G_N_ELEMENTS (params); i++) + { + int *value = + (int *) ((uint8_t *) bits + params[i].offset); + + GE (ctx, glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, + params[i].attachment, + params[i].pname, + value)); + } + } + else +#endif /* HAVE_COGL_GL */ + { + GE (ctx, glGetIntegerv (GL_RED_BITS, &bits->red)); + GE (ctx, glGetIntegerv (GL_GREEN_BITS, &bits->green)); + GE (ctx, glGetIntegerv (GL_BLUE_BITS, &bits->blue)); + GE (ctx, glGetIntegerv (GL_ALPHA_BITS, &bits->alpha)); + GE (ctx, glGetIntegerv (GL_DEPTH_BITS, &bits->depth)); + GE (ctx, glGetIntegerv (GL_STENCIL_BITS, &bits->stencil)); + } + + COGL_NOTE (FRAMEBUFFER, + "RGBA/D/S Bits for framebuffer[%p, %s]: %d, %d, %d, %d, %d, %d", + framebuffer, + COGL_IS_OFFSCREEN (framebuffer) ? "offscreen" : "onscreen", + bits->red, + bits->blue, + bits->green, + bits->alpha, + bits->depth, + bits->stencil); + + gl_framebuffer_back->dirty_bitmasks = FALSE; + + return TRUE; +} + +static void +cogl_gl_framebuffer_back_query_bits (CoglFramebufferDriver *driver, + CoglFramebufferBits *bits) +{ + CoglGlFramebufferBack *gl_framebuffer_back = COGL_GL_FRAMEBUFFER_BACK (driver); + + if (!ensure_bits_initialized (gl_framebuffer_back)) + return; + + *bits = gl_framebuffer_back->bits; +} + +static void +cogl_gl_framebuffer_back_bind (CoglGlFramebuffer *gl_framebuffer, + GLenum target) +{ + CoglGlFramebufferBack *gl_framebuffer_back = + COGL_GL_FRAMEBUFFER_BACK (gl_framebuffer); + CoglFramebufferDriver *driver = COGL_FRAMEBUFFER_DRIVER (gl_framebuffer_back); + CoglFramebuffer *framebuffer = + cogl_framebuffer_driver_get_framebuffer (driver); + CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); + const CoglWinsysVtable *winsys = + _cogl_framebuffer_get_winsys (framebuffer); + + winsys->onscreen_bind (COGL_ONSCREEN (framebuffer)); + + GE (ctx, glBindFramebuffer (target, 0)); + + /* Initialise the glDrawBuffer state the first time the context + * is bound to the default framebuffer. If the winsys is using a + * surfaceless context for the initial make current then the + * default draw buffer will be GL_NONE so we need to correct + * that. We can't do it any earlier because binding GL_BACK when + * there is no default framebuffer won't work */ + if (!ctx->was_bound_to_onscreen) + { + if (ctx->glDrawBuffer) + { + GE (ctx, glDrawBuffer (GL_BACK)); + } + else if (ctx->glDrawBuffers) + { + /* glDrawBuffer isn't available on GLES 3.0 so we need + * to be able to use glDrawBuffers as well. On GLES 2 + * neither is available but the state should always be + * GL_BACK anyway so we don't need to set anything. On + * desktop GL this must be GL_BACK_LEFT instead of + * GL_BACK but as this code path will only be hit for + * GLES we can just use GL_BACK. */ + static const GLenum buffers[] = { GL_BACK }; + + GE (ctx, glDrawBuffers (G_N_ELEMENTS (buffers), buffers)); + } + + ctx->was_bound_to_onscreen = TRUE; + } +} + +CoglGlFramebufferBack * +cogl_gl_framebuffer_back_new (CoglFramebuffer *framebuffer, + const CoglFramebufferDriverConfig *driver_config, + GError **error) +{ + if (!COGL_IS_ONSCREEN (framebuffer)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Incompatible framebuffer"); + return NULL; + } + + return g_object_new (COGL_TYPE_GL_FRAMEBUFFER_BACK, + "framebuffer", framebuffer, + NULL); +} + +static void +cogl_gl_framebuffer_back_init (CoglGlFramebufferBack *gl_framebuffer_back) +{ + gl_framebuffer_back->dirty_bitmasks = TRUE; +} + +static void +cogl_gl_framebuffer_back_class_init (CoglGlFramebufferBackClass *klass) +{ + CoglFramebufferDriverClass *driver_class = + COGL_FRAMEBUFFER_DRIVER_CLASS (klass); + CoglGlFramebufferClass *gl_framebuffer_class = + COGL_GL_FRAMEBUFFER_CLASS (klass); + + driver_class->query_bits = cogl_gl_framebuffer_back_query_bits; + + gl_framebuffer_class->bind = cogl_gl_framebuffer_back_bind; +} diff --git a/cogl/cogl/driver/gl/cogl-gl-framebuffer-back.h b/cogl/cogl/driver/gl/cogl-gl-framebuffer-back.h new file mode 100644 index 000000000..417f8a738 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-gl-framebuffer-back.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 Red Hat + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef COGL_GL_FRAMEBUFFER_BACK_H +#define COGL_GL_FRAMEBUFFER_BACK_H + +#include "cogl-framebuffer-gl-private.h" + +#define COGL_TYPE_GL_FRAMEBUFFER_BACK (cogl_gl_framebuffer_back_get_type ()) +G_DECLARE_FINAL_TYPE (CoglGlFramebufferBack, cogl_gl_framebuffer_back, + COGL, GL_FRAMEBUFFER_BACK, + CoglGlFramebuffer) + +CoglGlFramebufferBack * +cogl_gl_framebuffer_back_new (CoglFramebuffer *framebuffer, + const CoglFramebufferDriverConfig *driver_config, + GError **error); + +#endif /* COGL_GL_FRAMEBUFFER_BACK_H */ diff --git a/cogl/cogl/driver/gl/cogl-gl-framebuffer-fbo.c b/cogl/cogl/driver/gl/cogl-gl-framebuffer-fbo.c new file mode 100644 index 000000000..368e04b6f --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-gl-framebuffer-fbo.c @@ -0,0 +1,611 @@ +/* + * Copyright (C) 2007,2008,2009,2012 Intel Corporation. + * Copyright (C) 2018 DisplayLink (UK) Ltd. + * Copyright (C) 2020 Red Hat + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include "cogl-config.h" + +#include "driver/gl/cogl-gl-framebuffer-fbo.h" + +#include + +#include "cogl-context-private.h" +#include "cogl-framebuffer-private.h" +#include "cogl-offscreen-private.h" +#include "driver/gl/cogl-texture-gl-private.h" +#include "driver/gl/cogl-util-gl-private.h" + +typedef struct _CoglGlFbo +{ + GLuint fbo_handle; + GList *renderbuffers; + int samples_per_pixel; +} CoglGlFbo; + +struct _CoglGlFramebufferFbo +{ + CoglGlFramebuffer parent; + + CoglGlFbo gl_fbo; + + gboolean dirty_bitmasks; + CoglFramebufferBits bits; +}; + +G_DEFINE_TYPE (CoglGlFramebufferFbo, cogl_gl_framebuffer_fbo, + COGL_TYPE_GL_FRAMEBUFFER) + +static gboolean +ensure_bits_initialized (CoglGlFramebufferFbo *gl_framebuffer_fbo) +{ + CoglFramebufferDriver *driver = COGL_FRAMEBUFFER_DRIVER (gl_framebuffer_fbo); + CoglFramebuffer *framebuffer = + cogl_framebuffer_driver_get_framebuffer (driver); + CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); + CoglFramebufferBits *bits = &gl_framebuffer_fbo->bits; + g_autoptr (GError) error = NULL; + + if (!gl_framebuffer_fbo->dirty_bitmasks) + return TRUE; + + cogl_context_flush_framebuffer_state (ctx, + framebuffer, + framebuffer, + COGL_FRAMEBUFFER_STATE_BIND); + +#ifdef HAVE_COGL_GL + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_QUERY_FRAMEBUFFER_BITS)) + { + const struct { + GLenum attachment, pname; + size_t offset; + } params[] = { + { + .attachment = GL_COLOR_ATTACHMENT0, + .pname = GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, + .offset = offsetof (CoglFramebufferBits, red), + }, + { + .attachment = GL_COLOR_ATTACHMENT0, + .pname = GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, + .offset = offsetof (CoglFramebufferBits, green), + }, + { + .attachment = GL_COLOR_ATTACHMENT0, + .pname = GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, + .offset = offsetof (CoglFramebufferBits, blue), + }, + { + .attachment = GL_COLOR_ATTACHMENT0, + .pname = GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, + .offset = offsetof (CoglFramebufferBits, alpha), + }, + { + .attachment = GL_DEPTH_ATTACHMENT, + .pname = GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, + .offset = offsetof (CoglFramebufferBits, depth), + }, + { + .attachment = GL_STENCIL_ATTACHMENT, + .pname = GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, + .offset = offsetof (CoglFramebufferBits, stencil), + }, + }; + int i; + + for (i = 0; i < G_N_ELEMENTS (params); i++) + { + int *value = + (int *) ((uint8_t *) bits + params[i].offset); + + GE (ctx, glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, + params[i].attachment, + params[i].pname, + value)); + } + } + else +#endif /* HAVE_COGL_GL */ + { + GE (ctx, glGetIntegerv (GL_RED_BITS, &bits->red)); + GE (ctx, glGetIntegerv (GL_GREEN_BITS, &bits->green)); + GE (ctx, glGetIntegerv (GL_BLUE_BITS, &bits->blue)); + GE (ctx, glGetIntegerv (GL_ALPHA_BITS, &bits->alpha)); + GE (ctx, glGetIntegerv (GL_DEPTH_BITS, &bits->depth)); + GE (ctx, glGetIntegerv (GL_STENCIL_BITS, &bits->stencil)); + } + + if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) && + (cogl_framebuffer_get_internal_format (framebuffer) == + COGL_PIXEL_FORMAT_A_8)) + { + bits->alpha = bits->red; + bits->red = 0; + } + + COGL_NOTE (FRAMEBUFFER, + "RGBA/D/S Bits for framebuffer[%p, %s]: %d, %d, %d, %d, %d, %d", + framebuffer, + COGL_IS_OFFSCREEN (framebuffer) ? "offscreen" : "onscreen", + bits->red, + bits->blue, + bits->green, + bits->alpha, + bits->depth, + bits->stencil); + + gl_framebuffer_fbo->dirty_bitmasks = FALSE; + + return TRUE; +} + +static void +cogl_gl_framebuffer_fbo_query_bits (CoglFramebufferDriver *driver, + CoglFramebufferBits *bits) +{ + CoglGlFramebufferFbo *gl_framebuffer_fbo = COGL_GL_FRAMEBUFFER_FBO (driver); + + if (!ensure_bits_initialized (gl_framebuffer_fbo)) + return; + + *bits = gl_framebuffer_fbo->bits; +} + +static void +cogl_gl_framebuffer_fbo_bind (CoglGlFramebuffer *gl_framebuffer, + GLenum target) +{ + CoglGlFramebufferFbo *gl_framebuffer_fbo = + COGL_GL_FRAMEBUFFER_FBO (gl_framebuffer); + CoglFramebufferDriver *driver = COGL_FRAMEBUFFER_DRIVER (gl_framebuffer_fbo); + CoglFramebuffer *framebuffer = + cogl_framebuffer_driver_get_framebuffer (driver); + CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); + + GE (ctx, glBindFramebuffer (target, gl_framebuffer_fbo->gl_fbo.fbo_handle)); +} + +static GList * +try_creating_renderbuffers (CoglContext *ctx, + int width, + int height, + CoglOffscreenAllocateFlags flags, + int n_samples) +{ + GList *renderbuffers = NULL; + GLuint gl_depth_stencil_handle; + + if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL) + { + GLenum format; + + /* WebGL adds a GL_DEPTH_STENCIL_ATTACHMENT and requires that we + * use the GL_DEPTH_STENCIL format. */ + /* Although GL_OES_packed_depth_stencil is mostly equivalent to + * GL_EXT_packed_depth_stencil, one notable difference is that + * GL_OES_packed_depth_stencil doesn't allow GL_DEPTH_STENCIL to + * be passed as an internal format to glRenderbufferStorage. + */ + if (_cogl_has_private_feature + (ctx, COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL)) + format = GL_DEPTH_STENCIL; + else + { + g_return_val_if_fail ( + _cogl_has_private_feature (ctx, + COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL), + NULL); + format = GL_DEPTH24_STENCIL8; + } + + /* 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, + format, + width, height)); + else + GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, format, + 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)); + renderbuffers = + g_list_prepend (renderbuffers, + GUINT_TO_POINTER (gl_depth_stencil_handle)); + } + + if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH) + { + GLuint gl_depth_handle; + + 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)); + renderbuffers = + g_list_prepend (renderbuffers, GUINT_TO_POINTER (gl_depth_handle)); + } + + if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL) + { + GLuint gl_stencil_handle; + + 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)); + renderbuffers = + g_list_prepend (renderbuffers, GUINT_TO_POINTER (gl_stencil_handle)); + } + + return renderbuffers; +} + +static void +delete_renderbuffers (CoglContext *ctx, + GList *renderbuffers) +{ + GList *l; + + for (l = renderbuffers; l; l = l->next) + { + GLuint renderbuffer = GPOINTER_TO_UINT (l->data); + GE (ctx, glDeleteRenderbuffers (1, &renderbuffer)); + } + + g_list_free (renderbuffers); +} + +/* + * NB: This function may be called with a standalone GLES2 context + * bound so we can create a shadow framebuffer that wraps the same + * CoglTexture as the given CoglOffscreen. This function shouldn't + * modify anything in + */ +static gboolean +try_creating_fbo (CoglContext *ctx, + CoglTexture *texture, + int texture_level, + int texture_level_width, + int texture_level_height, + const CoglFramebufferConfig *config, + CoglOffscreenAllocateFlags flags, + CoglGlFbo *gl_fbo) +{ + GLuint tex_gl_handle; + GLenum tex_gl_target; + GLenum status; + int n_samples; + + if (!cogl_texture_get_gl_texture (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 (config->samples_per_pixel) + { + if (!ctx->glFramebufferTexture2DMultisampleIMG) + return FALSE; + n_samples = config->samples_per_pixel; + } + else + n_samples = 0; + + /* 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->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_BIND; + + /* Generate framebuffer */ + ctx->glGenFramebuffers (1, &gl_fbo->fbo_handle); + GE (ctx, glBindFramebuffer (GL_FRAMEBUFFER, gl_fbo->fbo_handle)); + + if (n_samples) + { + GE (ctx, glFramebufferTexture2DMultisampleIMG (GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + tex_gl_target, tex_gl_handle, + n_samples, + texture_level)); + } + else + GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + tex_gl_target, tex_gl_handle, + texture_level)); + + if (flags) + { + gl_fbo->renderbuffers = + try_creating_renderbuffers (ctx, + texture_level_width, + texture_level_height, + flags, + n_samples); + } + + /* Make sure it's complete */ + status = ctx->glCheckFramebufferStatus (GL_FRAMEBUFFER); + + if (status != GL_FRAMEBUFFER_COMPLETE) + { + GE (ctx, glDeleteFramebuffers (1, &gl_fbo->fbo_handle)); + + delete_renderbuffers (ctx, gl_fbo->renderbuffers); + gl_fbo->renderbuffers = NULL; + + return FALSE; + } + + /* Update the real number of samples_per_pixel now that we have a + * complete framebuffer */ + if (n_samples) + { + GLenum attachment = GL_COLOR_ATTACHMENT0; + GLenum pname = GL_TEXTURE_SAMPLES_IMG; + int texture_samples; + + GE( ctx, glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, + attachment, + pname, + &texture_samples) ); + gl_fbo->samples_per_pixel = texture_samples; + } + + return TRUE; +} + +CoglGlFramebufferFbo * +cogl_gl_framebuffer_fbo_new (CoglFramebuffer *framebuffer, + const CoglFramebufferDriverConfig *driver_config, + GError **error) +{ + CoglContext *context = cogl_framebuffer_get_context (framebuffer); + CoglOffscreen *offscreen; + CoglTexture *texture; + int texture_level; + int level_width; + int level_height; + const CoglFramebufferConfig *config; + CoglGlFbo *gl_fbo; + CoglGlFramebufferFbo *gl_framebuffer_fbo; + CoglOffscreenAllocateFlags allocate_flags; + + if (!COGL_IS_OFFSCREEN (framebuffer)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Incompatible framebuffer"); + return NULL; + } + + offscreen = COGL_OFFSCREEN (framebuffer); + texture = cogl_offscreen_get_texture (offscreen); + texture_level = cogl_offscreen_get_texture_level (offscreen); + + g_return_val_if_fail (texture_level < _cogl_texture_get_n_levels (texture), + NULL); + + _cogl_texture_get_level_size (texture, + texture_level, + &level_width, + &level_height, + NULL); + + /* 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_gl_flush_legacy_texobj_filters (texture, + GL_NEAREST, GL_NEAREST); + + config = cogl_framebuffer_get_config (framebuffer); + + gl_framebuffer_fbo = g_object_new (COGL_TYPE_GL_FRAMEBUFFER_FBO, + "framebuffer", framebuffer, + NULL); + gl_fbo = &gl_framebuffer_fbo->gl_fbo; + + if ((driver_config->disable_depth_and_stencil && + try_creating_fbo (context, + texture, + texture_level, + level_width, + level_height, + config, + allocate_flags = 0, + gl_fbo)) || + + (context->have_last_offscreen_allocate_flags && + try_creating_fbo (context, + texture, + texture_level, + level_width, + level_height, + config, + allocate_flags = context->last_offscreen_allocate_flags, + gl_fbo)) || + + ( + /* NB: WebGL introduces a DEPTH_STENCIL_ATTACHMENT and doesn't + * need an extension to handle _FLAG_DEPTH_STENCIL */ + (_cogl_has_private_feature + (context, COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL) || + _cogl_has_private_feature + (context, COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL)) && + try_creating_fbo (context, + texture, + texture_level, + level_width, + level_height, + config, + allocate_flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL, + gl_fbo)) || + + try_creating_fbo (context, + texture, + texture_level, + level_width, + level_height, + config, + allocate_flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH | + COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL, + gl_fbo) || + + try_creating_fbo (context, + texture, + texture_level, + level_width, + level_height, + config, + allocate_flags = COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL, + gl_fbo) || + + try_creating_fbo (context, + texture, + texture_level, + level_width, + level_height, + config, + allocate_flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH, + gl_fbo) || + + try_creating_fbo (context, + texture, + texture_level, + level_width, + level_height, + config, + allocate_flags = 0, + gl_fbo)) + { + cogl_framebuffer_update_samples_per_pixel (framebuffer, + gl_fbo->samples_per_pixel); + + if (!driver_config->disable_depth_and_stencil) + { + /* Record that the last set of flags succeeded so that we can + try that set first next time */ + context->last_offscreen_allocate_flags = allocate_flags; + context->have_last_offscreen_allocate_flags = TRUE; + } + + return gl_framebuffer_fbo; + } + else + { + g_object_unref (gl_framebuffer_fbo); + g_set_error (error, COGL_FRAMEBUFFER_ERROR, + COGL_FRAMEBUFFER_ERROR_ALLOCATE, + "Failed to create an OpenGL framebuffer object"); + return NULL; + } +} + +static void +cogl_gl_framebuffer_fbo_dispose (GObject *object) +{ + CoglGlFramebufferFbo *gl_framebuffer_fbo = COGL_GL_FRAMEBUFFER_FBO (object); + CoglFramebufferDriver *driver = COGL_FRAMEBUFFER_DRIVER (gl_framebuffer_fbo); + CoglFramebuffer *framebuffer = + cogl_framebuffer_driver_get_framebuffer (driver); + CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); + + delete_renderbuffers (ctx, gl_framebuffer_fbo->gl_fbo.renderbuffers); + gl_framebuffer_fbo->gl_fbo.renderbuffers = NULL; + + if (gl_framebuffer_fbo->gl_fbo.fbo_handle) + { + GE (ctx, glDeleteFramebuffers (1, + &gl_framebuffer_fbo->gl_fbo.fbo_handle)); + gl_framebuffer_fbo->gl_fbo.fbo_handle = 0; + } + + G_OBJECT_CLASS (cogl_gl_framebuffer_fbo_parent_class)->dispose (object); +} + +static void +cogl_gl_framebuffer_fbo_init (CoglGlFramebufferFbo *gl_framebuffer_fbo) +{ + gl_framebuffer_fbo->dirty_bitmasks = TRUE; +} + +static void +cogl_gl_framebuffer_fbo_class_init (CoglGlFramebufferFboClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + CoglFramebufferDriverClass *driver_class = + COGL_FRAMEBUFFER_DRIVER_CLASS (klass); + CoglGlFramebufferClass *gl_framebuffer_class = + COGL_GL_FRAMEBUFFER_CLASS (klass); + + object_class->dispose = cogl_gl_framebuffer_fbo_dispose; + + driver_class->query_bits = cogl_gl_framebuffer_fbo_query_bits; + + gl_framebuffer_class->bind = cogl_gl_framebuffer_fbo_bind; +} diff --git a/cogl/cogl/driver/gl/cogl-gl-framebuffer-fbo.h b/cogl/cogl/driver/gl/cogl-gl-framebuffer-fbo.h new file mode 100644 index 000000000..896847cf8 --- /dev/null +++ b/cogl/cogl/driver/gl/cogl-gl-framebuffer-fbo.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 Red Hat + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#ifndef COGL_GL_FRAMEBUFFER_FBO_H +#define COGL_GL_FRAMEBUFFER_FBO_H + +#include "driver/gl/cogl-framebuffer-gl-private.h" + +#define COGL_TYPE_GL_FRAMEBUFFER_FBO (cogl_gl_framebuffer_fbo_get_type ()) +G_DECLARE_FINAL_TYPE (CoglGlFramebufferFbo, cogl_gl_framebuffer_fbo, + COGL, GL_FRAMEBUFFER_FBO, + CoglGlFramebuffer) + +CoglGlFramebufferFbo * +cogl_gl_framebuffer_fbo_new (CoglFramebuffer *framebuffer, + const CoglFramebufferDriverConfig *driver_config, + GError **error); + +#endif /* COGL_GL_FRAMEBUFFER_FBO_H */ diff --git a/cogl/cogl/driver/gl/cogl-util-gl-private.h b/cogl/cogl/driver/gl/cogl-util-gl-private.h index 6bada2cd0..0b7eaa772 100644 --- a/cogl/cogl/driver/gl/cogl-util-gl-private.h +++ b/cogl/cogl/driver/gl/cogl-util-gl-private.h @@ -146,4 +146,87 @@ _cogl_gl_util_parse_gl_version (const char *version_string, CoglGraphicsResetStatus _cogl_gl_get_graphics_reset_status (CoglContext *context); +#ifndef GL_FRAMEBUFFER +#define GL_FRAMEBUFFER 0x8D40 +#endif +#ifndef GL_RENDERBUFFER +#define GL_RENDERBUFFER 0x8D41 +#endif +#ifndef GL_STENCIL_ATTACHMENT +#define GL_STENCIL_ATTACHMENT 0x8D00 +#endif +#ifndef GL_COLOR_ATTACHMENT0 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#endif +#ifndef GL_FRAMEBUFFER_COMPLETE +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#endif +#ifndef GL_STENCIL_INDEX8 +#define GL_STENCIL_INDEX8 0x8D48 +#endif +#ifndef GL_DEPTH_STENCIL +#define GL_DEPTH_STENCIL 0x84F9 +#endif +#ifndef GL_DEPTH24_STENCIL8 +#define GL_DEPTH24_STENCIL8 0x88F0 +#endif +#ifndef GL_DEPTH_ATTACHMENT +#define GL_DEPTH_ATTACHMENT 0x8D00 +#endif +#ifndef GL_DEPTH_STENCIL_ATTACHMENT +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#endif +#ifndef GL_DEPTH_COMPONENT16 +#define GL_DEPTH_COMPONENT16 0x81A5 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#endif +#ifndef GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#endif +#ifndef GL_READ_FRAMEBUFFER +#define GL_READ_FRAMEBUFFER 0x8CA8 +#endif +#ifndef GL_DRAW_FRAMEBUFFER +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#endif +#ifndef GL_TEXTURE_SAMPLES_IMG +#define GL_TEXTURE_SAMPLES_IMG 0x9136 +#endif +#ifndef GL_PACK_INVERT_MESA +#define GL_PACK_INVERT_MESA 0x8758 +#endif +#ifndef GL_PACK_REVERSE_ROW_ORDER_ANGLE +#define GL_PACK_REVERSE_ROW_ORDER_ANGLE 0x93A4 +#endif +#ifndef GL_BACK_LEFT +#define GL_BACK_LEFT 0x0402 +#endif +#ifndef GL_BACK_RIGHT +#define GL_BACK_RIGHT 0x0403 +#endif + +#ifndef GL_COLOR +#define GL_COLOR 0x1800 +#endif +#ifndef GL_DEPTH +#define GL_DEPTH 0x1801 +#endif +#ifndef GL_STENCIL +#define GL_STENCIL 0x1802 +#endif + #endif /* _COGL_UTIL_GL_PRIVATE_H_ */ diff --git a/cogl/cogl/driver/gl/cogl-util-gl.c b/cogl/cogl/driver/gl/cogl-util-gl.c index 2c1c4e544..9b59bef1d 100644 --- a/cogl/cogl/driver/gl/cogl-util-gl.c +++ b/cogl/cogl/driver/gl/cogl-util-gl.c @@ -35,6 +35,8 @@ #include "cogl-types.h" #include "cogl-context-private.h" #include "driver/gl/cogl-framebuffer-gl-private.h" +#include "driver/gl/cogl-gl-framebuffer-fbo.h" +#include "driver/gl/cogl-gl-framebuffer-back.h" #include "driver/gl/cogl-pipeline-opengl-private.h" #include "driver/gl/cogl-util-gl-private.h" @@ -131,6 +133,46 @@ _cogl_driver_gl_context_deinit (CoglContext *context) g_free (context->driver_context); } +CoglFramebufferDriver * +_cogl_driver_gl_create_framebuffer_driver (CoglContext *context, + CoglFramebuffer *framebuffer, + const CoglFramebufferDriverConfig *driver_config, + GError **error) +{ + g_return_val_if_fail (driver_config, NULL); + + switch (driver_config->type) + { + case COGL_FRAMEBUFFER_DRIVER_TYPE_FBO: + { + CoglGlFramebufferFbo *gl_framebuffer_fbo; + + gl_framebuffer_fbo = cogl_gl_framebuffer_fbo_new (framebuffer, + driver_config, + error); + if (!gl_framebuffer_fbo) + return NULL; + + return COGL_FRAMEBUFFER_DRIVER (gl_framebuffer_fbo); + } + case COGL_FRAMEBUFFER_DRIVER_TYPE_BACK: + { + CoglGlFramebufferBack *gl_framebuffer_back; + + gl_framebuffer_back = cogl_gl_framebuffer_back_new (framebuffer, + driver_config, + error); + if (!gl_framebuffer_back) + return NULL; + + return COGL_FRAMEBUFFER_DRIVER (gl_framebuffer_back); + } + } + + g_assert_not_reached (); + return NULL; +} + void _cogl_driver_gl_flush_framebuffer_state (CoglContext *ctx, CoglFramebuffer *draw_buffer, @@ -138,6 +180,7 @@ _cogl_driver_gl_flush_framebuffer_state (CoglContext *ctx, CoglFramebufferState state) { CoglGlFramebuffer *draw_gl_framebuffer; + CoglGlFramebuffer *read_gl_framebuffer; unsigned long differences; /* We can assume that any state that has changed for the current @@ -193,13 +236,20 @@ _cogl_driver_gl_flush_framebuffer_state (CoglContext *ctx, if (G_UNLIKELY (!cogl_framebuffer_is_allocated (read_buffer))) cogl_framebuffer_allocate (read_buffer, NULL); + draw_gl_framebuffer = + COGL_GL_FRAMEBUFFER (cogl_framebuffer_get_driver (draw_buffer)); + read_gl_framebuffer = + COGL_GL_FRAMEBUFFER (cogl_framebuffer_get_driver (read_buffer)); + /* We handle buffer binding separately since the method depends on whether * we are binding the same buffer for read and write or not unlike all * other state that only relates to the draw_buffer. */ if (differences & COGL_FRAMEBUFFER_STATE_BIND) { if (draw_buffer == read_buffer) - _cogl_framebuffer_gl_bind (draw_buffer, GL_FRAMEBUFFER); + { + cogl_gl_framebuffer_bind (draw_gl_framebuffer, GL_FRAMEBUFFER); + } else { /* NB: Currently we only take advantage of binding separate @@ -207,15 +257,13 @@ _cogl_driver_gl_flush_framebuffer_state (CoglContext *ctx, g_return_if_fail (cogl_has_feature (ctx, COGL_FEATURE_ID_BLIT_FRAMEBUFFER)); - _cogl_framebuffer_gl_bind (draw_buffer, GL_DRAW_FRAMEBUFFER); - _cogl_framebuffer_gl_bind (read_buffer, GL_READ_FRAMEBUFFER); + cogl_gl_framebuffer_bind (draw_gl_framebuffer, GL_DRAW_FRAMEBUFFER); + cogl_gl_framebuffer_bind (read_gl_framebuffer, GL_READ_FRAMEBUFFER); } differences &= ~COGL_FRAMEBUFFER_STATE_BIND; } - draw_gl_framebuffer = - COGL_GL_FRAMEBUFFER (cogl_framebuffer_get_driver (draw_buffer)); cogl_gl_framebuffer_flush_state_differences (draw_gl_framebuffer, differences); diff --git a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c index 31f6c898f..bcb7f566e 100644 --- a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c +++ b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c @@ -572,7 +572,6 @@ _cogl_driver_gl = _cogl_driver_gl_create_framebuffer_driver, _cogl_driver_gl_flush_framebuffer_state, _cogl_framebuffer_gl_clear, - _cogl_framebuffer_gl_query_bits, _cogl_framebuffer_gl_finish, _cogl_framebuffer_gl_flush, _cogl_framebuffer_gl_discard_buffers, diff --git a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c index 506d5250b..a0688bd51 100644 --- a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c +++ b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c @@ -460,7 +460,6 @@ _cogl_driver_gles = _cogl_driver_gl_create_framebuffer_driver, _cogl_driver_gl_flush_framebuffer_state, _cogl_framebuffer_gl_clear, - _cogl_framebuffer_gl_query_bits, _cogl_framebuffer_gl_finish, _cogl_framebuffer_gl_flush, _cogl_framebuffer_gl_discard_buffers, diff --git a/cogl/cogl/driver/nop/cogl-driver-nop.c b/cogl/cogl/driver/nop/cogl-driver-nop.c index 7dfc1c5bc..c52a2c299 100644 --- a/cogl/cogl/driver/nop/cogl-driver-nop.c +++ b/cogl/cogl/driver/nop/cogl-driver-nop.c @@ -100,7 +100,6 @@ _cogl_driver_nop = _cogl_driver_nop_create_framebuffer_driver, _cogl_driver_nop_flush_framebuffer_state, _cogl_framebuffer_nop_clear, - _cogl_framebuffer_nop_query_bits, _cogl_framebuffer_nop_finish, _cogl_framebuffer_nop_flush, _cogl_framebuffer_nop_discard_buffers, diff --git a/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h b/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h index 19013c752..a1cf77c89 100644 --- a/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h +++ b/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h @@ -45,10 +45,6 @@ _cogl_framebuffer_nop_clear (CoglFramebuffer *framebuffer, float blue, float alpha); -void -_cogl_framebuffer_nop_query_bits (CoglFramebuffer *framebuffer, - CoglFramebufferBits *bits); - void _cogl_framebuffer_nop_finish (CoglFramebuffer *framebuffer); diff --git a/cogl/cogl/driver/nop/cogl-framebuffer-nop.c b/cogl/cogl/driver/nop/cogl-framebuffer-nop.c index d7fc293f2..70488a1b5 100644 --- a/cogl/cogl/driver/nop/cogl-framebuffer-nop.c +++ b/cogl/cogl/driver/nop/cogl-framebuffer-nop.c @@ -45,13 +45,6 @@ _cogl_framebuffer_nop_clear (CoglFramebuffer *framebuffer, { } -void -_cogl_framebuffer_nop_query_bits (CoglFramebuffer *framebuffer, - CoglFramebufferBits *bits) -{ - memset (bits, 0, sizeof (CoglFramebufferBits)); -} - void _cogl_framebuffer_nop_finish (CoglFramebuffer *framebuffer) { diff --git a/cogl/cogl/driver/nop/cogl-nop-framebuffer.c b/cogl/cogl/driver/nop/cogl-nop-framebuffer.c index 85bed95a7..3e155f3c3 100644 --- a/cogl/cogl/driver/nop/cogl-nop-framebuffer.c +++ b/cogl/cogl/driver/nop/cogl-nop-framebuffer.c @@ -27,6 +27,8 @@ #include "cogl-nop-framebuffer.h" +#include "cogl-framebuffer-private.h" + struct _CoglNopFramebuffer { CoglFramebufferDriver parent; @@ -35,6 +37,13 @@ struct _CoglNopFramebuffer G_DEFINE_TYPE (CoglNopFramebuffer, cogl_nop_framebuffer, COGL_TYPE_FRAMEBUFFER_DRIVER) +static void +cogl_nop_framebuffer_query_bits (CoglFramebufferDriver *driver, + CoglFramebufferBits *bits) +{ + memset (bits, 0, sizeof (CoglFramebufferBits)); +} + static void cogl_nop_framebuffer_init (CoglNopFramebuffer *nop_framebuffer) { @@ -43,4 +52,8 @@ cogl_nop_framebuffer_init (CoglNopFramebuffer *nop_framebuffer) static void cogl_nop_framebuffer_class_init (CoglNopFramebufferClass *klass) { + CoglFramebufferDriverClass *driver_class = + COGL_FRAMEBUFFER_DRIVER_CLASS (klass); + + driver_class->query_bits = cogl_nop_framebuffer_query_bits; } diff --git a/cogl/cogl/meson.build b/cogl/cogl/meson.build index 638f5a827..27ad72b2d 100644 --- a/cogl/cogl/meson.build +++ b/cogl/cogl/meson.build @@ -155,6 +155,10 @@ cogl_common_driver_sources = [ 'driver/gl/cogl-util-gl.c', 'driver/gl/cogl-framebuffer-gl-private.h', 'driver/gl/cogl-framebuffer-gl.c', + 'driver/gl/cogl-gl-framebuffer-back.c', + 'driver/gl/cogl-gl-framebuffer-back.h', + 'driver/gl/cogl-gl-framebuffer-fbo.c', + 'driver/gl/cogl-gl-framebuffer-fbo.h', 'driver/gl/cogl-texture-gl-private.h', 'driver/gl/cogl-texture-gl.c', 'driver/gl/cogl-texture-2d-gl-private.h', diff --git a/cogl/cogl/winsys/cogl-onscreen-glx.c b/cogl/cogl/winsys/cogl-onscreen-glx.c index b578494b0..24ee9cddc 100644 --- a/cogl/cogl/winsys/cogl-onscreen-glx.c +++ b/cogl/cogl/winsys/cogl-onscreen-glx.c @@ -1131,8 +1131,14 @@ cogl_onscreen_glx_new (CoglContext *context, int width, int height) { + CoglFramebufferDriverConfig driver_config; + + driver_config = (CoglFramebufferDriverConfig) { + .type = COGL_FRAMEBUFFER_DRIVER_TYPE_BACK, + }; return g_object_new (COGL_TYPE_ONSCREEN_GLX, "context", context, + "driver-config", &driver_config, "width", width, "height", height, NULL); diff --git a/cogl/cogl/winsys/cogl-onscreen-xlib.c b/cogl/cogl/winsys/cogl-onscreen-xlib.c index 97540639a..8eaf1976d 100644 --- a/cogl/cogl/winsys/cogl-onscreen-xlib.c +++ b/cogl/cogl/winsys/cogl-onscreen-xlib.c @@ -338,8 +338,14 @@ cogl_onscreen_xlib_new (CoglContext *context, int width, int height) { + CoglFramebufferDriverConfig driver_config; + + driver_config = (CoglFramebufferDriverConfig) { + .type = COGL_FRAMEBUFFER_DRIVER_TYPE_BACK, + }; return g_object_new (COGL_TYPE_ONSCREEN_XLIB, "context", context, + "driver-config", &driver_config, "width", width, "height", height, NULL); diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c index 45bd769bc..e21d90545 100644 --- a/src/backends/native/meta-onscreen-native.c +++ b/src/backends/native/meta-onscreen-native.c @@ -1979,9 +1979,14 @@ meta_onscreen_native_new (MetaRendererNative *renderer_native, int height) { MetaOnscreenNative *onscreen_native; + CoglFramebufferDriverConfig driver_config; + driver_config = (CoglFramebufferDriverConfig) { + .type = COGL_FRAMEBUFFER_DRIVER_TYPE_BACK, + }; onscreen_native = g_object_new (META_TYPE_ONSCREEN_NATIVE, "context", cogl_context, + "driver-config", &driver_config, "width", width, "height", height, NULL);