Compare commits

...

4 Commits

Author SHA1 Message Date
Matthias Clasen
6c5e8515b7 gsk: Rename ngl to gl
For now, this is just doing the code reshuffling. The environment
variable still expects you to set
GSK_RENDERER=ngl

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.
2024-10-03 21:53:58 -04:00
Matthias Clasen
37c24ba186 Use the gl renderer again everywhere
We're renaming ngl back to gl.
2024-10-03 21:53:58 -04:00
Matthias Clasen
0dcb35047e Drop the gl renderer
This requires cleanups in quite a few places.
2024-10-03 21:53:58 -04:00
Matthias Clasen
971ee42321 tools: Drop support for the gl renderer
The GL renderer is going away in 4.18.
2024-10-03 21:53:58 -04:00
78 changed files with 161 additions and 17528 deletions

View File

@@ -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 (),

View File

@@ -231,7 +231,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);
@@ -275,7 +274,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))
{

View File

@@ -1,116 +0,0 @@
/* gskglattachmentstate.c
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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;
}
}

View File

@@ -1,102 +0,0 @@
/* gskglattachmentstateprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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

View File

@@ -1,69 +0,0 @@
/* gskglbufferprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <string.h>
#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);
}

View File

@@ -1,80 +0,0 @@
/* gskglbufferprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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

File diff suppressed because it is too large Load Diff

View File

@@ -1,431 +0,0 @@
/* gskglcommandqueueprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <gsk/gskprofilerprivate.h>
#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

View File

@@ -1,703 +0,0 @@
/* gskglcompiler.c
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <gsk/gskdebugprivate.h>
#include <gio/gio.h>
#include <string.h>
#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);
}

View File

@@ -1,68 +0,0 @@
/* gskglcompilerprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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

File diff suppressed because it is too large Load Diff

View File

@@ -1,261 +0,0 @@
/* gskgldriverprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <gdk/gdkgltextureprivate.h>
#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

View File

@@ -1,439 +0,0 @@
/* gskglglyphlibrary.c
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdkmemoryformatprivate.h>
#include <gdk/gdkprofilerprivate.h>
#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;
}

View File

@@ -1,103 +0,0 @@
/* gskglglyphlibraryprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <pango/pango.h>
#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

View File

@@ -1,212 +0,0 @@
/* gskgliconlibrary.c
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdkmemoryformatprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdktextureprivate.h>
#include <gdk/gdktexturedownloaderprivate.h>
#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);
}
}

View File

@@ -1,58 +0,0 @@
/* gskgliconlibraryprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <pango/pango.h>
#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

View File

@@ -1,179 +0,0 @@
#include "config.h"
#include "gskglprofilerprivate.h"
#include <epoxy/gl.h>
#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 */
}

View File

@@ -1,16 +0,0 @@
#pragma once
#include <gsk/gsktypes.h>
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

View File

@@ -1,173 +0,0 @@
/* gskglprogram.c
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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;
}

View File

@@ -1,318 +0,0 @@
/* gskglprogramprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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

View File

@@ -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)

View File

@@ -1,489 +0,0 @@
/* gskglrenderer.c
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include "gskglrendererprivate.h"
#include "gskglcommandqueueprivate.h"
#include "gskgldriverprivate.h"
#include "gskglprogramprivate.h"
#include "gskglrenderjobprivate.h"
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdkdisplayprivate.h>
#include <gdk/gdkdmabufdownloaderprivate.h>
#include <gdk/gdkdmabuftextureprivate.h>
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdksurfaceprivate.h>
#include <gdk/gdksubsurfaceprivate.h>
#include <glib/gi18n-lib.h>
#include <gsk/gskdebugprivate.h>
#include <gsk/gskrendererprivate.h>
#include <gsk/gskrendernodeprivate.h>
#include <gsk/gskroundedrectprivate.h>
#include <gsk/gskrectprivate.h>
/**
* 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)
{
iface->close = gsk_gl_renderer_dmabuf_downloader_close;
iface->download = gsk_gl_renderer_dmabuf_downloader_download;
}
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:
*
* 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);
}
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

View File

@@ -20,35 +20,6 @@
#pragma once
#if !defined (__GSK_H_INSIDE__) && !defined (GTK_COMPILATION)
#warning "#include <gsk/gsk.h> instead of <gsk/gl/gskglrenderer.h> to avoid this warning"
#include <gsk/gsk.h>
#define GSK_INCLUDE_WARNING(x) GDK_DEPRECATED_IN_4_14_FOR("#include <gsk/gsk.h> instead of <gsk/gl/gskglrenderer.h> to avoid this warning")
#else
#include <gsk/gsktypes.h>
#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

View File

@@ -1,38 +0,0 @@
/* gskglrendererprivate.h
*
* Copyright 2021 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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

File diff suppressed because it is too large Load Diff

View File

@@ -1,36 +0,0 @@
/* gskglrenderjobprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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);

View File

@@ -1,256 +0,0 @@
/* gskglshadowlibrary.c
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <string.h>
#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--;
}
}
}

View File

@@ -1,42 +0,0 @@
/* gskglshadowlibraryprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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

View File

@@ -1,95 +0,0 @@
/* gskgltexture.c
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <gdk/gdktextureprivate.h>
#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;
}

View File

@@ -1,583 +0,0 @@
/* gskgltexturelibrary.c
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <gdk/gdkglcontextprivate.h>
#include <gsk/gskdebugprivate.h>
#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);
}

View File

@@ -1,232 +0,0 @@
/* gskgltexturelibraryprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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

View File

@@ -1,93 +0,0 @@
/* gskgltextureprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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

View File

@@ -1,64 +0,0 @@
/* gskgltypesprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <epoxy/gl.h>
#include <graphene.h>
#include <gdk/gdk.h>
#include <gsk/gsk.h>
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

View File

@@ -1,269 +0,0 @@
/* gskgluniformstate.c
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <gsk/gskroundedrectprivate.h>
#include <string.h>
#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;
}

View File

@@ -1,834 +0,0 @@
/* gskgluniformstateprivate.h
*
* Copyright 2020 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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

View File

@@ -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]; \
}

View File

@@ -1,308 +0,0 @@
/* ninesliceprivate.h
*
* Copyright 2017 Timm Bäder <mail@baedert.org>
* Copyright 2021 Christian Hergert <chergert@redhat.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
* 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

View File

@@ -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 = lum (c);
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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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));
}

View File

@@ -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));
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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));
}

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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));
}

View File

@@ -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);
}

View File

@@ -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
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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 <stdlib.h>
#define STBRP_SORT qsort
#endif
#ifndef STBRP_ASSERT
#include <assert.h>
#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.
------------------------------------------------------------------------------
*/

View File

@@ -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

View File

@@ -1,6 +1,6 @@
#include "config.h"
#include "gsknglrendererprivate.h"
#include "gskglrenderer.h"
#include "gskgpuimageprivate.h"
#include "gskgpurendererprivate.h"
@@ -15,7 +15,7 @@
#include <glib/gi18n-lib.h>
/**
* 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,30 +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;
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_ngl_renderer_unrealize;
renderer_class->unrealize = gsk_gl_renderer_unrealize;
}
static void
gsk_ngl_renderer_init (GskNglRenderer *self)
gsk_gl_renderer_init (GskGLRenderer *self)
{
}
/**
* gsk_gl_renderer_new:
*
* Creates an instance of the GL renderer.
*
* Returns: (transfer full): a GL renderer
*/
GskRenderer *
gsk_gl_renderer_new (void)
{
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:
*
* Creates an instance of the new experimental GL renderer.
* Same as gsk_gl_renderer_new().
*
* Returns: (transfer full): a new GL renderer
* Returns: (transfer full): a GL renderer
*
* Deprecated: 4.18: Use gsk_gl_renderer_new()
*/
GskRenderer *
gsk_ngl_renderer_new (void)
{
return g_object_new (GSK_TYPE_NGL_RENDERER, NULL);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
return g_object_new (gsk_ngl_renderer_get_type (), NULL);
G_GNUC_END_IGNORE_DEPRECATIONS
}

49
gsk/gpu/gskglrenderer.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gdk/gdk.h>
#include <gsk/gsk.h>
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;
GDK_AVAILABLE_IN_ALL
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

View File

@@ -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

View File

@@ -32,8 +32,8 @@
#include <gsk/gskglshader.h>
#include <gsk/gskcairorenderer.h>
#include <gsk/gl/gskglrenderer.h>
#include <gsk/gpu/gskvulkanrenderer.h>
#include <gsk/gpu/gskglrenderer.h>
#include <gsk/gsktypes.h>
#include <gsk/gskenumtypes.h>

View File

@@ -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"
@@ -529,11 +529,8 @@ 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 ();
return GSK_TYPE_GL_RENDERER;
#ifdef GDK_RENDERING_VULKAN
else if (g_ascii_strcasecmp (renderer_name, "vulkan") == 0)
return GSK_TYPE_VULKAN_RENDERER;
@@ -641,19 +638,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

View File

@@ -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"
@@ -8160,7 +8160,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);

View File

@@ -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,8 +15,7 @@ gsk_public_sources = files([
'gskroundedrect.c',
'gskstroke.c',
'gsktransform.c',
'gl/gskglrenderer.c',
'gpu/gsknglrenderer.c',
'gpu/gskglrenderer.c',
'gpu/gskvulkanrenderer.c',
])
@@ -53,21 +26,6 @@ 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',
'gpu/gskglbuffer.c',
'gpu/gskgldevice.c',
@@ -135,11 +93,10 @@ 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'
'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
@@ -181,7 +138,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,

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -215,7 +215,6 @@ informative_render_tests = [
]
renderers = [
{ 'name': 'gl' },
{ 'name': 'broadway' },
{ 'name': 'cairo' },
{ 'name': 'ngl' },

View File

@@ -203,12 +203,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
@@ -242,7 +242,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 ();

View File

@@ -6,7 +6,7 @@ static void
test_normalize (GskRenderNode *node1,
GskRenderNode *node2)
{
GskRenderer *renderer = gsk_ngl_renderer_new ();
GskRenderer *renderer = gsk_gl_renderer_new ();
graphene_rect_t bounds1, bounds2;
GdkTexture *texture1, *texture2, *diff;
GError *error = NULL;

View File

@@ -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,
},
};

View File

@@ -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 *[]) { "gl", "vulkan", "cairo", NULL });
node = load_node_file (filenames[0]);

View File

@@ -89,11 +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, "opengl") == 0 ||
g_ascii_strcasecmp (renderer_name, "gl") == 0)
else if (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
else if (g_ascii_strcasecmp (renderer_name, "vulkan") == 0)
return gsk_vulkan_renderer_new ();