[gtk/wip/chergert/glproto: 178/493] implement basics of blur node/texture
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/chergert/glproto: 178/493] implement basics of blur node/texture
- Date: Fri, 19 Feb 2021 02:25:09 +0000 (UTC)
commit 64a1def64fa82b6a18f6f33ed5a31b5f135b5126
Author: Christian Hergert <chergert redhat com>
Date: Tue Jan 12 16:23:29 2021 -0800
implement basics of blur node/texture
gsk/next/gskglcommandqueue.c | 6 +-
gsk/next/gskglcommandqueueprivate.h | 2 +
gsk/next/gskgldriver.c | 6 +
gsk/next/gskgldriverprivate.h | 2 +
gsk/next/gskglrenderer.c | 7 +-
gsk/next/gskglrenderjob.c | 231 +++++++++++++++++++++++++++++++++++-
6 files changed, 245 insertions(+), 9 deletions(-)
---
diff --git a/gsk/next/gskglcommandqueue.c b/gsk/next/gskglcommandqueue.c
index 5956fb3637..77854e7e2b 100644
--- a/gsk/next/gskglcommandqueue.c
+++ b/gsk/next/gskglcommandqueue.c
@@ -843,6 +843,8 @@ gboolean
gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self,
int width,
int height,
+ int min_filter,
+ int mag_filter,
guint *out_fbo_id,
guint *out_texture_id)
{
@@ -857,7 +859,9 @@ gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self,
gsk_gl_command_queue_save (self);
- texture_id = gsk_gl_command_queue_create_texture (self, width, height, GL_NEAREST, GL_NEAREST);
+ texture_id = gsk_gl_command_queue_create_texture (self,
+ width, height,
+ min_filter, mag_filter);
if (texture_id == -1)
{
diff --git a/gsk/next/gskglcommandqueueprivate.h b/gsk/next/gskglcommandqueueprivate.h
index 2ecbaa27cf..e76742945b 100644
--- a/gsk/next/gskglcommandqueueprivate.h
+++ b/gsk/next/gskglcommandqueueprivate.h
@@ -133,6 +133,8 @@ guint gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue
gboolean gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self,
int width,
int height,
+ int min_filter,
+ int mag_filter,
guint *out_fbo_id,
guint *out_texture_id);
void gsk_gl_command_queue_delete_program (GskGLCommandQueue *self,
diff --git a/gsk/next/gskgldriver.c b/gsk/next/gskgldriver.c
index d9d5018d9b..64cb9894d4 100644
--- a/gsk/next/gskgldriver.c
+++ b/gsk/next/gskgldriver.c
@@ -383,6 +383,8 @@ gsk_next_driver_get_context (GskNextDriver *self)
* @self: a #GskNextDriver
* @width: the width for the render target
* @height: the height for the render target
+ * @min_filter: the min filter to use for the texture
+ * @mag_filter: the mag filter to use for the texture
* @out_fbo_id: (out): location for framebuffer id
* @out_texture_id: (out): location for texture id
*
@@ -396,6 +398,8 @@ gboolean
gsk_next_driver_create_render_target (GskNextDriver *self,
int width,
int height,
+ int min_filter,
+ int mag_filter,
guint *out_fbo_id,
guint *out_texture_id)
{
@@ -405,6 +409,8 @@ gsk_next_driver_create_render_target (GskNextDriver *self,
return gsk_gl_command_queue_create_render_target (self->command_queue,
width,
height,
+ min_filter,
+ mag_filter,
out_fbo_id,
out_texture_id);
}
diff --git a/gsk/next/gskgldriverprivate.h b/gsk/next/gskgldriverprivate.h
index 388df35784..af8f392fe9 100644
--- a/gsk/next/gskgldriverprivate.h
+++ b/gsk/next/gskgldriverprivate.h
@@ -101,6 +101,8 @@ void gsk_next_driver_autorelease_framebuffer (GskNextDriver *se
gboolean gsk_next_driver_create_render_target (GskNextDriver *self,
int width,
int height,
+ int min_filter,
+ int mag_filter,
guint *out_fbo_id,
guint *out_texture_id);
void gsk_next_driver_begin_frame (GskNextDriver *self);
diff --git a/gsk/next/gskglrenderer.c b/gsk/next/gskglrenderer.c
index e79513578b..b7b1b13811 100644
--- a/gsk/next/gskglrenderer.c
+++ b/gsk/next/gskglrenderer.c
@@ -261,10 +261,9 @@ gsk_gl_renderer_render_texture (GskRenderer *renderer,
gdk_gl_context_make_current (context);
if (!gsk_next_driver_create_render_target (self->driver,
- width,
- height,
- &fbo_id,
- &texture_id))
+ width, height,
+ GL_NEAREST, GL_NEAREST,
+ &fbo_id, &texture_id))
return NULL;
gsk_next_driver_autorelease_framebuffer (self->driver, fbo_id);
diff --git a/gsk/next/gskglrenderjob.c b/gsk/next/gskglrenderjob.c
index 9c4b264282..1d414cc921 100644
--- a/gsk/next/gskglrenderjob.c
+++ b/gsk/next/gskglrenderjob.c
@@ -1790,11 +1790,230 @@ gsk_gl_render_job_visit_shadow_node (GskGLRenderJob *job,
gsk_gl_render_job_visit_as_fallback (job, node);
}
+static inline guint
+blur_offscreen (GskGLRenderJob *job,
+ GskGLRenderOffscreen *offscreen,
+ int texture_to_blur_width,
+ int texture_to_blur_height,
+ float blur_radius_x,
+ float blur_radius_y)
+{
+ const GskRoundedRect new_clip = GSK_ROUNDED_RECT_INIT (0, 0, texture_to_blur_width,
texture_to_blur_height);
+ guint pass1_texture_id, pass1_framebuffer_id;
+ guint pass2_texture_id, pass2_framebuffer_id;
+ GskGLRenderState state;
+
+ g_assert (blur_radius_x > 0);
+ g_assert (blur_radius_y > 0);
+ g_assert (offscreen->texture_id > 0);
+ g_assert (offscreen->area.size.width > 0);
+ g_assert (offscreen->area.size.height > 0);
+
+ gsk_next_driver_create_render_target (job->driver,
+ MAX (texture_to_blur_width, 1),
+ MAX (texture_to_blur_height, 1),
+ GL_NEAREST, GL_NEAREST,
+ &pass1_framebuffer_id,
+ &pass1_texture_id);
+
+ gsk_next_driver_autorelease_framebuffer (job->driver, pass1_framebuffer_id);
+
+ if (texture_to_blur_width <= 0 || texture_to_blur_height <= 0)
+ return pass1_texture_id;
+
+ gsk_next_driver_create_render_target (job->driver,
+ texture_to_blur_width,
+ texture_to_blur_height,
+ GL_NEAREST, GL_NEAREST,
+ &pass2_framebuffer_id,
+ &pass2_texture_id);
+
+ gsk_next_driver_autorelease_framebuffer (job->driver, pass2_framebuffer_id);
+
+ gsk_gl_render_state_save (&state, job);
+
+ init_projection_matrix (&job->projection, &new_clip.bounds);
+ gsk_gl_render_job_set_modelview (job, NULL);
+ job->viewport = new_clip.bounds;
+ gsk_gl_render_job_push_clip (job, &new_clip);
+
+ /* Bind new framebuffer and clear it */
+ gsk_gl_command_queue_bind_framebuffer (job->command_queue, pass1_framebuffer_id);
+ gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
+
+ /* Begin drawing the first horizontal pass, using offscreen as the
+ * source texture for the program.
+ */
+ gsk_gl_program_begin_draw (job->driver->blur,
+ &job->viewport,
+ &job->projection,
+ gsk_gl_render_job_get_modelview_matrix (job),
+ gsk_gl_render_job_get_clip (job),
+ job->alpha);
+ gsk_gl_program_set_uniform_texture (job->driver->blur,
+ UNIFORM_SHARED_SOURCE,
+ GL_TEXTURE_2D,
+ GL_TEXTURE0,
+ offscreen->texture_id);
+ gsk_gl_program_set_uniform1f (job->driver->blur,
+ UNIFORM_BLUR_RADIUS,
+ blur_radius_x);
+ gsk_gl_program_set_uniform2f (job->driver->blur,
+ UNIFORM_BLUR_SIZE,
+ texture_to_blur_width,
+ texture_to_blur_height);
+ gsk_gl_program_set_uniform2f (job->driver->blur,
+ UNIFORM_BLUR_DIR,
+ 1, 0);
+ gsk_gl_render_job_load_vertices_from_offscreen (job, &new_clip.bounds, offscreen);
+ gsk_gl_program_end_draw (job->driver->blur);
+
+ /* Bind second pass framebuffer and clear it */
+ gsk_gl_command_queue_bind_framebuffer (job->command_queue, pass2_framebuffer_id);
+ gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
+
+ /* Draw using blur program with first pass as source texture */
+ gsk_gl_program_begin_draw (job->driver->blur,
+ &job->viewport,
+ &job->projection,
+ gsk_gl_render_job_get_modelview_matrix (job),
+ gsk_gl_render_job_get_clip (job),
+ job->alpha);
+ gsk_gl_program_set_uniform_texture (job->driver->blur,
+ UNIFORM_SHARED_SOURCE,
+ GL_TEXTURE_2D,
+ GL_TEXTURE0,
+ pass1_texture_id);
+ gsk_gl_program_set_uniform1f (job->driver->blur,
+ UNIFORM_BLUR_RADIUS,
+ blur_radius_y);
+ gsk_gl_program_set_uniform2f (job->driver->blur,
+ UNIFORM_BLUR_SIZE,
+ texture_to_blur_width,
+ texture_to_blur_height);
+ gsk_gl_program_set_uniform2f (job->driver->blur,
+ UNIFORM_BLUR_DIR,
+ 0, 1);
+ gsk_gl_render_job_load_vertices_from_offscreen (job, &new_clip.bounds, offscreen);
+ gsk_gl_program_end_draw (job->driver->blur);
+
+ gsk_gl_render_job_pop_modelview (job);
+ gsk_gl_render_job_pop_clip (job);
+
+ gsk_gl_render_state_restore (&state, job);
+
+ glDeleteTextures (1, &pass1_texture_id);
+
+ return pass2_texture_id;
+}
+
+static void
+blur_node (GskGLRenderJob *job,
+ GskGLRenderOffscreen *offscreen,
+ GskRenderNode *node,
+ float blur_radius,
+ float *min_x,
+ float *max_x,
+ float *min_y,
+ float *max_y)
+{
+ const float blur_extra = blur_radius * 2.0; /* 2.0 = shader radius_multiplier */
+ const float half_blur_extra = blur_radius;
+ float scale_x = job->scale_x;
+ float scale_y = job->scale_y;
+ float texture_width;
+ float texture_height;
+
+ g_assert (blur_radius > 0);
+
+ /* Increase texture size for the given blur radius and scale it */
+ texture_width = ceilf ((node->bounds.size.width + blur_extra));
+ texture_height = ceilf ((node->bounds.size.height + blur_extra));
+
+ /* Only blur this if the out region has no texture id yet */
+ if (offscreen->texture_id == 0)
+ {
+ const graphene_rect_t bounds = GRAPHENE_RECT_INIT (node->bounds.origin.x - half_blur_extra,
+ node->bounds.origin.y - half_blur_extra,
+ texture_width, texture_height);
+
+ offscreen->bounds = &bounds;
+ offscreen->reset_clip = TRUE;
+ offscreen->force_offscreen = TRUE;
+
+ if (!gsk_gl_render_job_visit_node_with_offscreen (job, node, offscreen))
+ g_assert_not_reached ();
+
+ g_assert (offscreen->texture_id != 0);
+ g_assert (offscreen->do_not_cache == FALSE);
+
+ offscreen->texture_id = blur_offscreen (job,
+ offscreen,
+ texture_width * scale_x,
+ texture_height * scale_y,
+ blur_radius * scale_x,
+ blur_radius * scale_y);
+ init_full_texture_region (&offscreen->area);
+ }
+
+ *min_x = job->offset_x + node->bounds.origin.x - half_blur_extra;
+ *max_x = job->offset_x + node->bounds.origin.x + node->bounds.size.width + half_blur_extra;
+ *min_y = job->offset_y + node->bounds.origin.y - half_blur_extra;
+ *max_y = job->offset_y + node->bounds.origin.y + node->bounds.size.height + half_blur_extra;
+}
+
static void
gsk_gl_render_job_visit_blur_node (GskGLRenderJob *job,
GskRenderNode *node)
{
- gsk_gl_render_job_visit_as_fallback (job, node);
+ GskRenderNode *child = gsk_blur_node_get_child (node);
+ float blur_radius = gsk_blur_node_get_radius (node);
+ GskGLRenderOffscreen offscreen = {0};
+ GskTextureKey key;
+ gboolean cache_texture;
+ float min_x;
+ float max_x;
+ float min_y;
+ float max_y;
+
+ g_assert (blur_radius > 0);
+
+ if (node_is_invisible (child))
+ return;
+
+ key.pointer = node;
+ key.pointer_is_child = FALSE;
+ key.scale_x = job->scale_x;
+ key.scale_y = job->scale_y;
+ key.filter = GL_NEAREST;
+
+ offscreen.texture_id = gsk_next_driver_lookup_texture (job->driver, &key);
+ cache_texture = offscreen.texture_id == 0;
+
+ blur_node (job,
+ &offscreen,
+ child,
+ blur_radius,
+ &min_x, &max_x, &min_y, &max_y);
+
+ g_assert (offscreen.texture_id != 0);
+
+ if (cache_texture)
+ gsk_next_driver_cache_texture (job->driver, &key, offscreen.texture_id);
+
+ gsk_gl_program_begin_draw (job->driver->blit,
+ &job->viewport,
+ &job->projection,
+ gsk_gl_render_job_get_modelview_matrix (job),
+ gsk_gl_render_job_get_clip (job),
+ job->alpha);
+ gsk_gl_program_set_uniform_texture (job->driver->blit,
+ UNIFORM_SHARED_SOURCE,
+ GL_TEXTURE_2D,
+ GL_TEXTURE0,
+ offscreen.texture_id);
+ gsk_gl_render_job_draw_coords (job, min_x, min_y, max_x, max_y);
+ gsk_gl_program_end_draw (job->driver->blit);
}
static void
@@ -1986,7 +2205,10 @@ gsk_gl_render_job_visit_node (GskGLRenderJob *job,
break;
case GSK_BLUR_NODE:
- gsk_gl_render_job_visit_blur_node (job, node);
+ if (gsk_blur_node_get_radius (node) > 0)
+ gsk_gl_render_job_visit_blur_node (job, node);
+ else
+ gsk_gl_render_job_visit_node (job, gsk_blur_node_get_child (node));
break;
case GSK_BORDER_NODE:
@@ -2224,6 +2446,7 @@ gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob *job,
gsk_next_driver_create_render_target (job->driver,
width, height,
+ filter, filter,
&texture_id, &framebuffer_id);
if (gdk_gl_context_has_debug (job->command_queue->context))
@@ -2307,8 +2530,8 @@ gsk_gl_render_job_render_flipped (GskGLRenderJob *job,
if (!gsk_gl_command_queue_create_render_target (job->command_queue,
job->viewport.size.width,
job->viewport.size.height,
- &framebuffer_id,
- &texture_id))
+ GL_NEAREST, GL_NEAREST,
+ &framebuffer_id, &texture_id))
return;
gsk_next_driver_begin_frame (job->driver);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]