From a02ea8c3934eddbf63b635b47fb7bb7bf88d8b73 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 30 Sep 2024 19:16:57 -0400 Subject: [PATCH 1/6] tools: Drop support for the gl renderer The GL renderer is going away in 4.18. --- tools/gtk-rendernode-tool-benchmark.c | 2 +- tools/gtk-rendernode-tool-utils.c | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/tools/gtk-rendernode-tool-benchmark.c b/tools/gtk-rendernode-tool-benchmark.c index fe4b88c81ca..2f2afeaf61c 100644 --- a/tools/gtk-rendernode-tool-benchmark.c +++ b/tools/gtk-rendernode-tool-benchmark.c @@ -137,7 +137,7 @@ do_benchmark (int *argc, } if (renderers == NULL || renderers[0] == NULL) - renderers = g_strdupv ((char **) (const char *[]) { "gl", "ngl", "vulkan", "cairo", NULL }); + renderers = g_strdupv ((char **) (const char *[]) { "ngl", "vulkan", "cairo", NULL }); node = load_node_file (filenames[0]); diff --git a/tools/gtk-rendernode-tool-utils.c b/tools/gtk-rendernode-tool-utils.c index deab3e36fcd..9e8da7f2ef9 100644 --- a/tools/gtk-rendernode-tool-utils.c +++ b/tools/gtk-rendernode-tool-utils.c @@ -89,9 +89,6 @@ get_renderer_for_name (const char *renderer_name) #endif else if (g_ascii_strcasecmp (renderer_name, "cairo") == 0) return gsk_cairo_renderer_new (); - else if (g_ascii_strcasecmp (renderer_name, "opengl") == 0 || - g_ascii_strcasecmp (renderer_name, "gl") == 0) - return gsk_gl_renderer_new (); else if (g_ascii_strcasecmp (renderer_name, "ngl") == 0) return gsk_ngl_renderer_new (); #ifdef GDK_RENDERING_VULKAN -- GitLab From af2aadbe9b4683b752eb2ee10776e36f3fd9f2a7 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 30 Sep 2024 17:08:50 -0400 Subject: [PATCH 2/6] Drop the gl renderer This requires cleanups in quite a few places. --- gsk/gl/gskglattachmentstate.c | 116 - gsk/gl/gskglattachmentstateprivate.h | 102 - gsk/gl/gskglbuffer.c | 69 - gsk/gl/gskglbufferprivate.h | 80 - gsk/gl/gskglcommandqueue.c | 1782 ------- gsk/gl/gskglcommandqueueprivate.h | 431 -- gsk/gl/gskglcompiler.c | 703 --- gsk/gl/gskglcompilerprivate.h | 68 - gsk/gl/gskgldriver.c | 1867 ------- gsk/gl/gskgldriverprivate.h | 261 - gsk/gl/gskglglyphlibrary.c | 439 -- gsk/gl/gskglglyphlibraryprivate.h | 103 - gsk/gl/gskgliconlibrary.c | 212 - gsk/gl/gskgliconlibraryprivate.h | 58 - gsk/gl/gskglprofiler.c | 179 - gsk/gl/gskglprofilerprivate.h | 16 - gsk/gl/gskglprogram.c | 173 - gsk/gl/gskglprogramprivate.h | 318 -- gsk/gl/gskglprograms.defs | 104 - gsk/gl/gskglrenderer.c | 451 +- gsk/gl/gskglrendererprivate.h | 38 - gsk/gl/gskglrenderjob.c | 4725 ----------------- gsk/gl/gskglrenderjobprivate.h | 36 - gsk/gl/gskglshadowlibrary.c | 256 - gsk/gl/gskglshadowlibraryprivate.h | 42 - gsk/gl/gskgltexture.c | 95 - gsk/gl/gskgltexturelibrary.c | 583 -- gsk/gl/gskgltexturelibraryprivate.h | 232 - gsk/gl/gskgltextureprivate.h | 93 - gsk/gl/gskgltypesprivate.h | 68 - gsk/gl/gskgluniformstate.c | 269 - gsk/gl/gskgluniformstateprivate.h | 834 --- gsk/gl/inlinearray.h | 75 - gsk/gl/ninesliceprivate.h | 308 -- gsk/gl/resources/blend.glsl | 314 -- gsk/gl/resources/blit.glsl | 17 - gsk/gl/resources/blur.glsl | 59 - gsk/gl/resources/border.glsl | 45 - gsk/gl/resources/color.glsl | 20 - gsk/gl/resources/color_matrix.glsl | 27 - gsk/gl/resources/coloring.glsl | 33 - gsk/gl/resources/conic_gradient.glsl | 85 - gsk/gl/resources/cross_fade.glsl | 24 - gsk/gl/resources/custom.glsl | 25 - gsk/gl/resources/external.glsl | 34 - gsk/gl/resources/filled_border.glsl | 50 - gsk/gl/resources/inset_shadow.glsl | 47 - gsk/gl/resources/linear_gradient.glsl | 107 - gsk/gl/resources/mask.glsl | 39 - gsk/gl/resources/outset_shadow.glsl | 35 - gsk/gl/resources/preamble.fs.glsl | 166 - gsk/gl/resources/preamble.glsl | 118 - gsk/gl/resources/preamble.vs.glsl | 75 - gsk/gl/resources/premultiply.glsl | 19 - gsk/gl/resources/radial_gradient.glsl | 87 - gsk/gl/resources/repeat.glsl | 44 - gsk/gl/resources/unblurred_outset_shadow.glsl | 44 - gsk/gl/stb_rect_pack.c | 427 -- gsk/gl/stb_rect_pack.h | 197 - gsk/gskrenderer.c | 3 - gsk/meson.build | 46 +- 61 files changed, 5 insertions(+), 17368 deletions(-) delete mode 100644 gsk/gl/gskglattachmentstate.c delete mode 100644 gsk/gl/gskglattachmentstateprivate.h delete mode 100644 gsk/gl/gskglbuffer.c delete mode 100644 gsk/gl/gskglbufferprivate.h delete mode 100644 gsk/gl/gskglcommandqueue.c delete mode 100644 gsk/gl/gskglcommandqueueprivate.h delete mode 100644 gsk/gl/gskglcompiler.c delete mode 100644 gsk/gl/gskglcompilerprivate.h delete mode 100644 gsk/gl/gskgldriver.c delete mode 100644 gsk/gl/gskgldriverprivate.h delete mode 100644 gsk/gl/gskglglyphlibrary.c delete mode 100644 gsk/gl/gskglglyphlibraryprivate.h delete mode 100644 gsk/gl/gskgliconlibrary.c delete mode 100644 gsk/gl/gskgliconlibraryprivate.h delete mode 100644 gsk/gl/gskglprofiler.c delete mode 100644 gsk/gl/gskglprofilerprivate.h delete mode 100644 gsk/gl/gskglprogram.c delete mode 100644 gsk/gl/gskglprogramprivate.h delete mode 100644 gsk/gl/gskglprograms.defs delete mode 100644 gsk/gl/gskglrendererprivate.h delete mode 100644 gsk/gl/gskglrenderjob.c delete mode 100644 gsk/gl/gskglrenderjobprivate.h delete mode 100644 gsk/gl/gskglshadowlibrary.c delete mode 100644 gsk/gl/gskglshadowlibraryprivate.h delete mode 100644 gsk/gl/gskgltexture.c delete mode 100644 gsk/gl/gskgltexturelibrary.c delete mode 100644 gsk/gl/gskgltexturelibraryprivate.h delete mode 100644 gsk/gl/gskgltextureprivate.h delete mode 100644 gsk/gl/gskgltypesprivate.h delete mode 100644 gsk/gl/gskgluniformstate.c delete mode 100644 gsk/gl/gskgluniformstateprivate.h delete mode 100644 gsk/gl/inlinearray.h delete mode 100644 gsk/gl/ninesliceprivate.h delete mode 100644 gsk/gl/resources/blend.glsl delete mode 100644 gsk/gl/resources/blit.glsl delete mode 100644 gsk/gl/resources/blur.glsl delete mode 100644 gsk/gl/resources/border.glsl delete mode 100644 gsk/gl/resources/color.glsl delete mode 100644 gsk/gl/resources/color_matrix.glsl delete mode 100644 gsk/gl/resources/coloring.glsl delete mode 100644 gsk/gl/resources/conic_gradient.glsl delete mode 100644 gsk/gl/resources/cross_fade.glsl delete mode 100644 gsk/gl/resources/custom.glsl delete mode 100644 gsk/gl/resources/external.glsl delete mode 100644 gsk/gl/resources/filled_border.glsl delete mode 100644 gsk/gl/resources/inset_shadow.glsl delete mode 100644 gsk/gl/resources/linear_gradient.glsl delete mode 100644 gsk/gl/resources/mask.glsl delete mode 100644 gsk/gl/resources/outset_shadow.glsl delete mode 100644 gsk/gl/resources/preamble.fs.glsl delete mode 100644 gsk/gl/resources/preamble.glsl delete mode 100644 gsk/gl/resources/preamble.vs.glsl delete mode 100644 gsk/gl/resources/premultiply.glsl delete mode 100644 gsk/gl/resources/radial_gradient.glsl delete mode 100644 gsk/gl/resources/repeat.glsl delete mode 100644 gsk/gl/resources/unblurred_outset_shadow.glsl delete mode 100644 gsk/gl/stb_rect_pack.c delete mode 100644 gsk/gl/stb_rect_pack.h diff --git a/gsk/gl/gskglattachmentstate.c b/gsk/gl/gskglattachmentstate.c deleted file mode 100644 index 3765e95f799..00000000000 --- a/gsk/gl/gskglattachmentstate.c +++ /dev/null @@ -1,116 +0,0 @@ -/* gskglattachmentstate.c - * - * Copyright 2020 Christian Hergert - * - * This file is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "gskglattachmentstateprivate.h" - -GskGLAttachmentState * -gsk_gl_attachment_state_new (void) -{ - GskGLAttachmentState *self; - - self = g_atomic_rc_box_new0 (GskGLAttachmentState); - - self->fbo.changed = FALSE; - self->fbo.id = 0; - self->n_changed = 0; - - /* Initialize textures, assume we are 2D by default since it - * doesn't really matter until we bind something other than - * GL_TEXTURE0 to it anyway. - */ - for (guint i = 0; i < G_N_ELEMENTS (self->textures); i++) - { - self->textures[i].target = GL_TEXTURE_2D; - self->textures[i].texture = GL_TEXTURE0; - self->textures[i].id = 0; - self->textures[i].changed = FALSE; - self->textures[i].initial = TRUE; - self->textures[i].sampler = sampler_index (GL_LINEAR, GL_LINEAR); - } - - return self; -} - -GskGLAttachmentState * -gsk_gl_attachment_state_ref (GskGLAttachmentState *self) -{ - return g_atomic_rc_box_acquire (self); -} - -void -gsk_gl_attachment_state_unref (GskGLAttachmentState *self) -{ - g_atomic_rc_box_release (self); -} - -void -gsk_gl_attachment_state_bind_texture (GskGLAttachmentState *self, - GLenum target, - GLenum texture, - guint id, - GLint min_filter, - GLint mag_filter) -{ - GskGLBindTexture *attach; - unsigned int sampler; - - g_assert (self != NULL); - g_assert (target == GL_TEXTURE_1D || - target == GL_TEXTURE_2D || - target == GL_TEXTURE_3D || - target == GL_TEXTURE_EXTERNAL_OES); - g_assert (texture >= GL_TEXTURE0 && texture <= GL_TEXTURE16); - g_assert (texture - GL_TEXTURE0 < G_N_ELEMENTS (self->textures)); - - attach = &self->textures[texture - GL_TEXTURE0]; - - sampler = sampler_index (min_filter, mag_filter); - - if (attach->target != target || attach->texture != texture || attach->id != id || - attach->sampler != sampler) - { - attach->target = target; - attach->texture = texture; - attach->id = id; - attach->initial = FALSE; - attach->sampler = sampler; - - if (attach->changed == FALSE) - { - attach->changed = TRUE; - self->n_changed++; - } - } -} - -void -gsk_gl_attachment_state_bind_framebuffer (GskGLAttachmentState *self, - guint id) -{ - g_assert (self != NULL); - - if (self->fbo.id != id) - { - self->fbo.id = id; - self->fbo.changed = TRUE; - } -} diff --git a/gsk/gl/gskglattachmentstateprivate.h b/gsk/gl/gskglattachmentstateprivate.h deleted file mode 100644 index e2ecf1a5394..00000000000 --- a/gsk/gl/gskglattachmentstateprivate.h +++ /dev/null @@ -1,102 +0,0 @@ -/* gskglattachmentstateprivate.h - * - * Copyright 2020 Christian Hergert - * - * This file is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "gskgltypesprivate.h" - -G_BEGIN_DECLS - -typedef struct _GskGLAttachmentState GskGLAttachmentState; -typedef struct _GskGLBindFramebuffer GskGLBindFramebuffer; -typedef struct _GskGLBindTexture GskGLBindTexture; - -#define GSK_GL_N_FILTERS 3 -#define SAMPLER_EXTERNAL 9 - -G_STATIC_ASSERT (SAMPLER_EXTERNAL >= GSK_GL_N_FILTERS * GSK_GL_N_FILTERS); - -static inline guint -filter_index (GLint filter) -{ - switch (filter) - { - case GL_LINEAR: - return 0; - case GL_NEAREST: - return 1; - case GL_LINEAR_MIPMAP_LINEAR: - return 2; - default: - g_assert_not_reached (); - } -} - -static inline guint -sampler_index (GLint min_filter, - GLint mag_filter) -{ - return filter_index (min_filter) * GSK_GL_N_FILTERS + filter_index (mag_filter); -} - -struct _GskGLBindTexture -{ - guint changed : 1; - guint initial : 1; - GLenum target : 26; - guint sampler : 4; - GLenum texture; - guint id; -}; - -G_STATIC_ASSERT (sizeof (GskGLBindTexture) == 12); - -struct _GskGLBindFramebuffer -{ - guint changed : 1; - guint id : 31; -}; - -G_STATIC_ASSERT (sizeof (GskGLBindFramebuffer) == 4); - -/* Increase if shaders add more textures */ -#define GSK_GL_MAX_TEXTURES_PER_PROGRAM 4 - -struct _GskGLAttachmentState -{ - GskGLBindFramebuffer fbo; - GskGLBindTexture textures[GSK_GL_MAX_TEXTURES_PER_PROGRAM]; - guint n_changed; -}; - -GskGLAttachmentState *gsk_gl_attachment_state_new (void); -GskGLAttachmentState *gsk_gl_attachment_state_ref (GskGLAttachmentState *self); -void gsk_gl_attachment_state_unref (GskGLAttachmentState *self); -void gsk_gl_attachment_state_bind_texture (GskGLAttachmentState *self, - GLenum target, - GLenum texture, - guint id, - GLint min_filter, - GLint mag_filter); -void gsk_gl_attachment_state_bind_framebuffer (GskGLAttachmentState *self, - guint id); - -G_END_DECLS - diff --git a/gsk/gl/gskglbuffer.c b/gsk/gl/gskglbuffer.c deleted file mode 100644 index 63aabd1dc3b..00000000000 --- a/gsk/gl/gskglbuffer.c +++ /dev/null @@ -1,69 +0,0 @@ -/* gskglbufferprivate.h - * - * Copyright 2020 Christian Hergert - * - * This file is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include - -#include "gskglbufferprivate.h" - -/** - * gsk_gl_buffer_init: - * @target: the target buffer such as %GL_ARRAY_BUFFER or %GL_UNIFORM_BUFFER - * @element_size: the size of elements within the buffer - * - * Creates a new `GskGLBuffer` which can be used to deliver data to shaders - * within a GLSL program. You can use this to store vertices such as with - * %GL_ARRAY_BUFFER or uniform data with %GL_UNIFORM_BUFFER. - */ -void -gsk_gl_buffer_init (GskGLBuffer *self, - GLenum target, - guint element_size) -{ - memset (self, 0, sizeof *self); - - /* Default to 2 pages, power-of-two growth from there */ - self->buffer_len = 4096 * 2; - self->buffer = g_malloc (self->buffer_len); - self->target = target; - self->element_size = element_size; -} - -GLuint -gsk_gl_buffer_submit (GskGLBuffer *buffer) -{ - GLuint id; - - glGenBuffers (1, &id); - glBindBuffer (buffer->target, id); - glBufferData (buffer->target, buffer->buffer_pos, buffer->buffer, GL_STATIC_DRAW); - - buffer->buffer_pos = 0; - buffer->count = 0; - - return id; -} - -void -gsk_gl_buffer_destroy (GskGLBuffer *buffer) -{ - g_clear_pointer (&buffer->buffer, g_free); -} diff --git a/gsk/gl/gskglbufferprivate.h b/gsk/gl/gskglbufferprivate.h deleted file mode 100644 index fa4e65cc377..00000000000 --- a/gsk/gl/gskglbufferprivate.h +++ /dev/null @@ -1,80 +0,0 @@ -/* gskglbufferprivate.h - * - * Copyright 2020 Christian Hergert - * - * This file is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "gskgltypesprivate.h" - -G_BEGIN_DECLS - -typedef struct _GskGLBuffer -{ - guint8 *buffer; - gsize buffer_pos; - gsize buffer_len; - guint count; - GLenum target; - gsize element_size; -} GskGLBuffer; - -void gsk_gl_buffer_init (GskGLBuffer *self, - GLenum target, - guint element_size); -void gsk_gl_buffer_destroy (GskGLBuffer *buffer); -GLuint gsk_gl_buffer_submit (GskGLBuffer *buffer); - -static inline gpointer -gsk_gl_buffer_advance (GskGLBuffer *buffer, - guint count) -{ - gpointer ret; - gsize to_alloc = count * buffer->element_size; - - if G_UNLIKELY (buffer->buffer_pos + to_alloc > buffer->buffer_len) - { - while (buffer->buffer_pos + to_alloc > buffer->buffer_len) - buffer->buffer_len *= 2; - buffer->buffer = g_realloc (buffer->buffer, buffer->buffer_len); - } - - ret = buffer->buffer + buffer->buffer_pos; - - buffer->buffer_pos += to_alloc; - buffer->count += count; - - return ret; -} - -static inline void -gsk_gl_buffer_retract (GskGLBuffer *buffer, - guint count) -{ - buffer->buffer_pos -= count * buffer->element_size; - buffer->count -= count; -} - -static inline guint -gsk_gl_buffer_get_offset (GskGLBuffer *buffer) -{ - return buffer->count; -} - -G_END_DECLS - diff --git a/gsk/gl/gskglcommandqueue.c b/gsk/gl/gskglcommandqueue.c deleted file mode 100644 index ae4ee166869..00000000000 --- a/gsk/gl/gskglcommandqueue.c +++ /dev/null @@ -1,1782 +0,0 @@ -/* gskglcommandqueue.c - * - * Copyright 2017 Timm Bäder - * Copyright 2018 Matthias Clasen - * Copyright 2018 Alexander Larsson - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include "gskglattachmentstateprivate.h" -#include "gskglbufferprivate.h" -#include "gskglcommandqueueprivate.h" -#include "gskgluniformstateprivate.h" - -#include "inlinearray.h" - -G_DEFINE_TYPE (GskGLCommandQueue, gsk_gl_command_queue, G_TYPE_OBJECT) - -#if 0 -G_GNUC_UNUSED static inline void -print_uniform (GskGLUniformFormat format, - guint array_count, - gconstpointer valueptr) -{ - const union { - graphene_matrix_t matrix[0]; - GskRoundedRect rounded_rect[0]; - float fval[0]; - int ival[0]; - guint uval[0]; - } *data = valueptr; - - switch (format) - { - case GSK_GL_UNIFORM_FORMAT_1F: - g_printerr ("1f<%f>", data->fval[0]); - break; - - case GSK_GL_UNIFORM_FORMAT_2F: - g_printerr ("2f<%f,%f>", data->fval[0], data->fval[1]); - break; - - case GSK_GL_UNIFORM_FORMAT_3F: - g_printerr ("3f<%f,%f,%f>", data->fval[0], data->fval[1], data->fval[2]); - break; - - case GSK_GL_UNIFORM_FORMAT_4F: - g_printerr ("4f<%f,%f,%f,%f>", data->fval[0], data->fval[1], data->fval[2], data->fval[3]); - break; - - case GSK_GL_UNIFORM_FORMAT_1I: - case GSK_GL_UNIFORM_FORMAT_TEXTURE: - g_printerr ("1i<%d>", data->ival[0]); - break; - - case GSK_GL_UNIFORM_FORMAT_1UI: - g_printerr ("1ui<%u>", data->uval[0]); - break; - - case GSK_GL_UNIFORM_FORMAT_COLOR: { - char *str = gdk_rgba_to_string (valueptr); - g_printerr ("%s", str); - g_free (str); - break; - } - - case GSK_GL_UNIFORM_FORMAT_ROUNDED_RECT: { - char *str = gsk_rounded_rect_to_string (valueptr); - g_printerr ("%s", str); - g_free (str); - break; - } - - case GSK_GL_UNIFORM_FORMAT_MATRIX: { - float mat[16]; - graphene_matrix_to_float (&data->matrix[0], mat); - g_printerr ("matrix<"); - for (guint i = 0; i < G_N_ELEMENTS (mat)-1; i++) - g_printerr ("%f,", mat[i]); - g_printerr ("%f>", mat[G_N_ELEMENTS (mat)-1]); - break; - } - - case GSK_GL_UNIFORM_FORMAT_1FV: - case GSK_GL_UNIFORM_FORMAT_2FV: - case GSK_GL_UNIFORM_FORMAT_3FV: - case GSK_GL_UNIFORM_FORMAT_4FV: - /* non-V variants are -4 from V variants */ - format -= 4; - g_printerr ("["); - for (guint i = 0; i < array_count; i++) - { - print_uniform (format, 0, valueptr); - if (i + 1 != array_count) - g_printerr (","); - valueptr = ((guint8*)valueptr + gsk_gl_uniform_format_size (format)); - } - g_printerr ("]"); - break; - - case GSK_GL_UNIFORM_FORMAT_2I: - g_printerr ("2i<%d,%d>", data->ival[0], data->ival[1]); - break; - - case GSK_GL_UNIFORM_FORMAT_3I: - g_printerr ("3i<%d,%d,%d>", data->ival[0], data->ival[1], data->ival[2]); - break; - - case GSK_GL_UNIFORM_FORMAT_4I: - g_printerr ("3i<%d,%d,%d,%d>", data->ival[0], data->ival[1], data->ival[2], data->ival[3]); - break; - - case GSK_GL_UNIFORM_FORMAT_LAST: - default: - g_assert_not_reached (); - } -} - -G_GNUC_UNUSED static inline void -gsk_gl_command_queue_print_batch (GskGLCommandQueue *self, - const GskGLCommandBatch *batch) -{ - static const char *command_kinds[] = { "Clear", "Draw", }; - guint framebuffer_id; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (batch != NULL); - - if (batch->any.kind == GSK_GL_COMMAND_KIND_CLEAR) - framebuffer_id = batch->clear.framebuffer; - else if (batch->any.kind == GSK_GL_COMMAND_KIND_DRAW) - framebuffer_id = batch->draw.framebuffer; - else - return; - - g_printerr ("Batch {\n"); - g_printerr (" Kind: %s\n", command_kinds[batch->any.kind]); - g_printerr (" Viewport: %dx%d\n", batch->any.viewport.width, batch->any.viewport.height); - g_printerr (" Framebuffer: %d\n", framebuffer_id); - - if (batch->any.kind == GSK_GL_COMMAND_KIND_DRAW) - { - g_printerr (" Program: %d\n", batch->any.program); - g_printerr (" Vertices: %d\n", batch->draw.vbo_count); - - for (guint i = 0; i < batch->draw.bind_count; i++) - { - const GskGLCommandBind *bind = &self->batch_binds.items[batch->draw.bind_offset + i]; - g_printerr (" Bind[%d]: %u\n", bind->texture, bind->id); - } - - for (guint i = 0; i < batch->draw.uniform_count; i++) - { - const GskGLCommandUniform *uniform = &self->batch_uniforms.items[batch->draw.uniform_offset + i]; - g_printerr (" Uniform[%02d]: ", uniform->location); - print_uniform (uniform->info.format, - uniform->info.array_count, - gsk_gl_uniform_state_get_uniform_data (self->uniforms, uniform->info.offset)); - g_printerr ("\n"); - } - } - else if (batch->any.kind == GSK_GL_COMMAND_KIND_CLEAR) - { - g_printerr (" Bits: 0x%x\n", batch->clear.bits); - } - - g_printerr ("}\n"); -} - -G_GNUC_UNUSED static inline void -gsk_gl_command_queue_capture_png (GskGLCommandQueue *self, - const char *filename, - guint width, - guint height, - gboolean flip_y) -{ - guint stride; - guint8 *data; - GBytes *bytes; - GdkTexture *texture; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (filename != NULL); - - stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width); - data = g_malloc_n (height, stride); - - glReadPixels (0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, data); - - if (flip_y) - { - guint8 *flipped = g_malloc_n (height, stride); - - for (guint i = 0; i < height; i++) - memcpy (flipped + (height * stride) - ((i + 1) * stride), - data + (stride * i), - stride); - - g_free (data); - data = flipped; - } - - bytes = g_bytes_new_take (data, height * stride); - texture = gdk_memory_texture_new (width, height, GDK_MEMORY_DEFAULT, bytes, stride); - g_bytes_unref (bytes); - - gdk_texture_save_to_png (texture, filename); - g_object_unref (texture); -} -#endif - -static inline gboolean -will_ignore_batch (GskGLCommandQueue *self) -{ - if G_LIKELY (self->batches.len < G_MAXINT16) - return FALSE; - - if (!self->have_truncated) - { - self->have_truncated = TRUE; - g_critical ("GL command queue too large, truncating further batches."); - } - - return TRUE; -} - -static inline GLint -min_filter_from_index (guint index) -{ - GLint filters[3] = { GL_LINEAR, GL_NEAREST, GL_LINEAR_MIPMAP_LINEAR }; - - g_assert (index < GSK_GL_N_FILTERS); - - return filters[index]; -} - -static inline GLint -mag_filter_from_index (guint index) -{ - GLint filters[3] = { GL_LINEAR, GL_NEAREST, GL_LINEAR }; - - g_assert (index < GSK_GL_N_FILTERS); - - return filters[index]; -} - -static inline guint -snapshot_attachments (const GskGLAttachmentState *state, - GskGLCommandBinds *array) -{ - GskGLCommandBind *bind = gsk_gl_command_binds_append_n (array, G_N_ELEMENTS (state->textures)); - guint count = 0; - - for (guint i = 0; i < G_N_ELEMENTS (state->textures); i++) - { - if (state->textures[i].id) - { - bind[count].id = state->textures[i].id; - bind[count].texture = state->textures[i].texture; - if (state->textures[i].target == GL_TEXTURE_EXTERNAL_OES) - bind[count].sampler = SAMPLER_EXTERNAL; - else - bind[count].sampler = state->textures[i].sampler; - count++; - } - } - - if (count != G_N_ELEMENTS (state->textures)) - array->len -= G_N_ELEMENTS (state->textures) - count; - - return count; -} - -static inline guint -snapshot_uniforms (GskGLUniformState *state, - GskGLUniformProgram *program, - GskGLCommandUniforms *array) -{ - GskGLCommandUniform *uniform = gsk_gl_command_uniforms_append_n (array, program->n_mappings); - guint count = 0; - - for (guint i = 0; i < program->n_mappings; i++) - { - const GskGLUniformMapping *mapping = &program->mappings[i]; - - if (!mapping->info.initial && mapping->info.format && mapping->location > -1) - { - uniform[count].location = mapping->location; - uniform[count].info = mapping->info; - count++; - } - } - - if (count != program->n_mappings) - array->len -= program->n_mappings - count; - - return count; -} - -static inline gboolean -snapshots_equal (GskGLCommandQueue *self, - GskGLCommandBatch *first, - GskGLCommandBatch *second) -{ - if (first->draw.bind_count != second->draw.bind_count || - first->draw.uniform_count != second->draw.uniform_count) - return FALSE; - - for (guint i = 0; i < first->draw.bind_count; i++) - { - const GskGLCommandBind *fb = &self->batch_binds.items[first->draw.bind_offset+i]; - const GskGLCommandBind *sb = &self->batch_binds.items[second->draw.bind_offset+i]; - - if (fb->id != sb->id || fb->texture != sb->texture) - return FALSE; - } - - for (guint i = 0; i < first->draw.uniform_count; i++) - { - const GskGLCommandUniform *fu = &self->batch_uniforms.items[first->draw.uniform_offset+i]; - const GskGLCommandUniform *su = &self->batch_uniforms.items[second->draw.uniform_offset+i]; - gconstpointer fdata; - gconstpointer sdata; - gsize len; - - /* Short circuit if we'd end up with the same memory */ - if (fu->info.offset == su->info.offset) - continue; - - if (fu->info.format != su->info.format || - fu->info.array_count != su->info.array_count) - return FALSE; - - fdata = gsk_gl_uniform_state_get_uniform_data (self->uniforms, fu->info.offset); - sdata = gsk_gl_uniform_state_get_uniform_data (self->uniforms, su->info.offset); - - switch (fu->info.format) - { - case GSK_GL_UNIFORM_FORMAT_1F: - case GSK_GL_UNIFORM_FORMAT_1FV: - case GSK_GL_UNIFORM_FORMAT_1I: - case GSK_GL_UNIFORM_FORMAT_TEXTURE: - case GSK_GL_UNIFORM_FORMAT_1UI: - len = 4; - break; - - case GSK_GL_UNIFORM_FORMAT_2F: - case GSK_GL_UNIFORM_FORMAT_2FV: - case GSK_GL_UNIFORM_FORMAT_2I: - len = 8; - break; - - case GSK_GL_UNIFORM_FORMAT_3F: - case GSK_GL_UNIFORM_FORMAT_3FV: - case GSK_GL_UNIFORM_FORMAT_3I: - len = 12; - break; - - case GSK_GL_UNIFORM_FORMAT_4F: - case GSK_GL_UNIFORM_FORMAT_4FV: - case GSK_GL_UNIFORM_FORMAT_4I: - len = 16; - break; - - - case GSK_GL_UNIFORM_FORMAT_MATRIX: - len = sizeof (float) * 16; - break; - - case GSK_GL_UNIFORM_FORMAT_ROUNDED_RECT: - len = sizeof (float) * 12; - break; - - case GSK_GL_UNIFORM_FORMAT_COLOR: - len = sizeof (float) * 4; - break; - - default: - g_assert_not_reached (); - } - - len *= fu->info.array_count; - - if (memcmp (fdata, sdata, len) != 0) - return FALSE; - } - - return TRUE; -} - -static void -gsk_gl_command_queue_dispose (GObject *object) -{ - GskGLCommandQueue *self = (GskGLCommandQueue *)object; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - - g_clear_object (&self->profiler); - g_clear_object (&self->gl_profiler); - g_clear_object (&self->context); - g_clear_pointer (&self->attachments, gsk_gl_attachment_state_unref); - g_clear_pointer (&self->uniforms, gsk_gl_uniform_state_unref); - - if (self->has_samplers) - glDeleteSamplers (G_N_ELEMENTS (self->samplers), self->samplers); - - gsk_gl_command_batches_clear (&self->batches); - gsk_gl_command_binds_clear (&self->batch_binds); - gsk_gl_command_uniforms_clear (&self->batch_uniforms); - gsk_gl_syncs_clear (&self->syncs); - - gsk_gl_buffer_destroy (&self->vertices); - - G_OBJECT_CLASS (gsk_gl_command_queue_parent_class)->dispose (object); -} - -static void -gsk_gl_command_queue_class_init (GskGLCommandQueueClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = gsk_gl_command_queue_dispose; -} - -static void -gsk_gl_command_queue_init (GskGLCommandQueue *self) -{ - self->max_texture_size = -1; - - gsk_gl_command_batches_init (&self->batches, 128); - gsk_gl_command_binds_init (&self->batch_binds, 1024); - gsk_gl_command_uniforms_init (&self->batch_uniforms, 2048); - gsk_gl_syncs_init (&self->syncs, 10); - - gsk_gl_buffer_init (&self->vertices, GL_ARRAY_BUFFER, sizeof (GskGLDrawVertex)); -} - -GskGLCommandQueue * -gsk_gl_command_queue_new (GdkGLContext *context, - GskGLUniformState *uniforms) -{ - GskGLCommandQueue *self; - guint i; - - g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL); - - self = g_object_new (GSK_TYPE_GL_COMMAND_QUEUE, NULL); - self->context = g_object_ref (context); - self->attachments = gsk_gl_attachment_state_new (); - - /* Use shared uniform state if we're provided one */ - if (uniforms != NULL) - self->uniforms = gsk_gl_uniform_state_ref (uniforms); - else - self->uniforms = gsk_gl_uniform_state_new (); - - /* Determine max texture size immediately and restore context */ - gdk_gl_context_make_current (context); - - glGetIntegerv (GL_MAX_TEXTURE_SIZE, &self->max_texture_size); - if (g_getenv ("GSK_MAX_TEXTURE_SIZE")) - { - int max_texture_size = atoi (g_getenv ("GSK_MAX_TEXTURE_SIZE")); - if (max_texture_size == 0) - { - g_warning ("Failed to parse %s", "GSK_MAX_TEXTURE_SIZE"); - } - else - { - max_texture_size = MAX (max_texture_size, 512); - GSK_DEBUG (RENDERER, - "Limiting texture size in the GL renderer to %d (via %s)", - max_texture_size, "GSK_MAX_TEXTURE_SIZE"); - self->max_texture_size = MIN (self->max_texture_size, max_texture_size); - } - } - - self->has_samplers = gdk_gl_context_check_version (context, "3.3", "3.0"); - self->can_swizzle = gdk_gl_context_check_version (context, "3.0", "3.0"); - - /* create the samplers */ - if (self->has_samplers) - { - glGenSamplers (G_N_ELEMENTS (self->samplers), self->samplers); - for (i = 0; i < G_N_ELEMENTS (self->samplers); i++) - { - glSamplerParameteri (self->samplers[i], GL_TEXTURE_MIN_FILTER, min_filter_from_index (i / GSK_GL_N_FILTERS)); - glSamplerParameteri (self->samplers[i], GL_TEXTURE_MAG_FILTER, mag_filter_from_index (i % GSK_GL_N_FILTERS)); - glSamplerParameteri (self->samplers[i], GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri (self->samplers[i], GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - } - - return g_steal_pointer (&self); -} - -static inline GskGLCommandBatch * -begin_next_batch (GskGLCommandQueue *self) -{ - GskGLCommandBatch *batch; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - - /* GskGLCommandBatch contains an embedded linked list using integers into the - * self->batches array. We can't use pointer because the batches could be - * realloc()'d at runtime. - * - * Before we execute the command queue, we sort the batches by framebuffer but - * leave the batches in place as we can just tweak the links via prev/next. - * - * Generally we only traverse forwards, so we could ignore the previous field. - * But to optimize the reordering of batches by framebuffer we walk backwards - * so we sort by most-recently-seen framebuffer to ensure draws happen in the - * proper order. - */ - - batch = gsk_gl_command_batches_append (&self->batches); - batch->any.next_batch_index = -1; - batch->any.prev_batch_index = self->tail_batch_index; - - return batch; -} - -static void -enqueue_batch (GskGLCommandQueue *self) -{ - guint index; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (self->batches.len > 0); - - /* Batches are linked lists but using indexes into the batches array instead - * of pointers. This is for two main reasons. First, 16-bit indexes allow us - * to store the information in 4 bytes, where as two pointers would take 16 - * bytes. Furthermore, we have an array here so pointers would get - * invalidated if we realloc()'d (and that can happen from time to time). - */ - - index = self->batches.len - 1; - - if (self->head_batch_index == -1) - self->head_batch_index = index; - - if (self->tail_batch_index != -1) - { - GskGLCommandBatch *prev = &self->batches.items[self->tail_batch_index]; - - prev->any.next_batch_index = index; - } - - self->tail_batch_index = index; -} - -static void -discard_batch (GskGLCommandQueue *self) -{ - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (self->batches.len > 0); - - self->batches.len--; -} - -gboolean -gsk_gl_command_queue_begin_draw (GskGLCommandQueue *self, - GskGLUniformProgram *program, - guint width, - guint height) -{ - GskGLCommandBatch *batch; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (self->in_draw == FALSE); - g_assert (width <= G_MAXUINT16); - g_assert (height <= G_MAXUINT16); - - /* Our internal links use 16-bits, so that is our max number - * of batches we can have in one frame. - */ - if (will_ignore_batch (self)) - return FALSE; - - self->program_info = program; - - batch = begin_next_batch (self); - batch->any.kind = GSK_GL_COMMAND_KIND_DRAW; - batch->any.program = program->program_id; - batch->any.next_batch_index = -1; - batch->any.viewport.width = width; - batch->any.viewport.height = height; - batch->draw.blend = 1; - batch->draw.framebuffer = 0; - batch->draw.uniform_count = 0; - batch->draw.uniform_offset = self->batch_uniforms.len; - batch->draw.bind_count = 0; - batch->draw.bind_offset = self->batch_binds.len; - batch->draw.vbo_count = 0; - batch->draw.vbo_offset = gsk_gl_buffer_get_offset (&self->vertices); - - self->fbo_max = MAX (self->fbo_max, batch->draw.framebuffer); - - self->in_draw = TRUE; - - return TRUE; -} - -void -gsk_gl_command_queue_end_draw (GskGLCommandQueue *self) -{ - GskGLCommandBatch *last_batch; - GskGLCommandBatch *batch; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (self->batches.len > 0); - - if (will_ignore_batch (self)) - { - self->in_draw = FALSE; - return; - } - - batch = gsk_gl_command_batches_tail (&self->batches); - - g_assert (self->in_draw == TRUE); - g_assert (batch->any.kind == GSK_GL_COMMAND_KIND_DRAW); - - if G_UNLIKELY (batch->draw.vbo_count == 0) - { - discard_batch (self); - self->in_draw = FALSE; - return; - } - - /* Track the destination framebuffer in case it changed */ - batch->draw.framebuffer = self->attachments->fbo.id; - self->attachments->fbo.changed = FALSE; - self->fbo_max = MAX (self->fbo_max, self->attachments->fbo.id); - - /* Save our full uniform state for this draw so we can possibly - * reorder the draw later. - */ - batch->draw.uniform_offset = self->batch_uniforms.len; - batch->draw.uniform_count = snapshot_uniforms (self->uniforms, self->program_info, &self->batch_uniforms); - - /* Track the bind attachments that changed */ - if (self->program_info->has_attachments) - { - batch->draw.bind_offset = self->batch_binds.len; - batch->draw.bind_count = snapshot_attachments (self->attachments, &self->batch_binds); - } - else - { - batch->draw.bind_offset = 0; - batch->draw.bind_count = 0; - } - - if (self->batches.len > 1) - last_batch = &self->batches.items[self->batches.len - 2]; - else - last_batch = NULL; - - /* Do simple chaining of draw to last batch. */ - if (last_batch != NULL && - last_batch->any.kind == GSK_GL_COMMAND_KIND_DRAW && - last_batch->any.program == batch->any.program && - last_batch->any.viewport.width == batch->any.viewport.width && - last_batch->any.viewport.height == batch->any.viewport.height && - last_batch->draw.blend == batch->draw.blend && - last_batch->draw.framebuffer == batch->draw.framebuffer && - last_batch->draw.vbo_offset + last_batch->draw.vbo_count == batch->draw.vbo_offset && - last_batch->draw.vbo_count + batch->draw.vbo_count <= 0xffff && - snapshots_equal (self, last_batch, batch)) - { - last_batch->draw.vbo_count += batch->draw.vbo_count; - discard_batch (self); - } - else - { - enqueue_batch (self); - } - - self->in_draw = FALSE; - self->program_info = NULL; -} - -/** - * gsk_gl_command_queue_split_draw: - * @self a `GskGLCommandQueue` - * - * This function is like calling gsk_gl_command_queue_end_draw() followed by - * a gsk_gl_command_queue_begin_draw() with the same parameters as a - * previous begin draw (if shared uniforms where not changed further). - * - * This is useful to avoid comparisons inside of loops where we know shared - * uniforms are not changing. - * - * This generally should just be called from gsk_gl_program_split_draw() - * as that is where the begin/end flow happens from the render job. - */ -void -gsk_gl_command_queue_split_draw (GskGLCommandQueue *self) -{ - GskGLCommandBatch *batch; - GskGLUniformProgram *program; - guint width; - guint height; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (self->batches.len > 0); - - if (will_ignore_batch (self)) - return; - - g_assert (self->in_draw == TRUE); - - program = self->program_info; - - batch = gsk_gl_command_batches_tail (&self->batches); - - g_assert (batch->any.kind == GSK_GL_COMMAND_KIND_DRAW); - - width = batch->any.viewport.width; - height = batch->any.viewport.height; - - gsk_gl_command_queue_end_draw (self); - gsk_gl_command_queue_begin_draw (self, program, width, height); -} - -void -gsk_gl_command_queue_clear (GskGLCommandQueue *self, - guint clear_bits, - const graphene_rect_t *viewport) -{ - GskGLCommandBatch *batch; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (self->in_draw == FALSE); - - if (will_ignore_batch (self)) - return; - - if (clear_bits == 0) - clear_bits = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; - - batch = begin_next_batch (self); - batch->any.kind = GSK_GL_COMMAND_KIND_CLEAR; - batch->any.viewport.width = viewport->size.width; - batch->any.viewport.height = viewport->size.height; - batch->clear.bits = clear_bits; - batch->clear.framebuffer = self->attachments->fbo.id; - batch->any.next_batch_index = -1; - batch->any.program = 0; - - self->fbo_max = MAX (self->fbo_max, batch->clear.framebuffer); - - enqueue_batch (self); - - self->attachments->fbo.changed = FALSE; -} - -GdkGLContext * -gsk_gl_command_queue_get_context (GskGLCommandQueue *self) -{ - g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self), NULL); - - return self->context; -} - -void -gsk_gl_command_queue_make_current (GskGLCommandQueue *self) -{ - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (GDK_IS_GL_CONTEXT (self->context)); - - gdk_gl_context_make_current (self->context); -} - -void -gsk_gl_command_queue_delete_program (GskGLCommandQueue *self, - guint program) -{ - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - - glDeleteProgram (program); -} - -static inline void -apply_viewport (guint *current_width, - guint *current_height, - guint width, - guint height) -{ - if G_UNLIKELY (*current_width != width || *current_height != height) - { - *current_width = width; - *current_height = height; - glViewport (0, 0, width, height); - } -} - -static inline void -apply_scissor (gboolean *state, - guint framebuffer, - const graphene_rect_t *scissor, - gboolean has_scissor, - guint default_framebuffer) -{ - g_assert (framebuffer != (guint)-1); - - if (framebuffer != default_framebuffer || !has_scissor) - { - if (*state != FALSE) - { - glDisable (GL_SCISSOR_TEST); - *state = FALSE; - } - } - else - { - if (*state != TRUE) - { - glEnable (GL_SCISSOR_TEST); - glScissor (scissor->origin.x, - scissor->origin.y, - scissor->size.width, - scissor->size.height); - *state = TRUE; - } - } -} - -static inline gboolean -apply_framebuffer (int *framebuffer, - guint new_framebuffer) -{ - if G_UNLIKELY (new_framebuffer != *framebuffer) - { - *framebuffer = new_framebuffer; - glBindFramebuffer (GL_FRAMEBUFFER, new_framebuffer); - return TRUE; - } - - return FALSE; -} - -static inline void -gsk_gl_command_queue_unlink (GskGLCommandQueue *self, - GskGLCommandBatch *batch) -{ - if (batch->any.prev_batch_index == -1) - self->head_batch_index = batch->any.next_batch_index; - else - self->batches.items[batch->any.prev_batch_index].any.next_batch_index = batch->any.next_batch_index; - - if (batch->any.next_batch_index == -1) - self->tail_batch_index = batch->any.prev_batch_index; - else - self->batches.items[batch->any.next_batch_index].any.prev_batch_index = batch->any.prev_batch_index; - - batch->any.prev_batch_index = -1; - batch->any.next_batch_index = -1; -} - -static inline void -gsk_gl_command_queue_insert_before (GskGLCommandQueue *self, - GskGLCommandBatch *batch, - GskGLCommandBatch *sibling) -{ - int sibling_index; - int index; - - g_assert (batch >= self->batches.items); - g_assert (batch < &self->batches.items[self->batches.len]); - g_assert (sibling >= self->batches.items); - g_assert (sibling < &self->batches.items[self->batches.len]); - - index = gsk_gl_command_batches_index_of (&self->batches, batch); - sibling_index = gsk_gl_command_batches_index_of (&self->batches, sibling); - - batch->any.next_batch_index = sibling_index; - batch->any.prev_batch_index = sibling->any.prev_batch_index; - - if (batch->any.prev_batch_index > -1) - self->batches.items[batch->any.prev_batch_index].any.next_batch_index = index; - - sibling->any.prev_batch_index = index; - - if (batch->any.prev_batch_index == -1) - self->head_batch_index = index; -} - -static void -gsk_gl_command_queue_sort_batches (GskGLCommandQueue *self) -{ - int *seen; - int *seen_free = NULL; - int index; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (self->tail_batch_index >= 0); - g_assert (self->fbo_max >= 0); - - /* Create our seen list with most recent index set to -1, - * meaning we haven't yet seen that framebuffer. - */ - if (self->fbo_max < 1024) - seen = g_alloca (sizeof (int) * (self->fbo_max + 1)); - else - seen = seen_free = g_new0 (int, (self->fbo_max + 1)); - for (int i = 0; i <= self->fbo_max; i++) - seen[i] = -1; - - /* Walk in reverse, and if we've seen that framebuffer before, we want to - * delay this operation until right before the last batch we saw for that - * framebuffer. - * - * We can do this because we don't use a framebuffer's texture until it has - * been completely drawn. - */ - index = self->tail_batch_index; - - while (index >= 0) - { - GskGLCommandBatch *batch = &self->batches.items[index]; - int cur_index = index; - int fbo = -1; - - g_assert (index > -1); - g_assert (index < self->batches.len); - - switch (batch->any.kind) - { - case GSK_GL_COMMAND_KIND_DRAW: - fbo = batch->draw.framebuffer; - break; - - case GSK_GL_COMMAND_KIND_CLEAR: - fbo = batch->clear.framebuffer; - break; - - default: - g_assert_not_reached (); - } - - index = batch->any.prev_batch_index; - - g_assert (index >= -1); - g_assert (index < (int)self->batches.len); - g_assert (fbo >= -1); - - if (fbo == -1) - continue; - - g_assert (fbo <= self->fbo_max); - g_assert (seen[fbo] >= -1); - g_assert (seen[fbo] < (int)self->batches.len); - - if (seen[fbo] != -1 && seen[fbo] != batch->any.next_batch_index) - { - int mru_index = seen[fbo]; - GskGLCommandBatch *mru = &self->batches.items[mru_index]; - - g_assert (mru_index > -1); - - gsk_gl_command_queue_unlink (self, batch); - - g_assert (batch->any.prev_batch_index == -1); - g_assert (batch->any.next_batch_index == -1); - - gsk_gl_command_queue_insert_before (self, batch, mru); - - g_assert (batch->any.prev_batch_index > -1 || - self->head_batch_index == cur_index); - g_assert (batch->any.next_batch_index == seen[fbo]); - } - - g_assert (cur_index > -1); - g_assert (seen[fbo] >= -1); - - seen[fbo] = cur_index; - } - - g_free (seen_free); -} - -/** - * gsk_gl_command_queue_execute: - * @self: a `GskGLCommandQueue` - * @surface_height: the height of the backing surface - * @scale: the scale of the backing surface - * @scissor: (nullable): the scissor clip if any - * @default_framebuffer: the default framebuffer id if not zero - * - * Executes all of the batches in the command queue. - * - * Typically, the scissor rect is only applied when rendering to the default - * framebuffer (zero in most cases). However, if @default_framebuffer is not - * zero, it will be checked to see if the rendering target matches so that - * the scissor rect is applied. This should be used in cases where rendering - * to the backbuffer for display is not the default GL framebuffer of zero. - * Currently, this happens when rendering on macOS using IOSurface. - */ -void -gsk_gl_command_queue_execute (GskGLCommandQueue *self, - guint surface_height, - float scale, - const cairo_region_t *scissor, - guint default_framebuffer) -{ - G_GNUC_UNUSED guint count = 0; - graphene_rect_t scissor_test = GRAPHENE_RECT_INIT (0, 0, 0, 0); - gboolean has_scissor = scissor != NULL; - gboolean scissor_state = -1; - guint program = 0; - guint width = 0; - guint height = 0; - G_GNUC_UNUSED unsigned int n_binds = 0; - G_GNUC_UNUSED unsigned int n_fbos = 0; - G_GNUC_UNUSED unsigned int n_uniforms = 0; - G_GNUC_UNUSED unsigned int n_programs = 0; - guint vao_id; - guint vbo_id; - int textures[GSK_GL_MAX_TEXTURES_PER_PROGRAM]; - int samplers[GSK_GL_MAX_TEXTURES_PER_PROGRAM]; - int framebuffer = -1; - int next_batch_index; - int active = -1; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (self->in_draw == FALSE); - - if (self->batches.len == 0) - return; - - for (guint i = 0; i < G_N_ELEMENTS (textures); i++) - textures[i] = -1; - for (guint i = 0; i < G_N_ELEMENTS (samplers); i++) - samplers[i] = -1; - - gsk_gl_command_queue_sort_batches (self); - - gsk_gl_command_queue_make_current (self); - - gsk_gl_profiler_begin_gpu_region (self->gl_profiler); - gsk_profiler_timer_begin (self->profiler, self->metrics.cpu_time); - - glDisable (GL_DEPTH_TEST); - - /* Pre-multiplied alpha */ - glEnable (GL_BLEND); - glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glBlendEquation (GL_FUNC_ADD); - - if (gdk_gl_context_has_vertex_arrays (self->context)) - { - glGenVertexArrays (1, &vao_id); - glBindVertexArray (vao_id); - } - - vbo_id = gsk_gl_buffer_submit (&self->vertices); - - /* 0 = position location */ - glEnableVertexAttribArray (0); - glVertexAttribPointer (0, 2, GL_FLOAT, GL_FALSE, - sizeof (GskGLDrawVertex), - GSIZE_TO_POINTER ((gsize) G_STRUCT_OFFSET (GskGLDrawVertex, position))); - - /* 1 = texture coord location */ - glEnableVertexAttribArray (1); - glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, - sizeof (GskGLDrawVertex), - GSIZE_TO_POINTER ((gsize) G_STRUCT_OFFSET (GskGLDrawVertex, uv))); - - /* 2 = color location */ - glEnableVertexAttribArray (2); - glVertexAttribPointer (2, 4, GL_HALF_FLOAT, GL_FALSE, - sizeof (GskGLDrawVertex), - GSIZE_TO_POINTER ((gsize) G_STRUCT_OFFSET (GskGLDrawVertex, color))); - - /* 3 = color2 location */ - glEnableVertexAttribArray (3); - glVertexAttribPointer (3, 4, GL_HALF_FLOAT, GL_FALSE, - sizeof (GskGLDrawVertex), - GSIZE_TO_POINTER ((gsize) G_STRUCT_OFFSET (GskGLDrawVertex, color2))); - - /* Setup initial scissor clip */ - if (scissor != NULL && cairo_region_num_rectangles (scissor) > 0) - { - cairo_rectangle_int_t r; - - g_assert (cairo_region_num_rectangles (scissor) == 1); - cairo_region_get_rectangle (scissor, 0, &r); - - scissor_test.origin.x = (int) floor (r.x * scale); - scissor_test.origin.y = (int) floor (surface_height - (r.height * scale) - (r.y * scale)); - scissor_test.size.width = (int) ceil (r.width * scale); - scissor_test.size.height = (int) ceil (r.height * scale); - } - - next_batch_index = self->head_batch_index; - - while (next_batch_index >= 0) - { - const GskGLCommandBatch *batch = &self->batches.items[next_batch_index]; - - g_assert (next_batch_index >= 0); - g_assert (next_batch_index < self->batches.len); - g_assert (batch->any.next_batch_index != next_batch_index); - - count++; - - switch (batch->any.kind) - { - case GSK_GL_COMMAND_KIND_CLEAR: - if (apply_framebuffer (&framebuffer, batch->clear.framebuffer)) - { - apply_scissor (&scissor_state, framebuffer, &scissor_test, has_scissor, default_framebuffer); - n_fbos++; - } - - apply_viewport (&width, - &height, - batch->any.viewport.width, - batch->any.viewport.height); - - glClearColor (0, 0, 0, 0); - glClear (batch->clear.bits); - break; - - case GSK_GL_COMMAND_KIND_DRAW: - if (batch->any.program != program) - { - program = batch->any.program; - glUseProgram (program); - - n_programs++; - } - - if (apply_framebuffer (&framebuffer, batch->draw.framebuffer)) - { - apply_scissor (&scissor_state, framebuffer, &scissor_test, has_scissor, default_framebuffer); - n_fbos++; - } - - apply_viewport (&width, - &height, - batch->any.viewport.width, - batch->any.viewport.height); - - if G_UNLIKELY (batch->draw.bind_count > 0) - { - const GskGLCommandBind *bind = &self->batch_binds.items[batch->draw.bind_offset]; - - g_assert (bind->texture < G_N_ELEMENTS (textures)); - for (guint i = 0; i < batch->draw.bind_count; i++) - { - if (textures[bind->texture] != bind->id) - { - GskGLSync *s; - - if (active != bind->texture) - { - active = bind->texture; - glActiveTexture (GL_TEXTURE0 + bind->texture); - } - - s = gsk_gl_syncs_get_sync (&self->syncs, bind->id); - if (s && s->sync) - { - glWaitSync ((GLsync) s->sync, 0, GL_TIMEOUT_IGNORED); - s->sync = NULL; - } - - if (bind->sampler == SAMPLER_EXTERNAL) - glBindTexture (GL_TEXTURE_EXTERNAL_OES, bind->id); - else - glBindTexture (GL_TEXTURE_2D, bind->id); - textures[bind->texture] = bind->id; - if (!self->has_samplers) - { - if (bind->sampler == SAMPLER_EXTERNAL) - { - glTexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - else - { - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_from_index (bind->sampler / GSK_GL_N_FILTERS)); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_from_index (bind->sampler % GSK_GL_N_FILTERS)); - } - } - } - - if (samplers[bind->texture] != bind->sampler) - { - if (self->has_samplers) - glBindSampler (bind->texture, self->samplers[bind->sampler]); - else - { - if (bind->sampler == SAMPLER_EXTERNAL) - { - glTexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri (GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - else - { - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter_from_index (bind->sampler / GSK_GL_N_FILTERS)); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter_from_index (bind->sampler % GSK_GL_N_FILTERS)); - } - } - samplers[bind->texture] = bind->sampler; - } - - bind++; - } - - n_binds += batch->draw.bind_count; - } - - if (batch->draw.uniform_count > 0) - { - const GskGLCommandUniform *u = &self->batch_uniforms.items[batch->draw.uniform_offset]; - - for (guint i = 0; i < batch->draw.uniform_count; i++, u++) - gsk_gl_uniform_state_apply (self->uniforms, program, u->location, u->info); - - n_uniforms += batch->draw.uniform_count; - } - - if (batch->draw.blend == 0) - glDisable (GL_BLEND); - - glDrawArrays (GL_TRIANGLES, batch->draw.vbo_offset, batch->draw.vbo_count); - - if (batch->draw.blend == 0) - glEnable (GL_BLEND); - break; - - default: - g_assert_not_reached (); - } - -#if 0 - if (batch->any.kind == GSK_GL_COMMAND_KIND_DRAW || - batch->any.kind == GSK_GL_COMMAND_KIND_CLEAR) - { - char filename[128]; - g_snprintf (filename, sizeof filename, - "capture%03u_batch%03d_kind%u_program%u_u%u_b%u_fb%u_ctx%p.png", - count, next_batch_index, - batch->any.kind, batch->any.program, - batch->any.kind == GSK_GL_COMMAND_KIND_DRAW ? batch->draw.uniform_count : 0, - batch->any.kind == GSK_GL_COMMAND_KIND_DRAW ? batch->draw.bind_count : 0, - framebuffer, - gdk_gl_context_get_current ()); - gsk_gl_command_queue_capture_png (self, filename, width, height, TRUE); - gsk_gl_command_queue_print_batch (self, batch); - } -#endif - - next_batch_index = batch->any.next_batch_index; - } - - glDeleteBuffers (1, &vbo_id); - if (gdk_gl_context_has_vertex_arrays (self->context)) - glDeleteVertexArrays (1, &vao_id); - - gdk_profiler_set_int_counter (self->metrics.n_binds, n_binds); - gdk_profiler_set_int_counter (self->metrics.n_uniforms, n_uniforms); - gdk_profiler_set_int_counter (self->metrics.n_fbos, n_fbos); - gdk_profiler_set_int_counter (self->metrics.n_programs, n_programs); - gdk_profiler_set_int_counter (self->metrics.n_uploads, self->n_uploads); - gdk_profiler_set_int_counter (self->metrics.queue_depth, self->batches.len); - - { - gint64 start_time G_GNUC_UNUSED = gsk_profiler_timer_get_start (self->profiler, self->metrics.cpu_time); - gint64 cpu_time = gsk_profiler_timer_end (self->profiler, self->metrics.cpu_time); - gint64 gpu_time = gsk_gl_profiler_end_gpu_region (self->gl_profiler); - - gsk_profiler_timer_set (self->profiler, self->metrics.gpu_time, gpu_time); - gsk_profiler_timer_set (self->profiler, self->metrics.cpu_time, cpu_time); - gsk_profiler_counter_inc (self->profiler, self->metrics.n_frames); - - gsk_profiler_push_samples (self->profiler); - } -} - -void -gsk_gl_command_queue_begin_frame (GskGLCommandQueue *self) -{ - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (self->batches.len == 0); - - gsk_gl_command_queue_make_current (self); - - self->fbo_max = 0; - self->tail_batch_index = -1; - self->head_batch_index = -1; - self->in_frame = TRUE; -} - -/** - * gsk_gl_command_queue_end_frame: - * @self: a `GskGLCommandQueue` - * - * This function performs cleanup steps that need to be done after - * a frame has finished. This is not performed as part of the command - * queue execution to allow for the frame to be submitted as soon - * as possible. - * - * However, it should be executed after the draw contexts end_frame - * has been called to swap the OpenGL framebuffers. - */ -void -gsk_gl_command_queue_end_frame (GskGLCommandQueue *self) -{ - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - - gsk_gl_command_queue_make_current (self); - gsk_gl_uniform_state_end_frame (self->uniforms); - - /* Reset attachments so we don't hold on to any textures - * that might be released after the frame. - */ - for (guint i = 0; i < G_N_ELEMENTS (self->attachments->textures); i++) - { - if (self->attachments->textures[i].id != 0) - { - glActiveTexture (GL_TEXTURE0 + i); - glBindTexture (self->attachments->textures[i].target, 0); - - self->attachments->textures[i].id = 0; - self->attachments->textures[i].changed = FALSE; - self->attachments->textures[i].initial = TRUE; - } - } - - self->batches.len = 0; - self->batch_binds.len = 0; - self->batch_uniforms.len = 0; - self->syncs.len = 0; - self->n_uploads = 0; - self->tail_batch_index = -1; - self->in_frame = FALSE; -} - -gboolean -gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self, - int width, - int height, - int format, - guint *out_fbo_id, - guint *out_texture_id) -{ - GLuint fbo_id = 0; - GLint texture_id; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (width > 0); - g_assert (height > 0); - g_assert (out_fbo_id != NULL); - g_assert (out_texture_id != NULL); - - texture_id = gsk_gl_command_queue_create_texture (self, - width, height, - format); - - if (texture_id == -1) - { - *out_fbo_id = 0; - *out_texture_id = 0; - return FALSE; - } - - fbo_id = gsk_gl_command_queue_create_framebuffer (self); - - glBindFramebuffer (GL_FRAMEBUFFER, fbo_id); - glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0); - g_assert_cmphex (glCheckFramebufferStatus (GL_FRAMEBUFFER), ==, GL_FRAMEBUFFER_COMPLETE); - - *out_fbo_id = fbo_id; - *out_texture_id = texture_id; - - return TRUE; -} - -int -gsk_gl_command_queue_create_texture (GskGLCommandQueue *self, - int width, - int height, - int format) -{ - GLuint texture_id = 0; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - - if G_UNLIKELY (self->max_texture_size == -1) - glGetIntegerv (GL_MAX_TEXTURE_SIZE, &self->max_texture_size); - - if (width > self->max_texture_size || height > self->max_texture_size) - return -1; - - glGenTextures (1, &texture_id); - - glActiveTexture (GL_TEXTURE0); - glBindTexture (GL_TEXTURE_2D, texture_id); - - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - switch (format) - { - case 0: - break; - case GL_RGBA8: - glTexImage2D (GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - break; - case GL_RGBA16F: - glTexImage2D (GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, GL_HALF_FLOAT, NULL); - break; - case GL_RGBA32F: - glTexImage2D (GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, GL_FLOAT, NULL); - break; - default: - /* If you add new formats, make sure to set the correct format and type here - * so that GLES doesn't barf invalid operations at you. - * Because it is very important that these 3 values match when data is set to - * NULL, do you hear me? - */ - g_assert_not_reached (); - break; - } - - /* Restore the previous texture if it was set */ - if (self->attachments->textures[0].id != 0 && - self->attachments->textures[0].target == GL_TEXTURE_2D) - glBindTexture (self->attachments->textures[0].target, self->attachments->textures[0].id); - - return (int)texture_id; -} - -guint -gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self) -{ - GLuint fbo_id; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - - glGenFramebuffers (1, &fbo_id); - - return fbo_id; -} - -static GdkMemoryFormat -memory_format_gl_format (GskGLCommandQueue *self, - GdkMemoryFormat data_format, - gboolean ensure_mipmap, - gboolean *out_can_mipmap, - GLint *gl_internalformat, - GLint *gl_internalsrgbformat, - GLenum *gl_format, - GLenum *gl_type, - GLint gl_swizzle[4]) -{ - GdkGLMemoryFlags flags, required_flags; - GdkMemoryFormat alt_format; - const GdkMemoryFormat *fallbacks; - gsize i; - - /* No support for straight formats yet */ - if (gdk_memory_format_alpha (data_format) == GDK_MEMORY_ALPHA_STRAIGHT) - data_format = gdk_memory_format_get_premultiplied (data_format); - - required_flags = GDK_GL_FORMAT_USABLE | GDK_GL_FORMAT_FILTERABLE; - if (ensure_mipmap) - required_flags |= GDK_GL_FORMAT_RENDERABLE; - - /* First, try the format itself */ - flags = gdk_gl_context_get_format_flags (self->context, data_format); - if ((flags & required_flags) == required_flags) - { - gdk_memory_format_gl_format (data_format, - gdk_gl_context_get_use_es (self->context), - gl_internalformat, - gl_internalsrgbformat, - gl_format, - gl_type, - gl_swizzle); - *out_can_mipmap = (flags & GDK_GL_FORMAT_RENDERABLE) ? TRUE : FALSE; - return data_format; - } - - /* Second, try the potential RGBA format */ - if (gdk_memory_format_gl_rgba_format (data_format, - gdk_gl_context_get_use_es (self->context), - &alt_format, - gl_internalformat, - gl_internalsrgbformat, - gl_format, - gl_type, - gl_swizzle)) - { - flags = gdk_gl_context_get_format_flags (self->context, alt_format); - if ((flags & required_flags) == required_flags) - { - *out_can_mipmap = (flags & GDK_GL_FORMAT_RENDERABLE) ? TRUE : FALSE; - - if (self->can_swizzle) - return data_format; - - gdk_memory_format_gl_format (alt_format, - gdk_gl_context_get_use_es (self->context), - gl_internalformat, - gl_internalsrgbformat, - gl_format, - gl_type, - gl_swizzle); - - return alt_format; - } - } - - /* Next, try the fallbacks */ - fallbacks = gdk_memory_format_get_fallbacks (data_format); - for (i = 0; fallbacks[i] != -1; i++) - { - flags = gdk_gl_context_get_format_flags (self->context, fallbacks[i]); - if (((flags & required_flags) == required_flags)) - { - gdk_memory_format_gl_format (fallbacks[i], - gdk_gl_context_get_use_es (self->context), - gl_internalformat, - gl_internalsrgbformat, - gl_format, - gl_type, - gl_swizzle); - - *out_can_mipmap = (flags & GDK_GL_FORMAT_RENDERABLE) ? TRUE : FALSE; - return fallbacks[i]; - } - } - - g_assert_not_reached (); - - return GDK_MEMORY_R8G8B8A8_PREMULTIPLIED; -} - -static void -gsk_gl_command_queue_do_upload_texture_chunk (GskGLCommandQueue *self, - GdkTexture *texture, - int x, - int y, - GdkMemoryFormat data_format, - GLenum gl_format, - GLenum gl_type, - GLint gl_swizzle[4]) - -{ - G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME; - const guchar *data; - gsize stride; - GBytes *bytes; - GdkTextureDownloader downloader; - int width, height; - gsize bpp; - - width = gdk_texture_get_width (texture); - height = gdk_texture_get_height (texture); - - if (GSK_DEBUG_CHECK (FALLBACK) && - data_format != gdk_texture_get_format (texture)) - { - GEnumClass *enum_class = g_type_class_ref (GDK_TYPE_MEMORY_FORMAT); - - gdk_debug_message ("Unsupported format %s, converting on CPU to %s", - g_enum_get_value (enum_class, gdk_texture_get_format (texture))->value_nick, - g_enum_get_value (enum_class, data_format)->value_nick); - - g_type_class_unref (enum_class); - } - - gdk_texture_downloader_init (&downloader, texture); - gdk_texture_downloader_set_format (&downloader, data_format); - bytes = gdk_texture_downloader_download_bytes (&downloader, &stride); - gdk_texture_downloader_finish (&downloader); - data = g_bytes_get_data (bytes, NULL); - bpp = gdk_memory_format_bytes_per_pixel (data_format); - - if (gdk_profiler_is_running ()) - { - gdk_profiler_end_markf (start_time, - "Download texture chunk", - "Tile %dx%d Size %dx%d", x, y, width, height); - start_time = GDK_PROFILER_CURRENT_TIME; - } - - glPixelStorei (GL_UNPACK_ALIGNMENT, gdk_memory_format_alignment (data_format)); - - /* GL_UNPACK_ROW_LENGTH is available on desktop GL, OpenGL ES >= 3.0, or if - * the GL_EXT_unpack_subimage extension for OpenGL ES 2.0 is available - */ - if (stride == width * bpp) - { - glTexSubImage2D (GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, data); - } - else if (stride % bpp == 0) - { - glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp); - - glTexSubImage2D (GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, data); - - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); - } - else - { - for (int i = 0; i < height; i++) - glTexSubImage2D (GL_TEXTURE_2D, 0, x, y + i, width, 1, gl_format, gl_type, data + (i * stride)); - } - - glPixelStorei (GL_UNPACK_ALIGNMENT, 4); - - /* Only apply swizzle if really needed, might not even be - * supported if default values are set - */ - if (gl_swizzle[0] != GL_RED || gl_swizzle[1] != GL_GREEN || gl_swizzle[2] != GL_BLUE || gl_swizzle[3] != GL_ALPHA) - { - /* Set each channel independently since GLES 3.0 doesn't support the iv method */ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, gl_swizzle[0]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, gl_swizzle[1]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, gl_swizzle[2]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, gl_swizzle[3]); - } - - g_bytes_unref (bytes); - - if (gdk_profiler_is_running ()) - gdk_profiler_end_markf (start_time, - "Upload texture chunk", - "Tile %dx%d Size %dx%d", x, y, width, height); -} - -int -gsk_gl_command_queue_upload_texture_chunks (GskGLCommandQueue *self, - gboolean ensure_mipmap, - unsigned int n_chunks, - GskGLTextureChunk *chunks, - gboolean *out_can_mipmap) -{ - G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME; - int width, height; - GdkMemoryFormat data_format; - GLint gl_internalformat, gl_internalsrgbformat; - GLenum gl_format; - GLenum gl_type; - GLint gl_swizzle[4]; - int texture_id; - - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - - width = height = 0; - for (unsigned int i = 0; i < n_chunks; i++) - { - GskGLTextureChunk *c = &chunks[i]; - - width = MAX (width, c->x + gdk_texture_get_width (c->texture)); - height = MAX (height, c->y + gdk_texture_get_height (c->texture)); - } - - if (width > self->max_texture_size || height > self->max_texture_size) - { - g_warning ("Attempt to create texture of size %ux%u but max size is %d. " - "Clipping will occur.", - width, height, self->max_texture_size); - width = MIN (width, self->max_texture_size); - height = MIN (height, self->max_texture_size); - } - - texture_id = gsk_gl_command_queue_create_texture (self, width, height, 0); - if (texture_id == -1) - return texture_id; - - self->n_uploads++; - - /* Switch to texture0 as 2D. We'll restore it later. */ - glActiveTexture (GL_TEXTURE0); - glBindTexture (GL_TEXTURE_2D, texture_id); - - /* Initialize the texture */ - data_format = gdk_texture_get_format (chunks[0].texture); - data_format = memory_format_gl_format (self, - data_format, - ensure_mipmap, - out_can_mipmap, - &gl_internalformat, - &gl_internalsrgbformat, - &gl_format, - &gl_type, - gl_swizzle); - - glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, NULL); - - for (unsigned int i = 0; i < n_chunks; i++) - { - GskGLTextureChunk *c = &chunks[i]; - gsk_gl_command_queue_do_upload_texture_chunk (self, c->texture, c->x, c->y, data_format, gl_format, gl_type, gl_swizzle); - } - - /* Restore previous texture state if any */ - if (self->attachments->textures[0].id > 0) - glBindTexture (self->attachments->textures[0].target, - self->attachments->textures[0].id); - - if (gdk_profiler_is_running ()) - gdk_profiler_add_markf (start_time, GDK_PROFILER_CURRENT_TIME-start_time, - "Upload Texture", - "Size %dx%d", width, height); - - return texture_id; -} - -int -gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self, - GdkTexture *texture, - gboolean ensure_mipmap, - gboolean *out_can_mipmap) -{ - return gsk_gl_command_queue_upload_texture_chunks (self, - ensure_mipmap, - 1, - &(GskGLTextureChunk){ texture, 0, 0}, - out_can_mipmap); -} - -void -gsk_gl_command_queue_set_profiler (GskGLCommandQueue *self, - GskProfiler *profiler) -{ - g_assert (GSK_IS_GL_COMMAND_QUEUE (self)); - g_assert (GSK_IS_PROFILER (profiler)); - - if (g_set_object (&self->profiler, profiler)) - { - self->gl_profiler = gsk_gl_profiler_new (self->context); - - self->metrics.n_frames = gsk_profiler_add_counter (profiler, "frames", "Frames", FALSE); - self->metrics.cpu_time = gsk_profiler_add_timer (profiler, "cpu-time", "CPU Time", FALSE, TRUE); - self->metrics.gpu_time = gsk_profiler_add_timer (profiler, "gpu-time", "GPU Time", FALSE, TRUE); - - self->metrics.n_binds = gdk_profiler_define_int_counter ("attachments", "Number of texture attachments"); - self->metrics.n_fbos = gdk_profiler_define_int_counter ("fbos", "Number of framebuffers attached"); - self->metrics.n_uniforms = gdk_profiler_define_int_counter ("uniforms", "Number of uniforms changed"); - self->metrics.n_uploads = gdk_profiler_define_int_counter ("uploads", "Number of texture uploads"); - self->metrics.n_programs = gdk_profiler_define_int_counter ("programs", "Number of program changes"); - self->metrics.queue_depth = gdk_profiler_define_int_counter ("gl-queue-depth", "Depth of GL command batches"); - } -} diff --git a/gsk/gl/gskglcommandqueueprivate.h b/gsk/gl/gskglcommandqueueprivate.h deleted file mode 100644 index f9100a69b56..00000000000 --- a/gsk/gl/gskglcommandqueueprivate.h +++ /dev/null @@ -1,431 +0,0 @@ -/* gskglcommandqueueprivate.h - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#include "gskgltypesprivate.h" -#include "gskglbufferprivate.h" -#include "gskglattachmentstateprivate.h" -#include "gskgluniformstateprivate.h" - -#include "inlinearray.h" - -#include "gskglprofilerprivate.h" - -G_BEGIN_DECLS - -#define GSK_TYPE_GL_COMMAND_QUEUE (gsk_gl_command_queue_get_type()) - -G_DECLARE_FINAL_TYPE (GskGLCommandQueue, gsk_gl_command_queue, GSK, GL_COMMAND_QUEUE, GObject) - -typedef enum _GskGLCommandKind -{ - /* The batch will perform a glClear() */ - GSK_GL_COMMAND_KIND_CLEAR, - - /* The batch will perform a glDrawArrays() */ - GSK_GL_COMMAND_KIND_DRAW, -} GskGLCommandKind; - -typedef struct _GskGLCommandBind -{ - /* @texture is the value passed to glActiveTexture(), the "slot" the - * texture will be placed into. We always use GL_TEXTURE_2D so we don't - * waste any bits here to indicate that. - */ - guint texture : 4; - - /* the sampler to use. We set sampler to 15 to indicate external textures */ - guint sampler : 4; - - /* The identifier for the texture created with glGenTextures(). */ - guint id: 24; -} GskGLCommandBind; - -G_STATIC_ASSERT (sizeof (GskGLCommandBind) == 4); - -typedef struct _GskGLCommandBatchAny -{ - /* A GskGLCommandKind indicating what the batch will do */ - guint kind : 8; - - /* The program's identifier to use for determining if we can merge two - * batches together into a single set of draw operations. We put this - * here instead of the GskGLCommandDraw so that we can use the extra - * bits here without making the structure larger. - */ - guint program : 24; - - /* The index of the next batch following this one. This is used - * as a sort of integer-based linked list to simplify out-of-order - * batching without moving memory around. -1 indicates last batch. - */ - gint16 next_batch_index; - - /* Same but for reverse direction as we sort in reverse to get the - * batches ordered by framebuffer. - */ - gint16 prev_batch_index; - - /* The viewport size of the batch. We check this as we process - * batches to determine if we need to resize the viewport. - */ - struct { - guint16 width; - guint16 height; - } viewport; -} GskGLCommandBatchAny; - -G_STATIC_ASSERT (sizeof (GskGLCommandBatchAny) == 12); - -typedef struct _GskGLCommandDraw -{ - GskGLCommandBatchAny head; - - guint blend : 1; - - /* There doesn't seem to be a limit on the framebuffer identifier that - * can be returned, so we have to use a whole unsigned for the framebuffer - * we are drawing to. When processing batches, we check to see if this - * changes and adjust the render target accordingly. Some sorting is - * performed to reduce the amount we change framebuffers. - */ - guint framebuffer : 31; - - /* The number of uniforms to change. This must be less than or equal to - * GL_MAX_UNIFORM_LOCATIONS but only guaranteed up to 1024 by any OpenGL - * implementation to be conformant. - */ - guint uniform_count : 11; - - /* The number of textures to bind, which is only guaranteed up to 16 - * by the OpenGL specification to be conformant. - */ - guint bind_count : 5; - - /* GL_MAX_ELEMENTS_VERTICES specifies 33000 for this which requires 16-bit - * to address all possible counts <= GL_MAX_ELEMENTS_VERTICES. - */ - guint vbo_count : 16; - - /* The offset within the VBO containing @vbo_count vertices to send with - * glDrawArrays(). - */ - guint vbo_offset; - - /* The offset within the array of uniform changes to be made containing - * @uniform_count `GskGLCommandUniform` elements to apply. - */ - guint uniform_offset; - - /* The offset within the array of bind changes to be made containing - * @bind_count `GskGLCommandBind` elements to apply. - */ - guint bind_offset; -} GskGLCommandDraw; - -G_STATIC_ASSERT (sizeof (GskGLCommandDraw) == 32); - -typedef struct _GskGLCommandClear -{ - GskGLCommandBatchAny any; - guint bits; - guint framebuffer; -} GskGLCommandClear; - -G_STATIC_ASSERT (sizeof (GskGLCommandClear) == 20); - -typedef struct _GskGLCommandUniform -{ - GskGLUniformInfo info; - guint location; -} GskGLCommandUniform; - -G_STATIC_ASSERT (sizeof (GskGLCommandUniform) == 8); - -typedef union _GskGLCommandBatch -{ - GskGLCommandBatchAny any; - GskGLCommandDraw draw; - GskGLCommandClear clear; -} GskGLCommandBatch; - -G_STATIC_ASSERT (sizeof (GskGLCommandBatch) == 32); - -typedef struct _GskGLSync { - guint id; - gpointer sync; -} GskGLSync; - -DEFINE_INLINE_ARRAY (GskGLCommandBatches, gsk_gl_command_batches, GskGLCommandBatch) -DEFINE_INLINE_ARRAY (GskGLCommandBinds, gsk_gl_command_binds, GskGLCommandBind) -DEFINE_INLINE_ARRAY (GskGLCommandUniforms, gsk_gl_command_uniforms, GskGLCommandUniform) -DEFINE_INLINE_ARRAY (GskGLSyncs, gsk_gl_syncs, GskGLSync) - -struct _GskGLCommandQueue -{ - GObject parent_instance; - - /* The GdkGLContext we make current before executing GL commands. */ - GdkGLContext *context; - - /* Array of GskGLCommandBatch which is a fixed size structure that will - * point into offsets of other arrays so that all similar data is stored - * together. The idea here is that we reduce the need for pointers so that - * using g_realloc()'d arrays is fine. - */ - GskGLCommandBatches batches; - - /* Contains array of vertices and some wrapper code to help upload them - * to the GL driver. We can also tweak this to use double buffered arrays - * if we find that to be faster on some hardware and/or drivers. - */ - GskGLBuffer vertices; - - /* The GskGLAttachmentState contains information about our FBO and texture - * attachments as we process incoming operations. We snapshot them into - * various batches so that we can compare differences between merge - * candidates. - */ - GskGLAttachmentState *attachments; - - /* The uniform state across all programs. We snapshot this into batches so - * that we can compare uniform state between batches to give us more - * chances at merging draw commands. - */ - GskGLUniformState *uniforms; - - /* Current program if we are in a draw so that we can send commands - * to the uniform state as needed. - */ - GskGLUniformProgram *program_info; - - /* The profiler instance to deliver timing/etc data */ - GskProfiler *profiler; - GskGLProfiler *gl_profiler; - - /* Array of GskGLCommandBind which denote what textures need to be attached - * to which slot. GskGLCommandDraw.bind_offset and bind_count reference this - * array to determine what to attach. - */ - GskGLCommandBinds batch_binds; - - /* Array of GskGLCommandUniform denoting which uniforms must be updated - * before the glDrawArrays() may be called. These are referenced from the - * GskGLCommandDraw.uniform_offset and uniform_count fields. - */ - GskGLCommandUniforms batch_uniforms; - - /* Array of samplers that we use for mag/min filter handling. It is indexed - * by the sampler_index() function. - * - * Note that when samplers are not supported (hello GLES), we fall back to - * setting the texture filter, but that needs to be done for every texture. - * - * Also note that we don't use all of these samplers since some combinations - * are invalid. An index of SAMPLER_EXTERNAL is used to indicate an external - * texture, which needs special sampler treatment. - */ - GLuint samplers[GSK_GL_N_FILTERS * GSK_GL_N_FILTERS]; - - /* Array of sync objects to wait on. - */ - GskGLSyncs syncs; - - /* Discovered max texture size when loading the command queue so that we - * can either scale down or slice textures to fit within this size. Assumed - * to be both height and width. - */ - int max_texture_size; - - /* The index of the last batch in @batches, which may not be the element - * at the end of the array, as batches can be reordered. This is used to - * update the "next" index when adding a new batch. - */ - gint16 tail_batch_index; - gint16 head_batch_index; - - /* Max framebuffer we used, so we can sort items faster */ - guint fbo_max; - - /* Various GSK and GDK metric counter ids */ - struct { - GQuark n_frames; - GQuark cpu_time; - GQuark gpu_time; - guint n_binds; - guint n_fbos; - guint n_uniforms; - guint n_uploads; - guint n_programs; - guint queue_depth; - } metrics; - - /* Counter for uploads on the frame */ - guint n_uploads; - - /* If the GL context is new enough for sampler support */ - guint has_samplers : 1; - - /* If the GL context is new enough to support swizzling (ie is not GLES2) */ - guint can_swizzle : 1; - - /* If we're inside a begin/end_frame pair */ - guint in_frame : 1; - - /* If we're inside of a begin_draw()/end_draw() pair. */ - guint in_draw : 1; - - /* If we've warned about truncating batches */ - guint have_truncated : 1; -}; - -GskGLCommandQueue *gsk_gl_command_queue_new (GdkGLContext *context, - GskGLUniformState *uniforms); -void gsk_gl_command_queue_set_profiler (GskGLCommandQueue *self, - GskProfiler *profiler); -GdkGLContext *gsk_gl_command_queue_get_context (GskGLCommandQueue *self); -void gsk_gl_command_queue_make_current (GskGLCommandQueue *self); -void gsk_gl_command_queue_begin_frame (GskGLCommandQueue *self); -void gsk_gl_command_queue_end_frame (GskGLCommandQueue *self); -void gsk_gl_command_queue_execute (GskGLCommandQueue *self, - guint surface_height, - float scale, - const cairo_region_t *scissor, - guint default_framebuffer); -int gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self, - GdkTexture *texture, - gboolean ensure_mipmap, - gboolean *out_can_mipmap); -int gsk_gl_command_queue_create_texture (GskGLCommandQueue *self, - int width, - int height, - int format); - - -typedef struct { - GdkTexture *texture; - int x; - int y; -} GskGLTextureChunk; - -int gsk_gl_command_queue_upload_texture_chunks(GskGLCommandQueue *self, - gboolean ensure_mipmap, - unsigned int n_chunks, - GskGLTextureChunk *chunks, - gboolean *out_can_mipmap); - -guint gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self); -gboolean gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self, - int width, - int height, - int format, - guint *out_fbo_id, - guint *out_texture_id); -void gsk_gl_command_queue_delete_program (GskGLCommandQueue *self, - guint program_id); -void gsk_gl_command_queue_clear (GskGLCommandQueue *self, - guint clear_bits, - const graphene_rect_t *viewport); -gboolean gsk_gl_command_queue_begin_draw (GskGLCommandQueue *self, - GskGLUniformProgram *program_info, - guint width, - guint height); -void gsk_gl_command_queue_end_draw (GskGLCommandQueue *self); -void gsk_gl_command_queue_split_draw (GskGLCommandQueue *self); - -static inline GskGLCommandBatch * -gsk_gl_command_queue_get_batch (GskGLCommandQueue *self) -{ - return gsk_gl_command_batches_tail (&self->batches); -} - -static inline GskGLDrawVertex * -gsk_gl_command_queue_add_vertices (GskGLCommandQueue *self) -{ - gsk_gl_command_queue_get_batch (self)->draw.vbo_count += GSK_GL_N_VERTICES; - return gsk_gl_buffer_advance (&self->vertices, GSK_GL_N_VERTICES); -} - -static inline GskGLDrawVertex * -gsk_gl_command_queue_add_n_vertices (GskGLCommandQueue *self, - guint count) -{ - /* This is a batch form of gsk_gl_command_queue_add_vertices(). Note that - * it does *not* add the count to .draw.vbo_count as the caller is responsible - * for that. - */ - return gsk_gl_buffer_advance (&self->vertices, GSK_GL_N_VERTICES * count); -} - -static inline void -gsk_gl_command_queue_retract_n_vertices (GskGLCommandQueue *self, - guint count) -{ - /* Like gsk_gl_command_queue_add_n_vertices(), this does not tweak - * the draw vbo_count. - */ - gsk_gl_buffer_retract (&self->vertices, GSK_GL_N_VERTICES * count); -} - -static inline guint -gsk_gl_command_queue_bind_framebuffer (GskGLCommandQueue *self, - guint framebuffer) -{ - guint ret = self->attachments->fbo.id; - gsk_gl_attachment_state_bind_framebuffer (self->attachments, framebuffer); - return ret; -} - -static inline GskGLSync * -gsk_gl_syncs_get_sync (GskGLSyncs *syncs, - guint id) -{ - for (unsigned int i = 0; i < syncs->len; i++) - { - GskGLSync *sync = &syncs->items[i]; - if (sync->id == id) - return sync; - } - return NULL; -} - -static inline void -gsk_gl_syncs_add_sync (GskGLSyncs *syncs, - guint id, - gpointer sync) -{ - GskGLSync *s; - - s = gsk_gl_syncs_get_sync (syncs, id); - if (s) - g_assert (s->sync == sync); - else - { - s = gsk_gl_syncs_append (syncs); - s->id = id; - s->sync = sync; - } -} - -G_END_DECLS - diff --git a/gsk/gl/gskglcompiler.c b/gsk/gl/gskglcompiler.c deleted file mode 100644 index 74b42a18aa6..00000000000 --- a/gsk/gl/gskglcompiler.c +++ /dev/null @@ -1,703 +0,0 @@ -/* gskglcompiler.c - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include -#include -#include - -#include "gskglcommandqueueprivate.h" -#include "gskglcompilerprivate.h" -#include "gskglprogramprivate.h" - -#define SHADER_VERSION_GLES "100" -#define SHADER_VERSION_GLES3 "300 es" -#define SHADER_VERSION_GL2_LEGACY "110" -#define SHADER_VERSION_GL3_LEGACY "130" -#define SHADER_VERSION_GL3 "150" - -struct _GskGLCompiler -{ - GObject parent_instance; - - GskGLDriver *driver; - - GBytes *all_preamble; - GBytes *fragment_preamble; - GBytes *vertex_preamble; - GBytes *fragment_source; - GBytes *fragment_suffix; - GBytes *vertex_source; - GBytes *vertex_suffix; - - GArray *attrib_locations; - - const char *glsl_version; - - guint gl3 : 1; - guint gles : 1; - guint gles3 : 1; - guint legacy : 1; - guint debug_shaders : 1; -}; - -typedef struct _GskGLProgramAttrib -{ - const char *name; - guint location; -} GskGLProgramAttrib; - -static GBytes *empty_bytes; - -G_DEFINE_TYPE (GskGLCompiler, gsk_gl_compiler, G_TYPE_OBJECT) - -static void -gsk_gl_compiler_finalize (GObject *object) -{ - GskGLCompiler *self = (GskGLCompiler *)object; - - g_clear_pointer (&self->all_preamble, g_bytes_unref); - g_clear_pointer (&self->fragment_preamble, g_bytes_unref); - g_clear_pointer (&self->vertex_preamble, g_bytes_unref); - g_clear_pointer (&self->vertex_suffix, g_bytes_unref); - g_clear_pointer (&self->fragment_source, g_bytes_unref); - g_clear_pointer (&self->fragment_suffix, g_bytes_unref); - g_clear_pointer (&self->vertex_source, g_bytes_unref); - g_clear_pointer (&self->attrib_locations, g_array_unref); - g_clear_object (&self->driver); - - G_OBJECT_CLASS (gsk_gl_compiler_parent_class)->finalize (object); -} - -static void -gsk_gl_compiler_class_init (GskGLCompilerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = gsk_gl_compiler_finalize; - - empty_bytes = g_bytes_new (NULL, 0); -} - -static void -gsk_gl_compiler_init (GskGLCompiler *self) -{ - self->glsl_version = "150"; - self->attrib_locations = g_array_new (FALSE, FALSE, sizeof (GskGLProgramAttrib)); - self->all_preamble = g_bytes_ref (empty_bytes); - self->vertex_preamble = g_bytes_ref (empty_bytes); - self->fragment_preamble = g_bytes_ref (empty_bytes); - self->vertex_source = g_bytes_ref (empty_bytes); - self->vertex_suffix = g_bytes_ref (empty_bytes); - self->fragment_source = g_bytes_ref (empty_bytes); - self->fragment_suffix = g_bytes_ref (empty_bytes); -} - -GskGLCompiler * -gsk_gl_compiler_new (GskGLDriver *driver, - gboolean debug_shaders) -{ - GskGLCompiler *self; - GdkGLContext *context; - - g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL); - g_return_val_if_fail (driver->shared_command_queue != NULL, NULL); - - self = g_object_new (GSK_TYPE_GL_COMPILER, NULL); - self->driver = g_object_ref (driver); - self->debug_shaders = !!debug_shaders; - - context = gsk_gl_driver_get_context (self->driver); - - if (gdk_gl_context_get_use_es (context)) - { - int maj, min; - - /* for OpenGL/ES 3.0+, use "300 es" as our shader version */ - gdk_gl_context_get_version (context, &maj, &min); - - if (maj >= 3) - { - self->glsl_version = SHADER_VERSION_GLES3; - self->gles3 = TRUE; - } - else - { - self->glsl_version = SHADER_VERSION_GLES; - self->gles = TRUE; - } - } - else if (gdk_gl_context_is_legacy (context)) - { - int maj, min; - - gdk_gl_context_get_version (context, &maj, &min); - - /* On Windows, legacy contexts can give us a GL 4.x context */ - if (maj >= 3) - self->glsl_version = SHADER_VERSION_GL3_LEGACY; - else - self->glsl_version = SHADER_VERSION_GL2_LEGACY; - - self->legacy = TRUE; - } - else - { - self->glsl_version = SHADER_VERSION_GL3; - self->gl3 = TRUE; - } - - gsk_gl_command_queue_make_current (self->driver->shared_command_queue); - - return g_steal_pointer (&self); -} - -void -gsk_gl_compiler_bind_attribute (GskGLCompiler *self, - const char *name, - guint location) -{ - GskGLProgramAttrib attrib; - - g_return_if_fail (GSK_IS_GL_COMPILER (self)); - g_return_if_fail (name != NULL); - g_return_if_fail (location < 32); - - attrib.name = g_intern_string (name); - attrib.location = location; - - g_array_append_val (self->attrib_locations, attrib); -} - -void -gsk_gl_compiler_clear_attributes (GskGLCompiler *self) -{ - g_return_if_fail (GSK_IS_GL_COMPILER (self)); - - g_array_set_size (self->attrib_locations, 0); -} - -void -gsk_gl_compiler_set_preamble (GskGLCompiler *self, - GskGLCompilerKind kind, - GBytes *preamble_bytes) -{ - GBytes **loc = NULL; - - g_return_if_fail (GSK_IS_GL_COMPILER (self)); - g_return_if_fail (preamble_bytes != NULL); - - if (kind == GSK_GL_COMPILER_ALL) - loc = &self->all_preamble; - else if (kind == GSK_GL_COMPILER_FRAGMENT) - loc = &self->fragment_preamble; - else if (kind == GSK_GL_COMPILER_VERTEX) - loc = &self->vertex_preamble; - else - g_return_if_reached (); - - g_assert (loc != NULL); - - if (*loc != preamble_bytes) - { - g_clear_pointer (loc, g_bytes_unref); - *loc = preamble_bytes ? g_bytes_ref (preamble_bytes) : NULL; - } -} - -void -gsk_gl_compiler_set_preamble_from_resource (GskGLCompiler *self, - GskGLCompilerKind kind, - const char *resource_path) -{ - GError *error = NULL; - GBytes *bytes; - - g_return_if_fail (GSK_IS_GL_COMPILER (self)); - g_return_if_fail (kind == GSK_GL_COMPILER_ALL || - kind == GSK_GL_COMPILER_VERTEX || - kind == GSK_GL_COMPILER_FRAGMENT); - g_return_if_fail (resource_path != NULL); - - bytes = g_resources_lookup_data (resource_path, - G_RESOURCE_LOOKUP_FLAGS_NONE, - &error); - - if (bytes == NULL) - g_warning ("Cannot set shader from resource: %s", error->message); - else - gsk_gl_compiler_set_preamble (self, kind, bytes); - - g_clear_pointer (&bytes, g_bytes_unref); - g_clear_error (&error); -} - -void -gsk_gl_compiler_set_source (GskGLCompiler *self, - GskGLCompilerKind kind, - GBytes *source_bytes) -{ - GBytes **loc = NULL; - - g_return_if_fail (GSK_IS_GL_COMPILER (self)); - g_return_if_fail (kind == GSK_GL_COMPILER_ALL || - kind == GSK_GL_COMPILER_VERTEX || - kind == GSK_GL_COMPILER_FRAGMENT); - - if (source_bytes == NULL) - source_bytes = empty_bytes; - - /* If kind is ALL, then we need to split the fragment and - * vertex shaders from the bytes and assign them individually. - * This safely scans for FRAGMENT_SHADER and VERTEX_SHADER as - * specified within the GLSL resources. Some care is taken to - * use GBytes which reference the original bytes instead of - * copying them. - */ - if (kind == GSK_GL_COMPILER_ALL) - { - gsize len = 0; - const char *source; - const char *vertex_shader_start; - const char *fragment_shader_start; - const char *endpos; - GBytes *fragment_bytes; - GBytes *vertex_bytes; - - g_clear_pointer (&self->fragment_source, g_bytes_unref); - g_clear_pointer (&self->vertex_source, g_bytes_unref); - - source = g_bytes_get_data (source_bytes, &len); - endpos = source + len; - vertex_shader_start = g_strstr_len (source, len, "VERTEX_SHADER"); - fragment_shader_start = g_strstr_len (source, len, "FRAGMENT_SHADER"); - - if (vertex_shader_start == NULL) - { - g_warning ("Failed to locate VERTEX_SHADER in shader source"); - return; - } - - if (fragment_shader_start == NULL) - { - g_warning ("Failed to locate FRAGMENT_SHADER in shader source"); - return; - } - - if (vertex_shader_start > fragment_shader_start) - { - g_warning ("VERTEX_SHADER must come before FRAGMENT_SHADER"); - return; - } - - /* Locate next newlines */ - while (vertex_shader_start < endpos && vertex_shader_start[0] != '\n') - vertex_shader_start++; - while (fragment_shader_start < endpos && fragment_shader_start[0] != '\n') - fragment_shader_start++; - - vertex_bytes = g_bytes_new_from_bytes (source_bytes, - vertex_shader_start - source, - fragment_shader_start - vertex_shader_start); - fragment_bytes = g_bytes_new_from_bytes (source_bytes, - fragment_shader_start - source, - endpos - fragment_shader_start); - - gsk_gl_compiler_set_source (self, GSK_GL_COMPILER_VERTEX, vertex_bytes); - gsk_gl_compiler_set_source (self, GSK_GL_COMPILER_FRAGMENT, fragment_bytes); - - g_bytes_unref (fragment_bytes); - g_bytes_unref (vertex_bytes); - - return; - } - - if (kind == GSK_GL_COMPILER_FRAGMENT) - loc = &self->fragment_source; - else if (kind == GSK_GL_COMPILER_VERTEX) - loc = &self->vertex_source; - else - g_return_if_reached (); - - if (*loc != source_bytes) - { - g_clear_pointer (loc, g_bytes_unref); - *loc = g_bytes_ref (source_bytes); - } -} - -void -gsk_gl_compiler_set_source_from_resource (GskGLCompiler *self, - GskGLCompilerKind kind, - const char *resource_path) -{ - GError *error = NULL; - GBytes *bytes; - - g_return_if_fail (GSK_IS_GL_COMPILER (self)); - g_return_if_fail (kind == GSK_GL_COMPILER_ALL || - kind == GSK_GL_COMPILER_VERTEX || - kind == GSK_GL_COMPILER_FRAGMENT); - g_return_if_fail (resource_path != NULL); - - bytes = g_resources_lookup_data (resource_path, - G_RESOURCE_LOOKUP_FLAGS_NONE, - &error); - - if (bytes == NULL) - g_warning ("Cannot set shader from resource: %s", error->message); - else - gsk_gl_compiler_set_source (self, kind, bytes); - - g_clear_pointer (&bytes, g_bytes_unref); - g_clear_error (&error); -} - -void -gsk_gl_compiler_set_suffix (GskGLCompiler *self, - GskGLCompilerKind kind, - GBytes *suffix_bytes) -{ - GBytes **loc; - - g_return_if_fail (GSK_IS_GL_COMPILER (self)); - g_return_if_fail (kind == GSK_GL_COMPILER_VERTEX || - kind == GSK_GL_COMPILER_FRAGMENT); - g_return_if_fail (suffix_bytes != NULL); - - if (suffix_bytes == NULL) - suffix_bytes = empty_bytes; - - if (kind == GSK_GL_COMPILER_FRAGMENT) - loc = &self->fragment_suffix; - else if (kind == GSK_GL_COMPILER_VERTEX) - loc = &self->vertex_suffix; - else - g_return_if_reached (); - - if (*loc != suffix_bytes) - { - g_clear_pointer (loc, g_bytes_unref); - *loc = g_bytes_ref (suffix_bytes); - } -} - -void -gsk_gl_compiler_set_suffix_from_resource (GskGLCompiler *self, - GskGLCompilerKind kind, - const char *resource_path) -{ - GError *error = NULL; - GBytes *bytes; - - g_return_if_fail (GSK_IS_GL_COMPILER (self)); - g_return_if_fail (kind == GSK_GL_COMPILER_VERTEX || - kind == GSK_GL_COMPILER_FRAGMENT); - g_return_if_fail (resource_path != NULL); - - bytes = g_resources_lookup_data (resource_path, - G_RESOURCE_LOOKUP_FLAGS_NONE, - &error); - - if (bytes == NULL) - g_warning ("Cannot set suffix from resource: %s", error->message); - else - gsk_gl_compiler_set_suffix (self, kind, bytes); - - g_clear_pointer (&bytes, g_bytes_unref); - g_clear_error (&error); -} - -static void -prepend_line_numbers (char *code, - GString *s) -{ - char *p; - int line; - - p = code; - line = 1; - while (*p) - { - char *end = strchr (p, '\n'); - if (end) - end = end + 1; /* Include newline */ - else - end = p + strlen (p); - - g_string_append_printf (s, "%3d| ", line++); - g_string_append_len (s, p, end - p); - - p = end; - } -} - -static gboolean -check_shader_error (int shader_id, - GError **error) -{ - GLint status; - GLint log_len; - GLint code_len; - char *buffer; - char *code; - GString *s; - - glGetShaderiv (shader_id, GL_COMPILE_STATUS, &status); - - if G_LIKELY (status == GL_TRUE) - return TRUE; - - glGetShaderiv (shader_id, GL_INFO_LOG_LENGTH, &log_len); - buffer = g_malloc0 (log_len + 1); - glGetShaderInfoLog (shader_id, log_len, NULL, buffer); - - glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len); - code = g_malloc0 (code_len + 1); - glGetShaderSource (shader_id, code_len, NULL, code); - - s = g_string_new (""); - prepend_line_numbers (code, s); - - g_set_error (error, - GDK_GL_ERROR, - GDK_GL_ERROR_COMPILATION_FAILED, - "Compilation failure in shader.\n" - "Source Code: %s\n" - "\n" - "Error Message:\n" - "%s\n" - "\n", - s->str, - buffer); - - g_string_free (s, TRUE); - g_free (buffer); - g_free (code); - - return FALSE; -} - -static void -print_shader_info (const char *prefix, - int shader_id, - const char *name) -{ - if (GSK_DEBUG_CHECK(SHADERS)) - { - int code_len; - - glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len); - - if (code_len > 0) - { - char *code; - GString *s; - - code = g_malloc0 (code_len + 1); - glGetShaderSource (shader_id, code_len, NULL, code); - - s = g_string_new (NULL); - prepend_line_numbers (code, s); - - g_message ("%s %d, %s:\n%s", - prefix, shader_id, - name ? name : "unnamed", - s->str); - g_string_free (s, TRUE); - g_free (code); - } - } -} - -static const char * -get_shader_string (GBytes *bytes) -{ - /* 0 length bytes will give us NULL back */ - const char *str = g_bytes_get_data (bytes, NULL); - return str ? str : ""; -} - -GskGLProgram * -gsk_gl_compiler_compile (GskGLCompiler *self, - const char *name, - const char *clip, - GError **error) -{ - char version[32]; - const char *debug = ""; - const char *legacy = ""; - const char *gl3 = ""; - const char *gles = ""; - const char *gles3 = ""; - int program_id; - int vertex_id; - int fragment_id; - int status; - - g_return_val_if_fail (GSK_IS_GL_COMPILER (self), NULL); - g_return_val_if_fail (self->all_preamble != NULL, NULL); - g_return_val_if_fail (self->fragment_preamble != NULL, NULL); - g_return_val_if_fail (self->vertex_preamble != NULL, NULL); - g_return_val_if_fail (self->fragment_source != NULL, NULL); - g_return_val_if_fail (self->vertex_source != NULL, NULL); - g_return_val_if_fail (self->driver != NULL, NULL); - - gsk_gl_command_queue_make_current (self->driver->command_queue); - - g_snprintf (version, sizeof version, "#version %s\n", self->glsl_version); - - if (self->debug_shaders) - debug = "#define GSK_DEBUG 1\n"; - - if (self->legacy) - legacy = "#define GSK_LEGACY 1\n"; - - if (self->gles) - gles = "#define GSK_GLES 1\n"; - - if (self->gles3) - gles3 = "#define GSK_GLES3 1\n"; - - if (self->gl3) - gl3 = "#define GSK_GL3 1\n"; - - vertex_id = glCreateShader (GL_VERTEX_SHADER); - glShaderSource (vertex_id, - 11, - (const char *[]) { - version, debug, legacy, gl3, gles, gles3, clip, - get_shader_string (self->all_preamble), - get_shader_string (self->vertex_preamble), - get_shader_string (self->vertex_source), - get_shader_string (self->vertex_suffix), - }, - (int[]) { - strlen (version), - strlen (debug), - strlen (legacy), - strlen (gl3), - strlen (gles), - strlen (gles3), - strlen (clip), - g_bytes_get_size (self->all_preamble), - g_bytes_get_size (self->vertex_preamble), - g_bytes_get_size (self->vertex_source), - g_bytes_get_size (self->vertex_suffix), - }); - glCompileShader (vertex_id); - - if (!check_shader_error (vertex_id, error)) - { - glDeleteShader (vertex_id); - return NULL; - } - - print_shader_info ("Vertex shader", vertex_id, name); - - fragment_id = glCreateShader (GL_FRAGMENT_SHADER); - glShaderSource (fragment_id, - 11, - (const char *[]) { - version, debug, legacy, gl3, gles, gles3, clip, - get_shader_string (self->all_preamble), - get_shader_string (self->fragment_preamble), - get_shader_string (self->fragment_source), - get_shader_string (self->fragment_suffix), - }, - (int[]) { - strlen (version), - strlen (debug), - strlen (legacy), - strlen (gl3), - strlen (gles), - strlen (gles3), - strlen (clip), - g_bytes_get_size (self->all_preamble), - g_bytes_get_size (self->fragment_preamble), - g_bytes_get_size (self->fragment_source), - g_bytes_get_size (self->fragment_suffix), - }); - glCompileShader (fragment_id); - - if (!check_shader_error (fragment_id, error)) - { - glDeleteShader (vertex_id); - glDeleteShader (fragment_id); - return NULL; - } - - print_shader_info ("Fragment shader", fragment_id, name); - - program_id = glCreateProgram (); - glAttachShader (program_id, vertex_id); - glAttachShader (program_id, fragment_id); - - for (guint i = 0; i < self->attrib_locations->len; i++) - { - const GskGLProgramAttrib *attrib; - - attrib = &g_array_index (self->attrib_locations, GskGLProgramAttrib, i); - glBindAttribLocation (program_id, attrib->location, attrib->name); - } - - glLinkProgram (program_id); - - glGetProgramiv (program_id, GL_LINK_STATUS, &status); - - glDetachShader (program_id, vertex_id); - glDeleteShader (vertex_id); - - glDetachShader (program_id, fragment_id); - glDeleteShader (fragment_id); - - if (status == GL_FALSE) - { - char *buffer = NULL; - int log_len = 0; - - glGetProgramiv (program_id, GL_INFO_LOG_LENGTH, &log_len); - - if (log_len > 0) - { - /* log_len includes NULL */ - buffer = g_malloc0 (log_len); - glGetProgramInfoLog (program_id, log_len, NULL, buffer); - } - - g_warning ("Linking failure in shader:\n%s", - buffer ? buffer : ""); - - g_set_error (error, - GDK_GL_ERROR, - GDK_GL_ERROR_LINK_FAILED, - "Linking failure in shader: %s", - buffer ? buffer : ""); - - g_free (buffer); - - glDeleteProgram (program_id); - - return NULL; - } - - return gsk_gl_program_new (self->driver, name, program_id); -} diff --git a/gsk/gl/gskglcompilerprivate.h b/gsk/gl/gskglcompilerprivate.h deleted file mode 100644 index 82a2dda81a2..00000000000 --- a/gsk/gl/gskglcompilerprivate.h +++ /dev/null @@ -1,68 +0,0 @@ -/* gskglcompilerprivate.h - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "gskgltypesprivate.h" - -G_BEGIN_DECLS - -typedef enum _GskGLCompilerKind -{ - GSK_GL_COMPILER_ALL, - GSK_GL_COMPILER_FRAGMENT, - GSK_GL_COMPILER_VERTEX, -} GskGLCompilerKind; - -#define GSK_TYPE_GL_COMPILER (gsk_gl_compiler_get_type()) - -G_DECLARE_FINAL_TYPE (GskGLCompiler, gsk_gl_compiler, GSK, GL_COMPILER, GObject) - -GskGLCompiler * gsk_gl_compiler_new (GskGLDriver *driver, - gboolean debug); -void gsk_gl_compiler_set_preamble (GskGLCompiler *self, - GskGLCompilerKind kind, - GBytes *preamble_bytes); -void gsk_gl_compiler_set_preamble_from_resource (GskGLCompiler *self, - GskGLCompilerKind kind, - const char *resource_path); -void gsk_gl_compiler_set_source (GskGLCompiler *self, - GskGLCompilerKind kind, - GBytes *source_bytes); -void gsk_gl_compiler_set_source_from_resource (GskGLCompiler *self, - GskGLCompilerKind kind, - const char *resource_path); -void gsk_gl_compiler_set_suffix (GskGLCompiler *self, - GskGLCompilerKind kind, - GBytes *suffix_bytes); -void gsk_gl_compiler_set_suffix_from_resource (GskGLCompiler *self, - GskGLCompilerKind kind, - const char *resource_path); -void gsk_gl_compiler_bind_attribute (GskGLCompiler *self, - const char *name, - guint location); -void gsk_gl_compiler_clear_attributes (GskGLCompiler *self); -GskGLProgram * gsk_gl_compiler_compile (GskGLCompiler *self, - const char *name, - const char *clip, - GError **error); - -G_END_DECLS - diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c deleted file mode 100644 index 1d0a1542454..00000000000 --- a/gsk/gl/gskgldriver.c +++ /dev/null @@ -1,1867 +0,0 @@ -/* gskgldriver.c - * - * Copyright 2017 Timm Bäder - * Copyright 2018 Matthias Clasen - * Copyright 2018 Alexander Larsson - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "gskgldriverprivate.h" - -#include -#include -#include - -#include "gskglcommandqueueprivate.h" -#include "gskglcompilerprivate.h" -#include "gskglglyphlibraryprivate.h" -#include "gskgliconlibraryprivate.h" -#include "gskglprogramprivate.h" -#include "gskglshadowlibraryprivate.h" -#include "fp16private.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - - -G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT) - -static guint -texture_key_hash (gconstpointer v) -{ - const GskTextureKey *k = (const GskTextureKey *)v; - - /* Optimize for 0..3 where 0 is the scaled out case. Usually - * we'll be squarely on 1 or 2 for standard vs HiDPI. When rendering - * to a texture scaled out like in node-editor, we might be < 1. - */ - guint scale_x = floorf (k->scale_x); - guint scale_y = floorf (k->scale_y); - - return GPOINTER_TO_SIZE (k->pointer) ^ - ((scale_x << 8) | - (scale_y << 4) | - k->pointer_is_child); -} - -static gboolean -texture_key_equal (gconstpointer v1, - gconstpointer v2) -{ - const GskTextureKey *k1 = (const GskTextureKey *)v1; - const GskTextureKey *k2 = (const GskTextureKey *)v2; - - return k1->pointer == k2->pointer && - k1->scale_x == k2->scale_x && - k1->scale_y == k2->scale_y && - k1->pointer_is_child == k2->pointer_is_child && - (!k1->pointer_is_child || memcmp (&k1->parent_rect, &k2->parent_rect, sizeof k1->parent_rect) == 0); -} - -static void -remove_texture_key_for_id (GskGLDriver *self, - guint texture_id) -{ - GskTextureKey *key; - - g_assert (GSK_IS_GL_DRIVER (self)); - g_assert (texture_id > 0); - - /* g_hash_table_remove() will cause @key to be freed */ - if (g_hash_table_steal_extended (self->texture_id_to_key, - GUINT_TO_POINTER (texture_id), - NULL, - (gpointer *)&key)) - g_hash_table_remove (self->key_to_texture_id, key); -} - -static void -gsk_gl_texture_destroyed (gpointer data) -{ - ((GskGLTexture *)data)->user = NULL; -} - -static void -gsk_gl_driver_autorelease_texture (GskGLDriver *self, - guint texture_id) -{ - g_assert (GSK_IS_GL_DRIVER (self)); - - g_array_append_val (self->texture_pool, texture_id); -} - -static guint -gsk_gl_driver_collect_unused_textures (GskGLDriver *self, - gint64 watermark) -{ - GHashTableIter iter; - gpointer k, v; - guint old_size; - guint collected; - - g_assert (GSK_IS_GL_DRIVER (self)); - - old_size = g_hash_table_size (self->textures); - - g_hash_table_iter_init (&iter, self->textures); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - GskGLTexture *t = v; - - if (t->user || t->permanent) - continue; - - if (t->last_used_in_frame <= watermark) - { - g_hash_table_iter_steal (&iter); - - g_assert (t->link.prev == NULL); - g_assert (t->link.next == NULL); - g_assert (t->link.data == t); - - remove_texture_key_for_id (self, t->texture_id); - gsk_gl_driver_autorelease_texture (self, t->texture_id); - t->texture_id = 0; - gsk_gl_texture_free (t); - } - } - - collected = old_size - g_hash_table_size (self->textures); - - return collected; -} - -static void -remove_program (gpointer data) -{ - GskGLProgram *program = data; - - g_assert (!program || GSK_IS_GL_PROGRAM (program)); - - if (program != NULL) - { - gsk_gl_program_delete (program); - g_object_unref (program); - } -} - -static void -gsk_gl_driver_shader_weak_cb (gpointer data, - GObject *where_object_was) -{ - GskGLDriver *self = data; - - g_assert (GSK_IS_GL_DRIVER (self)); - - if (self->shader_cache != NULL) - { - if (self->command_queue != NULL) - gsk_gl_command_queue_make_current (self->command_queue); - - g_hash_table_remove (self->shader_cache, where_object_was); - } -} - -G_GNUC_UNUSED G_GNUC_NULL_TERMINATED static inline GBytes * -join_sources (GBytes *first_bytes, - ...) -{ - GByteArray *byte_array = g_byte_array_new (); - GBytes *bytes = first_bytes; - va_list args; - - va_start (args, first_bytes); - while (bytes != NULL) - { - gsize len; - const guint8 *data = g_bytes_get_data (bytes, &len); - if (len > 0) - g_byte_array_append (byte_array, data, len); - g_bytes_unref (bytes); - bytes = va_arg (args, GBytes *); - } - va_end (args); - - return g_byte_array_free_to_bytes (byte_array); -} - -static void -gsk_gl_driver_dispose (GObject *object) -{ - GskGLDriver *self = (GskGLDriver *)object; - - g_assert (GSK_IS_GL_DRIVER (self)); - g_assert (self->in_frame == FALSE); - - if (self->shared_command_queue) - gsk_gl_command_queue_make_current (self->shared_command_queue); - -#define GSK_GL_NO_UNIFORMS -#define GSK_GL_SHADER_RESOURCE(name) -#define GSK_GL_SHADER_STRING(str) -#define GSK_GL_SHADER_SINGLE(name) -#define GSK_GL_SHADER_JOINED(kind, ...) -#define GSK_GL_ADD_UNIFORM(pos, KEY, name) -#define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) \ - GSK_GL_DELETE_PROGRAM(name); \ - GSK_GL_DELETE_PROGRAM(name ## _no_clip); \ - GSK_GL_DELETE_PROGRAM(name ## _rect_clip); -#define GSK_GL_DEFINE_PROGRAM_NO_CLIP(name, resource, uniforms) \ - GSK_GL_DELETE_PROGRAM(name); -#define GSK_GL_DELETE_PROGRAM(name) \ - G_STMT_START { \ - if (self->name) \ - gsk_gl_program_delete (self->name); \ - g_clear_object (&self->name); \ - } G_STMT_END; -# include "gskglprograms.defs" -#undef GSK_GL_NO_UNIFORMS -#undef GSK_GL_SHADER_RESOURCE -#undef GSK_GL_SHADER_STRING -#undef GSK_GL_SHADER_SINGLE -#undef GSK_GL_SHADER_JOINED -#undef GSK_GL_ADD_UNIFORM -#undef GSK_GL_DEFINE_PROGRAM -#undef GSK_GL_DEFINE_PROGRAM_NO_CLIP - -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - if (self->shader_cache != NULL) - { - GHashTableIter iter; - gpointer k, v; - - g_hash_table_iter_init (&iter, self->shader_cache); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - GskGLShader *shader = k; - g_object_weak_unref (G_OBJECT (shader), - gsk_gl_driver_shader_weak_cb, - self); - g_hash_table_iter_remove (&iter); - } - - g_clear_pointer (&self->shader_cache, g_hash_table_unref); - } -G_GNUC_END_IGNORE_DEPRECATIONS - - if (self->command_queue != NULL) - { - gsk_gl_command_queue_make_current (self->command_queue); - gsk_gl_driver_collect_unused_textures (self, 0); - g_clear_object (&self->command_queue); - } - - if (self->autorelease_framebuffers != NULL && - self->autorelease_framebuffers->len > 0) - { - glDeleteFramebuffers (self->autorelease_framebuffers->len, - (GLuint *)(gpointer)self->autorelease_framebuffers->data); - self->autorelease_framebuffers->len = 0; - } - - g_clear_object (&self->glyphs_library); - g_clear_object (&self->icons_library); - g_clear_object (&self->shadows_library); - - g_clear_pointer (&self->texture_pool, g_array_unref); - - g_clear_pointer (&self->autorelease_framebuffers, g_array_unref); - g_clear_pointer (&self->key_to_texture_id, g_hash_table_unref); - g_clear_pointer (&self->textures, g_hash_table_unref); - g_clear_pointer (&self->key_to_texture_id, g_hash_table_unref); - g_clear_pointer (&self->texture_id_to_key, g_hash_table_unref); - g_clear_pointer (&self->render_targets, g_ptr_array_unref); - g_clear_pointer (&self->shader_cache, g_hash_table_unref); - - g_clear_object (&self->command_queue); - g_clear_object (&self->shared_command_queue); - - G_OBJECT_CLASS (gsk_gl_driver_parent_class)->dispose (object); -} - -static void -gsk_gl_driver_class_init (GskGLDriverClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = gsk_gl_driver_dispose; -} - -static void -gsk_gl_driver_init (GskGLDriver *self) -{ - self->autorelease_framebuffers = g_array_new (FALSE, FALSE, sizeof (guint)); - self->textures = g_hash_table_new_full (NULL, NULL, NULL, - (GDestroyNotify)gsk_gl_texture_free); - self->texture_id_to_key = g_hash_table_new (NULL, NULL); - self->key_to_texture_id = g_hash_table_new_full (texture_key_hash, - texture_key_equal, - g_free, - NULL); - self->shader_cache = g_hash_table_new_full (NULL, NULL, NULL, remove_program); - self->texture_pool = g_array_new (FALSE, FALSE, sizeof (guint)); - self->render_targets = g_ptr_array_new (); -} - -static gboolean -gsk_gl_driver_load_programs (GskGLDriver *self, - GError **error) -{ - GskGLCompiler *compiler; - gboolean ret = FALSE; - G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME; - - g_assert (GSK_IS_GL_DRIVER (self)); - g_assert (GSK_IS_GL_COMMAND_QUEUE (self->command_queue)); - - compiler = gsk_gl_compiler_new (self, self->debug); - - /* Setup preambles that are shared by all shaders */ - gsk_gl_compiler_set_preamble_from_resource (compiler, - GSK_GL_COMPILER_ALL, - "/org/gtk/libgsk/gl/preamble.glsl"); - gsk_gl_compiler_set_preamble_from_resource (compiler, - GSK_GL_COMPILER_VERTEX, - "/org/gtk/libgsk/gl/preamble.vs.glsl"); - gsk_gl_compiler_set_preamble_from_resource (compiler, - GSK_GL_COMPILER_FRAGMENT, - "/org/gtk/libgsk/gl/preamble.fs.glsl"); - - /* Setup attributes that are provided via VBO */ - gsk_gl_compiler_bind_attribute (compiler, "aPosition", 0); - gsk_gl_compiler_bind_attribute (compiler, "aUv", 1); - gsk_gl_compiler_bind_attribute (compiler, "aColor", 2); - gsk_gl_compiler_bind_attribute (compiler, "aColor2", 3); - - /* Use XMacros to register all of our programs and their uniforms */ -#define GSK_GL_NO_UNIFORMS -#define GSK_GL_SHADER_RESOURCE(name) \ - g_resources_lookup_data("/org/gtk/libgsk/gl/" name, 0, NULL) -#define GSK_GL_SHADER_STRING(str) \ - g_bytes_new_static(str, strlen(str)) -#define GSK_GL_SHADER_SINGLE(bytes) \ - G_STMT_START { \ - GBytes *b = bytes; \ - gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_ALL, b); \ - g_bytes_unref (b); \ - } G_STMT_END; -#define GSK_GL_SHADER_JOINED(kind, ...) \ - G_STMT_START { \ - GBytes *bytes = join_sources(__VA_ARGS__); \ - gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_##kind, bytes); \ - g_bytes_unref (bytes); \ - } G_STMT_END; -#define GSK_GL_ADD_UNIFORM(pos, KEY, name) \ - gsk_gl_program_add_uniform (program, #name, UNIFORM_##KEY); -#define GSK_GL_DEFINE_PROGRAM(name, sources, uniforms) \ - gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_VERTEX, NULL); \ - gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_FRAGMENT, NULL); \ - sources \ - GSK_GL_COMPILE_PROGRAM(name ## _no_clip, uniforms, "#define NO_CLIP 1\n"); \ - GSK_GL_COMPILE_PROGRAM(name ## _rect_clip, uniforms, "#define RECT_CLIP 1\n"); \ - GSK_GL_COMPILE_PROGRAM(name, uniforms, ""); -#define GSK_GL_DEFINE_PROGRAM_NO_CLIP(name, sources, uniforms) \ - gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_VERTEX, NULL); \ - gsk_gl_compiler_set_source (compiler, GSK_GL_COMPILER_FRAGMENT, NULL); \ - sources \ - GSK_GL_COMPILE_PROGRAM(name, uniforms, "#define NO_CLIP 1\n"); -#define GSK_GL_COMPILE_PROGRAM(name, uniforms, clip) \ - G_STMT_START { \ - GskGLProgram *program; \ - gboolean have_alpha; \ - gboolean have_source; \ - \ - if (!(program = gsk_gl_compiler_compile (compiler, #name, clip, error))) \ - goto failure; \ - \ - have_alpha = gsk_gl_program_add_uniform (program, "u_alpha", UNIFORM_SHARED_ALPHA); \ - have_source = gsk_gl_program_add_uniform (program, "u_source", UNIFORM_SHARED_SOURCE); \ - gsk_gl_program_add_uniform (program, "u_clip_rect", UNIFORM_SHARED_CLIP_RECT); \ - gsk_gl_program_add_uniform (program, "u_viewport", UNIFORM_SHARED_VIEWPORT); \ - gsk_gl_program_add_uniform (program, "u_projection", UNIFORM_SHARED_PROJECTION); \ - gsk_gl_program_add_uniform (program, "u_modelview", UNIFORM_SHARED_MODELVIEW); \ - \ - uniforms \ - \ - gsk_gl_program_uniforms_added (program, have_source); \ - if (have_alpha) \ - gsk_gl_program_set_uniform1f (program, UNIFORM_SHARED_ALPHA, 0, 1.0f); \ - \ - *(GskGLProgram **)(((guint8 *)self) + G_STRUCT_OFFSET (GskGLDriver, name)) = \ - g_steal_pointer (&program); \ - } G_STMT_END; -# include "gskglprograms.defs" -#undef GSK_GL_DEFINE_PROGRAM -#undef GSK_GL_DEFINE_PROGRAM_NO_CLIP -#undef GSK_GL_ADD_UNIFORM -#undef GSK_GL_SHADER_SINGLE -#undef GSK_GL_SHADER_JOINED -#undef GSK_GL_SHADER_RESOURCE -#undef GSK_GL_SHADER_STRING -#undef GSK_GL_NO_UNIFORMS - - ret = TRUE; - -failure: - g_clear_object (&compiler); - - gdk_profiler_end_mark (start_time, "Load GL programs", NULL); - - return ret; -} - -/** - * gsk_gl_driver_autorelease_framebuffer: - * @self: a `GskGLDriver` - * @framebuffer_id: the id of the OpenGL framebuffer - * - * Marks @framebuffer_id to be deleted when the current frame has cmopleted. - */ -static void -gsk_gl_driver_autorelease_framebuffer (GskGLDriver *self, - guint framebuffer_id) -{ - g_assert (GSK_IS_GL_DRIVER (self)); - - g_array_append_val (self->autorelease_framebuffers, framebuffer_id); -} - -static GskGLDriver * -gsk_gl_driver_new (GskGLCommandQueue *command_queue, - gboolean debug_shaders, - GError **error) -{ - GskGLDriver *self; - GdkGLContext *context; - gint64 before G_GNUC_UNUSED; - - g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (command_queue), NULL); - - before = GDK_PROFILER_CURRENT_TIME; - - context = gsk_gl_command_queue_get_context (command_queue); - - gdk_gl_context_make_current (context); - - self = g_object_new (GSK_TYPE_GL_DRIVER, NULL); - self->command_queue = g_object_ref (command_queue); - self->shared_command_queue = g_object_ref (command_queue); - self->debug = !!debug_shaders; - - if (!gsk_gl_driver_load_programs (self, error)) - { - g_object_unref (self); - return NULL; - } - - self->glyphs_library = gsk_gl_glyph_library_new (self); - self->icons_library = gsk_gl_icon_library_new (self); - self->shadows_library = gsk_gl_shadow_library_new (self); - - gdk_profiler_end_mark (before, "Create GL driver", NULL); - - return g_steal_pointer (&self); -} - -static void -free_driver (GskGLDriver *driver) -{ - g_object_run_dispose (G_OBJECT (driver)); - g_object_unref (driver); -} - -static void -display_closed (GdkDisplay *display) -{ - g_object_set_data (G_OBJECT (display), "GSK_GL_DRIVER", NULL); -} - -/** - * gsk_gl_driver_for_display: - * @display: A #GdkDisplay that is known to support GL - * @debug_shaders: if debug information for shaders should be displayed - * @error: location for error information - * - * Retrieves a driver for a shared display. Generally this is shared across all GL - * contexts for a display so that fewer programs are necessary for driving output. - * - * Returns: (transfer full): a `GskGLDriver` if successful; otherwise %NULL and - * @error is set. - */ -GskGLDriver * -gsk_gl_driver_for_display (GdkDisplay *display, - gboolean debug_shaders, - GError **error) -{ - GdkGLContext *context; - GskGLCommandQueue *command_queue = NULL; - GskGLDriver *driver; - - g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); - - if ((driver = g_object_get_data (G_OBJECT (display), "GSK_GL_DRIVER"))) - return g_object_ref (driver); - - context = gdk_display_get_gl_context (display); - g_assert (context); - - gdk_gl_context_make_current (context); - - /* Initially we create a command queue using the shared context. However, - * as frames are processed this will be replaced with the command queue - * for a given renderer. But since the programs are compiled into the - * shared context, all other contexts sharing with it will have access - * to those programs. - */ - command_queue = gsk_gl_command_queue_new (context, NULL); - - if (!(driver = gsk_gl_driver_new (command_queue, debug_shaders, error))) - goto failure; - - g_object_set_data_full (G_OBJECT (display), - "GSK_GL_DRIVER", - g_object_ref (driver), - (GDestroyNotify) free_driver); - g_signal_connect (display, "closed", G_CALLBACK (display_closed), NULL); - -failure: - g_clear_object (&command_queue); - - return g_steal_pointer (&driver); -} - -/** - * gsk_gl_driver_begin_frame: - * @self: a `GskGLDriver` - * @command_queue: A `GskGLCommandQueue` from the renderer - * - * Begin a new frame. - * - * Texture atlases, pools, and other resources will be prepared to draw the - * next frame. The command queue should be one that was created for the - * target context to be drawn into (the context of the renderer's surface). - */ -void -gsk_gl_driver_begin_frame (GskGLDriver *self, - GskGLCommandQueue *command_queue) -{ - gint64 last_frame_id; - - g_return_if_fail (GSK_IS_GL_DRIVER (self)); - g_return_if_fail (GSK_IS_GL_COMMAND_QUEUE (command_queue)); - g_return_if_fail (self->in_frame == FALSE); - - last_frame_id = self->current_frame_id; - - self->in_frame = TRUE; - self->current_frame_id++; - - g_set_object (&self->command_queue, command_queue); - - gsk_gl_command_queue_begin_frame (self->command_queue); - - /* Mark unused pixel regions of the atlases */ - gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->icons_library), - self->current_frame_id); - gsk_gl_texture_library_begin_frame (GSK_GL_TEXTURE_LIBRARY (self->glyphs_library), - self->current_frame_id); - - /* Cleanup old shadows */ - gsk_gl_shadow_library_begin_frame (self->shadows_library); - - /* Remove all textures that are from a previous frame or are no - * longer used by linked GdkTexture. We do this at the beginning - * of the following frame instead of the end so that we reduce chances - * we block on any resources while delivering our frames. - */ - gsk_gl_driver_collect_unused_textures (self, last_frame_id - 1); -} - -/** - * gsk_gl_driver_end_frame: - * @self: a `GskGLDriver` - * - * Clean up resources from drawing the current frame. - * - * Temporary resources used while drawing will be released. - */ -void -gsk_gl_driver_end_frame (GskGLDriver *self) -{ - g_return_if_fail (GSK_IS_GL_DRIVER (self)); - g_return_if_fail (self->in_frame == TRUE); - - gsk_gl_command_queue_make_current (self->command_queue); - gsk_gl_command_queue_end_frame (self->command_queue); - - self->in_frame = FALSE; -} - -/** - * gsk_gl_driver_after_frame: - * @self: a `GskGLDriver` - * - * This function does post-frame cleanup operations. - * - * To reduce the chances of blocking on the driver it is performed - * after the frame has swapped buffers. - */ -void -gsk_gl_driver_after_frame (GskGLDriver *self) -{ - g_return_if_fail (GSK_IS_GL_DRIVER (self)); - g_return_if_fail (self->in_frame == FALSE); - - /* Release any render targets (possibly adding them to - * self->autorelease_framebuffers) so we can release the FBOs immediately - * afterwards. - */ - while (self->render_targets->len > 0) - { - GskGLRenderTarget *render_target = g_ptr_array_index (self->render_targets, self->render_targets->len - 1); - - gsk_gl_driver_autorelease_framebuffer (self, render_target->framebuffer_id); - gsk_gl_driver_autorelease_texture (self, render_target->texture_id); - g_free (render_target); - - self->render_targets->len--; - } - - /* Now that we have collected render targets, release all the FBOs */ - if (self->autorelease_framebuffers->len > 0) - { - glDeleteFramebuffers (self->autorelease_framebuffers->len, - (GLuint *)(gpointer)self->autorelease_framebuffers->data); - self->autorelease_framebuffers->len = 0; - } - - /* Release any cached textures we used during the frame */ - if (self->texture_pool->len > 0) - { - glDeleteTextures (self->texture_pool->len, - (GLuint *)(gpointer)self->texture_pool->data); - self->texture_pool->len = 0; - } - - /* Reset command queue to our shared queue in case we have operations - * that need to be processed outside of a frame (such as callbacks - * from external systems such as GDK). - */ - g_set_object (&self->command_queue, self->shared_command_queue); -} - -GdkGLContext * -gsk_gl_driver_get_context (GskGLDriver *self) -{ - g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL); - g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->command_queue), NULL); - - return gsk_gl_command_queue_get_context (self->command_queue); -} - -/** - * gsk_gl_driver_cache_texture: - * @self: a `GskGLDriver` - * @key: the key for the texture - * @texture_id: the id of the texture to be cached - * - * Inserts @texture_id into the texture cache using @key. - * - * Textures can be looked up by @key after calling this function using - * gsk_gl_driver_lookup_texture(). - * - * Textures that have not been used within a number of frames will be - * purged from the texture cache automatically. - */ -void -gsk_gl_driver_cache_texture (GskGLDriver *self, - const GskTextureKey *key, - guint texture_id) -{ - g_assert (GSK_IS_GL_DRIVER (self)); - g_assert (key != NULL); - g_assert (texture_id > 0); - g_assert (g_hash_table_contains (self->textures, GUINT_TO_POINTER (texture_id))); - - if (!g_hash_table_contains (self->key_to_texture_id, key)) - { - GskTextureKey *k; - - k = g_memdup2 (key, sizeof *key); - - g_assert (!g_hash_table_contains (self->texture_id_to_key, GUINT_TO_POINTER (texture_id))); - g_hash_table_insert (self->key_to_texture_id, k, GUINT_TO_POINTER (texture_id)); - g_hash_table_insert (self->texture_id_to_key, GUINT_TO_POINTER (texture_id), k); - } -} - -#if defined(HAVE_DMABUF) && defined (HAVE_EGL) -static void -set_viewport_for_size (GskGLDriver *self, - GskGLProgram *program, - float width, - float height) -{ - float viewport[4] = { 0, 0, width, height }; - - gsk_gl_uniform_state_set4fv (program->uniforms, - program->program_info, - UNIFORM_SHARED_VIEWPORT, 0, - 1, - (const float *)&viewport); - self->stamps[UNIFORM_SHARED_VIEWPORT]++; -} - -#define ORTHO_NEAR_PLANE -10000 -#define ORTHO_FAR_PLANE 10000 - -static void -set_projection_for_size (GskGLDriver *self, - GskGLProgram *program, - float width, - float height) -{ - graphene_matrix_t projection; - - graphene_matrix_init_ortho (&projection, 0, width, 0, height, ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE); - graphene_matrix_scale (&projection, 1, -1, 1); - - gsk_gl_uniform_state_set_matrix (program->uniforms, - program->program_info, - UNIFORM_SHARED_PROJECTION, 0, - &projection); - self->stamps[UNIFORM_SHARED_PROJECTION]++; -} - -static void -reset_modelview (GskGLDriver *self, - GskGLProgram *program) -{ - graphene_matrix_t modelview; - - graphene_matrix_init_identity (&modelview); - - gsk_gl_uniform_state_set_matrix (program->uniforms, - program->program_info, - UNIFORM_SHARED_MODELVIEW, 0, - &modelview); - self->stamps[UNIFORM_SHARED_MODELVIEW]++; -} - -static void -draw_rect (GskGLCommandQueue *command_queue, - float min_x, - float min_y, - float max_x, - float max_y) -{ - GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (command_queue); - float min_u = 0; - float max_u = 1; - float min_v = 1; - float max_v = 0; - guint16 c = FP16_ZERO; - - vertices[0] = (GskGLDrawVertex) { .position = { min_x, min_y }, .uv = { min_u, min_v }, .color = { c, c, c, c } }; - vertices[1] = (GskGLDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c, c, c, c } }; - vertices[2] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c, c, c, c } }; - vertices[3] = (GskGLDrawVertex) { .position = { max_x, max_y }, .uv = { max_u, max_v }, .color = { c, c, c, c } }; - vertices[4] = (GskGLDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c, c, c, c } }; - vertices[5] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c, c, c, c } }; -} - -static unsigned int release_render_target (GskGLDriver *self, - GskGLRenderTarget *render_target, - gboolean release_texture, - gboolean cache_texture); - -static guint -gsk_gl_driver_import_dmabuf_texture (GskGLDriver *self, - GdkDmabufTexture *texture) -{ - GdkGLContext *context = self->command_queue->context; - int max_texture_size = self->command_queue->max_texture_size; - const GdkDmabuf *dmabuf; - guint texture_id; - int width, height; - GskGLProgram *program; - GskGLRenderTarget *render_target; - guint prev_fbo; - gboolean external; - GdkMemoryFormat format; - gboolean premultiply; - - gdk_gl_context_make_current (context); - - width = gdk_texture_get_width (GDK_TEXTURE (texture)); - height = gdk_texture_get_height (GDK_TEXTURE (texture)); - - if (width > max_texture_size || height > max_texture_size) - { - GDK_DISPLAY_DEBUG (gdk_gl_context_get_display (context), DMABUF, - "Can't import dmabuf bigger than MAX_TEXTURE_SIZE (%d)", - max_texture_size); - return 0; - } - - dmabuf = gdk_dmabuf_texture_get_dmabuf (texture); - format = gdk_texture_get_format (GDK_TEXTURE (texture)); - premultiply = gdk_memory_format_alpha (format) == GDK_MEMORY_ALPHA_STRAIGHT; - - texture_id = gdk_gl_context_import_dmabuf (context, - width, height, - dmabuf, - &external); - if (texture_id == 0) - return 0; - - if (!external && !premultiply) - return texture_id; - - gsk_gl_driver_autorelease_texture (self, texture_id); - - if (external) - program = self->external; - else - program = self->premultiply; - - if (!gsk_gl_driver_create_render_target (self, width, height, GL_RGBA8, &render_target)) - return texture_id; - - prev_fbo = gsk_gl_command_queue_bind_framebuffer (self->command_queue, render_target->framebuffer_id); - gsk_gl_command_queue_clear (self->command_queue, 0, &GRAPHENE_RECT_INIT (0, 0, width, height)); - - if (gsk_gl_command_queue_begin_draw (self->command_queue, program->program_info, width, height)) - { - set_projection_for_size (self, program, width, height); - set_viewport_for_size (self, program, width, height); - reset_modelview (self, program); - - if (external) - { - gsk_gl_program_set_uniform_texture (program, - UNIFORM_EXTERNAL_SOURCE, 0, - GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE0, texture_id); - - gsk_gl_program_set_uniform1i (program, UNIFORM_PREMULTIPLY, 0, premultiply); - } - else - { - gsk_gl_program_set_uniform_texture (program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, GL_TEXTURE0, texture_id); - } - - draw_rect (self->command_queue, 0, 0, width, height); - - gsk_gl_command_queue_end_draw (self->command_queue); - } - - gsk_gl_command_queue_bind_framebuffer (self->command_queue, prev_fbo); - - return release_render_target (self, render_target, FALSE, FALSE); -} - -#else - -static guint -gsk_gl_driver_import_dmabuf_texture (GskGLDriver *self, - GdkDmabufTexture *texture) -{ - return 0; -} - -#endif /* HAVE_DMABUF && HAVE_EGL */ - -/** - * gsk_gl_driver_load_texture: - * @self: a `GdkTexture` - * @texture: a `GdkTexture` - * @ensure_mipmap: Mipmaps for this texture must exist for downscaling - * - * Loads a `GdkTexture` by uploading the contents to the GPU when - * necessary. If @texture is a `GdkGLTexture`, it can be used without - * uploading contents to the GPU. - * - * If the texture has already been uploaded and not yet released - * from cache, this function returns that texture id without further - * work. - * - * If the texture has not been used for a number of frames, it will - * be removed from cache. - * - * There is no need to release the resulting texture identifier after - * using it. It will be released automatically. - * - * Returns: a texture identifier - */ -guint -gsk_gl_driver_load_texture (GskGLDriver *self, - GdkTexture *texture, - gboolean ensure_mipmap) -{ - GdkGLContext *context; - GdkMemoryTexture *downloaded_texture; - GskGLTexture *t; - guint texture_id; - int height; - int width; - gboolean can_mipmap = FALSE; - - g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0); - g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0); - g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->command_queue), 0); - - context = self->command_queue->context; - - texture_id = 0; - downloaded_texture = NULL; - - t = gdk_texture_get_render_data (texture, self); - if (t && t->texture_id) - { - if (ensure_mipmap && t->can_mipmap && !t->has_mipmap) - { - glBindTexture (GL_TEXTURE_2D, t->texture_id); - glGenerateMipmap (GL_TEXTURE_2D); - t->has_mipmap = TRUE; - } - - if (!ensure_mipmap || t->has_mipmap) - return t->texture_id; - - gdk_texture_clear_render_data (texture); - } - - if (GDK_IS_DMABUF_TEXTURE (texture) && !ensure_mipmap) - { - texture_id = gsk_gl_driver_import_dmabuf_texture (self, GDK_DMABUF_TEXTURE (texture)); - } - else if (GDK_IS_GL_TEXTURE (texture)) - { - GdkGLTexture *gl_texture = (GdkGLTexture *) texture; - GdkGLContext *texture_context = gdk_gl_texture_get_context (gl_texture); - - if (gdk_gl_context_is_shared (context, texture_context) && - (!ensure_mipmap || gdk_gl_texture_has_mipmap (gl_texture)) && - gdk_memory_format_alpha (gdk_texture_get_format (texture)) != GDK_MEMORY_ALPHA_STRAIGHT) - { - /* A GL texture from the same GL context is a simple task... */ - return gdk_gl_texture_get_id (gl_texture); - } - } - - if (texture_id == 0) - { - downloaded_texture = gdk_memory_texture_from_texture (texture); - - /* The download_texture() call may have switched the GL context. Make sure - * the right context is at work again. - */ - gdk_gl_context_make_current (context); - - texture_id = gsk_gl_command_queue_upload_texture (self->command_queue, GDK_TEXTURE (downloaded_texture), ensure_mipmap, &can_mipmap); - } - - width = gdk_texture_get_width (texture); - height = gdk_texture_get_height (texture); - - t = gsk_gl_texture_new (texture_id, - width, height, - self->current_frame_id); - t->can_mipmap = can_mipmap; - if (ensure_mipmap) - { - g_assert (can_mipmap); - glBindTexture (GL_TEXTURE_2D, t->texture_id); - glGenerateMipmap (GL_TEXTURE_2D); - t->has_mipmap = TRUE; - } - - g_hash_table_insert (self->textures, GUINT_TO_POINTER (texture_id), t); - - if (gdk_texture_set_render_data (texture, self, t, gsk_gl_texture_destroyed)) - t->user = texture; - - gdk_gl_context_label_object_printf (context, GL_TEXTURE, t->texture_id, - "GdkTexture<%p> %d", texture, t->texture_id); - - g_clear_object (&downloaded_texture); - - return t->texture_id; -} - -/** - * gsk_gl_driver_create_texture: - * @self: a `GskGLDriver` - * @width: the width of the texture - * @height: the height of the texture - * @format: format for the texture - * - * Creates a new texture immediately that can be used by the caller - * to upload data, map to a framebuffer, or other uses which may - * modify the texture immediately. - * - * Typical examples for @format are GL_RGBA8, GL_RGBA16F or GL_RGBA32F. - * - * Use gsk_gl_driver_release_texture() to release this texture back into - * the pool so it may be reused later in the pipeline. - * - * Returns: a `GskGLTexture` which can be returned to the pool with - * gsk_gl_driver_release_texture(). - */ -GskGLTexture * -gsk_gl_driver_create_texture (GskGLDriver *self, - float width, - float height, - int format) -{ - GskGLTexture *texture; - guint texture_id; - - g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL); - - texture_id = gsk_gl_command_queue_create_texture (self->command_queue, - width, height, - format); - texture = gsk_gl_texture_new (texture_id, - width, height, - self->current_frame_id); - g_hash_table_insert (self->textures, - GUINT_TO_POINTER (texture->texture_id), - texture); - - return texture; -} - -/** - * gsk_gl_driver_release_texture: - * @self: a `GskGLDriver` - * @texture: a `GskGLTexture` - * - * Releases @texture back into the pool so that it can be used later - * in the command stream by future batches. This helps reduce VRAM - * usage on the GPU. - * - * When the frame has completed, pooled textures will be released - * to free additional VRAM back to the system. - */ -void -gsk_gl_driver_release_texture (GskGLDriver *self, - GskGLTexture *texture) -{ - guint texture_id; - - g_assert (GSK_IS_GL_DRIVER (self)); - g_assert (texture != NULL); - - texture_id = texture->texture_id; - texture->texture_id = 0; - gsk_gl_texture_free (texture); - - if (texture_id > 0) - remove_texture_key_for_id (self, texture_id); - - g_hash_table_steal (self->textures, GUINT_TO_POINTER (texture_id)); - gsk_gl_driver_autorelease_texture (self, texture_id); -} - -/** - * gsk_gl_driver_create_render_target: - * @self: a `GskGLDriver` - * @width: the width for the render target - * @height: the height for the render target - * @format: the format to use - * @out_render_target: (out): a location for the render target - * - * Creates a new render target which contains a framebuffer and a texture - * bound to that framebuffer of the size @width x @height and using the - * appropriate filters. - * - * Typical examples for @format are GK_RGBA8, GL_RGBA16F or GL_RGBA32F. - * - * Use gsk_gl_driver_release_render_target() when you are finished with - * the render target to release it. You may steal the texture from the - * render target when releasing it. - * - * Returns: %TRUE if successful; otherwise %FALSE and @out_fbo_id and - * @out_texture_id are undefined. - */ -gboolean -gsk_gl_driver_create_render_target (GskGLDriver *self, - int width, - int height, - int format, - GskGLRenderTarget **out_render_target) -{ - guint framebuffer_id; - guint texture_id; - - g_return_val_if_fail (GSK_IS_GL_DRIVER (self), FALSE); - g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->command_queue), FALSE); - g_return_val_if_fail (out_render_target != NULL, FALSE); - -#if 0 - if (self->render_targets->len > 0) - { - for (guint i = self->render_targets->len; i > 0; i--) - { - GskGLRenderTarget *render_target = g_ptr_array_index (self->render_targets, i-1); - - if (render_target->width == width && - render_target->height == height) - { - *out_render_target = g_ptr_array_steal_index_fast (self->render_targets, i-1); - return TRUE; - } - } - } -#endif - - if (gsk_gl_command_queue_create_render_target (self->command_queue, - width, height, - format, - &framebuffer_id, &texture_id)) - { - GskGLRenderTarget *render_target; - - render_target = g_new0 (GskGLRenderTarget, 1); - render_target->format = format; - render_target->width = width; - render_target->height = height; - render_target->framebuffer_id = framebuffer_id; - render_target->texture_id = texture_id; - - *out_render_target = render_target; - - return TRUE; - } - - *out_render_target = NULL; - - return FALSE; -} - -static unsigned int -release_render_target (GskGLDriver *self, - GskGLRenderTarget *render_target, - gboolean release_texture, - gboolean cache_texture) -{ - guint texture_id; - - g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0); - g_return_val_if_fail (render_target != NULL, 0); - - if (release_texture) - { - texture_id = 0; - g_ptr_array_add (self->render_targets, render_target); - } - else - { - texture_id = render_target->texture_id; - - if (cache_texture) - { - GskGLTexture *texture; - - texture = gsk_gl_texture_new (render_target->texture_id, - render_target->width, - render_target->height, - self->current_frame_id); - g_hash_table_insert (self->textures, - GUINT_TO_POINTER (texture_id), - g_steal_pointer (&texture)); - } - - gsk_gl_driver_autorelease_framebuffer (self, render_target->framebuffer_id); - g_free (render_target); - - } - - return texture_id; -} - -/** - * gsk_gl_driver_release_render_target: - * @self: a `GskGLDriver` - * @render_target: a `GskGLRenderTarget` created with - * gsk_gl_driver_create_render_target(). - * @release_texture: if the texture should also be released - * - * Releases a render target that was previously created. An attempt may - * be made to cache the render target so that future creations of render - * targets are performed faster. - * - * If @release_texture is %FALSE, the backing texture id is returned and - * the framebuffer is released. Otherwise, both the texture and framebuffer - * are released or cached until the end of the frame. - * - * This may be called when building the render job as the texture or - * framebuffer will not be removed immediately. - * - * Returns: a texture id if @release_texture is %FALSE, otherwise zero. - */ -guint -gsk_gl_driver_release_render_target (GskGLDriver *self, - GskGLRenderTarget *render_target, - gboolean release_texture) -{ - return release_render_target (self, render_target, release_texture, TRUE); -} - -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - -/** - * gsk_gl_driver_lookup_shader: - * @self: a `GskGLDriver` - * @shader: the shader to lookup or load - * @error: a location for a `GError` - * - * Attempts to load @shader from the shader cache. - * - * If it has not been loaded, then it will compile the shader on demand. - * - * Returns: (nullable) (transfer none): a `GskGLShader` - */ -GskGLProgram * -gsk_gl_driver_lookup_shader (GskGLDriver *self, - GskGLShader *shader, - GError **error) -{ - GskGLProgram *program; - - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (shader != NULL, NULL); - - program = g_hash_table_lookup (self->shader_cache, shader); - - if (program == NULL) - { - const GskGLUniform *uniforms; - GskGLCompiler *compiler; - GBytes *suffix; - int n_required_textures; - int n_uniforms; - - uniforms = gsk_gl_shader_get_uniforms (shader, &n_uniforms); - if (n_uniforms > GSK_GL_PROGRAM_MAX_CUSTOM_ARGS) - { - g_set_error (error, - GDK_GL_ERROR, - GDK_GL_ERROR_UNSUPPORTED_FORMAT, - "Tried to use %d uniforms, while only %d is supported", - n_uniforms, - GSK_GL_PROGRAM_MAX_CUSTOM_ARGS); - return NULL; - } - - n_required_textures = gsk_gl_shader_get_n_textures (shader); - if (n_required_textures > GSK_GL_PROGRAM_MAX_CUSTOM_TEXTURES) - { - g_set_error (error, - GDK_GL_ERROR, - GDK_GL_ERROR_UNSUPPORTED_FORMAT, - "Tried to use %d textures, while only %d is supported", - n_required_textures, - GSK_GL_PROGRAM_MAX_CUSTOM_TEXTURES); - return NULL; - } - - compiler = gsk_gl_compiler_new (self, FALSE); - suffix = gsk_gl_shader_get_source (shader); - - gsk_gl_compiler_set_preamble_from_resource (compiler, - GSK_GL_COMPILER_ALL, - "/org/gtk/libgsk/gl/preamble.glsl"); - gsk_gl_compiler_set_preamble_from_resource (compiler, - GSK_GL_COMPILER_VERTEX, - "/org/gtk/libgsk/gl/preamble.vs.glsl"); - gsk_gl_compiler_set_preamble_from_resource (compiler, - GSK_GL_COMPILER_FRAGMENT, - "/org/gtk/libgsk/gl/preamble.fs.glsl"); - gsk_gl_compiler_set_source_from_resource (compiler, - GSK_GL_COMPILER_ALL, - "/org/gtk/libgsk/gl/custom.glsl"); - gsk_gl_compiler_set_suffix (compiler, GSK_GL_COMPILER_FRAGMENT, suffix); - - /* Setup attributes that are provided via VBO */ - gsk_gl_compiler_bind_attribute (compiler, "aPosition", 0); - gsk_gl_compiler_bind_attribute (compiler, "aUv", 1); - gsk_gl_compiler_bind_attribute (compiler, "aColor", 2); - gsk_gl_compiler_bind_attribute (compiler, "aColor2", 3); - - if ((program = gsk_gl_compiler_compile (compiler, NULL, "", error))) - { - gboolean have_alpha; - - gsk_gl_program_add_uniform (program, "u_source", UNIFORM_SHARED_SOURCE); - gsk_gl_program_add_uniform (program, "u_clip_rect", UNIFORM_SHARED_CLIP_RECT); - gsk_gl_program_add_uniform (program, "u_viewport", UNIFORM_SHARED_VIEWPORT); - gsk_gl_program_add_uniform (program, "u_projection", UNIFORM_SHARED_PROJECTION); - gsk_gl_program_add_uniform (program, "u_modelview", UNIFORM_SHARED_MODELVIEW); - have_alpha = gsk_gl_program_add_uniform (program, "u_alpha", UNIFORM_SHARED_ALPHA); - - gsk_gl_program_add_uniform (program, "u_size", UNIFORM_CUSTOM_SIZE); - gsk_gl_program_add_uniform (program, "u_texture1", UNIFORM_CUSTOM_TEXTURE1); - gsk_gl_program_add_uniform (program, "u_texture2", UNIFORM_CUSTOM_TEXTURE2); - gsk_gl_program_add_uniform (program, "u_texture3", UNIFORM_CUSTOM_TEXTURE3); - gsk_gl_program_add_uniform (program, "u_texture4", UNIFORM_CUSTOM_TEXTURE4); - - /* Custom arguments (max is 8) */ - for (guint i = 0; i < n_uniforms; i++) - gsk_gl_program_add_uniform (program, uniforms[i].name, UNIFORM_CUSTOM_ARG0+i); - - gsk_gl_program_uniforms_added (program, TRUE); - - if (have_alpha) - gsk_gl_program_set_uniform1f (program, UNIFORM_SHARED_ALPHA, 0, 1.0f); - - g_hash_table_insert (self->shader_cache, shader, program); - g_object_weak_ref (G_OBJECT (shader), - gsk_gl_driver_shader_weak_cb, - self); - } - - g_object_unref (compiler); - } - - return program; -} - -G_GNUC_END_IGNORE_DEPRECATIONS - -#if 0 -void -gsk_gl_driver_save_texture_to_png (GskGLDriver *driver, - int texture_id, - int width, - int height, - const char *filename) -{ - GdkGLTextureBuilder *builder; - GdkTexture *texture; - - builder = gdk_gl_texture_builder_new (); - gdk_gl_texture_builder_set_context (builder, gsk_gl_driver_get_context (driver)); - gdk_gl_texture_builder_set_id (builder, texture_id); - gdk_gl_texture_builder_set_width (builder, width); - gdk_gl_texture_builder_set_height (builder, height); - - texture = gdk_gl_texture_builder_build (builder, NULL, NULL); - gdk_texture_save_to_png (texture, filename); - - g_object_unref (texture); - g_object_unref (builder); -} - -void -gsk_gl_driver_save_atlases_to_png (GskGLDriver *self, - const char *directory) -{ - GPtrArray *atlases; - - g_return_if_fail (GSK_IS_GL_DRIVER (self)); - - if (directory == NULL) - directory = "."; - -#define copy_atlases(dst, library) \ - g_ptr_array_extend(dst, GSK_GL_TEXTURE_LIBRARY(library)->atlases, NULL, NULL) - atlases = g_ptr_array_new (); - copy_atlases (atlases, self->glyphs_library); - copy_atlases (atlases, self->icons_library); -#undef copy_atlases - - for (guint i = 0; i < atlases->len; i++) - { - GskGLTextureAtlas *atlas = g_ptr_array_index (atlases, i); - char *filename = g_strdup_printf ("%s%sframe-%d-atlas-%d.png", - directory, - G_DIR_SEPARATOR_S, - (int)self->current_frame_id, - atlas->texture_id); - gsk_gl_driver_save_texture_to_png (self, atlas->texture_id, atlas->width, atlas->height, filename); - g_free (filename); - } - - g_ptr_array_unref (atlases); -} -#endif - -GskGLCommandQueue * -gsk_gl_driver_create_command_queue (GskGLDriver *self, - GdkGLContext *context) -{ - g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL); - g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL); - - return gsk_gl_command_queue_new (context, self->shared_command_queue->uniforms); -} - -void -gsk_gl_driver_add_texture_slices (GskGLDriver *self, - GdkTexture *texture, - gboolean ensure_mipmap, - GskGLTextureSlice **out_slices, - guint *out_n_slices) -{ - int max_slice_size; - GskGLTextureSlice *slices; - GskGLTexture *t; - guint n_slices; - guint cols; - guint rows; - int tex_width; - int tex_height; - int x = 0, y = 0; - GdkMemoryTexture *memtex; - GdkMemoryFormat format; - gsize bpp; - int extra_pixels; - GdkMemoryTexture *memtex1 = NULL; - GdkMemoryTexture *memtex2 = NULL; - GdkMemoryTexture *memtex3 = NULL; - GdkMemoryTexture *memtex4 = NULL; - gboolean can_mipmap = TRUE, slice_can_mipmap; - - g_assert (GSK_IS_GL_DRIVER (self)); - g_assert (GDK_IS_TEXTURE (texture)); - g_assert (out_slices != NULL); - g_assert (out_n_slices != NULL); - - max_slice_size = self->command_queue->max_texture_size / 2; - tex_width = texture->width; - tex_height = texture->height; - - cols = (texture->width / max_slice_size) + 1; - rows = (texture->height / max_slice_size) + 1; - - n_slices = cols * rows; - - t = gdk_texture_get_render_data (texture, self); - - if (t) - { - if (t->n_slices == n_slices && - (t->has_mipmap || !ensure_mipmap)) - { - *out_slices = t->slices; - *out_n_slices = t->n_slices; - return; - } - - gdk_texture_clear_render_data (texture); - } - - slices = g_new0 (GskGLTextureSlice, n_slices); - memtex = gdk_memory_texture_from_texture (texture); - - if (ensure_mipmap) - { - guchar *data1, *data2, *data3, *data4; - guchar *top_row, *bot_row, *left_row, *right_row; - GdkTexture *tmp; - int w; - GBytes *bytes; - - /* We need some extra pixels around our tiles, in order for - * GL to properly determine the right level of detail to use. - * This number should probably depend on the scale, but for - * now we just hardcode it. - * - * We create some auxiliary textures to hold the extra pixels: - * - * +---------------------+ - * | memtex1 | - * *---+-------------+---+ - * | | | | - * memtex2| memtex |memtex3 - * | | | | - * *---+-------------+---+ - * | memtex4 | | - * +---------------------+ - */ - - extra_pixels = 15; - - top_row = g_malloc (4 * tex_width); - tmp = gdk_memory_texture_new_subtexture (memtex, 0, 0, tex_width, 1); - gdk_texture_download (tmp, top_row, 4 * tex_width); - g_object_unref (tmp); - - bot_row = g_malloc (4 * tex_width); - tmp = gdk_memory_texture_new_subtexture (memtex, 0, tex_height - 1, tex_width, 1); - gdk_texture_download (tmp, bot_row, 4 * tex_width); - g_object_unref (tmp); - - left_row = g_malloc (4 * tex_height); - tmp = gdk_memory_texture_new_subtexture (memtex, 0, 0, 1, tex_height); - gdk_texture_download (tmp, left_row, 4); - g_object_unref (tmp); - - right_row = g_malloc (4 * tex_height); - tmp = gdk_memory_texture_new_subtexture (memtex, tex_width - 1, 0, 1, tex_height); - gdk_texture_download (tmp, right_row, 4); - g_object_unref (tmp); - - w = tex_width + 2 * extra_pixels; - - data1 = g_malloc (4 * w * extra_pixels); - data2 = g_malloc (4 * extra_pixels * tex_height); - data3 = g_malloc (4 * extra_pixels * tex_height); - data4 = g_malloc (4 * w * extra_pixels); - - format = gdk_texture_get_format (GDK_TEXTURE (memtex)); - bpp = gdk_memory_format_bytes_per_pixel (format); - - for (int i = 0; i < w; i++) - { - int ii = CLAMP (i, extra_pixels, (tex_width - 1) + extra_pixels) - extra_pixels; - - for (int j = 0; j < extra_pixels; j++) - { - for (int k = 0; k < bpp; k++) - { - data1[(j * w + i) * 4 + k] = top_row[ii * 4 + k]; - data4[(j * w + i) * 4 + k] = bot_row[ii * 4 + k]; - } - } - } - - for (int i = 0; i < extra_pixels; i++) - { - for (int j = 0; j < tex_height; j++) - { - for (int k = 0; k < bpp; k++) - { - data2[(j * extra_pixels + i) * 4 + k] = left_row[j * 4 + k]; - data3[(j * extra_pixels + i) * 4 + k] = right_row[j * 4 + k]; - } - } - } - - g_free (top_row); - g_free (bot_row); - g_free (left_row); - g_free (right_row); - - bytes = g_bytes_new_take (data1, 4 * w * extra_pixels); - memtex1 = GDK_MEMORY_TEXTURE (gdk_memory_texture_new (w, extra_pixels, format, bytes, 4 * w)); - g_bytes_unref (bytes); - - bytes = g_bytes_new_take (data2, 4 * extra_pixels * tex_height); - memtex2 = GDK_MEMORY_TEXTURE (gdk_memory_texture_new (extra_pixels, tex_height, format, bytes, 4 * extra_pixels)); - g_bytes_unref (bytes); - - bytes = g_bytes_new_take (data3, 4 * extra_pixels * tex_height); - memtex3 = GDK_MEMORY_TEXTURE (gdk_memory_texture_new (extra_pixels, tex_height, format, bytes, 4 * extra_pixels)); - g_bytes_unref (bytes); - - bytes = g_bytes_new_take (data4, 4 * w * extra_pixels); - memtex4 = GDK_MEMORY_TEXTURE (gdk_memory_texture_new (w, extra_pixels, format, bytes, 4 * w)); - g_bytes_unref (bytes); - } - else - extra_pixels = 0; - - x = 0; - for (guint col = 0; col < cols; col++) - { - int slice_width = col + 1 < cols ? tex_width / cols : tex_width - x; - - y = 0; - for (guint row = 0; row < rows; row++) - { - int slice_height = row + 1 < rows ? tex_height / rows : tex_height - y; - int slice_index = (col * rows) + row; - guint texture_id; - - if (ensure_mipmap) - { - GskGLTextureChunk chunks[5]; - unsigned int n_chunks = 0; - - if (row == 0) - { - chunks[n_chunks].texture = gdk_memory_texture_new_subtexture (memtex1, - x, 0, - slice_width + 2 * extra_pixels, extra_pixels); - chunks[n_chunks].x = 0; - chunks[n_chunks].y = 0; - n_chunks++; - } - - if (row == rows - 1) - { - chunks[n_chunks].texture = gdk_memory_texture_new_subtexture (memtex4, - x, 0, - slice_width + 2 * extra_pixels, extra_pixels); - chunks[n_chunks].x = 0; - chunks[n_chunks].y = slice_height + extra_pixels; - n_chunks++; - } - - if (col == 0) - { - int yy = y - extra_pixels; - int hh = slice_height + 2 * extra_pixels; - int y0 = 0; - - if (row == 0) - { - yy = 0; - y0 = extra_pixels; - hh -= extra_pixels; - } - if (row == rows - 1) - { - hh -= extra_pixels; - } - - chunks[n_chunks].texture = gdk_memory_texture_new_subtexture (memtex2, - 0, yy, extra_pixels, hh); - chunks[n_chunks].x = 0; - chunks[n_chunks].y = y0; - n_chunks++; - } - - if (col == cols - 1) - { - int yy = y - extra_pixels; - int hh = slice_height + 2 * extra_pixels; - int y0 = 0; - - if (row == 0) - { - yy = 0; - y0 = extra_pixels; - hh -= extra_pixels; - } - if (row == rows - 1) - { - hh -= extra_pixels; - } - - chunks[n_chunks].texture = gdk_memory_texture_new_subtexture (memtex3, - 0, yy, extra_pixels, hh); - chunks[n_chunks].x = slice_width + extra_pixels; - chunks[n_chunks].y = y0; - n_chunks++; - } - - { - int xx = x - extra_pixels; - int yy = y - extra_pixels; - int ww = slice_width + 2 * extra_pixels; - int hh = slice_height + 2 * extra_pixels; - int x0 = 0; - int y0 = 0; - if (col == 0) - { - xx = 0; - ww -= extra_pixels; - x0 = extra_pixels; - } - if (col == cols - 1) - { - ww -= extra_pixels; - } - if (row == 0) - { - yy = 0; - hh -= extra_pixels; - y0 = extra_pixels; - } - if (row == rows - 1) - { - hh -= extra_pixels; - } - - chunks[n_chunks].texture = gdk_memory_texture_new_subtexture (memtex, xx, yy, ww, hh); - chunks[n_chunks].x = x0; - chunks[n_chunks].y = y0; - n_chunks++; - } - - texture_id = gsk_gl_command_queue_upload_texture_chunks (self->command_queue, TRUE, n_chunks, chunks, &slice_can_mipmap); - - glBindTexture (GL_TEXTURE_2D, texture_id); - glGenerateMipmap (GL_TEXTURE_2D); - - for (unsigned int i = 0; i < n_chunks; i++) - g_object_unref (chunks[i].texture); - } - else - { - GdkTexture *subtex; - - subtex = gdk_memory_texture_new_subtexture (memtex, x, y, slice_width, slice_height); - texture_id = gsk_gl_command_queue_upload_texture (self->command_queue, subtex, FALSE, &slice_can_mipmap); - g_object_unref (subtex); - } - - can_mipmap &= slice_can_mipmap; - - slices[slice_index].rect.x = x; - slices[slice_index].rect.y = y; - slices[slice_index].rect.width = slice_width; - slices[slice_index].rect.height = slice_height; - slices[slice_index].texture_id = texture_id; - slices[slice_index].area.x = extra_pixels / (float) (slice_width + 2 * extra_pixels); - slices[slice_index].area.y = extra_pixels / (float) (slice_height + 2 * extra_pixels); - slices[slice_index].area.x2 = (extra_pixels + slice_width) / (float) (slice_width + 2 * extra_pixels); - slices[slice_index].area.y2 = (extra_pixels + slice_height) / (float) (slice_height + 2 * extra_pixels); - - y += slice_height; - } - - x += slice_width; - } - - g_object_unref (memtex); - g_clear_object (&memtex1); - g_clear_object (&memtex2); - g_clear_object (&memtex3); - g_clear_object (&memtex4); - - /* Allocate one Texture for the entire thing. */ - t = gsk_gl_texture_new (0, - tex_width, tex_height, - self->current_frame_id); - t->can_mipmap = can_mipmap; - t->has_mipmap = ensure_mipmap; - - /* Use gsk_gl_texture_free() as destroy notify here since we are - * not inserting this GskGLTexture into self->textures! - */ - gdk_texture_set_render_data (texture, self, t, - (GDestroyNotify)gsk_gl_texture_free); - - t->slices = *out_slices = slices; - t->n_slices = *out_n_slices = n_slices; -} - -GskGLTexture * -gsk_gl_driver_mark_texture_permanent (GskGLDriver *self, - guint texture_id) -{ - GskGLTexture *t; - - g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL); - g_return_val_if_fail (texture_id > 0, NULL); - - if ((t = g_hash_table_lookup (self->textures, GUINT_TO_POINTER (texture_id)))) - t->permanent = TRUE; - - return t; -} - -void -gsk_gl_driver_release_texture_by_id (GskGLDriver *self, - guint texture_id) -{ - GskGLTexture *texture; - - g_return_if_fail (GSK_IS_GL_DRIVER (self)); - g_return_if_fail (texture_id > 0); - - remove_texture_key_for_id (self, texture_id); - - if ((texture = g_hash_table_lookup (self->textures, GUINT_TO_POINTER (texture_id)))) - gsk_gl_driver_release_texture (self, texture); -} - -typedef struct _GskGLTextureState -{ - GdkGLContext *context; - GLuint texture_id; - GLsync sync; -} GskGLTextureState; - -static void -create_texture_from_texture_destroy (gpointer data) -{ - GskGLTextureState *state = data; - - g_assert (state != NULL); - g_assert (GDK_IS_GL_CONTEXT (state->context)); - - gdk_gl_context_make_current (state->context); - glDeleteTextures (1, &state->texture_id); - if (state->sync) - glDeleteSync (state->sync); - g_clear_object (&state->context); - g_free (state); -} - -GdkTexture * -gsk_gl_driver_create_gdk_texture (GskGLDriver *self, - guint texture_id, - GdkMemoryFormat format) -{ - GskGLTextureState *state; - GdkGLTextureBuilder *builder; - GskGLTexture *texture; - GdkTexture *result; - - g_return_val_if_fail (GSK_IS_GL_DRIVER (self), NULL); - g_return_val_if_fail (self->command_queue != NULL, NULL); - g_return_val_if_fail (GDK_IS_GL_CONTEXT (self->command_queue->context), NULL); - g_return_val_if_fail (texture_id > 0, NULL); - g_return_val_if_fail (!g_hash_table_contains (self->texture_id_to_key, GUINT_TO_POINTER (texture_id)), NULL); - - /* We must be tracking this texture_id already to use it */ - if (!(texture = g_hash_table_lookup (self->textures, GUINT_TO_POINTER (texture_id)))) - g_return_val_if_reached (NULL); - - state = g_new0 (GskGLTextureState, 1); - state->texture_id = texture_id; - state->context = g_object_ref (self->shared_command_queue->context); - state->sync = glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - - g_hash_table_steal (self->textures, GUINT_TO_POINTER (texture_id)); - - builder = gdk_gl_texture_builder_new (); - gdk_gl_texture_builder_set_context (builder, self->shared_command_queue->context); - gdk_gl_texture_builder_set_id (builder, texture_id); - gdk_gl_texture_builder_set_format (builder, format); - gdk_gl_texture_builder_set_width (builder, texture->width); - gdk_gl_texture_builder_set_height (builder, texture->height); - gdk_gl_texture_builder_set_sync (builder, state->sync); - - result = gdk_gl_texture_builder_build (builder, - create_texture_from_texture_destroy, - state); - - texture->texture_id = 0; - gsk_gl_texture_free (texture); - g_object_unref (builder); - - return result; -} diff --git a/gsk/gl/gskgldriverprivate.h b/gsk/gl/gskgldriverprivate.h deleted file mode 100644 index 9eb84b17a24..00000000000 --- a/gsk/gl/gskgldriverprivate.h +++ /dev/null @@ -1,261 +0,0 @@ -/* gskgldriverprivate.h - * - * Copyright 2020 Christian Hergert - * - * This file is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#include "gskgltypesprivate.h" -#include "gskgltextureprivate.h" - -G_BEGIN_DECLS - -enum { - UNIFORM_SHARED_ALPHA, - UNIFORM_SHARED_SOURCE, - UNIFORM_SHARED_CLIP_RECT, - UNIFORM_SHARED_VIEWPORT, - UNIFORM_SHARED_PROJECTION, - UNIFORM_SHARED_MODELVIEW, - - UNIFORM_SHARED_LAST -}; - -enum { - UNIFORM_CUSTOM_SIZE = UNIFORM_SHARED_LAST, - UNIFORM_CUSTOM_TEXTURE1, - UNIFORM_CUSTOM_TEXTURE2, - UNIFORM_CUSTOM_TEXTURE3, - UNIFORM_CUSTOM_TEXTURE4, - UNIFORM_CUSTOM_ARG0, - UNIFORM_CUSTOM_ARG1, - UNIFORM_CUSTOM_ARG2, - UNIFORM_CUSTOM_ARG3, - UNIFORM_CUSTOM_ARG4, - UNIFORM_CUSTOM_ARG5, - UNIFORM_CUSTOM_ARG6, - UNIFORM_CUSTOM_ARG7, - - UNIFORM_CUSTOM_LAST -}; - -typedef struct { - gconstpointer pointer; - float scale_x; - float scale_y; - int pointer_is_child; - graphene_rect_t parent_rect; /* Valid when pointer_is_child */ -} GskTextureKey; - -#define GSK_GL_NO_UNIFORMS CONCAT_EXPANDED(UNIFORM_INVALID_,__COUNTER__) -#define CONCAT_EXPANDED(a,b) CONCAT_EXPANDED2(a,b) -#define CONCAT_EXPANDED2(a,b) a##b -#define GSK_GL_ADD_UNIFORM(pos, KEY, name) UNIFORM_##KEY = UNIFORM_SHARED_LAST + pos, -#define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) enum { uniforms }; -#define GSK_GL_DEFINE_PROGRAM_NO_CLIP(name, resource, uniforms) enum { uniforms }; -# include "gskglprograms.defs" -#undef GSK_GL_DEFINE_PROGRAM_NO_CLIP -#undef GSK_GL_DEFINE_PROGRAM -#undef GSK_GL_ADD_UNIFORM -#undef GSK_GL_NO_UNIFORMS -#undef CONCAT_EXPANDED -#undef CONCAT_EXPANDED2 - -#define GSK_TYPE_GL_DRIVER (gsk_gl_driver_get_type()) - -G_DECLARE_FINAL_TYPE (GskGLDriver, gsk_gl_driver, GSK, GL_DRIVER, GObject) - -struct _GskGLRenderTarget -{ - guint framebuffer_id; - guint texture_id; - int format; - int width; - int height; -}; - -struct _GskGLDriver -{ - GObject parent_instance; - - GskGLCommandQueue *shared_command_queue; - GskGLCommandQueue *command_queue; - - GskGLGlyphLibrary *glyphs_library; - GskGLIconLibrary *icons_library; - GskGLShadowLibrary *shadows_library; - - GArray *texture_pool; - GHashTable *textures; - GHashTable *key_to_texture_id; - GHashTable *texture_id_to_key; - - GHashTable *shader_cache; - - GArray *autorelease_framebuffers; - GPtrArray *render_targets; - -#define GSK_GL_NO_UNIFORMS -#define GSK_GL_ADD_UNIFORM(pos, KEY, name) -#define GSK_GL_DEFINE_PROGRAM(name, resource, uniforms) \ - GskGLProgram *name ## _no_clip; \ - GskGLProgram *name ## _rect_clip; \ - GskGLProgram *name; -#define GSK_GL_DEFINE_PROGRAM_NO_CLIP(name, resource, uniforms) \ - GskGLProgram *name; -# include "gskglprograms.defs" -#undef GSK_GL_NO_UNIFORMS -#undef GSK_GL_ADD_UNIFORM -#undef GSK_GL_DEFINE_PROGRAM -#undef GSK_GL_DEFINE_PROGRAM_NO_CLIP - - gint64 current_frame_id; - - /* Used to reduce number of comparisons */ - guint stamps[UNIFORM_SHARED_LAST]; - - guint debug : 1; - guint in_frame : 1; -}; - -GskGLDriver * gsk_gl_driver_for_display (GdkDisplay *display, - gboolean debug_shaders, - GError **error); -GskGLCommandQueue * gsk_gl_driver_create_command_queue (GskGLDriver *self, - GdkGLContext *context); -GdkGLContext * gsk_gl_driver_get_context (GskGLDriver *self); -gboolean gsk_gl_driver_create_render_target (GskGLDriver *self, - int width, - int height, - int format, - GskGLRenderTarget **render_target); -guint gsk_gl_driver_release_render_target (GskGLDriver *self, - GskGLRenderTarget *render_target, - gboolean release_texture); -void gsk_gl_driver_begin_frame (GskGLDriver *self, - GskGLCommandQueue *command_queue); -void gsk_gl_driver_end_frame (GskGLDriver *self); -void gsk_gl_driver_after_frame (GskGLDriver *self); -GdkTexture * gsk_gl_driver_create_gdk_texture (GskGLDriver *self, - guint texture_id, - GdkMemoryFormat format); -void gsk_gl_driver_cache_texture (GskGLDriver *self, - const GskTextureKey *key, - guint texture_id); -guint gsk_gl_driver_load_texture (GskGLDriver *self, - GdkTexture *texture, - gboolean ensure_mipmap); -GskGLTexture * gsk_gl_driver_create_texture (GskGLDriver *self, - float width, - float height, - int format); -void gsk_gl_driver_release_texture (GskGLDriver *self, - GskGLTexture *texture); -void gsk_gl_driver_release_texture_by_id (GskGLDriver *self, - guint texture_id); -GskGLTexture * gsk_gl_driver_mark_texture_permanent (GskGLDriver *self, - guint texture_id); -void gsk_gl_driver_add_texture_slices (GskGLDriver *self, - GdkTexture *texture, - gboolean ensure_mipmap, - GskGLTextureSlice **out_slices, - guint *out_n_slices); -G_GNUC_BEGIN_IGNORE_DEPRECATIONS -GskGLProgram * gsk_gl_driver_lookup_shader (GskGLDriver *self, - GskGLShader *shader, - GError **error); -G_GNUC_END_IGNORE_DEPRECATIONS - -#if 0 -void gsk_gl_driver_save_texture_to_png (GskGLDriver *self, - int texture_id, - int width, - int height, - const char *filename); -void gsk_gl_driver_save_atlases_to_png (GskGLDriver *self, - const char *filename); -#endif - -static inline GskGLTexture * -gsk_gl_driver_get_texture_by_id (GskGLDriver *self, - guint texture_id) -{ - return g_hash_table_lookup (self->textures, GUINT_TO_POINTER (texture_id)); -} - -/** - * gsk_gl_driver_lookup_texture: - * @self: a `GskGLDriver` - * @key: the key for the texture - * @has_mipmap: (out): Return location for whether the texture has a mipmap - * - * Looks up a texture in the texture cache by @key. - * - * If the texture could not be found, then zero is returned. - * - * Returns: a positive integer if the texture was found; otherwise 0. - */ -static inline guint -gsk_gl_driver_lookup_texture (GskGLDriver *self, - const GskTextureKey *key, - gboolean *has_mipmap) -{ - gpointer id; - - if (g_hash_table_lookup_extended (self->key_to_texture_id, key, NULL, &id)) - { - GskGLTexture *texture = g_hash_table_lookup (self->textures, id); - - if (texture != NULL) - texture->last_used_in_frame = self->current_frame_id; - - if (has_mipmap) - *has_mipmap = texture ? texture->has_mipmap : FALSE; - - return GPOINTER_TO_UINT (id); - } - - return 0; -} - -static inline void -gsk_gl_driver_slice_texture (GskGLDriver *self, - GdkTexture *texture, - gboolean ensure_mipmap, - GskGLTextureSlice **out_slices, - guint *out_n_slices) -{ - GskGLTexture *t; - - t = gdk_texture_get_render_data (texture, self); - - if (t && t->slices && - (t->has_mipmap || !ensure_mipmap)) - { - *out_slices = t->slices; - *out_n_slices = t->n_slices; - return; - } - - gsk_gl_driver_add_texture_slices (self, texture, ensure_mipmap, out_slices, out_n_slices); -} - -G_END_DECLS - diff --git a/gsk/gl/gskglglyphlibrary.c b/gsk/gl/gskglglyphlibrary.c deleted file mode 100644 index c6d85f78093..00000000000 --- a/gsk/gl/gskglglyphlibrary.c +++ /dev/null @@ -1,439 +0,0 @@ -/* gskglglyphlibrary.c - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include -#include -#include - -#include "gskglcommandqueueprivate.h" -#include "gskgldriverprivate.h" -#include "gskglglyphlibraryprivate.h" - -#include "gskdebugprivate.h" - -#define MAX_GLYPH_SIZE 128 - -G_DEFINE_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK_TYPE_GL_TEXTURE_LIBRARY) - -GskGLGlyphLibrary * -gsk_gl_glyph_library_new (GskGLDriver *driver) -{ - g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL); - - return g_object_new (GSK_TYPE_GL_GLYPH_LIBRARY, - "driver", driver, - NULL); -} - -static guint -gsk_gl_glyph_key_hash (gconstpointer data) -{ - const GskGLGlyphKey *key = data; - - /* We do not store the hash within the key because GHashTable will already - * store the hash value for us and so this is called only a single time per - * cached item. This saves an extra 4 bytes per GskGLGlyphKey which means on - * 64-bit, we fit nicely within 2 pointers (the smallest allocation size - * for GSlice). - */ - - return GPOINTER_TO_UINT (key->font) ^ - key->glyph ^ - (key->xshift << 24) ^ - (key->yshift << 26) ^ - key->scale; -} - -static gboolean -gsk_gl_glyph_key_equal (gconstpointer v1, - gconstpointer v2) -{ - return memcmp (v1, v2, sizeof (GskGLGlyphKey)) == 0; -} - -static void -gsk_gl_glyph_key_free (gpointer data) -{ - GskGLGlyphKey *key = data; - - g_clear_object (&key->font); - g_free (key); -} - -static void -gsk_gl_glyph_value_free (gpointer data) -{ - g_free (data); -} - -static void -gsk_gl_glyph_library_clear_cache (GskGLTextureLibrary *library) -{ - GskGLGlyphLibrary *self = (GskGLGlyphLibrary *)library; - - g_assert (GSK_IS_GL_GLYPH_LIBRARY (self)); - - memset (self->front, 0, sizeof self->front); -} - -static void -gsk_gl_glyph_library_init_atlas (GskGLTextureLibrary *self, - GskGLTextureAtlas *atlas) -{ - gboolean packed G_GNUC_UNUSED; - int x, y; - guint gl_format; - guint gl_type; - guint8 pixel_data[4 * 3 * 3]; - - g_assert (GSK_IS_GL_GLYPH_LIBRARY (self)); - g_assert (atlas != NULL); - - /* Insert a single pixel at 0,0 for use in coloring */ - - gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (), - "Initializing Atlas"); - - packed = gsk_gl_texture_library_allocate (self, atlas, 3, 3, &x, &y); - g_assert (packed); - g_assert (x == 0 && y == 0); - - memset (pixel_data, 255, sizeof pixel_data); - - if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ())) - { - gl_format = GL_RGBA; - gl_type = GL_UNSIGNED_BYTE; - } - else - { - gl_format = GL_BGRA; - gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; - } - glBindTexture (GL_TEXTURE_2D, atlas->texture_id); - - glTexSubImage2D (GL_TEXTURE_2D, 0, - 0, 0, - 3, 3, - gl_format, gl_type, - pixel_data); - - gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ()); - - self->driver->command_queue->n_uploads++; -} - - -static void -gsk_gl_glyph_library_finalize (GObject *object) -{ - GskGLGlyphLibrary *self = (GskGLGlyphLibrary *)object; - - g_clear_pointer (&self->surface_data, g_free); - - G_OBJECT_CLASS (gsk_gl_glyph_library_parent_class)->finalize (object); -} - -static void -gsk_gl_glyph_library_class_init (GskGLGlyphLibraryClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GskGLTextureLibraryClass *library_class = GSK_GL_TEXTURE_LIBRARY_CLASS (klass); - - object_class->finalize = gsk_gl_glyph_library_finalize; - - library_class->clear_cache = gsk_gl_glyph_library_clear_cache; - library_class->init_atlas = gsk_gl_glyph_library_init_atlas; -} - -static void -gsk_gl_glyph_library_init (GskGLGlyphLibrary *self) -{ - GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self; - - tl->max_entry_size = MAX_GLYPH_SIZE; - gsk_gl_texture_library_set_funcs (tl, - gsk_gl_glyph_key_hash, - gsk_gl_glyph_key_equal, - gsk_gl_glyph_key_free, - gsk_gl_glyph_value_free); -} - -static cairo_surface_t * -gsk_gl_glyph_library_create_surface (GskGLGlyphLibrary *self, - int stride, - int width, - int height, - int uwidth, - int uheight) -{ - cairo_surface_t *surface; - gsize n_bytes; - - g_assert (GSK_IS_GL_GLYPH_LIBRARY (self)); - g_assert (width > 0); - g_assert (height > 0); - - n_bytes = stride * height; - - if G_LIKELY (n_bytes > self->surface_data_len) - { - self->surface_data = g_realloc (self->surface_data, n_bytes); - self->surface_data_len = n_bytes; - } - - memset (self->surface_data, 0, n_bytes); - surface = cairo_image_surface_create_for_data (self->surface_data, - CAIRO_FORMAT_ARGB32, - width, height, stride); - cairo_surface_set_device_scale (surface, width / (double)uwidth, height / (double)uheight); - - return surface; -} - -static void -render_glyph (cairo_surface_t *surface, - const GskGLGlyphKey *key, - const GskGLGlyphValue *value) -{ - cairo_t *cr; - PangoGlyphString glyph_string; - PangoGlyphInfo glyph_info; - - g_assert (surface != NULL); - - cr = cairo_create (surface); - cairo_set_source_rgba (cr, 1, 1, 1, 1); - - glyph_info.glyph = key->glyph; - glyph_info.geometry.width = value->ink_rect.width * 1024; - glyph_info.geometry.x_offset = (0.25 * key->xshift - value->ink_rect.x) * 1024; - glyph_info.geometry.y_offset = (0.25 * key->yshift - value->ink_rect.y) * 1024; - - glyph_string.num_glyphs = 1; - glyph_string.glyphs = &glyph_info; - - pango_cairo_show_glyph_string (cr, key->font, &glyph_string); - cairo_destroy (cr); - - cairo_surface_flush (surface); -} - -static void -gsk_gl_glyph_library_upload_glyph (GskGLGlyphLibrary *self, - const GskGLGlyphKey *key, - const GskGLGlyphValue *value, - int packed_x, - int packed_y, - int width, - int height, - int uwidth, - int uheight) -{ - GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self; - G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME; - cairo_surface_t *surface; - guchar *pixel_data; - guchar *free_data = NULL; - guint gl_format; - guint gl_type; - guint texture_id; - gsize stride; - - g_assert (GSK_IS_GL_GLYPH_LIBRARY (self)); - g_assert (key != NULL); - g_assert (value != NULL); - - stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width); - - gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (), - "Uploading glyph %d", - key->glyph); - - surface = gsk_gl_glyph_library_create_surface (self, stride, width, height, uwidth, uheight); - render_glyph (surface, key, value); - - texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (value); - - g_assert (texture_id > 0); - - if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ())) - { - pixel_data = free_data = g_malloc (width * height * 4); - gdk_memory_convert (pixel_data, width * 4, - GDK_MEMORY_R8G8B8A8_PREMULTIPLIED, - GDK_COLOR_STATE_SRGB, - cairo_image_surface_get_data (surface), - stride, - GDK_MEMORY_DEFAULT, - GDK_COLOR_STATE_SRGB, - width, height); - stride = width * 4; - gl_format = GL_RGBA; - gl_type = GL_UNSIGNED_BYTE; - } - else - { - pixel_data = cairo_image_surface_get_data (surface); - gl_format = GL_BGRA; - gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; - } - - glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / 4); - glBindTexture (GL_TEXTURE_2D, texture_id); - - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + 1, packed_y + 1, - width, height, - gl_format, gl_type, - pixel_data); - - /* Padding top */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + 1, packed_y, - width, 1, - gl_format, gl_type, - pixel_data); - /* Padding left */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x, packed_y + 1, - 1, height, - gl_format, gl_type, - pixel_data); - /* Padding top left */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x, packed_y, - 1, 1, - gl_format, gl_type, - pixel_data); - /* Padding right */ - glPixelStorei (GL_UNPACK_ROW_LENGTH, width); - glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1); - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + width + 1, packed_y + 1, - 1, height, - gl_format, gl_type, - pixel_data); - /* Padding top right */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + width + 1, packed_y, - 1, 1, - gl_format, gl_type, - pixel_data); - /* Padding bottom */ - glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei (GL_UNPACK_SKIP_ROWS, height - 1); - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + 1, packed_y + 1 + height, - width, 1, - gl_format, gl_type, - pixel_data); - /* Padding bottom left */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x, packed_y + 1 + height, - 1, 1, - gl_format, gl_type, - pixel_data); - /* Padding bottom right */ - glPixelStorei (GL_UNPACK_ROW_LENGTH, width); - glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1); - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + 1 + width, packed_y + 1 + height, - 1, 1, - gl_format, gl_type, - pixel_data); - /* Reset this */ - glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei (GL_UNPACK_SKIP_ROWS, 0); - - cairo_surface_destroy (surface); - g_free (free_data); - - gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ()); - - tl->driver->command_queue->n_uploads++; - - if (gdk_profiler_is_running ()) - { - char message[64]; - g_snprintf (message, sizeof message, "Size %dx%d", width, height); - gdk_profiler_add_mark (start_time, GDK_PROFILER_CURRENT_TIME-start_time, "Upload glyph", message); - } -} - -gboolean -gsk_gl_glyph_library_add (GskGLGlyphLibrary *self, - GskGLGlyphKey *key, - const GskGLGlyphValue **out_value) -{ - GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self; - PangoRectangle ink_rect; - GskGLGlyphValue *value; - int width; - int height; - guint packed_x; - guint packed_y; - - g_assert (GSK_IS_GL_GLYPH_LIBRARY (self)); - g_assert (key != NULL); - g_assert (out_value != NULL); - - pango_font_get_glyph_extents (key->font, key->glyph, &ink_rect, NULL); - pango_extents_to_pixels (&ink_rect, NULL); - - ink_rect.x -= 1; - ink_rect.width += 2; - ink_rect.y -= 1; - ink_rect.height += 2; - - width = (int) ceil (ink_rect.width * key->scale / 1024.0); - height = (int) ceil (ink_rect.height * key->scale / 1024.0); - - GSK_DEBUG (CACHE, "font %p glyph %u: %u x %u pixels", key->font, key->glyph, width, height); - - value = gsk_gl_texture_library_pack (tl, - key, - sizeof *value, - width, - height, - 1, - &packed_x, &packed_y); - - memcpy (&value->ink_rect, &ink_rect, sizeof ink_rect); - - if (key->scale > 0 && width > 0 && height > 0) - gsk_gl_glyph_library_upload_glyph (self, - key, - value, - packed_x, - packed_y, - width, - height, - ink_rect.width, - ink_rect.height); - - *out_value = value; - - return GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (value) != 0; -} diff --git a/gsk/gl/gskglglyphlibraryprivate.h b/gsk/gl/gskglglyphlibraryprivate.h deleted file mode 100644 index b3022b4630f..00000000000 --- a/gsk/gl/gskglglyphlibraryprivate.h +++ /dev/null @@ -1,103 +0,0 @@ -/* gskglglyphlibraryprivate.h - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#include "gskgltexturelibraryprivate.h" - -G_BEGIN_DECLS - -#define GSK_TYPE_GL_GLYPH_LIBRARY (gsk_gl_glyph_library_get_type()) - -typedef struct _GskGLGlyphKey -{ - PangoFont *font; - PangoGlyph glyph; - guint xshift : 2; - guint yshift : 2; - guint scale : 28; /* times 1024 */ -} GskGLGlyphKey; - -typedef struct _GskGLGlyphValue -{ - GskGLTextureAtlasEntry entry; - PangoRectangle ink_rect; -} GskGLGlyphValue; - -#if GLIB_SIZEOF_VOID_P == 8 -G_STATIC_ASSERT (sizeof (GskGLGlyphKey) == 16); -#elif GLIB_SIZEOF_VOID_P == 4 -G_STATIC_ASSERT (sizeof (GskGLGlyphKey) == 12); -#endif - -G_DECLARE_FINAL_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK, GL_GLYPH_LIBRARY, GskGLTextureLibrary) - -struct _GskGLGlyphLibrary -{ - GskGLTextureLibrary parent_instance; - guint8 *surface_data; - gsize surface_data_len; - struct { - GskGLGlyphKey key; - const GskGLGlyphValue *value; - } front[256]; -}; - -GskGLGlyphLibrary *gsk_gl_glyph_library_new (GskGLDriver *driver); -gboolean gsk_gl_glyph_library_add (GskGLGlyphLibrary *self, - GskGLGlyphKey *key, - const GskGLGlyphValue **out_value); - -static inline guint -gsk_gl_glyph_library_lookup_or_add (GskGLGlyphLibrary *self, - const GskGLGlyphKey *key, - const GskGLGlyphValue **out_value) -{ - GskGLTextureAtlasEntry *entry; - guint front_index = ((key->glyph << 2) | key->xshift) & 0xFF; - - if (memcmp (key, &self->front[front_index], sizeof *key) == 0) - { - *out_value = self->front[front_index].value; - } - else if (gsk_gl_texture_library_lookup ((GskGLTextureLibrary *)self, key, &entry)) - { - *out_value = (GskGLGlyphValue *)entry; - self->front[front_index].key = *key; - self->front[front_index].value = *out_value; - } - else - { - GskGLGlyphKey *k; - k = g_new (GskGLGlyphKey, 1); - memcpy (k, key, sizeof (GskGLGlyphKey)); - g_object_ref (k->font); - gsk_gl_glyph_library_add (self, k, out_value); - self->front[front_index].key = *key; - self->front[front_index].value = *out_value; - } - - return GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (*out_value); -} - -G_END_DECLS - diff --git a/gsk/gl/gskgliconlibrary.c b/gsk/gl/gskgliconlibrary.c deleted file mode 100644 index 63b25dea4b7..00000000000 --- a/gsk/gl/gskgliconlibrary.c +++ /dev/null @@ -1,212 +0,0 @@ -/* gskgliconlibrary.c - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include "gskglcommandqueueprivate.h" -#include "gskgldriverprivate.h" -#include "gskgliconlibraryprivate.h" - -struct _GskGLIconLibrary -{ - GskGLTextureLibrary parent_instance; -}; - -G_DEFINE_TYPE (GskGLIconLibrary, gsk_gl_icon_library, GSK_TYPE_GL_TEXTURE_LIBRARY) - -GskGLIconLibrary * -gsk_gl_icon_library_new (GskGLDriver *driver) -{ - g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL); - - return g_object_new (GSK_TYPE_GL_ICON_LIBRARY, - "driver", driver, - NULL); -} - -static void -gsk_gl_icon_data_free (gpointer data) -{ - GskGLIconData *icon_data = data; - - g_clear_object (&icon_data->source_texture); - g_free (icon_data); -} - -static void -gsk_gl_icon_library_class_init (GskGLIconLibraryClass *klass) -{ -} - -static void -gsk_gl_icon_library_init (GskGLIconLibrary *self) -{ - GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self; - - tl->max_entry_size = 128; - gsk_gl_texture_library_set_funcs (tl, - NULL, NULL, NULL, - gsk_gl_icon_data_free); -} - -void -gsk_gl_icon_library_add (GskGLIconLibrary *self, - GdkTexture *key, - const GskGLIconData **out_value) -{ - GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self; - G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME; - GskGLIconData *icon_data; - GdkTextureDownloader downloader; - guint8 *pixel_data; - guint gl_format; - guint gl_type; - guint packed_x; - guint packed_y; - int width; - int height; - guint texture_id; - - g_assert (GSK_IS_GL_ICON_LIBRARY (self)); - g_assert (GDK_IS_TEXTURE (key)); - g_assert (out_value != NULL); - - width = key->width; - height = key->height; - - icon_data = gsk_gl_texture_library_pack (tl, - key, - sizeof (GskGLIconData), - width, height, 1, - &packed_x, &packed_y); - icon_data->source_texture = g_object_ref (key); - - /* actually upload the texture */ - gdk_texture_downloader_init (&downloader, key); - gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (), - "Uploading texture"); - - if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ())) - { - gdk_texture_downloader_set_format (&downloader, GDK_MEMORY_R8G8B8A8_PREMULTIPLIED); - gl_format = GL_RGBA; - gl_type = GL_UNSIGNED_BYTE; - } - else - { - gdk_texture_downloader_set_format (&downloader, GDK_MEMORY_DEFAULT); - gl_format = GL_BGRA; - gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; - } - pixel_data = g_malloc (width * height * 4); - gdk_texture_downloader_download_into (&downloader, pixel_data, width * 4); - gdk_texture_downloader_finish (&downloader); - - texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (icon_data); - - glBindTexture (GL_TEXTURE_2D, texture_id); - - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + 1, packed_y + 1, - width, height, - gl_format, gl_type, - pixel_data); - /* Padding top */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + 1, packed_y, - width, 1, - gl_format, gl_type, - pixel_data); - /* Padding left */ - glPixelStorei (GL_UNPACK_ROW_LENGTH, width); - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x, packed_y + 1, - 1, height, - gl_format, gl_type, - pixel_data); - /* Padding top left */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x, packed_y, - 1, 1, - gl_format, gl_type, - pixel_data); - - /* Padding right */ - glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1); - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + width + 1, packed_y + 1, - 1, height, - gl_format, gl_type, - pixel_data); - /* Padding top right */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + width + 1, packed_y, - 1, 1, - gl_format, gl_type, - pixel_data); - /* Padding bottom */ - glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei (GL_UNPACK_SKIP_ROWS, height - 1); - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + 1, packed_y + 1 + height, - width, 1, - gl_format, gl_type, - pixel_data); - /* Padding bottom left */ - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x, packed_y + 1 + height, - 1, 1, - gl_format, gl_type, - pixel_data); - /* Padding bottom right */ - glPixelStorei (GL_UNPACK_ROW_LENGTH, width); - glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1); - glTexSubImage2D (GL_TEXTURE_2D, 0, - packed_x + 1 + width, packed_y + 1 + height, - 1, 1, - gl_format, gl_type, - pixel_data); - /* Reset this */ - glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); - glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); - glPixelStorei (GL_UNPACK_SKIP_ROWS, 0); - - gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ()); - - *out_value = icon_data; - - g_free (pixel_data); - - tl->driver->command_queue->n_uploads++; - - if (gdk_profiler_is_running ()) - { - char message[64]; - g_snprintf (message, sizeof message, "Size %dx%d", width, height); - gdk_profiler_add_mark (start_time, GDK_PROFILER_CURRENT_TIME-start_time, "Upload icon", message); - } -} diff --git a/gsk/gl/gskgliconlibraryprivate.h b/gsk/gl/gskgliconlibraryprivate.h deleted file mode 100644 index dc99b546ce9..00000000000 --- a/gsk/gl/gskgliconlibraryprivate.h +++ /dev/null @@ -1,58 +0,0 @@ -/* gskgliconlibraryprivate.h - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#include "gskgltexturelibraryprivate.h" - -G_BEGIN_DECLS - -#define GSK_TYPE_GL_ICON_LIBRARY (gsk_gl_icon_library_get_type()) - -typedef struct _GskGLIconData -{ - GskGLTextureAtlasEntry entry; - GdkTexture *source_texture; -} GskGLIconData; - -G_DECLARE_FINAL_TYPE (GskGLIconLibrary, gsk_gl_icon_library, GSK, GL_ICON_LIBRARY, GskGLTextureLibrary) - -GskGLIconLibrary *gsk_gl_icon_library_new (GskGLDriver *driver); -void gsk_gl_icon_library_add (GskGLIconLibrary *self, - GdkTexture *key, - const GskGLIconData **out_value); - -static inline void -gsk_gl_icon_library_lookup_or_add (GskGLIconLibrary *self, - GdkTexture *key, - const GskGLIconData **out_value) -{ - GskGLTextureAtlasEntry *entry; - - if G_LIKELY (gsk_gl_texture_library_lookup ((GskGLTextureLibrary *)self, key, &entry)) - *out_value = (GskGLIconData *)entry; - else - gsk_gl_icon_library_add (self, key, out_value); -} - -G_END_DECLS - diff --git a/gsk/gl/gskglprofiler.c b/gsk/gl/gskglprofiler.c deleted file mode 100644 index 6aff5ab0a50..00000000000 --- a/gsk/gl/gskglprofiler.c +++ /dev/null @@ -1,179 +0,0 @@ -#include "config.h" - -#include "gskglprofilerprivate.h" - -#include - -#define N_QUERIES 4 - -struct _GskGLProfiler -{ - GObject parent_instance; - - GdkGLContext *gl_context; - - /* Creating GL queries is kind of expensive, so we pay the - * price upfront and create a circular buffer of queries - */ - GLuint gl_queries[N_QUERIES]; - GLuint active_query; - - unsigned int has_queries : 1; - unsigned int has_timer : 1; - unsigned int first_frame : 1; -}; - -enum { - PROP_GL_CONTEXT = 1, - - N_PROPERTIES -}; - -static GParamSpec *gsk_gl_profiler_properties[N_PROPERTIES]; - -G_DEFINE_TYPE (GskGLProfiler, gsk_gl_profiler, G_TYPE_OBJECT) - -static void -gsk_gl_profiler_finalize (GObject *gobject) -{ - GskGLProfiler *self = GSK_GL_PROFILER (gobject); - - if (self->has_queries) - glDeleteQueries (N_QUERIES, self->gl_queries); - - g_clear_object (&self->gl_context); - - G_OBJECT_CLASS (gsk_gl_profiler_parent_class)->finalize (gobject); -} - -static void -gsk_gl_profiler_set_property (GObject *gobject, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GskGLProfiler *self = GSK_GL_PROFILER (gobject); - - switch (prop_id) - { - case PROP_GL_CONTEXT: - self->gl_context = g_value_dup_object (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - } -} - -static void -gsk_gl_profiler_get_property (GObject *gobject, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GskGLProfiler *self = GSK_GL_PROFILER (gobject); - - switch (prop_id) - { - case PROP_GL_CONTEXT: - g_value_set_object (value, self->gl_context); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); - } -} - -static void -gsk_gl_profiler_class_init (GskGLProfilerClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->set_property = gsk_gl_profiler_set_property; - gobject_class->get_property = gsk_gl_profiler_get_property; - gobject_class->finalize = gsk_gl_profiler_finalize; - - gsk_gl_profiler_properties[PROP_GL_CONTEXT] = - g_param_spec_object ("gl-context", NULL, NULL, - GDK_TYPE_GL_CONTEXT, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (gobject_class, N_PROPERTIES, gsk_gl_profiler_properties); -} - -static void -gsk_gl_profiler_init (GskGLProfiler *self) -{ - self->has_queries = epoxy_is_desktop_gl(); - self->has_timer = epoxy_is_desktop_gl() && (epoxy_gl_version () >= 33 || epoxy_has_gl_extension ("GL_ARB_timer_query")); - - if (!self->has_queries) - return; - - glGenQueries (N_QUERIES, self->gl_queries); - self->first_frame = TRUE; -} - -GskGLProfiler * -gsk_gl_profiler_new (GdkGLContext *context) -{ - g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL); - - return g_object_new (GSK_TYPE_GL_PROFILER, "gl-context", context, NULL); -} - -void -gsk_gl_profiler_begin_gpu_region (GskGLProfiler *profiler) -{ - GLuint query_id; - - g_return_if_fail (GSK_IS_GL_PROFILER (profiler)); - - if (!profiler->has_timer || !profiler->has_queries) - return; - - query_id = profiler->gl_queries[profiler->active_query]; - glBeginQuery (GL_TIME_ELAPSED, query_id); -} - -guint64 -gsk_gl_profiler_end_gpu_region (GskGLProfiler *profiler) -{ - GLuint last_query_id; - GLint res; - GLuint64 elapsed; - - g_return_val_if_fail (GSK_IS_GL_PROFILER (profiler), 0); - - if (!profiler->has_timer || !profiler->has_queries) - return 0; - - glEndQuery (GL_TIME_ELAPSED); - - if (profiler->active_query == 0) - last_query_id = N_QUERIES - 1; - else - last_query_id = profiler->active_query - 1; - - /* Advance iterator */ - profiler->active_query += 1; - if (profiler->active_query == N_QUERIES) - profiler->active_query = 0; - - /* If this is the first frame we already have a result */ - if (profiler->first_frame) - { - profiler->first_frame = FALSE; - return 0; - } - - glGetQueryObjectiv (profiler->gl_queries[last_query_id], GL_QUERY_RESULT_AVAILABLE, &res); - if (res == 1) - glGetQueryObjectui64v (profiler->gl_queries[last_query_id], GL_QUERY_RESULT, &elapsed); - else - elapsed = 0; - - return elapsed / 1000; /* Convert to usec to match other profiler APIs */ -} diff --git a/gsk/gl/gskglprofilerprivate.h b/gsk/gl/gskglprofilerprivate.h deleted file mode 100644 index 3aa0650c117..00000000000 --- a/gsk/gl/gskglprofilerprivate.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -G_BEGIN_DECLS - -#define GSK_TYPE_GL_PROFILER (gsk_gl_profiler_get_type ()) -G_DECLARE_FINAL_TYPE (GskGLProfiler, gsk_gl_profiler, GSK, GL_PROFILER, GObject) - -GskGLProfiler * gsk_gl_profiler_new (GdkGLContext *context); - -void gsk_gl_profiler_begin_gpu_region (GskGLProfiler *profiler); -guint64 gsk_gl_profiler_end_gpu_region (GskGLProfiler *profiler); - -G_END_DECLS - diff --git a/gsk/gl/gskglprogram.c b/gsk/gl/gskglprogram.c deleted file mode 100644 index 72882845932..00000000000 --- a/gsk/gl/gskglprogram.c +++ /dev/null @@ -1,173 +0,0 @@ -/* gskglprogram.c - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "gskglcommandqueueprivate.h" -#include "gskglprogramprivate.h" -#include "gskgluniformstateprivate.h" - -G_DEFINE_TYPE (GskGLProgram, gsk_gl_program, G_TYPE_OBJECT) - -GskGLProgram * -gsk_gl_program_new (GskGLDriver *driver, - const char *name, - int program_id) -{ - GskGLProgram *self; - - g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL); - g_return_val_if_fail (program_id >= -1, NULL); - - self = g_object_new (GSK_TYPE_GL_PROGRAM, NULL); - self->id = program_id; - self->name = g_strdup (name); - self->driver = g_object_ref (driver); - self->n_mappings = 0; - - return self; -} - -static void -gsk_gl_program_finalize (GObject *object) -{ - GskGLProgram *self = (GskGLProgram *)object; - - if (self->id >= 0) - g_warning ("Leaking GLSL program %d (%s)", - self->id, - self->name ? self->name : ""); - - g_clear_pointer (&self->name, g_free); - g_clear_object (&self->driver); - - G_OBJECT_CLASS (gsk_gl_program_parent_class)->finalize (object); -} - -static void -gsk_gl_program_class_init (GskGLProgramClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = gsk_gl_program_finalize; -} - -static void -gsk_gl_program_init (GskGLProgram *self) -{ - self->id = -1; - - for (guint i = 0; i < G_N_ELEMENTS (self->mappings); i++) - self->mappings[i].location = -1; -} - -/** - * gsk_gl_program_add_uniform: - * @self: a `GskGLProgram` - * @name: the name of the uniform such as "u_source" - * @key: the identifier to use for the uniform - * - * This method will create a mapping between @key and the location - * of the uniform on the GPU. This simplifies calling code to not - * need to know where the uniform location is and only register it - * when creating the program. - * - * You might use this with an enum of all your uniforms for the - * program and then register each of them like: - * - * ``` - * gsk_gl_program_add_uniform (program, "u_source", UNIFORM_SOURCE); - * ``` - * - * That allows you to set values for the program with something - * like the following: - * - * ``` - * gsk_gl_program_set_uniform1i (program, UNIFORM_SOURCE, 1); - * ``` - * - * Returns: %TRUE if the uniform was found; otherwise %FALSE - */ -gboolean -gsk_gl_program_add_uniform (GskGLProgram *self, - const char *name, - guint key) -{ - GLint location; - - g_return_val_if_fail (GSK_IS_GL_PROGRAM (self), FALSE); - g_return_val_if_fail (name != NULL, FALSE); - g_return_val_if_fail (key < G_N_ELEMENTS (self->mappings), FALSE); - - location = glGetUniformLocation (self->id, name); - - /* Register the information even if unused */ - self->mappings[key].name = g_intern_string (name); - self->mappings[key].location = location; - if (key >= self->n_mappings) - self->n_mappings = key + 1; - -#if 0 - g_print ("program [%d] %s uniform %s [%u of %u] at location %d.\n", - self->id, self->name, name, key, self->n_mappings, location); -#endif - - return location > -1; -} - -/** - * gsk_gl_program_delete: - * @self: a `GskGLProgram` - * - * Deletes the GLSL program. - */ -void -gsk_gl_program_delete (GskGLProgram *self) -{ - g_return_if_fail (GSK_IS_GL_PROGRAM (self)); - g_return_if_fail (self->driver->command_queue != NULL); - - gsk_gl_command_queue_delete_program (self->driver->command_queue, self->id); - self->id = -1; -} - -/** - * gsk_gl_program_uniforms_added: - * @self: a `GskGLProgram` - * @has_attachments: if any uniform is for a bind/texture attachment - * - * This function should be called after all of the uniforms have - * been added with gsk_gl_program_add_uniform(). - * - * This function will setup the uniform state so that the program - * has fast access to the data buffers without as many lookups at - * runtime for comparison data. - */ -void -gsk_gl_program_uniforms_added (GskGLProgram *self, - gboolean has_attachments) -{ - g_return_if_fail (GSK_IS_GL_PROGRAM (self)); - g_return_if_fail (self->uniforms == NULL); - - self->uniforms = self->driver->command_queue->uniforms; - self->program_info = gsk_gl_uniform_state_get_program (self->uniforms, self->id, self->mappings, self->n_mappings); - self->program_info->has_attachments = has_attachments; -} diff --git a/gsk/gl/gskglprogramprivate.h b/gsk/gl/gskglprogramprivate.h deleted file mode 100644 index 6bfbe48f024..00000000000 --- a/gsk/gl/gskglprogramprivate.h +++ /dev/null @@ -1,318 +0,0 @@ -/* gskglprogramprivate.h - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "gskgltypesprivate.h" - -#include "gskglattachmentstateprivate.h" -#include "gskglcommandqueueprivate.h" -#include "gskgldriverprivate.h" - -G_BEGIN_DECLS - -#define GSK_TYPE_GL_PROGRAM (gsk_gl_program_get_type()) -#define GSK_GL_PROGRAM_MAX_CUSTOM_TEXTURES 4 -#define GSK_GL_PROGRAM_MAX_CUSTOM_ARGS 8 - -G_DECLARE_FINAL_TYPE (GskGLProgram, gsk_gl_program, GSK, GL_PROGRAM, GObject) - -struct _GskGLProgram -{ - GObject parent_instance; - - int id; - char *name; - GskGLDriver *driver; - - /* Cached pointer to avoid lots of pointer chasing/lookups */ - GskGLUniformState *uniforms; - GskGLUniformProgram *program_info; - - /* Static array for key->location transforms */ - GskGLUniformMapping mappings[32]; - guint n_mappings; -}; - -GskGLProgram * gsk_gl_program_new (GskGLDriver *driver, - const char *name, - int program_id); -gboolean gsk_gl_program_add_uniform (GskGLProgram *self, - const char *name, - guint key); -void gsk_gl_program_uniforms_added (GskGLProgram *self, - gboolean has_attachments); -void gsk_gl_program_delete (GskGLProgram *self); - -static inline void -gsk_gl_program_set_uniform1fv (GskGLProgram *self, - guint key, - guint stamp, - guint count, - const float *values) -{ - gsk_gl_uniform_state_set1fv (self->uniforms, self->program_info, - key, - stamp, - count, - values); -} - -static inline void -gsk_gl_program_set_uniform2fv (GskGLProgram *self, - guint key, - guint stamp, - guint count, - const float *values) -{ - gsk_gl_uniform_state_set2fv (self->uniforms, self->program_info, - key, - stamp, - count, - values); -} - -static inline void -gsk_gl_program_set_uniform4fv (GskGLProgram *self, - guint key, - guint stamp, - guint count, - const float *values) -{ - gsk_gl_uniform_state_set4fv (self->uniforms, self->program_info, - key, - stamp, - count, - values); -} - -static inline void -gsk_gl_program_set_uniform_rounded_rect (GskGLProgram *self, - guint key, - guint stamp, - const GskRoundedRect *rounded_rect) -{ - gsk_gl_uniform_state_set_rounded_rect (self->uniforms, self->program_info, - key, - stamp, - rounded_rect); -} - -static inline void -gsk_gl_program_set_uniform1i (GskGLProgram *self, - guint key, - guint stamp, - int value0) -{ - gsk_gl_uniform_state_set1i (self->uniforms, - self->program_info, - key, - stamp, - value0); -} - -static inline void -gsk_gl_program_set_uniform2i (GskGLProgram *self, - guint key, - guint stamp, - int value0, - int value1) -{ - gsk_gl_uniform_state_set2i (self->uniforms, - self->program_info, - key, - stamp, - value0, value1); -} - -static inline void -gsk_gl_program_set_uniform3i (GskGLProgram *self, - guint key, - guint stamp, - int value0, - int value1, - int value2) -{ - gsk_gl_uniform_state_set3i (self->uniforms, - self->program_info, - key, - stamp, - value0, value1, value2); -} - -static inline void -gsk_gl_program_set_uniform4i (GskGLProgram *self, - guint key, - guint stamp, - int value0, - int value1, - int value2, - int value3) -{ - gsk_gl_uniform_state_set4i (self->uniforms, - self->program_info, - key, - stamp, - value0, value1, value2, value3); -} - -static inline void -gsk_gl_program_set_uniform1f (GskGLProgram *self, - guint key, - guint stamp, - float value0) -{ - gsk_gl_uniform_state_set1f (self->uniforms, - self->program_info, - key, - stamp, - value0); -} - -static inline void -gsk_gl_program_set_uniform2f (GskGLProgram *self, - guint key, - guint stamp, - float value0, - float value1) -{ - gsk_gl_uniform_state_set2f (self->uniforms, - self->program_info, - key, - stamp, - value0, value1); -} - -static inline void -gsk_gl_program_set_uniform3f (GskGLProgram *self, - guint key, - guint stamp, - float value0, - float value1, - float value2) -{ - gsk_gl_uniform_state_set3f (self->uniforms, - self->program_info, - key, - stamp, - value0, value1, value2); -} - -static inline void -gsk_gl_program_set_uniform4f (GskGLProgram *self, - guint key, - guint stamp, - float value0, - float value1, - float value2, - float value3) -{ - gsk_gl_uniform_state_set4f (self->uniforms, - self->program_info, - key, - stamp, - value0, value1, value2, value3); -} - -static inline void -gsk_gl_program_set_uniform_color (GskGLProgram *self, - guint key, - guint stamp, - const GdkRGBA *color) -{ - gsk_gl_uniform_state_set_color (self->uniforms, - self->program_info, - key, - stamp, - color); -} - -static inline void -gsk_gl_program_set_uniform_texture_with_filter (GskGLProgram *self, - guint key, - guint stamp, - GLenum texture_target, - GLenum texture_slot, - guint texture_id, - GLint min_filter, - GLint mag_filter) -{ - gsk_gl_attachment_state_bind_texture (self->driver->command_queue->attachments, - texture_target, - texture_slot, - texture_id, - min_filter, - mag_filter); - gsk_gl_uniform_state_set_texture (self->uniforms, - self->program_info, - key, - stamp, - texture_slot); -} - -static inline void -gsk_gl_program_set_uniform_texture (GskGLProgram *self, - guint key, - guint stamp, - GLenum texture_target, - GLenum texture_slot, - guint texture_id) -{ - gsk_gl_program_set_uniform_texture_with_filter (self, - key, - stamp, - texture_target, - texture_slot, - texture_id, - GL_LINEAR, - GL_LINEAR); -} - -static inline void -gsk_gl_program_set_uniform_texture_with_sync (GskGLProgram *self, - guint key, - guint stamp, - GLenum texture_target, - GLenum texture_slot, - guint texture_id, - GLint min_filter, - GLint max_filter, - gpointer sync) -{ - gsk_gl_program_set_uniform_texture_with_filter (self, key, stamp, texture_target, texture_slot, texture_id, - min_filter, max_filter); - gsk_gl_syncs_add_sync (&self->driver->command_queue->syncs, texture_id, sync); -} - -static inline void -gsk_gl_program_set_uniform_matrix (GskGLProgram *self, - guint key, - guint stamp, - const graphene_matrix_t *matrix) -{ - gsk_gl_uniform_state_set_matrix (self->uniforms, - self->program_info, - key, - stamp, - matrix); -} - -G_END_DECLS - diff --git a/gsk/gl/gskglprograms.defs b/gsk/gl/gskglprograms.defs deleted file mode 100644 index 0eabd8a9bd2..00000000000 --- a/gsk/gl/gskglprograms.defs +++ /dev/null @@ -1,104 +0,0 @@ -GSK_GL_DEFINE_PROGRAM (blend, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("blend.glsl")), - GSK_GL_ADD_UNIFORM (1, BLEND_SOURCE2, u_source2) - GSK_GL_ADD_UNIFORM (2, BLEND_MODE, u_mode)) - -GSK_GL_DEFINE_PROGRAM (blit, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("blit.glsl")), - GSK_GL_NO_UNIFORMS) - -GSK_GL_DEFINE_PROGRAM (blur, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("blur.glsl")), - GSK_GL_ADD_UNIFORM (1, BLUR_RADIUS, u_blur_radius) - GSK_GL_ADD_UNIFORM (2, BLUR_SIZE, u_blur_size) - GSK_GL_ADD_UNIFORM (3, BLUR_DIR, u_blur_dir)) - -GSK_GL_DEFINE_PROGRAM (border, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("border.glsl")), - GSK_GL_ADD_UNIFORM (1, BORDER_WIDTHS, u_widths) - GSK_GL_ADD_UNIFORM (2, BORDER_OUTLINE_RECT, u_outline_rect)) - -GSK_GL_DEFINE_PROGRAM (color, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("color.glsl")), - GSK_GL_NO_UNIFORMS) - -GSK_GL_DEFINE_PROGRAM (coloring, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("coloring.glsl")), - GSK_GL_NO_UNIFORMS) - -GSK_GL_DEFINE_PROGRAM (color_matrix, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("color_matrix.glsl")), - GSK_GL_ADD_UNIFORM (1, COLOR_MATRIX_COLOR_MATRIX, u_color_matrix) - GSK_GL_ADD_UNIFORM (2, COLOR_MATRIX_COLOR_OFFSET, u_color_offset)) - -GSK_GL_DEFINE_PROGRAM (conic_gradient, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("conic_gradient.glsl")), - GSK_GL_ADD_UNIFORM (1, CONIC_GRADIENT_COLOR_STOPS, u_color_stops) - GSK_GL_ADD_UNIFORM (2, CONIC_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops) - GSK_GL_ADD_UNIFORM (3, CONIC_GRADIENT_GEOMETRY, u_geometry)) - -GSK_GL_DEFINE_PROGRAM (cross_fade, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("cross_fade.glsl")), - GSK_GL_ADD_UNIFORM (1, CROSS_FADE_PROGRESS, u_progress) - GSK_GL_ADD_UNIFORM (2, CROSS_FADE_SOURCE2, u_source2)) - -GSK_GL_DEFINE_PROGRAM (filled_border, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("filled_border.glsl")), - GSK_GL_ADD_UNIFORM (1, FILLED_BORDER_WIDTHS, u_widths) - GSK_GL_ADD_UNIFORM (2, FILLED_BORDER_OUTLINE_RECT, u_outline_rect)) - -GSK_GL_DEFINE_PROGRAM (inset_shadow, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("inset_shadow.glsl")), - GSK_GL_ADD_UNIFORM (1, INSET_SHADOW_SPREAD, u_spread) - GSK_GL_ADD_UNIFORM (2, INSET_SHADOW_OFFSET, u_offset) - GSK_GL_ADD_UNIFORM (3, INSET_SHADOW_OUTLINE_RECT, u_outline_rect)) - -GSK_GL_DEFINE_PROGRAM (linear_gradient, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("linear_gradient.glsl")), - GSK_GL_ADD_UNIFORM (1, LINEAR_GRADIENT_COLOR_STOPS, u_color_stops) - GSK_GL_ADD_UNIFORM (2, LINEAR_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops) - GSK_GL_ADD_UNIFORM (3, LINEAR_GRADIENT_POINTS, u_points) - GSK_GL_ADD_UNIFORM (4, LINEAR_GRADIENT_REPEAT, u_repeat)) - -GSK_GL_DEFINE_PROGRAM (mask, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("mask.glsl")), - GSK_GL_ADD_UNIFORM (1, MASK_SOURCE, u_mask) - GSK_GL_ADD_UNIFORM (2, MASK_MODE, u_mode)) - -GSK_GL_DEFINE_PROGRAM (outset_shadow, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("outset_shadow.glsl")), - GSK_GL_ADD_UNIFORM (1, OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect)) - -GSK_GL_DEFINE_PROGRAM (radial_gradient, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("radial_gradient.glsl")), - GSK_GL_ADD_UNIFORM (1, RADIAL_GRADIENT_COLOR_STOPS, u_color_stops) - GSK_GL_ADD_UNIFORM (2, RADIAL_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops) - GSK_GL_ADD_UNIFORM (3, RADIAL_GRADIENT_REPEAT, u_repeat) - GSK_GL_ADD_UNIFORM (4, RADIAL_GRADIENT_RANGE, u_range) - GSK_GL_ADD_UNIFORM (5, RADIAL_GRADIENT_GEOMETRY, u_geometry)) - -GSK_GL_DEFINE_PROGRAM (repeat, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("repeat.glsl")), - GSK_GL_ADD_UNIFORM (1, REPEAT_CHILD_BOUNDS, u_child_bounds) - GSK_GL_ADD_UNIFORM (2, REPEAT_TEXTURE_RECT, u_texture_rect)) - -GSK_GL_DEFINE_PROGRAM (unblurred_outset_shadow, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("unblurred_outset_shadow.glsl")), - GSK_GL_ADD_UNIFORM (1, UNBLURRED_OUTSET_SHADOW_SPREAD, u_spread) - GSK_GL_ADD_UNIFORM (2, UNBLURRED_OUTSET_SHADOW_OFFSET, u_offset) - GSK_GL_ADD_UNIFORM (3, UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect)) - -/* Texture conversion shaders. - * - * Note: If you add new formats here, they need to be added - * to the list of supported formats in gdk/gdkdmabuftexture.c. - */ - -GSK_GL_DEFINE_PROGRAM_NO_CLIP (external, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("external.glsl")), - GSK_GL_ADD_UNIFORM (1, EXTERNAL_SOURCE, u_external_source) - GSK_GL_ADD_UNIFORM (2, PREMULTIPLY, u_premultiply)) - -GSK_GL_DEFINE_PROGRAM_NO_CLIP (premultiply, - GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("premultiply.glsl")), - GSK_GL_NO_UNIFORMS) diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c index c326286f0a8..3ae26ae151a 100644 --- a/gsk/gl/gskglrenderer.c +++ b/gsk/gl/gskglrenderer.c @@ -20,151 +20,14 @@ #include "config.h" -#include "gskglrendererprivate.h" +#include "gskglrenderer.h" -#include "gskglcommandqueueprivate.h" -#include "gskgldriverprivate.h" -#include "gskglprogramprivate.h" -#include "gskglrenderjobprivate.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * GskGLRenderer: - * - * A GL based renderer. - * - * See [class@Gsk.Renderer]. - * - * Since: 4.2 - */ -struct _GskGLRendererClass -{ - GskRendererClass parent_class; -}; - -struct _GskGLRenderer -{ - GskRenderer parent_instance; - - /* This context is used to swap buffers when we are rendering directly - * to a GDK surface. It is also used to locate the shared driver for - * the display that we use to drive the command queue. - */ - GdkGLContext *context; - - /* Our command queue is private to this renderer and talks to the GL - * context for our target surface. This ensure that framebuffer 0 matches - * the surface we care about. Since the context is shared with other - * contexts from other renderers on the display, texture atlases, - * programs, and other objects are available to them all. - */ - GskGLCommandQueue *command_queue; - - /* The driver manages our program state and command queues. It also - * deals with caching textures, shaders, shadows, glyph, and icon - * caches through various helpers. - */ - GskGLDriver *driver; -}; - -static gboolean -gsk_gl_renderer_dmabuf_downloader_supports (GdkDmabufDownloader *downloader, - GdkDmabufTexture *texture, - GError **error) -{ - GdkDisplay *display = gdk_dmabuf_texture_get_display (texture); - const GdkDmabuf *dmabuf = gdk_dmabuf_texture_get_dmabuf (texture); - - if (!gdk_dmabuf_formats_contains (display->egl_dmabuf_formats, dmabuf->fourcc, dmabuf->modifier)) - { - g_set_error (error, - GDK_DMABUF_ERROR, GDK_DMABUF_ERROR_UNSUPPORTED_FORMAT, - "Unsupported dmabuf format: %.4s:%#" G_GINT64_MODIFIER "x", - (char *) &dmabuf->fourcc, dmabuf->modifier); - return FALSE; - } - - return TRUE; -} - -static gboolean -gsk_gl_renderer_dmabuf_downloader_download (GdkDmabufDownloader *downloader_, - GdkDmabufTexture *texture, - GdkMemoryFormat format, - GdkColorState *color_state, - guchar *data, - gsize stride) -{ - GskRenderer *renderer = GSK_RENDERER (downloader_); - GdkGLContext *previous; - GdkTexture *native; - GdkTextureDownloader *downloader; - int width, height; - GskRenderNode *node; - - if (!gsk_gl_renderer_dmabuf_downloader_supports (downloader_, texture, NULL)) - return FALSE; - - previous = gdk_gl_context_get_current (); - if (previous) - g_object_ref (previous); - - width = gdk_texture_get_width (GDK_TEXTURE (texture)); - height = gdk_texture_get_height (GDK_TEXTURE (texture)); - - node = gsk_texture_node_new (GDK_TEXTURE (texture), &GRAPHENE_RECT_INIT (0, 0, width, height)); - native = gsk_renderer_render_texture (renderer, node, &GRAPHENE_RECT_INIT (0, 0, width, height)); - gsk_render_node_unref (node); - - downloader = gdk_texture_downloader_new (native); - gdk_texture_downloader_set_format (downloader, format); - gdk_texture_downloader_set_color_state (downloader, color_state); - gdk_texture_downloader_download_into (downloader, data, stride); - gdk_texture_downloader_free (downloader); - - g_object_unref (native); - - if (previous) - { - gdk_gl_context_make_current (previous); - g_object_unref (previous); - } - else - gdk_gl_context_clear_current (); - - return TRUE; -} - -static void -gsk_gl_renderer_dmabuf_downloader_close (GdkDmabufDownloader *downloader) -{ - gsk_renderer_unrealize (GSK_RENDERER (downloader)); -} - -static void -gsk_gl_renderer_dmabuf_downloader_init (GdkDmabufDownloaderInterface *iface) +GType +gsk_gl_renderer_get_type (void) { - iface->close = gsk_gl_renderer_dmabuf_downloader_close; - iface->download = gsk_gl_renderer_dmabuf_downloader_download; + return gsk_ngl_renderer_get_type (); } -G_DEFINE_TYPE_EXTENDED (GskGLRenderer, gsk_gl_renderer, GSK_TYPE_RENDERER, 0, - G_IMPLEMENT_INTERFACE (GDK_TYPE_DMABUF_DOWNLOADER, - gsk_gl_renderer_dmabuf_downloader_init)) - /** * gsk_gl_renderer_new: * @@ -180,310 +43,4 @@ gsk_gl_renderer_new (void) return g_object_new (GSK_TYPE_GL_RENDERER, NULL); } -static gboolean -gsk_gl_renderer_realize (GskRenderer *renderer, - GdkDisplay *display, - GdkSurface *surface, - GError **error) -{ - G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME; - GskGLRenderer *self = (GskGLRenderer *)renderer; - GdkGLContext *context = NULL; - GskGLDriver *driver = NULL; - gboolean ret = FALSE; - gboolean debug_shaders = FALSE; - - if (self->context != NULL) - return TRUE; - - g_assert (self->driver == NULL); - g_assert (self->context == NULL); - g_assert (self->command_queue == NULL); - - if (!gdk_display_prepare_gl (display, error)) - goto failure; - - context = gdk_gl_context_new (display, surface, surface != NULL); - - if (!gdk_gl_context_realize (context, error)) - goto failure; - - if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), SHADERS)) - debug_shaders = TRUE; - - if (!(driver = gsk_gl_driver_for_display (display, debug_shaders, error))) - goto failure; - - self->command_queue = gsk_gl_driver_create_command_queue (driver, context); - self->context = g_steal_pointer (&context); - self->driver = g_steal_pointer (&driver); - - gsk_gl_command_queue_set_profiler (self->command_queue, - gsk_renderer_get_profiler (renderer)); - - ret = TRUE; - -failure: - g_clear_object (&driver); - g_clear_object (&context); - - gdk_profiler_end_mark (start_time, "realize GskGLRenderer", NULL); - - /* Assert either all or no state was set */ - g_assert ((ret && self->driver != NULL && self->context != NULL && self->command_queue != NULL) || - (!ret && self->driver == NULL && self->context == NULL && self->command_queue == NULL)); - - return ret; -} - -static void -gsk_gl_renderer_unrealize (GskRenderer *renderer) -{ - GskGLRenderer *self = (GskGLRenderer *)renderer; - - g_assert (GSK_IS_GL_RENDERER (renderer)); - - gdk_gl_context_make_current (self->context); - - g_clear_object (&self->driver); - g_clear_object (&self->command_queue); - g_clear_object (&self->context); - - gdk_gl_context_clear_current (); -} - -static cairo_region_t * -get_render_region (GdkSurface *surface, - GdkGLContext *context) -{ - const cairo_region_t *damage; - GdkRectangle whole_surface; - GdkRectangle extents; - - g_assert (GDK_IS_SURFACE (surface)); - g_assert (GDK_IS_GL_CONTEXT (context)); - - whole_surface.x = 0; - whole_surface.y = 0; - whole_surface.width = gdk_surface_get_width (surface); - whole_surface.height = gdk_surface_get_height (surface); - - /* Damage does not have scale factor applied so we can compare it to - * @whole_surface which also doesn't have the scale factor applied. - */ - damage = gdk_draw_context_get_frame_region (GDK_DRAW_CONTEXT (context)); - - if (cairo_region_contains_rectangle (damage, &whole_surface) == CAIRO_REGION_OVERLAP_IN) - return NULL; - - /* If the extents match the full-scene, do the same as above */ - cairo_region_get_extents (damage, &extents); - if (gdk_rectangle_equal (&extents, &whole_surface)) - return NULL; - - /* Draw clipped to the bounding-box of the region. */ - return cairo_region_create_rectangle (&extents); -} - -static void -gsk_gl_renderer_render (GskRenderer *renderer, - GskRenderNode *root, - const cairo_region_t *update_area) -{ - GskGLRenderer *self = (GskGLRenderer *)renderer; - cairo_region_t *render_region; - graphene_rect_t viewport; - GskGLRenderJob *job; - GdkSurface *surface; - graphene_rect_t opaque_tmp; - const graphene_rect_t *opaque; - float scale; - - g_assert (GSK_IS_GL_RENDERER (renderer)); - g_assert (root != NULL); - - surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self->context)); - scale = gdk_gl_context_get_scale (self->context); - - if (cairo_region_is_empty (update_area)) - { - gdk_draw_context_empty_frame (GDK_DRAW_CONTEXT (self->context)); - return; - } - - viewport.origin.x = 0; - viewport.origin.y = 0; - viewport.size.width = gdk_surface_get_width (surface) * scale; - viewport.size.height = gdk_surface_get_height (surface) * scale; - - if (gsk_render_node_get_opaque_rect (root, &opaque_tmp)) - opaque = &opaque_tmp; - else - opaque = NULL; - gdk_draw_context_begin_frame_full (GDK_DRAW_CONTEXT (self->context), - gsk_render_node_get_preferred_depth (root), - update_area, - opaque); - - gdk_gl_context_make_current (self->context); - - /* Must be called *AFTER* gdk_draw_context_begin_frame() */ - render_region = get_render_region (surface, self->context); - - gsk_gl_driver_begin_frame (self->driver, self->command_queue); - job = gsk_gl_render_job_new (self->driver, &viewport, scale, render_region, 0, TRUE); - gsk_gl_render_job_render (job, root); - gsk_gl_driver_end_frame (self->driver); - gsk_gl_render_job_free (job); - - gdk_draw_context_end_frame_full (GDK_DRAW_CONTEXT (self->context)); - - gsk_gl_driver_after_frame (self->driver); - - cairo_region_destroy (render_region); -} - -static GdkTexture * -gsk_gl_renderer_render_texture (GskRenderer *renderer, - GskRenderNode *root, - const graphene_rect_t *viewport) -{ - GskGLRenderer *self = (GskGLRenderer *)renderer; - GskGLRenderTarget *render_target; - GskGLRenderJob *job; - GdkTexture *texture; - guint texture_id; - GdkMemoryFormat gdk_format; - int width, height, max_size; - int format; - - g_assert (GSK_IS_GL_RENDERER (renderer)); - g_assert (root != NULL); - - width = ceilf (viewport->size.width); - height = ceilf (viewport->size.height); - max_size = self->command_queue->max_texture_size; - if (width > max_size || height > max_size) - { - gsize x, y, size, stride; - GBytes *bytes; - guchar *data; - - stride = width * 4; - size = stride * height; - data = g_malloc_n (stride, height); - - for (y = 0; y < height; y += max_size) - { - for (x = 0; x < width; x += max_size) - { - texture = gsk_gl_renderer_render_texture (renderer, root, - &GRAPHENE_RECT_INIT (viewport->origin.x + x, - viewport->origin.y + y, - MIN (max_size, viewport->size.width - x), - MIN (max_size, viewport->size.height - y))); - gdk_texture_download (texture, - data + stride * y + x * 4, - stride); - g_object_unref (texture); - } - } - - bytes = g_bytes_new_take (data, size); - texture = gdk_memory_texture_new (width, height, GDK_MEMORY_DEFAULT, bytes, stride); - g_bytes_unref (bytes); - return texture; - } - - /* Don't use float textures for SRGB or node-editor turns on high - * depth unconditionally. */ - if (gsk_render_node_get_preferred_depth (root) != GDK_MEMORY_NONE && - gsk_render_node_get_preferred_depth (root) != GDK_MEMORY_U8 && - gsk_render_node_get_preferred_depth (root) != GDK_MEMORY_U8_SRGB && - gdk_gl_context_check_version (self->context, "3.0", "3.0")) - { - gdk_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED; - format = GL_RGBA32F; - } - else - { - format = GL_RGBA8; - gdk_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED; - } - - gdk_gl_context_make_current (self->context); - - if (gsk_gl_driver_create_render_target (self->driver, - width, height, - format, - &render_target)) - { - gsk_gl_driver_begin_frame (self->driver, self->command_queue); - job = gsk_gl_render_job_new (self->driver, viewport, 1, NULL, render_target->framebuffer_id, TRUE); - gsk_gl_render_job_render_flipped (job, root); - texture_id = gsk_gl_driver_release_render_target (self->driver, render_target, FALSE); - texture = gsk_gl_driver_create_gdk_texture (self->driver, texture_id, gdk_format); - gsk_gl_driver_end_frame (self->driver); - gsk_gl_render_job_free (job); - - gsk_gl_driver_after_frame (self->driver); - } - else - { - g_assert_not_reached (); - } - - return g_steal_pointer (&texture); -} - -static void -gsk_gl_renderer_dispose (GObject *object) -{ - GskGLRenderer *self = (GskGLRenderer *)object; - - if (self->driver != NULL) - g_critical ("Attempt to dispose %s without calling gsk_renderer_unrealize()", - G_OBJECT_TYPE_NAME (self)); - - G_OBJECT_CLASS (gsk_gl_renderer_parent_class)->dispose (object); -} - -static void -gsk_gl_renderer_class_init (GskGLRendererClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass); - - object_class->dispose = gsk_gl_renderer_dispose; - - renderer_class->supports_offload = TRUE; - - renderer_class->realize = gsk_gl_renderer_realize; - renderer_class->unrealize = gsk_gl_renderer_unrealize; - renderer_class->render = gsk_gl_renderer_render; - renderer_class->render_texture = gsk_gl_renderer_render_texture; -} - -static void -gsk_gl_renderer_init (GskGLRenderer *self) -{ -} - -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - -gboolean -gsk_gl_renderer_try_compile_gl_shader (GskGLRenderer *renderer, - GskGLShader *shader, - GError **error) -{ - GskGLProgram *program; - - g_return_val_if_fail (GSK_IS_GL_RENDERER (renderer), FALSE); - g_return_val_if_fail (shader != NULL, FALSE); - - program = gsk_gl_driver_lookup_shader (renderer->driver, shader, error); - - return program != NULL; -} -G_GNUC_END_IGNORE_DEPRECATIONS diff --git a/gsk/gl/gskglrendererprivate.h b/gsk/gl/gskglrendererprivate.h deleted file mode 100644 index 0bdb03efb29..00000000000 --- a/gsk/gl/gskglrendererprivate.h +++ /dev/null @@ -1,38 +0,0 @@ -/* gskglrendererprivate.h - * - * Copyright 2021 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "gskglrenderer.h" - -#include "gskglshader.h" - -G_BEGIN_DECLS - -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - -gboolean gsk_gl_renderer_try_compile_gl_shader (GskGLRenderer *renderer, - GskGLShader *shader, - GError **error); - -G_GNUC_END_IGNORE_DEPRECATIONS - -G_END_DECLS - diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c deleted file mode 100644 index 9c84c51fd51..00000000000 --- a/gsk/gl/gskglrenderjob.c +++ /dev/null @@ -1,4725 +0,0 @@ -/* gskglrenderjob.c - * - * Copyright 2017 Timm Bäder - * Copyright 2018 Matthias Clasen - * Copyright 2018 Alexander Larsson - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "gskglcommandqueueprivate.h" -#include "gskgldriverprivate.h" -#include "gskglglyphlibraryprivate.h" -#include "gskgliconlibraryprivate.h" -#include "gskglprogramprivate.h" -#include "gskglrenderjobprivate.h" -#include "gskglshadowlibraryprivate.h" -#include "gskdebugprivate.h" - -#include "ninesliceprivate.h" -#include "fp16private.h" - -#define ORTHO_NEAR_PLANE -10000 -#define ORTHO_FAR_PLANE 10000 -#define MAX_GRADIENT_STOPS 6 -#define SHADOW_EXTRA_SIZE 4 - -/* Make sure gradient stops fits in packed array_count */ -G_STATIC_ASSERT ((MAX_GRADIENT_STOPS * 5) < (1 << GSK_GL_UNIFORM_ARRAY_BITS)); - -#define ALPHA_IS_CLEAR(alpha) ((alpha) < ((float) 0x00ff / (float) 0xffff)) -#define RGBA_IS_CLEAR(rgba) ALPHA_IS_CLEAR((rgba)->alpha) - -typedef struct _GskGLRenderClip -{ - GskRoundedRect rect; - guint is_rectilinear : 1; - guint is_fully_contained : 1; -} GskGLRenderClip; - -#define GDK_ARRAY_NAME clips -#define GDK_ARRAY_TYPE_NAME Clips -#define GDK_ARRAY_ELEMENT_TYPE GskGLRenderClip -#define GDK_ARRAY_BY_VALUE 1 -#define GDK_ARRAY_PREALLOC 16 -#define GDK_ARRAY_NO_MEMSET -#include "gdk/gdkarrayimpl.c" - -typedef struct _GskGLRenderModelview -{ - GskTransform *transform; - float scale_x; - float scale_y; - float dx; - float dy; - float offset_x_before; - float offset_y_before; - graphene_matrix_t matrix; -} GskGLRenderModelview; - -#define GDK_ARRAY_NAME modelviews -#define GDK_ARRAY_TYPE_NAME Modelviews -#define GDK_ARRAY_ELEMENT_TYPE GskGLRenderModelview -#define GDK_ARRAY_BY_VALUE 1 -#define GDK_ARRAY_PREALLOC 16 -#define GDK_ARRAY_NO_MEMSET -#include "gdk/gdkarrayimpl.c" - -struct _GskGLRenderJob -{ - /* The context containing the framebuffer we are drawing to. Generally this - * is the context of the surface but may be a shared context if rendering to - * an offscreen texture such as gsk_gl_renderer_render_texture(). - */ - GdkGLContext *context; - - /* The driver to be used. This is shared among all the renderers on a given - * GdkDisplay and uses the shared GL context to send commands. - */ - GskGLDriver *driver; - - /* The command queue (which is just a faster pointer to the driver's - * command queue. - */ - GskGLCommandQueue *command_queue; - - /* The region that we are clipping. Normalized to a single rectangle region. */ - cairo_region_t *region; - - /* The framebuffer to draw to in the @context GL context. So 0 would be the - * default framebuffer of @context. This is important to note as many other - * operations could be done using objects shared from the command queues - * GL context. - */ - guint framebuffer; - guint default_framebuffer; - - /* The viewport we are using. This state is updated as we process render - * nodes in the specific visitor callbacks. - */ - graphene_rect_t viewport; - - /* The current projection, updated as we process nodes */ - graphene_matrix_t projection; - - /* An array of GskGLRenderModelview updated as nodes are processed. The - * current modelview is the last element. - */ - Modelviews modelview; - - /* An array of GskGLRenderClip updated as nodes are processed. The - * current clip is the last element. - */ - Clips clip; - - /* Our current alpha state as we process nodes */ - float alpha; - - /* Offset (delta x,y) as we process nodes. Occasionally this is merged into - * a transform that is referenced from child transform nodes. - */ - float offset_x; - float offset_y; - - /* The scale we are processing, possibly updated by transforms */ - float scale_x; - float scale_y; - - /* Cached pointers */ - const GskGLRenderClip *current_clip; - const GskGLRenderModelview *current_modelview; - GskGLProgram *current_program; - - guint source_is_glyph_atlas : 1; - - /* In some cases we might want to avoid clearing the framebuffer - * because we're going to render over the existing contents. - */ - guint clear_framebuffer : 1; - - /* Format we want to use for intermediate textures, determined by - * looking at the format of the framebuffer we are rendering on. - */ - int target_format; - - guint offscreen_count; /* if > 0, we're rendering to an offscreen */ -}; - -typedef struct _GskGLRenderOffscreen -{ - /* The bounds to render */ - const graphene_rect_t *bounds; - - /* Return location for texture coordinates */ - struct { - float x; - float y; - float x2; - float y2; - } area; - - /* Return location for texture ID */ - guint texture_id; - gpointer sync; - - /* Whether to force creating a new texture, even if the - * input already is a texture - */ - guint force_offscreen : 1; - guint reset_clip : 1; - guint do_not_cache : 1; - - /* Return location for whether we created a texture */ - guint was_offscreen : 1; - guint has_mipmap : 1; -} GskGLRenderOffscreen; - -static void gsk_gl_render_job_visit_node (GskGLRenderJob *job, - const GskRenderNode *node); -static gboolean gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob *job, - const GskRenderNode *node, - GskGLRenderOffscreen *offscreen); -static void gsk_gl_render_job_upload_texture (GskGLRenderJob *job, - GdkTexture *texture, - gboolean ensure_mipmap, - GskGLRenderOffscreen *offscreen); - -static inline GskGLRenderClip * -clips_grow_one (Clips *clips) -{ - guint len = clips_get_size (clips); - clips_set_size (clips, len + 1); - return clips_get (clips, len); -} - -static inline GskGLRenderModelview * -modelviews_grow_one (Modelviews *modelviews) -{ - guint len = modelviews_get_size (modelviews); - modelviews_set_size (modelviews, len + 1); - return modelviews_get (modelviews, len); -} - -static inline int -get_target_format (GskGLRenderJob *job, - const GskRenderNode *node) -{ - if (gsk_render_node_get_preferred_depth (node) != GDK_MEMORY_U8) - return job->target_format; - - return GL_RGBA8; -} - -static inline void -init_full_texture_region (GskGLRenderOffscreen *offscreen) -{ - offscreen->area.x = 0; - offscreen->area.y = 0; - offscreen->area.x2 = 1; - offscreen->area.y2 = 1; -} - -static inline gboolean G_GNUC_PURE -node_is_invisible (const GskRenderNode *node) -{ - return node->bounds.size.width == 0.0f || - node->bounds.size.height == 0.0f; -} - -static inline gboolean G_GNUC_PURE -rounded_rect_equal (const GskRoundedRect *r1, - const GskRoundedRect *r2) -{ - return memcmp (r1, r2, sizeof (GskRoundedRect)) == 0; -} - -static inline void -gsk_rounded_rect_shrink_to_minimum (GskRoundedRect *self) -{ - self->bounds.size.width = MAX (self->corner[0].width + self->corner[1].width, - self->corner[3].width + self->corner[2].width); - self->bounds.size.height = MAX (self->corner[0].height + self->corner[3].height, - self->corner[1].height + self->corner[2].height); -} - -static inline gboolean G_GNUC_PURE -node_supports_2d_transform (const GskRenderNode *node) -{ - switch (GSK_RENDER_NODE_TYPE (node)) - { - case GSK_COLOR_NODE: - case GSK_OPACITY_NODE: - case GSK_COLOR_MATRIX_NODE: - case GSK_TEXTURE_NODE: - case GSK_TEXTURE_SCALE_NODE: - case GSK_CROSS_FADE_NODE: - case GSK_LINEAR_GRADIENT_NODE: - case GSK_REPEATING_LINEAR_GRADIENT_NODE: - case GSK_CONIC_GRADIENT_NODE: - case GSK_RADIAL_GRADIENT_NODE: - case GSK_REPEATING_RADIAL_GRADIENT_NODE: - case GSK_DEBUG_NODE: - case GSK_TEXT_NODE: - case GSK_CAIRO_NODE: - case GSK_BLEND_NODE: - case GSK_BLUR_NODE: - case GSK_MASK_NODE: - case GSK_FILL_NODE: - case GSK_STROKE_NODE: - case GSK_SUBSURFACE_NODE: - return TRUE; - - case GSK_SHADOW_NODE: - return node_supports_2d_transform (gsk_shadow_node_get_child (node)); - - case GSK_TRANSFORM_NODE: - return node_supports_2d_transform (gsk_transform_node_get_child (node)); - - case GSK_CONTAINER_NODE: - for (guint i = 0, p = gsk_container_node_get_n_children (node); i < p; i++) - { - if (!node_supports_2d_transform (gsk_container_node_get_child (node, i))) - return FALSE; - } - return TRUE; - - case GSK_BORDER_NODE: - case GSK_INSET_SHADOW_NODE: - case GSK_OUTSET_SHADOW_NODE: - case GSK_REPEAT_NODE: - case GSK_CLIP_NODE: - case GSK_ROUNDED_CLIP_NODE: - case GSK_GL_SHADER_NODE: - return FALSE; - - case GSK_NOT_A_RENDER_NODE: - default: - g_assert_not_reached (); - } -} - -static inline gboolean G_GNUC_PURE -node_supports_transform (const GskRenderNode *node) -{ - /* Some nodes can't handle non-trivial transforms without being - * rendered to a texture (e.g. rotated clips, etc.). Some however work - * just fine, mostly because they already draw their child to a - * texture and just render the texture manipulated in some way, think - * opacity or color matrix. - */ - - switch (GSK_RENDER_NODE_TYPE (node)) - { - case GSK_COLOR_NODE: - case GSK_OPACITY_NODE: - case GSK_COLOR_MATRIX_NODE: - case GSK_TEXTURE_NODE: - case GSK_CROSS_FADE_NODE: - case GSK_DEBUG_NODE: - case GSK_TEXT_NODE: - case GSK_CAIRO_NODE: - case GSK_BLEND_NODE: - case GSK_BLUR_NODE: - case GSK_MASK_NODE: - case GSK_FILL_NODE: - case GSK_STROKE_NODE: - case GSK_SUBSURFACE_NODE: - return TRUE; - - case GSK_SHADOW_NODE: - return node_supports_transform (gsk_shadow_node_get_child (node)); - - case GSK_TRANSFORM_NODE: - return node_supports_transform (gsk_transform_node_get_child (node)); - - case GSK_CONTAINER_NODE: - case GSK_LINEAR_GRADIENT_NODE: - case GSK_REPEATING_LINEAR_GRADIENT_NODE: - case GSK_RADIAL_GRADIENT_NODE: - case GSK_REPEATING_RADIAL_GRADIENT_NODE: - case GSK_CONIC_GRADIENT_NODE: - case GSK_BORDER_NODE: - case GSK_INSET_SHADOW_NODE: - case GSK_OUTSET_SHADOW_NODE: - case GSK_REPEAT_NODE: - case GSK_CLIP_NODE: - case GSK_ROUNDED_CLIP_NODE: - case GSK_GL_SHADER_NODE: - case GSK_TEXTURE_SCALE_NODE: - return FALSE; - - case GSK_NOT_A_RENDER_NODE: - default: - g_assert_not_reached (); - } -} - -static inline gboolean G_GNUC_PURE -color_matrix_modifies_alpha (const GskRenderNode *node) -{ - const graphene_matrix_t *matrix = gsk_color_matrix_node_get_color_matrix (node); - const graphene_vec4_t *offset = gsk_color_matrix_node_get_color_offset (node); - graphene_vec4_t row3; - - if (graphene_vec4_get_w (offset) != 0.0f) - return TRUE; - - graphene_matrix_get_row (matrix, 3, &row3); - - return !graphene_vec4_equal (graphene_vec4_w_axis (), &row3); -} - -static inline void -init_projection_matrix (graphene_matrix_t *projection, - const graphene_rect_t *viewport) -{ - graphene_matrix_init_ortho (projection, - viewport->origin.x, - viewport->origin.x + viewport->size.width, - viewport->origin.y, - viewport->origin.y + viewport->size.height, - ORTHO_NEAR_PLANE, - ORTHO_FAR_PLANE); - graphene_matrix_scale (projection, 1, -1, 1); -} - -static inline float -gsk_gl_render_job_set_alpha (GskGLRenderJob *job, - float alpha) -{ - if (job->alpha != alpha) - { - float ret = job->alpha; - job->alpha = alpha; - job->driver->stamps[UNIFORM_SHARED_ALPHA]++; - return ret; - } - - return alpha; -} - -static void -extract_matrix_metadata (GskGLRenderModelview *modelview) -{ - gsk_transform_to_matrix (modelview->transform, &modelview->matrix); - - switch (gsk_transform_get_category (modelview->transform)) - { - case GSK_TRANSFORM_CATEGORY_IDENTITY: - modelview->scale_x = 1; - modelview->scale_y = 1; - modelview->dx = 0; - modelview->dy = 0; - break; - - case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE: - modelview->scale_x = 1; - modelview->scale_y = 1; - gsk_transform_to_translate (modelview->transform, - &modelview->dx, &modelview->dy); - break; - - case GSK_TRANSFORM_CATEGORY_2D_AFFINE: - gsk_transform_to_affine (modelview->transform, - &modelview->scale_x, &modelview->scale_y, - &modelview->dx, &modelview->dy); - break; - - case GSK_TRANSFORM_CATEGORY_2D: - { - float skew_x, skew_y, angle, dx, dy; - - gsk_transform_to_2d_components (modelview->transform, - &skew_x, &skew_y, - &modelview->scale_x, &modelview->scale_y, - &angle, &dx, &dy); - modelview->dx = 0; - modelview->dy = 0; - } - break; - - case GSK_TRANSFORM_CATEGORY_UNKNOWN: - case GSK_TRANSFORM_CATEGORY_ANY: - case GSK_TRANSFORM_CATEGORY_3D: - { - graphene_quaternion_t rotation; - graphene_vec4_t perspective; - graphene_vec3_t translation; - graphene_vec3_t scale; - graphene_vec3_t shear; - - graphene_matrix_decompose (&modelview->matrix, - &translation, - &scale, - &rotation, - &shear, - &perspective); - - modelview->scale_x = graphene_vec3_get_x (&scale); - modelview->scale_y = graphene_vec3_get_y (&scale); - modelview->dx = 0; - modelview->dy = 0; - } - break; - - default: - break; - } -} - -/* takes ownership of transform */ -static void -gsk_gl_render_job_set_modelview (GskGLRenderJob *job, - GskTransform *transform) -{ - GskGLRenderModelview *modelview; - - g_assert (job != NULL); - - job->driver->stamps[UNIFORM_SHARED_MODELVIEW]++; - - modelview = modelviews_grow_one (&job->modelview); - - modelview->transform = transform; - - modelview->offset_x_before = job->offset_x; - modelview->offset_y_before = job->offset_y; - - extract_matrix_metadata (modelview); - - job->offset_x = 0; - job->offset_y = 0; - job->scale_x = modelview->scale_x; - job->scale_y = modelview->scale_y; - - job->current_modelview = modelview; -} - -/* doesn't take ownership of transform */ -static void -gsk_gl_render_job_push_modelview (GskGLRenderJob *job, - GskTransform *transform) -{ - GskGLRenderModelview *modelview; - - g_assert (job != NULL); - g_assert (transform != NULL); - - job->driver->stamps[UNIFORM_SHARED_MODELVIEW]++; - - modelview = modelviews_grow_one (&job->modelview); - - if G_LIKELY (modelviews_get_size (&job->modelview) > 1) - { - GskGLRenderModelview *last = job->modelview.end - 2; - GskTransform *t = NULL; - - /* Multiply given matrix with our previous modelview */ - t = gsk_transform_translate (gsk_transform_ref (last->transform), - &(graphene_point_t) { - job->offset_x, - job->offset_y - }); - t = gsk_transform_transform (t, transform); - modelview->transform = t; - } - else - { - modelview->transform = gsk_transform_ref (transform); - } - - modelview->offset_x_before = job->offset_x; - modelview->offset_y_before = job->offset_y; - - extract_matrix_metadata (modelview); - - job->offset_x = 0; - job->offset_y = 0; - job->scale_x = modelview->scale_x; - job->scale_y = modelview->scale_y; - - job->current_modelview = modelview; -} - -static void -gsk_gl_render_job_pop_modelview (GskGLRenderJob *job) -{ - const GskGLRenderModelview *head; - - g_assert (job != NULL); - g_assert (modelviews_get_size (&job->modelview) > 0); - - job->driver->stamps[UNIFORM_SHARED_MODELVIEW]++; - - head = job->current_modelview; - - job->offset_x = head->offset_x_before; - job->offset_y = head->offset_y_before; - - gsk_transform_unref (head->transform); - - job->modelview.end--; - - if (modelviews_get_size (&job->modelview) >= 1) - { - head = job->modelview.end - 1; - - job->scale_x = head->scale_x; - job->scale_y = head->scale_y; - - job->current_modelview = head; - } - else - { - job->current_modelview = NULL; - } -} - -static void -gsk_gl_render_job_push_clip (GskGLRenderJob *job, - const GskRoundedRect *rect) -{ - GskGLRenderClip *clip; - - g_assert (job != NULL); - g_assert (rect != NULL); - - job->driver->stamps[UNIFORM_SHARED_CLIP_RECT]++; - - clip = clips_grow_one (&job->clip); - - memcpy (&clip->rect, rect, sizeof *rect); - clip->is_rectilinear = gsk_rounded_rect_is_rectilinear (rect); - clip->is_fully_contained = FALSE; - - job->current_clip = clip; -} - -static void -gsk_gl_render_job_push_contained_clip (GskGLRenderJob *job) -{ - GskGLRenderClip *clip; - GskGLRenderClip *old_clip; - - g_assert (job != NULL); - g_assert (clips_get_size (&job->clip) > 0); - - job->driver->stamps[UNIFORM_SHARED_CLIP_RECT]++; - - clip = clips_grow_one (&job->clip); - old_clip = clips_get (&job->clip, clips_get_size (&job->clip) - 2); - - memcpy (&clip->rect.bounds, &old_clip->rect.bounds, sizeof (graphene_rect_t)); - memset (clip->rect.corner, 0, sizeof clip->rect.corner); - clip->is_rectilinear = TRUE; - clip->is_fully_contained = TRUE; - - job->current_clip = clip; -} - -static void -gsk_gl_render_job_pop_clip (GskGLRenderJob *job) -{ - g_assert (job != NULL); - g_assert (clips_get_size (&job->clip) > 0); - - job->driver->stamps[UNIFORM_SHARED_CLIP_RECT]++; - job->current_clip--; - job->clip.end--; -} - -static inline void -gsk_gl_render_job_offset (GskGLRenderJob *job, - float offset_x, - float offset_y) -{ - if (offset_x || offset_y) - { - job->offset_x += offset_x; - job->offset_y += offset_y; - } -} - -static inline void -gsk_gl_render_job_set_projection (GskGLRenderJob *job, - const graphene_matrix_t *projection) -{ - memcpy (&job->projection, projection, sizeof job->projection); - job->driver->stamps[UNIFORM_SHARED_PROJECTION]++; -} - -static inline void -gsk_gl_render_job_set_projection_from_rect (GskGLRenderJob *job, - const graphene_rect_t *rect, - graphene_matrix_t *prev_projection) -{ - if (prev_projection) - memcpy (prev_projection, &job->projection, sizeof *prev_projection); - init_projection_matrix (&job->projection, rect); - job->driver->stamps[UNIFORM_SHARED_PROJECTION]++; -} - -static inline void -gsk_gl_render_job_set_projection_for_size (GskGLRenderJob *job, - float width, - float height, - graphene_matrix_t *prev_projection) -{ - if (prev_projection) - memcpy (prev_projection, &job->projection, sizeof *prev_projection); - graphene_matrix_init_ortho (&job->projection, 0, width, 0, height, ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE); - graphene_matrix_scale (&job->projection, 1, -1, 1); - job->driver->stamps[UNIFORM_SHARED_PROJECTION]++; -} - -static inline void -gsk_gl_render_job_set_viewport (GskGLRenderJob *job, - const graphene_rect_t *viewport, - graphene_rect_t *prev_viewport) -{ - if (prev_viewport) - memcpy (prev_viewport, &job->viewport, sizeof *prev_viewport); - memcpy (&job->viewport, viewport, sizeof job->viewport); - job->driver->stamps[UNIFORM_SHARED_VIEWPORT]++; -} - -static inline void -gsk_gl_render_job_set_viewport_for_size (GskGLRenderJob *job, - float width, - float height, - graphene_rect_t *prev_viewport) -{ - if (prev_viewport) - memcpy (prev_viewport, &job->viewport, sizeof *prev_viewport); - job->viewport.origin.x = 0; - job->viewport.origin.y = 0; - job->viewport.size.width = width; - job->viewport.size.height = height; - job->driver->stamps[UNIFORM_SHARED_VIEWPORT]++; -} - -static inline void -gsk_gl_render_job_transform_bounds (GskGLRenderJob *job, - const graphene_rect_t *rect, - graphene_rect_t *out_rect) -{ - GskTransform *transform; - GskTransformCategory category; - - g_assert (job != NULL); - g_assert (modelviews_get_size (&job->modelview) > 0); - g_assert (rect != NULL); - g_assert (out_rect != NULL); - - transform = job->current_modelview->transform; - category = gsk_transform_get_category (transform); - - /* Our most common transform is 2d-affine, so inline it. - * Both identity and 2d-translate are virtually unseen here. - */ - if G_LIKELY (category >= GSK_TRANSFORM_CATEGORY_2D_AFFINE) - { - float scale_x = job->current_modelview->scale_x; - float scale_y = job->current_modelview->scale_y; - float dx = job->current_modelview->dx; - float dy = job->current_modelview->dy; - - /* Init directly into out rect */ - out_rect->origin.x = ((rect->origin.x + job->offset_x) * scale_x) + dx; - out_rect->origin.y = ((rect->origin.y + job->offset_y) * scale_y) + dy; - out_rect->size.width = rect->size.width * scale_x; - out_rect->size.height = rect->size.height * scale_y; - - /* Normalize in place */ - if (out_rect->size.width < 0.f) - { - float size = fabsf (out_rect->size.width); - - out_rect->origin.x -= size; - out_rect->size.width = size; - } - - if (out_rect->size.height < 0.f) - { - float size = fabsf (out_rect->size.height); - - out_rect->origin.y -= size; - out_rect->size.height = size; - } - } - else - { - graphene_rect_t r; - - r.origin.x = rect->origin.x + job->offset_x; - r.origin.y = rect->origin.y + job->offset_y; - r.size.width = rect->size.width; - r.size.height = rect->size.height; - - gsk_transform_transform_bounds (transform, &r, out_rect); - } -} - -static inline void -gsk_gl_render_job_untransform_bounds (GskGLRenderJob *job, - const graphene_rect_t *rect, - graphene_rect_t *out_rect) -{ - GskTransform *transform; - - transform = gsk_transform_invert (gsk_transform_ref (job->current_modelview->transform)); - - gsk_transform_transform_bounds (transform, rect, out_rect); - - out_rect->origin.x -= job->offset_x; - out_rect->origin.y -= job->offset_y; - - gsk_transform_unref (transform); -} - -static inline void -gsk_gl_render_job_translate_rounded_rect (GskGLRenderJob *job, - const GskRoundedRect *rect, - GskRoundedRect *out_rect) - { - out_rect->bounds.origin.x = job->offset_x + rect->bounds.origin.x; - out_rect->bounds.origin.y = job->offset_y + rect->bounds.origin.y; - out_rect->bounds.size.width = rect->bounds.size.width; - out_rect->bounds.size.height = rect->bounds.size.height; - memcpy (out_rect->corner, rect->corner, sizeof rect->corner); -} - -static inline void -rounded_rect_scale_corners (const GskRoundedRect *rect, - GskRoundedRect *out_rect, - float scale_x, - float scale_y) -{ - for (guint i = 0; i < G_N_ELEMENTS (out_rect->corner); i++) - { - out_rect->corner[i].width = rect->corner[i].width * fabsf (scale_x); - out_rect->corner[i].height = rect->corner[i].height * fabsf (scale_y); - } - - if (scale_x < 0) - { - graphene_size_t p; - - p = out_rect->corner[GSK_CORNER_TOP_LEFT]; - out_rect->corner[GSK_CORNER_TOP_LEFT] = out_rect->corner[GSK_CORNER_TOP_RIGHT]; - out_rect->corner[GSK_CORNER_TOP_RIGHT] = p; - p = out_rect->corner[GSK_CORNER_BOTTOM_LEFT]; - out_rect->corner[GSK_CORNER_BOTTOM_LEFT] = out_rect->corner[GSK_CORNER_BOTTOM_RIGHT]; - out_rect->corner[GSK_CORNER_BOTTOM_RIGHT] = p; - } - - if (scale_y < 0) - { - graphene_size_t p; - - p = out_rect->corner[GSK_CORNER_TOP_LEFT]; - out_rect->corner[GSK_CORNER_TOP_LEFT] = out_rect->corner[GSK_CORNER_BOTTOM_LEFT]; - out_rect->corner[GSK_CORNER_BOTTOM_LEFT] = p; - p = out_rect->corner[GSK_CORNER_TOP_RIGHT]; - out_rect->corner[GSK_CORNER_TOP_RIGHT] = out_rect->corner[GSK_CORNER_BOTTOM_RIGHT]; - out_rect->corner[GSK_CORNER_BOTTOM_RIGHT] = p; - } -} - -static inline void -gsk_gl_render_job_transform_rounded_rect (GskGLRenderJob *job, - const GskRoundedRect *rect, - GskRoundedRect *out_rect) -{ - gsk_gl_render_job_transform_bounds (job, &rect->bounds, &out_rect->bounds); - rounded_rect_scale_corners (rect, out_rect, job->scale_x, job->scale_y); -} - -static inline void -rounded_rect_get_inner (const GskRoundedRect *rect, - graphene_rect_t *inner) -{ - float left = MAX (rect->corner[GSK_CORNER_TOP_LEFT].width, rect->corner[GSK_CORNER_BOTTOM_LEFT].width); - float right = MAX (rect->corner[GSK_CORNER_TOP_RIGHT].width, rect->corner[GSK_CORNER_BOTTOM_RIGHT].width); - float top = MAX (rect->corner[GSK_CORNER_TOP_LEFT].height, rect->corner[GSK_CORNER_TOP_RIGHT].height); - float bottom = MAX (rect->corner[GSK_CORNER_BOTTOM_LEFT].height, rect->corner[GSK_CORNER_BOTTOM_RIGHT].height); - - inner->origin.x = rect->bounds.origin.x + left; - inner->size.width = rect->bounds.size.width - (left + right); - - inner->origin.y = rect->bounds.origin.y + top; - inner->size.height = rect->bounds.size.height - (top + bottom); -} - -static inline gboolean -interval_contains (float p1, float w1, - float p2, float w2) -{ - if (p2 < p1) - return FALSE; - - if (p2 + w2 > p1 + w1) - return FALSE; - - return TRUE; -} - -static inline gboolean -gsk_gl_render_job_update_clip (GskGLRenderJob *job, - const graphene_rect_t *bounds, - gboolean *pushed_clip) -{ - graphene_rect_t transformed_bounds; - gboolean no_clip = FALSE; - gboolean rect_clip = FALSE; - - *pushed_clip = FALSE; - - if (job->current_clip->is_fully_contained) - { - /* Already fully contained - no further checks needed */ - return TRUE; - } - - gsk_gl_render_job_transform_bounds (job, bounds, &transformed_bounds); - - if (!gsk_rect_intersects (&job->current_clip->rect.bounds, &transformed_bounds)) - { - /* Completely clipped away */ - return FALSE; - } - - if (job->current_clip->is_rectilinear) - { - if (gsk_rect_contains_rect (&job->current_clip->rect.bounds, &transformed_bounds)) - no_clip = TRUE; - else - rect_clip = TRUE; - } - else if (gsk_rounded_rect_contains_rect (&job->current_clip->rect, &transformed_bounds)) - { - no_clip = TRUE; - } - else - { - graphene_rect_t inner; - - rounded_rect_get_inner (&job->current_clip->rect, &inner); - - if (interval_contains (inner.origin.x, inner.size.width, - transformed_bounds.origin.x, transformed_bounds.size.width) || - interval_contains (inner.origin.y, inner.size.height, - transformed_bounds.origin.y, transformed_bounds.size.height)) - rect_clip = TRUE; - } - - if (no_clip) - { - /* This node is completely contained inside the clip. - * Record this fact on the clip stack, so we don't do more work - * for child nodes. - */ - - gsk_gl_render_job_push_contained_clip (job); - - *pushed_clip = TRUE; - } - else if (rect_clip && !job->current_clip->is_rectilinear) - { - graphene_rect_t rect; - - /* The clip gets simpler for this node */ - - gsk_rect_intersection (&job->current_clip->rect.bounds, &transformed_bounds, &rect); - gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (rect)); - - *pushed_clip = TRUE; - } - - return TRUE; -} - -static inline void -rgba_to_half (const GdkRGBA *rgba, - guint16 h[4]) -{ - float_to_half4 ((const float *)rgba, h); -} - -/* fill_vertex_data */ -static void -gsk_gl_render_job_draw_coords (GskGLRenderJob *job, - float min_x, - float min_y, - float max_x, - float max_y, - float min_u, - float min_v, - float max_u, - float max_v, - guint16 c[4]) -{ - GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (job->command_queue); - - vertices[0] = (GskGLDrawVertex) { .position = { min_x, min_y }, .uv = { min_u, min_v }, .color = { c[0], c[1], c[2], c[3] } }; - vertices[1] = (GskGLDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c[0], c[1], c[2], c[3] } }; - vertices[2] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c[0], c[1], c[2], c[3] } }; - vertices[3] = (GskGLDrawVertex) { .position = { max_x, max_y }, .uv = { max_u, max_v }, .color = { c[0], c[1], c[2], c[3] } }; - vertices[4] = (GskGLDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c[0], c[1], c[2], c[3] } }; - vertices[5] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c[0], c[1], c[2], c[3] } }; -} - -/* load_vertex_data_with_region */ -static inline void -gsk_gl_render_job_draw_offscreen_with_color (GskGLRenderJob *job, - const graphene_rect_t *bounds, - const GskGLRenderOffscreen *offscreen, - guint16 color[4]) -{ - float min_x = job->offset_x + bounds->origin.x; - float min_y = job->offset_y + bounds->origin.y; - float max_x = min_x + bounds->size.width; - float max_y = min_y + bounds->size.height; - float y1 = offscreen->was_offscreen ? offscreen->area.y2 : offscreen->area.y; - float y2 = offscreen->was_offscreen ? offscreen->area.y : offscreen->area.y2; - - gsk_gl_render_job_draw_coords (job, - min_x, min_y, max_x, max_y, - offscreen->area.x, y1, offscreen->area.x2, y2, - color); -} - -static inline void -gsk_gl_render_job_draw_offscreen (GskGLRenderJob *job, - const graphene_rect_t *bounds, - const GskGLRenderOffscreen *offscreen) -{ - gsk_gl_render_job_draw_offscreen_with_color (job, bounds, offscreen, - (guint16[]) { FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO }); -} - -/* load_float_vertex_data */ -static inline void -gsk_gl_render_job_draw_with_color (GskGLRenderJob *job, - float x, - float y, - float width, - float height, - guint16 color[4]) -{ - float min_x = job->offset_x + x; - float min_y = job->offset_y + y; - float max_x = min_x + width; - float max_y = min_y + height; - - gsk_gl_render_job_draw_coords (job, min_x, min_y, max_x, max_y, 0, 0, 1, 1, color); -} - -static inline void -gsk_gl_render_job_draw (GskGLRenderJob *job, - float x, - float y, - float width, - float height) -{ - gsk_gl_render_job_draw_with_color (job, x, y, width, height, - (guint16[]) { FP_ZERO, FP_ZERO, FP_ZERO, FP_ZERO }); -} - -/* load_vertex_data */ -static inline void -gsk_gl_render_job_draw_rect_with_color (GskGLRenderJob *job, - const graphene_rect_t *bounds, - guint16 color[4]) -{ - gsk_gl_render_job_draw_with_color (job, - bounds->origin.x, - bounds->origin.y, - bounds->size.width, - bounds->size.height, - color); -} -static inline void -gsk_gl_render_job_draw_rect (GskGLRenderJob *job, - const graphene_rect_t *bounds) -{ - gsk_gl_render_job_draw (job, - bounds->origin.x, - bounds->origin.y, - bounds->size.width, - bounds->size.height); -} - -/* load_offscreen_vertex_data */ -static inline void -gsk_gl_render_job_draw_offscreen_rect (GskGLRenderJob *job, - const graphene_rect_t *bounds) -{ - float min_x = job->offset_x + bounds->origin.x; - float min_y = job->offset_y + bounds->origin.y; - float max_x = min_x + bounds->size.width; - float max_y = min_y + bounds->size.height; - guint16 color[4] = { FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO }; - - gsk_gl_render_job_draw_coords (job, - min_x, min_y, max_x, max_y, - 0, 1, 1, 0, - color); -} - -static inline gboolean -gsk_gl_render_job_begin_draw (GskGLRenderJob *job, - GskGLProgram *program) -{ - job->current_program = program; - - if (!gsk_gl_command_queue_begin_draw (job->command_queue, - program->program_info, - job->viewport.size.width, - job->viewport.size.height)) - return FALSE; - - gsk_gl_uniform_state_set4fv (program->uniforms, - program->program_info, - UNIFORM_SHARED_VIEWPORT, - job->driver->stamps[UNIFORM_SHARED_VIEWPORT], - 1, - (const float *)&job->viewport); - - gsk_gl_uniform_state_set_matrix (program->uniforms, - program->program_info, - UNIFORM_SHARED_MODELVIEW, - job->driver->stamps[UNIFORM_SHARED_MODELVIEW], - &job->current_modelview->matrix); - - gsk_gl_uniform_state_set_matrix (program->uniforms, - program->program_info, - UNIFORM_SHARED_PROJECTION, - job->driver->stamps[UNIFORM_SHARED_PROJECTION], - &job->projection); - - gsk_gl_uniform_state_set_rounded_rect (program->uniforms, - program->program_info, - UNIFORM_SHARED_CLIP_RECT, - job->driver->stamps[UNIFORM_SHARED_CLIP_RECT], - &job->current_clip->rect); - - gsk_gl_uniform_state_set1f (program->uniforms, - program->program_info, - UNIFORM_SHARED_ALPHA, - job->driver->stamps[UNIFORM_SHARED_ALPHA], - job->alpha); - - return TRUE; -} - -#define CHOOSE_PROGRAM(job,name) \ - (job->current_clip->is_fully_contained \ - ? job->driver->name ## _no_clip \ - : (job->current_clip->is_rectilinear \ - ? job->driver->name ## _rect_clip \ - : job->driver->name)) - -static inline void -gsk_gl_render_job_split_draw (GskGLRenderJob *job) -{ - gsk_gl_command_queue_split_draw (job->command_queue); -} - -static inline void -gsk_gl_render_job_end_draw (GskGLRenderJob *job) -{ - gsk_gl_command_queue_end_draw (job->command_queue); - - job->current_program = NULL; -} - -static inline void -gsk_gl_render_job_visit_as_fallback (GskGLRenderJob *job, - const GskRenderNode *node) -{ - float scale_x = job->scale_x; - float scale_y = job->scale_y; - int surface_width = ceilf (node->bounds.size.width * fabsf (scale_x)); - int surface_height = ceilf (node->bounds.size.height * fabsf (scale_y)); - GdkTexture *texture; - cairo_surface_t *surface; - cairo_surface_t *rendered_surface; - cairo_t *cr; - int texture_id; - GskTextureKey key; - - if (surface_width <= 0 || surface_height <= 0) - return; - - key.pointer = node; - key.pointer_is_child = FALSE; - key.scale_x = scale_x; - key.scale_y = scale_y; - - texture_id = gsk_gl_driver_lookup_texture (job->driver, &key, NULL); - - if (texture_id != 0) - goto done; - - /* We first draw the recording surface on an image surface, - * just because the scaleY(-1) later otherwise screws up the - * rendering... */ - { - rendered_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - surface_width, - surface_height); - - cairo_surface_set_device_scale (rendered_surface, fabsf (scale_x), fabsf (scale_y)); - cr = cairo_create (rendered_surface); - - cairo_save (cr); - cairo_translate (cr, - node->bounds.origin.x, - node->bounds.origin.y); - /* Render nodes don't modify state, so casting away the const is fine here */ - gsk_render_node_draw_fallback ((GskRenderNode *)node, cr); - cairo_restore (cr); - cairo_destroy (cr); - } - - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - surface_width, - surface_height); - cairo_surface_set_device_scale (surface, fabsf (scale_x), fabsf (scale_y)); - cr = cairo_create (surface); - - /* We draw upside down here, so it matches what GL does. */ - cairo_save (cr); - cairo_scale (cr, scale_x < 0 ? -1 : 1, scale_y < 0 ? 1 : -1); - cairo_translate (cr, scale_x < 0 ? - surface_width / fabsf (scale_x) : 0, - scale_y < 0 ? 0 : - surface_height / fabsf (scale_y)); - cairo_set_source_surface (cr, rendered_surface, 0, 0); - cairo_rectangle (cr, 0, 0, surface_width / fabsf (scale_x), surface_height / fabsf (scale_y)); - cairo_fill (cr); - cairo_restore (cr); - cairo_destroy (cr); - - /* Create texture to upload */ - texture = gdk_texture_new_for_surface (surface); - texture_id = gsk_gl_driver_load_texture (job->driver, texture, FALSE); - - if (gdk_gl_context_has_feature (job->command_queue->context, GDK_GL_FEATURE_DEBUG)) - gdk_gl_context_label_object_printf (job->command_queue->context, GL_TEXTURE, texture_id, - "Fallback %s %d", - g_type_name_from_instance ((GTypeInstance *) node), - texture_id); - - g_object_unref (texture); - cairo_surface_destroy (surface); - cairo_surface_destroy (rendered_surface); - - gsk_gl_driver_cache_texture (job->driver, &key, texture_id); - -done: - if (scale_x < 0 || scale_y < 0) - { - GskTransform *transform = gsk_transform_translate (gsk_transform_scale (NULL, scale_x < 0 ? -1 : 1, scale_y < 0 ? -1 : 1), - &GRAPHENE_POINT_INIT (scale_x < 0 ? - (node->bounds.size.width + 2 * node->bounds.origin.x) : 0, - scale_y < 0 ? - (node->bounds.size.height + 2 * node->bounds.origin.y) : 0)); - gsk_gl_render_job_push_modelview (job, transform); - gsk_transform_unref (transform); - } - - if (!gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - goto out; - - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_offscreen_rect (job, &node->bounds); - gsk_gl_render_job_end_draw (job); - -out: - if (scale_x < 0 || scale_y < 0) - gsk_gl_render_job_pop_modelview (job); -} - -static 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); - GskGLRenderTarget *pass1; - GskGLRenderTarget *pass2; - graphene_matrix_t prev_projection; - graphene_rect_t prev_viewport; - guint prev_fbo; - - g_assert (blur_radius_x > 0); - g_assert (blur_radius_y > 0); - g_assert (offscreen->texture_id > 0); - g_assert (offscreen->area.x2 > offscreen->area.x); - g_assert (offscreen->area.y2 > offscreen->area.y); - - if (!gsk_gl_driver_create_render_target (job->driver, - MAX (texture_to_blur_width, 1), - MAX (texture_to_blur_height, 1), - job->target_format, - &pass1)) - return 0; - - if (texture_to_blur_width <= 0 || texture_to_blur_height <= 0) - return gsk_gl_driver_release_render_target (job->driver, pass1, FALSE); - - if (!gsk_gl_driver_create_render_target (job->driver, - texture_to_blur_width, - texture_to_blur_height, - job->target_format, - &pass2)) - return gsk_gl_driver_release_render_target (job->driver, pass1, FALSE); - - gsk_gl_render_job_set_viewport (job, &new_clip.bounds, &prev_viewport); - gsk_gl_render_job_set_projection_from_rect (job, &new_clip.bounds, &prev_projection); - gsk_gl_render_job_set_modelview (job, NULL); - gsk_gl_render_job_push_clip (job, &new_clip); - - /* Bind new framebuffer and clear it */ - prev_fbo = 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. - */ - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blur))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - offscreen->texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_program_set_uniform1f (job->current_program, - UNIFORM_BLUR_RADIUS, 0, - blur_radius_x); - gsk_gl_program_set_uniform2f (job->current_program, - UNIFORM_BLUR_SIZE, 0, - texture_to_blur_width, - texture_to_blur_height); - gsk_gl_program_set_uniform2f (job->current_program, - UNIFORM_BLUR_DIR, 0, - 1, 0); - gsk_gl_render_job_draw_coords (job, - 0, 0, texture_to_blur_width, texture_to_blur_height, - 0, 1, 1, 0, - (guint16[]) { FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO }); - gsk_gl_render_job_end_draw (job); - } - - /* 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 */ - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blur))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - pass1->texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_program_set_uniform1f (job->current_program, - UNIFORM_BLUR_RADIUS, 0, - blur_radius_y); - gsk_gl_program_set_uniform2f (job->current_program, - UNIFORM_BLUR_SIZE, 0, - texture_to_blur_width, - texture_to_blur_height); - gsk_gl_program_set_uniform2f (job->current_program, - UNIFORM_BLUR_DIR, 0, - 0, 1); - gsk_gl_render_job_draw_coords (job, - 0, 0, texture_to_blur_width, texture_to_blur_height, - 0, 1, 1, 0, - (guint16[]) { FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO }); - gsk_gl_render_job_end_draw (job); - } - - gsk_gl_render_job_pop_modelview (job); - gsk_gl_render_job_pop_clip (job); - gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL); - gsk_gl_render_job_set_projection (job, &prev_projection); - gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo); - - gsk_gl_driver_release_render_target (job->driver, pass1, TRUE); - - return gsk_gl_driver_release_render_target (job->driver, pass2, FALSE); -} - -static void -blur_node (GskGLRenderJob *job, - GskGLRenderOffscreen *offscreen, - const 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_extra / 2.0); - 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 (); - - /* Ensure that we actually got a real texture_id */ - g_assert (offscreen->texture_id != 0); - - offscreen->texture_id = blur_offscreen (job, - offscreen, - texture_width * fabsf (scale_x), - texture_height * fabsf (scale_y), - blur_radius * fabsf (scale_x), - blur_radius * fabsf (scale_y)); - init_full_texture_region (offscreen); - } - - *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; -} - -#define ATLAS_SIZE 512 - -static void -get_color_node_color_as_srgb (const GskRenderNode *node, - GdkRGBA *rgba) -{ - const GdkColor *color = gsk_color_node_get_color2 (node); - gdk_color_to_float (color, GDK_COLOR_STATE_SRGB, (float *) rgba); -} - -static inline void -gsk_gl_render_job_visit_color_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - GdkRGBA rgba; - guint16 color[4]; - GskGLProgram *program; - GskGLCommandBatch *batch; - - get_color_node_color_as_srgb (node, &rgba); - if (RGBA_IS_CLEAR (&rgba)) - return; - - rgba_to_half (&rgba, color); - - /* Avoid switching away from the coloring program for - * rendering a solid color. - */ - program = CHOOSE_PROGRAM (job, coloring); - batch = gsk_gl_command_queue_get_batch (job->command_queue); - - /* Limit the size, or we end up with a coordinate overflow somewhere. */ - if (job->source_is_glyph_atlas && - node->bounds.size.width < 300 && - node->bounds.size.height < 300 && - batch->any.kind == GSK_GL_COMMAND_KIND_DRAW && - batch->any.program == program->id) - { - GskGLRenderOffscreen offscreen = {0}; - - if (gsk_gl_render_job_begin_draw (job, program)) - { - /* The top left few pixels in our atlases are always - * solid white, so we can use it here, without - * having to choose any particular atlas texture. - */ - offscreen.was_offscreen = FALSE; - offscreen.area.x = 1.f / ATLAS_SIZE; - offscreen.area.y = 1.f / ATLAS_SIZE; - offscreen.area.x2 = 2.f / ATLAS_SIZE; - offscreen.area.y2 = 2.f / ATLAS_SIZE; - - gsk_gl_render_job_draw_offscreen_with_color (job, - &node->bounds, - &offscreen, - color); - - gsk_gl_render_job_end_draw (job); - } - } - else - { - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, color))) - { - gsk_gl_render_job_draw_rect_with_color (job, &node->bounds, color); - gsk_gl_render_job_end_draw (job); - } - } -} - -static inline void -gsk_gl_render_job_visit_linear_gradient_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GskColorStop *stops = gsk_linear_gradient_node_get_color_stops (node, NULL); - const graphene_point_t *start = gsk_linear_gradient_node_get_start (node); - const graphene_point_t *end = gsk_linear_gradient_node_get_end (node); - int n_color_stops = gsk_linear_gradient_node_get_n_color_stops (node); - gboolean repeat = GSK_RENDER_NODE_TYPE (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE; - float x1 = job->offset_x + start->x; - float x2 = job->offset_x + end->x; - float y1 = job->offset_y + start->y; - float y2 = job->offset_y + end->y; - - g_assert (n_color_stops < MAX_GRADIENT_STOPS); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, linear_gradient))) - { - gsk_gl_program_set_uniform1i (job->current_program, - UNIFORM_LINEAR_GRADIENT_NUM_COLOR_STOPS, 0, - n_color_stops); - gsk_gl_program_set_uniform1fv (job->current_program, - UNIFORM_LINEAR_GRADIENT_COLOR_STOPS, 0, - n_color_stops * 5, - (const float *)stops); - gsk_gl_program_set_uniform4f (job->current_program, - UNIFORM_LINEAR_GRADIENT_POINTS, 0, - x1, y1, x2 - x1, y2 - y1); - gsk_gl_program_set_uniform1i (job->current_program, - UNIFORM_LINEAR_GRADIENT_REPEAT, 0, - repeat); - gsk_gl_render_job_draw_rect (job, &node->bounds); - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_conic_gradient_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - static const float scale = 0.5f * M_1_PI; - - const GskColorStop *stops = gsk_conic_gradient_node_get_color_stops (node, NULL); - const graphene_point_t *center = gsk_conic_gradient_node_get_center (node); - int n_color_stops = gsk_conic_gradient_node_get_n_color_stops (node); - float angle = gsk_conic_gradient_node_get_angle (node); - float bias = angle * scale + 2.0f; - - g_assert (n_color_stops < MAX_GRADIENT_STOPS); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, conic_gradient))) - { - gsk_gl_program_set_uniform1i (job->current_program, - UNIFORM_CONIC_GRADIENT_NUM_COLOR_STOPS, 0, - n_color_stops); - gsk_gl_program_set_uniform1fv (job->current_program, - UNIFORM_CONIC_GRADIENT_COLOR_STOPS, 0, - n_color_stops * 5, - (const float *)stops); - gsk_gl_program_set_uniform4f (job->current_program, - UNIFORM_CONIC_GRADIENT_GEOMETRY, 0, - job->offset_x + center->x, - job->offset_y + center->y, - scale, - bias); - gsk_gl_render_job_draw_rect (job, &node->bounds); - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_radial_gradient_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - int n_color_stops = gsk_radial_gradient_node_get_n_color_stops (node); - const GskColorStop *stops = gsk_radial_gradient_node_get_color_stops (node, NULL); - const graphene_point_t *center = gsk_radial_gradient_node_get_center (node); - float start = gsk_radial_gradient_node_get_start (node); - float end = gsk_radial_gradient_node_get_end (node); - float hradius = gsk_radial_gradient_node_get_hradius (node); - float vradius = gsk_radial_gradient_node_get_vradius (node); - gboolean repeat = GSK_RENDER_NODE_TYPE (node) == GSK_REPEATING_RADIAL_GRADIENT_NODE; - float scale = 1.0f / (end - start); - float bias = -start * scale; - - g_assert (n_color_stops < MAX_GRADIENT_STOPS); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, radial_gradient))) - { - gsk_gl_program_set_uniform1i (job->current_program, - UNIFORM_RADIAL_GRADIENT_NUM_COLOR_STOPS, 0, - n_color_stops); - gsk_gl_program_set_uniform1fv (job->current_program, - UNIFORM_RADIAL_GRADIENT_COLOR_STOPS, 0, - n_color_stops * 5, - (const float *)stops); - gsk_gl_program_set_uniform1i (job->current_program, - UNIFORM_RADIAL_GRADIENT_REPEAT, 0, - repeat); - gsk_gl_program_set_uniform2f (job->current_program, - UNIFORM_RADIAL_GRADIENT_RANGE, 0, - scale, bias); - gsk_gl_program_set_uniform4f (job->current_program, - UNIFORM_RADIAL_GRADIENT_GEOMETRY, 0, - job->offset_x + center->x, - job->offset_y + center->y, - 1.0f / (hradius * job->scale_x), - 1.0f / (vradius * job->scale_y)); - gsk_gl_render_job_draw_rect (job, &node->bounds); - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_clipped_child (GskGLRenderJob *job, - const GskRenderNode *child, - const graphene_rect_t *clip) -{ - graphene_rect_t transformed_clip; - GskRoundedRect intersection; - GskRoundedRectIntersection result; - - gsk_gl_render_job_transform_bounds (job, clip, &transformed_clip); - - if (job->current_clip->is_rectilinear) - { - memset (&intersection.corner, 0, sizeof intersection.corner); - gsk_rect_intersection (&transformed_clip, - &job->current_clip->rect.bounds, - &intersection.bounds); - - gsk_gl_render_job_push_clip (job, &intersection); - gsk_gl_render_job_visit_node (job, child); - gsk_gl_render_job_pop_clip (job); - return; - } - - result = gsk_rounded_rect_intersect_with_rect (&job->current_clip->rect, - &transformed_clip, - &intersection); - - if (result == GSK_INTERSECTION_EMPTY) - return; - - if (result == GSK_INTERSECTION_NONEMPTY) - { - gsk_gl_render_job_push_clip (job, &intersection); - gsk_gl_render_job_visit_node (job, child); - gsk_gl_render_job_pop_clip (job); - } - else - { - GskGLRenderOffscreen offscreen = {0}; - - offscreen.bounds = clip; - offscreen.force_offscreen = TRUE; - offscreen.reset_clip = TRUE; - offscreen.do_not_cache = TRUE; - - gsk_gl_render_job_visit_node_with_offscreen (job, child, &offscreen); - - g_assert (offscreen.texture_id); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - offscreen.texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_offscreen_rect (job, clip); - gsk_gl_render_job_end_draw (job); - } - } -} - -static inline void -gsk_gl_render_job_visit_clip_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const graphene_rect_t *clip = gsk_clip_node_get_clip (node); - const GskRenderNode *child = gsk_clip_node_get_child (node); - - gsk_gl_render_job_visit_clipped_child (job, child, clip); -} - -static inline void -gsk_gl_render_job_visit_rounded_clip_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GskRenderNode *child = gsk_rounded_clip_node_get_child (node); - const GskRoundedRect *clip = gsk_rounded_clip_node_get_clip (node); - GskRoundedRect transformed_clip; - gboolean need_offscreen; - - if (node_is_invisible (child)) - return; - - gsk_gl_render_job_transform_rounded_rect (job, clip, &transformed_clip); - - if (job->current_clip->is_rectilinear) - { - GskRoundedRect intersected_clip; - GskRoundedRectIntersection result; - - result = gsk_rounded_rect_intersect_with_rect (&transformed_clip, - &job->current_clip->rect.bounds, - &intersected_clip); - - if (result == GSK_INTERSECTION_EMPTY) - return; - - if (result == GSK_INTERSECTION_NONEMPTY) - { - gsk_gl_render_job_push_clip (job, &intersected_clip); - gsk_gl_render_job_visit_node (job, child); - gsk_gl_render_job_pop_clip (job); - return; - } - } - - /* After this point we are really working with a new and a current clip - * which both have rounded corners. - */ - - if (clips_get_size (&job->clip) <= 1) - need_offscreen = FALSE; - else if (gsk_rounded_rect_contains_rect (&job->current_clip->rect, &transformed_clip.bounds)) - need_offscreen = FALSE; - else - need_offscreen = TRUE; - - if (!need_offscreen) - { - gsk_gl_render_job_push_clip (job, &transformed_clip); - gsk_gl_render_job_visit_node (job, child); - gsk_gl_render_job_pop_clip (job); - } - else - { - GskGLRenderOffscreen offscreen = {0}; - - offscreen.bounds = &node->bounds; - offscreen.force_offscreen = TRUE; - offscreen.reset_clip = FALSE; - - gsk_gl_render_job_push_clip (job, &transformed_clip); - if (!gsk_gl_render_job_visit_node_with_offscreen (job, child, &offscreen)) - g_assert_not_reached (); - gsk_gl_render_job_pop_clip (job); - - g_assert (offscreen.texture_id); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - offscreen.texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_offscreen (job, &node->bounds, &offscreen); - gsk_gl_render_job_end_draw (job); - } - } -} - -static inline void -gsk_gl_render_job_visit_rect_border_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GdkRGBA *colors = gsk_border_node_get_colors (node); - const float *widths = gsk_border_node_get_widths (node); - const graphene_point_t *origin = &node->bounds.origin; - const graphene_size_t *size = &node->bounds.size; - guint16 color[4]; - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, color))) - { - if (widths[0] > 0) - { - rgba_to_half (&colors[0], color); - gsk_gl_render_job_draw_rect_with_color (job, - &GRAPHENE_RECT_INIT (origin->x, origin->y, size->width - widths[1], widths[0]), - color); - } - - if (widths[1] > 0) - { - rgba_to_half (&colors[1], color); - gsk_gl_render_job_draw_rect_with_color (job, - &GRAPHENE_RECT_INIT (origin->x + size->width - widths[1], origin->y, widths[1], size->height - widths[2]), - color); - } - - if (widths[2] > 0) - { - rgba_to_half (&colors[2], color); - gsk_gl_render_job_draw_rect_with_color (job, - &GRAPHENE_RECT_INIT (origin->x + widths[3], origin->y + size->height - widths[2], size->width - widths[3], widths[2]), - color); - } - - if (widths[3] > 0) - { - rgba_to_half (&colors[3], color); - gsk_gl_render_job_draw_rect_with_color (job, - &GRAPHENE_RECT_INIT (origin->x, origin->y + widths[0], widths[3], size->height - widths[0]), - color); - } - - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_border_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GskRoundedRect *rounded_outline = gsk_border_node_get_outline (node); - const GdkRGBA *colors = gsk_border_node_get_colors (node); - const float *widths = gsk_border_node_get_widths (node); - struct { - float w; - float h; - } sizes[4]; - float min_x = job->offset_x + node->bounds.origin.x; - float min_y = job->offset_y + node->bounds.origin.y; - float max_x = min_x + node->bounds.size.width; - float max_y = min_y + node->bounds.size.height; - GskRoundedRect outline; - guint16 color[4]; - - memset (sizes, 0, sizeof sizes); - - if (widths[0] > 0) - { - sizes[0].h = MAX (widths[0], rounded_outline->corner[0].height); - sizes[1].h = MAX (widths[0], rounded_outline->corner[1].height); - } - - if (widths[1] > 0) - { - sizes[1].w = MAX (widths[1], rounded_outline->corner[1].width); - sizes[2].w = MAX (widths[1], rounded_outline->corner[2].width); - } - - if (widths[2] > 0) - { - sizes[2].h = MAX (widths[2], rounded_outline->corner[2].height); - sizes[3].h = MAX (widths[2], rounded_outline->corner[3].height); - } - - if (widths[3] > 0) - { - sizes[0].w = MAX (widths[3], rounded_outline->corner[0].width); - sizes[3].w = MAX (widths[3], rounded_outline->corner[3].width); - } - - gsk_gl_render_job_translate_rounded_rect (job, rounded_outline, &outline); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, border))) - { - gsk_gl_program_set_uniform4fv (job->current_program, - UNIFORM_BORDER_WIDTHS, 0, - 1, - widths); - gsk_gl_program_set_uniform_rounded_rect (job->current_program, - UNIFORM_BORDER_OUTLINE_RECT, 0, - &outline); - - if (widths[0] > 0) - { - GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (job->command_queue); - - rgba_to_half (&colors[0], color); - - vertices[0] = (GskGLDrawVertex) { .position = { min_x, min_y }, .uv = { 0, 1 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[1] = (GskGLDrawVertex) { .position = { min_x + sizes[0].w, min_y + sizes[0].h }, .uv = { 0, 0 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[2] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { 1, 1 }, .color = { color[0], color[1], color[2], color[3] } }; - - vertices[3] = (GskGLDrawVertex) { .position = { max_x - sizes[1].w, min_y + sizes[1].h }, .uv = { 1, 0 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[4] = (GskGLDrawVertex) { .position = { min_x + sizes[0].w, min_y + sizes[0].h }, .uv = { 0, 0 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[5] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { 1, 1 }, .color = { color[0], color[1], color[2], color[3] } }; - } - - if (widths[1] > 0) - { - GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (job->command_queue); - - rgba_to_half (&colors[1], color); - - vertices[0] = (GskGLDrawVertex) { .position = { max_x - sizes[1].w, min_y + sizes[1].h }, .uv = { 0, 1 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[1] = (GskGLDrawVertex) { .position = { max_x - sizes[2].w, max_y - sizes[2].h }, .uv = { 0, 0 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[2] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { 1, 1 }, .color = { color[0], color[1], color[2], color[3] } }; - - vertices[3] = (GskGLDrawVertex) { .position = { max_x, max_y }, .uv = { 1, 0 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[4] = (GskGLDrawVertex) { .position = { max_x - sizes[2].w, max_y - sizes[2].h }, .uv = { 0, 0 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[5] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { 1, 1 }, .color = { color[0], color[1], color[2], color[3] } }; - } - - if (widths[2] > 0) - { - GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (job->command_queue); - - rgba_to_half (&colors[2], color); - - vertices[0] = (GskGLDrawVertex) { .position = { min_x + sizes[3].w, max_y - sizes[3].h }, .uv = { 0, 1 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[1] = (GskGLDrawVertex) { .position = { min_x, max_y }, .uv = { 0, 0 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[2] = (GskGLDrawVertex) { .position = { max_x - sizes[2].w, max_y - sizes[2].h }, .uv = { 1, 1 }, .color = { color[0], color[1], color[2], color[3] } }; - - vertices[3] = (GskGLDrawVertex) { .position = { max_x, max_y }, .uv = { 1, 0 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[4] = (GskGLDrawVertex) { .position = { min_x , max_y }, .uv = { 0, 0 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[5] = (GskGLDrawVertex) { .position = { max_x - sizes[2].w, max_y - sizes[2].h }, .uv = { 1, 1 }, .color = { color[0], color[1], color[2], color[3] } }; - } - - if (widths[3] > 0) - { - GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (job->command_queue); - - rgba_to_half (&colors[3], color); - - vertices[0] = (GskGLDrawVertex) { .position = { min_x, min_y }, .uv = { 0, 1 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[1] = (GskGLDrawVertex) { .position = { min_x, max_y }, .uv = { 0, 0 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[2] = (GskGLDrawVertex) { .position = { min_x + sizes[0].w, min_y + sizes[0].h }, .uv = { 1, 1 }, .color = { color[0], color[1], color[2], color[3] } }; - - vertices[3] = (GskGLDrawVertex) { .position = { min_x + sizes[3].w, max_y - sizes[3].h }, .uv = { 1, 0 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[4] = (GskGLDrawVertex) { .position = { min_x, max_y }, .uv = { 0, 0 }, .color = { color[0], color[1], color[2], color[3] } }; - vertices[5] = (GskGLDrawVertex) { .position = { min_x + sizes[0].w, min_y + sizes[0].h }, .uv = { 1, 1 }, .color = { color[0], color[1], color[2], color[3] } }; - } - - gsk_gl_render_job_end_draw (job); - } -} - -/* A special case for a pattern that occurs frequently with CSS - * backgrounds: two sibling nodes, the first of which is a rounded - * clip node with a color node as child, and the second one is a - * border node, with the same outline as the clip node. We render - * this using the filled_border shader. - */ -static void -gsk_gl_render_job_visit_css_background (GskGLRenderJob *job, - const GskRenderNode *node, - const GskRenderNode *node2) -{ - const GskRenderNode *child = gsk_rounded_clip_node_get_child (node); - const GskRoundedRect *rounded_outline = gsk_border_node_get_outline (node2); - const float *widths = gsk_border_node_get_widths (node2); - float min_x = job->offset_x + node2->bounds.origin.x; - float min_y = job->offset_y + node2->bounds.origin.y; - float max_x = min_x + node2->bounds.size.width; - float max_y = min_y + node2->bounds.size.height; - GskRoundedRect outline; - GskGLDrawVertex *vertices; - guint16 color[4]; - guint16 color2[4]; - GdkRGBA rgba; - - if (node_is_invisible (node2)) - return; - - get_color_node_color_as_srgb (child, &rgba); - rgba_to_half (&gsk_border_node_get_colors (node2)[0], color); - rgba_to_half (&rgba, color2); - - gsk_gl_render_job_translate_rounded_rect (job, rounded_outline, &outline); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, filled_border))) - { - gsk_gl_program_set_uniform4fv (job->current_program, - UNIFORM_FILLED_BORDER_WIDTHS, 0, - 1, - widths); - gsk_gl_program_set_uniform_rounded_rect (job->current_program, - UNIFORM_FILLED_BORDER_OUTLINE_RECT, 0, - &outline); - - vertices = gsk_gl_command_queue_add_vertices (job->command_queue); - - vertices[0] = (GskGLDrawVertex) { .position = { min_x, min_y }, .color = { color[0], color[1], color[2], color[3] }, .color2 = { color2[0], color2[1], color2[2], color2[3] } }; - vertices[1] = (GskGLDrawVertex) { .position = { min_x, max_y }, .color = { color[0], color[1], color[2], color[3] }, .color2 = { color2[0], color2[1], color2[2], color2[3] } }; - vertices[2] = (GskGLDrawVertex) { .position = { max_x, min_y }, .color = { color[0], color[1], color[2], color[3] }, .color2 = { color2[0], color2[1], color2[2], color2[3] } }; - vertices[3] = (GskGLDrawVertex) { .position = { max_x, max_y }, .color = { color[0], color[1], color[2], color[3] }, .color2 = { color2[0], color2[1], color2[2], color2[3] } }; - vertices[4] = (GskGLDrawVertex) { .position = { min_x, max_y }, .color = { color[0], color[1], color[2], color[3] }, .color2 = { color2[0], color2[1], color2[2], color2[3] } }; - vertices[5] = (GskGLDrawVertex) { .position = { max_x, min_y }, .color = { color[0], color[1], color[2], color[3] }, .color2 = { color2[0], color2[1], color2[2], color2[3] } }; - - gsk_gl_render_job_end_draw (job); - } -} - -/* Returns TRUE if applying @transform to @bounds - * yields an axis-aligned rectangle - */ -static gboolean -result_is_axis_aligned (GskTransform *transform, - const graphene_rect_t *bounds) -{ - graphene_matrix_t m; - graphene_quad_t q; - graphene_rect_t b; - graphene_point_t b1, b2; - const graphene_point_t *p; - - gsk_transform_to_matrix (transform, &m); - gsk_matrix_transform_rect (&m, bounds, &q); - graphene_quad_bounds (&q, &b); - graphene_rect_get_top_left (&b, &b1); - graphene_rect_get_bottom_right (&b, &b2); - - for (guint i = 0; i < 4; i++) - { - p = graphene_quad_get_point (&q, i); - if (fabsf (p->x - b1.x) > FLT_EPSILON && fabsf (p->x - b2.x) > FLT_EPSILON) - return FALSE; - if (fabsf (p->y - b1.y) > FLT_EPSILON && fabsf (p->y - b2.y) > FLT_EPSILON) - return FALSE; - } - - return TRUE; -} - -static inline void -gsk_gl_render_job_visit_transform_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - GskTransform *transform = gsk_transform_node_get_transform (node); - const GskTransformCategory category = gsk_transform_get_category (transform); - const GskRenderNode *child = gsk_transform_node_get_child (node); - - switch (category) - { - case GSK_TRANSFORM_CATEGORY_IDENTITY: - gsk_gl_render_job_visit_node (job, child); - break; - - case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE: - { - float dx, dy; - - gsk_transform_node_get_translate (node, &dx, &dy); - gsk_gl_render_job_offset (job, dx, dy); - gsk_gl_render_job_visit_node (job, child); - gsk_gl_render_job_offset (job, -dx, -dy); - } - break; - - case GSK_TRANSFORM_CATEGORY_2D_AFFINE: - { - gsk_gl_render_job_push_modelview (job, transform); - gsk_gl_render_job_visit_node (job, child); - gsk_gl_render_job_pop_modelview (job); - } - break; - - case GSK_TRANSFORM_CATEGORY_2D: - if (node_supports_2d_transform (child)) - { - gsk_gl_render_job_push_modelview (job, transform); - gsk_gl_render_job_visit_node (job, child); - gsk_gl_render_job_pop_modelview (job); - return; - } - G_GNUC_FALLTHROUGH; - case GSK_TRANSFORM_CATEGORY_3D: - case GSK_TRANSFORM_CATEGORY_ANY: - case GSK_TRANSFORM_CATEGORY_UNKNOWN: - if (node_supports_transform (child)) - { - gsk_gl_render_job_push_modelview (job, transform); - gsk_gl_render_job_visit_node (job, child); - gsk_gl_render_job_pop_modelview (job); - } - else - { - GskGLRenderOffscreen offscreen = {0}; - float sx = 1, sy = 1; - gboolean linear_filter = FALSE; - - offscreen.bounds = &child->bounds; - offscreen.force_offscreen = FALSE; - offscreen.reset_clip = TRUE; - - if (!result_is_axis_aligned (transform, &child->bounds)) - linear_filter = TRUE; - - if (category == GSK_TRANSFORM_CATEGORY_2D) - { - graphene_matrix_t m; - double a, b, c, d, tx, ty; - - g_assert (transform != NULL); - gsk_transform_to_matrix (transform, &m); - if (graphene_matrix_to_2d (&m, &a, &b, &c, &d, &tx, &ty)) - { - sx = sqrt (a * a + b * b); - sy = sqrt (c * c + d * d); - } - else - sx = sy = 1; - - if (sx != 1 || sy != 1) - { - GskTransform *scale; - - scale = gsk_transform_translate (gsk_transform_scale (NULL, sx, sy), &GRAPHENE_POINT_INIT (tx, ty)); - gsk_gl_render_job_push_modelview (job, scale); - transform = gsk_transform_transform (gsk_transform_invert (scale), transform); - } - } - - if (gsk_gl_render_job_visit_node_with_offscreen (job, child, &offscreen)) - { - /* For non-trivial transforms, we draw everything on a texture and then - * draw the texture transformed. - */ - if (transform) - gsk_gl_render_job_push_modelview (job, transform); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - { - gsk_gl_program_set_uniform_texture_with_filter (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - offscreen.texture_id, - linear_filter ? GL_LINEAR : GL_NEAREST, - linear_filter ? GL_LINEAR : GL_NEAREST); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_offscreen (job, &child->bounds, &offscreen); - gsk_gl_render_job_end_draw (job); - } - - if (transform) - gsk_gl_render_job_pop_modelview (job); - } - - if (category == GSK_TRANSFORM_CATEGORY_2D) - { - if (sx != 1 || sy != 1) - { - gsk_gl_render_job_pop_modelview (job); - gsk_transform_unref (transform); - } - } - } - break; - - default: - g_assert_not_reached (); - } -} - -static inline void -gsk_gl_render_job_visit_unblurred_inset_shadow_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GskRoundedRect *outline = gsk_inset_shadow_node_get_outline (node); - GskRoundedRect transformed_outline; - guint16 color[4]; - - gsk_gl_render_job_translate_rounded_rect (job, outline, &transformed_outline); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, inset_shadow))) - { - const GdkRGBA rgba; - - gdk_color_to_float (gsk_inset_shadow_node_get_color2 (node), GDK_COLOR_STATE_SRGB, (float *) &rgba); - - gsk_gl_program_set_uniform_rounded_rect (job->current_program, - UNIFORM_INSET_SHADOW_OUTLINE_RECT, 0, - &transformed_outline); - gsk_gl_program_set_uniform1f (job->current_program, - UNIFORM_INSET_SHADOW_SPREAD, 0, - gsk_inset_shadow_node_get_spread (node)); - gsk_gl_program_set_uniform2f (job->current_program, - UNIFORM_INSET_SHADOW_OFFSET, 0, - gsk_inset_shadow_node_get_dx (node), - gsk_inset_shadow_node_get_dy (node)); - rgba_to_half (&rgba, color); - gsk_gl_render_job_draw_rect_with_color (job, &node->bounds, color); - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_blurred_inset_shadow_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GskRoundedRect *node_outline = gsk_inset_shadow_node_get_outline (node); - float blur_radius = gsk_inset_shadow_node_get_blur_radius (node); - float offset_x = gsk_inset_shadow_node_get_dx (node); - float offset_y = gsk_inset_shadow_node_get_dy (node); - float scale_x = job->scale_x; - float scale_y = job->scale_y; - float blur_extra = blur_radius * 2.0; /* 2.0 = shader radius_multiplier */ - float half_blur_extra = blur_radius; - float texture_width; - float texture_height; - int blurred_texture_id; - GskTextureKey key; - GskGLRenderOffscreen offscreen = {0}; - guint16 color[4]; - - g_assert (blur_radius > 0); - - texture_width = ceilf ((node_outline->bounds.size.width + blur_extra) * scale_x); - texture_height = ceilf ((node_outline->bounds.size.height + blur_extra) * scale_y); - - key.pointer = node; - key.pointer_is_child = FALSE; - key.scale_x = scale_x; - key.scale_y = scale_y; - - blurred_texture_id = gsk_gl_driver_lookup_texture (job->driver, &key, NULL); - - if (blurred_texture_id == 0) - { - float spread = gsk_inset_shadow_node_get_spread (node) + half_blur_extra; - GskRoundedRect transformed_outline; - GskRoundedRect outline_to_blur; - GskGLRenderTarget *render_target; - graphene_matrix_t prev_projection; - graphene_rect_t prev_viewport; - guint prev_fbo; - - /* TODO: In the following code, we have to be careful about where we apply the scale. - * We're manually scaling stuff (e.g. the outline) so we can later use texture_width - * and texture_height (which are already scaled) as the geometry and keep the modelview - * at a scale of 1. That's kinda complicated though... */ - - /* Outline of what we actually want to blur later. - * Spread grows inside, so we don't need to account for that. But the blur will need - * to read outside of the inset shadow, so we need to draw some color in there. */ - outline_to_blur = *node_outline; - gsk_rounded_rect_shrink (&outline_to_blur, - -half_blur_extra, - -half_blur_extra, - -half_blur_extra, - -half_blur_extra); - - /* Fit to our texture */ - outline_to_blur.bounds.origin.x = 0; - outline_to_blur.bounds.origin.y = 0; - outline_to_blur.bounds.size.width *= scale_x; - outline_to_blur.bounds.size.height *= scale_y; - - for (guint i = 0; i < 4; i ++) - { - outline_to_blur.corner[i].width *= scale_x; - outline_to_blur.corner[i].height *= scale_y; - } - - if (!gsk_gl_driver_create_render_target (job->driver, - texture_width, texture_height, - get_target_format (job, node), - &render_target)) - g_assert_not_reached (); - - gsk_gl_render_job_set_viewport_for_size (job, texture_width, texture_height, &prev_viewport); - gsk_gl_render_job_set_projection_for_size (job, texture_width, texture_height, &prev_projection); - gsk_gl_render_job_set_modelview (job, NULL); - gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT (0, 0, texture_width, texture_height)); - - prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id); - gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport); - - gsk_gl_render_job_translate_rounded_rect (job, &outline_to_blur, &transformed_outline); - - /* Actual inset shadow outline drawing */ - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, inset_shadow))) - { - const GdkRGBA rgba; - - gdk_color_to_float (gsk_inset_shadow_node_get_color2 (node), GDK_COLOR_STATE_SRGB, (float *) &rgba); - - gsk_gl_program_set_uniform_rounded_rect (job->current_program, - UNIFORM_INSET_SHADOW_OUTLINE_RECT, 0, - &transformed_outline); - gsk_gl_program_set_uniform1f (job->current_program, - UNIFORM_INSET_SHADOW_SPREAD, 0, - spread * MAX (scale_x, scale_y)); - gsk_gl_program_set_uniform2f (job->current_program, - UNIFORM_INSET_SHADOW_OFFSET, 0, - offset_x * scale_x, - offset_y * scale_y); - rgba_to_half (&rgba, color); - gsk_gl_render_job_draw_with_color (job, - 0, 0, texture_width, texture_height, - color); - gsk_gl_render_job_end_draw (job); - } - - gsk_gl_render_job_pop_modelview (job); - gsk_gl_render_job_pop_clip (job); - gsk_gl_render_job_set_projection (job, &prev_projection); - gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL); - gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo); - - offscreen.texture_id = render_target->texture_id; - init_full_texture_region (&offscreen); - - blurred_texture_id = blur_offscreen (job, - &offscreen, - texture_width, - texture_height, - blur_radius * fabsf (scale_x), - blur_radius * fabsf (scale_y)); - - gsk_gl_driver_release_render_target (job->driver, render_target, TRUE); - - gsk_gl_driver_cache_texture (job->driver, &key, blurred_texture_id); - } - - g_assert (blurred_texture_id != 0); - - /* Blur the rendered unblurred inset shadow */ - /* Use a clip to cut away the unwanted parts outside of the original outline */ - { - const gboolean needs_clip = !gsk_rounded_rect_is_rectilinear (node_outline); - const float tx1 = half_blur_extra * scale_x / texture_width; - const float tx2 = 1.0 - tx1; - const float ty1 = half_blur_extra * scale_y / texture_height; - const float ty2 = 1.0 - ty1; - - if (needs_clip) - { - GskRoundedRect node_clip; - - gsk_gl_render_job_translate_rounded_rect (job, node_outline, &node_clip); - gsk_gl_render_job_push_clip (job, &node_clip); - } - - offscreen.was_offscreen = TRUE; - offscreen.area.x = tx1; - offscreen.area.y = ty1; - offscreen.area.x2 = tx2; - offscreen.area.y2 = ty2; - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - blurred_texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_offscreen (job, &node->bounds, &offscreen); - gsk_gl_render_job_end_draw (job); - } - - if (needs_clip) - gsk_gl_render_job_pop_clip (job); - } -} - -static inline void -gsk_gl_render_job_visit_unblurred_outset_shadow_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GskRoundedRect *outline = gsk_outset_shadow_node_get_outline (node); - GskRoundedRect transformed_outline; - float x = node->bounds.origin.x; - float y = node->bounds.origin.y; - float w = node->bounds.size.width; - float h = node->bounds.size.height; - float spread = gsk_outset_shadow_node_get_spread (node); - float dx = gsk_outset_shadow_node_get_dx (node); - float dy = gsk_outset_shadow_node_get_dy (node); - GdkRGBA rgba; - guint16 color[4]; - const float edge_sizes[] = { // Top, right, bottom, left - spread - dy, spread + dx, spread + dy, spread - dx - }; - const float corner_sizes[][2] = { // top left, top right, bottom right, bottom left - { outline->corner[0].width + spread - dx, outline->corner[0].height + spread - dy }, - { outline->corner[1].width + spread + dx, outline->corner[1].height + spread - dy }, - { outline->corner[2].width + spread + dx, outline->corner[2].height + spread + dy }, - { outline->corner[3].width + spread - dx, outline->corner[3].height + spread + dy }, - }; - - gdk_color_to_float (gsk_outset_shadow_node_get_color2 (node), GDK_COLOR_STATE_SRGB, (float *) &rgba); - rgba_to_half (&rgba, color); - - gsk_gl_render_job_translate_rounded_rect (job, outline, &transformed_outline); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, unblurred_outset_shadow))) - { - gsk_gl_program_set_uniform_rounded_rect (job->current_program, - UNIFORM_UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT, 0, - &transformed_outline); - gsk_gl_program_set_uniform1f (job->current_program, - UNIFORM_UNBLURRED_OUTSET_SHADOW_SPREAD, 0, - spread); - gsk_gl_program_set_uniform2f (job->current_program, - UNIFORM_UNBLURRED_OUTSET_SHADOW_OFFSET, 0, - dx, dy); - - /* Corners... */ - if (corner_sizes[0][0] > 0 && corner_sizes[0][1] > 0) /* Top left */ - gsk_gl_render_job_draw_with_color (job, - x, y, corner_sizes[0][0], corner_sizes[0][1], - color); - if (corner_sizes[1][0] > 0 && corner_sizes[1][1] > 0) /* Top right */ - gsk_gl_render_job_draw_with_color (job, - x + w - corner_sizes[1][0], y, - corner_sizes[1][0], corner_sizes[1][1], - color); - if (corner_sizes[2][0] > 0 && corner_sizes[2][1] > 0) /* Bottom right */ - gsk_gl_render_job_draw_with_color (job, - x + w - corner_sizes[2][0], y + h - corner_sizes[2][1], - corner_sizes[2][0], corner_sizes[2][1], - color); - if (corner_sizes[3][0] > 0 && corner_sizes[3][1] > 0) /* Bottom left */ - gsk_gl_render_job_draw_with_color (job, - x, y + h - corner_sizes[3][1], - corner_sizes[3][0], corner_sizes[3][1], - color); - /* Edges... */; - if (edge_sizes[0] > 0) /* Top */ - gsk_gl_render_job_draw_with_color (job, - x + corner_sizes[0][0], y, - w - corner_sizes[0][0] - corner_sizes[1][0], edge_sizes[0], - color); - if (edge_sizes[1] > 0) /* Right */ - gsk_gl_render_job_draw_with_color (job, - x + w - edge_sizes[1], y + corner_sizes[1][1], - edge_sizes[1], h - corner_sizes[1][1] - corner_sizes[2][1], - color); - if (edge_sizes[2] > 0) /* Bottom */ - gsk_gl_render_job_draw_with_color (job, - x + corner_sizes[3][0], y + h - edge_sizes[2], - w - corner_sizes[3][0] - corner_sizes[2][0], edge_sizes[2], - color); - if (edge_sizes[3] > 0) /* Left */ - gsk_gl_render_job_draw_with_color (job, - x, y + corner_sizes[0][1], - edge_sizes[3], h - corner_sizes[0][1] - corner_sizes[3][1], - color); - - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_blurred_outset_shadow_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GskRoundedRect *outline = gsk_outset_shadow_node_get_outline (node); - float scale_x = fabsf (job->scale_x); - float scale_y = fabsf (job->scale_y); - float blur_radius = gsk_outset_shadow_node_get_blur_radius (node); - float blur_extra = blur_radius * 2.0f; /* 2.0 = shader radius_multiplier */ - float half_blur_extra = blur_extra / 2.0f; - int extra_blur_pixels_x = ceilf (half_blur_extra * scale_x); - int extra_blur_pixels_y = ceilf (half_blur_extra * scale_y); - float spread = gsk_outset_shadow_node_get_spread (node); - float dx = gsk_outset_shadow_node_get_dx (node); - float dy = gsk_outset_shadow_node_get_dy (node); - GskRoundedRect scaled_outline; - GskRoundedRect transformed_outline; - GskGLRenderOffscreen offscreen = {0}; - int texture_width, texture_height; - int blurred_texture_id; - int cached_tid; - gboolean do_slicing; - GdkRGBA rgba; - guint16 color[4]; - float half_width = outline->bounds.size.width / 2; - float half_height = outline->bounds.size.height / 2; - - gdk_color_to_float (gsk_outset_shadow_node_get_color2 (node), GDK_COLOR_STATE_SRGB, (float *) &rgba); - rgba_to_half (&rgba, color); - - /* scaled_outline is the minimal outline we need to draw the given drop shadow, - * enlarged by the spread and offset by the blur radius. */ - scaled_outline = *outline; - - if (outline->bounds.size.width < blur_extra || - outline->bounds.size.height < blur_extra || - outline->corner[0].width >= half_width || - outline->corner[1].width >= half_width || - outline->corner[2].width >= half_width || - outline->corner[3].width >= half_width || - outline->corner[0].height >= half_height || - outline->corner[1].height >= half_height || - outline->corner[2].height >= half_height || - outline->corner[3].height >= half_height) - { - do_slicing = FALSE; - gsk_rounded_rect_shrink (&scaled_outline, -spread, -spread, -spread, -spread); - } - else - { - /* Shrink our outline to the minimum size that can still hold all the border radii */ - gsk_rounded_rect_shrink_to_minimum (&scaled_outline); - /* Increase by the spread */ - gsk_rounded_rect_shrink (&scaled_outline, -spread, -spread, -spread, -spread); - /* Grow bounds but don't grow corners */ - graphene_rect_inset (&scaled_outline.bounds, - blur_extra / 2.0, - blur_extra / 2.0); - /* For the center part, we add a few pixels */ - scaled_outline.bounds.size.width += SHADOW_EXTRA_SIZE; - scaled_outline.bounds.size.height += SHADOW_EXTRA_SIZE; - - do_slicing = TRUE; - } - - texture_width = (int)ceilf ((scaled_outline.bounds.size.width + blur_extra) * scale_x); - texture_height = (int)ceilf ((scaled_outline.bounds.size.height + blur_extra) * scale_y); - - scaled_outline.bounds.origin.x = extra_blur_pixels_x; - scaled_outline.bounds.origin.y = extra_blur_pixels_y; - scaled_outline.bounds.size.width = texture_width - (extra_blur_pixels_x * 2); - scaled_outline.bounds.size.height = texture_height - (extra_blur_pixels_y * 2); - - for (guint i = 0; i < G_N_ELEMENTS (scaled_outline.corner); i++) - { - scaled_outline.corner[i].width *= scale_x; - scaled_outline.corner[i].height *= scale_y; - } - - cached_tid = gsk_gl_shadow_library_lookup (job->driver->shadows_library, - &scaled_outline, - blur_radius); - - if (cached_tid == 0) - { - GdkGLContext *context = job->command_queue->context; - GskGLRenderTarget *render_target; - graphene_matrix_t prev_projection; - graphene_rect_t prev_viewport; - guint prev_fbo; - - gsk_gl_driver_create_render_target (job->driver, - texture_width, texture_height, - get_target_format (job, node), - &render_target); - - if (gdk_gl_context_has_feature (context, GDK_GL_FEATURE_DEBUG)) - { - gdk_gl_context_label_object_printf (context, - GL_TEXTURE, - render_target->texture_id, - "Outset Shadow Temp %d", - render_target->texture_id); - gdk_gl_context_label_object_printf (context, - GL_FRAMEBUFFER, - render_target->framebuffer_id, - "Outset Shadow FB Temp %d", - render_target->framebuffer_id); - } - - /* Change state for offscreen */ - gsk_gl_render_job_set_projection_for_size (job, texture_width, texture_height, &prev_projection); - gsk_gl_render_job_set_viewport_for_size (job, texture_width, texture_height, &prev_viewport); - gsk_gl_render_job_set_modelview (job, NULL); - gsk_gl_render_job_push_clip (job, &scaled_outline); - - /* Bind render target and clear it */ - prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id); - gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport); - - /* Draw the outline using color program */ - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, color))) - { - gsk_gl_render_job_draw_with_color (job, 0, 0, texture_width, texture_height, - (guint16[]){ FP16_ONE, FP16_ONE, FP16_ONE, FP16_ONE }); - gsk_gl_render_job_end_draw (job); - } - - /* Reset state from offscreen */ - gsk_gl_render_job_pop_clip (job); - gsk_gl_render_job_pop_modelview (job); - gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL); - gsk_gl_render_job_set_projection (job, &prev_projection); - - /* Now blur the outline */ - init_full_texture_region (&offscreen); - offscreen.texture_id = gsk_gl_driver_release_render_target (job->driver, render_target, FALSE); - blurred_texture_id = blur_offscreen (job, - &offscreen, - texture_width, - texture_height, - blur_radius * scale_x, - blur_radius * scale_y); - - gsk_gl_shadow_library_insert (job->driver->shadows_library, - &scaled_outline, - blur_radius, - blurred_texture_id); - - gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo); - } - else - { - blurred_texture_id = cached_tid; - } - - gsk_gl_render_job_translate_rounded_rect (job, outline, &transformed_outline); - - if (!do_slicing) - { - float min_x = floorf (outline->bounds.origin.x - spread - half_blur_extra + dx); - float min_y = floorf (outline->bounds.origin.y - spread - half_blur_extra + dy); - - offscreen.was_offscreen = TRUE; - offscreen.texture_id = blurred_texture_id; - init_full_texture_region (&offscreen); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, outset_shadow))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - blurred_texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_program_set_uniform_rounded_rect (job->current_program, - UNIFORM_OUTSET_SHADOW_OUTLINE_RECT, 0, - &transformed_outline); - gsk_gl_render_job_draw_offscreen_with_color (job, - &GRAPHENE_RECT_INIT (min_x, - min_y, - texture_width / scale_x, - texture_height / scale_y), - &offscreen, - color); - gsk_gl_render_job_end_draw (job); - } - - return; - } - - /* slicing */ - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, outset_shadow))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - blurred_texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_program_set_uniform_rounded_rect (job->current_program, - UNIFORM_OUTSET_SHADOW_OUTLINE_RECT, 0, - &transformed_outline); - - { - float min_x = floorf (outline->bounds.origin.x - spread - half_blur_extra + dx); - float min_y = floorf (outline->bounds.origin.y - spread - half_blur_extra + dy); - float max_x = ceilf (outline->bounds.origin.x + outline->bounds.size.width + - half_blur_extra + dx + spread); - float max_y = ceilf (outline->bounds.origin.y + outline->bounds.size.height + - half_blur_extra + dy + spread); - const GskGLTextureNineSlice *slices; - float left_width, center_width, right_width; - float top_height, center_height, bottom_height; - GskGLTexture *texture; - - texture = gsk_gl_driver_get_texture_by_id (job->driver, blurred_texture_id); - slices = gsk_gl_texture_get_nine_slice (texture, &scaled_outline, extra_blur_pixels_x, extra_blur_pixels_y); - - offscreen.was_offscreen = TRUE; - - /* Our texture coordinates MUST be scaled, while the actual vertex coords - * MUST NOT be scaled. - */ - - left_width = slices[NINE_SLICE_TOP_LEFT].rect.width / scale_x; - right_width = slices[NINE_SLICE_TOP_RIGHT].rect.width / scale_x; - center_width = (max_x - min_x) - (left_width + right_width); - - top_height = slices[NINE_SLICE_TOP_LEFT].rect.height / scale_y; - bottom_height = slices[NINE_SLICE_BOTTOM_LEFT].rect.height / scale_y; - center_height = (max_y - min_y) - (top_height + bottom_height); - - /* Top left */ - if (nine_slice_is_visible (&slices[NINE_SLICE_TOP_LEFT])) - { - memcpy (&offscreen.area, &slices[NINE_SLICE_TOP_LEFT].area, sizeof offscreen.area); - gsk_gl_render_job_draw_offscreen_with_color (job, - &GRAPHENE_RECT_INIT (min_x, - min_y, - left_width, - top_height), - &offscreen, - color); - } - - /* Top center */ - if (nine_slice_is_visible (&slices[NINE_SLICE_TOP_CENTER])) - { - memcpy (&offscreen.area, &slices[NINE_SLICE_TOP_CENTER].area, sizeof offscreen.area); - gsk_gl_render_job_draw_offscreen_with_color (job, - &GRAPHENE_RECT_INIT (min_x + left_width, - min_y, - center_width, - top_height), - &offscreen, - color); - } - - /* Top right */ - if (nine_slice_is_visible (&slices[NINE_SLICE_TOP_RIGHT])) - { - memcpy (&offscreen.area, &slices[NINE_SLICE_TOP_RIGHT].area, sizeof offscreen.area); - gsk_gl_render_job_draw_offscreen_with_color (job, - &GRAPHENE_RECT_INIT (max_x - right_width, - min_y, - right_width, - top_height), - &offscreen, - color); - } - - /* Bottom right */ - if (nine_slice_is_visible (&slices[NINE_SLICE_BOTTOM_RIGHT])) - { - memcpy (&offscreen.area, &slices[NINE_SLICE_BOTTOM_RIGHT].area, sizeof offscreen.area); - gsk_gl_render_job_draw_offscreen_with_color (job, - &GRAPHENE_RECT_INIT (max_x - right_width, - max_y - bottom_height, - right_width, - bottom_height), - &offscreen, - color); - } - - /* Bottom left */ - if (nine_slice_is_visible (&slices[NINE_SLICE_BOTTOM_LEFT])) - { - memcpy (&offscreen.area, &slices[NINE_SLICE_BOTTOM_LEFT].area, sizeof offscreen.area); - gsk_gl_render_job_draw_offscreen_with_color (job, - &GRAPHENE_RECT_INIT (min_x, - max_y - bottom_height, - left_width, - bottom_height), - &offscreen, - color); - } - - /* Left side */ - if (nine_slice_is_visible (&slices[NINE_SLICE_LEFT_CENTER])) - { - memcpy (&offscreen.area, &slices[NINE_SLICE_LEFT_CENTER].area, sizeof offscreen.area); - gsk_gl_render_job_draw_offscreen_with_color (job, - &GRAPHENE_RECT_INIT (min_x, - min_y + top_height, - left_width, - center_height), - &offscreen, - color); - } - - /* Right side */ - if (nine_slice_is_visible (&slices[NINE_SLICE_RIGHT_CENTER])) - { - memcpy (&offscreen.area, &slices[NINE_SLICE_RIGHT_CENTER].area, sizeof offscreen.area); - gsk_gl_render_job_draw_offscreen_with_color (job, - &GRAPHENE_RECT_INIT (max_x - right_width, - min_y + top_height, - right_width, - center_height), - &offscreen, - color); - } - - /* Bottom side */ - if (nine_slice_is_visible (&slices[NINE_SLICE_BOTTOM_CENTER])) - { - memcpy (&offscreen.area, &slices[NINE_SLICE_BOTTOM_CENTER].area, sizeof offscreen.area); - gsk_gl_render_job_draw_offscreen_with_color (job, - &GRAPHENE_RECT_INIT (min_x + left_width, - max_y - bottom_height, - center_width, - bottom_height), - &offscreen, - color); - } - - /* Middle */ - if (nine_slice_is_visible (&slices[NINE_SLICE_CENTER])) - { - if (!gsk_rounded_rect_contains_rect (outline, &GRAPHENE_RECT_INIT (min_x + left_width, - min_y + top_height, - center_width, - center_height))) - { - memcpy (&offscreen.area, &slices[NINE_SLICE_CENTER].area, sizeof offscreen.area); - gsk_gl_render_job_draw_offscreen_with_color (job, - &GRAPHENE_RECT_INIT (min_x + left_width, - min_y + top_height, - center_width, - center_height), - &offscreen, - color); - } - } - } - - gsk_gl_render_job_end_draw (job); - } -} - -static inline gboolean G_GNUC_PURE -equal_texture_nodes (const GskRenderNode *node1, - const GskRenderNode *node2) -{ - if (GSK_RENDER_NODE_TYPE (node1) != GSK_TEXTURE_NODE || - GSK_RENDER_NODE_TYPE (node2) != GSK_TEXTURE_NODE) - return FALSE; - - if (gsk_texture_node_get_texture (node1) != - gsk_texture_node_get_texture (node2)) - return FALSE; - - return gsk_rect_equal (&node1->bounds, &node2->bounds); -} - -static inline void -gsk_gl_render_job_visit_cross_fade_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GskRenderNode *start_node = gsk_cross_fade_node_get_start_child (node); - const GskRenderNode *end_node = gsk_cross_fade_node_get_end_child (node); - float progress = gsk_cross_fade_node_get_progress (node); - GskGLRenderOffscreen offscreen_start = {0}; - GskGLRenderOffscreen offscreen_end = {0}; - - g_assert (progress > 0.0); - g_assert (progress < 1.0); - - offscreen_start.force_offscreen = TRUE; - offscreen_start.reset_clip = TRUE; - offscreen_start.bounds = &node->bounds; - - offscreen_end.force_offscreen = TRUE; - offscreen_end.reset_clip = TRUE; - offscreen_end.bounds = &node->bounds; - - gsk_gl_render_job_set_modelview (job, gsk_transform_scale (NULL, fabsf (job->scale_x), fabsf (job->scale_y))); - - if (!gsk_gl_render_job_visit_node_with_offscreen (job, start_node, &offscreen_start)) - { - gsk_gl_render_job_pop_modelview (job); - - gsk_gl_render_job_visit_node (job, end_node); - return; - } - - g_assert (offscreen_start.texture_id); - - if (!gsk_gl_render_job_visit_node_with_offscreen (job, end_node, &offscreen_end)) - { - float prev_alpha; - - gsk_gl_render_job_pop_modelview (job); - - prev_alpha = gsk_gl_render_job_set_alpha (job, job->alpha * progress); - gsk_gl_render_job_visit_node (job, start_node); - gsk_gl_render_job_set_alpha (job, prev_alpha); - return; - } - - gsk_gl_render_job_pop_modelview (job); - - g_assert (offscreen_end.texture_id); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, cross_fade))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - offscreen_start.texture_id); - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_CROSS_FADE_SOURCE2, 0, - GL_TEXTURE_2D, - GL_TEXTURE1, - offscreen_end.texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_program_set_uniform1f (job->current_program, - UNIFORM_CROSS_FADE_PROGRESS, 0, - progress); - gsk_gl_render_job_draw_offscreen (job, &node->bounds, &offscreen_end); - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_opacity_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GskRenderNode *child = gsk_opacity_node_get_child (node); - float opacity = gsk_opacity_node_get_opacity (node); - float new_alpha = job->alpha * opacity; - - if (!ALPHA_IS_CLEAR (new_alpha)) - { - float prev_alpha = gsk_gl_render_job_set_alpha (job, new_alpha); - - if (!gsk_render_node_use_offscreen_for_opacity (child)) - { - gsk_gl_render_job_visit_node (job, child); - gsk_gl_render_job_set_alpha (job, prev_alpha); - } - else - { - GskGLRenderOffscreen offscreen = {0}; - - offscreen.bounds = &child->bounds; - offscreen.force_offscreen = TRUE; - offscreen.reset_clip = TRUE; - - /* Note: offscreen rendering resets alpha to 1.0 */ - if (!gsk_gl_render_job_visit_node_with_offscreen (job, child, &offscreen)) - return; - - g_assert (offscreen.texture_id); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - offscreen.texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_offscreen (job, &node->bounds, &offscreen); - gsk_gl_render_job_end_draw (job); - } - } - - gsk_gl_render_job_set_alpha (job, prev_alpha); - } -} - -static inline int -compute_phase_and_pos (float value, float *pos) -{ - float v; - - *pos = floorf (value); - - v = value - *pos; - - if (v < 0.125) - return 0; - else if (v < 0.375) - return 1; - else if (v < 0.625) - return 2; - else if (v < 0.875) - return 3; - else - { - *pos += 1; - return 0; - } -} - -static inline void -gsk_gl_render_job_visit_text_node (GskGLRenderJob *job, - const GskRenderNode *node, - const GdkRGBA *color, - gboolean force_color) -{ - const PangoFont *font = gsk_text_node_get_font (node); - const PangoGlyphInfo *glyphs = gsk_text_node_get_glyphs (node, NULL); - const graphene_point_t *offset = gsk_text_node_get_offset (node); - float text_scale = MAX (fabsf (job->scale_x), fabsf (job->scale_y)); /* TODO: Fix for uneven scales? */ - guint num_glyphs = gsk_text_node_get_num_glyphs (node); - float x = offset->x + job->offset_x; - float y = offset->y + job->offset_y; - GskGLGlyphLibrary *library = job->driver->glyphs_library; - GskGLCommandBatch *batch; - int x_position = 0; - GskGLGlyphKey lookup; - guint last_texture = 0; - GskGLDrawVertex *vertices; - guint used = 0; - guint16 nc[4] = { FP16_MINUS_ONE, FP16_MINUS_ONE, FP16_MINUS_ONE, FP16_MINUS_ONE }; - guint16 cc[4]; - const guint16 *c; - const PangoGlyphInfo *gi; - guint i; - int yshift; - float ypos; - - if (num_glyphs == 0) - return; - - if ((force_color || !gsk_text_node_has_color_glyphs (node)) && - RGBA_IS_CLEAR (color)) - return; - - rgba_to_half (color, cc); - - lookup.font = (PangoFont *)font; - lookup.scale = (guint) (text_scale * 1024); - - yshift = compute_phase_and_pos (y, &ypos); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, coloring))) - { - batch = gsk_gl_command_queue_get_batch (job->command_queue); - vertices = gsk_gl_command_queue_add_n_vertices (job->command_queue, num_glyphs); - - /* We use one quad per character */ - for (i = 0, gi = glyphs; i < num_glyphs; i++, gi++) - { - const GskGLGlyphValue *glyph; - float glyph_x, glyph_y, glyph_x2, glyph_y2; - float tx, ty, tx2, ty2; - float cx; - float cy; - guint texture_id; - - lookup.glyph = gi->glyph; - - /* If the glyph has color, we don't need to recolor anything. - * We tell the shader by setting the color to vec4(-1). - */ - if (!force_color && gi->attr.is_color) - c = nc; - else - c = cc; - - cx = (float)(x_position + gi->geometry.x_offset) / PANGO_SCALE; - lookup.xshift = compute_phase_and_pos (x + cx, &cx); - - if G_UNLIKELY (gi->geometry.y_offset != 0) - { - cy = (float)(gi->geometry.y_offset) / PANGO_SCALE; - lookup.yshift = compute_phase_and_pos (y + cy, &cy); - } - else - { - lookup.yshift = yshift; - cy = ypos; - } - - x_position += gi->geometry.width; - - texture_id = gsk_gl_glyph_library_lookup_or_add (library, &lookup, &glyph); - if G_UNLIKELY (texture_id == 0) - continue; - - if G_UNLIKELY (last_texture != texture_id || batch->draw.vbo_count + GSK_GL_N_VERTICES > 0xffff) - { - if G_LIKELY (last_texture != 0) - { - guint vbo_offset = batch->draw.vbo_offset + batch->draw.vbo_count; - - /* Since we have batched added our VBO vertices to avoid repeated - * calls to the buffer, we need to manually tweak the vbo offset - * of the new batch as otherwise it will point at the end of our - * vbo array. - */ - gsk_gl_render_job_split_draw (job); - batch = gsk_gl_command_queue_get_batch (job->command_queue); - batch->draw.vbo_offset = vbo_offset; - } - - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - texture_id); - job->source_is_glyph_atlas = TRUE; - last_texture = texture_id; - } - - tx = glyph->entry.area.x; - ty = glyph->entry.area.y; - tx2 = glyph->entry.area.x2; - ty2 = glyph->entry.area.y2; - - glyph_x = cx + glyph->ink_rect.x; - glyph_y = cy + glyph->ink_rect.y; - glyph_x2 = glyph_x + glyph->ink_rect.width; - glyph_y2 = glyph_y + glyph->ink_rect.height; - - *(vertices++) = (GskGLDrawVertex) { .position = { glyph_x, glyph_y }, .uv = { tx, ty }, .color = { c[0], c[1], c[2], c[3] } }; - *(vertices++) = (GskGLDrawVertex) { .position = { glyph_x, glyph_y2 }, .uv = { tx, ty2 }, .color = { c[0], c[1], c[2], c[3] } }; - *(vertices++) = (GskGLDrawVertex) { .position = { glyph_x2, glyph_y }, .uv = { tx2, ty }, .color = { c[0], c[1], c[2], c[3] } }; - - *(vertices++) = (GskGLDrawVertex) { .position = { glyph_x2, glyph_y2 }, .uv = { tx2, ty2 }, .color = { c[0], c[1], c[2], c[3] } }; - *(vertices++) = (GskGLDrawVertex) { .position = { glyph_x, glyph_y2 }, .uv = { tx, ty2 }, .color = { c[0], c[1], c[2], c[3] } }; - *(vertices++) = (GskGLDrawVertex) { .position = { glyph_x2, glyph_y }, .uv = { tx2, ty }, .color = { c[0], c[1], c[2], c[3] } }; - - batch->draw.vbo_count += GSK_GL_N_VERTICES; - used++; - } - - if (used != num_glyphs) - gsk_gl_command_queue_retract_n_vertices (job->command_queue, num_glyphs - used); - - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_shadow_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const gsize n_shadows = gsk_shadow_node_get_n_shadows (node); - const GskRenderNode *original_child = gsk_shadow_node_get_child (node); - const GskRenderNode *shadow_child = original_child; - - /* Shadow nodes recolor every pixel of the source texture, but leave the alpha in tact. - * If the child is a color matrix node that doesn't touch the alpha, we can throw that away. */ - if (GSK_RENDER_NODE_TYPE (shadow_child) == GSK_COLOR_MATRIX_NODE && - !color_matrix_modifies_alpha (shadow_child)) - shadow_child = gsk_color_matrix_node_get_child (shadow_child); - - for (guint i = 0; i < n_shadows; i++) - { - const GskShadow *shadow = gsk_shadow_node_get_shadow (node, i); - const float dx = shadow->dx; - const float dy = shadow->dy; - GskGLRenderOffscreen offscreen = {0}; - graphene_rect_t bounds; - guint16 color[4]; - - if (RGBA_IS_CLEAR (&shadow->color)) - continue; - - if (node_is_invisible (shadow_child)) - continue; - - if (shadow->radius == 0 && - GSK_RENDER_NODE_TYPE (shadow_child) == GSK_TEXT_NODE) - { - if (dx != 0 || dy != 0) - { - gsk_gl_render_job_offset (job, dx, dy); - gsk_gl_render_job_visit_text_node (job, shadow_child, &shadow->color, TRUE); - gsk_gl_render_job_offset (job, -dx, -dy); - } - continue; - } - - if (shadow->radius > 0) - { - float min_x; - float min_y; - float max_x; - float max_y; - - offscreen.do_not_cache = TRUE; - - blur_node (job, - &offscreen, - shadow_child, - shadow->radius, - &min_x, &max_x, - &min_y, &max_y); - - bounds.origin.x = min_x - job->offset_x; - bounds.origin.y = min_y - job->offset_y; - bounds.size.width = max_x - min_x; - bounds.size.height = max_y - min_y; - - offscreen.was_offscreen = TRUE; - } - else - { - offscreen.bounds = &shadow_child->bounds; - offscreen.reset_clip = TRUE; - offscreen.do_not_cache = TRUE; - - if (!gsk_gl_render_job_visit_node_with_offscreen (job, shadow_child, &offscreen)) - g_assert_not_reached (); - - bounds = shadow_child->bounds; - } - - gsk_gl_render_job_offset (job, dx, dy); - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, coloring))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - offscreen.texture_id); - job->source_is_glyph_atlas = FALSE; - rgba_to_half (&shadow->color, color); - gsk_gl_render_job_draw_offscreen_with_color (job, &bounds, &offscreen, color); - gsk_gl_render_job_end_draw (job); - } - gsk_gl_render_job_offset (job, -dx, -dy); - } - - /* Now draw the child normally */ - gsk_gl_render_job_visit_node (job, original_child); -} - -static inline void -gsk_gl_render_job_visit_blur_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const 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; - - offscreen.texture_id = gsk_gl_driver_lookup_texture (job->driver, &key, NULL); - 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_gl_driver_cache_texture (job->driver, &key, offscreen.texture_id); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - offscreen.texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_coords (job, - min_x, min_y, max_x, max_y, - 0, 1, 1, 0, - (guint16[]) { FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO } ); - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_blend_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GskRenderNode *top_child = gsk_blend_node_get_top_child (node); - const GskRenderNode *bottom_child = gsk_blend_node_get_bottom_child (node); - GskGLRenderOffscreen top_offscreen = {0}; - GskGLRenderOffscreen bottom_offscreen = {0}; - - top_offscreen.bounds = &node->bounds; - top_offscreen.force_offscreen = TRUE; - top_offscreen.reset_clip = TRUE; - - bottom_offscreen.bounds = &node->bounds; - bottom_offscreen.force_offscreen = TRUE; - bottom_offscreen.reset_clip = TRUE; - - gsk_gl_render_job_set_modelview (job, gsk_transform_scale (NULL, fabsf (job->scale_x), fabsf (job->scale_y))); - - /* TODO: We create 2 textures here as big as the blend node, but both the - * start and the end node might be a lot smaller than that. */ - if (!gsk_gl_render_job_visit_node_with_offscreen (job, bottom_child, &bottom_offscreen)) - { - gsk_gl_render_job_pop_modelview (job); - - gsk_gl_render_job_visit_node (job, top_child); - return; - } - - g_assert (bottom_offscreen.was_offscreen); - - if (!gsk_gl_render_job_visit_node_with_offscreen (job, top_child, &top_offscreen)) - { - gsk_gl_render_job_pop_modelview (job); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - bottom_offscreen.texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_offscreen (job, &node->bounds, &bottom_offscreen); - gsk_gl_render_job_end_draw (job); - } - return; - } - - g_assert (top_offscreen.was_offscreen); - - gsk_gl_render_job_pop_modelview (job); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blend))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - bottom_offscreen.texture_id); - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_BLEND_SOURCE2, 0, - GL_TEXTURE_2D, - GL_TEXTURE1, - top_offscreen.texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_program_set_uniform1i (job->current_program, - UNIFORM_BLEND_MODE, 0, - gsk_blend_node_get_blend_mode (node)); - gsk_gl_render_job_draw_offscreen_rect (job, &node->bounds); - gsk_gl_render_job_end_draw (job); - } -} - -static gboolean -gsk_gl_render_job_texture_mask_for_color (GskGLRenderJob *job, - const GskRenderNode *mask, - const GskRenderNode *color, - const graphene_rect_t *bounds) -{ - int max_texture_size = job->command_queue->max_texture_size; - GdkTexture *texture = gsk_texture_node_get_texture (mask); - GdkRGBA rgba; - - get_color_node_color_as_srgb (color, &rgba); - if (RGBA_IS_CLEAR (&rgba)) - return TRUE; - - if G_LIKELY (texture->width <= max_texture_size && - texture->height <= max_texture_size && - gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, coloring))) - { - GskGLRenderOffscreen offscreen = {0}; - float scale_x = mask->bounds.size.width / texture->width; - float scale_y = mask->bounds.size.height / texture->height; - gboolean use_mipmap; - guint16 cc[4]; - - use_mipmap = (scale_x * fabsf (job->scale_x)) < 0.5 || - (scale_y * fabsf (job->scale_y)) < 0.5; - - rgba_to_half (&rgba, cc); - gsk_gl_render_job_upload_texture (job, texture, use_mipmap, &offscreen); - gsk_gl_program_set_uniform_texture_with_sync (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - offscreen.texture_id, - offscreen.has_mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR, - GL_LINEAR, - offscreen.sync); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_offscreen_with_color (job, bounds, &offscreen, cc); - gsk_gl_render_job_end_draw (job); - - return TRUE; - } - - return FALSE; -} - -static inline void -gsk_gl_render_job_visit_mask_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GskRenderNode *source = gsk_mask_node_get_source (node); - const GskRenderNode *mask = gsk_mask_node_get_mask (node); - GskGLRenderOffscreen source_offscreen = {0}; - GskGLRenderOffscreen mask_offscreen = {0}; - - /* If the mask is a texture and the source is a color node - * then we can take a shortcut and avoid offscreens. - */ - if (GSK_RENDER_NODE_TYPE (mask) == GSK_TEXTURE_NODE && - GSK_RENDER_NODE_TYPE (source) == GSK_COLOR_NODE && - gsk_mask_node_get_mask_mode (node) == GSK_MASK_MODE_ALPHA) - { - if (gsk_gl_render_job_texture_mask_for_color (job, mask, source, &node->bounds)) - return; - } - - source_offscreen.bounds = &node->bounds; - source_offscreen.force_offscreen = TRUE; - source_offscreen.reset_clip = TRUE; - - mask_offscreen.bounds = &node->bounds; - mask_offscreen.force_offscreen = TRUE; - mask_offscreen.reset_clip = TRUE; - mask_offscreen.do_not_cache = TRUE; - - gsk_gl_render_job_set_modelview (job, gsk_transform_scale (NULL, fabsf (job->scale_x), fabsf (job->scale_y))); - - /* TODO: We create 2 textures here as big as the mask node, but both - * nodes might be a lot smaller than that. - */ - if (!gsk_gl_render_job_visit_node_with_offscreen (job, source, &source_offscreen)) - { - gsk_gl_render_job_pop_modelview (job); - gsk_gl_render_job_visit_node (job, source); - return; - } - - g_assert (source_offscreen.was_offscreen); - - if (!gsk_gl_render_job_visit_node_with_offscreen (job, mask, &mask_offscreen)) - { - gsk_gl_render_job_pop_modelview (job); - if (gsk_mask_node_get_mask_mode (node) == GSK_MASK_MODE_INVERTED_ALPHA) - gsk_gl_render_job_visit_node (job, source); - return; - } - - g_assert (mask_offscreen.was_offscreen); - - gsk_gl_render_job_pop_modelview (job); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, mask))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - source_offscreen.texture_id); - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_MASK_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE1, - mask_offscreen.texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_program_set_uniform1i (job->current_program, - UNIFORM_MASK_MODE, 0, - gsk_mask_node_get_mask_mode (node)); - gsk_gl_render_job_draw_offscreen_rect (job, &node->bounds); - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_color_matrix_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GskRenderNode *child = gsk_color_matrix_node_get_child (node); - GskGLRenderOffscreen offscreen = {0}; - float offset[4]; - - if (node_is_invisible (child)) - return; - - offscreen.bounds = &node->bounds; - offscreen.reset_clip = TRUE; - - if (!gsk_gl_render_job_visit_node_with_offscreen (job, child, &offscreen)) - g_assert_not_reached (); - - g_assert (offscreen.texture_id > 0); - - graphene_vec4_to_float (gsk_color_matrix_node_get_color_offset (node), offset); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, color_matrix))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - offscreen.texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_program_set_uniform_matrix (job->current_program, - UNIFORM_COLOR_MATRIX_COLOR_MATRIX, 0, - gsk_color_matrix_node_get_color_matrix (node)); - gsk_gl_program_set_uniform4fv (job->current_program, - UNIFORM_COLOR_MATRIX_COLOR_OFFSET, 0, - 1, - offset); - gsk_gl_render_job_draw_offscreen (job, &node->bounds, &offscreen); - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_gl_shader_node_fallback (GskGLRenderJob *job, - const GskRenderNode *node) -{ - guint16 pink[4] = { 15360, 13975, 14758, 15360 }; /* 255 105 180 */ - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, color))) - { - gsk_gl_render_job_draw_rect_with_color (job, &node->bounds, pink); - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_gl_shader_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - GError *error = NULL; - GskGLShader *shader; - GskGLProgram *program; - int n_children; - - shader = gsk_gl_shader_node_get_shader (node); - program = gsk_gl_driver_lookup_shader (job->driver, shader, &error); - n_children = gsk_gl_shader_node_get_n_children (node); - - if G_UNLIKELY (program == NULL) - { - if (g_object_get_data (G_OBJECT (shader), "gsk-did-warn") == NULL) - { - g_object_set_data (G_OBJECT (shader), "gsk-did-warn", GUINT_TO_POINTER (1)); - g_warning ("Failed to compile gl shader: %s", error->message); - } - gsk_gl_render_job_visit_gl_shader_node_fallback (job, node); - g_clear_error (&error); - } - else - { - GskGLRenderOffscreen offscreens[4] = {{0}}; - const GskGLUniform *uniforms; - const guint8 *base; - GBytes *args; - int n_uniforms; - - g_assert (n_children < G_N_ELEMENTS (offscreens)); - - for (guint i = 0; i < n_children; i++) - { - const GskRenderNode *child = gsk_gl_shader_node_get_child (node, i); - - offscreens[i].bounds = &node->bounds; - offscreens[i].force_offscreen = TRUE; - offscreens[i].reset_clip = TRUE; - - if (!gsk_gl_render_job_visit_node_with_offscreen (job, child, &offscreens[i])) - return; - } - - args = gsk_gl_shader_node_get_args (node); - base = g_bytes_get_data (args, NULL); - uniforms = gsk_gl_shader_get_uniforms (shader, &n_uniforms); - - if (gsk_gl_render_job_begin_draw (job, program)) - { - for (guint i = 0; i < n_children; i++) - gsk_gl_program_set_uniform_texture (program, - UNIFORM_CUSTOM_TEXTURE1 + i, 0, - GL_TEXTURE_2D, - GL_TEXTURE0 + i, - offscreens[i].texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_program_set_uniform2f (program, - UNIFORM_CUSTOM_SIZE, 0, - node->bounds.size.width, - node->bounds.size.height); - for (guint i = 0; i < n_uniforms; i++) - { - const GskGLUniform *u = &uniforms[i]; - const guint8 *data = base + u->offset; - - switch (u->type) - { - default: - case GSK_GL_UNIFORM_TYPE_NONE: - break; - case GSK_GL_UNIFORM_TYPE_FLOAT: - gsk_gl_uniform_state_set1fv (job->command_queue->uniforms, - program->program_info, - UNIFORM_CUSTOM_ARG0 + i, - 0, 1, (const float *)data); - break; - case GSK_GL_UNIFORM_TYPE_INT: - gsk_gl_uniform_state_set1i (job->command_queue->uniforms, - program->program_info, - UNIFORM_CUSTOM_ARG0 + i, - 0, *(const gint32 *)data); - break; - case GSK_GL_UNIFORM_TYPE_UINT: - case GSK_GL_UNIFORM_TYPE_BOOL: - gsk_gl_uniform_state_set1ui (job->command_queue->uniforms, - program->program_info, - UNIFORM_CUSTOM_ARG0 + i, - 0, *(const guint32 *)data); - break; - case GSK_GL_UNIFORM_TYPE_VEC2: - gsk_gl_uniform_state_set2fv (job->command_queue->uniforms, - program->program_info, - UNIFORM_CUSTOM_ARG0 + i, - 0, 1, (const float *)data); - break; - case GSK_GL_UNIFORM_TYPE_VEC3: - gsk_gl_uniform_state_set3fv (job->command_queue->uniforms, - program->program_info, - UNIFORM_CUSTOM_ARG0 + i, - 0, 1, (const float *)data); - break; - case GSK_GL_UNIFORM_TYPE_VEC4: - gsk_gl_uniform_state_set4fv (job->command_queue->uniforms, - program->program_info, - UNIFORM_CUSTOM_ARG0 + i, - 0, 1, (const float *)data); - break; - } - } - gsk_gl_render_job_draw_offscreen_rect (job, &node->bounds); - gsk_gl_render_job_end_draw (job); - } - } -G_GNUC_END_IGNORE_DEPRECATIONS -} - -static void -gsk_gl_render_job_upload_texture (GskGLRenderJob *job, - GdkTexture *texture, - gboolean ensure_mipmap, - GskGLRenderOffscreen *offscreen) -{ - /* Don't put GL or dmabuf textures into icon caches, they are already on the GPU side */ - if (!ensure_mipmap && - gsk_gl_texture_library_can_cache ((GskGLTextureLibrary *)job->driver->icons_library, - texture->width, - texture->height) && - !(GDK_IS_GL_TEXTURE (texture) || GDK_IS_DMABUF_TEXTURE (texture))) - { - const GskGLIconData *icon_data; - - gsk_gl_icon_library_lookup_or_add (job->driver->icons_library, texture, &icon_data); - offscreen->texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (icon_data); - memcpy (&offscreen->area, &icon_data->entry.area, sizeof offscreen->area); - offscreen->has_mipmap = FALSE; - } - else - { - /* Only generate a mipmap if it does not make use reupload - * a GL texture which we could otherwise use directly. - */ - if (GDK_IS_GL_TEXTURE (texture) && - gdk_gl_context_is_shared (gdk_gl_texture_get_context (GDK_GL_TEXTURE (texture)), - job->command_queue->context)) - ensure_mipmap = gdk_gl_texture_has_mipmap (GDK_GL_TEXTURE (texture)); - else if (GDK_IS_DMABUF_TEXTURE (texture)) - ensure_mipmap = FALSE; - - offscreen->texture_id = gsk_gl_driver_load_texture (job->driver, texture, ensure_mipmap); - init_full_texture_region (offscreen); - offscreen->has_mipmap = ensure_mipmap; - - if (GDK_IS_GL_TEXTURE (texture) && - offscreen->texture_id == gdk_gl_texture_get_id (GDK_GL_TEXTURE (texture))) - offscreen->sync = gdk_gl_texture_get_sync (GDK_GL_TEXTURE (texture)); - } -} - -static inline void -gsk_gl_render_job_visit_texture (GskGLRenderJob *job, - GdkTexture *texture, - const graphene_rect_t *bounds) -{ - int max_texture_size = job->command_queue->max_texture_size; - float scale_x = bounds->size.width / texture->width; - float scale_y = bounds->size.height / texture->height; - gboolean use_mipmap; - - use_mipmap = (scale_x * fabsf (job->scale_x)) < 0.5 || - (scale_y * fabsf (job->scale_y)) < 0.5; - - if G_LIKELY (texture->width <= max_texture_size && - texture->height <= max_texture_size) - { - GskGLRenderOffscreen offscreen = {0}; - - gsk_gl_render_job_upload_texture (job, texture, use_mipmap, &offscreen); - - g_assert (offscreen.texture_id); - g_assert (offscreen.was_offscreen == FALSE); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - { - gsk_gl_program_set_uniform_texture_with_sync (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - offscreen.texture_id, - offscreen.has_mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR, - GL_LINEAR, - offscreen.sync); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_offscreen (job, bounds, &offscreen); - gsk_gl_render_job_end_draw (job); - } - } - else - { - float min_x = job->offset_x + bounds->origin.x; - float min_y = job->offset_y + bounds->origin.y; - GskGLTextureSlice *slices = NULL; - guint n_slices = 0; - - gsk_gl_driver_slice_texture (job->driver, texture, use_mipmap, &slices, &n_slices); - - g_assert (slices != NULL); - g_assert (n_slices > 0); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - { - for (unsigned int i = 0; i < n_slices; i++) - { - const GskGLTextureSlice *slice = &slices[i]; - float x1, x2, y1, y2; - - x1 = min_x + (scale_x * slice->rect.x); - x2 = x1 + (slice->rect.width * scale_x); - y1 = min_y + (scale_y * slice->rect.y); - y2 = y1 + (slice->rect.height * scale_y); - - if (i > 0) - gsk_gl_render_job_split_draw (job); - gsk_gl_program_set_uniform_texture_with_filter (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - slice->texture_id, - use_mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR, - GL_LINEAR); - job->source_is_glyph_atlas = FALSE; - - gsk_gl_render_job_draw_coords (job, - x1, y1, x2, y2, - slice->area.x, slice->area.y, - slice->area.x2, slice->area.y2, - (guint16[]) { FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO }); - } - - gsk_gl_render_job_end_draw (job); - } - } -} - -static inline void -gsk_gl_render_job_visit_texture_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - GdkTexture *texture = gsk_texture_node_get_texture (node); - const graphene_rect_t *bounds = &node->bounds; - - gsk_gl_render_job_visit_texture (job, texture, bounds); -} - -static inline void -gsk_gl_render_job_visit_texture_scale_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - GdkTexture *texture = gsk_texture_scale_node_get_texture (node); - const graphene_rect_t *bounds = &node->bounds; - GskScalingFilter filter = gsk_texture_scale_node_get_filter (node); - int min_filters[] = { GL_LINEAR, GL_NEAREST, GL_LINEAR_MIPMAP_LINEAR }; - int mag_filters[] = { GL_LINEAR, GL_NEAREST, GL_LINEAR }; - int min_filter = min_filters[filter]; - int mag_filter = mag_filters[filter]; - int max_texture_size = job->command_queue->max_texture_size; - graphene_rect_t clip_rect; - GskGLRenderTarget *render_target; - graphene_rect_t viewport; - graphene_rect_t prev_viewport; - graphene_matrix_t prev_projection; - float prev_alpha; - guint prev_fbo; - float u0, u1, v0, v1; - GskTextureKey key; - guint texture_id; - gboolean need_mipmap; - gboolean has_mipmap; - - gsk_gl_render_job_untransform_bounds (job, &job->current_clip->rect.bounds, &clip_rect); - - if (!gsk_rect_intersection (bounds, &clip_rect, &clip_rect)) - return; - - key.pointer = node; - key.pointer_is_child = TRUE; - key.parent_rect = clip_rect; - key.scale_x = 1.; - key.scale_y = 1.; - - need_mipmap = (filter == GSK_SCALING_FILTER_TRILINEAR); - - texture_id = gsk_gl_driver_lookup_texture (job->driver, &key, &has_mipmap); - - if (texture_id != 0 && (!need_mipmap || has_mipmap)) - goto render_texture; - - viewport = GRAPHENE_RECT_INIT (0, 0, - clip_rect.size.width, - clip_rect.size.height); - - if (!gsk_gl_driver_create_render_target (job->driver, - (int) ceilf (clip_rect.size.width), - (int) ceilf (clip_rect.size.height), - get_target_format (job, node), - &render_target)) - { - gsk_gl_render_job_visit_texture (job, texture, bounds); - return; - } - - gsk_gl_render_job_set_viewport (job, &viewport, &prev_viewport); - gsk_gl_render_job_set_projection_from_rect (job, &viewport, &prev_projection); - gsk_gl_render_job_set_modelview (job, NULL); - prev_alpha = gsk_gl_render_job_set_alpha (job, 1.0f); - gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (viewport)); - - prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id); - gsk_gl_command_queue_clear (job->command_queue, 0, &viewport); - - if G_LIKELY (texture->width <= max_texture_size && - texture->height <= max_texture_size) - { - gpointer sync; - - texture_id = gsk_gl_driver_load_texture (job->driver, texture, need_mipmap); - - if (GDK_IS_GL_TEXTURE (texture) && texture_id == gdk_gl_texture_get_id (GDK_GL_TEXTURE (texture))) - sync = gdk_gl_texture_get_sync (GDK_GL_TEXTURE (texture)); - else - sync = NULL; - - u0 = (clip_rect.origin.x - bounds->origin.x) / bounds->size.width; - v0 = (clip_rect.origin.y - bounds->origin.y) / bounds->size.height; - u1 = (clip_rect.origin.x + clip_rect.size.width - bounds->origin.x) / bounds->size.width; - v1 = (clip_rect.origin.y + clip_rect.size.height - bounds->origin.y) / bounds->size.height; - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - { - gsk_gl_program_set_uniform_texture_with_sync (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - texture_id, - min_filter, - mag_filter, - sync); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_coords (job, - 0, 0, clip_rect.size.width, clip_rect.size.height, - u0, v0, u1, v1, - (guint16[]) { FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO }); - gsk_gl_render_job_end_draw (job); - } - } - else - { - float scale_x = bounds->size.width / texture->width; - float scale_y = bounds->size.height / texture->height; - GskGLTextureSlice *slices = NULL; - guint n_slices = 0; - - gsk_gl_driver_slice_texture (job->driver, texture, need_mipmap, &slices, &n_slices); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - { - for (guint i = 0; i < n_slices; i++) - { - const GskGLTextureSlice *slice = &slices[i]; - graphene_rect_t slice_bounds; - - slice_bounds.origin.x = bounds->origin.x - clip_rect.origin.x + slice->rect.x * scale_x; - slice_bounds.origin.y = bounds->origin.y - clip_rect.origin.y + slice->rect.y * scale_y; - slice_bounds.size.width = slice->rect.width * scale_x; - slice_bounds.size.height = slice->rect.height * scale_y; - - if (!gsk_rect_intersects (&slice_bounds, &viewport)) - continue; - - if (i > 0) - gsk_gl_render_job_split_draw (job); - - gsk_gl_program_set_uniform_texture_with_filter (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - slice->texture_id, - min_filter, - mag_filter); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_coords (job, - slice_bounds.origin.x, - slice_bounds.origin.y, - slice_bounds.origin.x + slice_bounds.size.width, - slice_bounds.origin.y + slice_bounds.size.height, - slice->area.x, slice->area.y, - slice->area.x2, slice->area.y2, - (guint16[]){ FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO } ); - } - - gsk_gl_render_job_end_draw (job); - } - } - - gsk_gl_render_job_pop_clip (job); - gsk_gl_render_job_pop_modelview (job); - gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL); - gsk_gl_render_job_set_projection (job, &prev_projection); - gsk_gl_render_job_set_alpha (job, prev_alpha); - gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo); - - texture_id = gsk_gl_driver_release_render_target (job->driver, render_target, FALSE); - gsk_gl_driver_cache_texture (job->driver, &key, texture_id); - -render_texture: - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_coords (job, - job->offset_x + clip_rect.origin.x, - job->offset_y + clip_rect.origin.y, - job->offset_x + clip_rect.origin.x + clip_rect.size.width, - job->offset_y + clip_rect.origin.y + clip_rect.size.height, - 0, clip_rect.size.width / ceilf (clip_rect.size.width), - clip_rect.size.height / ceilf (clip_rect.size.height), 0, - (guint16[]){ FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO } ); - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_repeat_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - const GskRenderNode *child = gsk_repeat_node_get_child (node); - const graphene_rect_t *child_bounds = gsk_repeat_node_get_child_bounds (node); - GskGLRenderOffscreen offscreen = {0}; - - if (node_is_invisible (child)) - return; - - if (!gsk_rect_equal (child_bounds, &child->bounds)) - { - /* TODO: implement these repeat nodes. */ - gsk_gl_render_job_visit_as_fallback (job, node); - return; - } - - /* If the size of the repeat node is smaller than the size of the - * child node, we don't repeat at all and can just draw that part - * of the child texture... */ - if (gsk_rect_contains_rect (child_bounds, &node->bounds)) - { - gsk_gl_render_job_visit_clipped_child (job, child, &node->bounds); - return; - } - - offscreen.bounds = &child->bounds; - offscreen.reset_clip = TRUE; - - if (!gsk_gl_render_job_visit_node_with_offscreen (job, child, &offscreen)) - g_assert_not_reached (); - - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, repeat))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - offscreen.texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_program_set_uniform4f (job->current_program, - UNIFORM_REPEAT_CHILD_BOUNDS, 0, - (node->bounds.origin.x - child_bounds->origin.x) / child_bounds->size.width, - (node->bounds.origin.y - child_bounds->origin.y) / child_bounds->size.height, - node->bounds.size.width / child_bounds->size.width, - node->bounds.size.height / child_bounds->size.height); - gsk_gl_program_set_uniform4f (job->current_program, - UNIFORM_REPEAT_TEXTURE_RECT, 0, - offscreen.area.x, - offscreen.was_offscreen ? offscreen.area.y2 : offscreen.area.y, - offscreen.area.x2, - offscreen.was_offscreen ? offscreen.area.y : offscreen.area.y2); - gsk_gl_render_job_draw_offscreen (job, &node->bounds, &offscreen); - gsk_gl_render_job_end_draw (job); - } -} - -static inline void -gsk_gl_render_job_visit_subsurface_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - GdkSubsurface *subsurface; - - subsurface = (GdkSubsurface *) gsk_subsurface_node_get_subsurface (node); - - if (subsurface && - gdk_subsurface_get_texture (subsurface) && - gdk_subsurface_get_parent (subsurface) == gdk_gl_context_get_surface (job->command_queue->context)) - { - if (!gdk_subsurface_is_above_parent (subsurface)) - { - if (job->offscreen_count > 0) - { - GDK_DISPLAY_DEBUG (gdk_gl_context_get_display (job->command_queue->context), OFFLOAD, "Hiding subsurface %p in offscreen context", subsurface); - gdk_subsurface_detach (subsurface); - gsk_gl_render_job_visit_node (job, gsk_subsurface_node_get_child (node)); - } - else if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, color))) - { - /* Clear the area so we can see through */ - GskGLCommandBatch *batch; - guint16 color[4]; - rgba_to_half (&GDK_RGBA_TRANSPARENT, color); - - batch = gsk_gl_command_queue_get_batch (job->command_queue); - batch->draw.blend = 0; - gsk_gl_render_job_draw_rect_with_color (job, &node->bounds, color); - gsk_gl_render_job_end_draw (job); - } - } - } - else - { - gsk_gl_render_job_visit_node (job, gsk_subsurface_node_get_child (node)); - } -} - -static void -gsk_gl_render_job_visit_node (GskGLRenderJob *job, - const GskRenderNode *node) -{ - gboolean has_clip; - - g_assert (job != NULL); - g_assert (node != NULL); - g_assert (GSK_IS_GL_DRIVER (job->driver)); - g_assert (GSK_IS_GL_COMMAND_QUEUE (job->command_queue)); - - if (node_is_invisible (node)) - return; - - if (!gsk_gl_render_job_update_clip (job, &node->bounds, &has_clip)) - return; - - switch (GSK_RENDER_NODE_TYPE (node)) - { - case GSK_BLEND_NODE: - gsk_gl_render_job_visit_blend_node (job, node); - break; - - case GSK_BLUR_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: - if (gsk_border_node_get_uniform_color (node) && - gsk_rounded_rect_is_rectilinear (gsk_border_node_get_outline (node))) - gsk_gl_render_job_visit_rect_border_node (job, node); - else - gsk_gl_render_job_visit_border_node (job, node); - break; - - case GSK_CLIP_NODE: - gsk_gl_render_job_visit_clip_node (job, node); - break; - - case GSK_COLOR_NODE: - gsk_gl_render_job_visit_color_node (job, node); - break; - - case GSK_COLOR_MATRIX_NODE: - gsk_gl_render_job_visit_color_matrix_node (job, node); - break; - - case GSK_CONIC_GRADIENT_NODE: - if (gsk_conic_gradient_node_get_n_color_stops (node) < MAX_GRADIENT_STOPS) - gsk_gl_render_job_visit_conic_gradient_node (job, node); - else - gsk_gl_render_job_visit_as_fallback (job, node); - break; - - case GSK_CONTAINER_NODE: - { - GskRenderNode **children; - guint n_children; - - children = gsk_container_node_get_children (node, &n_children); - - for (guint i = 0; i < n_children; i++) - { - const GskRenderNode *child = children[i]; - - if (i + 1 < n_children && - job->current_clip->is_fully_contained && - GSK_RENDER_NODE_TYPE (child) == GSK_ROUNDED_CLIP_NODE) - { - const GskRenderNode *grandchild = gsk_rounded_clip_node_get_child (child); - const GskRenderNode *child2 = children[i + 1]; - if (GSK_RENDER_NODE_TYPE (grandchild) == GSK_COLOR_NODE && - GSK_RENDER_NODE_TYPE (child2) == GSK_BORDER_NODE && - gsk_border_node_get_uniform_color (child2) && - rounded_rect_equal (gsk_rounded_clip_node_get_clip (child), - gsk_border_node_get_outline (child2))) - { - gsk_gl_render_job_visit_css_background (job, child, child2); - i++; /* skip the border node */ - continue; - } - } - - gsk_gl_render_job_visit_node (job, child); - } - } - break; - - case GSK_CROSS_FADE_NODE: - { - const GskRenderNode *start_node = gsk_cross_fade_node_get_start_child (node); - const GskRenderNode *end_node = gsk_cross_fade_node_get_end_child (node); - float progress = gsk_cross_fade_node_get_progress (node); - - if (progress <= 0.0f) - gsk_gl_render_job_visit_node (job, gsk_cross_fade_node_get_start_child (node)); - else if (progress >= 1.0f || equal_texture_nodes (start_node, end_node)) - gsk_gl_render_job_visit_node (job, gsk_cross_fade_node_get_end_child (node)); - else - gsk_gl_render_job_visit_cross_fade_node (job, node); - } - break; - - case GSK_DEBUG_NODE: - /* Debug nodes are ignored because draws get reordered anyway */ - gsk_gl_render_job_visit_node (job, gsk_debug_node_get_child (node)); - break; - - case GSK_GL_SHADER_NODE: - gsk_gl_render_job_visit_gl_shader_node (job, node); - break; - - case GSK_INSET_SHADOW_NODE: - if (gsk_inset_shadow_node_get_blur_radius (node) > 0) - gsk_gl_render_job_visit_blurred_inset_shadow_node (job, node); - else - gsk_gl_render_job_visit_unblurred_inset_shadow_node (job, node); - break; - - case GSK_LINEAR_GRADIENT_NODE: - case GSK_REPEATING_LINEAR_GRADIENT_NODE: - if (gsk_linear_gradient_node_get_n_color_stops (node) < MAX_GRADIENT_STOPS) - gsk_gl_render_job_visit_linear_gradient_node (job, node); - else - gsk_gl_render_job_visit_as_fallback (job, node); - break; - - case GSK_MASK_NODE: - gsk_gl_render_job_visit_mask_node (job, node); - break; - - case GSK_OPACITY_NODE: - gsk_gl_render_job_visit_opacity_node (job, node); - break; - - case GSK_OUTSET_SHADOW_NODE: - if (gsk_outset_shadow_node_get_blur_radius (node) > 0) - gsk_gl_render_job_visit_blurred_outset_shadow_node (job, node); - else - gsk_gl_render_job_visit_unblurred_outset_shadow_node (job, node); - break; - - case GSK_RADIAL_GRADIENT_NODE: - case GSK_REPEATING_RADIAL_GRADIENT_NODE: - if (gsk_radial_gradient_node_get_n_color_stops (node) < MAX_GRADIENT_STOPS) - gsk_gl_render_job_visit_radial_gradient_node (job, node); - else - gsk_gl_render_job_visit_as_fallback (job, node); - break; - - case GSK_REPEAT_NODE: - gsk_gl_render_job_visit_repeat_node (job, node); - break; - - case GSK_ROUNDED_CLIP_NODE: - gsk_gl_render_job_visit_rounded_clip_node (job, node); - break; - - case GSK_SHADOW_NODE: - gsk_gl_render_job_visit_shadow_node (job, node); - break; - - case GSK_TEXT_NODE: - { - GdkRGBA rgba; - - gdk_color_to_float (gsk_text_node_get_color2 (node), GDK_COLOR_STATE_SRGB, (float *) &rgba); - gsk_gl_render_job_visit_text_node (job, node, &rgba, FALSE); - } - break; - - case GSK_TEXTURE_NODE: - gsk_gl_render_job_visit_texture_node (job, node); - break; - - case GSK_TEXTURE_SCALE_NODE: - gsk_gl_render_job_visit_texture_scale_node (job, node); - break; - - case GSK_TRANSFORM_NODE: - gsk_gl_render_job_visit_transform_node (job, node); - break; - - case GSK_CAIRO_NODE: - gsk_gl_render_job_visit_as_fallback (job, node); - break; - - case GSK_FILL_NODE: - gsk_gl_render_job_visit_as_fallback (job, node); - break; - - case GSK_STROKE_NODE: - gsk_gl_render_job_visit_as_fallback (job, node); - break; - - case GSK_SUBSURFACE_NODE: - gsk_gl_render_job_visit_subsurface_node (job, node); - break; - - case GSK_NOT_A_RENDER_NODE: - default: - g_assert_not_reached (); - break; - } - - if (has_clip) - gsk_gl_render_job_pop_clip (job); -} - -static gboolean -gsk_gl_render_job_visit_node_with_offscreen (GskGLRenderJob *job, - const GskRenderNode *node, - GskGLRenderOffscreen *offscreen) -{ - GskTextureKey key; - guint cached_id; - - g_assert (job != NULL); - g_assert (node != NULL); - g_assert (offscreen != NULL); - g_assert (offscreen->texture_id == 0); - g_assert (offscreen->bounds != NULL); - - if (node_is_invisible (node)) - { - /* Just to be safe. */ - offscreen->texture_id = 0; - init_full_texture_region (offscreen); - offscreen->was_offscreen = FALSE; - return FALSE; - } - - if (GSK_RENDER_NODE_TYPE (node) == GSK_TEXTURE_NODE && - !offscreen->force_offscreen) - { - GdkTexture *texture = gsk_texture_node_get_texture (node); - gsk_gl_render_job_upload_texture (job, texture, FALSE, offscreen); - return TRUE; - } - - key.pointer = node; - key.pointer_is_child = TRUE; /* Don't conflict with the child using the cache too */ - key.parent_rect = *offscreen->bounds; - key.scale_x = job->scale_x; - key.scale_y = job->scale_y; - - float offset_x = job->offset_x; - float offset_y = job->offset_y; - gboolean flipped_x = job->scale_x < 0; - gboolean flipped_y = job->scale_y < 0; - graphene_rect_t viewport; - gboolean reset_clip = FALSE; - - if (flipped_x || flipped_y) - { - GskTransform *transform = gsk_transform_scale (NULL, - flipped_x ? -1 : 1, - flipped_y ? -1 : 1); - gsk_gl_render_job_push_modelview (job, transform); - gsk_transform_unref (transform); - } - - gsk_gl_render_job_transform_bounds (job, offscreen->bounds, &viewport); - - float aligned_x = floorf (viewport.origin.x); - float padding_left = viewport.origin.x - aligned_x; - float aligned_width = ceilf (viewport.size.width + padding_left); - float padding_right = aligned_width - viewport.size.width - padding_left; - - float aligned_y = floorf (viewport.origin.y); - float padding_top = viewport.origin.y - aligned_y; - float aligned_height = ceilf (viewport.size.height + padding_top); - float padding_bottom = aligned_height - viewport.size.height - padding_top; - - /* Tweak the scale factor so that the required texture doesn't - * exceed the max texture limit. This will render with a lower - * resolution, but this is better than clipping. - */ - - g_assert (job->command_queue->max_texture_size > 0); - - float downscale_x = 1; - float downscale_y = 1; - int texture_width; - int texture_height; - int max_texture_size = job->command_queue->max_texture_size; - - if (aligned_width > max_texture_size) - downscale_x = (float)max_texture_size / viewport.size.width; - - if (aligned_height > max_texture_size) - downscale_y = (float)max_texture_size / viewport.size.height; - - if (downscale_x != 1 || downscale_y != 1) - { - GskTransform *transform = gsk_transform_scale (NULL, downscale_x, downscale_y); - gsk_gl_render_job_push_modelview (job, transform); - gsk_transform_unref (transform); - gsk_gl_render_job_transform_bounds (job, offscreen->bounds, &viewport); - gsk_rect_scale (&viewport, downscale_x, downscale_y, &viewport); - } - - if (downscale_x == 1) - { - viewport.origin.x = aligned_x; - viewport.size.width = aligned_width; - offscreen->area.x = padding_left / aligned_width; - offscreen->area.x2 = 1.0f - (padding_right / aligned_width); - texture_width = aligned_width; - } - else - { - offscreen->area.x = 0; - offscreen->area.x2 = 1; - texture_width = max_texture_size; - } - - if (downscale_y == 1) - { - viewport.origin.y = aligned_y; - viewport.size.height = aligned_height; - offscreen->area.y = padding_bottom / aligned_height; - offscreen->area.y2 = 1.0f - padding_top / aligned_height; - texture_height = aligned_height; - } - else - { - offscreen->area.y = 0; - offscreen->area.y2 = 1; - texture_height = max_texture_size; - } - - /* Check if we've already cached the drawn texture. */ - cached_id = gsk_gl_driver_lookup_texture (job->driver, &key, NULL); - - if (cached_id != 0) - { - if (downscale_x != 1 || downscale_y != 1) - gsk_gl_render_job_pop_modelview (job); - if (flipped_x || flipped_y) - gsk_gl_render_job_pop_modelview (job); - offscreen->texture_id = cached_id; - /* We didn't render it offscreen, but hand out an offscreen texture id */ - offscreen->was_offscreen = TRUE; - return TRUE; - } - - GskGLRenderTarget *render_target; - graphene_matrix_t prev_projection; - graphene_rect_t prev_viewport; - float prev_alpha; - guint prev_fbo; - - if (!gsk_gl_driver_create_render_target (job->driver, - texture_width, texture_height, - get_target_format (job, node), - &render_target)) - g_assert_not_reached (); - - if (gdk_gl_context_has_feature (job->command_queue->context, GDK_GL_FEATURE_DEBUG)) - { - gdk_gl_context_label_object_printf (job->command_queue->context, - GL_TEXTURE, - render_target->texture_id, - "Offscreen<%s> %d", - g_type_name_from_instance ((GTypeInstance *) node), - render_target->texture_id); - gdk_gl_context_label_object_printf (job->command_queue->context, - GL_FRAMEBUFFER, - render_target->framebuffer_id, - "Offscreen<%s> FB %d", - g_type_name_from_instance ((GTypeInstance *) node), - render_target->framebuffer_id); - } - - gsk_gl_render_job_set_viewport (job, &viewport, &prev_viewport); - gsk_gl_render_job_set_projection_from_rect (job, &job->viewport, &prev_projection); - prev_alpha = gsk_gl_render_job_set_alpha (job, 1.0f); - - prev_fbo = gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id); - gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport); - - if (offscreen->reset_clip) - { - gsk_gl_render_job_push_clip (job, &GSK_ROUNDED_RECT_INIT_FROM_RECT (job->viewport)); - reset_clip = TRUE; - } - else if (flipped_x || flipped_y || downscale_x != 1 || downscale_y != 1) - { - GskRoundedRect new_clip; - float scale_x = flipped_x ? - downscale_x : downscale_x; - float scale_y = flipped_y ? - downscale_y : downscale_y; - - gsk_rect_scale (&job->current_clip->rect.bounds, scale_x, scale_y, &new_clip.bounds); - rounded_rect_scale_corners (&job->current_clip->rect, &new_clip, scale_x, scale_y); - gsk_gl_render_job_push_clip (job, &new_clip); - reset_clip = TRUE; - } - - job->offscreen_count++; - - gsk_gl_render_job_visit_node (job, node); - - job->offscreen_count--; - - if (reset_clip) - gsk_gl_render_job_pop_clip (job); - - if (downscale_x != 1 || downscale_y != 1) - gsk_gl_render_job_pop_modelview (job); - - if (flipped_x || flipped_y) - gsk_gl_render_job_pop_modelview (job); - - gsk_gl_render_job_set_viewport (job, &prev_viewport, NULL); - gsk_gl_render_job_set_projection (job, &prev_projection); - gsk_gl_render_job_set_alpha (job, prev_alpha); - gsk_gl_command_queue_bind_framebuffer (job->command_queue, prev_fbo); - - job->offset_x = offset_x; - job->offset_y = offset_y; - - offscreen->was_offscreen = TRUE; - offscreen->texture_id = gsk_gl_driver_release_render_target (job->driver, - render_target, - FALSE); - - if (!offscreen->do_not_cache) - gsk_gl_driver_cache_texture (job->driver, &key, offscreen->texture_id); - - return TRUE; -} - -void -gsk_gl_render_job_render_flipped (GskGLRenderJob *job, - GskRenderNode *root) -{ - graphene_matrix_t proj; - guint framebuffer_id; - guint texture_id; - guint surface_height; - - g_return_if_fail (job != NULL); - g_return_if_fail (root != NULL); - g_return_if_fail (GSK_IS_GL_DRIVER (job->driver)); - - surface_height = job->viewport.size.height; - - graphene_matrix_init_ortho (&proj, - job->viewport.origin.x, - job->viewport.origin.x + job->viewport.size.width, - job->viewport.origin.y, - job->viewport.origin.y + job->viewport.size.height, - ORTHO_NEAR_PLANE, - ORTHO_FAR_PLANE); - graphene_matrix_scale (&proj, 1, -1, 1); - - if (!gsk_gl_command_queue_create_render_target (job->command_queue, - MAX (1, job->viewport.size.width), - MAX (1, job->viewport.size.height), - job->target_format, - &framebuffer_id, &texture_id)) - return; - - /* Setup drawing to our offscreen texture/framebuffer which is flipped */ - gsk_gl_command_queue_bind_framebuffer (job->command_queue, framebuffer_id); - gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport); - - /* Visit all nodes creating batches */ - gdk_gl_context_push_debug_group (job->command_queue->context, "Building command queue"); - gsk_gl_render_job_visit_node (job, root); - gdk_gl_context_pop_debug_group (job->command_queue->context); - - /* Now draw to our real destination, but flipped */ - gsk_gl_render_job_set_alpha (job, 1.0f); - gsk_gl_command_queue_bind_framebuffer (job->command_queue, job->framebuffer); - gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport); - if (gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, blit))) - { - gsk_gl_program_set_uniform_texture (job->current_program, - UNIFORM_SHARED_SOURCE, 0, - GL_TEXTURE_2D, - GL_TEXTURE0, - texture_id); - job->source_is_glyph_atlas = FALSE; - gsk_gl_render_job_draw_rect (job, &job->viewport); - gsk_gl_render_job_end_draw (job); - } - - gdk_gl_context_push_debug_group (job->command_queue->context, "Executing command queue"); - gsk_gl_command_queue_execute (job->command_queue, surface_height, 1, NULL, job->default_framebuffer); - gdk_gl_context_pop_debug_group (job->command_queue->context); - - glDeleteFramebuffers (1, &framebuffer_id); - glDeleteTextures (1, &texture_id); -} - -void -gsk_gl_render_job_render (GskGLRenderJob *job, - GskRenderNode *root) -{ - G_GNUC_UNUSED gint64 start_time; - float scale; - guint surface_height; - - g_return_if_fail (job != NULL); - g_return_if_fail (root != NULL); - g_return_if_fail (GSK_IS_GL_DRIVER (job->driver)); - - scale = MAX (job->scale_x, job->scale_y); - surface_height = job->viewport.size.height; - - gsk_gl_command_queue_make_current (job->command_queue); - - /* Build the command queue using the shared GL context for all renderers - * on the same display. - */ - start_time = GDK_PROFILER_CURRENT_TIME; - gdk_gl_context_push_debug_group (job->command_queue->context, "Building command queue"); - gsk_gl_command_queue_bind_framebuffer (job->command_queue, job->framebuffer); - if (job->clear_framebuffer) - gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport); - gsk_gl_render_job_visit_node (job, root); - gdk_gl_context_pop_debug_group (job->command_queue->context); - - gdk_profiler_end_mark (start_time, "Build GL command queue", ""); - -#if 0 - /* At this point the atlases have uploaded content while we processed - * nodes but have not necessarily been used by the commands in the queue. - */ - gsk_gl_driver_save_atlases_to_png (job->driver, NULL); -#endif - - /* But now for executing the command queue, we want to use the context - * that was provided to us when creating the render job as framebuffer 0 - * is bound to that context. - */ - start_time = GDK_PROFILER_CURRENT_TIME; - gsk_gl_command_queue_make_current (job->command_queue); - gdk_gl_context_push_debug_group (job->command_queue->context, "Executing command queue"); - gsk_gl_command_queue_execute (job->command_queue, surface_height, scale, job->region, job->default_framebuffer); - gdk_gl_context_pop_debug_group (job->command_queue->context); - gdk_profiler_end_mark (start_time, "Execute GL command queue", ""); -} - -static int -get_framebuffer_format (GdkGLContext *context, - guint framebuffer) -{ - int size; - - if (!gdk_gl_context_check_version (context, NULL, "3.0")) - return GL_RGBA8; - - glBindFramebuffer (GL_FRAMEBUFFER, framebuffer); - glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, - framebuffer ? GL_COLOR_ATTACHMENT0 - : gdk_gl_context_get_use_es (context) ? GL_BACK - : GL_BACK_LEFT, - GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, &size); - - if (size > 16) - return GL_RGBA32F; - else if (size > 8) - return GL_RGBA16F; - else - return GL_RGBA8; -} - -GskGLRenderJob * -gsk_gl_render_job_new (GskGLDriver *driver, - const graphene_rect_t *viewport, - float scale, - const cairo_region_t *region, - guint framebuffer, - gboolean clear_framebuffer) -{ - const graphene_rect_t *clip_rect = viewport; - graphene_rect_t transformed_extents; - GskGLRenderJob *job; - GdkGLContext *context; - GLint default_framebuffer = 0; - - g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL); - g_return_val_if_fail (viewport != NULL, NULL); - g_return_val_if_fail (scale > 0, NULL); - - /* Check for non-standard framebuffer binding as we might not be using - * the default framebuffer on systems like macOS where we've bound an - * IOSurface to a GL_TEXTURE_RECTANGLE. Otherwise, no scissor clip will - * be applied in the command queue causing overdrawing. - */ - context = driver->command_queue->context; - default_framebuffer = GDK_GL_CONTEXT_GET_CLASS (context)->get_default_framebuffer (context); - if (framebuffer == 0 && default_framebuffer != 0) - framebuffer = default_framebuffer; - - job = g_new0 (GskGLRenderJob, 1); - job->driver = g_object_ref (driver); - job->command_queue = job->driver->command_queue; - clips_init (&job->clip); - modelviews_init (&job->modelview); - job->framebuffer = framebuffer; - job->clear_framebuffer = !!clear_framebuffer; - job->default_framebuffer = default_framebuffer; - job->offset_x = 0; - job->offset_y = 0; - job->scale_x = scale; - job->scale_y = scale; - job->viewport = *viewport; - job->target_format = get_framebuffer_format (job->command_queue->context, framebuffer); - - gsk_gl_render_job_set_alpha (job, 1.0f); - gsk_gl_render_job_set_projection_from_rect (job, viewport, NULL); - gsk_gl_render_job_set_modelview (job, gsk_transform_scale (NULL, scale, scale)); - - /* Setup our initial clip. If region is NULL then we are drawing the - * whole viewport. Otherwise, we need to convert the region to a - * bounding box and clip based on that. - */ - - if (region != NULL) - { - cairo_rectangle_int_t extents; - - cairo_region_get_extents (region, &extents); - gsk_gl_render_job_transform_bounds (job, - &GRAPHENE_RECT_INIT (extents.x, - extents.y, - extents.width, - extents.height), - &transformed_extents); - clip_rect = &transformed_extents; - job->region = cairo_region_create_rectangle (&extents); - } - - gsk_gl_render_job_push_clip (job, - &GSK_ROUNDED_RECT_INIT (clip_rect->origin.x, - clip_rect->origin.y, - clip_rect->size.width, - clip_rect->size.height)); - - return job; -} - -void -gsk_gl_render_job_free (GskGLRenderJob *job) -{ - job->current_modelview = NULL; - job->current_clip = NULL; - - while (job->modelview.end > job->modelview.start) - { - GskGLRenderModelview *modelview = job->modelview.end-1; - g_clear_pointer (&modelview->transform, gsk_transform_unref); - job->modelview.end--; - } - - g_clear_object (&job->driver); - g_clear_pointer (&job->region, cairo_region_destroy); - modelviews_clear (&job->modelview); - clips_clear (&job->clip); - g_free (job); -} diff --git a/gsk/gl/gskglrenderjobprivate.h b/gsk/gl/gskglrenderjobprivate.h deleted file mode 100644 index 2e38b07a2e7..00000000000 --- a/gsk/gl/gskglrenderjobprivate.h +++ /dev/null @@ -1,36 +0,0 @@ -/* gskglrenderjobprivate.h - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "gskgltypesprivate.h" - -GskGLRenderJob *gsk_gl_render_job_new (GskGLDriver *driver, - const graphene_rect_t *viewport, - float scale, - const cairo_region_t *region, - guint framebuffer, - gboolean clear_framebuffer); -void gsk_gl_render_job_free (GskGLRenderJob *job); -void gsk_gl_render_job_render (GskGLRenderJob *job, - GskRenderNode *root); -void gsk_gl_render_job_render_flipped (GskGLRenderJob *job, - GskRenderNode *root); - diff --git a/gsk/gl/gskglshadowlibrary.c b/gsk/gl/gskglshadowlibrary.c deleted file mode 100644 index d7c22c25880..00000000000 --- a/gsk/gl/gskglshadowlibrary.c +++ /dev/null @@ -1,256 +0,0 @@ -/* gskglshadowlibrary.c - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include - -#include "gskgldriverprivate.h" -#include "gskglshadowlibraryprivate.h" - -#define MAX_UNUSED_FRAMES (16 * 5) - -struct _GskGLShadowLibrary -{ - GObject parent_instance; - GskGLDriver *driver; - GArray *shadows; -}; - -typedef struct _Shadow -{ - GskRoundedRect outline; - float blur_radius; - guint texture_id; - gint64 last_used_in_frame; -} Shadow; - -G_DEFINE_TYPE (GskGLShadowLibrary, gsk_gl_shadow_library, G_TYPE_OBJECT) - -enum { - PROP_0, - PROP_DRIVER, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -GskGLShadowLibrary * -gsk_gl_shadow_library_new (GskGLDriver *driver) -{ - g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL); - - return g_object_new (GSK_TYPE_GL_SHADOW_LIBRARY, - "driver", driver, - NULL); -} - -static void -gsk_gl_shadow_library_dispose (GObject *object) -{ - GskGLShadowLibrary *self = (GskGLShadowLibrary *)object; - - for (guint i = 0; i < self->shadows->len; i++) - { - const Shadow *shadow = &g_array_index (self->shadows, Shadow, i); - gsk_gl_driver_release_texture_by_id (self->driver, shadow->texture_id); - } - - g_clear_pointer (&self->shadows, g_array_unref); - g_clear_object (&self->driver); - - G_OBJECT_CLASS (gsk_gl_shadow_library_parent_class)->dispose (object); -} - -static void -gsk_gl_shadow_library_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GskGLShadowLibrary *self = GSK_GL_SHADOW_LIBRARY (object); - - switch (prop_id) - { - case PROP_DRIVER: - g_value_set_object (value, self->driver); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gsk_gl_shadow_library_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GskGLShadowLibrary *self = GSK_GL_SHADOW_LIBRARY (object); - - switch (prop_id) - { - case PROP_DRIVER: - self->driver = g_value_dup_object (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gsk_gl_shadow_library_class_init (GskGLShadowLibraryClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = gsk_gl_shadow_library_dispose; - object_class->get_property = gsk_gl_shadow_library_get_property; - object_class->set_property = gsk_gl_shadow_library_set_property; - - properties [PROP_DRIVER] = - g_param_spec_object ("driver", NULL, NULL, - GSK_TYPE_GL_DRIVER, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -gsk_gl_shadow_library_init (GskGLShadowLibrary *self) -{ - self->shadows = g_array_new (FALSE, FALSE, sizeof (Shadow)); -} - -void -gsk_gl_shadow_library_insert (GskGLShadowLibrary *self, - const GskRoundedRect *outline, - float blur_radius, - guint texture_id) -{ - Shadow *shadow; - - g_assert (GSK_IS_GL_SHADOW_LIBRARY (self)); - g_assert (outline != NULL); - g_assert (texture_id != 0); - - gsk_gl_driver_mark_texture_permanent (self->driver, texture_id); - - g_array_set_size (self->shadows, self->shadows->len + 1); - - shadow = &g_array_index (self->shadows, Shadow, self->shadows->len - 1); - shadow->outline = *outline; - shadow->blur_radius = blur_radius; - shadow->texture_id = texture_id; - shadow->last_used_in_frame = self->driver->current_frame_id; -} - -guint -gsk_gl_shadow_library_lookup (GskGLShadowLibrary *self, - const GskRoundedRect *outline, - float blur_radius) -{ - Shadow *ret = NULL; - - g_assert (GSK_IS_GL_SHADOW_LIBRARY (self)); - g_assert (outline != NULL); - - /* Ensure GskRoundedRect is 12 packed floats without padding - * so that we can use memcmp instead of float comparisons. - */ - G_STATIC_ASSERT (sizeof *outline == (sizeof (float) * 12)); - - for (guint i = 0; i < self->shadows->len; i++) - { - Shadow *shadow = &g_array_index (self->shadows, Shadow, i); - - if (blur_radius == shadow->blur_radius && - memcmp (outline, &shadow->outline, sizeof *outline) == 0) - { - ret = shadow; - break; - } - } - - if (ret == NULL) - return 0; - - g_assert (ret->texture_id != 0); - - ret->last_used_in_frame = self->driver->current_frame_id; - - return ret->texture_id; -} - -#if 0 -static void -write_shadow_to_png (GskGLDriver *driver, - const Shadow *shadow) -{ - int width = shadow->outline.bounds.size.width + (shadow->outline.bounds.origin.x * 2); - int height = shadow->outline.bounds.size.height + (shadow->outline.bounds.origin.y * 2); - char *filename = g_strdup_printf ("shadow_cache_%d_%d_%d.png", - width, height, shadow->texture_id); - GdkTexture *texture; - - texture = gdk_gl_texture_new (gsk_gl_driver_get_context (driver), - shadow->texture_id, - width, height, - NULL, NULL); - gdk_texture_save_to_png (texture, filename); - - g_object_unref (texture); - g_free (filename); -} -#endif - -void -gsk_gl_shadow_library_begin_frame (GskGLShadowLibrary *self) -{ - gint64 watermark; - int i; - int p; - - g_return_if_fail (GSK_IS_GL_SHADOW_LIBRARY (self)); - -#if 0 - for (i = 0, p = self->shadows->len; i < p; i++) - { - const Shadow *shadow = &g_array_index (self->shadows, Shadow, i); - write_shadow_to_png (self->driver, shadow); - } -#endif - - watermark = self->driver->current_frame_id - MAX_UNUSED_FRAMES; - - for (i = 0, p = self->shadows->len; i < p; i++) - { - const Shadow *shadow = &g_array_index (self->shadows, Shadow, i); - - if (shadow->last_used_in_frame < watermark) - { - gsk_gl_driver_release_texture_by_id (self->driver, shadow->texture_id); - g_array_remove_index_fast (self->shadows, i); - p--; - i--; - } - } -} diff --git a/gsk/gl/gskglshadowlibraryprivate.h b/gsk/gl/gskglshadowlibraryprivate.h deleted file mode 100644 index 3c3c77c9e39..00000000000 --- a/gsk/gl/gskglshadowlibraryprivate.h +++ /dev/null @@ -1,42 +0,0 @@ -/* gskglshadowlibraryprivate.h - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "gskgltexturelibraryprivate.h" - -G_BEGIN_DECLS - -#define GSK_TYPE_GL_SHADOW_LIBRARY (gsk_gl_shadow_library_get_type()) - -G_DECLARE_FINAL_TYPE (GskGLShadowLibrary, gsk_gl_shadow_library, GSK, GL_SHADOW_LIBRARY, GObject) - -GskGLShadowLibrary * gsk_gl_shadow_library_new (GskGLDriver *driver); -void gsk_gl_shadow_library_begin_frame (GskGLShadowLibrary *self); -guint gsk_gl_shadow_library_lookup (GskGLShadowLibrary *self, - const GskRoundedRect *outline, - float blur_radius); -void gsk_gl_shadow_library_insert (GskGLShadowLibrary *self, - const GskRoundedRect *outline, - float blur_radius, - guint texture_id); - -G_END_DECLS - diff --git a/gsk/gl/gskgltexture.c b/gsk/gl/gskgltexture.c deleted file mode 100644 index a578e803585..00000000000 --- a/gsk/gl/gskgltexture.c +++ /dev/null @@ -1,95 +0,0 @@ -/* gskgltexture.c - * - * Copyright 2020 Christian Hergert - * - * This file is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include - -#include "gskgltextureprivate.h" -#include "ninesliceprivate.h" - -void -gsk_gl_texture_free (GskGLTexture *texture) -{ - if (texture != NULL) - { - g_assert (texture->link.prev == NULL); - g_assert (texture->link.next == NULL); - - if (texture->user) - g_clear_pointer (&texture->user, gdk_texture_clear_render_data); - - if (texture->texture_id != 0) - { - glDeleteTextures (1, &texture->texture_id); - texture->texture_id = 0; - } - - for (guint i = 0; i < texture->n_slices; i++) - { - glDeleteTextures (1, &texture->slices[i].texture_id); - texture->slices[i].texture_id = 0; - } - - g_clear_pointer (&texture->slices, g_free); - g_clear_pointer (&texture->nine_slice, g_free); - - g_free (texture); - } -} - -GskGLTexture * -gsk_gl_texture_new (guint texture_id, - int width, - int height, - gint64 frame_id) -{ - GskGLTexture *texture; - - texture = g_new0 (GskGLTexture, 1); - texture->texture_id = texture_id; - texture->link.data = texture; - texture->width = width; - texture->height = height; - texture->last_used_in_frame = frame_id; - - return texture; -} - -const GskGLTextureNineSlice * -gsk_gl_texture_get_nine_slice (GskGLTexture *texture, - const GskRoundedRect *outline, - float extra_pixels_x, - float extra_pixels_y) -{ - g_assert (texture != NULL); - g_assert (outline != NULL); - - if G_UNLIKELY (texture->nine_slice == NULL) - { - texture->nine_slice = g_new0 (GskGLTextureNineSlice, 9); - - nine_slice_rounded_rect (texture->nine_slice, outline); - nine_slice_grow (texture->nine_slice, extra_pixels_x, extra_pixels_y); - nine_slice_to_texture_coords (texture->nine_slice, texture->width, texture->height); - } - - return texture->nine_slice; -} diff --git a/gsk/gl/gskgltexturelibrary.c b/gsk/gl/gskgltexturelibrary.c deleted file mode 100644 index 772b3824776..00000000000 --- a/gsk/gl/gskgltexturelibrary.c +++ /dev/null @@ -1,583 +0,0 @@ -/* gskgltexturelibrary.c - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include -#include - -#include "gskglcommandqueueprivate.h" -#include "gskgldriverprivate.h" -#include "gskgltexturelibraryprivate.h" - -#define DEFAULT_MAX_FRAME_AGE 60 -#define DEFAULT_ATLAS_WIDTH 512 -#define DEFAULT_ATLAS_HEIGHT 512 -#define MAX_OLD_RATIO 0.5 - -G_DEFINE_ABSTRACT_TYPE (GskGLTextureLibrary, gsk_gl_texture_library, G_TYPE_OBJECT) - -enum { - PROP_0, - PROP_DRIVER, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -static void -gsk_gl_texture_atlas_free (GskGLTextureAtlas *atlas) -{ - if (atlas->texture_id != 0) - { - glDeleteTextures (1, &atlas->texture_id); - atlas->texture_id = 0; - } - - g_clear_pointer (&atlas->nodes, g_free); - g_free (atlas); -} - -static gboolean -gsk_gl_texture_library_real_compact (GskGLTextureLibrary *self, - gint64 frame_id) -{ - GPtrArray *removed = NULL; - gboolean ret = FALSE; - gboolean periodic_scan; - - g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self)); - - periodic_scan = (self->max_frame_age > 0 && - (frame_id % self->max_frame_age) == 0); - - for (guint i = self->atlases->len; i > 0; i--) - { - GskGLTextureAtlas *atlas = g_ptr_array_index (self->atlases, i - 1); - - if (gsk_gl_texture_atlas_get_unused_ratio (atlas) > MAX_OLD_RATIO) - { - GSK_DEBUG (CACHE, - "Dropping atlas %d (%g.2%% old)", i, - 100.0 * gsk_gl_texture_atlas_get_unused_ratio (atlas)); - if (removed == NULL) - removed = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free); - g_ptr_array_add (removed, g_ptr_array_steal_index (self->atlases, i - 1)); - } - } - - if (periodic_scan || removed != NULL) - { - GskGLTextureAtlasEntry *entry; - GHashTableIter iter; - guint dropped = 0; - G_GNUC_UNUSED guint atlased = 0; - - g_hash_table_iter_init (&iter, self->hash_table); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry)) - { - if (entry->is_atlased) - { - if (removed && g_ptr_array_find (removed, entry->atlas, NULL)) - { - g_hash_table_iter_remove (&iter); - dropped++; - } - else if (periodic_scan) - { - gsk_gl_texture_atlas_entry_mark_unused (entry); - entry->accessed = FALSE; - if (entry->is_atlased) - atlased++; - } - } - else - { - if (!entry->accessed) - { - if (entry->texture) - gsk_gl_driver_release_texture (self->driver, entry->texture); - g_hash_table_iter_remove (&iter); - dropped++; - } - else if (periodic_scan) - entry->accessed = FALSE; - } - } - - GSK_DEBUG (CACHE, "%s: Dropped %d individual items", - G_OBJECT_TYPE_NAME (self), - dropped); - GSK_DEBUG (CACHE, "%s: %d items cached (%d atlased, %d individually)", - G_OBJECT_TYPE_NAME (self), - g_hash_table_size (self->hash_table), - atlased, - g_hash_table_size (self->hash_table) - atlased); - - if (dropped > 0) - gsk_gl_texture_library_clear_cache (self); - - ret = TRUE; - - g_clear_pointer (&removed, g_ptr_array_unref); - } - - if (GSK_DEBUG_CHECK (CACHE)) - { - static gint64 last_message; - gint64 now = g_get_monotonic_time (); - if (now - last_message > G_USEC_PER_SEC) - { - last_message = now; - gdk_debug_message ("%s contains %d atlases", - G_OBJECT_TYPE_NAME (self), - self->atlases->len); - } - } - - return ret; -} - -static gboolean -gsk_gl_texture_library_real_allocate (GskGLTextureLibrary *self, - GskGLTextureAtlas *atlas, - int width, - int height, - int *out_x, - int *out_y) -{ - stbrp_rect rect; - - g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self)); - g_assert (atlas != NULL); - g_assert (width > 0); - g_assert (height > 0); - g_assert (out_x != NULL); - g_assert (out_y != NULL); - - rect.w = width; - rect.h = height; - - stbrp_pack_rects (&atlas->context, &rect, 1); - - if (rect.was_packed) - { - *out_x = rect.x; - *out_y = rect.y; - } - - return rect.was_packed; -} - -static void -gsk_gl_texture_library_constructed (GObject *object) -{ - G_OBJECT_CLASS (gsk_gl_texture_library_parent_class)->constructed (object); - - g_assert (GSK_GL_TEXTURE_LIBRARY (object)->hash_table != NULL); -} - -static void -gsk_gl_texture_library_dispose (GObject *object) -{ - GskGLTextureLibrary *self = (GskGLTextureLibrary *)object; - - g_clear_pointer (&self->atlases, g_ptr_array_unref); - g_clear_object (&self->driver); - g_clear_pointer (&self->hash_table, g_hash_table_unref); - - G_OBJECT_CLASS (gsk_gl_texture_library_parent_class)->dispose (object); -} - -static void -gsk_gl_texture_library_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GskGLTextureLibrary *self = GSK_GL_TEXTURE_LIBRARY (object); - - switch (prop_id) - { - case PROP_DRIVER: - g_value_set_object (value, self->driver); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gsk_gl_texture_library_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GskGLTextureLibrary *self = GSK_GL_TEXTURE_LIBRARY (object); - - switch (prop_id) - { - case PROP_DRIVER: - self->driver = g_value_dup_object (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gsk_gl_texture_library_class_init (GskGLTextureLibraryClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->constructed = gsk_gl_texture_library_constructed; - object_class->dispose = gsk_gl_texture_library_dispose; - object_class->get_property = gsk_gl_texture_library_get_property; - object_class->set_property = gsk_gl_texture_library_set_property; - - klass->compact = gsk_gl_texture_library_real_compact; - klass->allocate = gsk_gl_texture_library_real_allocate; - - properties [PROP_DRIVER] = - g_param_spec_object ("driver", NULL, NULL, - GSK_TYPE_GL_DRIVER, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -gsk_gl_texture_library_init (GskGLTextureLibrary *self) -{ - self->max_frame_age = DEFAULT_MAX_FRAME_AGE; - self->atlas_width = DEFAULT_ATLAS_WIDTH; - self->atlas_height = DEFAULT_ATLAS_HEIGHT; - self->atlases = g_ptr_array_new_with_free_func ((GDestroyNotify)gsk_gl_texture_atlas_free); -} - -void -gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self, - GHashFunc hash_func, - GEqualFunc equal_func, - GDestroyNotify key_destroy, - GDestroyNotify value_destroy) -{ - g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self)); - g_return_if_fail (self->hash_table == NULL); - - self->hash_table = g_hash_table_new_full (hash_func, equal_func, - key_destroy, value_destroy); -} - -void -gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self, - gint64 frame_id) -{ - g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self)); - - gsk_gl_texture_library_compact (self, frame_id); - - if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame) - GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame (self, frame_id); -} - -static GskGLTexture * -gsk_gl_texture_library_pack_one (GskGLTextureLibrary *self, - guint width, - guint height) -{ - GskGLTexture *texture; - - g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self)); - - if (width > self->driver->command_queue->max_texture_size || - height > self->driver->command_queue->max_texture_size) - { - g_warning ("Clipping requested texture of size %ux%u to maximum allowable size %u.", - width, height, self->driver->command_queue->max_texture_size); - width = MIN (width, self->driver->command_queue->max_texture_size); - height = MIN (height, self->driver->command_queue->max_texture_size); - } - - texture = gsk_gl_driver_create_texture (self->driver, width, height, GL_RGBA8); - texture->permanent = TRUE; - - return texture; -} - -static void -gsk_gl_texture_library_pack_any_atlas (GskGLTextureLibrary *self, - int width, - int height, - GskGLTextureAtlas **out_atlas, - int *out_x, - int *out_y) -{ - GskGLTextureAtlas *atlas = NULL; - int x, y; - - g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self)); - g_assert (width > 0); - g_assert (height > 0); - g_assert (out_atlas != NULL); - g_assert (out_x != NULL); - g_assert (out_y != NULL); - - for (guint i = 0; i < self->atlases->len; i++) - { - atlas = g_ptr_array_index (self->atlases, i); - - if (gsk_gl_texture_library_allocate (self, atlas, width, height, &x, &y)) - break; - - atlas = NULL; - } - - if (atlas == NULL) - { - /* No atlas has enough space, so create a new one... */ - atlas = gsk_gl_texture_library_acquire_atlas (self); - - /* Pack it onto that one, which surely has enough space... */ - if (!gsk_gl_texture_library_allocate (self, atlas, width, height, &x, &y)) - g_assert_not_reached (); - } - - *out_atlas = atlas; - *out_x = x; - *out_y = y; -} - -gpointer -gsk_gl_texture_library_pack (GskGLTextureLibrary *self, - gpointer key, - gsize valuelen, - guint width, - guint height, - int padding, - guint *out_packed_x, - guint *out_packed_y) -{ - GskGLTextureAtlasEntry *entry; - GskGLTextureAtlas *atlas = NULL; - - g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self)); - g_assert (key != NULL); - g_assert (valuelen > sizeof (GskGLTextureAtlasEntry)); - g_assert (out_packed_x != NULL); - g_assert (out_packed_y != NULL); - - entry = g_malloc0 (valuelen); - entry->n_pixels = width * height; - entry->accessed = TRUE; - entry->used = TRUE; - - /* If our size is invisible then we just want an entry in the - * cache for faster lookups, but do not actually spend any texture - * allocations on this entry. - */ - if (width <= 0 && height <= 0) - { - entry->is_atlased = FALSE; - entry->texture = NULL; - entry->area.x = 0.0f; - entry->area.y = 0.0f; - entry->area.x2 = 0.0f; - entry->area.y2 = 0.0f; - - *out_packed_x = 0; - *out_packed_y = 0; - } - else if (self->max_entry_size == 0 || - (width <= self->max_entry_size && - height <= self->max_entry_size)) - { - int packed_x; - int packed_y; - - gsk_gl_texture_library_pack_any_atlas (self, - padding + width + padding, - padding + height + padding, - &atlas, - &packed_x, - &packed_y); - - entry->atlas = atlas; - entry->is_atlased = TRUE; - entry->area.x = (packed_x + padding) / (float)atlas->width; - entry->area.y = (packed_y + padding) / (float)atlas->height; - entry->area.x2 = (packed_x + padding + width) / (float)atlas->width; - entry->area.y2 = (packed_y + padding + height) / (float)atlas->height; - - *out_packed_x = packed_x; - *out_packed_y = packed_y; - } - else - { - GskGLTexture *texture = gsk_gl_texture_library_pack_one (self, - padding + width + padding, - padding + height + padding); - - entry->texture = texture; - entry->is_atlased = FALSE; - entry->area.x = padding / (float) (padding + width + padding); - entry->area.y = padding / (float) (padding + height + padding); - entry->area.x2 = (padding + width) / (float) (padding + width + padding); - entry->area.y2 = (padding + height) / (float) (padding + height + padding); - - *out_packed_x = 0; - *out_packed_y = 0; - } - - g_hash_table_insert (self->hash_table, key, entry); - - return entry; -} - -/* - * gsk_gl_texture_library_clear_cache: - * - * Clear the front cache if the texture library is using one. For - * example the glyph cache would drop it's front cache to force - * next lookups to fall through to the GHashTable key lookup. - */ -void -gsk_gl_texture_library_clear_cache (GskGLTextureLibrary *self) -{ - g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self)); - - if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache) - GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->clear_cache (self); -} - -/* - * gsk_gl_texture_library_compact: - * - * Requests that the texture library compact it's altases. That - * generally means to traverse them to look for unused pixels over - * a certain threshold and release them if necessary. - * - * Returns: %TRUE if any compaction occurred. - */ -gboolean -gsk_gl_texture_library_compact (GskGLTextureLibrary *self, - gint64 frame_id) -{ - g_return_val_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self), FALSE); - - if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->compact) - return GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->compact (self, frame_id); - - return FALSE; -} - -void -gsk_gl_texture_library_reset (GskGLTextureLibrary *self) -{ - g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self)); - - gsk_gl_texture_library_clear_cache (self); - - g_hash_table_remove_all (self->hash_table); - - if (self->atlases->len) - g_ptr_array_remove_range (self->atlases, 0, self->atlases->len); -} - -void -gsk_gl_texture_library_set_atlas_size (GskGLTextureLibrary *self, - int width, - int height) -{ - g_return_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self)); - - if (width <= 0) - width = DEFAULT_ATLAS_WIDTH; - - if (height <= 0) - height = DEFAULT_ATLAS_HEIGHT; - - self->atlas_height = height; - self->atlas_width = width; - - gsk_gl_texture_library_reset (self); -} - -/* - * gsk_gl_texture_library_acquire_atlas: - * - * Allocates a new texture atlas based on the current size - * and format requirements. - */ -GskGLTextureAtlas * -gsk_gl_texture_library_acquire_atlas (GskGLTextureLibrary *self) -{ - GskGLTextureAtlas *atlas; - - g_return_val_if_fail (GSK_IS_GL_TEXTURE_LIBRARY (self), NULL); - g_return_val_if_fail (GSK_IS_GL_DRIVER (self->driver), NULL); - g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->driver->command_queue), NULL); - g_return_val_if_fail (self->atlas_width > 0, NULL); - g_return_val_if_fail (self->atlas_height > 0, NULL); - - atlas = g_new0 (GskGLTextureAtlas, 1); - atlas->width = self->atlas_width; - atlas->height = self->atlas_height; - /* TODO: We might want to change the strategy about the amount of - * nodes here? stb_rect_pack.h says width is optimal. */ - atlas->nodes = g_malloc0_n (atlas->width, sizeof (struct stbrp_node)); - stbrp_init_target (&atlas->context, atlas->width, atlas->height, atlas->nodes, atlas->width); - atlas->texture_id = gsk_gl_command_queue_create_texture (self->driver->command_queue, - atlas->width, - atlas->height, - GL_RGBA8); - - gdk_gl_context_label_object_printf (gdk_gl_context_get_current (), - GL_TEXTURE, atlas->texture_id, - "Texture atlas %d", - atlas->texture_id); - - g_ptr_array_add (self->atlases, atlas); - - if (GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->init_atlas) - GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->init_atlas (self, atlas); - - return atlas; -} - -gboolean -gsk_gl_texture_library_allocate (GskGLTextureLibrary *self, - GskGLTextureAtlas *atlas, - int width, - int height, - int *out_x, - int *out_y) -{ - g_assert (GSK_IS_GL_TEXTURE_LIBRARY (self)); - g_assert (atlas != NULL); - g_assert (width > 0); - g_assert (height > 0); - g_assert (out_x != NULL); - g_assert (out_y != NULL); - - return GSK_GL_TEXTURE_LIBRARY_GET_CLASS (self)->allocate (self, atlas, width, height, out_x, out_y); -} diff --git a/gsk/gl/gskgltexturelibraryprivate.h b/gsk/gl/gskgltexturelibraryprivate.h deleted file mode 100644 index e2c3589fab4..00000000000 --- a/gsk/gl/gskgltexturelibraryprivate.h +++ /dev/null @@ -1,232 +0,0 @@ -/* gskgltexturelibraryprivate.h - * - * Copyright 2020 Christian Hergert - * - * This file is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "gskgltypesprivate.h" -#include "gskgltextureprivate.h" - -#include "stb_rect_pack.h" - -G_BEGIN_DECLS - -#define GSK_TYPE_GL_TEXTURE_LIBRARY (gsk_gl_texture_library_get_type ()) -#define GSK_GL_TEXTURE_LIBRARY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_GL_TEXTURE_LIBRARY, GskGLTextureLibrary)) -#define GSK_IS_GL_TEXTURE_LIBRARY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_GL_TEXTURE_LIBRARY)) -#define GSK_GL_TEXTURE_LIBRARY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_GL_TEXTURE_LIBRARY, GskGLTextureLibraryClass)) -#define GSK_IS_GL_TEXTURE_LIBRARY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_GL_TEXTURE_LIBRARY)) -#define GSK_GL_TEXTURE_LIBRARY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_GL_TEXTURE_LIBRARY, GskGLTextureLibraryClass)) - -typedef struct _GskGLTextureAtlas -{ - struct stbrp_context context; - struct stbrp_node *nodes; - - int width; - int height; - - guint texture_id; - - /* Pixels of rects that have been used at some point, - * But are now unused. - */ - int unused_pixels; - -} GskGLTextureAtlas; - -typedef struct _GskGLTextureAtlasEntry -{ - /* A backreference to either the atlas or texture containing - * the contents of the atlas entry. For larger items, no atlas - * is used and instead a direct texture. - */ - union { - GskGLTextureAtlas *atlas; - GskGLTexture *texture; - }; - - /* The area within the atlas translated to 0..1 bounds */ - struct { - float x; - float y; - float x2; - float y2; - } area; - - /* Number of pixels in the entry, used to calculate usage - * of an atlas while processing. - */ - guint n_pixels : 29; - - /* If entry has marked pixels as used in the atlas this frame */ - guint used : 1; - - /* If entry was accessed this frame */ - guint accessed : 1; - - /* When true, backref is an atlas, otherwise texture */ - guint is_atlased : 1; -} GskGLTextureAtlasEntry; - -typedef struct _GskGLTextureLibrary -{ - GObject parent_instance; - GskGLDriver *driver; - GPtrArray *atlases; - GHashTable *hash_table; - guint max_entry_size; - guint max_frame_age; - guint atlas_width; - guint atlas_height; -} GskGLTextureLibrary; - -typedef struct _GskGLTextureLibraryClass -{ - GObjectClass parent_class; - - void (*begin_frame) (GskGLTextureLibrary *library, - gint64 frame_id); - gboolean (*compact) (GskGLTextureLibrary *library, - gint64 frame_id); - void (*clear_cache) (GskGLTextureLibrary *library); - void (*init_atlas) (GskGLTextureLibrary *library, - GskGLTextureAtlas *atlas); - gboolean (*allocate) (GskGLTextureLibrary *library, - GskGLTextureAtlas *atlas, - int width, - int height, - int *out_x, - int *out_y); -} GskGLTextureLibraryClass; - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (GskGLTextureLibrary, g_object_unref) - -GType gsk_gl_texture_library_get_type (void) G_GNUC_CONST; -gboolean gsk_gl_texture_library_compact (GskGLTextureLibrary *self, - gint64 frame_id); -void gsk_gl_texture_library_clear_cache (GskGLTextureLibrary *self); -void gsk_gl_texture_library_reset (GskGLTextureLibrary *self); -void gsk_gl_texture_library_set_atlas_size (GskGLTextureLibrary *self, - int width, - int height); -GskGLTextureAtlas *gsk_gl_texture_library_acquire_atlas (GskGLTextureLibrary *self); -void gsk_gl_texture_library_set_funcs (GskGLTextureLibrary *self, - GHashFunc hash_func, - GEqualFunc equal_func, - GDestroyNotify key_destroy, - GDestroyNotify value_destroy); -void gsk_gl_texture_library_begin_frame (GskGLTextureLibrary *self, - gint64 frame_id); -gboolean gsk_gl_texture_library_allocate (GskGLTextureLibrary *self, - GskGLTextureAtlas *atlas, - int width, - int height, - int *out_x, - int *out_y); -gpointer gsk_gl_texture_library_pack (GskGLTextureLibrary *self, - gpointer key, - gsize valuelen, - guint width, - guint height, - int padding, - guint *out_packed_x, - guint *out_packed_y); - -static inline void -gsk_gl_texture_atlas_mark_unused (GskGLTextureAtlas *self, - int n_pixels) -{ - g_assert (n_pixels >= 0); - - self->unused_pixels += n_pixels; -} - -static inline void -gsk_gl_texture_atlas_entry_mark_used (GskGLTextureAtlasEntry *entry) -{ - if (entry->used == TRUE || entry->is_atlased == FALSE) - return; - - entry->atlas->unused_pixels -= entry->n_pixels; - entry->used = TRUE; -} - -static inline void -gsk_gl_texture_atlas_entry_mark_unused (GskGLTextureAtlasEntry *entry) -{ - if (entry->used == FALSE || entry->is_atlased == FALSE) - return; - - entry->atlas->unused_pixels += entry->n_pixels; - entry->used = FALSE; -} - -static inline gboolean -gsk_gl_texture_library_lookup (GskGLTextureLibrary *self, - gconstpointer key, - GskGLTextureAtlasEntry **out_entry) -{ - GskGLTextureAtlasEntry *entry = g_hash_table_lookup (self->hash_table, key); - - if G_LIKELY (entry != NULL && entry->accessed && entry->used) - { - *out_entry = entry; - return TRUE; - } - - if (entry != NULL) - { - gsk_gl_texture_atlas_entry_mark_used (entry); - entry->accessed = TRUE; - *out_entry = entry; - return TRUE; - } - - return FALSE; -} - -static inline guint -GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (gconstpointer d) -{ - const GskGLTextureAtlasEntry *e = d; - - return e->is_atlased ? e->atlas->texture_id - : e->texture ? e->texture->texture_id : 0; -} - -static inline double -gsk_gl_texture_atlas_get_unused_ratio (const GskGLTextureAtlas *self) -{ - if (self->unused_pixels > 0) - return (double)(self->unused_pixels) / (double)(self->width * self->height); - return 0.0; -} - -static inline gboolean -gsk_gl_texture_library_can_cache (GskGLTextureLibrary *self, - int width, - int height) -{ - g_assert (self->max_entry_size > 0); - return width <= self->max_entry_size && height <= self->max_entry_size; -} - -G_END_DECLS - diff --git a/gsk/gl/gskgltextureprivate.h b/gsk/gl/gskgltextureprivate.h deleted file mode 100644 index 620a9ed0765..00000000000 --- a/gsk/gl/gskgltextureprivate.h +++ /dev/null @@ -1,93 +0,0 @@ -/* gskgltextureprivate.h - * - * Copyright 2020 Christian Hergert - * - * This file is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "gskgltypesprivate.h" - -G_BEGIN_DECLS - -struct _GskGLTextureSlice -{ - cairo_rectangle_int_t rect; - struct { - float x; - float y; - float x2; - float y2; - } area; - guint texture_id; -}; - -struct _GskGLTextureNineSlice -{ - cairo_rectangle_int_t rect; - struct { - float x; - float y; - float x2; - float y2; - } area; -}; - -struct _GskGLTexture -{ - /* Used to insert into queue */ - GList link; - - /* Identifier of the frame that created it */ - gint64 last_used_in_frame; - - /* Backpointer to texture (can be cleared asynchronously) */ - GdkTexture *user; - - /* Only used by nine-slice textures */ - GskGLTextureNineSlice *nine_slice; - - /* Only used by sliced textures */ - GskGLTextureSlice *slices; - guint n_slices; - - /* The actual GL texture identifier in some shared context */ - guint texture_id; - - int width; - int height; - - /* Set when used by an atlas so we don't drop the texture */ - guint permanent : 1; - /* we are allowed to call glGenerateMipmap() for this texture */ - guint can_mipmap : 1; - /* we called glGenerateMipmap() for this texture */ - guint has_mipmap : 1; -}; - -GskGLTexture * gsk_gl_texture_new (guint texture_id, - int width, - int height, - gint64 frame_id); -const GskGLTextureNineSlice * gsk_gl_texture_get_nine_slice (GskGLTexture *texture, - const GskRoundedRect *outline, - float extra_pixels_x, - float extra_pixels_y); -void gsk_gl_texture_free (GskGLTexture *texture); - -G_END_DECLS - diff --git a/gsk/gl/gskgltypesprivate.h b/gsk/gl/gskgltypesprivate.h deleted file mode 100644 index 301a232bf13..00000000000 --- a/gsk/gl/gskgltypesprivate.h +++ /dev/null @@ -1,68 +0,0 @@ -/* gskgltypesprivate.h - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include -#include -#ifdef GDK_WINDOWING_WIN32 -/* epoxy needs this, see https://github.com/anholt/libepoxy/issues/299 */ -#include -#endif -#include -#include - -G_BEGIN_DECLS - -#define GSK_GL_N_VERTICES 6 - -typedef struct _GskGLAttachmentState GskGLAttachmentState; -typedef struct _GskGLBuffer GskGLBuffer; -typedef struct _GskGLCommandQueue GskGLCommandQueue; -typedef struct _GskGLCompiler GskGLCompiler; -typedef struct _GskGLDrawVertex GskGLDrawVertex; -typedef struct _GskGLRenderTarget GskGLRenderTarget; -typedef struct _GskGLGlyphLibrary GskGLGlyphLibrary; -typedef struct _GskGLIconLibrary GskGLIconLibrary; -typedef struct _GskGLProgram GskGLProgram; -typedef struct _GskGLRenderJob GskGLRenderJob; -typedef struct _GskGLShadowLibrary GskGLShadowLibrary; -typedef struct _GskGLTexture GskGLTexture; -typedef struct _GskGLTextureSlice GskGLTextureSlice; -typedef struct _GskGLTextureAtlas GskGLTextureAtlas; -typedef struct _GskGLTextureLibrary GskGLTextureLibrary; -typedef struct _GskGLTextureNineSlice GskGLTextureNineSlice; -typedef struct _GskGLUniformInfo GskGLUniformInfo; -typedef struct _GskGLUniformProgram GskGLUniformProgram; -typedef struct _GskGLUniformState GskGLUniformState; -typedef struct _GskGLDriver GskGLDriver; - -struct _GskGLDrawVertex -{ - float position[2]; - union { - float uv[2]; - guint16 color2[4]; - }; - guint16 color[4]; -}; - -G_END_DECLS - diff --git a/gsk/gl/gskgluniformstate.c b/gsk/gl/gskgluniformstate.c deleted file mode 100644 index 77cd45a8595..00000000000 --- a/gsk/gl/gskgluniformstate.c +++ /dev/null @@ -1,269 +0,0 @@ -/* gskgluniformstate.c - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include -#include - -#include "gskgluniformstateprivate.h" - -static const guint8 uniform_sizes[] = { - 0, - - sizeof (Uniform1f), - sizeof (Uniform2f), - sizeof (Uniform3f), - sizeof (Uniform4f), - - sizeof (Uniform1f), - sizeof (Uniform2f), - sizeof (Uniform3f), - sizeof (Uniform4f), - - sizeof (Uniform1i), - sizeof (Uniform2i), - sizeof (Uniform3i), - sizeof (Uniform4i), - - sizeof (Uniform1ui), - - sizeof (guint), - - sizeof (graphene_matrix_t), - sizeof (GskRoundedRect), - sizeof (GdkRGBA), - - 0, -}; - -GskGLUniformState * -gsk_gl_uniform_state_new (void) -{ - GskGLUniformState *state; - - state = g_atomic_rc_box_new0 (GskGLUniformState); - state->programs = g_hash_table_new_full (NULL, NULL, NULL, g_free); - state->values_len = 4096; - state->values_pos = 0; - state->values_buf = g_malloc (4096); - - memset (state->apply_hash, 0, sizeof state->apply_hash); - - return g_steal_pointer (&state); -} - -GskGLUniformState * -gsk_gl_uniform_state_ref (GskGLUniformState *state) -{ - return g_atomic_rc_box_acquire (state); -} - -static void -gsk_gl_uniform_state_finalize (gpointer data) -{ - GskGLUniformState *state = data; - - g_clear_pointer (&state->programs, g_hash_table_unref); - g_clear_pointer (&state->values_buf, g_free); -} - -void -gsk_gl_uniform_state_unref (GskGLUniformState *state) -{ - g_atomic_rc_box_release_full (state, gsk_gl_uniform_state_finalize); -} - -gpointer -gsk_gl_uniform_state_init_value (GskGLUniformState *state, - GskGLUniformProgram *program, - GskGLUniformFormat format, - guint array_count, - guint key, - GskGLUniformMapping **infoptr) -{ - GskGLUniformMapping *mapping; - guint offset; - - g_assert (state != NULL); - g_assert (array_count < 32); - g_assert ((int)format >= 0 && format < GSK_GL_UNIFORM_FORMAT_LAST); - g_assert (format > 0); - g_assert (program != NULL); - g_assert (key < program->n_mappings); - - mapping = &program->mappings[key]; - - if (mapping->location == -1) - { - *infoptr = NULL; - return NULL; - } - - if G_LIKELY (format == mapping->info.format) - { - if G_LIKELY (array_count <= mapping->info.array_count) - { - *infoptr = mapping; - return GSK_GL_UNIFORM_VALUE (state->values_buf, mapping->info.offset); - } - - /* We found the uniform, but there is not enough space for the - * amount that was requested. Instead, allocate new space and - * set the value to "initial" so that the caller just writes - * over the previous value. - * - * This can happen when using dynamic array lengths like the - * "n_color_stops" in gradient shaders. - */ - goto setup_info; - } - else if (mapping->info.format == 0) - { - goto setup_info; - } - else - { - g_critical ("Attempt to access uniform with different type of value " - "than it was initialized with. Program %u Location %u. " - "Was %d now %d (array length %d now %d).", - program->program_id, key, mapping->info.format, format, - mapping->info.array_count, array_count); - *infoptr = NULL; - return NULL; - } - -setup_info: - - gsk_gl_uniform_state_realloc (state, - uniform_sizes[format] * MAX (1, array_count), - &offset); - - /* we have 21 bits for offset */ - g_assert (offset < (1 << GSK_GL_UNIFORM_OFFSET_BITS)); - - mapping->info.format = format; - mapping->info.offset = offset; - mapping->info.array_count = array_count; - mapping->info.initial = TRUE; - mapping->stamp = 0; - - *infoptr = mapping; - - return GSK_GL_UNIFORM_VALUE (state->values_buf, mapping->info.offset); -} - -void -gsk_gl_uniform_state_end_frame (GskGLUniformState *state) -{ - GHashTableIter iter; - GskGLUniformProgram *program; - guint allocator = 0; - - g_return_if_fail (state != NULL); - - /* After a frame finishes, we want to remove all our copies of uniform - * data that isn't needed any longer. Since we treat it as uninitialized - * after this frame (to reset it on first use next frame) we can just - * discard it but keep an allocation around to reuse. - */ - - g_hash_table_iter_init (&iter, state->programs); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&program)) - { - for (guint j = 0; j < program->n_mappings; j++) - { - GskGLUniformMapping *mapping = &program->mappings[j]; - guint size; - - /* Skip unused uniform mappings */ - if (mapping->info.format == 0 || mapping->location == -1) - continue; - - /* Calculate how much size is needed for the uniform, including arrays */ - size = uniform_sizes[mapping->info.format] * MAX (1, mapping->info.array_count); - - /* Adjust alignment for value */ - allocator += gsk_gl_uniform_state_align (allocator, size); - - /* Offset is in slots of 4 bytes */ - mapping->info.offset = allocator / 4; - mapping->info.initial = TRUE; - mapping->stamp = 0; - - /* Now advance for this items data */ - allocator += size; - } - } - - state->values_pos = allocator; - - /* It can happen that our space requirements grow due to - * difference in order increasing padding. As a pragmatic - * solution to this, just increase the allocation to cover - * the predefined mappins. - */ - if (allocator > state->values_len) - { - while (allocator > state->values_len) - state->values_len *= 2; - state->values_buf = g_realloc (state->values_buf, state->values_len); - } - - memset (state->apply_hash, 0, sizeof state->apply_hash); -} - -gsize -gsk_gl_uniform_format_size (GskGLUniformFormat format) -{ - g_assert (format > 0); - g_assert (format < GSK_GL_UNIFORM_FORMAT_LAST); - - return uniform_sizes[format]; -} - -GskGLUniformProgram * -gsk_gl_uniform_state_get_program (GskGLUniformState *state, - guint program, - const GskGLUniformMapping *mappings, - guint n_mappings) -{ - GskGLUniformProgram *ret; - - g_return_val_if_fail (state != NULL, NULL); - g_return_val_if_fail (program > 0, NULL); - g_return_val_if_fail (program < G_MAXUINT, NULL); - g_return_val_if_fail (n_mappings <= G_N_ELEMENTS (ret->mappings), NULL); - - ret = g_hash_table_lookup (state->programs, GUINT_TO_POINTER (program)); - - if (ret == NULL) - { - ret = g_new0 (GskGLUniformProgram, 1); - ret->program_id = program; - ret->n_mappings = n_mappings; - - memcpy (ret->mappings, mappings, n_mappings * sizeof *mappings); - - g_hash_table_insert (state->programs, GUINT_TO_POINTER (program), ret); - } - - return ret; -} diff --git a/gsk/gl/gskgluniformstateprivate.h b/gsk/gl/gskgluniformstateprivate.h deleted file mode 100644 index 3704be957ca..00000000000 --- a/gsk/gl/gskgluniformstateprivate.h +++ /dev/null @@ -1,834 +0,0 @@ -/* gskgluniformstateprivate.h - * - * Copyright 2020 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "gskgltypesprivate.h" - -G_BEGIN_DECLS - -typedef struct { float v0; } Uniform1f; -typedef struct { float v0; float v1; } Uniform2f; -typedef struct { float v0; float v1; float v2; } Uniform3f; -typedef struct { float v0; float v1; float v2; float v3; } Uniform4f; - -typedef struct { int v0; } Uniform1i; -typedef struct { int v0; int v1; } Uniform2i; -typedef struct { int v0; int v1; int v2; } Uniform3i; -typedef struct { int v0; int v1; int v2; int v3; } Uniform4i; - -typedef struct { guint v0; } Uniform1ui; - -#define GSK_GL_UNIFORM_ARRAY_BITS 5 -#define GSK_GL_UNIFORM_FORMAT_BITS 5 -#define GSK_GL_UNIFORM_OFFSET_BITS 21 - -typedef struct _GskGLUniformInfo -{ - guint initial : 1; - guint format : GSK_GL_UNIFORM_FORMAT_BITS; - guint array_count : GSK_GL_UNIFORM_ARRAY_BITS; - guint offset : GSK_GL_UNIFORM_OFFSET_BITS; -} GskGLUniformInfo; - -G_STATIC_ASSERT (sizeof (GskGLUniformInfo) == 4); - -typedef struct _GskGLUniformMapping -{ - const char *name; - GskGLUniformInfo info; - guint stamp; - int location; -} GskGLUniformMapping; - -typedef struct _GskGLUniformProgram -{ - guint program_id; - guint n_uniforms : 12; - guint has_attachments : 1; - guint n_mappings; - GskGLUniformMapping mappings[32]; -} GskGLUniformProgram; - -typedef struct _GskGLUniformState -{ - GHashTable *programs; - guint8 *values_buf; - guint values_pos; - guint values_len; - GskGLUniformInfo apply_hash[512]; -} GskGLUniformState; - -typedef enum _GskGLUniformKind -{ - GSK_GL_UNIFORM_FORMAT_1F = 1, - GSK_GL_UNIFORM_FORMAT_2F, - GSK_GL_UNIFORM_FORMAT_3F, - GSK_GL_UNIFORM_FORMAT_4F, - - GSK_GL_UNIFORM_FORMAT_1FV, - GSK_GL_UNIFORM_FORMAT_2FV, - GSK_GL_UNIFORM_FORMAT_3FV, - GSK_GL_UNIFORM_FORMAT_4FV, - - GSK_GL_UNIFORM_FORMAT_1I, - GSK_GL_UNIFORM_FORMAT_2I, - GSK_GL_UNIFORM_FORMAT_3I, - GSK_GL_UNIFORM_FORMAT_4I, - - GSK_GL_UNIFORM_FORMAT_1UI, - - GSK_GL_UNIFORM_FORMAT_TEXTURE, - - GSK_GL_UNIFORM_FORMAT_MATRIX, - GSK_GL_UNIFORM_FORMAT_ROUNDED_RECT, - GSK_GL_UNIFORM_FORMAT_COLOR, - - GSK_GL_UNIFORM_FORMAT_LAST -} GskGLUniformFormat; - -G_STATIC_ASSERT (GSK_GL_UNIFORM_FORMAT_LAST < (1 << GSK_GL_UNIFORM_FORMAT_BITS)); - -GskGLUniformState *gsk_gl_uniform_state_new (void); -GskGLUniformState *gsk_gl_uniform_state_ref (GskGLUniformState *state); -void gsk_gl_uniform_state_unref (GskGLUniformState *state); -GskGLUniformProgram *gsk_gl_uniform_state_get_program (GskGLUniformState *state, - guint program, - const GskGLUniformMapping *mappings, - guint n_mappings); -void gsk_gl_uniform_state_end_frame (GskGLUniformState *state); -gsize gsk_gl_uniform_format_size (GskGLUniformFormat format); -gpointer gsk_gl_uniform_state_init_value (GskGLUniformState *state, - GskGLUniformProgram *program, - GskGLUniformFormat format, - guint array_count, - guint key, - GskGLUniformMapping **out_mapping); - -#define GSK_GL_UNIFORM_VALUE(base, offset) ((gpointer)((base) + ((offset) * 4))) -#define gsk_gl_uniform_state_get_uniform_data(state,offset) GSK_GL_UNIFORM_VALUE((state)->values_buf, offset) - -static inline gpointer -gsk_gl_uniform_state_get_value (GskGLUniformState *state, - GskGLUniformProgram *program, - GskGLUniformFormat format, - guint array_count, - guint key, - guint stamp, - GskGLUniformMapping **out_mapping) -{ - GskGLUniformMapping *mapping; - - g_assert (key < G_N_ELEMENTS (program->mappings)); - g_assert (key < program->n_mappings); - - mapping = &program->mappings[key]; - - /* Short-circuit if the program optimized the uniform out */ - if (mapping->location == -1) - return NULL; - - /* If the stamp is the same, then we can ignore the request - * and short-circuit as early as possible. This requires the - * caller to increment their private stamp when they change - * internal state. - * - * This is generally used for the shared uniforms like projection, - * modelview, clip, etc to avoid so many comparisons which cost - * considerable CPU. - */ - if (stamp != 0 && stamp == mapping->stamp) - return NULL; - - if G_LIKELY (format == mapping->info.format && array_count <= mapping->info.array_count) - { - *out_mapping = mapping; - return GSK_GL_UNIFORM_VALUE (state->values_buf, mapping->info.offset); - } - - return gsk_gl_uniform_state_init_value (state, program, format, array_count, key, out_mapping); -} - -G_GNUC_PURE static inline guint -gsk_gl_uniform_state_align (guint current_pos, - guint size) -{ - guint align = size > 8 ? 16 : (size > 4 ? 8 : 4); - guint masked = current_pos & (align - 1); - - g_assert (size > 0); - g_assert (align == 4 || align == 8 || align == 16); - g_assert (masked < align); - - return align - masked; -} - -static inline gpointer -gsk_gl_uniform_state_realloc (GskGLUniformState *state, - guint size, - guint *offset) -{ - guint padding = gsk_gl_uniform_state_align (state->values_pos, size); - - if G_UNLIKELY (state->values_len - padding - size < state->values_pos) - { - state->values_len *= 2; - state->values_buf = g_realloc (state->values_buf, state->values_len); - } - - /* offsets are in slots of 4 to use fewer bits */ - g_assert ((state->values_pos + padding) % 4 == 0); - *offset = (state->values_pos + padding) / 4; - state->values_pos += padding + size; - - return GSK_GL_UNIFORM_VALUE (state->values_buf, *offset); -} - -#define GSK_GL_UNIFORM_STATE_REPLACE(info, u, type, count) \ - G_STMT_START { \ - if ((info)->info.initial && count == (info)->info.array_count) \ - { \ - u = GSK_GL_UNIFORM_VALUE (state->values_buf, (info)->info.offset); \ - } \ - else \ - { \ - guint offset; \ - u = gsk_gl_uniform_state_realloc (state, sizeof(type) * MAX (1, count), &offset); \ - g_assert (offset < (1 << GSK_GL_UNIFORM_OFFSET_BITS)); \ - (info)->info.offset = offset; \ - /* We might have increased array length */ \ - (info)->info.array_count = count; \ - } \ - } G_STMT_END - -static inline void -gsk_gl_uniform_info_changed (GskGLUniformMapping *info, - guint stamp) -{ - info->stamp = stamp; - info->info.initial = FALSE; -} - -static inline void -gsk_gl_uniform_state_set1f (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - float value0) -{ - Uniform1f *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != 0); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_1F, 1, key, stamp, &info))) - { - if (info->info.initial || u->v0 != value0) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, Uniform1f , 1); - u->v0 = value0; - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set2f (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - float value0, - float value1) -{ - Uniform2f *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_2F, 1, key, stamp, &info))) - { - if (info->info.initial || u->v0 != value0 || u->v1 != value1) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, Uniform2f, 1); - u->v0 = value0; - u->v1 = value1; - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set3f (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - float value0, - float value1, - float value2) -{ - Uniform3f *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_3F, 1, key, stamp, &info))) - { - if (info->info.initial || u->v0 != value0 || u->v1 != value1 || u->v2 != value2) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, Uniform3f, 1); - u->v0 = value0; - u->v1 = value1; - u->v2 = value2; - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set4f (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - float value0, - float value1, - float value2, - float value3) -{ - Uniform4f *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_4F, 1, key, stamp, &info))) - { - if (info->info.initial || u->v0 != value0 || u->v1 != value1 || u->v2 != value2 || u->v3 != value3) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, Uniform4f, 1); - u->v0 = value0; - u->v1 = value1; - u->v2 = value2; - u->v3 = value3; - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set1ui (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - guint value0) -{ - Uniform1ui *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_1UI, 1, key, stamp, &info))) - { - if (info->info.initial || u->v0 != value0) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, Uniform1ui, 1); - u->v0 = value0; - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set1i (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - int value0) -{ - Uniform1i *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_1I, 1, key, stamp, &info))) - { - if (info->info.initial || u->v0 != value0) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, Uniform1i, 1); - u->v0 = value0; - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set2i (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - int value0, - int value1) -{ - Uniform2i *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_2I, 1, key, stamp, &info))) - { - if (info->info.initial || u->v0 != value0 || u->v1 != value1) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, Uniform2i, 1); - u->v0 = value0; - u->v1 = value1; - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set3i (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - int value0, - int value1, - int value2) -{ - Uniform3i *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_3I, 1, key, stamp, &info))) - { - if (info->info.initial || u->v0 != value0 || u->v1 != value1 || u->v2 != value2) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, Uniform3i, 1); - u->v0 = value0; - u->v1 = value1; - u->v2 = value2; - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set4i (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - int value0, - int value1, - int value2, - int value3) -{ - Uniform4i *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_4I, 1, key, stamp, &info))) - { - if (info->info.initial || u->v0 != value0 || u->v1 != value1 || u->v2 != value2 || u->v3 != value3) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, Uniform4i, 1); - u->v0 = value0; - u->v1 = value1; - u->v2 = value2; - u->v3 = value3; - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set_rounded_rect (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - const GskRoundedRect *rounded_rect) -{ - GskRoundedRect *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - g_assert (rounded_rect != NULL); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_ROUNDED_RECT, 1, key, stamp, &info))) - { - if (info->info.initial || memcmp (u, rounded_rect, sizeof *u) != 0) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, GskRoundedRect, 1); - memcpy (u, rounded_rect, sizeof *rounded_rect); - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set_matrix (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - const graphene_matrix_t *matrix) -{ - graphene_matrix_t *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - g_assert (matrix != NULL); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_MATRIX, 1, key, stamp, &info))) - { - if (info->info.initial || memcmp (u, matrix, sizeof *u) != 0) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, graphene_matrix_t, 1); - memcpy (u, matrix, sizeof *matrix); - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -/** - * gsk_gl_uniform_state_set_texture: - * @state: a `GskGLUniformState` - * @program: the program id - * @location: the location of the texture - * @texture_slot: a texturing slot such as GL_TEXTURE0 - * - * Sets the uniform expecting a texture to @texture_slot. This API - * expects a texture slot such as GL_TEXTURE0 to reduce chances of - * miss-use by the caller. - * - * The value stored to the uniform is in the form of 0 for GL_TEXTURE0, - * 1 for GL_TEXTURE1, and so on. - */ -static inline void -gsk_gl_uniform_state_set_texture (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - guint texture_slot) -{ - GskGLUniformMapping *info; - guint *u; - - g_assert (texture_slot >= GL_TEXTURE0); - g_assert (texture_slot < GL_TEXTURE16); - - texture_slot -= GL_TEXTURE0; - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_TEXTURE, 1, key, stamp, &info))) - { - if (info->info.initial || *u != texture_slot) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, guint, 1); - *u = texture_slot; - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -/** - * gsk_gl_uniform_state_set_color: - * @state: a `GskGLUniformState` - * @program: a program id > 0 - * @location: the uniform location - * @color: a color to set or %NULL for transparent - * - * Sets a uniform to the color described by @color. This is a convenience - * function to allow callers to avoid having to translate colors to floats - * in other portions of the renderer. - */ -static inline void -gsk_gl_uniform_state_set_color (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - const GdkRGBA *color) -{ - static const GdkRGBA transparent = {0}; - GskGLUniformMapping *info; - GdkRGBA *u; - - g_assert (state != NULL); - g_assert (program != NULL); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_COLOR, 1, key, stamp, &info))) - { - if (color == NULL) - color = &transparent; - - if (info->info.initial || memcmp (color, u, sizeof *u) != 0) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, GdkRGBA, 1); - memcpy (u, color, sizeof *color); - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set1fv (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - guint count, - const float *value) -{ - Uniform1f *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - g_assert (count > 0); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_1FV, count, key, stamp, &info))) - { - if (info->info.initial || count != info->info.array_count || memcmp (u, value, sizeof *u * count) != 0) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, Uniform1f, count); - memcpy (u, value, sizeof (Uniform1f) * count); - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set2fv (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - guint count, - const float *value) -{ - Uniform2f *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - g_assert (count > 0); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_2FV, count, key, stamp, &info))) - { - if (info->info.initial || count != info->info.array_count || memcmp (u, value, sizeof *u * count) != 0) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, Uniform2f, count); - memcpy (u, value, sizeof (Uniform2f) * count); - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set3fv (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - guint count, - const float *value) -{ - Uniform3f *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - g_assert (count > 0); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_3FV, count, key, stamp, &info))) - { - if (info->info.initial || count != info->info.array_count || memcmp (u, value, sizeof *u * count) != 0) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, Uniform3f, count); - memcpy (u, value, sizeof (Uniform3f) * count); - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline void -gsk_gl_uniform_state_set4fv (GskGLUniformState *state, - GskGLUniformProgram *program, - guint key, - guint stamp, - guint count, - const float *value) -{ - Uniform4f *u; - GskGLUniformMapping *info; - - g_assert (state != NULL); - g_assert (program != NULL); - g_assert (count > 0); - - if ((u = gsk_gl_uniform_state_get_value (state, program, GSK_GL_UNIFORM_FORMAT_4FV, count, key, stamp, &info))) - { - if (info->info.initial || count != info->info.array_count || memcmp (u, value, sizeof *u * count) != 0) - { - GSK_GL_UNIFORM_STATE_REPLACE (info, u, Uniform4f, count); - memcpy (u, value, sizeof (Uniform4f) * count); - gsk_gl_uniform_info_changed (info, stamp); - } - } -} - -static inline guint -gsk_gl_uniform_state_fmix (guint program, - guint location) -{ - guint h = (program << 16) | location; - - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - - return h; -} - -/* - * gsk_gl_uniform_state_apply: - * @state: the uniform state - * @program: the program id - * @location: the location of the uniform - * @offset: the offset of the data within the buffer - * @info: the uniform info - * - * This function can be used to apply state that was previously recorded - * by the `GskGLUniformState`. - * - * It is specifically useful from the `GskGLCommandQueue` to execute uniform - * changes but only when they have changed from the current value. - */ -static inline void -gsk_gl_uniform_state_apply (GskGLUniformState *state, - guint program, - guint location, - GskGLUniformInfo info) -{ - guint index = gsk_gl_uniform_state_fmix (program, location) % G_N_ELEMENTS (state->apply_hash); - gconstpointer dataptr = GSK_GL_UNIFORM_VALUE (state->values_buf, info.offset); - - /* aligned, can treat as unsigned */ - if (*(guint *)&info == *(guint *)&state->apply_hash[index]) - return; - - state->apply_hash[index] = info; - - /* TODO: We could do additional comparisons here to make sure we are - * changing state. - */ - - switch (info.format) - { - case GSK_GL_UNIFORM_FORMAT_1F: - glUniform1fv (location, 1, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_2F: - glUniform2fv (location, 1, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_3F: - glUniform3fv (location, 1, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_4F: - glUniform4fv (location, 1, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_1FV: - glUniform1fv (location, info.array_count, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_2FV: - glUniform2fv (location, info.array_count, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_3FV: - glUniform3fv (location, info.array_count, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_4FV: - glUniform4fv (location, info.array_count, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_1I: - case GSK_GL_UNIFORM_FORMAT_TEXTURE: - glUniform1iv (location, 1, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_2I: - glUniform2iv (location, 1, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_3I: - glUniform3iv (location, 1, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_4I: - glUniform4iv (location, 1, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_1UI: - glUniform1uiv (location, 1, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_MATRIX: { - float mat[16]; - graphene_matrix_to_float (dataptr, mat); - glUniformMatrix4fv (location, 1, GL_FALSE, mat); -#if 0 - /* TODO: If Graphene can give us a peek here on platforms - * where the format is float[16] (most/all x86_64?) then - * We can avoid the SIMD operation to convert the format. - */ - G_STATIC_ASSERT (sizeof (graphene_matrix_t) == 16*4); - glUniformMatrix4fv (location, 1, GL_FALSE, dataptr); -#endif - } - break; - - case GSK_GL_UNIFORM_FORMAT_COLOR: - glUniform4fv (location, 1, dataptr); - break; - - case GSK_GL_UNIFORM_FORMAT_ROUNDED_RECT: - glUniform4fv (location, 3, dataptr); - break; - - default: - g_assert_not_reached (); - } -} - -G_END_DECLS - diff --git a/gsk/gl/inlinearray.h b/gsk/gl/inlinearray.h deleted file mode 100644 index 204513d23c0..00000000000 --- a/gsk/gl/inlinearray.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#define DEFINE_INLINE_ARRAY(Type, prefix, ElementType) \ - typedef struct _##Type { \ - gsize len; \ - gsize allocated; \ - ElementType *items; \ - } Type; \ - \ - static inline void \ - prefix##_init (Type *ar, \ - gsize initial_size) \ - { \ - ar->len = 0; \ - ar->allocated = initial_size ? initial_size : 16; \ - ar->items = g_new0 (ElementType, ar->allocated); \ - } \ - \ - static inline void \ - prefix##_clear (Type *ar) \ - { \ - ar->len = 0; \ - ar->allocated = 0; \ - g_clear_pointer (&ar->items, g_free); \ - } \ - \ - static inline ElementType * \ - prefix##_head (Type *ar) \ - { \ - return &ar->items[0]; \ - } \ - \ - static inline ElementType * \ - prefix##_tail (Type *ar) \ - { \ - return &ar->items[ar->len-1]; \ - } \ - \ - static inline ElementType * \ - prefix##_append (Type *ar) \ - { \ - if G_UNLIKELY (ar->len == ar->allocated) \ - { \ - ar->allocated *= 2; \ - ar->items = g_renew (ElementType, ar->items, ar->allocated);\ - } \ - \ - ar->len++; \ - \ - return prefix##_tail (ar); \ - } \ - \ - static inline ElementType * \ - prefix##_append_n (Type *ar, \ - gsize n) \ - { \ - if G_UNLIKELY ((ar->len + n) > ar->allocated) \ - { \ - while ((ar->len + n) > ar->allocated) \ - ar->allocated *= 2; \ - ar->items = g_renew (ElementType, ar->items, ar->allocated);\ - } \ - \ - ar->len += n; \ - \ - return &ar->items[ar->len-n]; \ - } \ - \ - static inline gsize \ - prefix##_index_of (Type *ar, \ - const ElementType *element) \ - { \ - return element - &ar->items[0]; \ - } - diff --git a/gsk/gl/ninesliceprivate.h b/gsk/gl/ninesliceprivate.h deleted file mode 100644 index 7191ec5f135..00000000000 --- a/gsk/gl/ninesliceprivate.h +++ /dev/null @@ -1,308 +0,0 @@ -/* ninesliceprivate.h - * - * Copyright 2017 Timm Bäder - * Copyright 2021 Christian Hergert - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "gskgltextureprivate.h" - -#if 0 -# define DEBUG_NINE_SLICE -#endif - -G_BEGIN_DECLS - -enum { - NINE_SLICE_TOP_LEFT = 0, - NINE_SLICE_TOP_CENTER = 1, - NINE_SLICE_TOP_RIGHT = 2, - NINE_SLICE_LEFT_CENTER = 3, - NINE_SLICE_CENTER = 4, - NINE_SLICE_RIGHT_CENTER = 5, - NINE_SLICE_BOTTOM_LEFT = 6, - NINE_SLICE_BOTTOM_CENTER = 7, - NINE_SLICE_BOTTOM_RIGHT = 8, -}; - -static inline bool G_GNUC_PURE -nine_slice_is_visible (const GskGLTextureNineSlice *slice) -{ - return slice->rect.width > 0 && slice->rect.height > 0; -} - -static inline void -nine_slice_rounded_rect (GskGLTextureNineSlice *slices, - const GskRoundedRect *rect) -{ - const graphene_point_t *origin = &rect->bounds.origin; - const graphene_size_t *size = &rect->bounds.size; - int top_height = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].height, - rect->corner[GSK_CORNER_TOP_RIGHT].height)); - int bottom_height = ceilf (MAX (rect->corner[GSK_CORNER_BOTTOM_LEFT].height, - rect->corner[GSK_CORNER_BOTTOM_RIGHT].height)); - int right_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_RIGHT].width, - rect->corner[GSK_CORNER_BOTTOM_RIGHT].width)); - int left_width = ceilf (MAX (rect->corner[GSK_CORNER_TOP_LEFT].width, - rect->corner[GSK_CORNER_BOTTOM_LEFT].width)); - - /* Top left */ - slices[0].rect.x = origin->x; - slices[0].rect.y = origin->y; - slices[0].rect.width = left_width; - slices[0].rect.height = top_height; - - /* Top center */ - slices[1].rect.x = origin->x + size->width / 2.0 - 0.5; - slices[1].rect.y = origin->y; - slices[1].rect.width = 1; - slices[1].rect.height = top_height; - - /* Top right */ - slices[2].rect.x = origin->x + size->width - right_width; - slices[2].rect.y = origin->y; - slices[2].rect.width = right_width; - slices[2].rect.height = top_height; - - /* Left center */ - slices[3].rect.x = origin->x; - slices[3].rect.y = origin->y + size->height / 2; - slices[3].rect.width = left_width; - slices[3].rect.height = 1; - - /* center */ - slices[4].rect.x = origin->x + size->width / 2.0 - 0.5; - slices[4].rect.y = origin->y + size->height / 2.0 - 0.5; - slices[4].rect.width = 1; - slices[4].rect.height = 1; - - /* Right center */ - slices[5].rect.x = origin->x + size->width - right_width; - slices[5].rect.y = origin->y + (size->height / 2.0) - 0.5; - slices[5].rect.width = right_width; - slices[5].rect.height = 1; - - /* Bottom Left */ - slices[6].rect.x = origin->x; - slices[6].rect.y = origin->y + size->height - bottom_height; - slices[6].rect.width = left_width; - slices[6].rect.height = bottom_height; - - /* Bottom center */ - slices[7].rect.x = origin->x + (size->width / 2.0) - 0.5; - slices[7].rect.y = origin->y + size->height - bottom_height; - slices[7].rect.width = 1; - slices[7].rect.height = bottom_height; - - /* Bottom right */ - slices[8].rect.x = origin->x + size->width - right_width; - slices[8].rect.y = origin->y + size->height - bottom_height; - slices[8].rect.width = right_width; - slices[8].rect.height = bottom_height; - -#ifdef DEBUG_NINE_SLICE - /* These only hold true when the values from ceilf() above - * are greater than one. Otherwise they fail, like will happen - * with the node editor viewing the textures zoomed out. - */ - if (size->width > 1) - g_assert_cmpfloat (size->width, >=, left_width + right_width); - if (size->height > 1) - g_assert_cmpfloat (size->height, >=, top_height + bottom_height); -#endif -} - -static inline void -nine_slice_to_texture_coords (GskGLTextureNineSlice *slices, - int texture_width, - int texture_height) -{ - float fw = texture_width; - float fh = texture_height; - - for (guint i = 0; i < 9; i++) - { - GskGLTextureNineSlice *slice = &slices[i]; - - slice->area.x = slice->rect.x / fw; - slice->area.y = 1.0 - ((slice->rect.y + slice->rect.height) / fh); - slice->area.x2 = ((slice->rect.x + slice->rect.width) / fw); - slice->area.y2 = (1.0 - (slice->rect.y / fh)); - -#ifdef DEBUG_NINE_SLICE - g_assert_cmpfloat (slice->area.x, >=, 0); - g_assert_cmpfloat (slice->area.x, <=, 1); - g_assert_cmpfloat (slice->area.y, >=, 0); - g_assert_cmpfloat (slice->area.y, <=, 1); - g_assert_cmpfloat (slice->area.x2, >, slice->area.x); - g_assert_cmpfloat (slice->area.y2, >, slice->area.y); -#endif - } -} - -static inline void -nine_slice_grow (GskGLTextureNineSlice *slices, - int amount_x, - int amount_y) -{ - if (amount_x == 0 && amount_y == 0) - return; - - /* top left */ - slices[0].rect.x -= amount_x; - slices[0].rect.y -= amount_y; - if (amount_x > slices[0].rect.width) - slices[0].rect.width += amount_x * 2; - else - slices[0].rect.width += amount_x; - - if (amount_y > slices[0].rect.height) - slices[0].rect.height += amount_y * 2; - else - slices[0].rect.height += amount_y; - - - /* Top center */ - slices[1].rect.y -= amount_y; - if (amount_y > slices[1].rect.height) - slices[1].rect.height += amount_y * 2; - else - slices[1].rect.height += amount_y; - - /* top right */ - slices[2].rect.y -= amount_y; - if (amount_x > slices[2].rect.width) - { - slices[2].rect.x -= amount_x; - slices[2].rect.width += amount_x * 2; - } - else - { - slices[2].rect.width += amount_x; - } - - if (amount_y > slices[2].rect.height) - slices[2].rect.height += amount_y * 2; - else - slices[2].rect.height += amount_y; - - - - slices[3].rect.x -= amount_x; - if (amount_x > slices[3].rect.width) - slices[3].rect.width += amount_x * 2; - else - slices[3].rect.width += amount_x; - - /* Leave center alone */ - - if (amount_x > slices[5].rect.width) - { - slices[5].rect.x -= amount_x; - slices[5].rect.width += amount_x * 2; - } - else - { - slices[5].rect.width += amount_x; - } - - - /* Bottom left */ - slices[6].rect.x -= amount_x; - if (amount_x > slices[6].rect.width) - { - slices[6].rect.width += amount_x * 2; - } - else - { - slices[6].rect.width += amount_x; - } - - if (amount_y > slices[6].rect.height) - { - slices[6].rect.y -= amount_y; - slices[6].rect.height += amount_y * 2; - } - else - { - slices[6].rect.height += amount_y; - } - - - /* Bottom center */ - if (amount_y > slices[7].rect.height) - { - slices[7].rect.y -= amount_y; - slices[7].rect.height += amount_y * 2; - } - else - { - slices[7].rect.height += amount_y; - } - - if (amount_x > slices[8].rect.width) - { - slices[8].rect.x -= amount_x; - slices[8].rect.width += amount_x * 2; - } - else - { - slices[8].rect.width += amount_x; - } - - if (amount_y > slices[8].rect.height) - { - slices[8].rect.y -= amount_y; - slices[8].rect.height += amount_y * 2; - } - else - { - slices[8].rect.height += amount_y; - } - -#ifdef DEBUG_NINE_SLICE - /* These cannot be relied on in all cases right now, specifically - * when viewing data zoomed out. - */ - for (guint i = 0; i < 9; i ++) - { - g_assert_cmpint (slices[i].rect.x, >=, 0); - g_assert_cmpint (slices[i].rect.y, >=, 0); - g_assert_cmpint (slices[i].rect.width, >=, 0); - g_assert_cmpint (slices[i].rect.height, >=, 0); - } - - /* Rows don't overlap */ - for (guint i = 0; i < 3; i++) - { - int lhs = slices[i * 3 + 0].rect.x + slices[i * 3 + 0].rect.width; - int rhs = slices[i * 3 + 1].rect.x; - - /* Ignore the case where we are scaled out and the - * positioning is degenerate, such as from node-editor. - */ - if (rhs > 1) - g_assert_cmpint (lhs, <, rhs); - } -#endif - -} - -G_END_DECLS - diff --git a/gsk/gl/resources/blend.glsl b/gsk/gl/resources/blend.glsl deleted file mode 100644 index 41301c4a8b5..00000000000 --- a/gsk/gl/resources/blend.glsl +++ /dev/null @@ -1,314 +0,0 @@ -// VERTEX_SHADER: -// blend.glsl - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -// blend.glsl - -uniform int u_mode; -uniform sampler2D u_source2; - -float -combine (float source, float backdrop) -{ - return source + backdrop * (1.0 - source); -} - -vec4 -composite (vec4 Cs, vec4 Cb, vec3 B) -{ - float ao = Cs.a + Cb.a * (1.0 - Cs.a); - vec3 Co = (Cs.a*(1.0 - Cb.a)*Cs.rgb + Cs.a*Cb.a*B + (1.0 - Cs.a)*Cb.a*Cb.rgb) / ao; - return vec4(Co, ao); -} - -vec4 -normal (vec4 Cs, vec4 Cb) -{ - return composite (Cs, Cb, Cs.rgb); -} - -vec4 -multiply (vec4 Cs, vec4 Cb) -{ - return composite (Cs, Cb, Cs.rgb * Cb.rgb); -} - -vec4 -difference (vec4 Cs, vec4 Cb) -{ - return composite (Cs, Cb, abs(Cs.rgb - Cb.rgb)); -} - -vec4 -screen (vec4 Cs, vec4 Cb) -{ - return composite (Cs, Cb, Cs.rgb + Cb.rgb - Cs.rgb * Cb.rgb); -} - -float -hard_light (float source, float backdrop) -{ - if (source <= 0.5) - return 2.0 * backdrop * source; - else - return 2.0 * (backdrop + source - backdrop * source) - 1.0; -} - -vec4 -hard_light (vec4 Cs, vec4 Cb) -{ - vec3 B = vec3 (hard_light (Cs.r, Cb.r), - hard_light (Cs.g, Cb.g), - hard_light (Cs.b, Cb.b)); - return composite (Cs, Cb, B); -} - -float -soft_light (float source, float backdrop) -{ - float db; - - if (backdrop <= 0.25) - db = ((16.0 * backdrop - 12.0) * backdrop + 4.0) * backdrop; - else - db = sqrt (backdrop); - - if (source <= 0.5) - return backdrop - (1.0 - 2.0 * source) * backdrop * (1.0 - backdrop); - else - return backdrop + (2.0 * source - 1.0) * (db - backdrop); -} - -vec4 -soft_light (vec4 Cs, vec4 Cb) -{ - vec3 B = vec3 (soft_light (Cs.r, Cb.r), - soft_light (Cs.g, Cb.g), - soft_light (Cs.b, Cb.b)); - return composite (Cs, Cb, B); -} - -vec4 -overlay (vec4 Cs, vec4 Cb) -{ - vec3 B = vec3 (hard_light (Cb.r, Cs.r), - hard_light (Cb.g, Cs.g), - hard_light (Cb.b, Cs.b)); - return composite (Cs, Cb, B); -} - -vec4 -darken (vec4 Cs, vec4 Cb) -{ - vec3 B = min (Cs.rgb, Cb.rgb); - return composite (Cs, Cb, B); -} - -vec4 -lighten (vec4 Cs, vec4 Cb) -{ - vec3 B = max (Cs.rgb, Cb.rgb); - return composite (Cs, Cb, B); -} - -float -color_dodge (float source, float backdrop) -{ - return (source == 1.0) ? source : min (backdrop / (1.0 - source), 1.0); -} - -vec4 -color_dodge (vec4 Cs, vec4 Cb) -{ - vec3 B = vec3 (color_dodge (Cs.r, Cb.r), - color_dodge (Cs.g, Cb.g), - color_dodge (Cs.b, Cb.b)); - return composite (Cs, Cb, B); -} - - -float -color_burn (float source, float backdrop) -{ - return (source == 0.0) ? source : max ((1.0 - ((1.0 - backdrop) / source)), 0.0); -} - -vec4 -color_burn (vec4 Cs, vec4 Cb) -{ - vec3 B = vec3 (color_burn (Cs.r, Cb.r), - color_burn (Cs.g, Cb.g), - color_burn (Cs.b, Cb.b)); - return composite (Cs, Cb, B); -} - -vec4 -exclusion (vec4 Cs, vec4 Cb) -{ - vec3 B = Cb.rgb + Cs.rgb - 2.0 * Cb.rgb * Cs.rgb; - return composite (Cs, Cb, B); -} - -float -lum (vec3 c) -{ - return 0.3 * c.r + 0.59 * c.g + 0.11 * c.b; -} - -vec3 -clip_color (vec3 c) -{ - float l = clamp (lum (c), 0.0, 1.0); - float n = min (c.r, min (c.g, c.b)); - float x = max (c.r, max (c.g, c.b)); - if (n < 0.0) c = l + (((c - l) * l) / (l - n)); - if (x > 1.0) c = l + (((c - l) * (1.0 - l)) / (x - l)); - return c; -} - -vec3 -set_lum (vec3 c, float l) -{ - float d = l - lum (c); - return clip_color (vec3 (c.r + d, c.g + d, c.b + d)); -} - -float -sat (vec3 c) -{ - return max (c.r, max (c.g, c.b)) - min (c.r, min (c.g, c.b)); -} - -vec3 -set_sat (vec3 c, float s) -{ - float cmin = min (c.r, min (c.g, c.b)); - float cmax = max (c.r, max (c.g, c.b)); - vec3 res; - - if (cmax == cmin) - res = vec3 (0, 0, 0); - else - { - if (c.r == cmax) - { - if (c.g == cmin) - { - res.b = ((c.b - cmin) * s) / (cmax - cmin); - res.g = 0.0; - } - else - { - res.g = ((c.g - cmin) * s) / (cmax - cmin); - res.b = 0.0; - } - res.r = s; - } - else if (c.g == cmax) - { - if (c.r == cmin) - { - res.b = ((c.b - cmin) * s) / (cmax - cmin); - res.r = 0.0; - } - else - { - res.r = ((c.r - cmin) * s) / (cmax - cmin); - res.b = 0.0; - } - res.g = s; - } - else - { - if (c.r == cmin) - { - res.g = ((c.g - cmin) * s) / (cmax - cmin); - res.r = 0.0; - } - else - { - res.r = ((c.r - cmin) * s) / (cmax - cmin); - res.g = 0.0; - } - res.b = s; - } - } - return res; -} - -vec4 -color (vec4 Cs, vec4 Cb) -{ - vec3 B = set_lum (Cs.rgb, lum (Cb.rgb)); - return composite (Cs, Cb, B); -} - -vec4 -hue (vec4 Cs, vec4 Cb) -{ - vec3 B = set_lum (set_sat (Cs.rgb, sat (Cb.rgb)), lum (Cb.rgb)); - return composite (Cs, Cb, B); -} - -vec4 -saturation (vec4 Cs, vec4 Cb) -{ - vec3 B = set_lum (set_sat (Cb.rgb, sat (Cs.rgb)), lum (Cb.rgb)); - return composite (Cs, Cb, B); -} - -vec4 -luminosity (vec4 Cs, vec4 Cb) -{ - vec3 B = set_lum (Cb.rgb, lum (Cs.rgb)); - return composite (Cs, Cb, B); -} - -void main() { - vec4 bottom_color = GskTexture(u_source, vUv); - vec4 top_color = GskTexture(u_source2, vUv); - - vec4 result; - if (u_mode == 0) - result = normal(top_color, bottom_color); - else if (u_mode == 1) - result = multiply(top_color, bottom_color); - else if (u_mode == 2) - result = screen(top_color, bottom_color); - else if (u_mode == 3) - result = overlay(top_color, bottom_color); - else if (u_mode == 4) - result = darken(top_color, bottom_color); - else if (u_mode == 5) - result = lighten(top_color, bottom_color); - else if (u_mode == 6) - result = color_dodge(top_color, bottom_color); - else if (u_mode == 7) - result = color_burn(top_color, bottom_color); - else if (u_mode == 8) - result = hard_light(top_color, bottom_color); - else if (u_mode == 9) - result = soft_light(top_color, bottom_color); - else if (u_mode == 10) - result = difference(top_color, bottom_color); - else if (u_mode == 11) - result = exclusion(top_color, bottom_color); - else if (u_mode == 12) - result = color(top_color, bottom_color); - else if (u_mode == 13) - result = hue(top_color, bottom_color); - else if (u_mode == 14) - result = saturation(top_color, bottom_color); - else if (u_mode == 15) - result = luminosity(top_color, bottom_color); - else - discard; - - gskSetScaledOutputColor(result, u_alpha); -} diff --git a/gsk/gl/resources/blit.glsl b/gsk/gl/resources/blit.glsl deleted file mode 100644 index ced047b9a98..00000000000 --- a/gsk/gl/resources/blit.glsl +++ /dev/null @@ -1,17 +0,0 @@ -// VERTEX_SHADER: -// blit.glsl - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -// blit.glsl - -void main() { - vec4 diffuse = GskTexture(u_source, vUv); - - gskSetScaledOutputColor(diffuse, u_alpha); -} diff --git a/gsk/gl/resources/blur.glsl b/gsk/gl/resources/blur.glsl deleted file mode 100644 index 7e7c1c25184..00000000000 --- a/gsk/gl/resources/blur.glsl +++ /dev/null @@ -1,59 +0,0 @@ -// VERTEX_SHADER: -// blur.glsl - -uniform float u_blur_radius; -uniform vec2 u_blur_size; -uniform vec2 u_blur_dir; - -_OUT_ vec2 pixel_step; -_OUT_ float pixels_per_side; -_OUT_ vec3 initial_gaussian; - -const float PI = 3.14159265; -const float RADIUS_MULTIPLIER = 2.0; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); - - pixel_step = (vec2(1.0) / u_blur_size) * u_blur_dir; - pixels_per_side = floor(u_blur_radius * RADIUS_MULTIPLIER / 2.0); - - float sigma = u_blur_radius / 2.0; // *shrug* - initial_gaussian.x = 1.0 / (sqrt(2.0 * PI) * sigma); - initial_gaussian.y = exp(-0.5 / (sigma * sigma)); - initial_gaussian.z = initial_gaussian.y * initial_gaussian.y; -} - -// FRAGMENT_SHADER: -// blur.glsl - -_IN_ vec2 pixel_step; -_IN_ float pixels_per_side; -_IN_ vec3 initial_gaussian; - -// blur_radius 0 is NOT supported and MUST be caught before. - -// Partially from http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html -void main() { - vec3 incrementalGaussian = initial_gaussian; - - float coefficientSum = 0.0; - vec4 sum = GskTexture(u_source, vUv) * incrementalGaussian.x; - coefficientSum += incrementalGaussian.x; - incrementalGaussian.xy *= incrementalGaussian.yz; - - vec2 p = pixel_step; - for (int i = 1; i <= int(pixels_per_side); i++) { - sum += GskTexture(u_source, vUv - p) * incrementalGaussian.x; - sum += GskTexture(u_source, vUv + p) * incrementalGaussian.x; - - coefficientSum += 2.0 * incrementalGaussian.x; - incrementalGaussian.xy *= incrementalGaussian.yz; - - p += pixel_step; - } - - gskSetOutputColor(sum / coefficientSum); -} diff --git a/gsk/gl/resources/border.glsl b/gsk/gl/resources/border.glsl deleted file mode 100644 index 0fa3203887a..00000000000 --- a/gsk/gl/resources/border.glsl +++ /dev/null @@ -1,45 +0,0 @@ -// VERTEX_SHADER: -// border.glsl - -uniform vec4 u_widths; -uniform vec4[3] u_outline_rect; - -_OUT_ vec4 final_color; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - final_color = gsk_scaled_premultiply(aColor, u_alpha); - - GskRoundedRect outside = gsk_create_rect(u_outline_rect); - GskRoundedRect inside = gsk_rounded_rect_shrink (outside, u_widths); - - gsk_rounded_rect_transform(outside, u_modelview); - gsk_rounded_rect_transform(inside, u_modelview); - - gsk_rounded_rect_normalize(outside); - gsk_rounded_rect_normalize(inside); - - gsk_rounded_rect_encode(outside, transformed_outside_outline); - gsk_rounded_rect_encode(inside, transformed_inside_outline); -} - -// FRAGMENT_SHADER: -// border.glsl - -uniform vec4[3] u_outline_rect; - -_IN_ vec4 final_color; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - vec2 frag = gsk_get_frag_coord(); - float outer_coverage = gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag); - float inner_coverage = gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag); - float alpha = clamp(outer_coverage - inner_coverage, 0.0, 1.0); - - gskSetScaledOutputColor(final_color, alpha); -} diff --git a/gsk/gl/resources/color.glsl b/gsk/gl/resources/color.glsl deleted file mode 100644 index ba98b1b1707..00000000000 --- a/gsk/gl/resources/color.glsl +++ /dev/null @@ -1,20 +0,0 @@ -// VERTEX_SHADER: -// color.glsl - -_OUT_ vec4 final_color; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - final_color = gsk_scaled_premultiply(aColor, u_alpha); -} - -// FRAGMENT_SHADER: -// color.glsl - -_IN_ vec4 final_color; - -void main() { - gskSetOutputColor(final_color); -} - diff --git a/gsk/gl/resources/color_matrix.glsl b/gsk/gl/resources/color_matrix.glsl deleted file mode 100644 index 51835f056fa..00000000000 --- a/gsk/gl/resources/color_matrix.glsl +++ /dev/null @@ -1,27 +0,0 @@ -// VERTEX_SHADER: -// color_matrix.glsl - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -// color_matrix.glsl - -uniform mat4 u_color_matrix; -uniform vec4 u_color_offset; - -void main() { - vec4 color = GskTexture(u_source, vUv); - - // Un-premultilpy - if (color.a != 0.0) - color.rgb /= color.a; - - color = u_color_matrix * color + u_color_offset; - color = clamp(color, 0.0, 1.0); - - gskSetOutputColor(gsk_scaled_premultiply(color, u_alpha)); -} diff --git a/gsk/gl/resources/coloring.glsl b/gsk/gl/resources/coloring.glsl deleted file mode 100644 index 0abe06ff05f..00000000000 --- a/gsk/gl/resources/coloring.glsl +++ /dev/null @@ -1,33 +0,0 @@ -// VERTEX_SHADER: -// coloring.glsl - -_OUT_ vec4 final_color; -_OUT_ float use_color; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); - - // We use this shader for both plain glyphs (used as mask) - // and color glyphs (used as source). The renderer sets - // aColor to vec4(-1) for color glyphs. - if (distance(aColor,vec4(-1)) < 0.1) - use_color = 0.0; - else - use_color = 1.0; - - final_color = gsk_scaled_premultiply(aColor, u_alpha); -} - -// FRAGMENT_SHADER: -// coloring.glsl - -_IN_ vec4 final_color; -_IN_ float use_color; - -void main() { - vec4 diffuse = GskTexture(u_source, vUv); - - gskSetOutputColor(mix(diffuse * u_alpha, final_color * diffuse.a, use_color)); -} diff --git a/gsk/gl/resources/conic_gradient.glsl b/gsk/gl/resources/conic_gradient.glsl deleted file mode 100644 index 9f569784396..00000000000 --- a/gsk/gl/resources/conic_gradient.glsl +++ /dev/null @@ -1,85 +0,0 @@ -// VERTEX_SHADER -// conic_gradient.glsl - -uniform vec4 u_geometry; - -_OUT_ vec2 coord; - -void main() { - gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0)); - - vec2 mv0 = u_modelview[0].xy; - vec2 mv1 = u_modelview[1].xy; - vec2 offset = aPosition - u_geometry.xy; - - coord = vec2(dot(mv0, offset), dot(mv1, offset)); -} - -// FRAGMENT_SHADER: -// conic_gradient.glsl - -#define MAX_COLOR_STOPS 6 - -#ifdef GSK_LEGACY -uniform int u_num_color_stops; -#else -uniform highp int u_num_color_stops; // Why? Because it works like this. -#endif - -uniform vec4 u_geometry; -uniform float u_color_stops[MAX_COLOR_STOPS * 5]; - -_IN_ vec2 coord; - -float get_offset(int index) { - // u_color_stops[5 * index] makes Intel Windows driver crash. - // See https://gitlab.gnome.org/GNOME/gtk/-/issues/3783 - int base = 5 * index; - return u_color_stops[base]; -} - -vec4 get_color(int index) { - int base = 5 * index + 1; - - return vec4(u_color_stops[base], - u_color_stops[base + 1], - u_color_stops[base + 2], - u_color_stops[base + 3]); -} - -void main() { - // direction of point in range [-PI, PI] - vec2 pos = floor(coord); - float angle = atan(pos.y, pos.x); - - // fract() does the modulo here, so now we have progress - // into the current conic - float offset = fract(angle * u_geometry.z + u_geometry.w); - float curr_offset; - float next_offset; - - next_offset = get_offset(0); - if (offset < next_offset) { - gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha)); - return; - } - - if (offset >= get_offset(u_num_color_stops - 1)) { - gskSetOutputColor(gsk_scaled_premultiply(get_color(u_num_color_stops - 1), u_alpha)); - return; - } - - for (int i = 0; i < MAX_COLOR_STOPS; i++) { - curr_offset = next_offset; - next_offset = get_offset(i + 1); - - if (offset < next_offset) { - float f = (offset - curr_offset) / (next_offset - curr_offset); - vec4 curr_color = gsk_scaled_premultiply (get_color(i), u_alpha); - vec4 next_color = gsk_scaled_premultiply (get_color(i + 1), u_alpha); - vec4 color = mix(curr_color, next_color, f); - gskSetOutputColor(color); - return; - } - } -} diff --git a/gsk/gl/resources/cross_fade.glsl b/gsk/gl/resources/cross_fade.glsl deleted file mode 100644 index e61af597e91..00000000000 --- a/gsk/gl/resources/cross_fade.glsl +++ /dev/null @@ -1,24 +0,0 @@ -// VERTEX_SHADER: -// cross_fade.glsl - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -// cross_fade.glsl - -uniform float u_progress; -uniform sampler2D u_source2; - -void main() { - vec4 source1 = GskTexture(u_source, vUv); // start child - vec4 source2 = GskTexture(u_source2, vUv); // end child - - float p_start = (1.0 - u_progress) * u_alpha; - float p_end = u_progress * u_alpha; - vec4 color = (p_start * source1) + (p_end * source2); - gskSetOutputColor(color); -} diff --git a/gsk/gl/resources/custom.glsl b/gsk/gl/resources/custom.glsl deleted file mode 100644 index 6f91df6cf4f..00000000000 --- a/gsk/gl/resources/custom.glsl +++ /dev/null @@ -1,25 +0,0 @@ -// VERTEX_SHADER: -// custom.glsl - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -// custom.glsl - -// The shader supplies: -void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv); - -uniform vec2 u_size; -uniform sampler2D u_source2; -uniform sampler2D u_source3; -uniform sampler2D u_source4; - -void main() { - vec4 fragColor; - vec2 fragCoord = vec2(vUv.x * u_size.x, (1.0-vUv.y) * u_size.y); - mainImage(fragColor, fragCoord, u_size, vUv); - gskSetOutputColor(fragColor); -} diff --git a/gsk/gl/resources/external.glsl b/gsk/gl/resources/external.glsl deleted file mode 100644 index d2aaadd0037..00000000000 --- a/gsk/gl/resources/external.glsl +++ /dev/null @@ -1,34 +0,0 @@ -// VERTEX_SHADER: -// external.glsl - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -// external.glsl - -#if defined(GSK_GLES) || defined(GSK_GLES3) -uniform samplerExternalOES u_external_source; -#else -/* Just to make this compile, we won't use it without GLES */ -uniform sampler2D u_external_source; -#endif - -uniform int u_premultiply; - -void main() { -/* Open-code this here, since GskTexture() expects a sampler2D */ -#if defined(GSK_GLES) || defined(GSK_LEGACY) - vec4 color = texture2D(u_external_source, vUv); -#else - vec4 color = texture(u_external_source, vUv); -#endif - - if (u_premultiply == 1) - color.rgb *= color.a; - - gskSetOutputColor(color); -} diff --git a/gsk/gl/resources/filled_border.glsl b/gsk/gl/resources/filled_border.glsl deleted file mode 100644 index 23af3aa89c6..00000000000 --- a/gsk/gl/resources/filled_border.glsl +++ /dev/null @@ -1,50 +0,0 @@ -// VERTEX_SHADER: -// filled_border.glsl - -uniform vec4 u_widths; -uniform vec4[3] u_outline_rect; - -_OUT_ vec4 outer_color; -_OUT_ vec4 inner_color; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - outer_color = gsk_scaled_premultiply(aColor, u_alpha); - inner_color = gsk_scaled_premultiply(aColor2, u_alpha); - - GskRoundedRect outside = gsk_create_rect(u_outline_rect); - GskRoundedRect inside = gsk_rounded_rect_shrink (outside, u_widths); - - gsk_rounded_rect_transform(outside, u_modelview); - gsk_rounded_rect_transform(inside, u_modelview); - - gsk_rounded_rect_normalize(outside); - gsk_rounded_rect_normalize(inside); - - gsk_rounded_rect_encode(outside, transformed_outside_outline); - gsk_rounded_rect_encode(inside, transformed_inside_outline); -} - -// FRAGMENT_SHADER: -// filled_border.glsl - -uniform vec4[3] u_outline_rect; - -_IN_ vec4 outer_color; -_IN_ vec4 inner_color; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - vec2 frag = gsk_get_frag_coord(); - float outer_coverage = gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag); - float inner_coverage = gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag); - - float alpha = clamp(outer_coverage - inner_coverage, 0.0, 1.0); - float alpha2 = clamp(inner_coverage, 0.0, 1.0); - - gskSetOutputColor((outer_color * alpha) + (inner_color * alpha2)); -} diff --git a/gsk/gl/resources/inset_shadow.glsl b/gsk/gl/resources/inset_shadow.glsl deleted file mode 100644 index 83d4287f130..00000000000 --- a/gsk/gl/resources/inset_shadow.glsl +++ /dev/null @@ -1,47 +0,0 @@ -// VERTEX_SHADER: -// inset_shadow.glsl - -uniform float u_spread; -uniform vec2 u_offset; -uniform vec4[3] u_outline_rect; - -_OUT_ vec4 final_color; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - final_color = gsk_scaled_premultiply(aColor, u_alpha); - - GskRoundedRect outside = gsk_create_rect(u_outline_rect); - GskRoundedRect inside = gsk_rounded_rect_shrink(outside, vec4(u_spread)); - - gsk_rounded_rect_offset(inside, u_offset); - - gsk_rounded_rect_transform(outside, u_modelview); - gsk_rounded_rect_transform(inside, u_modelview); - - gsk_rounded_rect_normalize(outside); - gsk_rounded_rect_normalize(inside); - - gsk_rounded_rect_encode(outside, transformed_outside_outline); - gsk_rounded_rect_encode(inside, transformed_inside_outline); -} - -// FRAGMENT_SHADER: -// inset_shadow.glsl - -_IN_ vec4 final_color; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - vec2 frag = gsk_get_frag_coord(); - - float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) - - gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag), - 0.0, 1.0); - - gskSetScaledOutputColor(final_color, alpha); -} diff --git a/gsk/gl/resources/linear_gradient.glsl b/gsk/gl/resources/linear_gradient.glsl deleted file mode 100644 index d266cdbc79f..00000000000 --- a/gsk/gl/resources/linear_gradient.glsl +++ /dev/null @@ -1,107 +0,0 @@ -// VERTEX_SHADER -// linear_gradient.glsl -uniform vec4 u_points; - -_OUT_ vec4 info; - -void main() { - gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0)); - - vec2 mv0 = u_modelview[0].xy; - vec2 mv1 = u_modelview[1].xy; - vec2 offset = aPosition - u_points.xy; - vec2 coord = vec2(dot(mv0, offset), - dot(mv1, offset)); - - // Original equation: - // VS | maxDist = length(end - start); - // VS | gradient = end - start; - // VS | gradientLength = length(gradient); - // FS | pos = frag_coord - start - // FS | proj = (dot(gradient, pos) / (gradientLength * gradientLength)) * gradient - // FS | offset = length(proj) / maxDist - - // Simplified formula derivation: - // 1. Notice that maxDist = gradientLength: - // offset = length(proj) / gradientLength - // 2. Let gnorm = gradient / gradientLength, then: - // proj = (dot(gnorm * gradientLength, pos) / (gradientLength * gradientLength)) * (gnorm * gradientLength) = - // = dot(gnorm, pos) * gnorm - // 3. Since gnorm is unit length then: - // length(proj) = length(dot(gnorm, pos) * gnorm) = dot(gnorm, pos) - // 4. We can avoid the FS division by passing a scaled pos from the VS: - // offset = dot(gnorm, pos) / gradientLength = dot(gnorm, pos / gradientLength) - // 5. 1.0 / length(gradient) is inversesqrt(dot(gradient, gradient)) in GLSL - vec2 gradient = vec2(dot(mv0, u_points.zw), - dot(mv1, u_points.zw)); - float rcp_gradient_length = inversesqrt(dot(gradient, gradient)); - - info = rcp_gradient_length * vec4(coord, gradient); -} - -// FRAGMENT_SHADER: -// linear_gradient.glsl - -#define MAX_COLOR_STOPS 6 - -#ifdef GSK_LEGACY -uniform int u_num_color_stops; -#else -uniform highp int u_num_color_stops; // Why? Because it works like this. -#endif - -uniform float u_color_stops[MAX_COLOR_STOPS * 5]; -uniform bool u_repeat; - -_IN_ vec4 info; - -float get_offset(int index) { - // u_color_stops[5 * index] makes Intel Windows driver crash. - // See https://gitlab.gnome.org/GNOME/gtk/-/issues/3783 - int base = 5 * index; - return u_color_stops[base]; -} - -vec4 get_color(int index) { - int base = 5 * index + 1; - - return vec4(u_color_stops[base], - u_color_stops[base + 1], - u_color_stops[base + 2], - u_color_stops[base + 3]); -} - -void main() { - float offset = dot(info.xy, info.zw); - float curr_offset; - float next_offset; - - if (u_repeat) { - offset = fract(offset); - } - - next_offset = get_offset(0); - if (offset < next_offset) { - gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha)); - return; - } - - if (offset >= get_offset(u_num_color_stops - 1)) { - gskSetOutputColor(gsk_scaled_premultiply(get_color(u_num_color_stops - 1), u_alpha)); - return; - } - - for (int i = 0; i < MAX_COLOR_STOPS; i++) { - curr_offset = next_offset; - next_offset = get_offset(i + 1); - - if (offset < next_offset) { - float f = (offset - curr_offset) / (next_offset - curr_offset); - vec4 curr_color = gsk_scaled_premultiply (get_color(i), u_alpha); - vec4 next_color = gsk_scaled_premultiply (get_color(i + 1), u_alpha); - vec4 color = mix(curr_color, next_color, f); - gskSetOutputColor(color); - return; - } - } -} diff --git a/gsk/gl/resources/mask.glsl b/gsk/gl/resources/mask.glsl deleted file mode 100644 index 0c2814be9f1..00000000000 --- a/gsk/gl/resources/mask.glsl +++ /dev/null @@ -1,39 +0,0 @@ -// VERTEX_SHADER: -// mask.glsl - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -// mask.glsl - -uniform int u_mode; -uniform sampler2D u_mask; - -float -luminance (vec3 color) -{ - return dot (vec3 (0.2126, 0.7152, 0.0722), color); -} - -void main() { - vec4 source = GskTexture(u_source, vUv); - vec4 mask = GskTexture(u_mask, vUv); - float mask_value; - - if (u_mode == 0) - mask_value = mask.a; - else if (u_mode == 1) - mask_value = 1.0 - mask.a; - else if (u_mode == 2) - mask_value = luminance (mask.rgb); - else if (u_mode == 3) - mask_value = mask.a - luminance (mask.rgb); - else - mask_value = 0.0; - - gskSetOutputColor(vec4 (source * mask_value)); -} diff --git a/gsk/gl/resources/outset_shadow.glsl b/gsk/gl/resources/outset_shadow.glsl deleted file mode 100644 index fac86c0093c..00000000000 --- a/gsk/gl/resources/outset_shadow.glsl +++ /dev/null @@ -1,35 +0,0 @@ -// VERTEX_SHADER: -// outset_shadow.glsl - -uniform vec4[3] u_outline_rect; - -_OUT_ vec4 final_color; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); - - final_color = gsk_scaled_premultiply(aColor, u_alpha); - - GskRoundedRect outline = gsk_create_rect(u_outline_rect); - gsk_rounded_rect_transform(outline, u_modelview); - gsk_rounded_rect_normalize(outline); - gsk_rounded_rect_encode(outline, transformed_outline); -} - -// FRAGMENT_SHADER: -// outset_shadow.glsl - -_IN_ vec4 final_color; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outline; - -void main() { - vec2 frag = gsk_get_frag_coord(); - float alpha = GskTexture(u_source, vUv).a; - - alpha *= (1.0 - clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outline), frag), 0.0, 1.0)); - - gskSetScaledOutputColor(final_color, alpha); -} diff --git a/gsk/gl/resources/preamble.fs.glsl b/gsk/gl/resources/preamble.fs.glsl deleted file mode 100644 index 0c366bd8476..00000000000 --- a/gsk/gl/resources/preamble.fs.glsl +++ /dev/null @@ -1,166 +0,0 @@ -uniform sampler2D u_source; -uniform mat4 u_projection; -uniform mat4 u_modelview; -uniform float u_alpha; -uniform vec4 u_viewport; -uniform vec4[3] u_clip_rect; - -#if defined(GSK_LEGACY) -_OUT_ vec4 outputColor; -#elif !defined(GSK_GLES) -_OUT_ vec4 outputColor; -#endif - -_IN_ vec2 vUv; - - -GskRoundedRect gsk_decode_rect(_GSK_ROUNDED_RECT_UNIFORM_ r) -{ - GskRoundedRect rect; -#if defined(GSK_GLES) || defined(GSK_LEGACY) - rect = GskRoundedRect(r[0], r[1], r[2]); -#else - rect = r; -#endif - gsk_rounded_rect_normalize (rect); - - return rect; -} - -float -gsk_ellipsis_dist (vec2 p, vec2 radius) -{ - if (radius == vec2(0, 0)) - return 0.0; - - vec2 p0 = p / radius; - vec2 p1 = 2.0 * p0 / radius; - - return (dot(p0, p0) - 1.0) / length (p1); -} - -float -gsk_ellipsis_coverage (vec2 point, vec2 center, vec2 radius) -{ - float d = gsk_ellipsis_dist (point - center, radius); - return clamp (0.5 - d, 0.0, 1.0); -} - -float -gsk_rounded_rect_coverage (GskRoundedRect r, vec2 p) -{ - if (p.x < r.bounds.x || p.y < r.bounds.y || - p.x >= r.bounds.z || p.y >= r.bounds.w) - return 0.0; - - vec2 ref_tl = r.corner_points1.xy; - vec2 ref_tr = r.corner_points1.zw; - vec2 ref_br = r.corner_points2.xy; - vec2 ref_bl = r.corner_points2.zw; - - if (p.x >= ref_tl.x && p.x >= ref_bl.x && - p.x <= ref_tr.x && p.x <= ref_br.x) - return 1.0; - - if (p.y >= ref_tl.y && p.y >= ref_tr.y && - p.y <= ref_bl.y && p.y <= ref_br.y) - return 1.0; - - vec2 rad_tl = r.corner_points1.xy - r.bounds.xy; - vec2 rad_tr = r.corner_points1.zw - r.bounds.zy; - vec2 rad_br = r.corner_points2.xy - r.bounds.zw; - vec2 rad_bl = r.corner_points2.zw - r.bounds.xw; - - float d_tl = gsk_ellipsis_coverage(p, ref_tl, rad_tl); - float d_tr = gsk_ellipsis_coverage(p, ref_tr, rad_tr); - float d_br = gsk_ellipsis_coverage(p, ref_br, rad_br); - float d_bl = gsk_ellipsis_coverage(p, ref_bl, rad_bl); - - vec4 corner_coverages = 1.0 - vec4(d_tl, d_tr, d_br, d_bl); - - bvec4 is_out = bvec4(p.x < ref_tl.x && p.y < ref_tl.y, - p.x > ref_tr.x && p.y < ref_tr.y, - p.x > ref_br.x && p.y > ref_br.y, - p.x < ref_bl.x && p.y > ref_bl.y); - - return 1.0 - dot(vec4(is_out), corner_coverages); -} - -float -gsk_rect_coverage (vec4 r, vec2 p) -{ - if (p.x < r.x || p.y < r.y || - p.x >= r.z || p.y >= r.w) - return 0.0; - - return 1.0; -} - -vec4 GskTexture(sampler2D sampler, vec2 texCoords) { -#if defined(GSK_GLES) || defined(GSK_LEGACY) - return texture2D(sampler, texCoords); -#else - return texture(sampler, texCoords); -#endif -} - -#ifdef GSK_GL3 -layout(origin_upper_left) in vec4 gl_FragCoord; -#endif - -vec2 gsk_get_frag_coord() { - vec2 fc = gl_FragCoord.xy; - -#ifdef GSK_GL3 - fc += u_viewport.xy; -#else - fc.x += u_viewport.x; - fc.y = (u_viewport.y + u_viewport.w) - fc.y; -#endif - - return fc; -} - -void gskSetOutputColor(vec4 color) { - vec4 result; - -#if defined(NO_CLIP) - result = color; -#elif defined(RECT_CLIP) - float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect), - gsk_get_frag_coord()); - result = color * coverage; -#else - float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect), - gsk_get_frag_coord()); - result = color * coverage; -#endif - -#if defined(GSK_GLES) || defined(GSK_LEGACY) - gl_FragColor = result; -#else - outputColor = result; -#endif -} - -void gskSetScaledOutputColor(vec4 color, float alpha) { - vec4 result; - -#if defined(NO_CLIP) - result = color * alpha; -#elif defined(RECT_CLIP) - float coverage = gsk_rect_coverage(gsk_get_bounds(u_clip_rect), - gsk_get_frag_coord()); - result = color * (alpha * coverage); -#else - float coverage = gsk_rounded_rect_coverage(gsk_create_rect(u_clip_rect), - gsk_get_frag_coord()); - result = color * (alpha * coverage); -#endif - -#if defined(GSK_GLES) || defined(GSK_LEGACY) - gl_FragColor = result; -#else - outputColor = result; -#endif -} diff --git a/gsk/gl/resources/preamble.glsl b/gsk/gl/resources/preamble.glsl deleted file mode 100644 index f37957947be..00000000000 --- a/gsk/gl/resources/preamble.glsl +++ /dev/null @@ -1,118 +0,0 @@ -#if defined(GSK_GLES3) -#extension GL_OES_EGL_image_external_essl3 : require -#elif defined (GSK_GLES) -#extension GL_OES_EGL_image_external : require -#endif - -#ifndef GSK_LEGACY -precision highp float; -#endif - -#if defined(GSK_GLES) || defined(GSK_LEGACY) -#define _OUT_ varying -#define _IN_ varying -#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3] -#else -#define _OUT_ out -#define _IN_ in -#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect -#endif - - -struct GskRoundedRect -{ - vec4 bounds; // Top left and bottom right - // Look, arrays can't be in structs if you want to return the struct - // from a function in gles or whatever. Just kill me. - vec4 corner_points1; // xy = top left, zw = top right - vec4 corner_points2; // xy = bottom right, zw = bottom left -}; - -void gsk_rounded_rect_normalize(inout GskRoundedRect r) -{ - if (r.bounds.x > r.bounds.z) - { - float t = r.bounds.x; - r.bounds.x = r.bounds.z; - r.bounds.z = t; - - vec2 c = r.corner_points1.xy; - r.corner_points1.xy = r.corner_points1.zw; - r.corner_points1.zw = c; - - c = r.corner_points2.xy; - r.corner_points2.xy = r.corner_points2.zw; - r.corner_points2.zw = c; - } - - if (r.bounds.y > r.bounds.w) - { - float t = r.bounds.y; - r.bounds.y = r.bounds.w; - r.bounds.w = t; - - vec2 c = r.corner_points1.xy; - r.corner_points1.xy = r.corner_points2.xy; - r.corner_points2.xy = c; - - c = r.corner_points1.zw; - r.corner_points1.zw = r.corner_points2.zw; - r.corner_points2.zw = c; - } -} - -void gsk_bounds_normalize (inout vec4 bounds) -{ - if (bounds.x > bounds.z) - { - float t = bounds.x; - bounds.x = bounds.z; - bounds.z = t; - } - if (bounds.y > bounds.w) - { - float t = bounds.y; - bounds.y = bounds.w; - bounds.w = t; - } -} - -// Transform from a C GskRoundedRect to what we need. -GskRoundedRect -gsk_create_rect(vec4[3] data) -{ - vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw); - - vec4 corner_points1 = vec4(bounds.xy + data[1].xy, - bounds.zy + vec2(data[1].zw * vec2(-1, 1))); - vec4 corner_points2 = vec4(bounds.zw + (data[2].xy * vec2(-1, -1)), - bounds.xw + vec2(data[2].zw * vec2(1, -1))); - - GskRoundedRect rect = GskRoundedRect(bounds, corner_points1, corner_points2); - - gsk_rounded_rect_normalize (rect); - - return rect; -} - -vec4 -gsk_get_bounds(vec4[3] data) -{ - vec4 bounds = vec4(data[0].xy, data[0].xy + data[0].zw); - - gsk_bounds_normalize (bounds); - - return bounds; -} - -vec4 gsk_premultiply(vec4 c) { - return vec4(c.rgb * c.a, c.a); -} - -vec4 gsk_scaled_premultiply(vec4 c, float s) { - // Fast version of gsk_premultiply(c) * s - // 4 muls instead of 7 - float a = s * c.a; - - return vec4(c.rgb * a, a); -} diff --git a/gsk/gl/resources/preamble.vs.glsl b/gsk/gl/resources/preamble.vs.glsl deleted file mode 100644 index ff3d5bb5a72..00000000000 --- a/gsk/gl/resources/preamble.vs.glsl +++ /dev/null @@ -1,75 +0,0 @@ -uniform mat4 u_projection; -uniform mat4 u_modelview; -uniform float u_alpha; - -#if defined(GSK_GLES) || defined(GSK_LEGACY) -attribute vec2 aPosition; -attribute vec2 aUv; -attribute vec4 aColor; -attribute vec4 aColor2; -_OUT_ vec2 vUv; -#else -_IN_ vec2 aPosition; -_IN_ vec2 aUv; -_IN_ vec4 aColor; -_IN_ vec4 aColor2; -_OUT_ vec2 vUv; -#endif - -// amount is: top, right, bottom, left -GskRoundedRect -gsk_rounded_rect_shrink (GskRoundedRect r, vec4 amount) -{ - vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz; - vec4 new_corner_points1 = r.corner_points1; - vec4 new_corner_points2 = r.corner_points2; - - if (r.corner_points1.xy == r.bounds.xy) new_corner_points1.xy = new_bounds.xy; - if (r.corner_points1.zw == r.bounds.zy) new_corner_points1.zw = new_bounds.zy; - if (r.corner_points2.xy == r.bounds.zw) new_corner_points2.xy = new_bounds.zw; - if (r.corner_points2.zw == r.bounds.xw) new_corner_points2.zw = new_bounds.xw; - new_corner_points1 = clamp (new_corner_points1, new_bounds.xyxy, new_bounds.zwzw); - new_corner_points2 = clamp (new_corner_points2, new_bounds.xyxy, new_bounds.zwzw); - - return GskRoundedRect (new_bounds, new_corner_points1, new_corner_points2); -} - -void -gsk_rounded_rect_offset(inout GskRoundedRect r, vec2 offset) -{ - r.bounds.xy += offset; - r.bounds.zw += offset; - r.corner_points1.xy += offset; - r.corner_points1.zw += offset; - r.corner_points2.xy += offset; - r.corner_points2.zw += offset; -} - -void gsk_rounded_rect_transform(inout GskRoundedRect r, mat4 mat) -{ - r.bounds.xy = (mat * vec4(r.bounds.xy, 0.0, 1.0)).xy; - r.bounds.zw = (mat * vec4(r.bounds.zw, 0.0, 1.0)).xy; - - r.corner_points1.xy = (mat * vec4(r.corner_points1.xy, 0.0, 1.0)).xy; - r.corner_points1.zw = (mat * vec4(r.corner_points1.zw, 0.0, 1.0)).xy; - - r.corner_points2.xy = (mat * vec4(r.corner_points2.xy, 0.0, 1.0)).xy; - r.corner_points2.zw = (mat * vec4(r.corner_points2.zw, 0.0, 1.0)).xy; -} - -#if defined(GSK_LEGACY) -// Can't have out or inout array parameters... -#define gsk_rounded_rect_encode(r, uni) uni[0] = r.bounds; uni[1] = r.corner_points1; uni[2] = r.corner_points2; -#else -void gsk_rounded_rect_encode(GskRoundedRect r, out _GSK_ROUNDED_RECT_UNIFORM_ out_r) -{ -#if defined(GSK_GLES) - out_r[0] = r.bounds; - out_r[1] = r.corner_points1; - out_r[2] = r.corner_points2; -#else - out_r = r; -#endif -} - -#endif diff --git a/gsk/gl/resources/premultiply.glsl b/gsk/gl/resources/premultiply.glsl deleted file mode 100644 index 892c2c44538..00000000000 --- a/gsk/gl/resources/premultiply.glsl +++ /dev/null @@ -1,19 +0,0 @@ -// VERTEX_SHADER: -// premultiply.glsl - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -// premultiply.glsl - -void main() { - vec4 color = GskTexture(u_source, vUv); - - color.rgb *= color.a; - - gskSetOutputColor(color); -} diff --git a/gsk/gl/resources/radial_gradient.glsl b/gsk/gl/resources/radial_gradient.glsl deleted file mode 100644 index df955162375..00000000000 --- a/gsk/gl/resources/radial_gradient.glsl +++ /dev/null @@ -1,87 +0,0 @@ -// VERTEX_SHADER -// radial_gradient.glsl - -uniform vec4 u_geometry; - -_OUT_ vec2 coord; - -void main() { - gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0)); - - vec2 mv0 = u_modelview[0].xy; - vec2 mv1 = u_modelview[1].xy; - vec2 offset = aPosition - u_geometry.xy; - vec2 dir = vec2(dot(mv0, offset), - dot(mv1, offset)); - - coord = dir * u_geometry.zw; -} - -// FRAGMENT_SHADER: -// radial_gradient.glsl - -#define MAX_COLOR_STOPS 6 - -#ifdef GSK_LEGACY -uniform int u_num_color_stops; -#else -uniform highp int u_num_color_stops; -#endif - -uniform bool u_repeat; -uniform vec2 u_range; -uniform float u_color_stops[MAX_COLOR_STOPS * 5]; - -_IN_ vec2 coord; - -float get_offset(int index) { - // u_color_stops[5 * index] makes Intel Windows driver crash. - // See https://gitlab.gnome.org/GNOME/gtk/-/issues/3783 - int base = 5 * index; - return u_color_stops[base]; -} - -vec4 get_color(int index) { - int base = 5 * index + 1; - - return vec4(u_color_stops[base], - u_color_stops[base + 1], - u_color_stops[base + 2], - u_color_stops[base + 3]); -} - -void main() { - // Reverse scale - float offset = length(coord) * u_range.x + u_range.y; - float curr_offset; - float next_offset; - - if (u_repeat) { - offset = fract(offset); - } - - next_offset = get_offset(0); - if (offset < next_offset) { - gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha)); - return; - } - - if (offset >= get_offset(u_num_color_stops - 1)) { - gskSetOutputColor(gsk_scaled_premultiply(get_color(u_num_color_stops - 1), u_alpha)); - return; - } - - for (int i = 0; i < MAX_COLOR_STOPS; i++) { - curr_offset = next_offset; - next_offset = get_offset(i + 1); - - if (offset < next_offset) { - float f = (offset - curr_offset) / (next_offset - curr_offset); - vec4 curr_color = gsk_scaled_premultiply (get_color(i), u_alpha); - vec4 next_color = gsk_scaled_premultiply (get_color(i + 1), u_alpha); - vec4 color = mix(curr_color, next_color, f); - gskSetOutputColor(color); - return; - } - } -} diff --git a/gsk/gl/resources/repeat.glsl b/gsk/gl/resources/repeat.glsl deleted file mode 100644 index 1b811df2daa..00000000000 --- a/gsk/gl/resources/repeat.glsl +++ /dev/null @@ -1,44 +0,0 @@ -// VERTEX_SHADER: -// repeat.glsl - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - vUv = vec2(aUv.x, aUv.y); -} - -// FRAGMENT_SHADER: -// repeat.glsl - -uniform vec4 u_child_bounds; -uniform vec4 u_texture_rect; - -float wrap(float f, float wrap_for) { - return mod(f, wrap_for); -} - -/* We get the texture coordinates via vUv, - * but that might be on a texture atlas, so we need to do the - * wrapping ourselves. - */ -void main() { - - /* We map the texture coordinate to [1;0], then wrap it and scale the result again */ - - float tw = u_texture_rect.z - u_texture_rect.x; - float th = u_texture_rect.w - u_texture_rect.y; - - float mapped_x = (vUv.x - u_texture_rect.x) / tw; - float mapped_y = (vUv.y - u_texture_rect.y) / th; - - float wrapped_x = wrap(u_child_bounds.x + mapped_x * u_child_bounds.z, 1.0); - float wrapped_y = wrap(u_child_bounds.y + mapped_y * u_child_bounds.w, 1.0); - - vec2 tp; - tp.x = u_texture_rect.x + (wrapped_x * tw); - tp.y = u_texture_rect.y + (wrapped_y * th); - - vec4 diffuse = GskTexture(u_source, tp); - - gskSetScaledOutputColor(diffuse, u_alpha); -} diff --git a/gsk/gl/resources/unblurred_outset_shadow.glsl b/gsk/gl/resources/unblurred_outset_shadow.glsl deleted file mode 100644 index cd44212c933..00000000000 --- a/gsk/gl/resources/unblurred_outset_shadow.glsl +++ /dev/null @@ -1,44 +0,0 @@ -// VERTEX_SHADER: -// unblurred_outset_shadow.glsl - -uniform float u_spread; -uniform vec2 u_offset; -uniform vec4[3] u_outline_rect; - -_OUT_ vec4 final_color; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_OUT_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0); - - final_color = gsk_premultiply(aColor) * u_alpha; - - GskRoundedRect inside = gsk_create_rect(u_outline_rect); - GskRoundedRect outside = gsk_rounded_rect_shrink(inside, vec4(- u_spread)); - - gsk_rounded_rect_offset(outside, u_offset); - - gsk_rounded_rect_transform(outside, u_modelview); - gsk_rounded_rect_transform(inside, u_modelview); - - gsk_rounded_rect_encode(outside, transformed_outside_outline); - gsk_rounded_rect_encode(inside, transformed_inside_outline); -} - -// FRAGMENT_SHADER: -// unblurred_outset_shadow.glsl -_IN_ vec4 final_color; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_outside_outline; -_IN_ _GSK_ROUNDED_RECT_UNIFORM_ transformed_inside_outline; - -void main() { - vec2 frag = gsk_get_frag_coord(); - - float alpha = clamp(gsk_rounded_rect_coverage(gsk_decode_rect(transformed_outside_outline), frag) - - gsk_rounded_rect_coverage(gsk_decode_rect(transformed_inside_outline), frag), - 0.0, 1.0); - - gskSetScaledOutputColor(final_color, alpha); -} - diff --git a/gsk/gl/stb_rect_pack.c b/gsk/gl/stb_rect_pack.c deleted file mode 100644 index 2ef31512b4d..00000000000 --- a/gsk/gl/stb_rect_pack.c +++ /dev/null @@ -1,427 +0,0 @@ - -#include "stb_rect_pack.h" -#define STB_RECT_PACK_IMPLEMENTATION -////////////////////////////////////////////////////////////////////////////// -// -// IMPLEMENTATION SECTION -// - -#ifdef STB_RECT_PACK_IMPLEMENTATION -#ifndef STBRP_SORT -#include -#define STBRP_SORT qsort -#endif - -#ifndef STBRP_ASSERT -#include -#define STBRP_ASSERT assert -#endif - -#ifdef _MSC_VER -#define STBRP__NOTUSED(v) (void)(v) -#define STBRP__CDECL __cdecl -#else -#define STBRP__NOTUSED(v) (void)sizeof(v) -#define STBRP__CDECL -#endif - -enum -{ - STBRP__INIT_skyline = 1 -}; - -STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) -{ - switch (context->init_mode) { - case STBRP__INIT_skyline: - STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); - context->heuristic = heuristic; - break; - default: - STBRP_ASSERT(0); - } -} - -STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) -{ - if (allow_out_of_mem) - // if it's ok to run out of memory, then don't bother aligning them; - // this gives better packing, but may fail due to OOM (even though - // the rectangles easily fit). @TODO a smarter approach would be to only - // quantize once we've hit OOM, then we could get rid of this parameter. - context->align = 1; - else { - // if it's not ok to run out of memory, then quantize the widths - // so that num_nodes is always enough nodes. - // - // I.e. num_nodes * align >= width - // align >= width / num_nodes - // align = ceil(width/num_nodes) - - context->align = (context->width + context->num_nodes-1) / context->num_nodes; - } -} - -STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) -{ - int i; - - for (i=0; i < num_nodes-1; ++i) - nodes[i].next = &nodes[i+1]; - nodes[i].next = NULL; - context->init_mode = STBRP__INIT_skyline; - context->heuristic = STBRP_HEURISTIC_Skyline_default; - context->free_head = &nodes[0]; - context->active_head = &context->extra[0]; - context->width = width; - context->height = height; - context->num_nodes = num_nodes; - stbrp_setup_allow_out_of_mem(context, 0); - - // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) - context->extra[0].x = 0; - context->extra[0].y = 0; - context->extra[0].next = &context->extra[1]; - context->extra[1].x = (stbrp_coord) width; - context->extra[1].y = (1<<30); - context->extra[1].next = NULL; -} - -// find minimum y position if it starts at x1 -static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) -{ - stbrp_node *node = first; - int x1 = x0 + width; - int min_y, visited_width, waste_area; - - STBRP__NOTUSED(c); - - STBRP_ASSERT(first->x <= x0); - - #if 0 - // skip in case we're past the node - while (node->next->x <= x0) - ++node; - #else - STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency - #endif - - STBRP_ASSERT(node->x <= x0); - - min_y = 0; - waste_area = 0; - visited_width = 0; - while (node->x < x1) { - if (node->y > min_y) { - // raise min_y higher. - // we've accounted for all waste up to min_y, - // but we'll now add more waste for everything we've visited - waste_area += visited_width * (node->y - min_y); - min_y = node->y; - // the first time through, visited_width might be reduced - if (node->x < x0) - visited_width += node->next->x - x0; - else - visited_width += node->next->x - node->x; - } else { - // add waste area - int under_width = node->next->x - node->x; - if (under_width + visited_width > width) - under_width = width - visited_width; - waste_area += under_width * (min_y - node->y); - visited_width += under_width; - } - node = node->next; - } - - *pwaste = waste_area; - return min_y; -} - -typedef struct -{ - int x,y; - stbrp_node **prev_link; -} stbrp__findresult; - -static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) -{ - int best_waste = (1<<30), best_x, best_y = (1 << 30); - stbrp__findresult fr; - stbrp_node **prev, *node, *tail, **best = NULL; - - // align to multiple of c->align - width = (width + c->align - 1); - width -= width % c->align; - STBRP_ASSERT(width % c->align == 0); - - // if it can't possibly fit, bail immediately - if (width > c->width || height > c->height) { - fr.prev_link = NULL; - fr.x = fr.y = 0; - return fr; - } - - node = c->active_head; - prev = &c->active_head; - while (node->x + width <= c->width) { - int y,waste; - y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); - if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL - // bottom left - if (y < best_y) { - best_y = y; - best = prev; - } - } else { - // best-fit - if (y + height <= c->height) { - // can only use it if it first vertically - if (y < best_y || (y == best_y && waste < best_waste)) { - best_y = y; - best_waste = waste; - best = prev; - } - } - } - prev = &node->next; - node = node->next; - } - - best_x = (best == NULL) ? 0 : (*best)->x; - - // if doing best-fit (BF), we also have to try aligning right edge to each node position - // - // e.g, if fitting - // - // ____________________ - // |____________________| - // - // into - // - // | | - // | ____________| - // |____________| - // - // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned - // - // This makes BF take about 2x the time - - if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { - tail = c->active_head; - node = c->active_head; - prev = &c->active_head; - // find first node that's admissible - while (tail->x < width) - tail = tail->next; - while (tail) { - int xpos = tail->x - width; - int y,waste; - STBRP_ASSERT(xpos >= 0); - // find the left position that matches this - while (node->next->x <= xpos) { - prev = &node->next; - node = node->next; - } - STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); - y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); - if (y + height <= c->height) { - if (y <= best_y) { - if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { - best_x = xpos; - STBRP_ASSERT(y <= best_y); - best_y = y; - best_waste = waste; - best = prev; - } - } - } - tail = tail->next; - } - } - - fr.prev_link = best; - fr.x = best_x; - fr.y = best_y; - return fr; -} - -static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) -{ - // find best position according to heuristic - stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); - stbrp_node *node, *cur; - - // bail if: - // 1. it failed - // 2. the best node doesn't fit (we don't always check this) - // 3. we're out of memory - if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { - res.prev_link = NULL; - return res; - } - - // on success, create new node - node = context->free_head; - node->x = (stbrp_coord) res.x; - node->y = (stbrp_coord) (res.y + height); - - context->free_head = node->next; - - // insert the new node into the right starting point, and - // let 'cur' point to the remaining nodes needing to be - // stiched back in - - cur = *res.prev_link; - if (cur->x < res.x) { - // preserve the existing one, so start testing with the next one - stbrp_node *next = cur->next; - cur->next = node; - cur = next; - } else { - *res.prev_link = node; - } - - // from here, traverse cur and free the nodes, until we get to one - // that shouldn't be freed - while (cur->next && cur->next->x <= res.x + width) { - stbrp_node *next = cur->next; - // move the current node to the free list - cur->next = context->free_head; - context->free_head = cur; - cur = next; - } - - // stitch the list back in - node->next = cur; - - if (cur->x < res.x + width) - cur->x = (stbrp_coord) (res.x + width); - -#ifdef _DEBUG - cur = context->active_head; - while (cur->x < context->width) { - STBRP_ASSERT(cur->x < cur->next->x); - cur = cur->next; - } - STBRP_ASSERT(cur->next == NULL); - - { - int count=0; - cur = context->active_head; - while (cur) { - cur = cur->next; - ++count; - } - cur = context->free_head; - while (cur) { - cur = cur->next; - ++count; - } - STBRP_ASSERT(count == context->num_nodes+2); - } -#endif - - return res; -} - -static int STBRP__CDECL rect_height_compare(const void *a, const void *b) -{ - const stbrp_rect *p = (const stbrp_rect *) a; - const stbrp_rect *q = (const stbrp_rect *) b; - if (p->h > q->h) - return -1; - if (p->h < q->h) - return 1; - return (p->w > q->w) ? -1 : (p->w < q->w); -} - -static int STBRP__CDECL rect_original_order(const void *a, const void *b) -{ - const stbrp_rect *p = (const stbrp_rect *) a; - const stbrp_rect *q = (const stbrp_rect *) b; - return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); -} - -STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) -{ - int i, all_rects_packed = 1; - - // we use the 'was_packed' field internally to allow sorting/unsorting - for (i=0; i < num_rects; ++i) { - rects[i].was_packed = i; - } - - // sort according to heuristic - STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); - - for (i=0; i < num_rects; ++i) { - if (rects[i].w == 0 || rects[i].h == 0) { - rects[i].x = rects[i].y = 0; // empty rect needs no space - } else { - stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); - if (fr.prev_link) { - rects[i].x = (stbrp_coord) fr.x; - rects[i].y = (stbrp_coord) fr.y; - } else { - rects[i].x = rects[i].y = STBRP__MAXVAL; - } - } - } - - // unsort - STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); - - // set was_packed flags and all_rects_packed status - for (i=0; i < num_rects; ++i) { - rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); - if (!rects[i].was_packed) - all_rects_packed = 0; - } - - // return the all_rects_packed status - return all_rects_packed; -} -#endif - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -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. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -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 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. ------------------------------------------------------------------------------- -*/ diff --git a/gsk/gl/stb_rect_pack.h b/gsk/gl/stb_rect_pack.h deleted file mode 100644 index 2a3833ec80d..00000000000 --- a/gsk/gl/stb_rect_pack.h +++ /dev/null @@ -1,197 +0,0 @@ -// stb_rect_pack.h - v1.01 - public domain - rectangle packing -// Sean Barrett 2014 -// -// Useful for e.g. packing rectangular textures into an atlas. -// Does not do rotation. -// -// Before #including, -// -// #define STB_RECT_PACK_IMPLEMENTATION -// -// in the file that you want to have the implementation. -// -// Not necessarily the awesomest packing method, but better than -// the totally naive one in stb_truetype (which is primarily what -// this is meant to replace). -// -// Has only had a few tests run, may have issues. -// -// More docs to come. -// -// No memory allocations; uses qsort() and assert() from stdlib. -// Can override those by defining STBRP_SORT and STBRP_ASSERT. -// -// This library currently uses the Skyline Bottom-Left algorithm. -// -// Please note: better rectangle packers are welcome! Please -// implement them to the same API, but with a different init -// function. -// -// Credits -// -// Library -// Sean Barrett -// Minor features -// Martins Mozeiko -// github:IntellectualKitty -// -// Bugfixes / warning fixes -// Jeremy Jaussaud -// Fabian Giesen -// -// Version history: -// -// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section -// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles -// 0.99 (2019-02-07) warning fixes -// 0.11 (2017-03-03) return packing success/fail result -// 0.10 (2016-10-25) remove cast-away-const to avoid warnings -// 0.09 (2016-08-27) fix compiler warnings -// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) -// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) -// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort -// 0.05: added STBRP_ASSERT to allow replacing assert -// 0.04: fixed minor bug in STBRP_LARGE_RECTS support -// 0.01: initial release -// -// LICENSE -// -// See end of file for license information. - -////////////////////////////////////////////////////////////////////////////// -// -// INCLUDE SECTION -// - -#pragma once - -#define STB_RECT_PACK_VERSION 1 - -#ifdef STBRP_STATIC -#define STBRP_DEF static -#else -#define STBRP_DEF extern -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct stbrp_context stbrp_context; -typedef struct stbrp_node stbrp_node; -typedef struct stbrp_rect stbrp_rect; - -typedef int stbrp_coord; - -#define STBRP__MAXVAL 0x7fffffff -// Mostly for internal use, but this is the maximum supported coordinate value. - -STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); -// Assign packed locations to rectangles. The rectangles are of type -// 'stbrp_rect' defined below, stored in the array 'rects', and there -// are 'num_rects' many of them. -// -// Rectangles which are successfully packed have the 'was_packed' flag -// set to a non-zero value and 'x' and 'y' store the minimum location -// on each axis (i.e. bottom-left in cartesian coordinates, top-left -// if you imagine y increasing downwards). Rectangles which do not fit -// have the 'was_packed' flag set to 0. -// -// You should not try to access the 'rects' array from another thread -// while this function is running, as the function temporarily reorders -// the array while it executes. -// -// To pack into another rectangle, you need to call stbrp_init_target -// again. To continue packing into the same rectangle, you can call -// this function again. Calling this multiple times with multiple rect -// arrays will probably produce worse packing results than calling it -// a single time with the full rectangle array, but the option is -// available. -// -// The function returns 1 if all of the rectangles were successfully -// packed and 0 otherwise. - -struct stbrp_rect -{ - // reserved for your use: - int id; - - // input: - stbrp_coord w, h; - - // output: - stbrp_coord x, y; - int was_packed; // non-zero if valid packing - -}; // 16 bytes, nominally - - -STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); -// Initialize a rectangle packer to: -// pack a rectangle that is 'width' by 'height' in dimensions -// using temporary storage provided by the array 'nodes', which is 'num_nodes' long -// -// You must call this function every time you start packing into a new target. -// -// There is no "shutdown" function. The 'nodes' memory must stay valid for -// the following stbrp_pack_rects() call (or calls), but can be freed after -// the call (or calls) finish. -// -// Note: to guarantee best results, either: -// 1. make sure 'num_nodes' >= 'width' -// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' -// -// If you don't do either of the above things, widths will be quantized to multiples -// of small integers to guarantee the algorithm doesn't run out of temporary storage. -// -// If you do #2, then the non-quantized algorithm will be used, but the algorithm -// may run out of temporary storage and be unable to pack some rectangles. - -STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); -// Optionally call this function after init but before doing any packing to -// change the handling of the out-of-temp-memory scenario, described above. -// If you call init again, this will be reset to the default (false). - - -STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); -// Optionally select which packing heuristic the library should use. Different -// heuristics will produce better/worse results for different data sets. -// If you call init again, this will be reset to the default. - -enum -{ - STBRP_HEURISTIC_Skyline_default=0, - STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, - STBRP_HEURISTIC_Skyline_BF_sortHeight -}; - - -////////////////////////////////////////////////////////////////////////////// -// -// the details of the following structures don't matter to you, but they must -// be visible so you can handle the memory allocations for them - -struct stbrp_node -{ - stbrp_coord x,y; - stbrp_node *next; -}; - -struct stbrp_context -{ - int width; - int height; - int align; - int init_mode; - int heuristic; - int num_nodes; - stbrp_node *active_head; - stbrp_node *free_head; - stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' -}; - -#ifdef __cplusplus -} -#endif - - diff --git a/gsk/gskrenderer.c b/gsk/gskrenderer.c index f6c8f7c0115..8947bc96f70 100644 --- a/gsk/gskrenderer.c +++ b/gsk/gskrenderer.c @@ -514,9 +514,6 @@ get_renderer_for_name (const char *renderer_name) #endif else if (g_ascii_strcasecmp (renderer_name, "cairo") == 0) return GSK_TYPE_CAIRO_RENDERER; - else if (g_ascii_strcasecmp (renderer_name, "opengl") == 0 || - g_ascii_strcasecmp (renderer_name, "gl") == 0) - return GSK_TYPE_GL_RENDERER; else if (g_ascii_strcasecmp (renderer_name, "ngl") == 0) return gsk_ngl_renderer_get_type (); #ifdef GDK_RENDERING_VULKAN diff --git a/gsk/meson.build b/gsk/meson.build index 0daa9fe72f2..3ce154fa242 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -1,29 +1,3 @@ -gsk_private_gl_shaders = [ - 'gl/resources/preamble.glsl', - 'gl/resources/preamble.fs.glsl', - 'gl/resources/preamble.vs.glsl', - 'gl/resources/border.glsl', - 'gl/resources/blit.glsl', - 'gl/resources/coloring.glsl', - 'gl/resources/color.glsl', - 'gl/resources/linear_gradient.glsl', - 'gl/resources/radial_gradient.glsl', - 'gl/resources/conic_gradient.glsl', - 'gl/resources/color_matrix.glsl', - 'gl/resources/blur.glsl', - 'gl/resources/inset_shadow.glsl', - 'gl/resources/outset_shadow.glsl', - 'gl/resources/unblurred_outset_shadow.glsl', - 'gl/resources/cross_fade.glsl', - 'gl/resources/blend.glsl', - 'gl/resources/repeat.glsl', - 'gl/resources/custom.glsl', - 'gl/resources/filled_border.glsl', - 'gl/resources/mask.glsl', - 'gl/resources/external.glsl', - 'gl/resources/premultiply.glsl', -] - gsk_public_sources = files([ 'gskcairorenderer.c', 'gskdiff.c', @@ -41,7 +15,6 @@ gsk_public_sources = files([ 'gskroundedrect.c', 'gskstroke.c', 'gsktransform.c', - 'gl/gskglrenderer.c', 'gpu/gsknglrenderer.c', 'gpu/gskvulkanrenderer.c', ]) @@ -53,22 +26,8 @@ gsk_private_sources = files([ 'gskdebug.c', 'gskprivate.c', 'gskprofiler.c', - 'gl/gskglattachmentstate.c', - 'gl/gskglbuffer.c', - 'gl/gskglcommandqueue.c', - 'gl/gskglcompiler.c', - 'gl/gskgldriver.c', - 'gl/gskglglyphlibrary.c', - 'gl/gskgliconlibrary.c', - 'gl/gskglprogram.c', - 'gl/gskglrenderjob.c', - 'gl/gskglshadowlibrary.c', - 'gl/gskgltexturelibrary.c', - 'gl/gskgluniformstate.c', - 'gl/gskgltexture.c', - 'gl/gskglprofiler.c', - 'gl/stb_rect_pack.c', 'gl/fp16.c', + 'gl/gskglrenderer.c', 'gpu/gskglbuffer.c', 'gpu/gskgldevice.c', 'gpu/gskglframe.c', @@ -136,8 +95,6 @@ install_headers(gsk_public_headers, 'gsk.h', subdir: 'gtk-4.0/gsk') gsk_public_gl_headers = files([ 'gl/gskglrenderer.h', ]) -install_headers(gsk_public_gl_headers, subdir: 'gtk-4.0/gsk/gl') -gsk_public_headers += gsk_public_gl_headers gsk_public_gpu_headers = files([ 'gpu/gskvulkanrenderer.h' @@ -182,7 +139,6 @@ gsk_resources_xml = custom_target(output: 'gsk.resources.xml', command: [ find_program('gen-gsk-gresources-xml.py'), '@OUTPUT@', - gsk_private_gl_shaders, gsk_private_vulkan_compiled_shaders, gsk_private_vulkan_shaders, gsk_private_gpu_gl_shaders, -- GitLab From dfab46947a142e339616f6d4fbdc3da29face0b3 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 3 Oct 2024 06:52:13 -0400 Subject: [PATCH 3/6] Use the gl renderer again everywhere We're renaming ngl back to gl. --- demos/node-editor/node-editor-window.c | 5 +---- gdk/gdkdmabufegl.c | 3 +-- gsk/gpu/gskglrenderer.h | 15 +++++++++++++++ gsk/gpu/gsknglrenderer.c | 2 ++ gsk/gskrendernodeimpl.c | 2 +- testsuite/gdk/memorytexture.c | 2 +- testsuite/gdk/texture-threads.c | 2 +- testsuite/gsk/misc.c | 6 +++--- testsuite/gsk/scaling.c | 11 ++--------- tools/gtk-rendernode-tool-benchmark.c | 2 +- tools/gtk-rendernode-tool-utils.c | 4 ++-- 11 files changed, 30 insertions(+), 24 deletions(-) create mode 100644 gsk/gpu/gskglrenderer.h diff --git a/demos/node-editor/node-editor-window.c b/demos/node-editor/node-editor-window.c index 058ced5b3a5..c084bc659dc 100644 --- a/demos/node-editor/node-editor-window.c +++ b/demos/node-editor/node-editor-window.c @@ -925,7 +925,7 @@ export_image_response_cb (GObject *source, GdkTexture *texture; GskRenderer *renderer; - renderer = gsk_ngl_renderer_new (); + renderer = gsk_gl_renderer_new (); if (!gsk_renderer_realize_for_display (renderer, gdk_display_get_default (), NULL)) { g_object_unref (renderer); @@ -1234,9 +1234,6 @@ node_editor_window_realize (GtkWidget *widget) node_editor_window_add_renderer (self, gsk_gl_renderer_new (), "OpenGL"); - node_editor_window_add_renderer (self, - gsk_ngl_renderer_new (), - "NGL"); #ifdef GDK_RENDERING_VULKAN node_editor_window_add_renderer (self, gsk_vulkan_renderer_new (), diff --git a/gdk/gdkdmabufegl.c b/gdk/gdkdmabufegl.c index c7d08459829..a8325ca32b9 100644 --- a/gdk/gdkdmabufegl.c +++ b/gdk/gdkdmabufegl.c @@ -229,7 +229,6 @@ gdk_dmabuf_egl_create_image (GdkDisplay *display, typedef struct _GskRenderer GskRenderer; extern GskRenderer * gsk_gl_renderer_new (void); -extern GskRenderer * gsk_ngl_renderer_new (void); extern gboolean gsk_renderer_realize_for_display (GskRenderer *renderer, GdkDisplay *display, GError **error); @@ -276,7 +275,7 @@ gdk_dmabuf_egl_init (GdkDisplay *display) return; } - renderer = gsk_ngl_renderer_new (); + renderer = gsk_gl_renderer_new (); if (!gsk_renderer_realize_for_display (renderer, display, &error)) { diff --git a/gsk/gpu/gskglrenderer.h b/gsk/gpu/gskglrenderer.h new file mode 100644 index 00000000000..7b78a3f60cf --- /dev/null +++ b/gsk/gpu/gskglrenderer.h @@ -0,0 +1,15 @@ +#pragma once + +#include "gskgpurendererprivate.h" + +G_BEGIN_DECLS + +#define GSK_TYPE_NGL_RENDERER (gsk_ngl_renderer_get_type ()) + +GDK_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (GskNglRenderer, gsk_ngl_renderer, GSK, NGL_RENDERER, GskGpuRenderer) + +GDK_AVAILABLE_IN_ALL +GskRenderer *gsk_ngl_renderer_new (void); + +G_END_DECLS diff --git a/gsk/gpu/gsknglrenderer.c b/gsk/gpu/gsknglrenderer.c index 94d3e2b8f21..3563db6134c 100644 --- a/gsk/gpu/gsknglrenderer.c +++ b/gsk/gpu/gsknglrenderer.c @@ -195,3 +195,5 @@ gsk_ngl_renderer_new (void) { return g_object_new (GSK_TYPE_NGL_RENDERER, NULL); } + + diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index 6af196575ca..bc6aad86b0d 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -9010,7 +9010,7 @@ gsk_render_node_png_serializer (GdkContentSerializer *serializer) node = gsk_value_get_render_node (gdk_content_serializer_get_value (serializer)); - renderer = gsk_ngl_renderer_new (); + renderer = gsk_gl_renderer_new (); if (!gsk_renderer_realize (renderer, NULL, NULL)) { g_object_unref (renderer); diff --git a/testsuite/gdk/memorytexture.c b/testsuite/gdk/memorytexture.c index e9484f17fc0..6ba38323d95 100644 --- a/testsuite/gdk/memorytexture.c +++ b/testsuite/gdk/memorytexture.c @@ -685,7 +685,7 @@ main (int argc, char *argv[]) g_clear_object (&gl_context); } - gl_renderer = gsk_ngl_renderer_new (); + gl_renderer = gsk_gl_renderer_new (); if (!gsk_renderer_realize_for_display (gl_renderer, display, NULL)) { g_clear_object (&gl_renderer); diff --git a/testsuite/gdk/texture-threads.c b/testsuite/gdk/texture-threads.c index 677d0ba3230..b5a49930516 100644 --- a/testsuite/gdk/texture-threads.c +++ b/testsuite/gdk/texture-threads.c @@ -64,7 +64,7 @@ texture_threads (void) GError *error = NULL; /* 1. Get a GL renderer */ - gl_renderer = gsk_ngl_renderer_new (); + gl_renderer = gsk_gl_renderer_new (); if (!gsk_renderer_realize_for_display (gl_renderer, gdk_display_get_default (), &error)) { g_test_skip (error->message); diff --git a/testsuite/gsk/misc.c b/testsuite/gsk/misc.c index 2038b2cd28b..d8596ade02d 100644 --- a/testsuite/gsk/misc.c +++ b/testsuite/gsk/misc.c @@ -208,12 +208,12 @@ test_cairo_renderer (void) } static void -test_ngl_renderer (void) +test_gl_renderer (void) { #ifdef GDK_RENDERING_GL GskRenderer *renderer; - renderer = gsk_ngl_renderer_new (); + renderer = gsk_gl_renderer_new (); test_renderer (renderer); g_clear_object (&renderer); #else @@ -247,7 +247,7 @@ main (int argc, char *argv[]) g_test_add_func ("/rendernode/conic-gradient/angle", test_conic_gradient_angle); g_test_add_func ("/rendernode/container/disjoint", test_container_disjoint); g_test_add_func ("/renderer/cairo", test_cairo_renderer); - g_test_add_func ("/renderer/ngl", test_ngl_renderer); + g_test_add_func ("/renderer/gl", test_gl_renderer); g_test_add_func ("/renderer/vulkan", test_vulkan_renderer); return g_test_run (); diff --git a/testsuite/gsk/scaling.c b/testsuite/gsk/scaling.c index 04208cfdf82..23562d0ebb9 100644 --- a/testsuite/gsk/scaling.c +++ b/testsuite/gsk/scaling.c @@ -10,13 +10,6 @@ struct { GskRenderer * (*create_func) (void); GskRenderer *renderer; } renderers[] = { -#if 0 - /* The GL renderer is broken, no idea why. It's suppsoed to work. */ - { - "gl", - gsk_gl_renderer_new, - }, -#endif { "cairo", gsk_cairo_renderer_new, @@ -26,8 +19,8 @@ struct { gsk_vulkan_renderer_new, }, { - "ngl", - gsk_ngl_renderer_new, + "gl", + gsk_gl_renderer_new, }, }; diff --git a/tools/gtk-rendernode-tool-benchmark.c b/tools/gtk-rendernode-tool-benchmark.c index 2f2afeaf61c..884ae283578 100644 --- a/tools/gtk-rendernode-tool-benchmark.c +++ b/tools/gtk-rendernode-tool-benchmark.c @@ -137,7 +137,7 @@ do_benchmark (int *argc, } if (renderers == NULL || renderers[0] == NULL) - renderers = g_strdupv ((char **) (const char *[]) { "ngl", "vulkan", "cairo", NULL }); + renderers = g_strdupv ((char **) (const char *[]) { "gl", "vulkan", "cairo", NULL }); node = load_node_file (filenames[0]); diff --git a/tools/gtk-rendernode-tool-utils.c b/tools/gtk-rendernode-tool-utils.c index 9e8da7f2ef9..26b0eef4598 100644 --- a/tools/gtk-rendernode-tool-utils.c +++ b/tools/gtk-rendernode-tool-utils.c @@ -89,8 +89,8 @@ get_renderer_for_name (const char *renderer_name) #endif else if (g_ascii_strcasecmp (renderer_name, "cairo") == 0) return gsk_cairo_renderer_new (); - else if (g_ascii_strcasecmp (renderer_name, "ngl") == 0) - return gsk_ngl_renderer_new (); + else if (g_ascii_strcasecmp (renderer_name, "gl") == 0) + return gsk_gl_renderer_new (); #ifdef GDK_RENDERING_VULKAN else if (g_ascii_strcasecmp (renderer_name, "vulkan") == 0) return gsk_vulkan_renderer_new (); -- GitLab From b111923d7a6e25c15a813caf350df31afee94706 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 3 Oct 2024 07:26:09 -0400 Subject: [PATCH 4/6] gsk: Rename ngl to gl For now, this is just doing the code reshuffling. The environment variable still accepts GSK_RENDERER=ngl, and gsk_ngl_renderer_new is still around, deprecated, and returns a renderer that fails to realize. The gsk/gl/gskglrenderer.h is still installed, but including it triggers a deprecation warning. --- gsk/gl/gskglrenderer.c | 46 ------- gsk/gl/gskglrenderer.h | 33 +---- gsk/gpu/{gsknglrenderer.c => gskglrenderer.c} | 124 ++++++++++++------ gsk/gpu/gskglrenderer.h | 39 +++++- gsk/gpu/gsknglrendererprivate.h | 16 --- gsk/gsk.h | 2 +- gsk/gskrenderer.c | 12 +- gsk/gskrendernodeimpl.c | 2 +- gsk/meson.build | 6 +- po/POTFILES.in | 3 +- testsuite/gsk/meson.build | 1 - 11 files changed, 136 insertions(+), 148 deletions(-) delete mode 100644 gsk/gl/gskglrenderer.c rename gsk/gpu/{gsknglrenderer.c => gskglrenderer.c} (54%) delete mode 100644 gsk/gpu/gsknglrendererprivate.h diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c deleted file mode 100644 index 3ae26ae151a..00000000000 --- a/gsk/gl/gskglrenderer.c +++ /dev/null @@ -1,46 +0,0 @@ -/* gskglrenderer.c - * - * Copyright 2020 Christian Hergert - * - * This file is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation; either version 2.1 of the License, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "gskglrenderer.h" - -GType -gsk_gl_renderer_get_type (void) -{ - return gsk_ngl_renderer_get_type (); -} - -/** - * gsk_gl_renderer_new: - * - * Creates a new `GskRenderer` using the new OpenGL renderer. - * - * Returns: a new GL renderer - * - * Since: 4.2 - */ -GskRenderer * -gsk_gl_renderer_new (void) -{ - return g_object_new (GSK_TYPE_GL_RENDERER, NULL); -} - - diff --git a/gsk/gl/gskglrenderer.h b/gsk/gl/gskglrenderer.h index 6d0095fe73e..e863b690cb0 100644 --- a/gsk/gl/gskglrenderer.h +++ b/gsk/gl/gskglrenderer.h @@ -20,35 +20,6 @@ #pragma once -#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION) -#include -#define GSK_INCLUDE_WARNING(x) GDK_DEPRECATED_IN_4_14_FOR("#include instead of to avoid this warning") -#else -#include -#define GSK_INCLUDE_WARNING(x) x -#endif - -G_BEGIN_DECLS - -#define GSK_TYPE_GL_RENDERER (gsk_gl_renderer_get_type()) - -#define GSK_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_GL_RENDERER, GskGLRenderer)) -#define GSK_IS_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_GL_RENDERER)) -#define GSK_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_GL_RENDERER, GskGLRendererClass)) -#define GSK_IS_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_GL_RENDERER)) -#define GSK_GL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_GL_RENDERER, GskGLRendererClass)) - -typedef struct _GskGLRenderer GskGLRenderer; -typedef struct _GskGLRendererClass GskGLRendererClass; - -GSK_INCLUDE_WARNING(GDK_AVAILABLE_IN_4_2) -GType gsk_gl_renderer_get_type (void) G_GNUC_CONST; -GSK_INCLUDE_WARNING(GDK_AVAILABLE_IN_4_2) -GskRenderer *gsk_gl_renderer_new (void); - -GSK_INCLUDE_WARNING(GDK_AVAILABLE_IN_ALL) -GType gsk_ngl_renderer_get_type (void) G_GNUC_CONST; -GSK_INCLUDE_WARNING(GDK_AVAILABLE_IN_ALL) -GskRenderer *gsk_ngl_renderer_new (void); -G_END_DECLS +#warning "#include instead of to avoid this warning" +#include diff --git a/gsk/gpu/gsknglrenderer.c b/gsk/gpu/gskglrenderer.c similarity index 54% rename from gsk/gpu/gsknglrenderer.c rename to gsk/gpu/gskglrenderer.c index 3563db6134c..1cdce228ce7 100644 --- a/gsk/gpu/gsknglrenderer.c +++ b/gsk/gpu/gskglrenderer.c @@ -1,6 +1,6 @@ #include "config.h" -#include "gsknglrendererprivate.h" +#include "gskglrenderer.h" #include "gskgpuimageprivate.h" #include "gskgpurendererprivate.h" @@ -15,7 +15,7 @@ #include /** - * GskNglRenderer: + * GskGLRenderer: * * A GL based renderer. * @@ -23,26 +23,26 @@ * * Since: 4.2 */ -struct _GskNglRenderer +struct _GskGLRenderer { GskGpuRenderer parent_instance; GskGpuImage *backbuffer; }; -struct _GskNglRendererClass +struct _GskGLRendererClass { GskGpuRendererClass parent_class; }; -G_DEFINE_TYPE (GskNglRenderer, gsk_ngl_renderer, GSK_TYPE_GPU_RENDERER) +G_DEFINE_TYPE (GskGLRenderer, gsk_gl_renderer, GSK_TYPE_GPU_RENDERER) static GdkDrawContext * -gsk_ngl_renderer_create_context (GskGpuRenderer *renderer, - GdkDisplay *display, - GdkSurface *surface, - GskGpuOptimizations *supported, - GError **error) +gsk_gl_renderer_create_context (GskGpuRenderer *renderer, + GdkDisplay *display, + GdkSurface *surface, + GskGpuOptimizations *supported, + GError **error) { GdkGLContext *context; @@ -74,13 +74,13 @@ gsk_ngl_renderer_create_context (GskGpuRenderer *renderer, } static void -gsk_ngl_renderer_make_current (GskGpuRenderer *renderer) +gsk_gl_renderer_make_current (GskGpuRenderer *renderer) { gdk_gl_context_make_current (GDK_GL_CONTEXT (gsk_gpu_renderer_get_context (renderer))); } static gpointer -gsk_ngl_renderer_save_current (GskGpuRenderer *renderer) +gsk_gl_renderer_save_current (GskGpuRenderer *renderer) { GdkGLContext *current; @@ -92,8 +92,8 @@ gsk_ngl_renderer_save_current (GskGpuRenderer *renderer) } static void -gsk_ngl_renderer_restore_current (GskGpuRenderer *renderer, - gpointer current) +gsk_gl_renderer_restore_current (GskGpuRenderer *renderer, + gpointer current) { if (current) { @@ -105,15 +105,15 @@ gsk_ngl_renderer_restore_current (GskGpuRenderer *renderer, } static void -gsk_ngl_renderer_free_backbuffer (GskNglRenderer *self) +gsk_gl_renderer_free_backbuffer (GskGLRenderer *self) { g_clear_object (&self->backbuffer); } static GskGpuImage * -gsk_ngl_renderer_get_backbuffer (GskGpuRenderer *renderer) +gsk_gl_renderer_get_backbuffer (GskGpuRenderer *renderer) { - GskNglRenderer *self = GSK_NGL_RENDERER (renderer); + GskGLRenderer *self = GSK_GL_RENDERER (renderer); GdkDrawContext *context; GdkSurface *surface; double scale; @@ -127,7 +127,7 @@ gsk_ngl_renderer_get_backbuffer (GskGpuRenderer *renderer) gsk_gpu_image_get_width (self->backbuffer) != ceil (gdk_surface_get_width (surface) * scale) || gsk_gpu_image_get_height (self->backbuffer) != ceil (gdk_surface_get_height (surface) * scale)) { - gsk_ngl_renderer_free_backbuffer (self); + gsk_gl_renderer_free_backbuffer (self); self->backbuffer = gsk_gl_image_new_backbuffer (GSK_GL_DEVICE (gsk_gpu_renderer_get_device (renderer)), GDK_GL_CONTEXT (context), GDK_MEMORY_DEFAULT /* FIXME */, @@ -140,7 +140,7 @@ gsk_ngl_renderer_get_backbuffer (GskGpuRenderer *renderer) } static double -gsk_ngl_renderer_get_scale (GskGpuRenderer *self) +gsk_gl_renderer_get_scale (GskGpuRenderer *self) { GdkDrawContext *context = gsk_gpu_renderer_get_context (self); @@ -148,19 +148,19 @@ gsk_ngl_renderer_get_scale (GskGpuRenderer *self) } static void -gsk_ngl_renderer_unrealize (GskRenderer *renderer) +gsk_gl_renderer_unrealize (GskRenderer *renderer) { - GskNglRenderer *self = GSK_NGL_RENDERER (renderer); + GskGLRenderer *self = GSK_GL_RENDERER (renderer); - gsk_ngl_renderer_free_backbuffer (self); + gsk_gl_renderer_free_backbuffer (self); gdk_gl_context_clear_current (); - GSK_RENDERER_CLASS (gsk_ngl_renderer_parent_class)->unrealize (renderer); + GSK_RENDERER_CLASS (gsk_gl_renderer_parent_class)->unrealize (renderer); } static void -gsk_ngl_renderer_class_init (GskNglRendererClass *klass) +gsk_gl_renderer_class_init (GskGLRendererClass *klass) { GskGpuRendererClass *gpu_renderer_class = GSK_GPU_RENDERER_CLASS (klass); GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass); @@ -168,32 +168,80 @@ gsk_ngl_renderer_class_init (GskNglRendererClass *klass) gpu_renderer_class->frame_type = GSK_TYPE_GL_FRAME; gpu_renderer_class->get_device = gsk_gl_device_get_for_display; - gpu_renderer_class->create_context = gsk_ngl_renderer_create_context; - gpu_renderer_class->make_current = gsk_ngl_renderer_make_current; - gpu_renderer_class->save_current = gsk_ngl_renderer_save_current; - gpu_renderer_class->restore_current = gsk_ngl_renderer_restore_current; - gpu_renderer_class->get_backbuffer = gsk_ngl_renderer_get_backbuffer; - gpu_renderer_class->get_scale = gsk_ngl_renderer_get_scale; - - renderer_class->unrealize = gsk_ngl_renderer_unrealize; + gpu_renderer_class->create_context = gsk_gl_renderer_create_context; + gpu_renderer_class->make_current = gsk_gl_renderer_make_current; + gpu_renderer_class->save_current = gsk_gl_renderer_save_current; + gpu_renderer_class->restore_current = gsk_gl_renderer_restore_current; + gpu_renderer_class->get_backbuffer = gsk_gl_renderer_get_backbuffer; + gpu_renderer_class->get_scale = gsk_gl_renderer_get_scale; + + renderer_class->unrealize = gsk_gl_renderer_unrealize; } static void -gsk_ngl_renderer_init (GskNglRenderer *self) +gsk_gl_renderer_init (GskGLRenderer *self) { } /** - * gsk_ngl_renderer_new: + * gsk_gl_renderer_new: * - * Creates an instance of the new experimental GL renderer. + * Creates an instance of the GL renderer. * - * Returns: (transfer full): a new GL renderer + * Returns: (transfer full): a GL renderer */ GskRenderer * -gsk_ngl_renderer_new (void) +gsk_gl_renderer_new (void) { - return g_object_new (GSK_TYPE_NGL_RENDERER, NULL); + return g_object_new (GSK_TYPE_GL_RENDERER, NULL); } +typedef struct { + GskRenderer parent_instance; +} GskNglRenderer; + +typedef struct { + GskRendererClass parent_class; +} GskNglRendererClass; + +G_DEFINE_TYPE (GskNglRenderer, gsk_ngl_renderer, GSK_TYPE_RENDERER) +static void +gsk_ngl_renderer_init (GskNglRenderer *renderer) +{ +} + +static gboolean +gsk_ngl_renderer_realize (GskRenderer *renderer, + GdkDisplay *display, + GdkSurface *surface, + GError **error) +{ + g_set_error_literal (error, + G_IO_ERROR, G_IO_ERROR_FAILED, + "Please use the GL renderer instead"); + return FALSE; +} + +static void +gsk_ngl_renderer_class_init (GskNglRendererClass *class) +{ + GSK_RENDERER_CLASS (class)->realize = gsk_ngl_renderer_realize; +} + +/** + * gsk_ngl_renderer_new: + * + * Same as gsk_gl_renderer_new(). + * + * Returns: (transfer full): a GL renderer + * + * Deprecated: 4.18: Use gsk_gl_renderer_new() + */ +GskRenderer * +gsk_ngl_renderer_new (void) +{ +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + return g_object_new (gsk_ngl_renderer_get_type (), NULL); +G_GNUC_END_IGNORE_DEPRECATIONS +} diff --git a/gsk/gpu/gskglrenderer.h b/gsk/gpu/gskglrenderer.h index 7b78a3f60cf..986c40d79b2 100644 --- a/gsk/gpu/gskglrenderer.h +++ b/gsk/gpu/gskglrenderer.h @@ -1,15 +1,48 @@ +/* + * Copyright © 2024 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + #pragma once -#include "gskgpurendererprivate.h" +#include +#include G_BEGIN_DECLS -#define GSK_TYPE_NGL_RENDERER (gsk_ngl_renderer_get_type ()) +#define GSK_TYPE_GL_RENDERER (gsk_gl_renderer_get_type()) + +#define GSK_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_GL_RENDERER, GskGLRenderer)) +#define GSK_IS_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_GL_RENDERER)) +#define GSK_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_GL_RENDERER, GskGLRendererClass)) +#define GSK_IS_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_GL_RENDERER)) +#define GSK_GL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_GL_RENDERER, GskGLRendererClass)) + +typedef struct _GskGLRenderer GskGLRenderer; +typedef struct _GskGLRendererClass GskGLRendererClass; GDK_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (GskNglRenderer, gsk_ngl_renderer, GSK, NGL_RENDERER, GskGpuRenderer) +GskRenderer *gsk_gl_renderer_new (void); GDK_AVAILABLE_IN_ALL +GType gsk_gl_renderer_get_type (void) G_GNUC_CONST; + +GDK_DEPRECATED_IN_4_18_FOR (gsk_gl_renderer_get_type) +GType gsk_ngl_renderer_get_type (void) G_GNUC_CONST; + +GDK_DEPRECATED_IN_4_18_FOR (gsk_gl_renderer_new) GskRenderer *gsk_ngl_renderer_new (void); G_END_DECLS diff --git a/gsk/gpu/gsknglrendererprivate.h b/gsk/gpu/gsknglrendererprivate.h deleted file mode 100644 index ad520880ae3..00000000000 --- a/gsk/gpu/gsknglrendererprivate.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "gskgpurendererprivate.h" - -G_BEGIN_DECLS - -#define GSK_TYPE_NGL_RENDERER (gsk_ngl_renderer_get_type ()) - -GDK_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (GskNglRenderer, gsk_ngl_renderer, GSK, NGL_RENDERER, GskGpuRenderer) - -GDK_AVAILABLE_IN_ALL -GskRenderer *gsk_ngl_renderer_new (void); - -G_END_DECLS - diff --git a/gsk/gsk.h b/gsk/gsk.h index d118ef3c0a6..c9545917199 100644 --- a/gsk/gsk.h +++ b/gsk/gsk.h @@ -32,8 +32,8 @@ #include #include -#include #include +#include #include #include diff --git a/gsk/gskrenderer.c b/gsk/gskrenderer.c index 8947bc96f70..e59060fb32c 100644 --- a/gsk/gskrenderer.c +++ b/gsk/gskrenderer.c @@ -43,7 +43,7 @@ #include "gskenumtypes.h" -#include "gl/gskglrenderer.h" +#include "gpu/gskglrenderer.h" #include "gpu/gskvulkanrenderer.h" #include "gdk/gdkvulkancontextprivate.h" #include "gdk/gdkdisplayprivate.h" @@ -515,7 +515,7 @@ get_renderer_for_name (const char *renderer_name) else if (g_ascii_strcasecmp (renderer_name, "cairo") == 0) return GSK_TYPE_CAIRO_RENDERER; else if (g_ascii_strcasecmp (renderer_name, "ngl") == 0) - return gsk_ngl_renderer_get_type (); + return GSK_TYPE_GL_RENDERER; #ifdef GDK_RENDERING_VULKAN else if (g_ascii_strcasecmp (renderer_name, "vulkan") == 0) return GSK_TYPE_VULKAN_RENDERER; @@ -623,19 +623,19 @@ gl_supported_platform (GdkSurface *surface, static GType get_renderer_for_gl (GdkSurface *surface) { - if (!gl_supported_platform (surface, gsk_ngl_renderer_get_type (), FALSE)) + if (!gl_supported_platform (surface, GSK_TYPE_GL_RENDERER, FALSE)) return G_TYPE_INVALID; - return gsk_ngl_renderer_get_type (); + return GSK_TYPE_GL_RENDERER; } static GType get_renderer_for_gl_fallback (GdkSurface *surface) { - if (!gl_supported_platform (surface, gsk_ngl_renderer_get_type (), TRUE)) + if (!gl_supported_platform (surface, GSK_TYPE_GL_RENDERER, TRUE)) return G_TYPE_INVALID; - return gsk_ngl_renderer_get_type (); + return GSK_TYPE_GL_RENDERER; } #ifdef GDK_RENDERING_VULKAN diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c index bc6aad86b0d..1369f12c42e 100644 --- a/gsk/gskrendernodeimpl.c +++ b/gsk/gskrendernodeimpl.c @@ -24,7 +24,6 @@ #include "gskcairorenderer.h" #include "gskdebugprivate.h" #include "gskdiffprivate.h" -#include "gl/gskglrenderer.h" #include "gskpathprivate.h" #include "gskrectprivate.h" #include "gskrendererprivate.h" @@ -32,6 +31,7 @@ #include "gskstrokeprivate.h" #include "gsktransformprivate.h" #include "gskprivate.h" +#include "gpu/gskglrenderer.h" #include "gdk/gdkcairoprivate.h" #include "gdk/gdkcolorstateprivate.h" diff --git a/gsk/meson.build b/gsk/meson.build index 3ce154fa242..69df4d81e71 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -15,7 +15,7 @@ gsk_public_sources = files([ 'gskroundedrect.c', 'gskstroke.c', 'gsktransform.c', - 'gpu/gsknglrenderer.c', + 'gpu/gskglrenderer.c', 'gpu/gskvulkanrenderer.c', ]) @@ -27,7 +27,6 @@ gsk_private_sources = files([ 'gskprivate.c', 'gskprofiler.c', 'gl/fp16.c', - 'gl/gskglrenderer.c', 'gpu/gskglbuffer.c', 'gpu/gskgldevice.c', 'gpu/gskglframe.c', @@ -97,7 +96,8 @@ gsk_public_gl_headers = files([ ]) gsk_public_gpu_headers = files([ - 'gpu/gskvulkanrenderer.h' + 'gpu/gskvulkanrenderer.h', + 'gpu/gskglrenderer.h' ]) install_headers(gsk_public_gpu_headers, subdir: 'gtk-4.0/gsk/gpu') gsk_public_headers += gsk_public_gpu_headers diff --git a/po/POTFILES.in b/po/POTFILES.in index aa11a330fe3..e8e7b0a0441 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -58,9 +58,8 @@ gdk/x11/gdkglcontext-x11.c gdk/x11/gdkmain-x11.c gdk/x11/gdkselectioninputstream-x11.c gdk/x11/gdktextlistconverter-x11.c -gsk/gl/gskglrenderer.c gsk/gpu/gskgldevice.c -gsk/gpu/gsknglrenderer.c +gsk/gpu/gskglrenderer.c gsk/gskrendernodeimpl.c gtk/a11y/gtkatspiaction.c gtk/a11y/gtkatspiroot.c diff --git a/testsuite/gsk/meson.build b/testsuite/gsk/meson.build index 97f606a1b52..29afa87c451 100644 --- a/testsuite/gsk/meson.build +++ b/testsuite/gsk/meson.build @@ -219,7 +219,6 @@ informative_render_tests = [ ] renderers = [ - { 'name': 'gl' }, { 'name': 'broadway' }, { 'name': 'cairo' }, { 'name': 'ngl' }, -- GitLab From bde0a536bb155610dac1c35ba7802aa4734d1196 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 19 Jan 2025 08:38:56 -0500 Subject: [PATCH 5/6] Make our env var printouts consistent We use fprintf (stderr,...) for all debug env vars and for GDK_BACKEND. Do the same for GSK_RENDERER. --- gsk/gskrenderer.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/gsk/gskrenderer.c b/gsk/gskrenderer.c index e59060fb32c..39b539f31bf 100644 --- a/gsk/gskrenderer.c +++ b/gsk/gskrenderer.c @@ -522,21 +522,21 @@ get_renderer_for_name (const char *renderer_name) #endif else if (g_ascii_strcasecmp (renderer_name, "help") == 0) { - g_print ("Supported arguments for GSK_RENDERER environment variable:\n"); + fprintf (stderr, "Supported arguments for GSK_RENDERER environment variable:\n"); #ifdef GDK_WINDOWING_BROADWAY - g_print (" broadway - Use the Broadway specific renderer\n"); + fprintf (stderr, " broadway - Use the Broadway specific renderer\n"); #else - g_print (" broadway - Disabled during GTK build\n"); + fprintf (stderr, " broadway - Disabled during GTK build\n"); #endif - g_print (" cairo - Use the Cairo fallback renderer\n"); - g_print (" ngl - Use the OpenGL renderer\n"); + fprintf (stderr, " cairo - Use the Cairo fallback renderer\n"); + fprintf (stderr, " ngl - Use the OpenGL renderer\n"); #ifdef GDK_RENDERING_VULKAN - g_print (" vulkan - Use the Vulkan renderer\n"); + fprintf (stderr, " vulkan - Use the Vulkan renderer\n"); #else - g_print (" vulkan - Disabled during GTK build\n"); + fprintf (stderr, " vulkan - Disabled during GTK build\n"); #endif - g_print (" help - Print this help\n\n"); - g_print ("Other arguments will cause a warning and be ignored.\n"); + fprintf (stderr, " help - Print this help\n\n"); + fprintf (stderr, "Other arguments will cause a warning and be ignored.\n"); } else { -- GitLab From e19de7ee959929828ff5342617879cc3d97bd2d9 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 19 Jan 2025 08:39:55 -0500 Subject: [PATCH 6/6] Accept gl as renderer name We only have one GL renderer now, and it doesn't matter if you call it ngl or opengl. But if you call it gl, we'll complain. --- docs/reference/gtk/running.md | 6 ++++++ gsk/gskrenderer.c | 12 +++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/reference/gtk/running.md b/docs/reference/gtk/running.md index 2b7187fce70..c358ca6ddbc 100644 --- a/docs/reference/gtk/running.md +++ b/docs/reference/gtk/running.md @@ -434,9 +434,15 @@ using and the GDK backend supports them: `cairo` : Selects the fallback Cairo renderer +`opengl` +: Selects the OpenGL renderer + `ngl` : Selects the OpenGL renderer +`gl`: +: Selects the OpenGL renderer. + `vulkan` : Selects the Vulkan renderer diff --git a/gsk/gskrenderer.c b/gsk/gskrenderer.c index 39b539f31bf..1cd3cae7aae 100644 --- a/gsk/gskrenderer.c +++ b/gsk/gskrenderer.c @@ -514,8 +514,14 @@ get_renderer_for_name (const char *renderer_name) #endif else if (g_ascii_strcasecmp (renderer_name, "cairo") == 0) return GSK_TYPE_CAIRO_RENDERER; - else if (g_ascii_strcasecmp (renderer_name, "ngl") == 0) + else if (g_ascii_strcasecmp (renderer_name, "ngl") == 0 || + g_ascii_strcasecmp (renderer_name, "opengl") == 0) return GSK_TYPE_GL_RENDERER; + else if (g_ascii_strcasecmp (renderer_name, "gl") == 0) + { + g_warning ("The old GL renderer has been removed. Try GSK_RENDERER=help"); + return GSK_TYPE_GL_RENDERER; + } #ifdef GDK_RENDERING_VULKAN else if (g_ascii_strcasecmp (renderer_name, "vulkan") == 0) return GSK_TYPE_VULKAN_RENDERER; @@ -529,13 +535,17 @@ get_renderer_for_name (const char *renderer_name) fprintf (stderr, " broadway - Disabled during GTK build\n"); #endif fprintf (stderr, " cairo - Use the Cairo fallback renderer\n"); + fprintf (stderr, " opengl - Use the OpenGL renderer\n"); fprintf (stderr, " ngl - Use the OpenGL renderer\n"); + fprintf (stderr, " gl - Use the OpenGL renderer\n"); #ifdef GDK_RENDERING_VULKAN fprintf (stderr, " vulkan - Use the Vulkan renderer\n"); #else fprintf (stderr, " vulkan - Disabled during GTK build\n"); #endif fprintf (stderr, " help - Print this help\n\n"); + fprintf (stderr, "The old OpenGL renderer has been removed in GTK 4.18, so using\n"); + fprintf (stderr, "GSK_RENDERER=gl will cause a warning and use the new OpenGL renderer.\n\n"); fprintf (stderr, "Other arguments will cause a warning and be ignored.\n"); } else -- GitLab