Compare commits

...

4 Commits

Author SHA1 Message Date
Emmanuele Bassi
e8d497b83a Do not leak the shaders cache
When finalizing the shader cache object we need to delete the shaders
and programs we are caching as well.
2018-03-30 13:59:06 +01:00
Emmanuele Bassi
37767e056f Cache linked programs
If we're trying to link the same vertex and fragment shaders together
then we can simply reuse the program object and avoid linking
altogether.
2018-03-30 13:55:50 +01:00
Emmanuele Bassi
c7e1ab67c6 Cache individual GLSL shaders
When compiling fragment or vertex shaders, we can store the shader
object id for later use, if the shader source is identical.
2018-03-30 13:55:01 +01:00
Emmanuele Bassi
4a2e73a07c Move GL shader compilation to a separate object
We want to cache the compiled and linked programs, so the first step is
to create an object in charge of compiling and linking GLSL programs.

We only create one per GL context, for the time being, but in the
following commits we're going to store it per display connection, and
introduce the cache.
2018-03-30 02:24:53 +01:00
6 changed files with 260 additions and 98 deletions

View File

@@ -1790,6 +1790,7 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
prog->index = i;
prog->id = gsk_shader_builder_create_program (builder,
self->gl_context,
program_definitions[i].vs,
program_definitions[i].fs,
&shader_error);
@@ -1914,7 +1915,6 @@ static void
gsk_gl_renderer_unrealize (GskRenderer *renderer)
{
GskGLRenderer *self = GSK_GL_RENDERER (renderer);
guint i;
if (self->gl_context == NULL)
return;
@@ -1926,9 +1926,6 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer)
*/
g_array_set_size (self->render_ops, 0);
for (i = 0; i < GL_N_PROGRAMS; i ++)
glDeleteProgram (self->programs[i].id);
gsk_gl_glyph_cache_free (&self->glyph_cache);
g_clear_object (&self->gl_profiler);

202
gsk/gl/gskglshadercache.c Normal file
View File

@@ -0,0 +1,202 @@
#include "config.h"
#include "gskglshadercacheprivate.h"
#include "gskdebugprivate.h"
#include <epoxy/gl.h>
struct _GskGLShaderCache
{
GObject parent_instance;
GHashTable *shader_cache;
GHashTable *program_cache;
};
G_DEFINE_TYPE (GskGLShaderCache, gsk_gl_shader_cache, G_TYPE_OBJECT)
static void
gsk_gl_shader_cache_finalize (GObject *gobject)
{
GskGLShaderCache *self = GSK_GL_SHADER_CACHE (gobject);
g_clear_pointer (&self->shader_cache, g_hash_table_unref);
g_clear_pointer (&self->program_cache, g_hash_table_unref);
G_OBJECT_CLASS (gsk_gl_shader_cache_parent_class)->finalize (gobject);
}
static void
gsk_gl_shader_cache_class_init (GskGLShaderCacheClass *klass)
{
G_OBJECT_CLASS (klass)->finalize = gsk_gl_shader_cache_finalize;
}
static void
gsk_gl_shader_cache_init (GskGLShaderCache *self)
{
}
GskGLShaderCache *
gsk_gl_shader_cache_new (void)
{
return g_object_new (GSK_TYPE_GL_SHADER_CACHE, NULL);
}
static void
delete_shader (gpointer data)
{
int shader_id = GPOINTER_TO_INT (data);
if (shader_id > 0)
glDeleteShader (shader_id);
}
static void
delete_program (gpointer data)
{
int program_id = GPOINTER_TO_INT (data);
if (program_id > 0)
glDeleteProgram (program_id);
}
int
gsk_gl_shader_cache_compile_shader (GskGLShaderCache *cache,
int shader_type,
const char *source,
GError **error)
{
if (cache->shader_cache == NULL)
cache->shader_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free,
delete_shader);
char *shasum = g_compute_checksum_for_string (G_CHECKSUM_SHA256, source, -1);
int shader_id = GPOINTER_TO_INT (g_hash_table_lookup (cache->shader_cache, shasum));
if (shader_id != 0)
{
GSK_NOTE (SHADERS,
g_debug ("*** Cache hit for %s shader (checksum: %s) ***\n"
"%*s%s\n",
shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
shasum,
64, source,
strlen (source) < 64 ? "" : "..."));
g_free (shasum);
return shader_id;
}
shader_id = glCreateShader (shader_type);
glShaderSource (shader_id, 1, (const GLchar **) &source, NULL);
glCompileShader (shader_id);
GSK_NOTE (SHADERS,
g_debug ("*** Compiling %s shader ***\n"
"%*s%s\n",
shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
64, source,
strlen (source) < 64 ? "" : "..."));
int status = GL_FALSE;
glGetShaderiv (shader_id, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
int log_len = 0;
glGetShaderiv (shader_id, GL_INFO_LOG_LENGTH, &log_len);
char *buffer = g_malloc0 (log_len + 1);
glGetShaderInfoLog (shader_id, log_len, NULL, buffer);
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_COMPILATION_FAILED,
"Compilation failure in %s shader:\n%s",
shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
buffer);
g_free (buffer);
glDeleteShader (shader_id);
shader_id = -1;
}
else
{
g_hash_table_insert (cache->shader_cache,
shasum,
GINT_TO_POINTER (shader_id));
}
return shader_id;
}
int
gsk_gl_shader_cache_link_program (GskGLShaderCache *cache,
int vertex_shader,
int fragment_shader,
GError **error)
{
g_return_val_if_fail (vertex_shader > 0 && fragment_shader > 0, -1);
if (cache->program_cache == NULL)
cache->program_cache = g_hash_table_new_full (g_int64_hash, g_int64_equal,
g_free,
delete_program);
gint64 *key = g_new (gint64, 1);
*key = (gint64) vertex_shader << 31 | fragment_shader;
int program_id = GPOINTER_TO_INT (g_hash_table_lookup (cache->program_cache, key));
if (program_id > 0)
{
GSK_NOTE (SHADERS,
g_debug ("*** Cache hit for program (vertex: %d, fragment: %d) ***",
vertex_shader,
fragment_shader));
g_free (key);
return program_id;
}
program_id = glCreateProgram ();
GSK_NOTE (SHADERS,
g_debug ("*** Linking %d, %d shaders ***\n",
vertex_shader,
fragment_shader));
glAttachShader (program_id, vertex_shader);
glAttachShader (program_id, fragment_shader);
glLinkProgram (program_id);
int status = GL_FALSE;
glGetProgramiv (program_id, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
int log_len = 0;
glGetProgramiv (program_id, GL_INFO_LOG_LENGTH, &log_len);
char *buffer = g_malloc0 (log_len + 1);
glGetProgramInfoLog (program_id, log_len, NULL, buffer);
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_LINK_FAILED,
"Linking failure in shader:\n%s", buffer);
g_free (buffer);
glDeleteProgram (program_id);
program_id = -1;
}
else
{
glDetachShader (program_id, vertex_shader);
glDetachShader (program_id, fragment_shader);
g_hash_table_insert (cache->program_cache,
key,
GINT_TO_POINTER (program_id));
}
return program_id;
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <gdk/gdk.h>
#include <graphene.h>
G_BEGIN_DECLS
#define GSK_TYPE_GL_SHADER_CACHE (gsk_gl_shader_cache_get_type ())
G_DECLARE_FINAL_TYPE (GskGLShaderCache, gsk_gl_shader_cache, GSK, GL_SHADER_CACHE, GObject)
GskGLShaderCache * gsk_gl_shader_cache_new (void);
int gsk_gl_shader_cache_compile_shader (GskGLShaderCache *cache,
int shader_type,
const char *code,
GError **error);
int gsk_gl_shader_cache_link_program (GskGLShaderCache *cache,
int vertex_shader,
int fragment_shader,
GError **error);
G_END_DECLS

View File

@@ -3,6 +3,7 @@
#include "gskshaderbuilderprivate.h"
#include "gskdebugprivate.h"
#include "gskglshadercacheprivate.h"
#include <gdk/gdk.h>
#include <epoxy/gl.h>
@@ -18,8 +19,6 @@ struct _GskShaderBuilder
int version;
GPtrArray *defines;
GPtrArray *uniforms;
GPtrArray *attributes;
};
G_DEFINE_TYPE (GskShaderBuilder, gsk_shader_builder, G_TYPE_OBJECT)
@@ -137,6 +136,7 @@ lookup_shader_code (GString *code,
static int
gsk_shader_builder_compile_shader (GskShaderBuilder *builder,
GskGLShaderCache *cache,
int shader_type,
const char *shader_preamble,
const char *shader_source,
@@ -145,7 +145,6 @@ gsk_shader_builder_compile_shader (GskShaderBuilder *builder,
GString *code;
char *source;
int shader_id;
int status;
int i;
code = g_string_new (NULL);
@@ -188,117 +187,56 @@ gsk_shader_builder_compile_shader (GskShaderBuilder *builder,
source = g_string_free (code, FALSE);
shader_id = glCreateShader (shader_type);
glShaderSource (shader_id, 1, (const GLchar **) &source, NULL);
glCompileShader (shader_id);
#ifdef G_ENABLE_DEBUG
if (GSK_DEBUG_CHECK (SHADERS))
{
g_print ("*** Compiling %s shader from '%s' + '%s' ***\n"
"%s\n",
shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
shader_preamble, shader_source,
source);
}
#endif
shader_id = gsk_gl_shader_cache_compile_shader (cache,
shader_type, source,
error);
g_free (source);
glGetShaderiv (shader_id, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE)
{
int log_len;
char *buffer;
glGetShaderiv (shader_id, GL_INFO_LOG_LENGTH, &log_len);
buffer = g_malloc0 (log_len + 1);
glGetShaderInfoLog (shader_id, log_len, NULL, buffer);
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_COMPILATION_FAILED,
"Compilation failure in %s shader:\n%s",
shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
buffer);
g_free (buffer);
glDeleteShader (shader_id);
return -1;
}
return shader_id;
}
int
gsk_shader_builder_create_program (GskShaderBuilder *builder,
GdkGLContext *gl_context,
const char *vertex_shader,
const char *fragment_shader,
GError **error)
{
int vertex_id, fragment_id;
int program_id;
int status;
g_return_val_if_fail (GSK_IS_SHADER_BUILDER (builder), -1);
g_return_val_if_fail (vertex_shader != NULL, -1);
g_return_val_if_fail (fragment_shader != NULL, -1);
vertex_id = gsk_shader_builder_compile_shader (builder, GL_VERTEX_SHADER,
GskGLShaderCache *cache = g_object_get_data (G_OBJECT (gl_context), "-gsk-gl-shader-cache");
if (cache == NULL)
{
cache = gsk_gl_shader_cache_new ();
g_object_set_data_full (G_OBJECT (gl_context),
"-gsk-gl-shader-cache",
cache,
(GDestroyNotify) g_object_unref);
}
vertex_id = gsk_shader_builder_compile_shader (builder, cache,
GL_VERTEX_SHADER,
builder->vertex_preamble,
vertex_shader,
error);
if (vertex_id < 0)
return -1;
fragment_id = gsk_shader_builder_compile_shader (builder, GL_FRAGMENT_SHADER,
fragment_id = gsk_shader_builder_compile_shader (builder, cache,
GL_FRAGMENT_SHADER,
builder->fragment_preamble,
fragment_shader,
error);
if (fragment_id < 0)
{
glDeleteShader (vertex_id);
return -1;
}
return -1;
program_id = glCreateProgram ();
glAttachShader (program_id, vertex_id);
glAttachShader (program_id, fragment_id);
glLinkProgram (program_id);
glGetProgramiv (program_id, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
{
char *buffer = NULL;
int log_len = 0;
glGetProgramiv (program_id, GL_INFO_LOG_LENGTH, &log_len);
buffer = g_malloc0 (log_len + 1);
glGetProgramInfoLog (program_id, log_len, NULL, buffer);
g_set_error (error, GDK_GL_ERROR, GDK_GL_ERROR_LINK_FAILED,
"Linking failure in shader:\n%s", buffer);
g_free (buffer);
glDeleteProgram (program_id);
program_id = -1;
goto out;
}
out:
if (vertex_id > 0)
{
glDetachShader (program_id, vertex_id);
glDeleteShader (vertex_id);
}
if (fragment_id > 0)
{
glDetachShader (program_id, fragment_id);
glDeleteShader (fragment_id);
}
return program_id;
return gsk_gl_shader_cache_link_program (cache,
vertex_id,
fragment_id,
error);
}

View File

@@ -26,6 +26,7 @@ void gsk_shader_builder_add_define (GskShad
const char *define_value);
int gsk_shader_builder_create_program (GskShaderBuilder *builder,
GdkGLContext *gl_context,
const char *vertex_shader,
const char *fragment_shader,
GError **error);

View File

@@ -25,7 +25,7 @@ gsk_public_sources = files([
'gskrenderer.c',
'gskrendernode.c',
'gskrendernodeimpl.c',
'gskroundedrect.c'
'gskroundedrect.c',
])
gsk_private_sources = files([
@@ -34,13 +34,14 @@ gsk_private_sources = files([
'gskdebug.c',
'gskprivate.c',
'gskprofiler.c',
'gl/gskshaderbuilder.c',
'gl/gskglprofiler.c',
'gl/gskglrenderer.c',
'gl/gskgldriver.c',
'gl/gskglglyphcache.c',
'gl/gskglimage.c',
'gl/gskgldriver.c',
'gl/gskglrenderops.c'
'gl/gskglprofiler.c',
'gl/gskglrenderer.c',
'gl/gskglrenderops.c',
'gl/gskglshadercache.c',
'gl/gskshaderbuilder.c',
])
gsk_public_headers = files([
@@ -49,7 +50,7 @@ gsk_public_headers = files([
'gskrendernode.h',
'gskroundedrect.h',
'gsktypes.h',
'gsk-autocleanup.h'
'gsk-autocleanup.h',
])
install_headers(gsk_public_headers, 'gsk.h', subdir: 'gtk-4.0/gsk')