forked from AuroraMiddleware/gtk
gsk: add OpenGL based GskNglRenderer
The primary goal here was to cleanup the current GL renderer to make maintenance easier going forward. Furthermore, it tracks state to allow us to implement more advanced renderer features going forward. Reordering This renderer will reorder batches by render target to reduce the number of times render targets are changed. In the future, we could also reorder by program within the render target if we can determine that vertices do not overlap. Uniform Snapshots To allow for reordering of batches all uniforms need to be tracked for the programs. This allows us to create the full uniform state when the batch has been moved into a new position. Some care was taken as it can be performance sensitive. Attachment Snapshots Similar to uniform snapshots, we need to know all of the texture attachments so that we can rebind them when necessary. Render Jobs To help isolate the process of creating GL commands from the renderer abstraction a render job abstraction was added. This could be extended in the future if we decided to do tiling. Command Queue Render jobs create batches using the command queue. The command queue will snapshot uniform and attachment state so that it can reorder batches right before executing them. Currently, the only reordering done is to ensure that we only visit each render target once. We could extend this by tracking vertices, attachments, and others. This code currently uses an inline array helper to reduce overhead from GArray which was showing up on profiles. It could be changed to use GdkArray without too much work, but had roughly double the instructions. Cycle counts have not yet been determined. GLSL Programs This was simplified to use XMACROS so that we can just extend one file (gskglprograms.defs) instead of multiple places. The programs are added as fields in the driver for easy access. Driver The driver manages textures, render targets, access to atlases, programs, and more. There is one driver per display, by using the shared GL context. Some work could be done here to batch uploads so that we make fewer calls to upload when sending icon theme data to the GPU. We'd need to keep a copy of the atlas data for such purposes.
This commit is contained in:
parent
9698d4aa2a
commit
2a38cecd33
@ -25,6 +25,7 @@
|
||||
|
||||
#include "gsk/gskrendernodeparserprivate.h"
|
||||
#include "gsk/gl/gskglrenderer.h"
|
||||
#include "gsk/ngl/gsknglrenderer.h"
|
||||
#ifdef GDK_WINDOWING_BROADWAY
|
||||
#include "gsk/broadway/gskbroadwayrenderer.h"
|
||||
#endif
|
||||
@ -762,6 +763,9 @@ node_editor_window_realize (GtkWidget *widget)
|
||||
node_editor_window_add_renderer (self,
|
||||
gsk_gl_renderer_new (),
|
||||
"OpenGL");
|
||||
node_editor_window_add_renderer (self,
|
||||
gsk_ngl_renderer_new (),
|
||||
"NGL");
|
||||
#ifdef GDK_RENDERING_VULKAN
|
||||
node_editor_window_add_renderer (self,
|
||||
gsk_vulkan_renderer_new (),
|
||||
|
@ -139,7 +139,9 @@
|
||||
#include "gskglshader.h"
|
||||
#include "gskglshaderprivate.h"
|
||||
#include "gskdebugprivate.h"
|
||||
|
||||
#include "gl/gskglrendererprivate.h"
|
||||
#include "ngl/gsknglrendererprivate.h"
|
||||
|
||||
static GskGLUniformType
|
||||
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);
|
||||
|
||||
if (GSK_IS_GL_RENDERER (renderer))
|
||||
return gsk_gl_renderer_try_compile_gl_shader (GSK_GL_RENDERER (renderer),
|
||||
shader, error);
|
||||
return gsk_gl_renderer_try_compile_gl_shader (GSK_GL_RENDERER (renderer), 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,
|
||||
"The renderer does not support gl shaders");
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "gskcairorenderer.h"
|
||||
#include "gskdebugprivate.h"
|
||||
#include "gl/gskglrenderer.h"
|
||||
#include "ngl/gsknglrenderer.h"
|
||||
#include "gskprofilerprivate.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
|
||||
|| g_ascii_strcasecmp (renderer_name, "gl") == 0)
|
||||
return GSK_TYPE_GL_RENDERER;
|
||||
else if (g_ascii_strcasecmp (renderer_name, "ngl") == 0)
|
||||
return GSK_TYPE_NGL_RENDERER;
|
||||
#ifdef GDK_RENDERING_VULKAN
|
||||
else if (g_ascii_strcasecmp (renderer_name, "vulkan") == 0)
|
||||
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 (" opengl - Use the default OpenGL renderer\n");
|
||||
g_print (" gl - Same as opengl\n");
|
||||
g_print (" next - Another OpenGL renderer\n");
|
||||
#ifdef GDK_RENDERING_VULKAN
|
||||
g_print (" vulkan - Use the Vulkan renderer\n");
|
||||
#else
|
||||
|
@ -31,6 +31,7 @@ gsk_public_sources = files([
|
||||
'gskroundedrect.c',
|
||||
'gsktransform.c',
|
||||
'gl/gskglrenderer.c',
|
||||
'ngl/gsknglrenderer.c',
|
||||
])
|
||||
|
||||
gsk_private_sources = files([
|
||||
@ -48,6 +49,19 @@ gsk_private_sources = files([
|
||||
'gl/gskgliconcache.c',
|
||||
'gl/opbuffer.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([
|
||||
@ -64,7 +78,8 @@ gsk_public_headers = files([
|
||||
install_headers(gsk_public_headers, 'gsk.h', subdir: 'gtk-4.0/gsk')
|
||||
|
||||
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')
|
||||
gsk_public_headers += gsk_public_gl_headers
|
||||
|
106
gsk/ngl/gsknglattachmentstate.c
Normal file
106
gsk/ngl/gsknglattachmentstate.c
Normal 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;
|
||||
}
|
||||
}
|
71
gsk/ngl/gsknglattachmentstateprivate.h
Normal file
71
gsk/ngl/gsknglattachmentstateprivate.h
Normal 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
69
gsk/ngl/gsknglbuffer.c
Normal 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);
|
||||
}
|
81
gsk/ngl/gsknglbufferprivate.h
Normal file
81
gsk/ngl/gsknglbufferprivate.h
Normal 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
1507
gsk/ngl/gsknglcommandqueue.c
Normal file
File diff suppressed because it is too large
Load Diff
362
gsk/ngl/gsknglcommandqueueprivate.h
Normal file
362
gsk/ngl/gsknglcommandqueueprivate.h
Normal 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
678
gsk/ngl/gsknglcompiler.c
Normal 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);
|
||||
}
|
69
gsk/ngl/gsknglcompilerprivate.h
Normal file
69
gsk/ngl/gsknglcompilerprivate.h
Normal 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
1296
gsk/ngl/gskngldriver.c
Normal file
File diff suppressed because it is too large
Load Diff
234
gsk/ngl/gskngldriverprivate.h
Normal file
234
gsk/ngl/gskngldriverprivate.h
Normal 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__ */
|
325
gsk/ngl/gsknglglyphlibrary.c
Normal file
325
gsk/ngl/gsknglglyphlibrary.c
Normal 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;
|
||||
}
|
119
gsk/ngl/gsknglglyphlibraryprivate.h
Normal file
119
gsk/ngl/gsknglglyphlibraryprivate.h
Normal 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
213
gsk/ngl/gskngliconlibrary.c
Normal 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);
|
||||
}
|
||||
}
|
60
gsk/ngl/gskngliconlibraryprivate.h
Normal file
60
gsk/ngl/gskngliconlibraryprivate.h
Normal 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
176
gsk/ngl/gsknglprogram.c
Normal 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;
|
||||
}
|
273
gsk/ngl/gsknglprogramprivate.h
Normal file
273
gsk/ngl/gsknglprogramprivate.h
Normal 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__ */
|
83
gsk/ngl/gsknglprograms.defs
Normal file
83
gsk/ngl/gsknglprograms.defs
Normal 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
312
gsk/ngl/gsknglrenderer.c
Normal 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
46
gsk/ngl/gsknglrenderer.h
Normal 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__ */
|
34
gsk/ngl/gsknglrendererprivate.h
Normal file
34
gsk/ngl/gsknglrendererprivate.h
Normal 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
3760
gsk/ngl/gsknglrenderjob.c
Normal file
File diff suppressed because it is too large
Load Diff
39
gsk/ngl/gsknglrenderjobprivate.h
Normal file
39
gsk/ngl/gsknglrenderjobprivate.h
Normal 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__ */
|
228
gsk/ngl/gsknglshadowlibrary.c
Normal file
228
gsk/ngl/gsknglshadowlibrary.c
Normal 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--;
|
||||
}
|
||||
}
|
||||
}
|
44
gsk/ngl/gsknglshadowlibraryprivate.h
Normal file
44
gsk/ngl/gsknglshadowlibraryprivate.h
Normal 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__ */
|
315
gsk/ngl/gskngltexturelibrary.c
Normal file
315
gsk/ngl/gskngltexturelibrary.c
Normal 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;
|
||||
}
|
202
gsk/ngl/gskngltexturelibraryprivate.h
Normal file
202
gsk/ngl/gskngltexturelibraryprivate.h
Normal 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
188
gsk/ngl/gskngltexturepool.c
Normal 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;
|
||||
}
|
102
gsk/ngl/gskngltexturepoolprivate.h
Normal file
102
gsk/ngl/gskngltexturepoolprivate.h
Normal 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__ */
|
62
gsk/ngl/gskngltypesprivate.h
Normal file
62
gsk/ngl/gskngltypesprivate.h
Normal 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__ */
|
270
gsk/ngl/gskngluniformstate.c
Normal file
270
gsk/ngl/gskngluniformstate.c
Normal 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;
|
||||
}
|
685
gsk/ngl/gskngluniformstateprivate.h
Normal file
685
gsk/ngl/gskngluniformstateprivate.h
Normal 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
77
gsk/ngl/inlinearray.h
Normal 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
309
gsk/ngl/ninesliceprivate.h
Normal 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__ */
|
@ -41,6 +41,7 @@
|
||||
#define GTK_COMPILATION
|
||||
|
||||
#include <gsk/gl/gskglrenderer.h>
|
||||
#include <gsk/ngl/gsknglrenderer.h>
|
||||
|
||||
#ifdef GDK_WINDOWING_BROADWAY
|
||||
#include <gsk/broadway/gskbroadwayrenderer.h>
|
||||
|
@ -147,6 +147,8 @@ init_version (GtkInspectorGeneral *gen)
|
||||
renderer = "Vulkan";
|
||||
else if (strcmp (G_OBJECT_TYPE_NAME (gsk_renderer), "GskGLRenderer") == 0)
|
||||
renderer = "GL";
|
||||
else if (strcmp (G_OBJECT_TYPE_NAME (gsk_renderer), "GskNgltRenderer") == 0)
|
||||
renderer = "NGL";
|
||||
else if (strcmp (G_OBJECT_TYPE_NAME (gsk_renderer), "GskCairoRenderer") == 0)
|
||||
renderer = "Cairo";
|
||||
else
|
||||
|
@ -90,6 +90,7 @@ informative_render_tests = [
|
||||
renderers = [
|
||||
# name exclude term
|
||||
[ 'opengl', '' ],
|
||||
[ 'next', '' ],
|
||||
[ 'broadway', '-3d' ],
|
||||
[ 'cairo', '-3d' ],
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user