[gtk/matthiasc/color-profile-rebased: 37/51] gsk: Do texture conversions in shaders
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/matthiasc/color-profile-rebased: 37/51] gsk: Do texture conversions in shaders
- Date: Sat, 1 Oct 2022 01:36:43 +0000 (UTC)
commit 0e0bb92ff97c891478b55d7bdd34ae67bfe3d557
Author: Matthias Clasen <mclasen redhat com>
Date: Sun May 8 14:42:32 2022 -0400
gsk: Do texture conversions in shaders
Do linearization and premultiplication of textures
in a shader after uploading the unconverted data.
This moves move work from the cpu to the gpu.
Conversions between other color spaces are still
done with lcms on the cpu.
gsk/gl/gskglcommandqueue.c | 67 +++++++++++++----
gsk/gl/gskglcommandqueueprivate.h | 9 ++-
gsk/gl/gskgldriver.c | 110 ++++++++++++++++++++++------
gsk/gl/gskglprograms.defs | 8 ++
gsk/gl/resources/linearize_premultiply.glsl | 21 ++++++
gsk/gl/resources/preamble.glsl | 5 +-
gsk/gl/resources/premultiply.glsl | 20 +++++
gsk/meson.build | 2 +
8 files changed, 201 insertions(+), 41 deletions(-)
---
diff --git a/gsk/gl/gskglcommandqueue.c b/gsk/gl/gskglcommandqueue.c
index 9f1710b8f4..736063891d 100644
--- a/gsk/gl/gskglcommandqueue.c
+++ b/gsk/gl/gskglcommandqueue.c
@@ -530,7 +530,7 @@ gsk_gl_command_queue_begin_draw (GskGLCommandQueue *self,
GskGLCommandBatch *batch;
g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
- g_assert (self->in_draw == FALSE);
+ g_assert (!self->in_draw);
g_assert (width <= G_MAXUINT16);
g_assert (height <= G_MAXUINT16);
@@ -568,6 +568,7 @@ gsk_gl_command_queue_end_draw (GskGLCommandQueue *self)
GskGLCommandBatch *batch;
g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
+ g_assert (self->in_draw);
g_assert (self->batches.len > 0);
if (will_ignore_batch (self))
@@ -1352,23 +1353,35 @@ gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self)
static void
gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
- GdkTexture *texture)
+ GdkTexture *texture,
+ GskConversion *conversion)
{
GdkGLContext *context;
const guchar *data;
gsize stride;
GdkMemoryTexture *memtex;
GdkMemoryFormat data_format;
+ GdkColorSpace *data_space;
int width, height;
GLenum gl_internalformat;
GLenum gl_format;
GLenum gl_type;
gsize bpp;
gboolean use_es;
+ gboolean convert_locally = FALSE;
context = gdk_gl_context_get_current ();
use_es = gdk_gl_context_get_use_es (context);
data_format = gdk_texture_get_format (texture);
+ data_space = gdk_texture_get_color_space (texture);
+
+ if (data_space == gdk_color_space_get_srgb ())
+ *conversion = GSK_CONVERSION_LINEARIZE;
+ else if (data_space == gdk_color_space_get_srgb_linear ())
+ *conversion = 0;
+ else /* FIXME: do colorspace conversion in a shader */
+ convert_locally = TRUE;
+
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
@@ -1378,21 +1391,38 @@ gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
&gl_format,
&gl_type))
{
- if (gdk_memory_format_prefers_high_depth (data_format))
- data_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
+ if (gdk_gl_context_get_api (context) == GDK_GL_API_GL)
+ {
+ *conversion |= GSK_CONVERSION_PREMULTIPLY;
+ }
else
- data_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
- if (!gdk_memory_format_gl_format (data_format,
- use_es,
- &gl_internalformat,
- &gl_format,
- &gl_type))
{
- g_assert_not_reached ();
+ convert_locally = TRUE;
+ if (gdk_memory_format_prefers_high_depth (data_format))
+ data_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
+ else
+ data_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
+ if (!gdk_memory_format_gl_format (data_format,
+ use_es,
+ &gl_internalformat,
+ &gl_format,
+ &gl_type))
+ {
+ g_assert_not_reached ();
+ }
}
}
- memtex = gdk_memory_texture_from_texture (texture, data_format, gdk_color_space_get_srgb ());
+ if (convert_locally)
+ {
+ memtex = gdk_memory_texture_from_texture (texture, data_format, gdk_color_space_get_srgb_linear ());
+ *conversion = 0;
+ }
+ else
+ {
+ memtex = gdk_memory_texture_from_texture (texture, gdk_texture_get_format (texture),
gdk_texture_get_color_space (texture));
+ }
+
data = gdk_memory_texture_get_data (memtex);
stride = gdk_memory_texture_get_stride (memtex);
bpp = gdk_memory_format_bytes_per_pixel (data_format);
@@ -1431,11 +1461,13 @@ int
gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
GdkTexture *texture,
int min_filter,
- int mag_filter)
+ int mag_filter,
+ GskConversion *conversion)
{
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
cairo_surface_t *surface = NULL;
int width, height;
+ int format;
int texture_id;
g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
@@ -1445,6 +1477,9 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
+
+ *conversion = 0;
+
if (width > self->max_texture_size || height > self->max_texture_size)
{
g_warning ("Attempt to create texture of size %ux%u but max size is %d. "
@@ -1453,7 +1488,9 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
width = MAX (width, self->max_texture_size);
height = MAX (height, self->max_texture_size);
}
- texture_id = gsk_gl_command_queue_create_texture (self, width, height, GL_RGBA8, min_filter, mag_filter);
+
+ format = gdk_memory_format_prefers_high_depth (gdk_texture_get_format (texture)) ? GL_RGBA16F : GL_RGBA8;
+ texture_id = gsk_gl_command_queue_create_texture (self, width, height, format, min_filter, mag_filter);
if (texture_id == -1)
return texture_id;
@@ -1463,7 +1500,7 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, texture_id);
- gsk_gl_command_queue_do_upload_texture (self, texture);
+ gsk_gl_command_queue_do_upload_texture (self, texture, conversion);
/* Restore previous texture state if any */
if (self->attachments->textures[0].id > 0)
diff --git a/gsk/gl/gskglcommandqueueprivate.h b/gsk/gl/gskglcommandqueueprivate.h
index 48911305a8..a6ffc9f96d 100644
--- a/gsk/gl/gskglcommandqueueprivate.h
+++ b/gsk/gl/gskglcommandqueueprivate.h
@@ -280,10 +280,17 @@ void gsk_gl_command_queue_execute (GskGLCommandQueue
guint scale_factor,
const cairo_region_t *scissor,
guint default_framebuffer);
+
+typedef enum {
+ GSK_CONVERSION_LINEARIZE = 1 << 0,
+ GSK_CONVERSION_PREMULTIPLY = 1 << 1,
+} GskConversion;
+
int gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
GdkTexture *texture,
int min_filter,
- int mag_filter);
+ int mag_filter,
+ GskConversion *remaining);
int gsk_gl_command_queue_create_texture (GskGLCommandQueue *self,
int width,
int height,
diff --git a/gsk/gl/gskgldriver.c b/gsk/gl/gskgldriver.c
index 81e69062c0..d631f81f4c 100644
--- a/gsk/gl/gskgldriver.c
+++ b/gsk/gl/gskgldriver.c
@@ -713,9 +713,10 @@ draw_offscreen (GskGLCommandQueue *command_queue,
}
static void
-set_viewport_for_size (GskGLProgram *program,
- float width,
- float height)
+set_viewport_for_size (GskGLDriver *self,
+ GskGLProgram *program,
+ float width,
+ float height)
{
float viewport[4] = { 0, 0, width, height };
@@ -724,15 +725,17 @@ set_viewport_for_size (GskGLProgram *program,
UNIFORM_SHARED_VIEWPORT, 0,
1,
(const float *)&viewport);
+ self->stamps[UNIFORM_SHARED_VIEWPORT]++;
}
#define ORTHO_NEAR_PLANE -10000
#define ORTHO_FAR_PLANE 10000
static void
-set_projection_for_size (GskGLProgram *program,
- float width,
- float height)
+set_projection_for_size (GskGLDriver *self,
+ GskGLProgram *program,
+ float width,
+ float height)
{
graphene_matrix_t projection;
@@ -743,10 +746,12 @@ set_projection_for_size (GskGLProgram *program,
program->program_info,
UNIFORM_SHARED_PROJECTION, 0,
&projection);
+ self->stamps[UNIFORM_SHARED_PROJECTION]++;
}
static void
-reset_modelview (GskGLProgram *program)
+reset_modelview (GskGLDriver *self,
+ GskGLProgram *program)
{
graphene_matrix_t modelview;
@@ -756,25 +761,38 @@ reset_modelview (GskGLProgram *program)
program->program_info,
UNIFORM_SHARED_MODELVIEW, 0,
&modelview);
+ self->stamps[UNIFORM_SHARED_MODELVIEW]++;
}
static GskGLTexture *
-gsk_gl_driver_convert_texture (GskGLDriver *self,
- int texture_id,
- int width,
- int height,
- int format)
+gsk_gl_driver_convert_texture (GskGLDriver *self,
+ int texture_id,
+ int width,
+ int height,
+ int format,
+ int min_filter,
+ int max_filter,
+ GskConversion conversion)
{
GskGLRenderTarget *target;
int prev_fbo;
- GskGLProgram *program = self->linearize_no_clip;
+ GskGLProgram *program;
+
+ if (conversion == (GSK_CONVERSION_LINEARIZE | GSK_CONVERSION_PREMULTIPLY))
+ program = self->linearize_premultiply_no_clip;
+ else if (conversion & GSK_CONVERSION_LINEARIZE)
+ program = self->linearize_no_clip;
+ else if (conversion & GSK_CONVERSION_PREMULTIPLY)
+ program = self->premultiply_no_clip;
+ else
+ g_assert_not_reached ();
gdk_gl_context_make_current (self->command_queue->context);
gsk_gl_driver_create_render_target (self,
width, height,
format,
- GL_LINEAR, GL_LINEAR,
+ min_filter, max_filter,
&target);
prev_fbo = gsk_gl_command_queue_bind_framebuffer (self->command_queue, target->framebuffer_id);
@@ -784,9 +802,9 @@ gsk_gl_driver_convert_texture (GskGLDriver *self,
program->program_info,
width, height);
- set_projection_for_size (program, width, height);
- set_viewport_for_size (program, width, height);
- reset_modelview (program);
+ set_projection_for_size (self, program, width, height);
+ set_viewport_for_size (self, program, width, height);
+ reset_modelview (self, program);
gsk_gl_program_set_uniform_texture (program,
UNIFORM_SHARED_SOURCE, 0,
@@ -839,6 +857,7 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
int height;
int width;
int format;
+ GskConversion conversion;
g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0);
g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
@@ -879,13 +898,18 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
* is nonlinear sRGB. Eventually, we should figure out how
* to convert from other color spaces to linear sRGB
*/
-
- t = gsk_gl_driver_convert_texture (self, gl_texture_id, width, height, format);
- if (gdk_texture_set_render_data (texture, self, t, (GDestroyNotify)gsk_gl_texture_destroyed))
+ t = gsk_gl_driver_convert_texture (self,
+ gl_texture_id,
+ width, height,
+ format,
+ min_filter, mag_filter,
+ GSK_CONVERSION_LINEARIZE);
+ if (gdk_texture_set_render_data (texture, self, t, gsk_gl_texture_destroyed))
t->user = texture;
return t->texture_id;
}
+ /* FIXME: do colorspace conversion in a shader */
}
}
@@ -900,8 +924,8 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
texture_id = gsk_gl_command_queue_upload_texture (self->command_queue,
GDK_TEXTURE (downloaded_texture),
- min_filter,
- mag_filter);
+ min_filter, mag_filter,
+ &conversion);
t = gsk_gl_texture_new (texture_id,
width, height, format, min_filter, mag_filter,
@@ -909,6 +933,15 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
g_hash_table_insert (self->textures, GUINT_TO_POINTER (texture_id), t);
+ g_clear_object (&downloaded_texture);
+
+ if (conversion)
+ t = gsk_gl_driver_convert_texture (self,
+ texture_id,
+ width, height, format,
+ min_filter, mag_filter,
+ conversion);
+
if (gdk_texture_set_render_data (texture, self, t, gsk_gl_texture_destroyed))
t->user = texture;
@@ -967,6 +1000,8 @@ gsk_gl_driver_create_texture (GskGLDriver *self,
GUINT_TO_POINTER (texture->texture_id),
texture);
+ texture->last_used_in_frame = self->current_frame_id;
+
return texture;
}
@@ -1334,6 +1369,7 @@ gsk_gl_driver_add_texture_slices (GskGLDriver *self,
GskGLTextureSlice *slices;
GskGLTexture *t;
guint n_slices;
+ int format;
guint cols;
guint rows;
int tex_width;
@@ -1351,6 +1387,9 @@ gsk_gl_driver_add_texture_slices (GskGLDriver *self,
tex_width = texture->width;
tex_height = texture->height;
+
+ format = gdk_memory_format_prefers_high_depth (gdk_texture_get_format (texture)) ? GL_RGBA16F : GL_RGBA8;
+
cols = (texture->width / max_texture_size) + 1;
rows = (texture->height / max_texture_size) + 1;
@@ -1377,13 +1416,38 @@ gsk_gl_driver_add_texture_slices (GskGLDriver *self,
int slice_index = (col * rows) + row;
GdkTexture *subtex;
guint texture_id;
+ GskConversion conversion;
subtex = gdk_memory_texture_new_subtexture (memtex,
x, y,
slice_width, slice_height);
texture_id = gsk_gl_command_queue_upload_texture (self->command_queue,
subtex,
- GL_NEAREST, GL_NEAREST);
+ GL_NEAREST, GL_NEAREST,
+ &conversion);
+
+ if (conversion)
+ {
+ t = gsk_gl_texture_new (texture_id,
+ slice_width, slice_height,
+ format,
+ GL_NEAREST, GL_NEAREST,
+ 0);
+ g_hash_table_insert (self->textures, GUINT_TO_POINTER (texture_id), t);
+
+ t = gsk_gl_driver_convert_texture (self,
+ texture_id,
+ slice_width, slice_height,
+ format,
+ GL_NEAREST, GL_NEAREST,
+ conversion);
+
+ texture_id = t->texture_id;
+ t->texture_id = 0;
+ g_hash_table_steal (self->textures, GUINT_TO_POINTER (texture_id));
+ gsk_gl_texture_free (t);
+ }
+
g_object_unref (subtex);
slices[slice_index].rect.x = x;
diff --git a/gsk/gl/gskglprograms.defs b/gsk/gl/gskglprograms.defs
index 9e820dc4ef..6791c90037 100644
--- a/gsk/gl/gskglprograms.defs
+++ b/gsk/gl/gskglprograms.defs
@@ -90,3 +90,11 @@ GSK_GL_DEFINE_PROGRAM (postprocessing,
GSK_GL_DEFINE_PROGRAM (linearize,
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("linearize.glsl")),
GSK_GL_NO_UNIFORMS)
+
+GSK_GL_DEFINE_PROGRAM (premultiply,
+ GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("premultiply.glsl")),
+ GSK_GL_NO_UNIFORMS)
+
+GSK_GL_DEFINE_PROGRAM (linearize_premultiply,
+ GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("linearize_premultiply.glsl")),
+ GSK_GL_NO_UNIFORMS)
diff --git a/gsk/gl/resources/linearize_premultiply.glsl b/gsk/gl/resources/linearize_premultiply.glsl
new file mode 100644
index 0000000000..85a37c4ca3
--- /dev/null
+++ b/gsk/gl/resources/linearize_premultiply.glsl
@@ -0,0 +1,21 @@
+
+// VERTEX_SHADER:
+// linearize_premultiply.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+// linearize_premultiply.glsl
+
+void main() {
+ vec4 diffuse = GskTexture(u_source, vUv);
+
+ diffuse = gsk_srgb_to_linear(diffuse);
+ diffuse = gsk_premultiply(diffuse);
+
+ gskSetOutputColor(diffuse);
+}
diff --git a/gsk/gl/resources/preamble.glsl b/gsk/gl/resources/preamble.glsl
index 68a89552e9..c32379150c 100644
--- a/gsk/gl/resources/preamble.glsl
+++ b/gsk/gl/resources/preamble.glsl
@@ -42,7 +42,8 @@ gsk_get_bounds(vec4[3] data)
return vec4(data[0].xy, data[0].xy + data[0].zw);
}
-vec4 gsk_premultiply(vec4 c) {
+vec4 gsk_premultiply(vec4 c)
+{
return vec4(c.rgb * c.a, c.a);
}
@@ -62,7 +63,7 @@ vec4 gsk_srgb_to_linear(vec4 srgb)
vec4 gsk_linear_to_srgb(vec4 linear_rgba)
{
- vec3 srgb = pow(linear_rgba.rgb , vec3(1/2.2));
+ vec3 srgb = pow(linear_rgba.rgb, vec3(1/2.2));
return vec4(srgb, linear_rgba.a);
}
diff --git a/gsk/gl/resources/premultiply.glsl b/gsk/gl/resources/premultiply.glsl
new file mode 100644
index 0000000000..b83ce9e90e
--- /dev/null
+++ b/gsk/gl/resources/premultiply.glsl
@@ -0,0 +1,20 @@
+
+// VERTEX_SHADER:
+// premultiply.glsl
+
+void main() {
+ gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
+
+ vUv = vec2(aUv.x, aUv.y);
+}
+
+// FRAGMENT_SHADER:
+// premultiply.glsl
+
+void main() {
+ vec4 diffuse = GskTexture(u_source, vUv);
+
+ diffuse = gsk_premultiply(diffuse);
+
+ gskSetOutputColor(diffuse);
+}
diff --git a/gsk/meson.build b/gsk/meson.build
index 3c3120508b..9a64967f49 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -21,6 +21,8 @@ gsk_private_gl_shaders = [
'gl/resources/filled_border.glsl',
'gl/resources/postprocessing.glsl',
'gl/resources/linearize.glsl',
+ 'gl/resources/premultiply.glsl',
+ 'gl/resources/linearize_premultiply.glsl',
]
gsk_public_sources = files([
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]