Merge branch 'wip/chergert/glproto' into 'master'

gsk: add OpenGL based GskNglRenderer

See merge request GNOME/gtk!3225
This commit is contained in:
Matthias Clasen 2021-02-24 01:14:14 +00:00
commit cd2854a5c6
40 changed files with 12428 additions and 3 deletions

View File

@ -25,6 +25,7 @@
#include "gsk/gskrendernodeparserprivate.h" #include "gsk/gskrendernodeparserprivate.h"
#include "gsk/gl/gskglrenderer.h" #include "gsk/gl/gskglrenderer.h"
#include "gsk/ngl/gsknglrenderer.h"
#ifdef GDK_WINDOWING_BROADWAY #ifdef GDK_WINDOWING_BROADWAY
#include "gsk/broadway/gskbroadwayrenderer.h" #include "gsk/broadway/gskbroadwayrenderer.h"
#endif #endif
@ -762,6 +763,9 @@ node_editor_window_realize (GtkWidget *widget)
node_editor_window_add_renderer (self, node_editor_window_add_renderer (self,
gsk_gl_renderer_new (), gsk_gl_renderer_new (),
"OpenGL"); "OpenGL");
node_editor_window_add_renderer (self,
gsk_ngl_renderer_new (),
"NGL");
#ifdef GDK_RENDERING_VULKAN #ifdef GDK_RENDERING_VULKAN
node_editor_window_add_renderer (self, node_editor_window_add_renderer (self,
gsk_vulkan_renderer_new (), gsk_vulkan_renderer_new (),

View File

@ -139,7 +139,9 @@
#include "gskglshader.h" #include "gskglshader.h"
#include "gskglshaderprivate.h" #include "gskglshaderprivate.h"
#include "gskdebugprivate.h" #include "gskdebugprivate.h"
#include "gl/gskglrendererprivate.h" #include "gl/gskglrendererprivate.h"
#include "ngl/gsknglrendererprivate.h"
static GskGLUniformType static GskGLUniformType
uniform_type_from_glsl (const char *str) uniform_type_from_glsl (const char *str)
@ -542,8 +544,9 @@ gsk_gl_shader_compile (GskGLShader *shader,
g_return_val_if_fail (GSK_IS_GL_SHADER (shader), FALSE); g_return_val_if_fail (GSK_IS_GL_SHADER (shader), FALSE);
if (GSK_IS_GL_RENDERER (renderer)) if (GSK_IS_GL_RENDERER (renderer))
return gsk_gl_renderer_try_compile_gl_shader (GSK_GL_RENDERER (renderer), return gsk_gl_renderer_try_compile_gl_shader (GSK_GL_RENDERER (renderer), shader, error);
shader, error); else if (GSK_IS_NGL_RENDERER (renderer))
return gsk_ngl_renderer_try_compile_gl_shader (GSK_NGL_RENDERER (renderer), shader, error);
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"The renderer does not support gl shaders"); "The renderer does not support gl shaders");

View File

@ -39,6 +39,7 @@
#include "gskcairorenderer.h" #include "gskcairorenderer.h"
#include "gskdebugprivate.h" #include "gskdebugprivate.h"
#include "gl/gskglrenderer.h" #include "gl/gskglrenderer.h"
#include "ngl/gsknglrenderer.h"
#include "gskprofilerprivate.h" #include "gskprofilerprivate.h"
#include "gskrendernodeprivate.h" #include "gskrendernodeprivate.h"
@ -496,6 +497,8 @@ get_renderer_for_name (const char *renderer_name)
else if (g_ascii_strcasecmp (renderer_name, "opengl") == 0 else if (g_ascii_strcasecmp (renderer_name, "opengl") == 0
|| g_ascii_strcasecmp (renderer_name, "gl") == 0) || g_ascii_strcasecmp (renderer_name, "gl") == 0)
return GSK_TYPE_GL_RENDERER; return GSK_TYPE_GL_RENDERER;
else if (g_ascii_strcasecmp (renderer_name, "ngl") == 0)
return GSK_TYPE_NGL_RENDERER;
#ifdef GDK_RENDERING_VULKAN #ifdef GDK_RENDERING_VULKAN
else if (g_ascii_strcasecmp (renderer_name, "vulkan") == 0) else if (g_ascii_strcasecmp (renderer_name, "vulkan") == 0)
return GSK_TYPE_VULKAN_RENDERER; return GSK_TYPE_VULKAN_RENDERER;
@ -511,6 +514,7 @@ get_renderer_for_name (const char *renderer_name)
g_print (" cairo - Use the Cairo fallback renderer\n"); g_print (" cairo - Use the Cairo fallback renderer\n");
g_print (" opengl - Use the default OpenGL renderer\n"); g_print (" opengl - Use the default OpenGL renderer\n");
g_print (" gl - Same as opengl\n"); g_print (" gl - Same as opengl\n");
g_print (" ngl - Another OpenGL renderer\n");
#ifdef GDK_RENDERING_VULKAN #ifdef GDK_RENDERING_VULKAN
g_print (" vulkan - Use the Vulkan renderer\n"); g_print (" vulkan - Use the Vulkan renderer\n");
#else #else

View File

@ -31,6 +31,7 @@ gsk_public_sources = files([
'gskroundedrect.c', 'gskroundedrect.c',
'gsktransform.c', 'gsktransform.c',
'gl/gskglrenderer.c', 'gl/gskglrenderer.c',
'ngl/gsknglrenderer.c',
]) ])
gsk_private_sources = files([ gsk_private_sources = files([
@ -48,6 +49,19 @@ gsk_private_sources = files([
'gl/gskgliconcache.c', 'gl/gskgliconcache.c',
'gl/opbuffer.c', 'gl/opbuffer.c',
'gl/stb_rect_pack.c', 'gl/stb_rect_pack.c',
'ngl/gsknglattachmentstate.c',
'ngl/gsknglbuffer.c',
'ngl/gsknglcommandqueue.c',
'ngl/gsknglcompiler.c',
'ngl/gskngldriver.c',
'ngl/gsknglglyphlibrary.c',
'ngl/gskngliconlibrary.c',
'ngl/gsknglprogram.c',
'ngl/gsknglrenderjob.c',
'ngl/gsknglshadowlibrary.c',
'ngl/gskngltexturelibrary.c',
'ngl/gskngluniformstate.c',
'ngl/gskngltexturepool.c',
]) ])
gsk_public_headers = files([ gsk_public_headers = files([
@ -64,7 +78,8 @@ gsk_public_headers = files([
install_headers(gsk_public_headers, 'gsk.h', subdir: 'gtk-4.0/gsk') install_headers(gsk_public_headers, 'gsk.h', subdir: 'gtk-4.0/gsk')
gsk_public_gl_headers = files([ gsk_public_gl_headers = files([
'gl/gskglrenderer.h' 'gl/gskglrenderer.h',
'ngl/gsknglrenderer.h',
]) ])
install_headers(gsk_public_gl_headers, subdir: 'gtk-4.0/gsk/gl') install_headers(gsk_public_gl_headers, subdir: 'gtk-4.0/gsk/gl')
gsk_public_headers += gsk_public_gl_headers gsk_public_headers += gsk_public_gl_headers

View File

@ -0,0 +1,106 @@
/* gsknglattachmentstate.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 "gsknglattachmentstateprivate.h"
GskNglAttachmentState *
gsk_ngl_attachment_state_new (void)
{
GskNglAttachmentState *self;
self = g_atomic_rc_box_new0 (GskNglAttachmentState);
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;
}
return self;
}
GskNglAttachmentState *
gsk_ngl_attachment_state_ref (GskNglAttachmentState *self)
{
return g_atomic_rc_box_acquire (self);
}
void
gsk_ngl_attachment_state_unref (GskNglAttachmentState *self)
{
g_atomic_rc_box_release (self);
}
void
gsk_ngl_attachment_state_bind_texture (GskNglAttachmentState *self,
GLenum target,
GLenum texture,
guint id)
{
GskNglBindTexture *attach;
g_assert (self != NULL);
g_assert (target == GL_TEXTURE_1D ||
target == GL_TEXTURE_2D ||
target == GL_TEXTURE_3D);
g_assert (texture >= GL_TEXTURE0 && texture <= GL_TEXTURE16);
attach = &self->textures[texture - GL_TEXTURE0];
if (attach->target != target || attach->texture != texture || attach->id != id)
{
attach->target = target;
attach->texture = texture;
attach->id = id;
attach->initial = FALSE;
if (attach->changed == FALSE)
{
attach->changed = TRUE;
self->n_changed++;
}
}
}
void
gsk_ngl_attachment_state_bind_framebuffer (GskNglAttachmentState *self,
guint id)
{
g_assert (self != NULL);
if (self->fbo.id != id)
{
self->fbo.id = id;
self->fbo.changed = TRUE;
}
}

View File

@ -0,0 +1,71 @@
/* gsknglattachmentstateprivate.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
*/
#ifndef __GSK_NGL_ATTACHMENT_STATE_PRIVATE_H__
#define __GSK_NGL_ATTACHMENT_STATE_PRIVATE_H__
#include "gskngltypesprivate.h"
G_BEGIN_DECLS
typedef struct _GskNglAttachmentState GskNglAttachmentState;
typedef struct _GskNglBindFramebuffer GskNglBindFramebuffer;
typedef struct _GskNglBindTexture GskNglBindTexture;
struct _GskNglBindTexture
{
guint changed : 1;
guint initial : 1;
GLenum target : 30;
GLenum texture;
guint id;
};
G_STATIC_ASSERT (sizeof (GskNglBindTexture) == 12);
struct _GskNglBindFramebuffer
{
guint changed : 1;
guint id : 31;
};
G_STATIC_ASSERT (sizeof (GskNglBindFramebuffer) == 4);
struct _GskNglAttachmentState
{
GskNglBindFramebuffer fbo;
/* Increase if shaders add more textures */
GskNglBindTexture textures[4];
guint n_changed;
};
GskNglAttachmentState *gsk_ngl_attachment_state_new (void);
GskNglAttachmentState *gsk_ngl_attachment_state_ref (GskNglAttachmentState *self);
void gsk_ngl_attachment_state_unref (GskNglAttachmentState *self);
void gsk_ngl_attachment_state_bind_texture (GskNglAttachmentState *self,
GLenum target,
GLenum texture,
guint id);
void gsk_ngl_attachment_state_bind_framebuffer (GskNglAttachmentState *self,
guint id);
G_END_DECLS
#endif /* __GSK_NGL_ATTACHMENT_STATE_PRIVATE_H__ */

69
gsk/ngl/gsknglbuffer.c Normal file
View File

@ -0,0 +1,69 @@
/* gsknglbufferprivate.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 "gsknglbufferprivate.h"
/**
* gsk_ngl_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 #GskNglBuffer 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_ngl_buffer_init (GskNglBuffer *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_ngl_buffer_submit (GskNglBuffer *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_ngl_buffer_destroy (GskNglBuffer *buffer)
{
g_clear_pointer (&buffer->buffer, g_free);
}

View File

@ -0,0 +1,81 @@
/* gsknglbufferprivate.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
*/
#ifndef __GSK_NGL_BUFFER_PRIVATE_H__
#define __GSK_NGL_BUFFER_PRIVATE_H__
#include "gskngltypesprivate.h"
G_BEGIN_DECLS
typedef struct _GskNglBuffer
{
guint8 *buffer;
gsize buffer_pos;
gsize buffer_len;
guint count;
GLenum target;
guint element_size;
} GskNglBuffer;
void gsk_ngl_buffer_init (GskNglBuffer *self,
GLenum target,
guint element_size);
void gsk_ngl_buffer_destroy (GskNglBuffer *buffer);
GLuint gsk_ngl_buffer_submit (GskNglBuffer *buffer);
static inline gpointer
gsk_ngl_buffer_advance (GskNglBuffer *buffer,
guint count)
{
gpointer ret;
gsize to_alloc = count * buffer->element_size;
if G_UNLIKELY (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_ngl_buffer_retract (GskNglBuffer *buffer,
guint count)
{
buffer->buffer_pos -= count * buffer->element_size;
buffer->count -= count;
}
static inline guint
gsk_ngl_buffer_get_offset (GskNglBuffer *buffer)
{
return buffer->count;
}
G_END_DECLS
#endif /* __GSK_NGL_BUFFER_PRIVATE_H__ */

1507
gsk/ngl/gsknglcommandqueue.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,362 @@
/* gsknglcommandqueueprivate.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
*/
#ifndef __GSK_NGL_COMMAND_QUEUE_PRIVATE_H__
#define __GSK_NGL_COMMAND_QUEUE_PRIVATE_H__
#include <gsk/gskprofilerprivate.h>
#include "gskngltypesprivate.h"
#include "gsknglbufferprivate.h"
#include "gsknglattachmentstateprivate.h"
#include "gskngluniformstateprivate.h"
#include "inlinearray.h"
#include "../gl/gskglprofilerprivate.h"
G_BEGIN_DECLS
#define GSK_TYPE_GL_COMMAND_QUEUE (gsk_ngl_command_queue_get_type())
G_DECLARE_FINAL_TYPE (GskNglCommandQueue, gsk_ngl_command_queue, GSK, NGL_COMMAND_QUEUE, GObject)
typedef enum _GskNglCommandKind
{
/* The batch will perform a glClear() */
GSK_NGL_COMMAND_KIND_CLEAR,
/* The batch will perform a glDrawArrays() */
GSK_NGL_COMMAND_KIND_DRAW,
} GskNglCommandKind;
typedef struct _GskNglCommandBind
{
/* @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 : 5;
/* The identifier for the texture created with glGenTextures(). */
guint id : 27;
} GskNglCommandBind;
G_STATIC_ASSERT (sizeof (GskNglCommandBind) == 4);
typedef struct _GskNglCommandBatchAny
{
/* A GskNglCommandKind 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 GskNglCommandDraw 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;
} GskNglCommandBatchAny;
G_STATIC_ASSERT (sizeof (GskNglCommandBatchAny) == 12);
typedef struct _GskNglCommandDraw
{
GskNglCommandBatchAny head;
/* 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;
/* 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 #GskNglCommandUniform elements to apply.
*/
guint uniform_offset;
/* The offset within the array of bind changes to be made containing
* @bind_count #GskNglCommandBind elements to apply.
*/
guint bind_offset;
} GskNglCommandDraw;
G_STATIC_ASSERT (sizeof (GskNglCommandDraw) == 32);
typedef struct _GskNglCommandClear
{
GskNglCommandBatchAny any;
guint bits;
guint framebuffer;
} GskNglCommandClear;
G_STATIC_ASSERT (sizeof (GskNglCommandClear) == 20);
typedef struct _GskNglCommandUniform
{
GskNglUniformInfo info;
guint location;
} GskNglCommandUniform;
G_STATIC_ASSERT (sizeof (GskNglCommandUniform) == 8);
typedef union _GskNglCommandBatch
{
GskNglCommandBatchAny any;
GskNglCommandDraw draw;
GskNglCommandClear clear;
} GskNglCommandBatch;
G_STATIC_ASSERT (sizeof (GskNglCommandBatch) == 32);
DEFINE_INLINE_ARRAY (GskNglCommandBatches, gsk_ngl_command_batches, GskNglCommandBatch)
DEFINE_INLINE_ARRAY (GskNglCommandBinds, gsk_ngl_command_binds, GskNglCommandBind)
DEFINE_INLINE_ARRAY (GskNglCommandUniforms, gsk_ngl_command_uniforms, GskNglCommandUniform)
struct _GskNglCommandQueue
{
GObject parent_instance;
/* The GdkGLContext we make current before executing GL commands. */
GdkGLContext *context;
/* Array of GskNglCommandBatch 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.
*/
GskNglCommandBatches 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.
*/
GskNglBuffer vertices;
/* The GskNglAttachmentState 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.
*/
GskNglAttachmentState *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.
*/
GskNglUniformState *uniforms;
/* Current program if we are in a draw so that we can send commands
* to the uniform state as needed.
*/
GskNglUniformProgram *program_info;
/* The profiler instance to deliver timing/etc data */
GskProfiler *profiler;
GskGLProfiler *gl_profiler;
/* Array of GskNglCommandBind which denote what textures need to be attached
* to which slot. GskNglCommandDraw.bind_offset and bind_count reference this
* array to determine what to attach.
*/
GskNglCommandBinds batch_binds;
/* Array of GskNglCommandUniform denoting which uniforms must be updated
* before the glDrawArrays() may be called. These are referenced from the
* GskNglCommandDraw.uniform_offset and uniform_count fields.
*/
GskNglCommandUniforms batch_uniforms;
/* String storage for debug groups */
GStringChunk *debug_groups;
/* 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 queue_depth;
} metrics;
/* Counter for uploads on the frame */
guint n_uploads;
/* 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;
};
GskNglCommandQueue *gsk_ngl_command_queue_new (GdkGLContext *context,
GskNglUniformState *uniforms);
void gsk_ngl_command_queue_set_profiler (GskNglCommandQueue *self,
GskProfiler *profiler);
GdkGLContext *gsk_ngl_command_queue_get_context (GskNglCommandQueue *self);
void gsk_ngl_command_queue_make_current (GskNglCommandQueue *self);
void gsk_ngl_command_queue_begin_frame (GskNglCommandQueue *self);
void gsk_ngl_command_queue_end_frame (GskNglCommandQueue *self);
void gsk_ngl_command_queue_execute (GskNglCommandQueue *self,
guint surface_height,
guint scale_factor,
const cairo_region_t *scissor);
int gsk_ngl_command_queue_upload_texture (GskNglCommandQueue *self,
GdkTexture *texture,
guint x_offset,
guint y_offset,
guint width,
guint height,
int min_filter,
int mag_filter);
int gsk_ngl_command_queue_create_texture (GskNglCommandQueue *self,
int width,
int height,
int min_filter,
int mag_filter);
guint gsk_ngl_command_queue_create_framebuffer (GskNglCommandQueue *self);
gboolean gsk_ngl_command_queue_create_render_target (GskNglCommandQueue *self,
int width,
int height,
int min_filter,
int mag_filter,
guint *out_fbo_id,
guint *out_texture_id);
void gsk_ngl_command_queue_delete_program (GskNglCommandQueue *self,
guint program_id);
void gsk_ngl_command_queue_clear (GskNglCommandQueue *self,
guint clear_bits,
const graphene_rect_t *viewport);
void gsk_ngl_command_queue_begin_draw (GskNglCommandQueue *self,
GskNglUniformProgram *program_info,
guint width,
guint height);
void gsk_ngl_command_queue_end_draw (GskNglCommandQueue *self);
void gsk_ngl_command_queue_split_draw (GskNglCommandQueue *self);
static inline GskNglCommandBatch *
gsk_ngl_command_queue_get_batch (GskNglCommandQueue *self)
{
return gsk_ngl_command_batches_tail (&self->batches);
}
static inline GskNglDrawVertex *
gsk_ngl_command_queue_add_vertices (GskNglCommandQueue *self)
{
gsk_ngl_command_queue_get_batch (self)->draw.vbo_count += GSK_NGL_N_VERTICES;
return gsk_ngl_buffer_advance (&self->vertices, GSK_NGL_N_VERTICES);
}
static inline GskNglDrawVertex *
gsk_ngl_command_queue_add_n_vertices (GskNglCommandQueue *self,
guint count)
{
/* This is a batch form of gsk_ngl_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_ngl_buffer_advance (&self->vertices, GSK_NGL_N_VERTICES * count);
}
static inline void
gsk_ngl_command_queue_retract_n_vertices (GskNglCommandQueue *self,
guint count)
{
/* Like gsk_ngl_command_queue_add_n_vertices(), this does not tweak
* the draw vbo_count.
*/
gsk_ngl_buffer_retract (&self->vertices, GSK_NGL_N_VERTICES * count);
}
static inline guint
gsk_ngl_command_queue_bind_framebuffer (GskNglCommandQueue *self,
guint framebuffer)
{
guint ret = self->attachments->fbo.id;
gsk_ngl_attachment_state_bind_framebuffer (self->attachments, framebuffer);
return ret;
}
G_END_DECLS
#endif /* __GSK_NGL_COMMAND_QUEUE_PRIVATE_H__ */

678
gsk/ngl/gsknglcompiler.c Normal file
View File

@ -0,0 +1,678 @@
/* gsknglcompiler.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 "gsknglcommandqueueprivate.h"
#include "gsknglcompilerprivate.h"
#include "gsknglprogramprivate.h"
#define SHADER_VERSION_GLES 100
#define SHADER_VERSION_GL2_LEGACY 110
#define SHADER_VERSION_GL3_LEGACY 130
#define SHADER_VERSION_GL3 150
struct _GskNglCompiler
{
GObject parent_instance;
GskNglDriver *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;
int glsl_version;
guint gl3 : 1;
guint gles : 1;
guint legacy : 1;
guint debug_shaders : 1;
};
typedef struct _GskNglProgramAttrib
{
const char *name;
guint location;
} GskNglProgramAttrib;
static GBytes *empty_bytes;
G_DEFINE_TYPE (GskNglCompiler, gsk_ngl_compiler, G_TYPE_OBJECT)
static void
gsk_ngl_compiler_finalize (GObject *object)
{
GskNglCompiler *self = (GskNglCompiler *)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_ngl_compiler_parent_class)->finalize (object);
}
static void
gsk_ngl_compiler_class_init (GskNglCompilerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gsk_ngl_compiler_finalize;
empty_bytes = g_bytes_new (NULL, 0);
}
static void
gsk_ngl_compiler_init (GskNglCompiler *self)
{
self->glsl_version = 150;
self->attrib_locations = g_array_new (FALSE, FALSE, sizeof (GskNglProgramAttrib));
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);
}
GskNglCompiler *
gsk_ngl_compiler_new (GskNglDriver *driver,
gboolean debug_shaders)
{
GskNglCompiler *self;
GdkGLContext *context;
g_return_val_if_fail (GSK_IS_NGL_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_ngl_command_queue_get_context (self->driver->shared_command_queue);
if (gdk_gl_context_get_use_es (context))
{
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);
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_ngl_command_queue_make_current (self->driver->shared_command_queue);
return g_steal_pointer (&self);
}
void
gsk_ngl_compiler_bind_attribute (GskNglCompiler *self,
const char *name,
guint location)
{
GskNglProgramAttrib attrib;
g_return_if_fail (GSK_IS_NGL_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_ngl_compiler_clear_attributes (GskNglCompiler *self)
{
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
g_array_set_size (self->attrib_locations, 0);
}
void
gsk_ngl_compiler_set_preamble (GskNglCompiler *self,
GskNglCompilerKind kind,
GBytes *preamble_bytes)
{
GBytes **loc = NULL;
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
g_return_if_fail (preamble_bytes != NULL);
if (kind == GSK_NGL_COMPILER_ALL)
loc = &self->all_preamble;
else if (kind == GSK_NGL_COMPILER_FRAGMENT)
loc = &self->fragment_preamble;
else if (kind == GSK_NGL_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_ngl_compiler_set_preamble_from_resource (GskNglCompiler *self,
GskNglCompilerKind kind,
const char *resource_path)
{
GError *error = NULL;
GBytes *bytes;
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
g_return_if_fail (kind == GSK_NGL_COMPILER_ALL ||
kind == GSK_NGL_COMPILER_VERTEX ||
kind == GSK_NGL_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_ngl_compiler_set_preamble (self, kind, bytes);
g_clear_pointer (&bytes, g_bytes_unref);
g_clear_error (&error);
}
void
gsk_ngl_compiler_set_source (GskNglCompiler *self,
GskNglCompilerKind kind,
GBytes *source_bytes)
{
GBytes **loc = NULL;
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
g_return_if_fail (kind == GSK_NGL_COMPILER_ALL ||
kind == GSK_NGL_COMPILER_VERTEX ||
kind == GSK_NGL_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_NGL_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_ngl_compiler_set_source (self, GSK_NGL_COMPILER_VERTEX, vertex_bytes);
gsk_ngl_compiler_set_source (self, GSK_NGL_COMPILER_FRAGMENT, fragment_bytes);
g_bytes_unref (fragment_bytes);
g_bytes_unref (vertex_bytes);
return;
}
if (kind == GSK_NGL_COMPILER_FRAGMENT)
loc = &self->fragment_source;
else if (kind == GSK_NGL_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_ngl_compiler_set_source_from_resource (GskNglCompiler *self,
GskNglCompilerKind kind,
const char *resource_path)
{
GError *error = NULL;
GBytes *bytes;
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
g_return_if_fail (kind == GSK_NGL_COMPILER_ALL ||
kind == GSK_NGL_COMPILER_VERTEX ||
kind == GSK_NGL_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_ngl_compiler_set_source (self, kind, bytes);
g_clear_pointer (&bytes, g_bytes_unref);
g_clear_error (&error);
}
void
gsk_ngl_compiler_set_suffix (GskNglCompiler *self,
GskNglCompilerKind kind,
GBytes *suffix_bytes)
{
GBytes **loc;
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
g_return_if_fail (kind == GSK_NGL_COMPILER_VERTEX ||
kind == GSK_NGL_COMPILER_FRAGMENT);
g_return_if_fail (suffix_bytes != NULL);
if (suffix_bytes == NULL)
suffix_bytes = empty_bytes;
if (kind == GSK_NGL_COMPILER_FRAGMENT)
loc = &self->fragment_suffix;
else if (kind == GSK_NGL_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_ngl_compiler_set_suffix_from_resource (GskNglCompiler *self,
GskNglCompilerKind kind,
const char *resource_path)
{
GError *error = NULL;
GBytes *bytes;
g_return_if_fail (GSK_IS_NGL_COMPILER (self));
g_return_if_fail (kind == GSK_NGL_COMPILER_VERTEX ||
kind == GSK_NGL_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_ngl_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 : "";
}
GskNglProgram *
gsk_ngl_compiler_compile (GskNglCompiler *self,
const char *name,
GError **error)
{
char version[32];
const char *debug = "";
const char *legacy = "";
const char *gl3 = "";
const char *gles = "";
int program_id;
int vertex_id;
int fragment_id;
int status;
g_return_val_if_fail (GSK_IS_NGL_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_ngl_command_queue_make_current (self->driver->command_queue);
g_snprintf (version, sizeof version, "#version %d\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_NGLES 1\n";
if (self->gl3)
gl3 = "#define GSK_NGL3 1\n";
vertex_id = glCreateShader (GL_VERTEX_SHADER);
glShaderSource (vertex_id,
9,
(const char *[]) {
version, debug, legacy, gl3, gles,
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),
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,
9,
(const char *[]) {
version, debug, legacy, gl3, gles,
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),
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 GskNglProgramAttrib *attrib;
attrib = &g_array_index (self->attrib_locations, GskNglProgramAttrib, 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_ngl_program_new (self->driver, name, program_id);
}

View File

@ -0,0 +1,69 @@
/* gsknglcompilerprivate.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
*/
#ifndef __GSK_NGL_COMPILER_PRIVATE_H__
#define __GSK_NGL_COMPILER_PRIVATE_H__
#include "gskngltypesprivate.h"
G_BEGIN_DECLS
typedef enum _GskNglCompilerKind
{
GSK_NGL_COMPILER_ALL,
GSK_NGL_COMPILER_FRAGMENT,
GSK_NGL_COMPILER_VERTEX,
} GskNglCompilerKind;
#define GSK_TYPE_GL_COMPILER (gsk_ngl_compiler_get_type())
G_DECLARE_FINAL_TYPE (GskNglCompiler, gsk_ngl_compiler, GSK, NGL_COMPILER, GObject)
GskNglCompiler *gsk_ngl_compiler_new (GskNglDriver *driver,
gboolean debug);
void gsk_ngl_compiler_set_preamble (GskNglCompiler *self,
GskNglCompilerKind kind,
GBytes *preamble_bytes);
void gsk_ngl_compiler_set_preamble_from_resource (GskNglCompiler *self,
GskNglCompilerKind kind,
const char *resource_path);
void gsk_ngl_compiler_set_source (GskNglCompiler *self,
GskNglCompilerKind kind,
GBytes *source_bytes);
void gsk_ngl_compiler_set_source_from_resource (GskNglCompiler *self,
GskNglCompilerKind kind,
const char *resource_path);
void gsk_ngl_compiler_set_suffix (GskNglCompiler *self,
GskNglCompilerKind kind,
GBytes *suffix_bytes);
void gsk_ngl_compiler_set_suffix_from_resource (GskNglCompiler *self,
GskNglCompilerKind kind,
const char *resource_path);
void gsk_ngl_compiler_bind_attribute (GskNglCompiler *self,
const char *name,
guint location);
void gsk_ngl_compiler_clear_attributes (GskNglCompiler *self);
GskNglProgram *gsk_ngl_compiler_compile (GskNglCompiler *self,
const char *name,
GError **error);
G_END_DECLS
#endif /* __GSK_NGL_COMPILER_PRIVATE_H__ */

1296
gsk/ngl/gskngldriver.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,234 @@
/* gskngldriverprivate.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
*/
#ifndef __GSK_NGL_DRIVER_PRIVATE_H__
#define __GSK_NGL_DRIVER_PRIVATE_H__
#include <gdk/gdkgltextureprivate.h>
#include "gskngltypesprivate.h"
#include "gskngltexturepoolprivate.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_LAST
};
typedef struct {
gconstpointer pointer;
float scale_x;
float scale_y;
int filter;
int pointer_is_child;
graphene_rect_t parent_rect; /* Valid when pointer_is_child */
} GskTextureKey;
#define GSL_GK_NO_UNIFORMS UNIFORM_INVALID_##__COUNTER__
#define GSK_NGL_ADD_UNIFORM(pos, KEY, name) UNIFORM_##KEY = UNIFORM_SHARED_LAST + pos,
#define GSK_NGL_DEFINE_PROGRAM(name, resource, uniforms) enum { uniforms };
# include "gsknglprograms.defs"
#undef GSK_NGL_DEFINE_PROGRAM
#undef GSK_NGL_ADD_UNIFORM
#undef GSL_GK_NO_UNIFORMS
#define GSK_TYPE_NGL_DRIVER (gsk_ngl_driver_get_type())
G_DECLARE_FINAL_TYPE (GskNglDriver, gsk_ngl_driver, GSK, NGL_DRIVER, GObject)
struct _GskNglRenderTarget
{
guint framebuffer_id;
guint texture_id;
int min_filter;
int mag_filter;
int width;
int height;
};
struct _GskNglDriver
{
GObject parent_instance;
GskNglCommandQueue *shared_command_queue;
GskNglCommandQueue *command_queue;
GskNglTexturePool texture_pool;
GskNglGlyphLibrary *glyphs;
GskNglIconLibrary *icons;
GskNglShadowLibrary *shadows;
GHashTable *textures;
GHashTable *key_to_texture_id;
GHashTable *texture_id_to_key;
GPtrArray *atlases;
GHashTable *shader_cache;
GArray *autorelease_framebuffers;
GPtrArray *render_targets;
#define GSK_NGL_NO_UNIFORMS
#define GSK_NGL_ADD_UNIFORM(pos, KEY, name)
#define GSK_NGL_DEFINE_PROGRAM(name, resource, uniforms) GskNglProgram *name;
# include "gsknglprograms.defs"
#undef GSK_NGL_NO_UNIFORMS
#undef GSK_NGL_ADD_UNIFORM
#undef GSK_NGL_DEFINE_PROGRAM
gint64 current_frame_id;
/* Used to reduce number of comparisons */
guint stamps[UNIFORM_SHARED_LAST];
guint debug : 1;
guint in_frame : 1;
};
GskNglDriver *gsk_ngl_driver_from_shared_context (GdkGLContext *context,
gboolean debug_shaders,
GError **error);
GskNglCommandQueue *gsk_ngl_driver_create_command_queue (GskNglDriver *self,
GdkGLContext *context);
GdkGLContext *gsk_ngl_driver_get_context (GskNglDriver *self);
gboolean gsk_ngl_driver_create_render_target (GskNglDriver *self,
int width,
int height,
int min_filter,
int mag_filter,
GskNglRenderTarget **render_target);
guint gsk_ngl_driver_release_render_target (GskNglDriver *self,
GskNglRenderTarget *render_target,
gboolean release_texture);
void gsk_ngl_driver_begin_frame (GskNglDriver *self,
GskNglCommandQueue *command_queue);
void gsk_ngl_driver_end_frame (GskNglDriver *self);
void gsk_ngl_driver_after_frame (GskNglDriver *self);
GdkTexture *gsk_ngl_driver_create_gdk_texture (GskNglDriver *self,
guint texture_id);
void gsk_ngl_driver_cache_texture (GskNglDriver *self,
const GskTextureKey *key,
guint texture_id);
guint gsk_ngl_driver_load_texture (GskNglDriver *self,
GdkTexture *texture,
int min_filter,
int mag_filter);
GskNglTexture *gsk_ngl_driver_create_texture (GskNglDriver *self,
float width,
float height,
int min_filter,
int mag_filter);
void gsk_ngl_driver_release_texture (GskNglDriver *self,
GskNglTexture *texture);
void gsk_ngl_driver_release_texture_by_id (GskNglDriver *self,
guint texture_id);
GskNglTexture *gsk_ngl_driver_mark_texture_permanent (GskNglDriver *self,
guint texture_id);
void gsk_ngl_driver_add_texture_slices (GskNglDriver *self,
GdkTexture *texture,
GskNglTextureSlice **out_slices,
guint *out_n_slices);
GskNglProgram *gsk_ngl_driver_lookup_shader (GskNglDriver *self,
GskGLShader *shader,
GError **error);
GskNglTextureAtlas *gsk_ngl_driver_create_atlas (GskNglDriver *self);
#ifdef G_ENABLE_DEBUG
void gsk_ngl_driver_save_atlases_to_png (GskNglDriver *self,
const char *directory);
#endif
static inline GskNglTexture *
gsk_ngl_driver_get_texture_by_id (GskNglDriver *self,
guint texture_id)
{
return g_hash_table_lookup (self->textures, GUINT_TO_POINTER (texture_id));
}
/**
* gsk_ngl_driver_lookup_texture:
* @self: a #GskNglDriver
* @key: the key for the texture
*
* 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_ngl_driver_lookup_texture (GskNglDriver *self,
const GskTextureKey *key)
{
gpointer id;
if (g_hash_table_lookup_extended (self->key_to_texture_id, key, NULL, &id))
{
GskNglTexture *texture = g_hash_table_lookup (self->textures, id);
if (texture != NULL)
texture->last_used_in_frame = self->current_frame_id;
return GPOINTER_TO_UINT (id);
}
return 0;
}
static inline void
gsk_ngl_driver_slice_texture (GskNglDriver *self,
GdkTexture *texture,
GskNglTextureSlice **out_slices,
guint *out_n_slices)
{
GskNglTexture *t;
if ((t = gdk_texture_get_render_data (texture, self)))
{
*out_slices = t->slices;
*out_n_slices = t->n_slices;
return;
}
gsk_ngl_driver_add_texture_slices (self, texture, out_slices, out_n_slices);
}
G_END_DECLS
#endif /* __GSK_NGL_DRIVER_PRIVATE_H__ */

View File

@ -0,0 +1,325 @@
/* gsknglglyphlibrary.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/gdkmemorytextureprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include "gsknglcommandqueueprivate.h"
#include "gskngldriverprivate.h"
#include "gsknglglyphlibraryprivate.h"
#define MAX_GLYPH_SIZE 128
G_DEFINE_TYPE (GskNglGlyphLibrary, gsk_ngl_glyph_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
GskNglGlyphLibrary *
gsk_ngl_glyph_library_new (GskNglDriver *driver)
{
g_return_val_if_fail (GSK_IS_NGL_DRIVER (driver), NULL);
return g_object_new (GSK_TYPE_GL_GLYPH_LIBRARY,
"driver", driver,
NULL);
}
static guint
gsk_ngl_glyph_key_hash (gconstpointer data)
{
const GskNglGlyphKey *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 GskNglGlyphKey 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_ngl_glyph_key_equal (gconstpointer v1,
gconstpointer v2)
{
return memcmp (v1, v2, sizeof (GskNglGlyphKey)) == 0;
}
static void
gsk_ngl_glyph_key_free (gpointer data)
{
GskNglGlyphKey *key = data;
g_clear_object (&key->font);
g_slice_free (GskNglGlyphKey, key);
}
static void
gsk_ngl_glyph_value_free (gpointer data)
{
g_slice_free (GskNglGlyphValue, data);
}
static void
gsk_ngl_glyph_library_finalize (GObject *object)
{
GskNglGlyphLibrary *self = (GskNglGlyphLibrary *)object;
g_clear_pointer (&self->hash_table, g_hash_table_unref);
g_clear_pointer (&self->surface_data, g_free);
G_OBJECT_CLASS (gsk_ngl_glyph_library_parent_class)->finalize (object);
}
static void
gsk_ngl_glyph_library_class_init (GskNglGlyphLibraryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gsk_ngl_glyph_library_finalize;
}
static void
gsk_ngl_glyph_library_init (GskNglGlyphLibrary *self)
{
GSK_NGL_TEXTURE_LIBRARY (self)->max_entry_size = MAX_GLYPH_SIZE;
gsk_ngl_texture_library_set_funcs (GSK_NGL_TEXTURE_LIBRARY (self),
gsk_ngl_glyph_key_hash,
gsk_ngl_glyph_key_equal,
gsk_ngl_glyph_key_free,
gsk_ngl_glyph_value_free);
}
static cairo_surface_t *
gsk_ngl_glyph_library_create_surface (GskNglGlyphLibrary *self,
int stride,
int width,
int height,
double device_scale)
{
cairo_surface_t *surface;
gsize n_bytes;
g_assert (GSK_IS_NGL_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, device_scale, device_scale);
return surface;
}
static void
render_glyph (cairo_surface_t *surface,
const cairo_scaled_font_t *scaled_font,
const GskNglGlyphKey *key,
const GskNglGlyphValue *value)
{
cairo_t *cr;
PangoGlyphString glyph_string;
PangoGlyphInfo glyph_info;
g_assert (surface != NULL);
g_assert (scaled_font != NULL);
cr = cairo_create (surface);
cairo_set_scaled_font (cr, scaled_font);
cairo_set_source_rgba (cr, 1, 1, 1, 1);
glyph_info.glyph = key->glyph;
glyph_info.geometry.width = value->ink_rect.width * 1024;
if (glyph_info.glyph & PANGO_GLYPH_UNKNOWN_FLAG)
glyph_info.geometry.x_offset = 250 * key->xshift;
else
glyph_info.geometry.x_offset = 250 * key->xshift - value->ink_rect.x * 1024;
glyph_info.geometry.y_offset = 250 * 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_ngl_glyph_library_upload_glyph (GskNglGlyphLibrary *self,
const GskNglGlyphKey *key,
const GskNglGlyphValue *value,
int width,
int height,
double device_scale)
{
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
cairo_scaled_font_t *scaled_font;
GskNglTextureAtlas *atlas;
cairo_surface_t *surface;
guchar *pixel_data;
guchar *free_data = NULL;
guint gl_format;
guint gl_type;
guint texture_id;
gsize stride;
int x, y;
g_assert (GSK_IS_NGL_GLYPH_LIBRARY (self));
g_assert (key != NULL);
g_assert (value != NULL);
scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *)key->font);
if G_UNLIKELY (scaled_font == NULL ||
cairo_scaled_font_status (scaled_font) != CAIRO_STATUS_SUCCESS)
return;
stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width);
atlas = value->entry.is_atlased ? value->entry.atlas : NULL;
gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
"Uploading glyph %d",
key->glyph);
surface = gsk_ngl_glyph_library_create_surface (self, stride, width, height, device_scale);
render_glyph (surface, scaled_font, key, value);
texture_id = GSK_NGL_TEXTURE_ATLAS_ENTRY_TEXTURE (value);
g_assert (texture_id > 0);
glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / 4);
glBindTexture (GL_TEXTURE_2D, texture_id);
if G_UNLIKELY (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,
cairo_image_surface_get_data (surface),
width * 4,
GDK_MEMORY_DEFAULT,
width, height);
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;
}
if G_LIKELY (atlas != NULL)
{
x = atlas->width * value->entry.area.x;
y = atlas->width * value->entry.area.y;
}
else
{
x = 0;
y = 0;
}
glTexSubImage2D (GL_TEXTURE_2D, 0, x, y, width, height,
gl_format, gl_type, pixel_data);
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
cairo_surface_destroy (surface);
g_free (free_data);
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
GSK_NGL_TEXTURE_LIBRARY (self)->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_ngl_glyph_library_add (GskNglGlyphLibrary *self,
GskNglGlyphKey *key,
const GskNglGlyphValue **out_value)
{
PangoRectangle ink_rect;
GskNglGlyphValue *value;
int width;
int height;
guint packed_x;
guint packed_y;
g_assert (GSK_IS_NGL_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);
if (key->xshift != 0)
ink_rect.width++;
if (key->yshift != 0)
ink_rect.height++;
width = ink_rect.width * key->scale / 1024;
height = ink_rect.height * key->scale / 1024;
value = gsk_ngl_texture_library_pack (GSK_NGL_TEXTURE_LIBRARY (self),
key,
sizeof *value,
width,
height,
0,
&packed_x, &packed_y);
memcpy (&value->ink_rect, &ink_rect, sizeof ink_rect);
if (key->scale > 0 && width > 0 && height > 0)
gsk_ngl_glyph_library_upload_glyph (self,
key,
value,
width,
height,
key->scale / 1024.0);
*out_value = value;
return GSK_NGL_TEXTURE_ATLAS_ENTRY_TEXTURE (value) != 0;
}

View File

@ -0,0 +1,119 @@
/* gsknglglyphlibraryprivate.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
*/
#ifndef __GSK_NGL_GLYPH_LIBRARY_PRIVATE_H__
#define __GSK_NGL_GLYPH_LIBRARY_PRIVATE_H__
#include <pango/pango.h>
#include "gskngltexturelibraryprivate.h"
G_BEGIN_DECLS
#define GSK_TYPE_GL_GLYPH_LIBRARY (gsk_ngl_glyph_library_get_type())
typedef struct _GskNglGlyphKey
{
PangoFont *font;
PangoGlyph glyph;
guint xshift : 3;
guint yshift : 3;
guint scale : 26; /* times 1024 */
} GskNglGlyphKey;
typedef struct _GskNglGlyphValue
{
GskNglTextureAtlasEntry entry;
PangoRectangle ink_rect;
} GskNglGlyphValue;
#if GLIB_SIZEOF_VOID_P == 8
G_STATIC_ASSERT (sizeof (GskNglGlyphKey) == 16);
#elif GLIB_SIZEOF_VOID_P == 4
G_STATIC_ASSERT (sizeof (GskNglGlyphKey) == 12);
#endif
G_DECLARE_FINAL_TYPE (GskNglGlyphLibrary, gsk_ngl_glyph_library, GSK, NGL_GLYPH_LIBRARY, GskNglTextureLibrary)
struct _GskNglGlyphLibrary
{
GskNglTextureLibrary parent_instance;
GHashTable *hash_table;
guint8 *surface_data;
gsize surface_data_len;
struct {
GskNglGlyphKey key;
const GskNglGlyphValue *value;
} front[256];
};
GskNglGlyphLibrary *gsk_ngl_glyph_library_new (GskNglDriver *driver);
gboolean gsk_ngl_glyph_library_add (GskNglGlyphLibrary *self,
GskNglGlyphKey *key,
const GskNglGlyphValue **out_value);
static inline int
gsk_ngl_glyph_key_phase (float value)
{
return floor (4 * (value + 0.125)) - 4 * floor (value + 0.125);
}
static inline void
gsk_ngl_glyph_key_set_glyph_and_shift (GskNglGlyphKey *key,
PangoGlyph glyph,
float x,
float y)
{
key->glyph = glyph;
key->xshift = gsk_ngl_glyph_key_phase (x);
key->yshift = gsk_ngl_glyph_key_phase (y);
}
static inline gboolean
gsk_ngl_glyph_library_lookup_or_add (GskNglGlyphLibrary *self,
const GskNglGlyphKey *key,
const GskNglGlyphValue **out_value)
{
GskNglTextureAtlasEntry *entry;
guint front_index = key->glyph & 0xFF;
if (memcmp (key, &self->front[front_index], sizeof *key) == 0)
{
*out_value = self->front[front_index].value;
}
else if (gsk_ngl_texture_library_lookup ((GskNglTextureLibrary *)self, key, &entry))
{
*out_value = (GskNglGlyphValue *)entry;
self->front[front_index].key = *key;
self->front[front_index].value = *out_value;
}
else
{
GskNglGlyphKey *k = g_slice_copy (sizeof *key, key);
g_object_ref (k->font);
gsk_ngl_glyph_library_add (self, k, out_value);
}
return GSK_NGL_TEXTURE_ATLAS_ENTRY_TEXTURE (*out_value) != 0;
}
G_END_DECLS
#endif /* __GSK_NGL_GLYPH_LIBRARY_PRIVATE_H__ */

213
gsk/ngl/gskngliconlibrary.c Normal file
View File

@ -0,0 +1,213 @@
/* gskngliconlibrary.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/gdkmemorytextureprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdktextureprivate.h>
#include "gsknglcommandqueueprivate.h"
#include "gskngldriverprivate.h"
#include "gskngliconlibraryprivate.h"
struct _GskNglIconLibrary
{
GskNglTextureLibrary parent_instance;
};
G_DEFINE_TYPE (GskNglIconLibrary, gsk_ngl_icon_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
GskNglIconLibrary *
gsk_ngl_icon_library_new (GskNglDriver *driver)
{
g_return_val_if_fail (GSK_IS_NGL_DRIVER (driver), NULL);
return g_object_new (GSK_TYPE_GL_ICON_LIBRARY,
"driver", driver,
NULL);
}
static void
gsk_ngl_icon_data_free (gpointer data)
{
GskNglIconData *icon_data = data;
g_clear_object (&icon_data->source_texture);
g_slice_free (GskNglIconData, icon_data);
}
static void
gsk_ngl_icon_library_class_init (GskNglIconLibraryClass *klass)
{
}
static void
gsk_ngl_icon_library_init (GskNglIconLibrary *self)
{
GSK_NGL_TEXTURE_LIBRARY (self)->max_entry_size = 128;
gsk_ngl_texture_library_set_funcs (GSK_NGL_TEXTURE_LIBRARY (self),
NULL, NULL, NULL,
gsk_ngl_icon_data_free);
}
void
gsk_ngl_icon_library_add (GskNglIconLibrary *self,
GdkTexture *key,
const GskNglIconData **out_value)
{
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
cairo_surface_t *surface;
GskNglIconData *icon_data;
guint8 *pixel_data;
guint8 *surface_data;
guint8 *free_data = NULL;
guint gl_format;
guint gl_type;
guint packed_x;
guint packed_y;
int width;
int height;
guint texture_id;
g_assert (GSK_IS_NGL_ICON_LIBRARY (self));
g_assert (GDK_IS_TEXTURE (key));
g_assert (out_value != NULL);
width = key->width;
height = key->height;
icon_data = gsk_ngl_texture_library_pack (GSK_NGL_TEXTURE_LIBRARY (self),
key,
sizeof (GskNglIconData),
width, height, 1,
&packed_x, &packed_y);
icon_data->source_texture = g_object_ref (key);
/* actually upload the texture */
surface = gdk_texture_download_surface (key);
surface_data = cairo_image_surface_get_data (surface);
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 ()))
{
pixel_data = free_data = g_malloc (width * height * 4);
gdk_memory_convert (pixel_data, width * 4,
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
surface_data, cairo_image_surface_get_stride (surface),
GDK_MEMORY_DEFAULT, width, height);
gl_format = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;
}
else
{
pixel_data = surface_data;
gl_format = GL_BGRA;
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
}
texture_id = GSK_NGL_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 */
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);
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
*out_value = icon_data;
cairo_surface_destroy (surface);
g_free (free_data);
GSK_NGL_TEXTURE_LIBRARY (self)->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

@ -0,0 +1,60 @@
/* gskngliconlibraryprivate.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
*/
#ifndef __GSK_NGL_ICON_LIBRARY_PRIVATE_H__
#define __GSK_NGL_ICON_LIBRARY_PRIVATE_H__
#include <pango/pango.h>
#include "gskngltexturelibraryprivate.h"
G_BEGIN_DECLS
#define GSK_TYPE_GL_ICON_LIBRARY (gsk_ngl_icon_library_get_type())
typedef struct _GskNglIconData
{
GskNglTextureAtlasEntry entry;
GdkTexture *source_texture;
} GskNglIconData;
G_DECLARE_FINAL_TYPE (GskNglIconLibrary, gsk_ngl_icon_library, GSK, NGL_ICON_LIBRARY, GskNglTextureLibrary)
GskNglIconLibrary *gsk_ngl_icon_library_new (GskNglDriver *driver);
void gsk_ngl_icon_library_add (GskNglIconLibrary *self,
GdkTexture *key,
const GskNglIconData **out_value);
static inline void
gsk_ngl_icon_library_lookup_or_add (GskNglIconLibrary *self,
GdkTexture *key,
const GskNglIconData **out_value)
{
GskNglTextureAtlasEntry *entry;
if G_LIKELY (gsk_ngl_texture_library_lookup ((GskNglTextureLibrary *)self, key, &entry))
*out_value = (GskNglIconData *)entry;
else
gsk_ngl_icon_library_add (self, key, out_value);
}
G_END_DECLS
#endif /* __GSK_NGL_ICON_LIBRARY_PRIVATE_H__ */

176
gsk/ngl/gsknglprogram.c Normal file
View File

@ -0,0 +1,176 @@
/* gsknglprogram.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 "gsknglcommandqueueprivate.h"
#include "gsknglprogramprivate.h"
#include "gskngluniformstateprivate.h"
G_DEFINE_TYPE (GskNglProgram, gsk_ngl_program, G_TYPE_OBJECT)
GskNglProgram *
gsk_ngl_program_new (GskNglDriver *driver,
const char *name,
int program_id)
{
GskNglProgram *self;
g_return_val_if_fail (GSK_IS_NGL_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_uniforms = 0;
return self;
}
static void
gsk_ngl_program_finalize (GObject *object)
{
GskNglProgram *self = (GskNglProgram *)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_ngl_program_parent_class)->finalize (object);
}
static void
gsk_ngl_program_class_init (GskNglProgramClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gsk_ngl_program_finalize;
}
static void
gsk_ngl_program_init (GskNglProgram *self)
{
self->id = -1;
for (guint i = 0; i < G_N_ELEMENTS (self->uniform_locations); i++)
self->uniform_locations[i] = -1;
}
/**
* gsk_ngl_program_add_uniform:
* @self: a #GskNglProgram
* @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_ngl_program_add_uniform (program, "u_source", UNIFORM_SOURCE);
* ```
*
* That allows you to set values for the program with something
* like the following:
*
* ```
* gsk_ngl_program_set_uniform1i (program, UNIFORM_SOURCE, 1);
* ```
*
* Returns: %TRUE if the uniform was found; otherwise %FALSE
*/
gboolean
gsk_ngl_program_add_uniform (GskNglProgram *self,
const char *name,
guint key)
{
GLint location;
g_return_val_if_fail (GSK_IS_NGL_PROGRAM (self), FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (key < 1024, FALSE);
if (-1 == (location = glGetUniformLocation (self->id, name)))
return FALSE;
self->uniform_locations[key] = location;
if (location >= self->n_uniforms)
self->n_uniforms = location + 1;
#if 0
g_print ("program [%d] %s uniform %s at location %d.\n",
self->id, self->name, name, location);
#endif
return TRUE;
}
/**
* gsk_ngl_program_delete:
* @self: a #GskNglProgram
*
* Deletes the GLSL program.
*
* You must call gsk_ngl_program_use() before and
* gsk_ngl_program_unuse() after this function.
*/
void
gsk_ngl_program_delete (GskNglProgram *self)
{
g_return_if_fail (GSK_IS_NGL_PROGRAM (self));
g_return_if_fail (self->driver->command_queue != NULL);
gsk_ngl_command_queue_delete_program (self->driver->command_queue, self->id);
self->id = -1;
}
/**
* gsk_ngl_program_uniforms_added:
* @self: a #GskNglProgram
* @has_attachments: if any uniform is for a bind/texture attachment
*
* This function should be called after all of the uniforms ahve
* been added with gsk_ngl_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_ngl_program_uniforms_added (GskNglProgram *self,
gboolean has_attachments)
{
g_return_if_fail (GSK_IS_NGL_PROGRAM (self));
g_return_if_fail (self->uniforms == NULL);
self->uniforms = self->driver->command_queue->uniforms;
self->program_info = gsk_ngl_uniform_state_get_program (self->uniforms, self->id, self->n_uniforms);
self->program_info->has_attachments = has_attachments;
}

View File

@ -0,0 +1,273 @@
/* gsknglprogramprivate.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
*/
#ifndef __GSK_NGL_PROGRAM_PRIVATE_H__
#define __GSK_NGL_PROGRAM_PRIVATE_H__
#include "gskngltypesprivate.h"
#include "gsknglcommandqueueprivate.h"
#include "gskngldriverprivate.h"
G_BEGIN_DECLS
#define GSK_TYPE_GL_PROGRAM (gsk_ngl_program_get_type())
G_DECLARE_FINAL_TYPE (GskNglProgram, gsk_ngl_program, GSK, NGL_PROGRAM, GObject)
struct _GskNglProgram
{
GObject parent_instance;
int id;
char *name;
GskNglDriver *driver;
/* In reality, this is the largest uniform position
* as returned after linking so that we can use direct
* indexes based on location.
*/
guint n_uniforms;
/* Cached pointer to avoid lots of pointer chasing/lookups */
GskNglUniformState *uniforms;
GskNglUniformProgram *program_info;
/* For custom programs */
int texture_locations[4];
int args_locations[8];
int size_location;
/* Static array for key->location transforms */
int uniform_locations[32];
};
GskNglProgram *gsk_ngl_program_new (GskNglDriver *driver,
const char *name,
int program_id);
gboolean gsk_ngl_program_add_uniform (GskNglProgram *self,
const char *name,
guint key);
void gsk_ngl_program_uniforms_added (GskNglProgram *self,
gboolean has_attachments);
void gsk_ngl_program_delete (GskNglProgram *self);
#define gsk_ngl_program_get_uniform_location(s,k) ((s)->uniform_locations[(k)])
static inline void
gsk_ngl_program_set_uniform1fv (GskNglProgram *self,
guint key,
guint stamp,
guint count,
const float *values)
{
gsk_ngl_uniform_state_set1fv (self->uniforms, self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, count, values);
}
static inline void
gsk_ngl_program_set_uniform2fv (GskNglProgram *self,
guint key,
guint stamp,
guint count,
const float *values)
{
gsk_ngl_uniform_state_set2fv (self->uniforms, self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, count, values);
}
static inline void
gsk_ngl_program_set_uniform4fv (GskNglProgram *self,
guint key,
guint stamp,
guint count,
const float *values)
{
gsk_ngl_uniform_state_set4fv (self->uniforms, self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, count, values);
}
static inline void
gsk_ngl_program_set_uniform_rounded_rect (GskNglProgram *self,
guint key,
guint stamp,
const GskRoundedRect *rounded_rect)
{
gsk_ngl_uniform_state_set_rounded_rect (self->uniforms, self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, rounded_rect);
}
static inline void
gsk_ngl_program_set_uniform1i (GskNglProgram *self,
guint key,
guint stamp,
int value0)
{
gsk_ngl_uniform_state_set1i (self->uniforms,
self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, value0);
}
static inline void
gsk_ngl_program_set_uniform2i (GskNglProgram *self,
guint key,
guint stamp,
int value0,
int value1)
{
gsk_ngl_uniform_state_set2i (self->uniforms,
self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, value0, value1);
}
static inline void
gsk_ngl_program_set_uniform3i (GskNglProgram *self,
guint key,
guint stamp,
int value0,
int value1,
int value2)
{
gsk_ngl_uniform_state_set3i (self->uniforms,
self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, value0, value1, value2);
}
static inline void
gsk_ngl_program_set_uniform4i (GskNglProgram *self,
guint key,
guint stamp,
int value0,
int value1,
int value2,
int value3)
{
gsk_ngl_uniform_state_set4i (self->uniforms,
self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, value0, value1, value2, value3);
}
static inline void
gsk_ngl_program_set_uniform1f (GskNglProgram *self,
guint key,
guint stamp,
float value0)
{
gsk_ngl_uniform_state_set1f (self->uniforms,
self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, value0);
}
static inline void
gsk_ngl_program_set_uniform2f (GskNglProgram *self,
guint key,
guint stamp,
float value0,
float value1)
{
gsk_ngl_uniform_state_set2f (self->uniforms,
self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, value0, value1);
}
static inline void
gsk_ngl_program_set_uniform3f (GskNglProgram *self,
guint key,
guint stamp,
float value0,
float value1,
float value2)
{
gsk_ngl_uniform_state_set3f (self->uniforms,
self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, value0, value1, value2);
}
static inline void
gsk_ngl_program_set_uniform4f (GskNglProgram *self,
guint key,
guint stamp,
float value0,
float value1,
float value2,
float value3)
{
gsk_ngl_uniform_state_set4f (self->uniforms,
self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, value0, value1, value2, value3);
}
static inline void
gsk_ngl_program_set_uniform_color (GskNglProgram *self,
guint key,
guint stamp,
const GdkRGBA *color)
{
gsk_ngl_uniform_state_set_color (self->uniforms,
self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, color);
}
static inline void
gsk_ngl_program_set_uniform_texture (GskNglProgram *self,
guint key,
guint stamp,
GLenum texture_target,
GLenum texture_slot,
guint texture_id)
{
gsk_ngl_attachment_state_bind_texture (self->driver->command_queue->attachments,
texture_target,
texture_slot,
texture_id);
gsk_ngl_uniform_state_set_texture (self->uniforms,
self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, texture_slot);
}
static inline void
gsk_ngl_program_set_uniform_matrix (GskNglProgram *self,
guint key,
guint stamp,
const graphene_matrix_t *matrix)
{
gsk_ngl_uniform_state_set_matrix (self->uniforms,
self->program_info,
gsk_ngl_program_get_uniform_location (self, key),
stamp, matrix);
}
G_END_DECLS
#endif /* __GSK_NGL_PROGRAM_PRIVATE_H__ */

View File

@ -0,0 +1,83 @@
GSK_NGL_DEFINE_PROGRAM (blend,
"/org/gtk/libgsk/glsl/blend.glsl",
GSK_NGL_ADD_UNIFORM (1, BLEND_SOURCE2, u_source2)
GSK_NGL_ADD_UNIFORM (2, BLEND_MODE, u_mode))
GSK_NGL_DEFINE_PROGRAM (blit,
"/org/gtk/libgsk/glsl/blit.glsl",
GSK_NGL_NO_UNIFORMS)
GSK_NGL_DEFINE_PROGRAM (blur,
"/org/gtk/libgsk/glsl/blur.glsl",
GSK_NGL_ADD_UNIFORM (1, BLUR_RADIUS, u_blur_radius)
GSK_NGL_ADD_UNIFORM (2, BLUR_SIZE, u_blur_size)
GSK_NGL_ADD_UNIFORM (3, BLUR_DIR, u_blur_dir))
GSK_NGL_DEFINE_PROGRAM (border,
"/org/gtk/libgsk/glsl/border.glsl",
GSK_NGL_ADD_UNIFORM (1, BORDER_COLOR, u_color)
GSK_NGL_ADD_UNIFORM (2, BORDER_WIDTHS, u_widths)
GSK_NGL_ADD_UNIFORM (3, BORDER_OUTLINE_RECT, u_outline_rect))
GSK_NGL_DEFINE_PROGRAM (color,
"/org/gtk/libgsk/glsl/color.glsl",
GSK_NGL_ADD_UNIFORM (1, COLOR_COLOR, u_color))
GSK_NGL_DEFINE_PROGRAM (coloring,
"/org/gtk/libgsk/glsl/coloring.glsl",
GSK_NGL_ADD_UNIFORM (1, COLORING_COLOR, u_color))
GSK_NGL_DEFINE_PROGRAM (color_matrix,
"/org/gtk/libgsk/glsl/color_matrix.glsl",
GSK_NGL_ADD_UNIFORM (1, COLOR_MATRIX_COLOR_MATRIX, u_color_matrix)
GSK_NGL_ADD_UNIFORM (2, COLOR_MATRIX_COLOR_OFFSET, u_color_offset))
GSK_NGL_DEFINE_PROGRAM (conic_gradient,
"/org/gtk/libgsk/glsl/conic_gradient.glsl",
GSK_NGL_ADD_UNIFORM (1, CONIC_GRADIENT_COLOR_STOPS, u_color_stops)
GSK_NGL_ADD_UNIFORM (2, CONIC_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops)
GSK_NGL_ADD_UNIFORM (3, CONIC_GRADIENT_GEOMETRY, u_geometry))
GSK_NGL_DEFINE_PROGRAM (cross_fade,
"/org/gtk/libgsk/glsl/cross_fade.glsl",
GSK_NGL_ADD_UNIFORM (1, CROSS_FADE_PROGRESS, u_progress)
GSK_NGL_ADD_UNIFORM (2, CROSS_FADE_SOURCE2, u_source2))
GSK_NGL_DEFINE_PROGRAM (inset_shadow,
"/org/gtk/libgsk/glsl/inset_shadow.glsl",
GSK_NGL_ADD_UNIFORM (1, INSET_SHADOW_COLOR, u_color)
GSK_NGL_ADD_UNIFORM (2, INSET_SHADOW_SPREAD, u_spread)
GSK_NGL_ADD_UNIFORM (3, INSET_SHADOW_OFFSET, u_offset)
GSK_NGL_ADD_UNIFORM (4, INSET_SHADOW_OUTLINE_RECT, u_outline_rect))
GSK_NGL_DEFINE_PROGRAM (linear_gradient,
"/org/gtk/libgsk/glsl/linear_gradient.glsl",
GSK_NGL_ADD_UNIFORM (1, LINEAR_GRADIENT_COLOR_STOPS, u_color_stops)
GSK_NGL_ADD_UNIFORM (2, LINEAR_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops)
GSK_NGL_ADD_UNIFORM (3, LINEAR_GRADIENT_POINTS, u_points)
GSK_NGL_ADD_UNIFORM (4, LINEAR_GRADIENT_REPEAT, u_repeat))
GSK_NGL_DEFINE_PROGRAM (outset_shadow,
"/org/gtk/libgsk/glsl/outset_shadow.glsl",
GSK_NGL_ADD_UNIFORM (1, OUTSET_SHADOW_COLOR, u_color)
GSK_NGL_ADD_UNIFORM (2, OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect))
GSK_NGL_DEFINE_PROGRAM (radial_gradient,
"/org/gtk/libgsk/glsl/radial_gradient.glsl",
GSK_NGL_ADD_UNIFORM (1, RADIAL_GRADIENT_COLOR_STOPS, u_color_stops)
GSK_NGL_ADD_UNIFORM (2, RADIAL_GRADIENT_NUM_COLOR_STOPS, u_num_color_stops)
GSK_NGL_ADD_UNIFORM (3, RADIAL_GRADIENT_REPEAT, u_repeat)
GSK_NGL_ADD_UNIFORM (4, RADIAL_GRADIENT_RANGE, u_range)
GSK_NGL_ADD_UNIFORM (5, RADIAL_GRADIENT_GEOMETRY, u_geometry))
GSK_NGL_DEFINE_PROGRAM (repeat,
"/org/gtk/libgsk/glsl/repeat.glsl",
GSK_NGL_ADD_UNIFORM (1, REPEAT_CHILD_BOUNDS, u_child_bounds)
GSK_NGL_ADD_UNIFORM (2, REPEAT_TEXTURE_RECT, u_texture_rect))
GSK_NGL_DEFINE_PROGRAM (unblurred_outset_shadow,
"/org/gtk/libgsk/glsl/unblurred_outset_shadow.glsl",
GSK_NGL_ADD_UNIFORM (1, UNBLURRED_OUTSET_SHADOW_COLOR, u_color)
GSK_NGL_ADD_UNIFORM (2, UNBLURRED_OUTSET_SHADOW_SPREAD, u_spread)
GSK_NGL_ADD_UNIFORM (3, UNBLURRED_OUTSET_SHADOW_OFFSET, u_offset)
GSK_NGL_ADD_UNIFORM (4, UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect))

312
gsk/ngl/gsknglrenderer.c Normal file
View File

@ -0,0 +1,312 @@
/* gsknglrenderer.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/gdkprofilerprivate.h>
#include <gdk/gdksurfaceprivate.h>
#include <gsk/gskdebugprivate.h>
#include <gsk/gskrendererprivate.h>
#include "gsknglcommandqueueprivate.h"
#include "gskngldriverprivate.h"
#include "gsknglprogramprivate.h"
#include "gsknglrenderjobprivate.h"
#include "gsknglrendererprivate.h"
struct _GskNglRendererClass
{
GskRendererClass parent_class;
};
struct _GskNglRenderer
{
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.
*/
GskNglCommandQueue *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.
*/
GskNglDriver *driver;
};
G_DEFINE_TYPE (GskNglRenderer, gsk_ngl_renderer, GSK_TYPE_RENDERER)
GskRenderer *
gsk_ngl_renderer_new (void)
{
return g_object_new (GSK_TYPE_NGL_RENDERER, NULL);
}
static gboolean
gsk_ngl_renderer_realize (GskRenderer *renderer,
GdkSurface *surface,
GError **error)
{
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
GskNglRenderer *self = (GskNglRenderer *)renderer;
GdkGLContext *context = NULL;
GdkGLContext *shared_context;
GskNglDriver *driver = NULL;
gboolean ret = FALSE;
gboolean debug_shaders = FALSE;
g_assert (GSK_IS_NGL_RENDERER (self));
g_assert (GDK_IS_SURFACE (surface));
if (self->context != NULL)
return TRUE;
g_assert (self->driver == NULL);
g_assert (self->context == NULL);
g_assert (self->command_queue == NULL);
if (!(context = gdk_surface_create_gl_context (surface, error)) ||
!gdk_gl_context_realize (context, error))
goto failure;
if (!(shared_context = gdk_surface_get_shared_data_gl_context (surface)))
{
g_set_error (error,
GDK_GL_ERROR,
GDK_GL_ERROR_NOT_AVAILABLE,
"Failed to locate shared GL context for driver");
goto failure;
}
#ifdef G_ENABLE_DEBUG
if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), SHADERS))
debug_shaders = TRUE;
#endif
if (!(driver = gsk_ngl_driver_from_shared_context (shared_context, debug_shaders, error)))
goto failure;
self->command_queue = gsk_ngl_driver_create_command_queue (driver, context);
self->context = g_steal_pointer (&context);
self->driver = g_steal_pointer (&driver);
gsk_ngl_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, "GskNglRenderer realize", NULL);
return ret;
}
static void
gsk_ngl_renderer_unrealize (GskRenderer *renderer)
{
GskNglRenderer *self = (GskNglRenderer *)renderer;
g_assert (GSK_IS_NGL_RENDERER (renderer));
g_clear_object (&self->driver);
g_clear_object (&self->context);
g_clear_object (&self->command_queue);
}
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_ngl_renderer_render (GskRenderer *renderer,
GskRenderNode *root,
const cairo_region_t *update_area)
{
GskNglRenderer *self = (GskNglRenderer *)renderer;
cairo_region_t *render_region;
graphene_rect_t viewport;
GskNglRenderJob *job;
GdkSurface *surface;
float scale_factor;
g_assert (GSK_IS_NGL_RENDERER (renderer));
g_assert (root != NULL);
surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self->context));
scale_factor = gdk_surface_get_scale_factor (surface);
viewport.origin.x = 0;
viewport.origin.y = 0;
viewport.size.width = gdk_surface_get_width (surface) * scale_factor;
viewport.size.height = gdk_surface_get_height (surface) * scale_factor;
gdk_gl_context_make_current (self->context);
gdk_draw_context_begin_frame (GDK_DRAW_CONTEXT (self->context), update_area);
/* Must be called *AFTER* gdk_draw_context_begin_frame() */
render_region = get_render_region (surface, self->context);
gsk_ngl_driver_begin_frame (self->driver, self->command_queue);
job = gsk_ngl_render_job_new (self->driver, &viewport, scale_factor, render_region, 0);
#ifdef G_ENABLE_DEBUG
if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), FALLBACK))
gsk_ngl_render_job_set_debug_fallback (job, TRUE);
#endif
gsk_ngl_render_job_render (job, root);
gsk_ngl_driver_end_frame (self->driver);
gsk_ngl_render_job_free (job);
gdk_gl_context_make_current (self->context);
gdk_draw_context_end_frame (GDK_DRAW_CONTEXT (self->context));
gsk_ngl_driver_after_frame (self->driver);
cairo_region_destroy (render_region);
}
static GdkTexture *
gsk_ngl_renderer_render_texture (GskRenderer *renderer,
GskRenderNode *root,
const graphene_rect_t *viewport)
{
GskNglRenderer *self = (GskNglRenderer *)renderer;
GskNglRenderTarget *render_target;
GskNglRenderJob *job;
GdkTexture *texture = NULL;
guint texture_id;
int width;
int height;
g_assert (GSK_IS_NGL_RENDERER (renderer));
g_assert (root != NULL);
width = ceilf (viewport->size.width);
height = ceilf (viewport->size.height);
if (gsk_ngl_driver_create_render_target (self->driver,
width, height,
GL_NEAREST, GL_NEAREST,
&render_target))
{
gsk_ngl_driver_begin_frame (self->driver, self->command_queue);
job = gsk_ngl_render_job_new (self->driver, viewport, 1, NULL, render_target->framebuffer_id);
#ifdef G_ENABLE_DEBUG
if (GSK_RENDERER_DEBUG_CHECK (GSK_RENDERER (self), FALLBACK))
gsk_ngl_render_job_set_debug_fallback (job, TRUE);
#endif
gsk_ngl_render_job_render_flipped (job, root);
texture_id = gsk_ngl_driver_release_render_target (self->driver, render_target, FALSE);
texture = gsk_ngl_driver_create_gdk_texture (self->driver, texture_id);
gsk_ngl_driver_end_frame (self->driver);
gsk_ngl_render_job_free (job);
gsk_ngl_driver_after_frame (self->driver);
}
return g_steal_pointer (&texture);
}
static void
gsk_ngl_renderer_dispose (GObject *object)
{
#ifdef G_ENABLE_DEBUG
GskNglRenderer *self = (GskNglRenderer *)object;
g_assert (self->driver == NULL);
#endif
G_OBJECT_CLASS (gsk_ngl_renderer_parent_class)->dispose (object);
}
static void
gsk_ngl_renderer_class_init (GskNglRendererClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
object_class->dispose = gsk_ngl_renderer_dispose;
renderer_class->realize = gsk_ngl_renderer_realize;
renderer_class->unrealize = gsk_ngl_renderer_unrealize;
renderer_class->render = gsk_ngl_renderer_render;
renderer_class->render_texture = gsk_ngl_renderer_render_texture;
}
static void
gsk_ngl_renderer_init (GskNglRenderer *self)
{
}
gboolean
gsk_ngl_renderer_try_compile_gl_shader (GskNglRenderer *renderer,
GskGLShader *shader,
GError **error)
{
GskNglProgram *program;
g_return_val_if_fail (GSK_IS_NGL_RENDERER (renderer), FALSE);
g_return_val_if_fail (shader != NULL, FALSE);
program = gsk_ngl_driver_lookup_shader (renderer->driver, shader, error);
return program != NULL;
}

46
gsk/ngl/gsknglrenderer.h Normal file
View File

@ -0,0 +1,46 @@
/* gsknglrenderer.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
*/
#ifndef __GSK_NGL_RENDERER_H__
#define __GSK_NGL_RENDERER_H__
#include <gsk/gskrenderer.h>
G_BEGIN_DECLS
#define GSK_TYPE_NGL_RENDERER (gsk_ngl_renderer_get_type())
#define GSK_NGL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_NGL_RENDERER, GskNglRenderer))
#define GSK_IS_NGL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_NGL_RENDERER))
#define GSK_NGL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_NGL_RENDERER, GskNglRendererClass))
#define GSK_IS_NGL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_NGL_RENDERER))
#define GSK_NGL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_NGL_RENDERER, GskNglRendererClass))
typedef struct _GskNglRenderer GskNglRenderer;
typedef struct _GskNglRendererClass GskNglRendererClass;
GDK_AVAILABLE_IN_ALL
GType gsk_ngl_renderer_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GskRenderer *gsk_ngl_renderer_new (void);
G_END_DECLS
#endif /* __GSK_NGL_RENDERER__ */

View File

@ -0,0 +1,34 @@
/* gsknglrendererprivate.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
*/
#ifndef __GSK_NGL_RENDERER_PRIVATE_H__
#define __GSK_NGL_RENDERER_PRIVATE_H__
#include "gsknglrenderer.h"
G_BEGIN_DECLS
gboolean gsk_ngl_renderer_try_compile_gl_shader (GskNglRenderer *renderer,
GskGLShader *shader,
GError **error);
G_END_DECLS
#endif /* __GSK_NGL_RENDERER_PRIVATE_H__ */

3760
gsk/ngl/gsknglrenderjob.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
/* gsknglrenderjobprivate.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
*/
#ifndef __GSK_NGL_RENDER_JOB_H__
#define __GSK_NGL_RENDER_JOB_H__
#include "gskngltypesprivate.h"
GskNglRenderJob *gsk_ngl_render_job_new (GskNglDriver *driver,
const graphene_rect_t *viewport,
float scale_factor,
const cairo_region_t *region,
guint framebuffer);
void gsk_ngl_render_job_free (GskNglRenderJob *job);
void gsk_ngl_render_job_render (GskNglRenderJob *job,
GskRenderNode *root);
void gsk_ngl_render_job_render_flipped (GskNglRenderJob *job,
GskRenderNode *root);
void gsk_ngl_render_job_set_debug_fallback (GskNglRenderJob *job,
gboolean debug_fallback);
#endif /* __GSK_NGL_RENDER_JOB_H__ */

View File

@ -0,0 +1,228 @@
/* gsknglshadowlibrary.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 "gskngldriverprivate.h"
#include "gsknglshadowlibraryprivate.h"
#define MAX_UNUSED_FRAMES (16 * 5)
struct _GskNglShadowLibrary
{
GObject parent_instance;
GskNglDriver *driver;
GArray *shadows;
};
typedef struct _Shadow
{
GskRoundedRect outline;
float blur_radius;
guint texture_id;
gint64 last_used_in_frame;
} Shadow;
G_DEFINE_TYPE (GskNglShadowLibrary, gsk_ngl_shadow_library, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_DRIVER,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
GskNglShadowLibrary *
gsk_ngl_shadow_library_new (GskNglDriver *driver)
{
g_return_val_if_fail (GSK_IS_NGL_DRIVER (driver), NULL);
return g_object_new (GSK_TYPE_GL_SHADOW_LIBRARY,
"driver", driver,
NULL);
}
static void
gsk_ngl_shadow_library_dispose (GObject *object)
{
GskNglShadowLibrary *self = (GskNglShadowLibrary *)object;
for (guint i = 0; i < self->shadows->len; i++)
{
const Shadow *shadow = &g_array_index (self->shadows, Shadow, i);
gsk_ngl_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_ngl_shadow_library_parent_class)->dispose (object);
}
static void
gsk_ngl_shadow_library_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GskNglShadowLibrary *self = GSK_NGL_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_ngl_shadow_library_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GskNglShadowLibrary *self = GSK_NGL_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_ngl_shadow_library_class_init (GskNglShadowLibraryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gsk_ngl_shadow_library_dispose;
object_class->get_property = gsk_ngl_shadow_library_get_property;
object_class->set_property = gsk_ngl_shadow_library_set_property;
properties [PROP_DRIVER] =
g_param_spec_object ("driver",
"Driver",
"Driver",
GSK_TYPE_NGL_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_ngl_shadow_library_init (GskNglShadowLibrary *self)
{
self->shadows = g_array_new (FALSE, FALSE, sizeof (Shadow));
}
void
gsk_ngl_shadow_library_insert (GskNglShadowLibrary *self,
const GskRoundedRect *outline,
float blur_radius,
guint texture_id)
{
Shadow *shadow;
g_assert (GSK_IS_NGL_SHADOW_LIBRARY (self));
g_assert (outline != NULL);
g_assert (texture_id != 0);
gsk_ngl_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_ngl_shadow_library_lookup (GskNglShadowLibrary *self,
const GskRoundedRect *outline,
float blur_radius)
{
Shadow *ret = NULL;
g_assert (GSK_IS_NGL_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;
}
void
gsk_ngl_shadow_library_begin_frame (GskNglShadowLibrary *self)
{
gint64 watermark;
int i;
int p;
g_return_if_fail (GSK_IS_NGL_SHADOW_LIBRARY (self));
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_ngl_driver_release_texture_by_id (self->driver, shadow->texture_id);
g_array_remove_index_fast (self->shadows, i);
p--;
i--;
}
}
}

View File

@ -0,0 +1,44 @@
/* gsknglshadowlibraryprivate.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
*/
#ifndef __GSK_NGL_SHADOW_LIBRARY_PRIVATE_H__
#define __GSK_NGL_SHADOW_LIBRARY_PRIVATE_H__
#include "gskngltexturelibraryprivate.h"
G_BEGIN_DECLS
#define GSK_TYPE_GL_SHADOW_LIBRARY (gsk_ngl_shadow_library_get_type())
G_DECLARE_FINAL_TYPE (GskNglShadowLibrary, gsk_ngl_shadow_library, GSK, NGL_SHADOW_LIBRARY, GObject)
GskNglShadowLibrary *gsk_ngl_shadow_library_new (GskNglDriver *driver);
void gsk_ngl_shadow_library_begin_frame (GskNglShadowLibrary *self);
guint gsk_ngl_shadow_library_lookup (GskNglShadowLibrary *self,
const GskRoundedRect *outline,
float blur_radius);
void gsk_ngl_shadow_library_insert (GskNglShadowLibrary *self,
const GskRoundedRect *outline,
float blur_radius,
guint texture_id);
G_END_DECLS
#endif /* __GSK_NGL_SHADOW_LIBRARY_PRIVATE_H__ */

View File

@ -0,0 +1,315 @@
/* gskngltexturelibrary.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 "gsknglcommandqueueprivate.h"
#include "gskngldriverprivate.h"
#include "gskngltexturelibraryprivate.h"
G_DEFINE_ABSTRACT_TYPE (GskNglTextureLibrary, gsk_ngl_texture_library, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_DRIVER,
N_PROPS
};
static GParamSpec *properties [N_PROPS];
static void
gsk_ngl_texture_library_constructed (GObject *object)
{
G_OBJECT_CLASS (gsk_ngl_texture_library_parent_class)->constructed (object);
g_assert (GSK_NGL_TEXTURE_LIBRARY (object)->hash_table != NULL);
}
static void
gsk_ngl_texture_library_dispose (GObject *object)
{
GskNglTextureLibrary *self = (GskNglTextureLibrary *)object;
g_clear_object (&self->driver);
G_OBJECT_CLASS (gsk_ngl_texture_library_parent_class)->dispose (object);
}
static void
gsk_ngl_texture_library_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GskNglTextureLibrary *self = GSK_NGL_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_ngl_texture_library_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GskNglTextureLibrary *self = GSK_NGL_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_ngl_texture_library_class_init (GskNglTextureLibraryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = gsk_ngl_texture_library_constructed;
object_class->dispose = gsk_ngl_texture_library_dispose;
object_class->get_property = gsk_ngl_texture_library_get_property;
object_class->set_property = gsk_ngl_texture_library_set_property;
properties [PROP_DRIVER] =
g_param_spec_object ("driver",
"Driver",
"Driver",
GSK_TYPE_NGL_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_ngl_texture_library_init (GskNglTextureLibrary *self)
{
}
void
gsk_ngl_texture_library_set_funcs (GskNglTextureLibrary *self,
GHashFunc hash_func,
GEqualFunc equal_func,
GDestroyNotify key_destroy,
GDestroyNotify value_destroy)
{
g_return_if_fail (GSK_IS_NGL_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_ngl_texture_library_begin_frame (GskNglTextureLibrary *self)
{
g_return_if_fail (GSK_IS_NGL_TEXTURE_LIBRARY (self));
if (GSK_NGL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame)
GSK_NGL_TEXTURE_LIBRARY_GET_CLASS (self)->begin_frame (self);
}
void
gsk_ngl_texture_library_end_frame (GskNglTextureLibrary *self)
{
g_return_if_fail (GSK_IS_NGL_TEXTURE_LIBRARY (self));
if (GSK_NGL_TEXTURE_LIBRARY_GET_CLASS (self)->end_frame)
GSK_NGL_TEXTURE_LIBRARY_GET_CLASS (self)->end_frame (self);
}
static GskNglTexture *
gsk_ngl_texture_library_pack_one (GskNglTextureLibrary *self,
guint width,
guint height)
{
GskNglTexture *texture;
g_assert (GSK_IS_NGL_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_ngl_driver_create_texture (self->driver, width, height, GL_LINEAR, GL_LINEAR);
texture->permanent = TRUE;
return texture;
}
static inline gboolean
gsk_ngl_texture_atlas_pack (GskNglTextureAtlas *self,
int width,
int height,
int *out_x,
int *out_y)
{
stbrp_rect rect;
rect.w = width;
rect.h = height;
stbrp_pack_rects (&self->context, &rect, 1);
if (rect.was_packed)
{
*out_x = rect.x;
*out_y = rect.y;
}
return rect.was_packed;
}
static void
gsk_ngl_texture_atlases_pack (GskNglDriver *driver,
int width,
int height,
GskNglTextureAtlas **out_atlas,
int *out_x,
int *out_y)
{
GskNglTextureAtlas *atlas = NULL;
int x, y;
for (guint i = 0; i < driver->atlases->len; i++)
{
atlas = g_ptr_array_index (driver->atlases, i);
if (gsk_ngl_texture_atlas_pack (atlas, width, height, &x, &y))
break;
atlas = NULL;
}
if (atlas == NULL)
{
/* No atlas has enough space, so create a new one... */
atlas = gsk_ngl_driver_create_atlas (driver);
/* Pack it onto that one, which surely has enough space... */
if (!gsk_ngl_texture_atlas_pack (atlas, width, height, &x, &y))
g_assert_not_reached ();
}
*out_atlas = atlas;
*out_x = x;
*out_y = y;
}
gpointer
gsk_ngl_texture_library_pack (GskNglTextureLibrary *self,
gpointer key,
gsize valuelen,
guint width,
guint height,
int padding,
guint *out_packed_x,
guint *out_packed_y)
{
GskNglTextureAtlasEntry *entry;
GskNglTextureAtlas *atlas = NULL;
g_assert (GSK_IS_NGL_TEXTURE_LIBRARY (self));
g_assert (key != NULL);
g_assert (valuelen > sizeof (GskNglTextureAtlasEntry));
g_assert (out_packed_x != NULL);
g_assert (out_packed_y != NULL);
entry = g_slice_alloc0 (valuelen);
entry->n_pixels = width * height;
entry->accessed = 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 (width <= self->max_entry_size && height <= self->max_entry_size)
{
int packed_x;
int packed_y;
gsk_ngl_texture_atlases_pack (self->driver,
padding + width + padding,
padding + height + padding,
&atlas,
&packed_x,
&packed_y);
entry->atlas = atlas;
entry->is_atlased = TRUE;
entry->area.x = (float)(packed_x + padding) / atlas->width;
entry->area.y = (float)(packed_y + padding) / atlas->height;
entry->area.x2 = entry->area.x + (float)width / atlas->width;
entry->area.y2 = entry->area.y + (float)height / atlas->height;
*out_packed_x = packed_x;
*out_packed_y = packed_y;
}
else
{
GskNglTexture *texture = gsk_ngl_texture_library_pack_one (self,
padding + width + padding,
padding + height + padding);
entry->texture = texture;
entry->is_atlased = FALSE;
entry->accessed = TRUE;
entry->area.x = 0.0f;
entry->area.y = 0.0f;
entry->area.x2 = 1.0f;
entry->area.y2 = 1.0f;
*out_packed_x = padding;
*out_packed_y = padding;
}
g_hash_table_insert (self->hash_table, key, entry);
return entry;
}

View File

@ -0,0 +1,202 @@
/* gskngltexturelibraryprivate.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
*/
#ifndef __GSK_NGL_TEXTURE_LIBRARY_PRIVATE_H__
#define __GSK_NGL_TEXTURE_LIBRARY_PRIVATE_H__
#include "gskngltypesprivate.h"
#include "gskngltexturepoolprivate.h"
#include "../gl/stb_rect_pack.h"
G_BEGIN_DECLS
#define GSK_TYPE_GL_TEXTURE_LIBRARY (gsk_ngl_texture_library_get_type ())
#define GSK_NGL_TEXTURE_LIBRARY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_GL_TEXTURE_LIBRARY, GskNglTextureLibrary))
#define GSK_IS_NGL_TEXTURE_LIBRARY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_GL_TEXTURE_LIBRARY))
#define GSK_NGL_TEXTURE_LIBRARY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_GL_TEXTURE_LIBRARY, GskNglTextureLibraryClass))
#define GSK_IS_NGL_TEXTURE_LIBRARY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_GL_TEXTURE_LIBRARY))
#define GSK_NGL_TEXTURE_LIBRARY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_GL_TEXTURE_LIBRARY, GskNglTextureLibraryClass))
typedef struct _GskNglTextureAtlas
{
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;
void *user_data;
} GskNglTextureAtlas;
typedef struct _GskNglTextureAtlasEntry
{
/* 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 {
GskNglTextureAtlas *atlas;
GskNglTexture *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;
/* Suffix data that is per-library specific. gpointer used to
* guarantee the alignment for the entries using this.
*/
gpointer data[0];
} GskNglTextureAtlasEntry;
typedef struct _GskNglTextureLibrary
{
GObject parent_instance;
GskNglDriver *driver;
GHashTable *hash_table;
guint max_entry_size;
} GskNglTextureLibrary;
typedef struct _GskNglTextureLibraryClass
{
GObjectClass parent_class;
void (*begin_frame) (GskNglTextureLibrary *library);
void (*end_frame) (GskNglTextureLibrary *library);
} GskNglTextureLibraryClass;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GskNglTextureLibrary, g_object_unref)
GType gsk_ngl_texture_library_get_type (void) G_GNUC_CONST;
void gsk_ngl_texture_library_set_funcs (GskNglTextureLibrary *self,
GHashFunc hash_func,
GEqualFunc equal_func,
GDestroyNotify key_destroy,
GDestroyNotify value_destroy);
void gsk_ngl_texture_library_begin_frame (GskNglTextureLibrary *self);
void gsk_ngl_texture_library_end_frame (GskNglTextureLibrary *self);
gpointer gsk_ngl_texture_library_pack (GskNglTextureLibrary *self,
gpointer key,
gsize valuelen,
guint width,
guint height,
int padding,
guint *out_packed_x,
guint *out_packed_y);
static inline void
gsk_ngl_texture_atlas_mark_unused (GskNglTextureAtlas *self,
int n_pixels)
{
self->unused_pixels += n_pixels;
}
static inline void
gsk_ngl_texture_atlas_mark_used (GskNglTextureAtlas *self,
int n_pixels)
{
self->unused_pixels -= n_pixels;
}
static inline gboolean
gsk_ngl_texture_library_lookup (GskNglTextureLibrary *self,
gconstpointer key,
GskNglTextureAtlasEntry **out_entry)
{
GskNglTextureAtlasEntry *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)
{
if (!entry->used && entry->is_atlased)
{
g_assert (entry->atlas != NULL);
gsk_ngl_texture_atlas_mark_used (entry->atlas, entry->n_pixels);
entry->used = TRUE;
}
entry->accessed = TRUE;
*out_entry = entry;
return TRUE;
}
return FALSE;
}
static inline guint
GSK_NGL_TEXTURE_ATLAS_ENTRY_TEXTURE (gconstpointer d)
{
const GskNglTextureAtlasEntry *e = d;
return e->is_atlased ? e->atlas->texture_id
: e->texture ? e->texture->texture_id : 0;
}
static inline double
gsk_ngl_texture_atlas_get_unused_ratio (const GskNglTextureAtlas *self)
{
if (self->unused_pixels > 0)
return (double)(self->unused_pixels) / (double)(self->width * self->height);
return 0.0;
}
static inline gboolean
gsk_ngl_texture_library_can_cache (GskNglTextureLibrary *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
#endif /* __GSK_NGL_TEXTURE_LIBRARY_PRIVATE_H__ */

188
gsk/ngl/gskngltexturepool.c Normal file
View File

@ -0,0 +1,188 @@
/* gskngltexturepool.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 "gskngltexturepoolprivate.h"
#include "ninesliceprivate.h"
void
gsk_ngl_texture_free (GskNglTexture *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_slice_free (GskNglTexture, texture);
}
}
void
gsk_ngl_texture_pool_init (GskNglTexturePool *self)
{
g_queue_init (&self->queue);
}
void
gsk_ngl_texture_pool_clear (GskNglTexturePool *self)
{
guint *free_me = NULL;
guint *texture_ids;
guint i = 0;
if G_LIKELY (self->queue.length <= 1024)
texture_ids = g_newa (guint, self->queue.length);
else
texture_ids = free_me = g_new (guint, self->queue.length);
while (self->queue.length > 0)
{
GskNglTexture *head = g_queue_peek_head (&self->queue);
g_queue_unlink (&self->queue, &head->link);
texture_ids[i++] = head->texture_id;
head->texture_id = 0;
gsk_ngl_texture_free (head);
}
g_assert (self->queue.length == 0);
if (i > 0)
glDeleteTextures (i, texture_ids);
g_free (free_me);
}
void
gsk_ngl_texture_pool_put (GskNglTexturePool *self,
GskNglTexture *texture)
{
g_assert (self != NULL);
g_assert (texture != NULL);
g_assert (texture->user == NULL);
g_assert (texture->link.prev == NULL);
g_assert (texture->link.next == NULL);
g_assert (texture->link.data == texture);
if (texture->permanent)
gsk_ngl_texture_free (texture);
else
g_queue_push_tail_link (&self->queue, &texture->link);
}
GskNglTexture *
gsk_ngl_texture_pool_get (GskNglTexturePool *self,
int width,
int height,
int min_filter,
int mag_filter)
{
GskNglTexture *texture;
g_assert (self != NULL);
texture = g_slice_new0 (GskNglTexture);
texture->link.data = texture;
texture->min_filter = min_filter;
texture->mag_filter = mag_filter;
glGenTextures (1, &texture->texture_id);
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, texture->texture_id);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
else
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glBindTexture (GL_TEXTURE_2D, 0);
return texture;
}
GskNglTexture *
gsk_ngl_texture_new (guint texture_id,
int width,
int height,
int min_filter,
int mag_filter,
gint64 frame_id)
{
GskNglTexture *texture;
texture = g_slice_new0 (GskNglTexture);
texture->texture_id = texture_id;
texture->link.data = texture;
texture->min_filter = min_filter;
texture->mag_filter = mag_filter;
texture->width = width;
texture->height = height;
texture->last_used_in_frame = frame_id;
return texture;
}
const GskNglTextureNineSlice *
gsk_ngl_texture_get_nine_slice (GskNglTexture *texture,
const GskRoundedRect *outline,
float extra_pixels)
{
g_assert (texture != NULL);
g_assert (outline != NULL);
if G_UNLIKELY (texture->nine_slice == NULL)
{
texture->nine_slice = g_new0 (GskNglTextureNineSlice, 9);
nine_slice_rounded_rect (texture->nine_slice, outline);
nine_slice_grow (texture->nine_slice, extra_pixels);
nine_slice_to_texture_coords (texture->nine_slice, texture->width, texture->height);
}
return texture->nine_slice;
}

View File

@ -0,0 +1,102 @@
/* gskngltexturepoolprivate.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
*/
#ifndef _GSK_NGL_TEXTURE_POOL_PRIVATE_H__
#define _GSK_NGL_TEXTURE_POOL_PRIVATE_H__
#include "gskngltypesprivate.h"
G_BEGIN_DECLS
typedef struct _GskNglTexturePool
{
GQueue queue;
} GskNglTexturePool;
struct _GskNglTextureSlice
{
cairo_rectangle_int_t rect;
guint texture_id;
};
struct _GskNglTextureNineSlice
{
cairo_rectangle_int_t rect;
struct {
float x;
float y;
float x2;
float y2;
} area;
};
struct _GskNglTexture
{
/* 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 sliced textures */
GskNglTextureSlice *slices;
guint n_slices;
/* Only used by nine-slice textures */
GskNglTextureNineSlice *nine_slice;
/* The actual GL texture identifier in some shared context */
guint texture_id;
int width;
int height;
int min_filter;
int mag_filter;
/* Set when used by an atlas so we don't drop the texture */
guint permanent : 1;
};
void gsk_ngl_texture_pool_init (GskNglTexturePool *self);
void gsk_ngl_texture_pool_clear (GskNglTexturePool *self);
GskNglTexture *gsk_ngl_texture_pool_get (GskNglTexturePool *self,
int width,
int height,
int min_filter,
int mag_filter);
void gsk_ngl_texture_pool_put (GskNglTexturePool *self,
GskNglTexture *texture);
GskNglTexture *gsk_ngl_texture_new (guint texture_id,
int width,
int height,
int min_filter,
int mag_filter,
gint64 frame_id);
const GskNglTextureNineSlice *gsk_ngl_texture_get_nine_slice (GskNglTexture *texture,
const GskRoundedRect *outline,
float extra_pixels);
void gsk_ngl_texture_free (GskNglTexture *texture);
G_END_DECLS
#endif /* _GSK_NGL_TEXTURE_POOL_PRIVATE_H__ */

View File

@ -0,0 +1,62 @@
/* gskngltypesprivate.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
*/
#ifndef __GSK_NGL_TYPES_PRIVATE_H__
#define __GSK_NGL_TYPES_PRIVATE_H__
#include <epoxy/gl.h>
#include <graphene.h>
#include <gdk/gdk.h>
#include <gsk/gsk.h>
G_BEGIN_DECLS
#define GSK_NGL_N_VERTICES 6
typedef struct _GskNglAttachmentState GskNglAttachmentState;
typedef struct _GskNglBuffer GskNglBuffer;
typedef struct _GskNglCommandQueue GskNglCommandQueue;
typedef struct _GskNglCompiler GskNglCompiler;
typedef struct _GskNglDrawVertex GskNglDrawVertex;
typedef struct _GskNglRenderTarget GskNglRenderTarget;
typedef struct _GskNglGlyphLibrary GskNglGlyphLibrary;
typedef struct _GskNglIconLibrary GskNglIconLibrary;
typedef struct _GskNglProgram GskNglProgram;
typedef struct _GskNglRenderJob GskNglRenderJob;
typedef struct _GskNglShadowLibrary GskNglShadowLibrary;
typedef struct _GskNglTexture GskNglTexture;
typedef struct _GskNglTextureSlice GskNglTextureSlice;
typedef struct _GskNglTextureAtlas GskNglTextureAtlas;
typedef struct _GskNglTextureLibrary GskNglTextureLibrary;
typedef struct _GskNglTextureNineSlice GskNglTextureNineSlice;
typedef struct _GskNglUniformInfo GskNglUniformInfo;
typedef struct _GskNglUniformProgram GskNglUniformProgram;
typedef struct _GskNglUniformState GskNglUniformState;
typedef struct _GskNglDriver GskNglDriver;
struct _GskNglDrawVertex
{
float position[2];
float uv[2];
};
G_END_DECLS
#endif /* __GSK_NGL_TYPES_PRIVATE_H__ */

View File

@ -0,0 +1,270 @@
/* gskngluniformstate.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 "gskngluniformstateprivate.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,
};
GskNglUniformState *
gsk_ngl_uniform_state_new (void)
{
GskNglUniformState *state;
state = g_atomic_rc_box_new0 (GskNglUniformState);
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);
return g_steal_pointer (&state);
}
GskNglUniformState *
gsk_ngl_uniform_state_ref (GskNglUniformState *state)
{
return g_atomic_rc_box_acquire (state);
}
static void
gsk_ngl_uniform_state_finalize (gpointer data)
{
GskNglUniformState *state = data;
g_clear_pointer (&state->programs, g_hash_table_unref);
g_clear_pointer (&state->values_buf, g_free);
}
void
gsk_ngl_uniform_state_unref (GskNglUniformState *state)
{
g_atomic_rc_box_release_full (state, gsk_ngl_uniform_state_finalize);
}
gpointer
gsk_ngl_uniform_state_init_value (GskNglUniformState *state,
GskNglUniformProgram *program,
GskNglUniformFormat format,
guint array_count,
guint location,
GskNglUniformInfoElement **infoptr)
{
GskNglUniformInfoElement *info;
guint offset;
g_assert (state != NULL);
g_assert (array_count < 32);
g_assert ((int)format >= 0 && format < GSK_NGL_UNIFORM_FORMAT_LAST);
g_assert (format > 0);
g_assert (program != NULL);
g_assert (program->sparse != NULL);
g_assert (program->n_sparse <= program->n_uniforms);
g_assert (location < GL_MAX_UNIFORM_LOCATIONS || location == (guint)-1);
g_assert (location < program->n_uniforms);
/* Handle unused uniforms gracefully */
if G_UNLIKELY (location == (guint)-1)
return NULL;
info = &program->uniforms[location];
if G_LIKELY (format == info->info.format)
{
if G_LIKELY (array_count <= info->info.array_count)
{
*infoptr = info;
return GSK_NGL_UNIFORM_VALUE (state->values_buf, info->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 (info->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, location, info->info.format, format,
info->info.array_count, array_count);
*infoptr = NULL;
return NULL;
}
setup_info:
gsk_ngl_uniform_state_realloc (state,
uniform_sizes[format] * MAX (1, array_count),
&offset);
/* we have 21 bits for offset */
g_assert (offset < (1 << GSK_NGL_UNIFORM_OFFSET_BITS));
/* We could once again be setting up this info if the array size grew.
* So make sure that we have space in our space array for the value.
*/
g_assert (info->info.format != 0 || program->n_sparse < program->n_uniforms);
if (info->info.format == 0)
program->sparse[program->n_sparse++] = location;
info->info.format = format;
info->info.offset = offset;
info->info.array_count = array_count;
info->info.initial = TRUE;
info->stamp = 0;
*infoptr = info;
return GSK_NGL_UNIFORM_VALUE (state->values_buf, info->info.offset);
}
void
gsk_ngl_uniform_state_end_frame (GskNglUniformState *state)
{
GHashTableIter iter;
GskNglUniformProgram *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_sparse; j++)
{
guint location = program->sparse[j];
GskNglUniformInfoElement *info = &program->uniforms[location];
guint size;
g_assert (info->info.format > 0);
/* Calculate how much size is needed for the uniform, including arrays */
size = uniform_sizes[info->info.format] * MAX (1, info->info.array_count);
/* Adjust alignment for value */
allocator += gsk_ngl_uniform_state_align (allocator, size);
/* Offset is in slots of 4 bytes */
info->info.offset = allocator / 4;
info->info.initial = TRUE;
info->stamp = 0;
/* Now advance for this items data */
allocator += size;
}
}
state->values_pos = allocator;
g_assert (allocator <= state->values_len);
}
gsize
gsk_ngl_uniform_format_size (GskNglUniformFormat format)
{
g_assert (format > 0);
g_assert (format < GSK_NGL_UNIFORM_FORMAT_LAST);
return uniform_sizes[format];
}
GskNglUniformProgram *
gsk_ngl_uniform_state_get_program (GskNglUniformState *state,
guint program,
guint n_uniforms)
{
GskNglUniformProgram *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);
ret = g_hash_table_lookup (state->programs, GUINT_TO_POINTER (program));
if (ret == NULL)
{
gsize uniform_size = n_uniforms * sizeof (GskNglUniformInfoElement);
gsize sparse_size = n_uniforms * sizeof (guint);
gsize size = sizeof (GskNglUniformProgram) + uniform_size + sparse_size;
/* Must be multiple of 4 for space pointer to align */
G_STATIC_ASSERT (sizeof (GskNglUniformInfoElement) == 8);
ret = g_malloc0 (size);
ret->program_id = program;
ret->n_uniforms = n_uniforms;
ret->n_sparse = 0;
ret->sparse = (guint *)&ret->uniforms[n_uniforms];
for (guint i = 0; i < n_uniforms; i++)
ret->uniforms[i].info.initial = TRUE;
g_hash_table_insert (state->programs, GUINT_TO_POINTER (program), ret);
}
return ret;
}

View File

@ -0,0 +1,685 @@
/* gskngluniformstateprivate.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
*/
#ifndef GSK_NGL_UNIFORM_STATE_PRIVATE_H
#define GSK_NGL_UNIFORM_STATE_PRIVATE_H
#include "gskngltypesprivate.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_NGL_UNIFORM_ARRAY_BITS 5
#define GSK_NGL_UNIFORM_FORMAT_BITS 5
#define GSK_NGL_UNIFORM_OFFSET_BITS 21
typedef struct _GskNglUniformInfo
{
guint initial : 1;
guint format : GSK_NGL_UNIFORM_FORMAT_BITS;
guint array_count : GSK_NGL_UNIFORM_ARRAY_BITS;
guint offset : GSK_NGL_UNIFORM_OFFSET_BITS;
} GskNglUniformInfo;
G_STATIC_ASSERT (sizeof (GskNglUniformInfo) == 4);
typedef struct _GskNglUniformInfoElement
{
GskNglUniformInfo info;
guint stamp;
} GskNglUniformInfoElement;
G_STATIC_ASSERT (sizeof (GskNglUniformInfoElement) == 8);
typedef struct _GskNglUniformProgram
{
guint program_id;
guint n_uniforms : 12;
guint has_attachments : 1;
/* To avoid walking our 1:1 array of location->uniform slots, we have
* a sparse index that allows us to skip the empty zones.
*/
guint *sparse;
guint n_sparse;
/* Uniforms are provided inline at the end of structure to avoid
* an extra dereference.
*/
GskNglUniformInfoElement uniforms[0];
} GskNglUniformProgram;
typedef struct _GskNglUniformState
{
GHashTable *programs;
guint8 *values_buf;
guint values_pos;
guint values_len;
} GskNglUniformState;
/**
* GskNglUniformStateCallback:
* @info: a pointer to the information about the uniform
* @location: the location of the uniform within the GPU program.
* @user_data: closure data for the callback
*
* This callback can be used to snapshot state of a program which
* is useful when batching commands so that the state may be compared
* with future evocations of the program.
*/
typedef void (*GskNglUniformStateCallback) (const GskNglUniformInfo *info,
guint location,
gpointer user_data);
typedef enum _GskNglUniformKind
{
GSK_NGL_UNIFORM_FORMAT_1F = 1,
GSK_NGL_UNIFORM_FORMAT_2F,
GSK_NGL_UNIFORM_FORMAT_3F,
GSK_NGL_UNIFORM_FORMAT_4F,
GSK_NGL_UNIFORM_FORMAT_1FV,
GSK_NGL_UNIFORM_FORMAT_2FV,
GSK_NGL_UNIFORM_FORMAT_3FV,
GSK_NGL_UNIFORM_FORMAT_4FV,
GSK_NGL_UNIFORM_FORMAT_1I,
GSK_NGL_UNIFORM_FORMAT_2I,
GSK_NGL_UNIFORM_FORMAT_3I,
GSK_NGL_UNIFORM_FORMAT_4I,
GSK_NGL_UNIFORM_FORMAT_1UI,
GSK_NGL_UNIFORM_FORMAT_TEXTURE,
GSK_NGL_UNIFORM_FORMAT_MATRIX,
GSK_NGL_UNIFORM_FORMAT_ROUNDED_RECT,
GSK_NGL_UNIFORM_FORMAT_COLOR,
GSK_NGL_UNIFORM_FORMAT_LAST
} GskNglUniformFormat;
G_STATIC_ASSERT (GSK_NGL_UNIFORM_FORMAT_LAST < (1 << GSK_NGL_UNIFORM_FORMAT_BITS));
GskNglUniformState *gsk_ngl_uniform_state_new (void);
GskNglUniformState *gsk_ngl_uniform_state_ref (GskNglUniformState *state);
void gsk_ngl_uniform_state_unref (GskNglUniformState *state);
GskNglUniformProgram *gsk_ngl_uniform_state_get_program (GskNglUniformState *state,
guint program,
guint n_uniforms);
void gsk_ngl_uniform_state_end_frame (GskNglUniformState *state);
gsize gsk_ngl_uniform_format_size (GskNglUniformFormat format);
gpointer gsk_ngl_uniform_state_init_value (GskNglUniformState *state,
GskNglUniformProgram *program,
GskNglUniformFormat format,
guint array_count,
guint location,
GskNglUniformInfoElement **infoptr);
#define GSK_NGL_UNIFORM_VALUE(base, offset) ((gpointer)((base) + ((offset) * 4)))
#define gsk_ngl_uniform_state_get_uniform_data(state,offset) GSK_NGL_UNIFORM_VALUE((state)->values_buf, offset)
#define gsk_ngl_uniform_state_snapshot(state, program_info, callback, user_data) \
G_STMT_START { \
for (guint z = 0; z < program_info->n_sparse; z++) \
{ \
guint location = program_info->sparse[z]; \
GskNglUniformInfoElement *info = &program_info->uniforms[location]; \
\
g_assert (location < GL_MAX_UNIFORM_LOCATIONS); \
g_assert (location < program_info->n_uniforms); \
\
if (info->info.format > 0) \
callback (&info->info, location, user_data); \
} \
} G_STMT_END
static inline gpointer
gsk_ngl_uniform_state_get_value (GskNglUniformState *state,
GskNglUniformProgram *program,
GskNglUniformFormat format,
guint array_count,
guint location,
guint stamp,
GskNglUniformInfoElement **infoptr)
{
GskNglUniformInfoElement *info;
if (location == (guint)-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.
*/
info = &program->uniforms[location];
if (stamp != 0 && stamp == info->stamp)
return NULL;
if G_LIKELY (format == info->info.format && array_count <= info->info.array_count)
{
*infoptr = info;
return GSK_NGL_UNIFORM_VALUE (state->values_buf, info->info.offset);
}
return gsk_ngl_uniform_state_init_value (state, program, format, array_count, location, infoptr);
}
static inline guint
gsk_ngl_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_ngl_uniform_state_realloc (GskNglUniformState *state,
guint size,
guint *offset)
{
guint padding = gsk_ngl_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_NGL_UNIFORM_VALUE (state->values_buf, *offset);
}
#define GSK_NGL_UNIFORM_STATE_REPLACE(info, u, type, count) \
G_STMT_START { \
if ((info)->info.initial && count == (info)->info.array_count) \
{ \
u = GSK_NGL_UNIFORM_VALUE (state->values_buf, (info)->info.offset); \
} \
else \
{ \
guint offset; \
u = gsk_ngl_uniform_state_realloc (state, sizeof(type) * MAX (1, count), &offset); \
g_assert (offset < (1 << GSK_NGL_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_ngl_uniform_info_changed (GskNglUniformInfoElement *info,
guint location,
guint stamp)
{
info->stamp = stamp;
info->info.initial = FALSE;
}
static inline void
gsk_ngl_uniform_state_set1f (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
float value0)
{
Uniform1f *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != 0);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_1F, 1, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, Uniform1f , 1);
u->v0 = value0;
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set2f (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
float value0,
float value1)
{
Uniform2f *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_2F, 1, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, Uniform2f, 1);
u->v0 = value0;
u->v1 = value1;
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set3f (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
float value0,
float value1,
float value2)
{
Uniform3f *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_3F, 1, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, Uniform3f, 1);
u->v0 = value0;
u->v1 = value1;
u->v2 = value2;
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set4f (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
float value0,
float value1,
float value2,
float value3)
{
Uniform4f *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_4F, 1, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, Uniform4f, 1);
u->v0 = value0;
u->v1 = value1;
u->v2 = value2;
u->v3 = value3;
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set1ui (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
guint value0)
{
Uniform1ui *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_1UI, 1, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, Uniform1ui, 1);
u->v0 = value0;
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set1i (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
int value0)
{
Uniform1i *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_1I, 1, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, Uniform1i, 1);
u->v0 = value0;
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set2i (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
int value0,
int value1)
{
Uniform2i *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_2I, 1, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, Uniform2i, 1);
u->v0 = value0;
u->v1 = value1;
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set3i (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
int value0,
int value1,
int value2)
{
Uniform3i *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_3I, 1, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, Uniform3i, 1);
u->v0 = value0;
u->v1 = value1;
u->v2 = value2;
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set4i (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
int value0,
int value1,
int value2,
int value3)
{
Uniform4i *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_4I, 1, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, Uniform4i, 1);
u->v0 = value0;
u->v1 = value1;
u->v2 = value2;
u->v3 = value3;
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set_rounded_rect (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
const GskRoundedRect *rounded_rect)
{
GskRoundedRect *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
g_assert (rounded_rect != NULL);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_ROUNDED_RECT, 1, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, GskRoundedRect, 1);
memcpy (u, rounded_rect, sizeof *rounded_rect);
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set_matrix (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
const graphene_matrix_t *matrix)
{
graphene_matrix_t *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
g_assert (matrix != NULL);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_MATRIX, 1, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, graphene_matrix_t, 1);
memcpy (u, matrix, sizeof *matrix);
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
/**
* gsk_ngl_uniform_state_set_texture:
* @state: a #GskNglUniformState
* @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_ngl_uniform_state_set_texture (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
guint texture_slot)
{
GskNglUniformInfoElement *info;
guint *u;
g_assert (texture_slot >= GL_TEXTURE0);
g_assert (texture_slot < GL_TEXTURE16);
texture_slot -= GL_TEXTURE0;
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_TEXTURE, 1, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, guint, 1);
*u = texture_slot;
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
/**
* gsk_ngl_uniform_state_set_color:
* @state: a #GskNglUniformState
* @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_ngl_uniform_state_set_color (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
const GdkRGBA *color)
{
static const GdkRGBA transparent = {0};
GskNglUniformInfoElement *info;
GdkRGBA *u;
g_assert (state != NULL);
g_assert (program != NULL);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_COLOR, 1, location, stamp, &info)))
{
if (color == NULL)
color = &transparent;
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, GdkRGBA, 1);
memcpy (u, color, sizeof *color);
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set1fv (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
guint count,
const float *value)
{
Uniform1f *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
g_assert (count > 0);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_1FV, count, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, Uniform1f, count);
memcpy (u, value, sizeof (Uniform1f) * count);
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set2fv (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
guint count,
const float *value)
{
Uniform2f *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
g_assert (count > 0);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_2FV, count, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, Uniform2f, count);
memcpy (u, value, sizeof (Uniform2f) * count);
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set3fv (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
guint count,
const float *value)
{
Uniform3f *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
g_assert (count > 0);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_3FV, count, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, Uniform3f, count);
memcpy (u, value, sizeof (Uniform3f) * count);
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
static inline void
gsk_ngl_uniform_state_set4fv (GskNglUniformState *state,
GskNglUniformProgram *program,
guint location,
guint stamp,
guint count,
const float *value)
{
Uniform4f *u;
GskNglUniformInfoElement *info;
g_assert (state != NULL);
g_assert (program != NULL);
g_assert (count > 0);
if ((u = gsk_ngl_uniform_state_get_value (state, program, GSK_NGL_UNIFORM_FORMAT_4FV, count, location, stamp, &info)))
{
GSK_NGL_UNIFORM_STATE_REPLACE (info, u, Uniform4f, count);
memcpy (u, value, sizeof (Uniform4f) * count);
gsk_ngl_uniform_info_changed (info, location, stamp);
}
}
G_END_DECLS
#endif /* GSK_NGL_UNIFORM_STATE_PRIVATE_H */

77
gsk/ngl/inlinearray.h Normal file
View File

@ -0,0 +1,77 @@
#ifndef __INLINE_ARRAY_H__
#define __INLINE_ARRAY_H__
#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]; \
}
#endif /* __INLINE_ARRAY_H__ */

309
gsk/ngl/ninesliceprivate.h Normal file
View File

@ -0,0 +1,309 @@
/* 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
*/
#ifndef __NINE_SLICE_PRIVATE_H__
#define __NINE_SLICE_PRIVATE_H__
#include "gskngltexturepoolprivate.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 GskNglTextureNineSlice *slice)
{
return slice->rect.width > 0 && slice->rect.height > 0;
}
static inline void
nine_slice_rounded_rect (GskNglTextureNineSlice *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 (GskNglTextureNineSlice *slices,
int texture_width,
int texture_height)
{
float fw = texture_width;
float fh = texture_height;
for (guint i = 0; i < 9; i++)
{
GskNglTextureNineSlice *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 (GskNglTextureNineSlice *slices,
int amount)
{
if (amount == 0)
return;
/* top left */
slices[0].rect.x -= amount;
slices[0].rect.y -= amount;
if (amount > slices[0].rect.width)
slices[0].rect.width += amount * 2;
else
slices[0].rect.width += amount;
if (amount > slices[0].rect.height)
slices[0].rect.height += amount * 2;
else
slices[0].rect.height += amount;
/* Top center */
slices[1].rect.y -= amount;
if (amount > slices[1].rect.height)
slices[1].rect.height += amount * 2;
else
slices[1].rect.height += amount;
/* top right */
slices[2].rect.y -= amount;
if (amount > slices[2].rect.width)
{
slices[2].rect.x -= amount;
slices[2].rect.width += amount * 2;
}
else
{
slices[2].rect.width += amount;
}
if (amount > slices[2].rect.height)
slices[2].rect.height += amount * 2;
else
slices[2].rect.height += amount;
slices[3].rect.x -= amount;
if (amount > slices[3].rect.width)
slices[3].rect.width += amount * 2;
else
slices[3].rect.width += amount;
/* Leave center alone */
if (amount > slices[5].rect.width)
{
slices[5].rect.x -= amount;
slices[5].rect.width += amount * 2;
}
else
{
slices[5].rect.width += amount;
}
/* Bottom left */
slices[6].rect.x -= amount;
if (amount > slices[6].rect.width)
{
slices[6].rect.width += amount * 2;
}
else
{
slices[6].rect.width += amount;
}
if (amount > slices[6].rect.height)
{
slices[6].rect.y -= amount;
slices[6].rect.height += amount * 2;
}
else
{
slices[6].rect.height += amount;
}
/* Bottom center */
if (amount > slices[7].rect.height)
{
slices[7].rect.y -= amount;
slices[7].rect.height += amount * 2;
}
else
{
slices[7].rect.height += amount;
}
if (amount > slices[8].rect.width)
{
slices[8].rect.x -= amount;
slices[8].rect.width += amount * 2;
}
else
{
slices[8].rect.width += amount;
}
if (amount > slices[8].rect.height)
{
slices[8].rect.y -= amount;
slices[8].rect.height += amount * 2;
}
else
{
slices[8].rect.height += amount;
}
#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
#endif /* __NINE_SLICE_PRIVATE_H__ */

View File

@ -41,6 +41,7 @@
#define GTK_COMPILATION #define GTK_COMPILATION
#include <gsk/gl/gskglrenderer.h> #include <gsk/gl/gskglrenderer.h>
#include <gsk/ngl/gsknglrenderer.h>
#ifdef GDK_WINDOWING_BROADWAY #ifdef GDK_WINDOWING_BROADWAY
#include <gsk/broadway/gskbroadwayrenderer.h> #include <gsk/broadway/gskbroadwayrenderer.h>

View File

@ -147,6 +147,8 @@ init_version (GtkInspectorGeneral *gen)
renderer = "Vulkan"; renderer = "Vulkan";
else if (strcmp (G_OBJECT_TYPE_NAME (gsk_renderer), "GskGLRenderer") == 0) else if (strcmp (G_OBJECT_TYPE_NAME (gsk_renderer), "GskGLRenderer") == 0)
renderer = "GL"; renderer = "GL";
else if (strcmp (G_OBJECT_TYPE_NAME (gsk_renderer), "GskNglRenderer") == 0)
renderer = "NGL";
else if (strcmp (G_OBJECT_TYPE_NAME (gsk_renderer), "GskCairoRenderer") == 0) else if (strcmp (G_OBJECT_TYPE_NAME (gsk_renderer), "GskCairoRenderer") == 0)
renderer = "Cairo"; renderer = "Cairo";
else else

View File

@ -90,6 +90,7 @@ informative_render_tests = [
renderers = [ renderers = [
# name exclude term # name exclude term
[ 'opengl', '' ], [ 'opengl', '' ],
[ 'next', '' ],
[ 'broadway', '-3d' ], [ 'broadway', '-3d' ],
[ 'cairo', '-3d' ], [ 'cairo', '-3d' ],
] ]