From 1a85d569e349f90d8df8f89a5ccf59cf4ca0b64c Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Mon, 21 Aug 2023 02:18:37 +0200 Subject: [PATCH] gpu: Add ability to run shaders This heaves over an inital chunk of code from the Vulkan renderer to execute shaders. The only shader that exists for now is a shader that draws a single texture. We use that to replace the blit op we were doing before. --- gsk/gen-gsk-gresources-xml.py | 19 +- gsk/gpu/gskglbuffer.c | 92 +++++ gsk/gpu/gskglbufferprivate.h | 20 + gsk/gpu/gskgldevice.c | 385 ++++++++++++++++++ gsk/gpu/gskgldeviceprivate.h | 6 + gsk/gpu/gskglframe.c | 120 +++++- gsk/gpu/gskglframeprivate.h | 3 + gsk/gpu/gskgpublitop.c | 15 +- gsk/gpu/gskgpubuffer.c | 52 +++ gsk/gpu/gskgpubufferprivate.h | 42 ++ gsk/gpu/gskgpuclip.c | 293 ++++++++++++++ gsk/gpu/gskgpuclipprivate.h | 68 ++++ gsk/gpu/gskgpudownloadop.c | 35 +- gsk/gpu/gskgpuframe.c | 98 ++++- gsk/gpu/gskgpuframeprivate.h | 17 +- gsk/gpu/gskgpuglobalsop.c | 97 +++++ gsk/gpu/gskgpuglobalsopprivate.h | 26 ++ gsk/gpu/gskgpunodeprocessor.c | 244 ++++++++++-- gsk/gpu/gskgpuop.c | 19 +- gsk/gpu/gskgpuopprivate.h | 8 +- gsk/gpu/gskgpurenderpassop.c | 327 ++++++++++++++++ gsk/gpu/gskgpurenderpassopprivate.h | 32 ++ gsk/gpu/gskgpushaderop.c | 151 ++++++++ gsk/gpu/gskgpushaderopprivate.h | 67 ++++ gsk/gpu/gskgputextureop.c | 74 ++++ gsk/gpu/gskgputextureopprivate.h | 19 + gsk/gpu/gskgputypesprivate.h | 17 + gsk/gpu/gskgpuuploadop.c | 48 +-- gsk/gpu/gskvulkanbuffer.c | 108 ++++-- gsk/gpu/gskvulkanbufferprivate.h | 27 +- gsk/gpu/gskvulkandevice.c | 471 +++++++++++++++++++++++ gsk/gpu/gskvulkandeviceprivate.h | 34 +- gsk/gpu/gskvulkanframe.c | 190 ++++++++- gsk/gpu/gskvulkanimage.c | 7 +- gsk/gpu/gskvulkanimageprivate.h | 4 +- gsk/gpu/shaders/common-gl.glsl | 49 +++ gsk/gpu/shaders/common-vulkan.glsl | 47 +++ gsk/gpu/shaders/common.glsl | 139 +++++++ gsk/gpu/shaders/ellipse.glsl | 38 ++ gsk/gpu/shaders/enums.glsl | 8 + gsk/gpu/shaders/generate-header.py | 291 ++++++++++++++ gsk/gpu/shaders/gskgputexture.glsl | 44 +++ gsk/gpu/shaders/meson.build | 82 ++++ gsk/gpu/shaders/process-glsl-includes.py | 25 ++ gsk/gpu/shaders/rect.glsl | 92 +++++ gsk/gpu/shaders/roundedrect.glsl | 90 +++++ gsk/gskrectprivate.h | 12 + gsk/meson.build | 21 +- 48 files changed, 3980 insertions(+), 193 deletions(-) create mode 100644 gsk/gpu/gskglbuffer.c create mode 100644 gsk/gpu/gskglbufferprivate.h create mode 100644 gsk/gpu/gskgpubuffer.c create mode 100644 gsk/gpu/gskgpubufferprivate.h create mode 100644 gsk/gpu/gskgpuclip.c create mode 100644 gsk/gpu/gskgpuclipprivate.h create mode 100644 gsk/gpu/gskgpuglobalsop.c create mode 100644 gsk/gpu/gskgpuglobalsopprivate.h create mode 100644 gsk/gpu/gskgpurenderpassop.c create mode 100644 gsk/gpu/gskgpurenderpassopprivate.h create mode 100644 gsk/gpu/gskgpushaderop.c create mode 100644 gsk/gpu/gskgpushaderopprivate.h create mode 100644 gsk/gpu/gskgputextureop.c create mode 100644 gsk/gpu/gskgputextureopprivate.h create mode 100644 gsk/gpu/shaders/common-gl.glsl create mode 100644 gsk/gpu/shaders/common-vulkan.glsl create mode 100644 gsk/gpu/shaders/common.glsl create mode 100644 gsk/gpu/shaders/ellipse.glsl create mode 100644 gsk/gpu/shaders/enums.glsl create mode 100644 gsk/gpu/shaders/generate-header.py create mode 100644 gsk/gpu/shaders/gskgputexture.glsl create mode 100644 gsk/gpu/shaders/meson.build create mode 100644 gsk/gpu/shaders/process-glsl-includes.py create mode 100644 gsk/gpu/shaders/rect.glsl create mode 100644 gsk/gpu/shaders/roundedrect.glsl diff --git a/gsk/gen-gsk-gresources-xml.py b/gsk/gen-gsk-gresources-xml.py index 25dc35a3b7..7febd19847 100644 --- a/gsk/gen-gsk-gresources-xml.py +++ b/gsk/gen-gsk-gresources-xml.py @@ -23,20 +23,24 @@ def replace_if_changed(new, old): gl_source_shaders = [] ngl_source_shaders = [] vulkan_compiled_shaders = [] +gpu_vulkan_compiled_shaders = [] vulkan_shaders = [] for f in sys.argv[2:]: if f.endswith('.glsl'): - if f.startswith('ngl'): - ngl_source_shaders.append(f); + if f.find('gsk/gpu') > -1: + ngl_source_shaders.append(f) else: gl_source_shaders.append(f) elif f.endswith('.spv'): - vulkan_compiled_shaders.append(f) + if f.find('gsk/gpu') > -1: + gpu_vulkan_compiled_shaders.append(f) + else: + vulkan_compiled_shaders.append(f) elif f.endswith('.frag') or f.endswith('.vert'): vulkan_shaders.append(f) else: - sys.exit(-1) # FIXME: error message + raise Exception(f"No idea what XML to generate for {f}") xml = ''' @@ -50,7 +54,7 @@ for f in gl_source_shaders: xml += '\n' for f in ngl_source_shaders: - xml += ' ngl/resources/{0}\n'.format(os.path.basename(f)) + xml += ' gpu/shaders/{0}\n'.format(os.path.basename(f)) xml += '\n' @@ -59,6 +63,11 @@ for f in vulkan_compiled_shaders: xml += '\n' +for f in gpu_vulkan_compiled_shaders: + xml += ' gpu/shaders/{0}\n'.format(os.path.basename(f)) + +xml += '\n' + for f in vulkan_shaders: xml += ' vulkan/resources/{0}\n'.format(os.path.basename(f)) diff --git a/gsk/gpu/gskglbuffer.c b/gsk/gpu/gskglbuffer.c new file mode 100644 index 0000000000..bb85b8bef3 --- /dev/null +++ b/gsk/gpu/gskglbuffer.c @@ -0,0 +1,92 @@ +#include "config.h" + +#include "gskglbufferprivate.h" + +struct _GskGLBuffer +{ + GskGpuBuffer parent_instance; + + GLenum target; + GLuint buffer_id; + GLenum access; +}; + +G_DEFINE_TYPE (GskGLBuffer, gsk_gl_buffer, GSK_TYPE_GPU_BUFFER) + +static void +gsk_gl_buffer_finalize (GObject *object) +{ + GskGLBuffer *self = GSK_GL_BUFFER (object); + + glDeleteBuffers (1, &self->buffer_id); + + G_OBJECT_CLASS (gsk_gl_buffer_parent_class)->finalize (object); +} + +static guchar * +gsk_gl_buffer_map (GskGpuBuffer *buffer) +{ + GskGLBuffer *self = GSK_GL_BUFFER (buffer); + + gsk_gl_buffer_bind (self); + + return glMapBuffer (self->target, self->access); +} + +static void +gsk_gl_buffer_unmap (GskGpuBuffer *buffer) +{ + GskGLBuffer *self = GSK_GL_BUFFER (buffer); + + gsk_gl_buffer_bind (self); + + if (!glUnmapBuffer (self->target)) + { + g_warning ("glUnmapBuffer failed"); + } +} + +static void +gsk_gl_buffer_class_init (GskGLBufferClass *klass) +{ + GskGpuBufferClass *buffer_class = GSK_GPU_BUFFER_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + buffer_class->map = gsk_gl_buffer_map; + buffer_class->unmap = gsk_gl_buffer_unmap; + + gobject_class->finalize = gsk_gl_buffer_finalize; +} + +static void +gsk_gl_buffer_init (GskGLBuffer *self) +{ +} + +GskGpuBuffer * +gsk_gl_buffer_new (GLenum target, + gsize size, + GLenum access) +{ + GskGLBuffer *self; + + self = g_object_new (GSK_TYPE_GL_BUFFER, NULL); + + gsk_gpu_buffer_setup (GSK_GPU_BUFFER (self), size); + + self->target = target; + self->access = access; + + glGenBuffers (1, &self->buffer_id); + glBindBuffer (target, self->buffer_id); + glBufferData (target, size, NULL, GL_STATIC_DRAW); + + return GSK_GPU_BUFFER (self); +} + +void +gsk_gl_buffer_bind (GskGLBuffer *self) +{ + glBindBuffer (self->target, self->buffer_id); +} + diff --git a/gsk/gpu/gskglbufferprivate.h b/gsk/gpu/gskglbufferprivate.h new file mode 100644 index 0000000000..1360f1d245 --- /dev/null +++ b/gsk/gpu/gskglbufferprivate.h @@ -0,0 +1,20 @@ +#pragma once + +#include "gskgpubufferprivate.h" + +#include "gskgldeviceprivate.h" + +G_BEGIN_DECLS + +#define GSK_TYPE_GL_BUFFER (gsk_gl_buffer_get_type ()) + +G_DECLARE_FINAL_TYPE (GskGLBuffer, gsk_gl_buffer, GSK, GL_BUFFER, GskGpuBuffer) + +GskGpuBuffer * gsk_gl_buffer_new (GLenum target, + gsize size, + GLenum access); + +void gsk_gl_buffer_bind (GskGLBuffer *self); + +G_END_DECLS + diff --git a/gsk/gpu/gskgldevice.c b/gsk/gpu/gskgldevice.c index e9ec3f9818..2e7fb7584f 100644 --- a/gsk/gpu/gskgldevice.c +++ b/gsk/gpu/gskgldevice.c @@ -1,6 +1,10 @@ #include "config.h" #include "gskgldeviceprivate.h" + +#include "gskdebugprivate.h" +#include "gskgpushaderopprivate.h" +#include "gskglbufferprivate.h" #include "gskglimageprivate.h" #include "gdk/gdkdisplayprivate.h" @@ -11,6 +15,12 @@ struct _GskGLDevice { GskGpuDevice parent_instance; + + GHashTable *gl_programs; + const char *version_string; + GdkGLAPI api; + + guint sampler_ids[GSK_GPU_SAMPLER_N_SAMPLERS]; }; struct _GskGLDeviceClass @@ -18,8 +28,36 @@ struct _GskGLDeviceClass GskGpuDeviceClass parent_class; }; +typedef struct _GLProgramKey GLProgramKey; + +struct _GLProgramKey +{ + const GskGpuShaderOpClass *op_class; + GskGpuShaderClip clip; +}; + G_DEFINE_TYPE (GskGLDevice, gsk_gl_device, GSK_TYPE_GPU_DEVICE) +static guint +gl_program_key_hash (gconstpointer data) +{ + const GLProgramKey *key = data; + + return GPOINTER_TO_UINT (key->op_class) ^ + key->clip; +} + +static gboolean +gl_program_key_equal (gconstpointer a, + gconstpointer b) +{ + const GLProgramKey *keya = a; + const GLProgramKey *keyb = b; + + return keya->op_class == keyb->op_class && + keya->clip == keyb->clip; +} + static GskGpuImage * gsk_gl_device_create_offscreen_image (GskGpuDevice *device, GdkMemoryDepth depth, @@ -56,6 +94,11 @@ gsk_gl_device_finalize (GObject *object) g_object_steal_data (G_OBJECT (gsk_gpu_device_get_display (device)), "-gsk-gl-device"); + gdk_gl_context_make_current (gdk_display_get_gl_context (gsk_gpu_device_get_display (device))); + + g_hash_table_unref (self->gl_programs); + glDeleteSamplers (G_N_ELEMENTS (self->sampler_ids), self->sampler_ids); + G_OBJECT_CLASS (gsk_gl_device_parent_class)->finalize (object); } @@ -71,9 +114,49 @@ gsk_gl_device_class_init (GskGLDeviceClass *klass) object_class->finalize = gsk_gl_device_finalize; } +static void +free_gl_program (gpointer program) +{ + glDeleteProgram (GPOINTER_TO_UINT (program)); +} + static void gsk_gl_device_init (GskGLDevice *self) { + self->gl_programs = g_hash_table_new_full (gl_program_key_hash, gl_program_key_equal, g_free, free_gl_program); +} + +static void +gsk_gl_device_setup_samplers (GskGLDevice *self) +{ + struct { + GLuint filter; + GLuint wrap; + } sampler_flags[GSK_GPU_SAMPLER_N_SAMPLERS] = { + [GSK_GPU_SAMPLER_DEFAULT] = { + GL_LINEAR, + GL_CLAMP_TO_EDGE, + }, + [GSK_GPU_SAMPLER_REPEAT] = { + GL_LINEAR, + GL_REPEAT, + }, + [GSK_GPU_SAMPLER_NEAREST] = { + GL_NEAREST, + GL_CLAMP_TO_EDGE, + } + }; + guint i; + + glGenSamplers (G_N_ELEMENTS (self->sampler_ids), self->sampler_ids); + + for (i = 0; i < G_N_ELEMENTS (self->sampler_ids); i++) + { + glSamplerParameteri (self->sampler_ids[i], GL_TEXTURE_MIN_FILTER, sampler_flags[i].filter); + glSamplerParameteri (self->sampler_ids[i], GL_TEXTURE_MAG_FILTER, sampler_flags[i].filter); + glSamplerParameteri (self->sampler_ids[i], GL_TEXTURE_WRAP_S, sampler_flags[i].wrap); + glSamplerParameteri (self->sampler_ids[i], GL_TEXTURE_WRAP_T, sampler_flags[i].wrap); + } } GskGpuDevice * @@ -104,11 +187,313 @@ gsk_gl_device_get_for_display (GdkDisplay *display, gsk_gpu_device_setup (GSK_GPU_DEVICE (self), display); + context = gdk_display_get_gl_context (display); + + gdk_gl_context_make_current (context); + + self->version_string = gdk_gl_context_get_glsl_version_string (context); + self->api = gdk_gl_context_get_api (context); + gsk_gl_device_setup_samplers (self); + g_object_set_data (G_OBJECT (display), "-gsk-gl-device", self); return GSK_GPU_DEVICE (self); } +static char * +prepend_line_numbers (char *code) +{ + GString *s; + char *p; + int line; + + s = g_string_new (""); + 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; + } + + g_free (code); + + return g_string_free (s, FALSE); +} + +static gboolean +gsk_gl_device_check_shader_error (int shader_id, + GError **error) +{ + GLint status; + GLint log_len; + GLint code_len; + char *log; + char *code; + + glGetShaderiv (shader_id, GL_COMPILE_STATUS, &status); + + if G_LIKELY (status == GL_TRUE) + return TRUE; + + glGetShaderiv (shader_id, GL_INFO_LOG_LENGTH, &log_len); + log = g_malloc0 (log_len + 1); + glGetShaderInfoLog (shader_id, log_len, NULL, log); + + glGetShaderiv (shader_id, GL_SHADER_SOURCE_LENGTH, &code_len); + code = g_malloc0 (code_len + 1); + glGetShaderSource (shader_id, code_len, NULL, code); + + code = prepend_line_numbers (code); + + g_set_error (error, + GDK_GL_ERROR, + GDK_GL_ERROR_COMPILATION_FAILED, + "Compilation failure in shader.\n" + "Source Code:\n" + "%s\n" + "\n" + "Error Message:\n" + "%s\n" + "\n", + code, + log); + + g_free (code); + g_free (log); + + return FALSE; +} + +static void +print_shader_info (const char *prefix, + GLuint 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; + + code = g_malloc0 (code_len + 1); + glGetShaderSource (shader_id, code_len, NULL, code); + + code = prepend_line_numbers (code); + + g_message ("%s %d, %s:\n%s", + prefix, shader_id, + name ? name : "unnamed", + code); + g_free (code); + } + } +} + +static GLuint +gsk_gl_device_load_shader (GskGLDevice *self, + const char *program_name, + GLenum shader_type, + GskGpuShaderClip clip, + GError **error) +{ + GString *preamble; + char *resource_name; + GBytes *bytes; + GLuint shader_id; + + preamble = g_string_new (NULL); + + g_string_append (preamble, self->version_string); + g_string_append (preamble, "\n"); + if (self->api == GDK_GL_API_GLES) + g_string_append (preamble, "#define GSK_GLES 1\n"); + + switch (shader_type) + { + case GL_VERTEX_SHADER: + g_string_append (preamble, "#define GSK_VERTEX_SHADER 1\n"); + break; + + case GL_FRAGMENT_SHADER: + g_string_append (preamble, "#define GSK_FRAGMENT_SHADER 1\n"); + break; + + default: + g_assert_not_reached (); + return 0; + } + + switch (clip) + { + case GSK_GPU_SHADER_CLIP_NONE: + g_string_append (preamble, "#define GSK_SHADER_CLIP GSK_GPU_SHADER_CLIP_NONE\n"); + break; + case GSK_GPU_SHADER_CLIP_RECT: + g_string_append (preamble, "#define GSK_SHADER_CLIP GSK_GPU_SHADER_CLIP_RECT\n"); + break; + case GSK_GPU_SHADER_CLIP_ROUNDED: + g_string_append (preamble, "#define GSK_SHADER_CLIP GSK_GPU_SHADER_CLIP_ROUNDED\n"); + break; + default: + g_assert_not_reached (); + break; + } + + resource_name = g_strconcat ("/org/gtk/libgsk/shaders/gl/", program_name, ".glsl", NULL); + bytes = g_resources_lookup_data (resource_name, 0, error); + g_free (resource_name); + if (bytes == NULL) + return 0; + + shader_id = glCreateShader (shader_type); + + glShaderSource (shader_id, + 2, + (const char *[]) { + preamble->str, + g_bytes_get_data (bytes, NULL), + }, + NULL); + + g_bytes_unref (bytes); + g_string_free (preamble, TRUE); + + glCompileShader (shader_id); + + print_shader_info (shader_type == GL_FRAGMENT_SHADER ? "fragment" : "vertex", shader_id, program_name); + + if (!gsk_gl_device_check_shader_error (shader_id, error)) + { + glDeleteShader (shader_id); + return 0; + } + + return shader_id; +} + +static GLuint +gsk_gl_device_load_program (GskGLDevice *self, + const char *program_name, + GskGpuShaderClip clip, + GError **error) +{ + GLuint vertex_shader_id, fragment_shader_id, program_id; + GLint link_status; + + vertex_shader_id = gsk_gl_device_load_shader (self, program_name, GL_VERTEX_SHADER, clip, error); + if (vertex_shader_id == 0) + return 0; + + fragment_shader_id = gsk_gl_device_load_shader (self, program_name, GL_FRAGMENT_SHADER, clip, error); + if (fragment_shader_id == 0) + return 0; + + program_id = glCreateProgram (); + + glAttachShader (program_id, vertex_shader_id); + glAttachShader (program_id, fragment_shader_id); + + glLinkProgram (program_id); + + glGetProgramiv (program_id, GL_LINK_STATUS, &link_status); + + glDetachShader (program_id, vertex_shader_id); + glDeleteShader (vertex_shader_id); + glDetachShader (program_id, fragment_shader_id); + glDeleteShader (fragment_shader_id); + + if (link_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_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 0; + } + + return program_id; +} + +void +gsk_gl_device_use_program (GskGLDevice *self, + const GskGpuShaderOpClass *op_class, + GskGpuShaderClip clip) +{ + GError *error = NULL; + GLuint program_id; + GLProgramKey key = { + .op_class = op_class, + .clip = clip, + }; + guint i; + + program_id = GPOINTER_TO_UINT (g_hash_table_lookup (self->gl_programs, &key)); + if (program_id) + { + glUseProgram (program_id); + return; + } + + program_id = gsk_gl_device_load_program (self, op_class->shader_name, clip, &error); + if (program_id == 0) + { + g_critical ("Failed to load shader program: %s", error->message); + g_clear_error (&error); + return; + } + + g_hash_table_insert (self->gl_programs, g_memdup (&key, sizeof (GLProgramKey)), GUINT_TO_POINTER (program_id)); + + glUseProgram (program_id); + + for (i = 0; i < 16; i++) + { + char *name = g_strdup_printf ("textures[%u]", i); + glUniform1i (glGetUniformLocation (program_id, name), i); + g_free (name); + } +} + +GLuint +gsk_gl_device_get_sampler_id (GskGLDevice *self, + GskGpuSampler sampler) +{ + g_return_val_if_fail (sampler < G_N_ELEMENTS (self->sampler_ids), 0); + + return self->sampler_ids[sampler]; +} + void gsk_gl_device_find_gl_format (GskGLDevice *self, GdkMemoryFormat format, diff --git a/gsk/gpu/gskgldeviceprivate.h b/gsk/gpu/gskgldeviceprivate.h index fd32c86b36..3b68167051 100644 --- a/gsk/gpu/gskgldeviceprivate.h +++ b/gsk/gpu/gskgldeviceprivate.h @@ -11,6 +11,12 @@ G_DECLARE_FINAL_TYPE (GskGLDevice, gsk_gl_device, GSK, GL_DEVICE, GskGpuDevice) GskGpuDevice * gsk_gl_device_get_for_display (GdkDisplay *display, GError **error); +void gsk_gl_device_use_program (GskGLDevice *self, + const GskGpuShaderOpClass *op_class, + GskGpuShaderClip clip); + +GLuint gsk_gl_device_get_sampler_id (GskGLDevice *self, + GskGpuSampler sampler); void gsk_gl_device_find_gl_format (GskGLDevice *self, GdkMemoryFormat format, diff --git a/gsk/gpu/gskglframe.c b/gsk/gpu/gskglframe.c index 54b0364021..945c7b80e2 100644 --- a/gsk/gpu/gskglframe.c +++ b/gsk/gpu/gskglframe.c @@ -2,14 +2,19 @@ #include "gskglframeprivate.h" +#include "gskgpuglobalsopprivate.h" #include "gskgpuopprivate.h" - -#include "gdk/gdkdisplayprivate.h" -#include "gdk/gdkglcontextprivate.h" +#include "gskglbufferprivate.h" +#include "gskgldeviceprivate.h" struct _GskGLFrame { GskGpuFrame parent_instance; + + GLuint globals_buffer_id; + guint next_texture_slot; + + GHashTable *vaos; }; struct _GskGLFrameClass @@ -26,26 +31,131 @@ gsk_gl_frame_is_busy (GskGpuFrame *frame) } static void -gsk_gl_frame_submit (GskGpuFrame *frame, - GskGpuOp *op) +gsk_gl_frame_setup (GskGpuFrame *frame) { + GskGLFrame *self = GSK_GL_FRAME (frame); + + glGenBuffers (1, &self->globals_buffer_id); +} + +static void +gsk_gl_frame_cleanup (GskGpuFrame *frame) +{ + GskGLFrame *self = GSK_GL_FRAME (frame); + + self->next_texture_slot = 0; + + GSK_GPU_FRAME_CLASS (gsk_gl_frame_parent_class)->cleanup (frame); +} + +static guint32 +gsk_gl_frame_get_image_descriptor (GskGpuFrame *frame, + GskGpuImage *image, + GskGpuSampler sampler) +{ + GskGLFrame *self = GSK_GL_FRAME (frame); + guint32 slot; + + slot = self->next_texture_slot; + self->next_texture_slot = (self->next_texture_slot + 1) % 16; + + return slot; +} + +static GskGpuBuffer * +gsk_gl_frame_create_vertex_buffer (GskGpuFrame *frame, + gsize size) +{ + return gsk_gl_buffer_new (GL_ARRAY_BUFFER, size, GL_WRITE_ONLY); +} + +static void +gsk_gl_frame_submit (GskGpuFrame *frame, + GskGpuBuffer *vertex_buffer, + GskGpuOp *op) +{ + GskGLFrame *self = GSK_GL_FRAME (frame); + + glEnable (GL_DEPTH_TEST); + glDepthFunc (GL_LEQUAL); + + /* Pre-multiplied alpha */ + glEnable (GL_BLEND); + glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation (GL_FUNC_ADD); + + gsk_gl_buffer_bind (GSK_GL_BUFFER (vertex_buffer)); + glBindBufferBase (GL_UNIFORM_BUFFER, 0, self->globals_buffer_id); + glBufferData (GL_UNIFORM_BUFFER, + sizeof (GskGpuGlobalsInstance), + NULL, + GL_DYNAMIC_DRAW); + while (op) { op = gsk_gpu_op_gl_command (op, frame); } } +static void +gsk_gl_frame_finalize (GObject *object) +{ + GskGLFrame *self = GSK_GL_FRAME (object); + + g_hash_table_unref (self->vaos); + glDeleteBuffers (1, &self->globals_buffer_id); + + G_OBJECT_CLASS (gsk_gl_frame_parent_class)->finalize (object); +} + static void gsk_gl_frame_class_init (GskGLFrameClass *klass) { GskGpuFrameClass *gpu_frame_class = GSK_GPU_FRAME_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); gpu_frame_class->is_busy = gsk_gl_frame_is_busy; + gpu_frame_class->setup = gsk_gl_frame_setup; + gpu_frame_class->cleanup = gsk_gl_frame_cleanup; + gpu_frame_class->get_image_descriptor = gsk_gl_frame_get_image_descriptor; + gpu_frame_class->create_vertex_buffer = gsk_gl_frame_create_vertex_buffer; gpu_frame_class->submit = gsk_gl_frame_submit; + + object_class->finalize = gsk_gl_frame_finalize; +} + +static void +free_vao (gpointer vao) +{ + glDeleteVertexArrays (1, (GLuint[1]) { GPOINTER_TO_UINT (vao) }); } static void gsk_gl_frame_init (GskGLFrame *self) { + self->vaos = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, free_vao); +} + +void +gsk_gl_frame_use_program (GskGLFrame *self, + const GskGpuShaderOpClass *op_class, + GskGpuShaderClip clip) +{ + GLuint vao; + + gsk_gl_device_use_program (GSK_GL_DEVICE (gsk_gpu_frame_get_device (GSK_GPU_FRAME (self))), + op_class, + clip); + + vao = GPOINTER_TO_UINT (g_hash_table_lookup (self->vaos, op_class)); + if (vao) + { + glBindVertexArray(vao); + return; + } + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + g_hash_table_insert (self->vaos, (gpointer) op_class, GUINT_TO_POINTER (vao)); } diff --git a/gsk/gpu/gskglframeprivate.h b/gsk/gpu/gskglframeprivate.h index a4d54b6f42..367187327c 100644 --- a/gsk/gpu/gskglframeprivate.h +++ b/gsk/gpu/gskglframeprivate.h @@ -8,5 +8,8 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (GskGLFrame, gsk_gl_frame, GSK, GL_FRAME, GskGpuFrame) +void gsk_gl_frame_use_program (GskGLFrame *self, + const GskGpuShaderOpClass *op_class, + GskGpuShaderClip clip); G_END_DECLS diff --git a/gsk/gpu/gskgpublitop.c b/gsk/gpu/gskgpublitop.c index 82f421e337..8a6e160ebd 100644 --- a/gsk/gpu/gskgpublitop.c +++ b/gsk/gpu/gskgpublitop.c @@ -31,9 +31,10 @@ gsk_gpu_blit_op_finish (GskGpuOp *op) } static void -gsk_gpu_blit_op_print (GskGpuOp *op, - GString *string, - guint indent) +gsk_gpu_blit_op_print (GskGpuOp *op, + GskGpuFrame *frame, + GString *string, + guint indent) { GskGpuBlitOp *self = (GskGpuBlitOp *) op; @@ -43,16 +44,11 @@ gsk_gpu_blit_op_print (GskGpuOp *op, } #ifdef GDK_RENDERING_VULKAN -static void -gsk_gpu_blit_op_vk_reserve_descriptor_sets (GskGpuOp *op, - GskGpuFrame *frame) -{ -} - static GskGpuOp * gsk_gpu_blit_op_vk_command (GskGpuOp *op, GskGpuFrame *frame, VkRenderPass render_pass, + VkFormat format, VkCommandBuffer command_buffer) { GskGpuBlitOp *self = (GskGpuBlitOp *) op; @@ -195,7 +191,6 @@ static const GskGpuOpClass GSK_GPU_BLIT_OP_CLASS = { gsk_gpu_blit_op_finish, gsk_gpu_blit_op_print, #ifdef GDK_RENDERING_VULKAN - gsk_gpu_blit_op_vk_reserve_descriptor_sets, gsk_gpu_blit_op_vk_command, #endif gsk_gpu_blit_op_gl_command diff --git a/gsk/gpu/gskgpubuffer.c b/gsk/gpu/gskgpubuffer.c new file mode 100644 index 0000000000..218f5a448a --- /dev/null +++ b/gsk/gpu/gskgpubuffer.c @@ -0,0 +1,52 @@ +#include "config.h" + +#include "gskgpubufferprivate.h" + +typedef struct _GskGpuBufferPrivate GskGpuBufferPrivate; + +struct _GskGpuBufferPrivate +{ + gsize size; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (GskGpuBuffer, gsk_gpu_buffer, G_TYPE_OBJECT) + +static void +gsk_gpu_buffer_class_init (GskGpuBufferClass *klass) +{ +} + +static void +gsk_gpu_buffer_init (GskGpuBuffer *self) +{ +} + +void +gsk_gpu_buffer_setup (GskGpuBuffer *self, + gsize size) +{ + GskGpuBufferPrivate *priv = gsk_gpu_buffer_get_instance_private (self); + + priv->size = size; +} + +gsize +gsk_gpu_buffer_get_size (GskGpuBuffer *self) +{ + GskGpuBufferPrivate *priv = gsk_gpu_buffer_get_instance_private (self); + + return priv->size; +} + +guchar * +gsk_gpu_buffer_map (GskGpuBuffer *self) +{ + return GSK_GPU_BUFFER_GET_CLASS (self)->map (self); +} + +void +gsk_gpu_buffer_unmap (GskGpuBuffer *self) +{ + GSK_GPU_BUFFER_GET_CLASS (self)->unmap (self); +} + diff --git a/gsk/gpu/gskgpubufferprivate.h b/gsk/gpu/gskgpubufferprivate.h new file mode 100644 index 0000000000..143e5a37d0 --- /dev/null +++ b/gsk/gpu/gskgpubufferprivate.h @@ -0,0 +1,42 @@ +#pragma once + +#include "gskgputypesprivate.h" + +G_BEGIN_DECLS + +#define GSK_TYPE_GPU_BUFFER (gsk_gpu_buffer_get_type ()) +#define GSK_GPU_BUFFER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSK_TYPE_GPU_BUFFER, GskGpuBuffer)) +#define GSK_GPU_BUFFER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GSK_TYPE_GPU_BUFFER, GskGpuBufferClass)) +#define GSK_IS_GPU_BUFFER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSK_TYPE_GPU_BUFFER)) +#define GSK_IS_GPU_BUFFER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSK_TYPE_GPU_BUFFER)) +#define GSK_GPU_BUFFER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSK_TYPE_GPU_BUFFER, GskGpuBufferClass)) + +typedef struct _GskGpuBufferClass GskGpuBufferClass; + +struct _GskGpuBuffer +{ + GObject parent_instance; +}; + +struct _GskGpuBufferClass +{ + GObjectClass parent_class; + + guchar * (* map) (GskGpuBuffer *self); + void (* unmap) (GskGpuBuffer *self); +}; + +GType gsk_gpu_buffer_get_type (void) G_GNUC_CONST; + +void gsk_gpu_buffer_setup (GskGpuBuffer *self, + gsize size); + +gsize gsk_gpu_buffer_get_size (GskGpuBuffer *self); + +guchar * gsk_gpu_buffer_map (GskGpuBuffer *self); +void gsk_gpu_buffer_unmap (GskGpuBuffer *self); + + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskGpuBuffer, g_object_unref) + +G_END_DECLS diff --git a/gsk/gpu/gskgpuclip.c b/gsk/gpu/gskgpuclip.c new file mode 100644 index 0000000000..838ffc528c --- /dev/null +++ b/gsk/gpu/gskgpuclip.c @@ -0,0 +1,293 @@ +#include "config.h" + +#include "gskgpuclipprivate.h" + +#include "gskrectprivate.h" +#include "gskroundedrectprivate.h" +#include "gsktransform.h" + +void +gsk_gpu_clip_init_empty (GskGpuClip *clip, + const graphene_rect_t *rect) +{ + clip->type = GSK_GPU_CLIP_NONE; + gsk_rounded_rect_init_from_rect (&clip->rect, rect, 0); +} + +void +gsk_gpu_clip_init_rect (GskGpuClip *clip, + const graphene_rect_t *rect) +{ + clip->type = GSK_GPU_CLIP_RECT; + gsk_rounded_rect_init_from_rect (&clip->rect, rect, 0); +} + +void +gsk_gpu_clip_init_copy (GskGpuClip *self, + const GskGpuClip *src) +{ + self->type = src->type; + gsk_rounded_rect_init_copy (&self->rect, &src->rect); +} + +static gboolean +gsk_gpu_clip_init_after_intersection (GskGpuClip *self, + GskRoundedRectIntersection res) +{ + if (res == GSK_INTERSECTION_NOT_REPRESENTABLE) + return FALSE; + + if (res == GSK_INTERSECTION_EMPTY) + self->type = GSK_GPU_CLIP_ALL_CLIPPED; + else if (gsk_rounded_rect_is_rectilinear (&self->rect)) + self->type = GSK_GPU_CLIP_RECT; + else + self->type = GSK_GPU_CLIP_ROUNDED; + + return TRUE; +} + +gboolean +gsk_gpu_clip_intersect_rect (GskGpuClip *dest, + const GskGpuClip *src, + const graphene_rect_t *rect) +{ + GskRoundedRectIntersection res; + + if (gsk_rect_contains_rect (rect, &src->rect.bounds)) + { + gsk_gpu_clip_init_copy (dest, src); + return TRUE; + } + if (!gsk_rect_intersects (rect, &src->rect.bounds)) + { + dest->type = GSK_GPU_CLIP_ALL_CLIPPED; + return TRUE; + } + + switch (src->type) + { + case GSK_GPU_CLIP_ALL_CLIPPED: + dest->type = GSK_GPU_CLIP_ALL_CLIPPED; + break; + + case GSK_GPU_CLIP_NONE: + gsk_gpu_clip_init_copy (dest, src); + if (gsk_rect_intersection (&dest->rect.bounds, rect, &dest->rect.bounds)) + dest->type = GSK_GPU_CLIP_RECT; + else + dest->type = GSK_GPU_CLIP_ALL_CLIPPED; + break; + + case GSK_GPU_CLIP_RECT: + gsk_gpu_clip_init_copy (dest, src); + if (!gsk_rect_intersection (&dest->rect.bounds, rect, &dest->rect.bounds)) + dest->type = GSK_GPU_CLIP_ALL_CLIPPED; + break; + + case GSK_GPU_CLIP_ROUNDED: + res = gsk_rounded_rect_intersect_with_rect (&src->rect, rect, &dest->rect); + if (!gsk_gpu_clip_init_after_intersection (dest, res)) + return FALSE; + break; + + default: + g_assert_not_reached (); + return FALSE; + } + + return TRUE; +} + +gboolean +gsk_gpu_clip_intersect_rounded_rect (GskGpuClip *dest, + const GskGpuClip *src, + const GskRoundedRect *rounded) +{ + GskRoundedRectIntersection res; + + if (gsk_rounded_rect_contains_rect (rounded, &src->rect.bounds)) + { + gsk_gpu_clip_init_copy (dest, src); + return TRUE; + } + if (!gsk_rect_intersects (&rounded->bounds, &src->rect.bounds)) + { + dest->type = GSK_GPU_CLIP_ALL_CLIPPED; + return TRUE; + } + + switch (src->type) + { + case GSK_GPU_CLIP_ALL_CLIPPED: + dest->type = GSK_GPU_CLIP_ALL_CLIPPED; + break; + + case GSK_GPU_CLIP_NONE: + case GSK_GPU_CLIP_RECT: + res = gsk_rounded_rect_intersect_with_rect (rounded, &src->rect.bounds, &dest->rect); + if (!gsk_gpu_clip_init_after_intersection (dest, res)) + return FALSE; + break; + + case GSK_GPU_CLIP_ROUNDED: + res = gsk_rounded_rect_intersection (&src->rect, rounded, &dest->rect); + if (!gsk_gpu_clip_init_after_intersection (dest, res)) + return FALSE; + break; + + default: + g_assert_not_reached (); + return FALSE; + } + + return TRUE; +} + +void +gsk_gpu_clip_scale (GskGpuClip *dest, + const GskGpuClip *src, + float scale_x, + float scale_y) +{ + dest->type = src->type; + gsk_rounded_rect_scale_affine (&dest->rect, + &src->rect, + 1.0f / scale_x, 1.0f / scale_y, + 0, 0); +} + +gboolean +gsk_gpu_clip_transform (GskGpuClip *dest, + const GskGpuClip *src, + GskTransform *transform, + const graphene_rect_t *viewport) +{ + switch (src->type) + { + default: + g_assert_not_reached(); + return FALSE; + + case GSK_GPU_CLIP_ALL_CLIPPED: + gsk_gpu_clip_init_copy (dest, src); + return TRUE; + + case GSK_GPU_CLIP_NONE: + case GSK_GPU_CLIP_RECT: + case GSK_GPU_CLIP_ROUNDED: + switch (gsk_transform_get_category (transform)) + { + case GSK_TRANSFORM_CATEGORY_IDENTITY: + gsk_gpu_clip_init_copy (dest, src); + return TRUE; + + case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE: + { + float dx, dy; + + gsk_transform_to_translate (transform, &dx, &dy); + gsk_gpu_clip_init_copy (dest, src); + dest->rect.bounds.origin.x -= dx; + dest->rect.bounds.origin.y -= dy; + } + return TRUE; + + case GSK_TRANSFORM_CATEGORY_2D_AFFINE: + { + float dx, dy, scale_x, scale_y; + + gsk_transform_to_affine (transform, &scale_x, &scale_y, &dx, &dy); + scale_x = 1. / scale_x; + scale_y = 1. / scale_y; + gsk_gpu_clip_init_copy (dest, src); + dest->rect.bounds.origin.x = (dest->rect.bounds.origin.x - dx) * scale_x; + dest->rect.bounds.origin.y = (dest->rect.bounds.origin.y - dy) * scale_y; + dest->rect.bounds.size.width *= scale_x; + dest->rect.bounds.size.height *= scale_y; + if (src->type == GSK_GPU_CLIP_ROUNDED) + { + dest->rect.corner[0].width *= scale_x; + dest->rect.corner[0].height *= scale_y; + dest->rect.corner[1].width *= scale_x; + dest->rect.corner[1].height *= scale_y; + dest->rect.corner[2].width *= scale_x; + dest->rect.corner[2].height *= scale_y; + dest->rect.corner[3].width *= scale_x; + dest->rect.corner[3].height *= scale_y; + } + } + return TRUE; + + case GSK_TRANSFORM_CATEGORY_UNKNOWN: + case GSK_TRANSFORM_CATEGORY_ANY: + case GSK_TRANSFORM_CATEGORY_3D: + case GSK_TRANSFORM_CATEGORY_2D: + default: + return FALSE; + } + } +} + +gboolean +gsk_gpu_clip_may_intersect_rect (const GskGpuClip *self, + const graphene_point_t *offset, + const graphene_rect_t *rect) +{ + graphene_rect_t r = *rect; + r.origin.x += offset->x; + r.origin.y += offset->y; + + switch (self->type) + { + default: + g_assert_not_reached(); + case GSK_GPU_CLIP_ALL_CLIPPED: + return FALSE; + + case GSK_GPU_CLIP_NONE: + case GSK_GPU_CLIP_RECT: + case GSK_GPU_CLIP_ROUNDED: + return gsk_rect_intersects (&self->rect.bounds, &r); + } +} + +gboolean +gsk_gpu_clip_contains_rect (const GskGpuClip *self, + const graphene_point_t *offset, + const graphene_rect_t *rect) +{ + graphene_rect_t r = *rect; + r.origin.x += offset->x; + r.origin.y += offset->y; + + switch (self->type) + { + default: + g_assert_not_reached(); + case GSK_GPU_CLIP_ALL_CLIPPED: + return FALSE; + + case GSK_GPU_CLIP_NONE: + case GSK_GPU_CLIP_RECT: + return gsk_rect_contains_rect (&self->rect.bounds, &r); + + case GSK_GPU_CLIP_ROUNDED: + return gsk_rounded_rect_contains_rect (&self->rect, &r); + } +} + +GskGpuShaderClip +gsk_gpu_clip_get_shader_clip (const GskGpuClip *self, + const graphene_point_t *offset, + const graphene_rect_t *rect) +{ + if (self->type == GSK_GPU_CLIP_NONE || + gsk_gpu_clip_contains_rect (self, offset, rect)) + return GSK_GPU_SHADER_CLIP_NONE; + else if (self->type == GSK_GPU_CLIP_RECT) + return GSK_GPU_SHADER_CLIP_RECT; + else + return GSK_GPU_SHADER_CLIP_ROUNDED; +} + diff --git a/gsk/gpu/gskgpuclipprivate.h b/gsk/gpu/gskgpuclipprivate.h new file mode 100644 index 0000000000..24a8e9a278 --- /dev/null +++ b/gsk/gpu/gskgpuclipprivate.h @@ -0,0 +1,68 @@ +#pragma once + +#include "gskgputypesprivate.h" + +#include +#include +#include + +G_BEGIN_DECLS + +typedef enum { + /* The whole area is clipped, no drawing is necessary. + * This can't be handled by return values because for return + * values we return if clips could even be computed. + */ + GSK_GPU_CLIP_ALL_CLIPPED, + /* No clipping is necessary, but the clip rect is set + * to the actual bounds of the underlying framebuffer + */ + GSK_GPU_CLIP_NONE, + /* The clip is a rectangular area */ + GSK_GPU_CLIP_RECT, + /* The clip is a rounded rectangle */ + GSK_GPU_CLIP_ROUNDED +} GskGpuClipComplexity; + +typedef struct _GskGpuClip GskGpuClip; + +struct _GskGpuClip +{ + GskGpuClipComplexity type; + GskRoundedRect rect; +}; + +void gsk_gpu_clip_init_empty (GskGpuClip *clip, + const graphene_rect_t *rect); +void gsk_gpu_clip_init_copy (GskGpuClip *self, + const GskGpuClip *src); +void gsk_gpu_clip_init_rect (GskGpuClip *clip, + const graphene_rect_t *rect); + +gboolean gsk_gpu_clip_intersect_rect (GskGpuClip *dest, + const GskGpuClip *src, + const graphene_rect_t *rect) G_GNUC_WARN_UNUSED_RESULT; +gboolean gsk_gpu_clip_intersect_rounded_rect (GskGpuClip *dest, + const GskGpuClip *src, + const GskRoundedRect *rounded) G_GNUC_WARN_UNUSED_RESULT; +void gsk_gpu_clip_scale (GskGpuClip *dest, + const GskGpuClip *src, + float scale_x, + float scale_y); +gboolean gsk_gpu_clip_transform (GskGpuClip *dest, + const GskGpuClip *src, + GskTransform *transform, + const graphene_rect_t *viewport) G_GNUC_WARN_UNUSED_RESULT; + +gboolean gsk_gpu_clip_contains_rect (const GskGpuClip *self, + const graphene_point_t *offset, + const graphene_rect_t *rect) G_GNUC_WARN_UNUSED_RESULT; +gboolean gsk_gpu_clip_may_intersect_rect (const GskGpuClip *self, + const graphene_point_t *offset, + const graphene_rect_t *rect) G_GNUC_WARN_UNUSED_RESULT; +GskGpuShaderClip gsk_gpu_clip_get_shader_clip (const GskGpuClip *self, + const graphene_point_t *offset, + const graphene_rect_t *rect); + +G_END_DECLS + diff --git a/gsk/gpu/gskgpudownloadop.c b/gsk/gpu/gskgpudownloadop.c index bb39f030d7..702c22847b 100644 --- a/gsk/gpu/gskgpudownloadop.c +++ b/gsk/gpu/gskgpudownloadop.c @@ -27,9 +27,7 @@ struct _GskGpuDownloadOp gpointer user_data; GdkTexture *texture; -#ifdef GDK_RENDERING_VULKAN - GskVulkanBuffer *buffer; -#endif + GskGpuBuffer *buffer; }; static void @@ -44,15 +42,14 @@ gsk_gpu_download_op_finish (GskGpuOp *op) g_object_unref (self->texture); g_object_unref (self->image); -#ifdef GDK_RENDERING_VULKAN - g_clear_pointer (&self->buffer, gsk_vulkan_buffer_free); -#endif + g_clear_object (&self->buffer); } static void -gsk_gpu_download_op_print (GskGpuOp *op, - GString *string, - guint indent) +gsk_gpu_download_op_print (GskGpuOp *op, + GskGpuFrame *frame, + GString *string, + guint indent) { GskGpuDownloadOp *self = (GskGpuDownloadOp *) op; @@ -62,12 +59,6 @@ gsk_gpu_download_op_print (GskGpuOp *op, } #ifdef GDK_RENDERING_VULKAN -static void -gsk_gpu_download_op_vk_reserve_descriptor_sets (GskGpuOp *op, - GskGpuFrame *frame) -{ -} - static void gsk_gpu_download_op_vk_create (GskGpuDownloadOp *self) { @@ -76,7 +67,7 @@ gsk_gpu_download_op_vk_create (GskGpuDownloadOp *self) gsize width, height, stride; GdkMemoryFormat format; - data = gsk_vulkan_buffer_get_data (self->buffer); + data = gsk_gpu_buffer_map (self->buffer); width = gsk_gpu_image_get_width (self->image); height = gsk_gpu_image_get_height (self->image); format = gsk_gpu_image_get_format (self->image); @@ -88,12 +79,14 @@ gsk_gpu_download_op_vk_create (GskGpuDownloadOp *self) bytes, stride); g_bytes_unref (bytes); + gsk_gpu_buffer_unmap (self->buffer); } static GskGpuOp * gsk_gpu_download_op_vk_command (GskGpuOp *op, GskGpuFrame *frame, VkRenderPass render_pass, + VkFormat format, VkCommandBuffer command_buffer) { GskGpuDownloadOp *self = (GskGpuDownloadOp *) op; @@ -102,9 +95,8 @@ gsk_gpu_download_op_vk_command (GskGpuOp *op, width = gsk_gpu_image_get_width (self->image); height = gsk_gpu_image_get_height (self->image); stride = width * gdk_memory_format_bytes_per_pixel (gsk_gpu_image_get_format (self->image)); - self->buffer = gsk_vulkan_buffer_new_map (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame)), - height * stride, - GSK_VULKAN_READ); + self->buffer = gsk_vulkan_buffer_new_read (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame)), + height * stride); gsk_vulkan_image_transition (GSK_VULKAN_IMAGE (self->image), command_buffer, @@ -115,7 +107,7 @@ gsk_gpu_download_op_vk_command (GskGpuOp *op, vkCmdCopyImageToBuffer (command_buffer, gsk_vulkan_image_get_vk_image (GSK_VULKAN_IMAGE (self->image)), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - gsk_vulkan_buffer_get_buffer (self->buffer), + gsk_vulkan_buffer_get_vk_buffer (GSK_VULKAN_BUFFER (self->buffer)), 1, (VkBufferImageCopy[1]) { { @@ -152,7 +144,7 @@ gsk_gpu_download_op_vk_command (GskGpuOp *op, .dstAccessMask = VK_ACCESS_HOST_READ_BIT, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .buffer = gsk_vulkan_buffer_get_buffer (self->buffer), + .buffer = gsk_vulkan_buffer_get_vk_buffer (GSK_VULKAN_BUFFER (self->buffer)), .offset = 0, .size = VK_WHOLE_SIZE, }, @@ -228,7 +220,6 @@ static const GskGpuOpClass GSK_GPU_DOWNLOAD_OP_CLASS = { gsk_gpu_download_op_finish, gsk_gpu_download_op_print, #ifdef GDK_RENDERING_VULKAN - gsk_gpu_download_op_vk_reserve_descriptor_sets, gsk_gpu_download_op_vk_command, #endif gsk_gpu_download_op_gl_command diff --git a/gsk/gpu/gskgpuframe.c b/gsk/gpu/gskgpuframe.c index 98e9b05d7a..d755f55c9a 100644 --- a/gsk/gpu/gskgpuframe.c +++ b/gsk/gpu/gskgpuframe.c @@ -2,16 +2,20 @@ #include "gskgpuframeprivate.h" +#include "gskgpubufferprivate.h" #include "gskgpudeviceprivate.h" #include "gskgpudownloadopprivate.h" #include "gskgpuimageprivate.h" #include "gskgpunodeprocessorprivate.h" #include "gskgpuopprivate.h" #include "gskgpurendererprivate.h" +#include "gskgpurenderpassopprivate.h" #include "gskdebugprivate.h" #include "gskrendererprivate.h" +#define DEFAULT_VERTEX_BUFFER_SIZE 128 * 1024 + #define GDK_ARRAY_NAME gsk_gpu_ops #define GDK_ARRAY_TYPE_NAME GskGpuOps #define GDK_ARRAY_ELEMENT_TYPE guchar @@ -27,6 +31,10 @@ struct _GskGpuFramePrivate GskGpuOps ops; GskGpuOp *first_op; + + GskGpuBuffer *vertex_buffer; + guchar *vertex_buffer_data; + gsize vertex_buffer_used; }; G_DEFINE_TYPE_WITH_PRIVATE (GskGpuFrame, gsk_gpu_frame, G_TYPE_OBJECT) @@ -76,6 +84,8 @@ gsk_gpu_frame_finalize (GObject *object) gsk_gpu_ops_clear (&priv->ops); + g_clear_object (&priv->vertex_buffer); + g_object_unref (priv->device); G_OBJECT_CLASS (gsk_gpu_frame_parent_class)->finalize (object); @@ -148,7 +158,7 @@ gsk_gpu_frame_verbose_print (GskGpuFrame *self, { if (op->op_class->stage == GSK_GPU_STAGE_END_PASS) indent--; - gsk_gpu_op_print (op, string, indent); + gsk_gpu_op_print (op, self, string, indent); if (op->op_class->stage == GSK_GPU_STAGE_BEGIN_PASS) indent++; } @@ -295,6 +305,72 @@ gsk_gpu_frame_alloc_op (GskGpuFrame *self, return gsk_gpu_ops_index (&priv->ops, pos); } +static GskGpuBuffer * +gsk_gpu_frame_create_vertex_buffer (GskGpuFrame *self, + gsize size) +{ + return GSK_GPU_FRAME_GET_CLASS (self)->create_vertex_buffer (self, size); +} + +static inline gsize +round_up (gsize number, gsize divisor) +{ + return (number + divisor - 1) / divisor * divisor; +} + +gsize +gsk_gpu_frame_reserve_vertex_data (GskGpuFrame *self, + gsize size) +{ + GskGpuFramePrivate *priv = gsk_gpu_frame_get_instance_private (self); + gsize size_needed; + + if (priv->vertex_buffer == NULL) + priv->vertex_buffer = gsk_gpu_frame_create_vertex_buffer (self, DEFAULT_VERTEX_BUFFER_SIZE); + + size_needed = round_up (priv->vertex_buffer_used, size) + size; + + if (gsk_gpu_buffer_get_size (priv->vertex_buffer) < size_needed) + { + gsize old_size = gsk_gpu_buffer_get_size (priv->vertex_buffer); + GskGpuBuffer *new_buffer = gsk_gpu_frame_create_vertex_buffer (self, old_size * 2); + guchar *new_data = gsk_gpu_buffer_map (new_buffer); + + if (priv->vertex_buffer_data) + { + memcpy (new_data, priv->vertex_buffer_data, old_size); + gsk_gpu_buffer_unmap (priv->vertex_buffer); + } + g_object_unref (priv->vertex_buffer); + priv->vertex_buffer = new_buffer; + priv->vertex_buffer_data = new_data; + } + + priv->vertex_buffer_used = size_needed; + + return size_needed - size; +} + +guchar * +gsk_gpu_frame_get_vertex_data (GskGpuFrame *self, + gsize offset) +{ + GskGpuFramePrivate *priv = gsk_gpu_frame_get_instance_private (self); + + if (priv->vertex_buffer_data == NULL) + priv->vertex_buffer_data = gsk_gpu_buffer_map (priv->vertex_buffer); + + return priv->vertex_buffer_data + offset; +} + +guint32 +gsk_gpu_frame_get_image_descriptor (GskGpuFrame *self, + GskGpuImage *image, + GskGpuSampler sampler) +{ + return GSK_GPU_FRAME_GET_CLASS (self)->get_image_descriptor (self, image, sampler); +} + gboolean gsk_gpu_frame_is_busy (GskGpuFrame *self) { @@ -333,13 +409,10 @@ gsk_gpu_frame_record (GskGpuFrame *self, }; } -#if 0 gsk_gpu_render_pass_begin_op (self, target, &extents, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); -#endif + GSK_RENDER_PASS_PRESENT); gsk_gpu_node_processor_process (self, target, @@ -347,11 +420,9 @@ gsk_gpu_frame_record (GskGpuFrame *self, node, viewport); -#if 0 gsk_gpu_render_pass_end_op (self, target, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); -#endif + GSK_RENDER_PASS_PRESENT); if (texture) gsk_gpu_download_op (self, target, copy_texture, texture); @@ -367,7 +438,16 @@ gsk_gpu_frame_submit (GskGpuFrame *self) gsk_gpu_frame_sort_ops (self); gsk_gpu_frame_verbose_print (self, "after sort"); - GSK_GPU_FRAME_GET_CLASS (self)->submit (self, priv->first_op); + if (priv->vertex_buffer) + { + gsk_gpu_buffer_unmap (priv->vertex_buffer); + priv->vertex_buffer_data = NULL; + priv->vertex_buffer_used = 0; + } + + GSK_GPU_FRAME_GET_CLASS (self)->submit (self, + priv->vertex_buffer, + priv->first_op); } void diff --git a/gsk/gpu/gskgpuframeprivate.h b/gsk/gpu/gskgpuframeprivate.h index 9b610f3c27..c097b3d064 100644 --- a/gsk/gpu/gskgpuframeprivate.h +++ b/gsk/gpu/gskgpuframeprivate.h @@ -26,7 +26,13 @@ struct _GskGpuFrameClass gboolean (* is_busy) (GskGpuFrame *self); void (* setup) (GskGpuFrame *self); void (* cleanup) (GskGpuFrame *self); + guint32 (* get_image_descriptor) (GskGpuFrame *self, + GskGpuImage *image, + GskGpuSampler sampler); + GskGpuBuffer * (* create_vertex_buffer) (GskGpuFrame *self, + gsize size); void (* submit) (GskGpuFrame *self, + GskGpuBuffer *vertex_buffer, GskGpuOp *op); }; @@ -37,11 +43,18 @@ void gsk_gpu_frame_setup (GskGpuF GskGpuRenderer *renderer, GskGpuDevice *device); -GdkDrawContext * gsk_gpu_frame_get_context (GskGpuFrame *self); -GskGpuDevice * gsk_gpu_frame_get_device (GskGpuFrame *self); +GdkDrawContext * gsk_gpu_frame_get_context (GskGpuFrame *self) G_GNUC_PURE; +GskGpuDevice * gsk_gpu_frame_get_device (GskGpuFrame *self) G_GNUC_PURE; gpointer gsk_gpu_frame_alloc_op (GskGpuFrame *self, gsize size); +gsize gsk_gpu_frame_reserve_vertex_data (GskGpuFrame *self, + gsize size); +guchar * gsk_gpu_frame_get_vertex_data (GskGpuFrame *self, + gsize offset); +guint32 gsk_gpu_frame_get_image_descriptor (GskGpuFrame *self, + GskGpuImage *image, + GskGpuSampler sampler); gboolean gsk_gpu_frame_is_busy (GskGpuFrame *self); diff --git a/gsk/gpu/gskgpuglobalsop.c b/gsk/gpu/gskgpuglobalsop.c new file mode 100644 index 0000000000..b9dcd3b504 --- /dev/null +++ b/gsk/gpu/gskgpuglobalsop.c @@ -0,0 +1,97 @@ +#include "config.h" + +#include "gskgpuglobalsopprivate.h" + +#include "gskgpuframeprivate.h" +#include "gskgpuprintprivate.h" +#include "gskroundedrectprivate.h" +#ifdef GDK_RENDERING_VULKAN +#include "gskvulkandeviceprivate.h" +#endif + +typedef struct _GskGpuGlobalsOp GskGpuGlobalsOp; + +struct _GskGpuGlobalsOp +{ + GskGpuOp op; + + GskGpuGlobalsInstance instance; +}; + +static void +gsk_gpu_globals_op_finish (GskGpuOp *op) +{ +} + +static void +gsk_gpu_globals_op_print (GskGpuOp *op, + GskGpuFrame *frame, + GString *string, + guint indent) +{ + gsk_gpu_print_op (string, indent, "globals"); + gsk_gpu_print_newline (string); +} + +#ifdef GDK_RENDERING_VULKAN +static GskGpuOp * +gsk_gpu_globals_op_vk_command (GskGpuOp *op, + GskGpuFrame *frame, + VkRenderPass render_pass, + VkFormat format, + VkCommandBuffer command_buffer) +{ + GskGpuGlobalsOp *self = (GskGpuGlobalsOp *) op; + + vkCmdPushConstants (command_buffer, + gsk_vulkan_device_get_vk_pipeline_layout (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame))), + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + 0, + sizeof (self->instance), + &self->instance); + + return op->next; +} +#endif + +static GskGpuOp * +gsk_gpu_globals_op_gl_command (GskGpuOp *op, + GskGpuFrame *frame) +{ + GskGpuGlobalsOp *self = (GskGpuGlobalsOp *) op; + + /* the GskGLFrame makes sure the uniform buffer points to the globals */ + /* FIXME: Does it matter if we glBufferData() or glSubBufferData() here? */ + glBufferSubData (GL_UNIFORM_BUFFER, + 0, + sizeof (self->instance), + &self->instance); + + return op->next; +} + +static const GskGpuOpClass GSK_GPU_GLOBALS_OP_CLASS = { + GSK_GPU_OP_SIZE (GskGpuGlobalsOp), + GSK_GPU_STAGE_COMMAND, + gsk_gpu_globals_op_finish, + gsk_gpu_globals_op_print, +#ifdef GDK_RENDERING_VULKAN + gsk_gpu_globals_op_vk_command, +#endif + gsk_gpu_globals_op_gl_command +}; + +void +gsk_gpu_globals_op (GskGpuFrame *frame, + const graphene_vec2_t *scale, + const graphene_matrix_t *mvp, + const GskRoundedRect *clip) +{ + GskGpuGlobalsOp *self; + + self = (GskGpuGlobalsOp *) gsk_gpu_op_alloc (frame, &GSK_GPU_GLOBALS_OP_CLASS); + + graphene_matrix_to_float (mvp, self->instance.mvp); + gsk_rounded_rect_to_float (clip, graphene_point_zero (), self->instance.clip); + graphene_vec2_to_float (scale, self->instance.scale); +} diff --git a/gsk/gpu/gskgpuglobalsopprivate.h b/gsk/gpu/gskgpuglobalsopprivate.h new file mode 100644 index 0000000000..6dcae7d833 --- /dev/null +++ b/gsk/gpu/gskgpuglobalsopprivate.h @@ -0,0 +1,26 @@ +#pragma once + +#include "gskgpuopprivate.h" + +#include +#include + +G_BEGIN_DECLS + +typedef struct _GskGpuGlobalsInstance GskGpuGlobalsInstance; + +struct _GskGpuGlobalsInstance +{ + float mvp[16]; + float clip[12]; + float scale[2]; +}; + +void gsk_gpu_globals_op (GskGpuFrame *frame, + const graphene_vec2_t *scale, + const graphene_matrix_t *mvp, + const GskRoundedRect *clip); + + +G_END_DECLS + diff --git a/gsk/gpu/gskgpunodeprocessor.c b/gsk/gpu/gskgpunodeprocessor.c index fefd115ff5..21cf4c763e 100644 --- a/gsk/gpu/gskgpunodeprocessor.c +++ b/gsk/gpu/gskgpunodeprocessor.c @@ -2,36 +2,50 @@ #include "gskgpunodeprocessorprivate.h" -#include "gskgpublitopprivate.h" +#include "gskgpuclipprivate.h" +#include "gskgpuframeprivate.h" +#include "gskgpuglobalsopprivate.h" #include "gskgpuimageprivate.h" +#include "gskgputextureopprivate.h" #include "gskgpuuploadopprivate.h" +#include "gskdebugprivate.h" #include "gskrendernodeprivate.h" +#include "gskroundedrectprivate.h" +#include "gsktransformprivate.h" #define ORTHO_NEAR_PLANE -10000 #define ORTHO_FAR_PLANE 10000 typedef struct _GskGpuNodeProcessor GskGpuNodeProcessor; +typedef enum { + GSK_GPU_GLOBAL_MATRIX = (1 << 0), + GSK_GPU_GLOBAL_SCALE = (1 << 1), + GSK_GPU_GLOBAL_CLIP = (1 << 2) +} GskGpuGlobals; + struct _GskGpuNodeProcessor { GskGpuFrame *frame; cairo_rectangle_int_t scissor; graphene_point_t offset; + graphene_matrix_t projection; graphene_vec2_t scale; GskTransform *modelview; - graphene_matrix_t projection; - /* GskGpuClip clip; */ + GskGpuClip clip; + + GskGpuGlobals pending_globals; }; static void gsk_gpu_node_processor_add_node (GskGpuNodeProcessor *self, - GskGpuImage *target, GskRenderNode *node); static void gsk_gpu_node_processor_finish (GskGpuNodeProcessor *self) { + g_clear_pointer (&self->modelview, gsk_transform_unref); } static void @@ -45,7 +59,7 @@ gsk_gpu_node_processor_init (GskGpuNodeProcessor *self, self->frame = frame; self->scissor = *clip; - //gsk_vulkan_clip_init_empty (&state.clip, &GRAPHENE_RECT_INIT (0, 0, viewport->size.width, viewport->size.height)); + gsk_gpu_clip_init_empty (&self->clip, &GRAPHENE_RECT_INIT (0, 0, viewport->size.width, viewport->size.height)); self->modelview = NULL; graphene_matrix_init_ortho (&self->projection, @@ -57,7 +71,28 @@ gsk_gpu_node_processor_init (GskGpuNodeProcessor *self, height / viewport->size.height); self->offset = GRAPHENE_POINT_INIT (-viewport->origin.x, -viewport->origin.y); + self->pending_globals = GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP; +} +static void +gsk_gpu_node_processor_emit_globals_op (GskGpuNodeProcessor *self) +{ + graphene_matrix_t mvp; + + if (self->modelview) + { + gsk_transform_to_matrix (self->modelview, &mvp); + graphene_matrix_multiply (&mvp, &self->projection, &mvp); + } + else + graphene_matrix_init_from_matrix (&mvp, &self->projection); + + gsk_gpu_globals_op (self->frame, + &self->scale, + &mvp, + &self->clip.rect); + + self->pending_globals &= ~(GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP); } void @@ -76,15 +111,14 @@ gsk_gpu_node_processor_process (GskGpuFrame *frame, clip, viewport); - gsk_gpu_node_processor_add_node (&self, target, node); + gsk_gpu_node_processor_add_node (&self, node); gsk_gpu_node_processor_finish (&self); } static void -gsk_gpu_node_processor_add_node (GskGpuNodeProcessor *self, - GskGpuImage *target, - GskRenderNode *node) +gsk_gpu_node_processor_add_fallback_node (GskGpuNodeProcessor *self, + GskRenderNode *node) { GskGpuImage *image; @@ -93,18 +127,182 @@ gsk_gpu_node_processor_add_node (GskGpuNodeProcessor *self, &self->scale, &node->bounds); - gsk_gpu_blit_op (self->frame, - image, - target, - &(cairo_rectangle_int_t) { - 0, 0, - gsk_gpu_image_get_width (image), - gsk_gpu_image_get_height (image) - }, - &(cairo_rectangle_int_t) { - 0, 0, - gsk_gpu_image_get_width (image), - gsk_gpu_image_get_height (image) - }, - GSK_GPU_BLIT_LINEAR); + gsk_gpu_texture_op (self->frame, + gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds), + image, + GSK_GPU_SAMPLER_DEFAULT, + &node->bounds, + &self->offset, + &node->bounds); +} + +static const struct +{ + GskGpuGlobals ignored_globals; + void (* process_node) (GskGpuNodeProcessor *self, + GskRenderNode *node); +} nodes_vtable[] = { + [GSK_NOT_A_RENDER_NODE] = { + 0, + NULL, + }, + [GSK_CONTAINER_NODE] = { + 0, + NULL, + }, + [GSK_CAIRO_NODE] = { + 0, + NULL, + }, + [GSK_COLOR_NODE] = { + 0, + NULL, + }, + [GSK_LINEAR_GRADIENT_NODE] = { + 0, + NULL, + }, + [GSK_REPEATING_LINEAR_GRADIENT_NODE] = { + 0, + NULL, + }, + [GSK_RADIAL_GRADIENT_NODE] = { + 0, + NULL, + }, + [GSK_REPEATING_RADIAL_GRADIENT_NODE] = { + 0, + NULL, + }, + [GSK_CONIC_GRADIENT_NODE] = { + 0, + NULL, + }, + [GSK_BORDER_NODE] = { + 0, + NULL, + }, + [GSK_TEXTURE_NODE] = { + 0, + NULL, + }, + [GSK_INSET_SHADOW_NODE] = { + 0, + NULL, + }, + [GSK_OUTSET_SHADOW_NODE] = { + 0, + NULL, + }, + [GSK_TRANSFORM_NODE] = { + 0, + NULL, + }, + [GSK_OPACITY_NODE] = { + 0, + NULL, + }, + [GSK_COLOR_MATRIX_NODE] = { + 0, + NULL, + }, + [GSK_REPEAT_NODE] = { + 0, + NULL, + }, + [GSK_CLIP_NODE] = { + 0, + NULL, + }, + [GSK_ROUNDED_CLIP_NODE] = { + 0, + NULL, + }, + [GSK_SHADOW_NODE] = { + 0, + NULL, + }, + [GSK_BLEND_NODE] = { + 0, + NULL, + }, + [GSK_CROSS_FADE_NODE] = { + 0, + NULL, + }, + [GSK_TEXT_NODE] = { + 0, + NULL, + }, + [GSK_BLUR_NODE] = { + 0, + NULL, + }, + [GSK_DEBUG_NODE] = { + 0, + NULL, + }, + [GSK_GL_SHADER_NODE] = { + 0, + NULL, + }, + [GSK_TEXTURE_SCALE_NODE] = { + 0, + NULL, + }, + [GSK_MASK_NODE] = { + 0, + NULL, + }, + [GSK_FILL_NODE] = { + 0, + NULL, + }, + [GSK_STROKE_NODE] = { + 0, + NULL, + }, + [GSK_SUBSURFACE_NODE] = { + 0, + NULL, + }, +}; + +static void +gsk_gpu_node_processor_add_node (GskGpuNodeProcessor *self, + GskRenderNode *node) +{ + GskRenderNodeType node_type; + GskGpuGlobals required_globals; + + /* This catches the corner cases of empty nodes, so after this check + * there's quaranteed to be at least 1 pixel that needs to be drawn */ + if (node->bounds.size.width == 0 || node->bounds.size.height == 0) + return; + if (!gsk_gpu_clip_may_intersect_rect (&self->clip, &self->offset, &node->bounds)) + return; + + node_type = gsk_render_node_get_node_type (node); + if (node_type >= G_N_ELEMENTS (nodes_vtable)) + { + g_critical ("unkonwn node type %u for %s", node_type, g_type_name_from_instance ((GTypeInstance *) node)); + gsk_gpu_node_processor_add_fallback_node (self, node); + return; + } + + required_globals = self->pending_globals & ~nodes_vtable[node_type].ignored_globals; + if (required_globals & (GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP)) + gsk_gpu_node_processor_emit_globals_op (self); + g_assert ((self->pending_globals & ~nodes_vtable[node_type].ignored_globals) == 0); + + if (nodes_vtable[node_type].process_node) + { + nodes_vtable[node_type].process_node (self, node); + } + else + { + GSK_DEBUG (FALLBACK, "Unsupported node '%s'", + g_type_name_from_instance ((GTypeInstance *) node)); + gsk_gpu_node_processor_add_fallback_node (self, node); + } } diff --git a/gsk/gpu/gskgpuop.c b/gsk/gpu/gskgpuop.c index f2307ec501..d3d7fbe38a 100644 --- a/gsk/gpu/gskgpuop.c +++ b/gsk/gpu/gskgpuop.c @@ -23,28 +23,23 @@ gsk_gpu_op_finish (GskGpuOp *op) } void -gsk_gpu_op_print (GskGpuOp *op, - GString *string, - guint indent) +gsk_gpu_op_print (GskGpuOp *op, + GskGpuFrame *frame, + GString *string, + guint indent) { - op->op_class->print (op, string, indent); + op->op_class->print (op, frame, string, indent); } #ifdef GDK_RENDERING_VULKAN -void -gsk_gpu_op_vk_reserve_descriptor_sets (GskGpuOp *op, - GskGpuFrame *frame) -{ - op->op_class->vk_reserve_descriptor_sets (op, frame); -} - GskGpuOp * gsk_gpu_op_vk_command (GskGpuOp *op, GskGpuFrame *frame, VkRenderPass render_pass, + VkFormat format, VkCommandBuffer command_buffer) { - return op->op_class->vk_command (op, frame, render_pass, command_buffer); + return op->op_class->vk_command (op, frame, render_pass, format, command_buffer); } #endif diff --git a/gsk/gpu/gskgpuopprivate.h b/gsk/gpu/gskgpuopprivate.h index 589399534c..2e6658c5fb 100644 --- a/gsk/gpu/gskgpuopprivate.h +++ b/gsk/gpu/gskgpuopprivate.h @@ -32,15 +32,15 @@ struct _GskGpuOpClass void (* finish) (GskGpuOp *op); void (* print) (GskGpuOp *op, + GskGpuFrame *frame, GString *string, guint indent); #ifdef GDK_RENDERING_VULKAN - void (* vk_reserve_descriptor_sets) (GskGpuOp *op, - GskGpuFrame *frame); GskGpuOp * (* vk_command) (GskGpuOp *op, GskGpuFrame *frame, VkRenderPass render_pass, + VkFormat format, VkCommandBuffer command_buffer); #endif GskGpuOp * (* gl_command) (GskGpuOp *op, @@ -55,15 +55,15 @@ GskGpuOp * gsk_gpu_op_alloc (GskGpuF void gsk_gpu_op_finish (GskGpuOp *op); void gsk_gpu_op_print (GskGpuOp *op, + GskGpuFrame *frame, GString *string, guint indent); #ifdef GDK_RENDERING_VULKAN -void gsk_gpu_op_vk_reserve_descriptor_sets (GskGpuOp *op, - GskGpuFrame *frame); GskGpuOp * gsk_gpu_op_vk_command (GskGpuOp *op, GskGpuFrame *frame, VkRenderPass render_pass, + VkFormat format, VkCommandBuffer command_buffer); #endif GskGpuOp * gsk_gpu_op_gl_command (GskGpuOp *op, diff --git a/gsk/gpu/gskgpurenderpassop.c b/gsk/gpu/gskgpurenderpassop.c new file mode 100644 index 0000000000..e9435410ae --- /dev/null +++ b/gsk/gpu/gskgpurenderpassop.c @@ -0,0 +1,327 @@ +#include "config.h" + +#include "gskgpurenderpassopprivate.h" + +#include "gskglimageprivate.h" +#include "gskgpudeviceprivate.h" +#include "gskgpuframeprivate.h" +#include "gskgpunodeprocessorprivate.h" +#include "gskgpuprintprivate.h" +#include "gskgpushaderopprivate.h" +#include "gskrendernodeprivate.h" +#ifdef GDK_RENDERING_VULKAN +#include "gskvulkanimageprivate.h" +#endif + +typedef struct _GskGpuRenderPassOp GskGpuRenderPassOp; + +struct _GskGpuRenderPassOp +{ + GskGpuOp op; + + GskGpuImage *target; + cairo_rectangle_int_t area; + GskRenderPassType pass_type; +}; + +static void +gsk_gpu_render_pass_op_finish (GskGpuOp *op) +{ + GskGpuRenderPassOp *self = (GskGpuRenderPassOp *) op; + + g_object_unref (self->target); +} + +static void +gsk_gpu_render_pass_op_print (GskGpuOp *op, + GskGpuFrame *frame, + GString *string, + guint indent) +{ + GskGpuRenderPassOp *self = (GskGpuRenderPassOp *) op; + + gsk_gpu_print_op (string, indent, "begin-render-pass"); + gsk_gpu_print_image (string, self->target); + gsk_gpu_print_newline (string); +} + +#ifdef GDK_RENDERING_VULKAN +static VkImageLayout +gsk_gpu_render_pass_type_to_vk_image_layout (GskRenderPassType type) +{ + switch (type) + { + default: + g_assert_not_reached (); + G_GNUC_FALLTHROUGH; + case GSK_RENDER_PASS_PRESENT: + return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + case GSK_RENDER_PASS_OFFSCREEN: + return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } +} + +static void +gsk_gpu_render_pass_op_do_barriers (GskGpuRenderPassOp *self, + VkCommandBuffer command_buffer) +{ + GskGpuShaderOp *shader; + GskGpuOp *op; + gsize i; + + for (op = ((GskGpuOp *) self)->next; + op->op_class->stage != GSK_GPU_STAGE_END_PASS; + op = op->next) + { + if (op->op_class->stage != GSK_GPU_STAGE_SHADER) + continue; + + shader = (GskGpuShaderOp *) op; + + for (i = 0; i < shader->n_images; i++) + { + gsk_vulkan_image_transition (GSK_VULKAN_IMAGE (shader->images[i].image), + command_buffer, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_ACCESS_SHADER_READ_BIT); + } + } +} + +static GskGpuOp * +gsk_gpu_render_pass_op_vk_command (GskGpuOp *op, + GskGpuFrame *frame, + VkRenderPass render_pass, + VkFormat format, + VkCommandBuffer command_buffer) +{ + GskGpuRenderPassOp *self = (GskGpuRenderPassOp *) op; + VkRenderPass vk_render_pass; + VkFormat vk_format; + + /* nesting frame passes not allowed */ + g_assert (render_pass == VK_NULL_HANDLE); + + gsk_gpu_render_pass_op_do_barriers (self, command_buffer); + + vk_format = gsk_vulkan_image_get_vk_format (GSK_VULKAN_IMAGE (self->target)); + vk_render_pass = gsk_vulkan_device_get_vk_render_pass (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame)), + vk_format, + gsk_vulkan_image_get_vk_image_layout (GSK_VULKAN_IMAGE (self->target)), + gsk_gpu_render_pass_type_to_vk_image_layout (self->pass_type)); + + + vkCmdSetViewport (command_buffer, + 0, + 1, + &(VkViewport) { + .x = 0, + .y = 0, + .width = gsk_gpu_image_get_width (self->target), + .height = gsk_gpu_image_get_height (self->target), + .minDepth = 0, + .maxDepth = 1 + }); + + vkCmdBeginRenderPass (command_buffer, + &(VkRenderPassBeginInfo) { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .renderPass = vk_render_pass, + .framebuffer = gsk_vulkan_image_get_vk_framebuffer (GSK_VULKAN_IMAGE(self->target), + vk_render_pass), + .renderArea = { + { self->area.x, self->area.y }, + { self->area.width, self->area.height } + }, + .clearValueCount = 1, + .pClearValues = (VkClearValue [1]) { + { .color = { .float32 = { 0.f, 0.f, 0.f, 0.f } } } + } + }, + VK_SUBPASS_CONTENTS_INLINE); + + op = op->next; + while (op->op_class->stage != GSK_GPU_STAGE_END_PASS) + { + op = gsk_gpu_op_vk_command (op, frame, vk_render_pass, vk_format, command_buffer); + } + + op = gsk_gpu_op_vk_command (op, frame, vk_render_pass, vk_format, command_buffer); + + return op; +} +#endif + +static GskGpuOp * +gsk_gpu_render_pass_op_gl_command (GskGpuOp *op, + GskGpuFrame *frame) +{ + GskGpuRenderPassOp *self = (GskGpuRenderPassOp *) op; + + gsk_gl_image_bind_framebuffer (GSK_GL_IMAGE (self->target)); + + glViewport (0, 0, + gsk_gpu_image_get_width (self->target), + gsk_gpu_image_get_height (self->target)); + + glClearColor (0, 0, 0, 0); + glClear (GL_COLOR_BUFFER_BIT); + + op = op->next; + while (op->op_class->stage != GSK_GPU_STAGE_END_PASS) + { + op = gsk_gpu_op_gl_command (op, frame); + } + + op = gsk_gpu_op_gl_command (op, frame); + + return op; +} + +static const GskGpuOpClass GSK_GPU_RENDER_PASS_OP_CLASS = { + GSK_GPU_OP_SIZE (GskGpuRenderPassOp), + GSK_GPU_STAGE_BEGIN_PASS, + gsk_gpu_render_pass_op_finish, + gsk_gpu_render_pass_op_print, +#ifdef GDK_RENDERING_VULKAN + gsk_gpu_render_pass_op_vk_command, +#endif + gsk_gpu_render_pass_op_gl_command +}; + +typedef struct _GskGpuFramePassEndOp GskGpuFramePassEndOp; + +struct _GskGpuFramePassEndOp +{ + GskGpuOp op; + + GskGpuImage *target; + GskRenderPassType pass_type; +}; + +static void +gsk_gpu_render_pass_end_op_finish (GskGpuOp *op) +{ + GskGpuFramePassEndOp *self = (GskGpuFramePassEndOp *) op; + + g_object_unref (self->target); +} + +static void +gsk_gpu_render_pass_end_op_print (GskGpuOp *op, + GskGpuFrame *frame, + GString *string, + guint indent) +{ + GskGpuFramePassEndOp *self = (GskGpuFramePassEndOp *) op; + + gsk_gpu_print_op (string, indent, "end-render-pass"); + gsk_gpu_print_image (string, self->target); + gsk_gpu_print_newline (string); +} + +#ifdef GDK_RENDERING_VULKAN +static GskGpuOp * +gsk_gpu_render_pass_end_op_vk_command (GskGpuOp *op, + GskGpuFrame *frame, + VkRenderPass render_pass, + VkFormat format, + VkCommandBuffer command_buffer) +{ + GskGpuFramePassEndOp *self = (GskGpuFramePassEndOp *) op; + + vkCmdEndRenderPass (command_buffer); + + gsk_vulkan_image_set_vk_image_layout (GSK_VULKAN_IMAGE (self->target), + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + gsk_gpu_render_pass_type_to_vk_image_layout (self->pass_type), + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + + return op->next; +} +#endif + +static GskGpuOp * +gsk_gpu_render_pass_end_op_gl_command (GskGpuOp *op, + GskGpuFrame *frame) +{ + /* nothing to do here */ + + return op->next; +} + +static const GskGpuOpClass GSK_GPU_RENDER_PASS_END_OP_CLASS = { + GSK_GPU_OP_SIZE (GskGpuFramePassEndOp), + GSK_GPU_STAGE_END_PASS, + gsk_gpu_render_pass_end_op_finish, + gsk_gpu_render_pass_end_op_print, +#ifdef GDK_RENDERING_VULKAN + gsk_gpu_render_pass_end_op_vk_command, +#endif + gsk_gpu_render_pass_end_op_gl_command +}; + +void +gsk_gpu_render_pass_begin_op (GskGpuFrame *frame, + GskGpuImage *image, + const cairo_rectangle_int_t *area, + GskRenderPassType pass_type) +{ + GskGpuRenderPassOp *self; + + self = (GskGpuRenderPassOp *) gsk_gpu_op_alloc (frame, &GSK_GPU_RENDER_PASS_OP_CLASS); + + self->target = g_object_ref (image); + self->area = *area; + self->pass_type = pass_type; +} + +void +gsk_gpu_render_pass_end_op (GskGpuFrame *frame, + GskGpuImage *image, + GskRenderPassType pass_type) +{ + GskGpuFramePassEndOp *self; + + self = (GskGpuFramePassEndOp *) gsk_gpu_op_alloc (frame, &GSK_GPU_RENDER_PASS_END_OP_CLASS); + + self->target = g_object_ref (image); + self->pass_type = pass_type; +} + +GskGpuImage * +gsk_gpu_render_pass_op_offscreen (GskGpuFrame *frame, + const graphene_vec2_t *scale, + const graphene_rect_t *viewport, + GskRenderNode *node) +{ + GskGpuImage *image; + int width, height; + + width = ceil (graphene_vec2_get_x (scale) * viewport->size.width); + height = ceil (graphene_vec2_get_y (scale) * viewport->size.height); + + image = gsk_gpu_device_create_offscreen_image (gsk_gpu_frame_get_device (frame), + gsk_render_node_get_preferred_depth (node), + width, height); + + gsk_gpu_render_pass_begin_op (frame, + image, + &(cairo_rectangle_int_t) { 0, 0, width, height }, + GSK_RENDER_PASS_OFFSCREEN); + + gsk_gpu_node_processor_process (frame, + image, + &(cairo_rectangle_int_t) { 0, 0, width, height }, + node, + viewport); + + gsk_gpu_render_pass_end_op (frame, + image, + GSK_RENDER_PASS_OFFSCREEN); + + g_object_unref (image); + + return image; +} diff --git a/gsk/gpu/gskgpurenderpassopprivate.h b/gsk/gpu/gskgpurenderpassopprivate.h new file mode 100644 index 0000000000..cff2e175b0 --- /dev/null +++ b/gsk/gpu/gskgpurenderpassopprivate.h @@ -0,0 +1,32 @@ +#pragma once + +#include "gskgputypesprivate.h" + +#include "gsktypes.h" + +#include + +G_BEGIN_DECLS + +/* We only need this for the final VkImageLayout, but don't tell anyone */ +typedef enum +{ + GSK_RENDER_PASS_OFFSCREEN, + GSK_RENDER_PASS_PRESENT +} GskRenderPassType; + +void gsk_gpu_render_pass_begin_op (GskGpuFrame *frame, + GskGpuImage *image, + const cairo_rectangle_int_t *area, + GskRenderPassType pass_type); +void gsk_gpu_render_pass_end_op (GskGpuFrame *frame, + GskGpuImage *image, + GskRenderPassType pass_type); + +GskGpuImage * gsk_gpu_render_pass_op_offscreen (GskGpuFrame *frame, + const graphene_vec2_t *scale, + const graphene_rect_t *viewport, + GskRenderNode *node); + +G_END_DECLS + diff --git a/gsk/gpu/gskgpushaderop.c b/gsk/gpu/gskgpushaderop.c new file mode 100644 index 0000000000..3eba9d11aa --- /dev/null +++ b/gsk/gpu/gskgpushaderop.c @@ -0,0 +1,151 @@ +#include "config.h" + +#include "gskgpushaderopprivate.h" + +#include "gskgpuframeprivate.h" +#include "gskgldeviceprivate.h" +#include "gskglframeprivate.h" +#include "gskglimageprivate.h" +#ifdef GDK_RENDERING_VULKAN +#include "gskvulkandeviceprivate.h" +#endif + +void +gsk_gpu_shader_op_finish (GskGpuOp *op) +{ + GskGpuShaderOp *self = (GskGpuShaderOp *) op; + gsize i; + + for (i = 0; i < self->n_images; i++) + g_object_unref (self->images[i].image); +} + +#ifdef GDK_RENDERING_VULKAN +GskGpuOp * +gsk_gpu_shader_op_vk_command_n (GskGpuOp *op, + GskGpuFrame *frame, + VkRenderPass render_pass, + VkFormat format, + VkCommandBuffer command_buffer, + gsize instance_scale) +{ + GskGpuShaderOp *self = (GskGpuShaderOp *) op; + GskGpuShaderOpClass *shader_op_class = (GskGpuShaderOpClass *) op->op_class; + GskGpuOp *next; + gsize i; + + i = 1; + for (next = op->next; next && i < 10 * 1000; next = next->next) + { + GskGpuShaderOp *next_shader = (GskGpuShaderOp *) next; + + if (next->op_class != op->op_class || + next_shader->vertex_offset != self->vertex_offset + i * shader_op_class->vertex_size) + break; + + i++; + } + + vkCmdBindPipeline (command_buffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + gsk_vulkan_device_get_vk_pipeline (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame)), + shader_op_class, + self->clip, + format, + render_pass)); + + vkCmdDraw (command_buffer, + 6 * instance_scale, i, + 0, self->vertex_offset / shader_op_class->vertex_size); + + return next; +} + +GskGpuOp * +gsk_gpu_shader_op_vk_command (GskGpuOp *op, + GskGpuFrame *frame, + VkRenderPass render_pass, + VkFormat format, + VkCommandBuffer command_buffer) +{ + return gsk_gpu_shader_op_vk_command_n (op, frame, render_pass, format, command_buffer, 1); +} +#endif + +GskGpuOp * +gsk_gpu_shader_op_gl_command_n (GskGpuOp *op, + GskGpuFrame *frame, + gsize instance_scale) +{ + GskGpuShaderOp *self = (GskGpuShaderOp *) op; + GskGpuShaderOpClass *shader_op_class = (GskGpuShaderOpClass *) op->op_class; + GskGLDevice *device; + gsize i; + + device = GSK_GL_DEVICE (gsk_gpu_frame_get_device (frame)); + + gsk_gl_frame_use_program (GSK_GL_FRAME (frame), + shader_op_class, + self->clip); + + for (i = 0; i < self->n_images; i++) + { + glActiveTexture (GL_TEXTURE0 + self->images[i].descriptor); + gsk_gl_image_bind_texture (GSK_GL_IMAGE (self->images[i].image)); + glBindSampler (self->images[i].descriptor, + gsk_gl_device_get_sampler_id (device, self->images[i].sampler)); + } + + shader_op_class->setup_vao (self->vertex_offset); + + glDrawArraysInstanced (GL_TRIANGLES, + 0, + 6 * instance_scale, + 1); + + return op->next; +} + +GskGpuOp * +gsk_gpu_shader_op_gl_command (GskGpuOp *op, + GskGpuFrame *frame) +{ + return gsk_gpu_shader_op_gl_command_n (op, frame, 1); +} + +GskGpuShaderOp * +gsk_gpu_shader_op_alloc (GskGpuFrame *frame, + const GskGpuShaderOpClass *op_class, + GskGpuShaderClip clip, + gpointer out_vertex_data) +{ + GskGpuShaderOp *self; + + self = (GskGpuShaderOp *) gsk_gpu_op_alloc (frame, &op_class->parent_class); + + self->clip = clip; + self->vertex_offset = gsk_gpu_frame_reserve_vertex_data (frame, op_class->vertex_size); + + *((gpointer *) out_vertex_data) = gsk_gpu_frame_get_vertex_data (frame, self->vertex_offset); + + return self; +} + +guint32 +gsk_gpu_shader_op_use_image (GskGpuShaderOp *self, + GskGpuFrame *frame, + GskGpuImage *image, + GskGpuSampler sampler) +{ + gsize id; + + g_assert (self->n_images < G_N_ELEMENTS (self->images)); + + id = self->n_images; + self->images[id].image = g_object_ref (image); + self->images[id].sampler = sampler; + self->images[id].descriptor = gsk_gpu_frame_get_image_descriptor (frame, image, sampler); + self->n_images++; + + return self->images[id].descriptor; +} diff --git a/gsk/gpu/gskgpushaderopprivate.h b/gsk/gpu/gskgpushaderopprivate.h new file mode 100644 index 0000000000..d05cdd496f --- /dev/null +++ b/gsk/gpu/gskgpushaderopprivate.h @@ -0,0 +1,67 @@ +#pragma once + +#include "gskgpuopprivate.h" + +#include "gskgputypesprivate.h" + +G_BEGIN_DECLS + +struct _GskGpuShaderOp +{ + GskGpuOp parent_op; + + GskGpuShaderClip clip; + gsize vertex_offset; + struct { + GskGpuImage *image; + GskGpuSampler sampler; + guint32 descriptor; + } images[2]; + gsize n_images; +}; + +struct _GskGpuShaderOpClass +{ + GskGpuOpClass parent_class; + + const char * shader_name; + gsize vertex_size; +#ifdef GDK_RENDERING_VULKAN + const VkPipelineVertexInputStateCreateInfo *vertex_input_state; +#endif + void (* setup_vao) (gsize offset); +}; + +GskGpuShaderOp * gsk_gpu_shader_op_alloc (GskGpuFrame *frame, + const GskGpuShaderOpClass *op_class, + GskGpuShaderClip clip, + gpointer out_vertex_data); + +void gsk_gpu_shader_op_finish (GskGpuOp *op); + +guint32 gsk_gpu_shader_op_use_image (GskGpuShaderOp *self, + GskGpuFrame *frame, + GskGpuImage *image, + GskGpuSampler sampler); + +#ifdef GDK_RENDERING_VULKAN +GskGpuOp * gsk_gpu_shader_op_vk_command_n (GskGpuOp *op, + GskGpuFrame *frame, + VkRenderPass render_pass, + VkFormat format, + VkCommandBuffer command_buffer, + gsize instance_scale); +GskGpuOp * gsk_gpu_shader_op_vk_command (GskGpuOp *op, + GskGpuFrame *frame, + VkRenderPass render_pass, + VkFormat format, + VkCommandBuffer command_buffer); +#endif +GskGpuOp * gsk_gpu_shader_op_gl_command_n (GskGpuOp *op, + GskGpuFrame *frame, + gsize instance_scale); +GskGpuOp * gsk_gpu_shader_op_gl_command (GskGpuOp *op, + GskGpuFrame *frame); + +G_END_DECLS + diff --git a/gsk/gpu/gskgputextureop.c b/gsk/gpu/gskgputextureop.c new file mode 100644 index 0000000000..956c796399 --- /dev/null +++ b/gsk/gpu/gskgputextureop.c @@ -0,0 +1,74 @@ +#include "config.h" + +#include "gskgputextureopprivate.h" + +#include "gskgpuframeprivate.h" +#include "gskgpuprintprivate.h" +#include "gskrectprivate.h" + +#include "gpu/shaders/gskgputextureinstance.h" + +typedef struct _GskGpuTextureOp GskGpuTextureOp; + +struct _GskGpuTextureOp +{ + GskGpuShaderOp op; +}; + +static void +gsk_gpu_texture_op_print (GskGpuOp *op, + GskGpuFrame *frame, + GString *string, + guint indent) +{ + GskGpuShaderOp *shader = (GskGpuShaderOp *) op; + GskGpuTextureInstance *instance; + + instance = (GskGpuTextureInstance *) gsk_gpu_frame_get_vertex_data (frame, shader->vertex_offset); + + gsk_gpu_print_op (string, indent, "texture"); + gsk_gpu_print_rect (string, instance->rect); + gsk_gpu_print_image (string, shader->images[0].image); + gsk_gpu_print_newline (string); +} + +static const GskGpuShaderOpClass GSK_GPU_TEXTURE_OP_CLASS = { + { + GSK_GPU_OP_SIZE (GskGpuTextureOp), + GSK_GPU_STAGE_SHADER, + gsk_gpu_shader_op_finish, + gsk_gpu_texture_op_print, +#ifdef GDK_RENDERING_VULKAN + gsk_gpu_shader_op_vk_command, +#endif + gsk_gpu_shader_op_gl_command + }, + "gskgputexture", + sizeof (GskGpuTextureInstance), +#ifdef GDK_RENDERING_VULKAN + &gsk_gpu_texture_info, +#endif + gsk_gpu_texture_setup_vao +}; + +void +gsk_gpu_texture_op (GskGpuFrame *frame, + GskGpuShaderClip clip, + GskGpuImage *image, + GskGpuSampler sampler, + const graphene_rect_t *rect, + const graphene_point_t *offset, + const graphene_rect_t *tex_rect) +{ + GskGpuTextureInstance *instance; + GskGpuTextureOp *self; + + self = (GskGpuTextureOp *) gsk_gpu_shader_op_alloc (frame, + &GSK_GPU_TEXTURE_OP_CLASS, + clip, + &instance); + + gsk_gpu_rect_to_float (rect, offset, instance->rect); + gsk_gpu_rect_to_float (tex_rect, offset, instance->tex_rect); + instance->tex_id = gsk_gpu_shader_op_use_image ((GskGpuShaderOp *) self, frame, image, sampler); +} diff --git a/gsk/gpu/gskgputextureopprivate.h b/gsk/gpu/gskgputextureopprivate.h new file mode 100644 index 0000000000..cf90e95ef2 --- /dev/null +++ b/gsk/gpu/gskgputextureopprivate.h @@ -0,0 +1,19 @@ +#pragma once + +#include "gskgpushaderopprivate.h" + +#include + +G_BEGIN_DECLS + +void gsk_gpu_texture_op (GskGpuFrame *frame, + GskGpuShaderClip clip, + GskGpuImage *image, + GskGpuSampler sampler, + const graphene_rect_t *rect, + const graphene_point_t *offset, + const graphene_rect_t *tex_rect); + + +G_END_DECLS + diff --git a/gsk/gpu/gskgputypesprivate.h b/gsk/gpu/gskgputypesprivate.h index e5dc6a55bf..544282c01b 100644 --- a/gsk/gpu/gskgputypesprivate.h +++ b/gsk/gpu/gskgputypesprivate.h @@ -4,9 +4,26 @@ #include "gdk/gdkmemoryformatprivate.h" +typedef struct _GskGpuBuffer GskGpuBuffer; typedef struct _GskGpuDevice GskGpuDevice; typedef struct _GskGpuFrame GskGpuFrame; typedef struct _GskGpuImage GskGpuImage; typedef struct _GskGpuOp GskGpuOp; typedef struct _GskGpuOpClass GskGpuOpClass; +typedef struct _GskGpuShaderOp GskGpuShaderOp; +typedef struct _GskGpuShaderOpClass GskGpuShaderOpClass; + +typedef enum { + GSK_GPU_SAMPLER_DEFAULT, + GSK_GPU_SAMPLER_REPEAT, + GSK_GPU_SAMPLER_NEAREST, + /* add more */ + GSK_GPU_SAMPLER_N_SAMPLERS +} GskGpuSampler; + +typedef enum { + GSK_GPU_SHADER_CLIP_NONE, + GSK_GPU_SHADER_CLIP_RECT, + GSK_GPU_SHADER_CLIP_ROUNDED +} GskGpuShaderClip; diff --git a/gsk/gpu/gskgpuuploadop.c b/gsk/gpu/gskgpuuploadop.c index 227d80ab13..dd1b101d6d 100644 --- a/gsk/gpu/gskgpuuploadop.c +++ b/gsk/gpu/gskgpuuploadop.c @@ -92,12 +92,6 @@ gsk_gpu_upload_op_gl_command (GskGpuOp *op, } #ifdef GDK_RENDERING_VULKAN -static void -gsk_gpu_upload_op_vk_reserve_descriptor_sets (GskGpuOp *op, - GskGpuFrame *frame) -{ -} - static GskGpuOp * gsk_gpu_upload_op_vk_command_with_area (GskGpuOp *op, GskGpuFrame *frame, @@ -105,18 +99,19 @@ gsk_gpu_upload_op_vk_command_with_area (GskGpuOp *op, GskVulkanImage *image, const cairo_rectangle_int_t *area, void (* draw_func) (GskGpuOp *, guchar *, gsize), - GskVulkanBuffer **buffer) + GskGpuBuffer **buffer) { gsize stride; guchar *data; stride = area->width * gdk_memory_format_bytes_per_pixel (gsk_gpu_image_get_format (GSK_GPU_IMAGE (image))); - *buffer = gsk_vulkan_buffer_new_map (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame)), - area->height * stride, - GSK_VULKAN_WRITE); - data = gsk_vulkan_buffer_get_data (*buffer); + *buffer = gsk_vulkan_buffer_new_write (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame)), + area->height * stride); + data = gsk_gpu_buffer_map (*buffer); draw_func (op, data, stride); + + gsk_gpu_buffer_unmap (*buffer); vkCmdPipelineBarrier (command_buffer, VK_PIPELINE_STAGE_HOST_BIT, @@ -129,7 +124,7 @@ gsk_gpu_upload_op_vk_command_with_area (GskGpuOp *op, .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .buffer = gsk_vulkan_buffer_get_buffer (*buffer), + .buffer = gsk_vulkan_buffer_get_vk_buffer (GSK_VULKAN_BUFFER (*buffer)), .offset = 0, .size = VK_WHOLE_SIZE, }, @@ -141,7 +136,7 @@ gsk_gpu_upload_op_vk_command_with_area (GskGpuOp *op, VK_ACCESS_TRANSFER_WRITE_BIT); vkCmdCopyBufferToImage (command_buffer, - gsk_vulkan_buffer_get_buffer (*buffer), + gsk_vulkan_buffer_get_vk_buffer (GSK_VULKAN_BUFFER (*buffer)), gsk_vulkan_image_get_vk_image (image), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, @@ -178,7 +173,7 @@ gsk_gpu_upload_op_vk_command (GskGpuOp *op, VkCommandBuffer command_buffer, GskVulkanImage *image, void (* draw_func) (GskGpuOp *, guchar *, gsize), - GskVulkanBuffer **buffer) + GskGpuBuffer **buffer) { gsize stride; guchar *data; @@ -218,9 +213,7 @@ struct _GskGpuUploadCairoOp GskRenderNode *node; graphene_rect_t viewport; -#ifdef GDK_RENDERING_VULKAN - GskVulkanBuffer *buffer; -#endif + GskGpuBuffer *buffer; }; static void @@ -230,15 +223,14 @@ gsk_gpu_upload_cairo_op_finish (GskGpuOp *op) g_object_unref (self->image); gsk_render_node_unref (self->node); -#ifdef GDK_RENDERING_VULKAN - g_clear_pointer (&self->buffer, gsk_vulkan_buffer_free); -#endif + g_clear_object (&self->buffer); } static void -gsk_gpu_upload_cairo_op_print (GskGpuOp *op, - GString *string, - guint indent) +gsk_gpu_upload_cairo_op_print (GskGpuOp *op, + GskGpuFrame *frame, + GString *string, + guint indent) { GskGpuUploadCairoOp *self = (GskGpuUploadCairoOp *) op; @@ -283,10 +275,11 @@ gsk_gpu_upload_cairo_op_draw (GskGpuOp *op, #ifdef GDK_RENDERING_VULKAN static GskGpuOp * -gsk_gpu_upload_cairo_op_vk_command (GskGpuOp *op, - GskGpuFrame *frame, - VkRenderPass render_pass, - VkCommandBuffer command_buffer) +gsk_gpu_upload_cairo_op_vk_command (GskGpuOp *op, + GskGpuFrame *frame, + VkRenderPass render_pass, + VkFormat format, + VkCommandBuffer command_buffer) { GskGpuUploadCairoOp *self = (GskGpuUploadCairoOp *) op; @@ -317,7 +310,6 @@ static const GskGpuOpClass GSK_GPU_UPLOAD_CAIRO_OP_CLASS = { gsk_gpu_upload_cairo_op_finish, gsk_gpu_upload_cairo_op_print, #ifdef GDK_RENDERING_VULKAN - gsk_gpu_upload_op_vk_reserve_descriptor_sets, gsk_gpu_upload_cairo_op_vk_command, #endif gsk_gpu_upload_cairo_op_gl_command diff --git a/gsk/gpu/gskvulkanbuffer.c b/gsk/gpu/gskvulkanbuffer.c index 85cf71f2db..f181666a24 100644 --- a/gsk/gpu/gskvulkanbuffer.c +++ b/gsk/gpu/gskvulkanbuffer.c @@ -7,6 +7,8 @@ struct _GskVulkanBuffer { + GskGpuBuffer parent_instance; + GskVulkanDevice *device; VkBuffer vk_buffer; @@ -15,7 +17,56 @@ struct _GskVulkanBuffer GskVulkanAllocation allocation; }; -static GskVulkanBuffer * +G_DEFINE_TYPE (GskVulkanBuffer, gsk_vulkan_buffer, GSK_TYPE_GPU_BUFFER) + +static void +gsk_vulkan_buffer_finalize (GObject *object) +{ + GskVulkanBuffer *self = GSK_VULKAN_BUFFER (object); + + vkDestroyBuffer (gsk_vulkan_device_get_vk_device (self->device), + self->vk_buffer, + NULL); + + gsk_vulkan_free (self->allocator, &self->allocation); + gsk_vulkan_allocator_unref (self->allocator); + + g_object_unref (self->device); + + G_OBJECT_CLASS (gsk_vulkan_buffer_parent_class)->finalize (object); +} + +static guchar * +gsk_vulkan_buffer_map (GskGpuBuffer *buffer) +{ + GskVulkanBuffer *self = GSK_VULKAN_BUFFER (buffer); + + return self->allocation.map; +} + +static void +gsk_vulkan_buffer_unmap (GskGpuBuffer *buffer) +{ +} + +static void +gsk_vulkan_buffer_class_init (GskVulkanBufferClass *klass) +{ + GskGpuBufferClass *buffer_class = GSK_GPU_BUFFER_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + buffer_class->map = gsk_vulkan_buffer_map; + buffer_class->unmap = gsk_vulkan_buffer_unmap; + + gobject_class->finalize = gsk_vulkan_buffer_finalize; +} + +static void +gsk_vulkan_buffer_init (GskVulkanBuffer *self) +{ +} + +static GskGpuBuffer * gsk_vulkan_buffer_new_internal (GskVulkanDevice *device, gsize size, VkBufferUsageFlags usage) @@ -23,7 +74,7 @@ gsk_vulkan_buffer_new_internal (GskVulkanDevice *device, VkMemoryRequirements requirements; GskVulkanBuffer *self; - self = g_new0 (GskVulkanBuffer, 1); + self = g_object_new (GSK_TYPE_VULKAN_BUFFER, NULL); self->device = g_object_ref (device); @@ -51,70 +102,49 @@ gsk_vulkan_buffer_new_internal (GskVulkanDevice *device, requirements.alignment, &self->allocation); + gsk_gpu_buffer_setup (GSK_GPU_BUFFER (self), self->allocation.size); + GSK_VK_CHECK (vkBindBufferMemory, gsk_vulkan_device_get_vk_device (device), self->vk_buffer, self->allocation.vk_memory, self->allocation.offset); - return self; + return GSK_GPU_BUFFER (self); } -GskVulkanBuffer * -gsk_vulkan_buffer_new (GskVulkanDevice *device, - gsize size) +GskGpuBuffer * +gsk_vulkan_buffer_new_vertex (GskVulkanDevice *device, + gsize size) { return gsk_vulkan_buffer_new_internal (device, size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); } -GskVulkanBuffer * +GskGpuBuffer * gsk_vulkan_buffer_new_storage (GskVulkanDevice *device, gsize size) { return gsk_vulkan_buffer_new_internal (device, size, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); } -GskVulkanBuffer * -gsk_vulkan_buffer_new_map (GskVulkanDevice *device, - gsize size, - GskVulkanMapMode mode) +GskGpuBuffer * +gsk_vulkan_buffer_new_write (GskVulkanDevice *device, + gsize size) { - return gsk_vulkan_buffer_new_internal (device, - size, - (mode & GSK_VULKAN_READ ? VK_BUFFER_USAGE_TRANSFER_DST_BIT : 0) | - (mode & GSK_VULKAN_WRITE ? VK_BUFFER_USAGE_TRANSFER_SRC_BIT : 0)); + return gsk_vulkan_buffer_new_internal (device, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT); } -void -gsk_vulkan_buffer_free (GskVulkanBuffer *self) +GskGpuBuffer * +gsk_vulkan_buffer_new_read (GskVulkanDevice *device, + gsize size) { - vkDestroyBuffer (gsk_vulkan_device_get_vk_device (self->device), - self->vk_buffer, - NULL); - - gsk_vulkan_free (self->allocator, &self->allocation); - - g_object_unref (self->device); - - g_free (self); + return gsk_vulkan_buffer_new_internal (device, size, VK_BUFFER_USAGE_TRANSFER_DST_BIT); } VkBuffer -gsk_vulkan_buffer_get_buffer (GskVulkanBuffer *self) +gsk_vulkan_buffer_get_vk_buffer (GskVulkanBuffer *self) { return self->vk_buffer; } -gsize -gsk_vulkan_buffer_get_size (GskVulkanBuffer *self) -{ - return self->allocation.size; -} - -guchar * -gsk_vulkan_buffer_get_data (GskVulkanBuffer *self) -{ - return self->allocation.map; -} - diff --git a/gsk/gpu/gskvulkanbufferprivate.h b/gsk/gpu/gskvulkanbufferprivate.h index 80f98b9826..531fc109b1 100644 --- a/gsk/gpu/gskvulkanbufferprivate.h +++ b/gsk/gpu/gskvulkanbufferprivate.h @@ -1,30 +1,25 @@ #pragma once +#include "gskgpubufferprivate.h" + #include "gskvulkandeviceprivate.h" G_BEGIN_DECLS -typedef struct _GskVulkanBuffer GskVulkanBuffer; +#define GSK_TYPE_VULKAN_BUFFER (gsk_vulkan_buffer_get_type ()) -typedef enum -{ - GSK_VULKAN_READ = (1 << 0), - GSK_VULKAN_WRITE = (1 << 1), - GSK_VULKAN_READWRITE = GSK_VULKAN_READ | GSK_VULKAN_WRITE -} GskVulkanMapMode; +G_DECLARE_FINAL_TYPE (GskVulkanBuffer, gsk_vulkan_buffer, GSK, VULKAN_BUFFER, GskGpuBuffer) -GskVulkanBuffer * gsk_vulkan_buffer_new (GskVulkanDevice *device, +GskGpuBuffer * gsk_vulkan_buffer_new_vertex (GskVulkanDevice *device, gsize size); -GskVulkanBuffer * gsk_vulkan_buffer_new_storage (GskVulkanDevice *device, +GskGpuBuffer * gsk_vulkan_buffer_new_storage (GskVulkanDevice *device, + gsize size); +GskGpuBuffer * gsk_vulkan_buffer_new_write (GskVulkanDevice *device, + gsize size); +GskGpuBuffer * gsk_vulkan_buffer_new_read (GskVulkanDevice *device, gsize size); -GskVulkanBuffer * gsk_vulkan_buffer_new_map (GskVulkanDevice *device, - gsize size, - GskVulkanMapMode mode); -void gsk_vulkan_buffer_free (GskVulkanBuffer *buffer); -VkBuffer gsk_vulkan_buffer_get_buffer (GskVulkanBuffer *self); -gsize gsk_vulkan_buffer_get_size (GskVulkanBuffer *self); -guchar * gsk_vulkan_buffer_get_data (GskVulkanBuffer *self); +VkBuffer gsk_vulkan_buffer_get_vk_buffer (GskVulkanBuffer *self); G_END_DECLS diff --git a/gsk/gpu/gskvulkandevice.c b/gsk/gpu/gskvulkandevice.c index 1b9266d9e8..3bdf5e33b4 100644 --- a/gsk/gpu/gskvulkandevice.c +++ b/gsk/gpu/gskvulkandevice.c @@ -2,18 +2,29 @@ #include "gskvulkandeviceprivate.h" +#include "gskgpuglobalsopprivate.h" +#include "gskgpushaderopprivate.h" +#include "gskvulkanbufferprivate.h" #include "gskvulkanimageprivate.h" #include "gdk/gdkdisplayprivate.h" #include "gdk/gdkvulkancontextprivate.h" +#define DESCRIPTOR_POOL_MAXITEMS 50000 + struct _GskVulkanDevice { GskGpuDevice parent_instance; GskVulkanAllocator *allocators[VK_MAX_MEMORY_TYPES]; + GHashTable *pipeline_cache; + GHashTable *render_pass_cache; + + VkDescriptorSetLayout vk_descriptor_set_layouts[GSK_VULKAN_N_DESCRIPTOR_SETS]; + VkPipelineLayout vk_pipeline_layout; VkCommandPool vk_command_pool; + VkSampler vk_samplers[GSK_GPU_SAMPLER_N_SAMPLERS]; }; struct _GskVulkanDeviceClass @@ -23,6 +34,67 @@ struct _GskVulkanDeviceClass G_DEFINE_TYPE (GskVulkanDevice, gsk_vulkan_device, GSK_TYPE_GPU_DEVICE) +typedef struct _PipelineCacheKey PipelineCacheKey; +typedef struct _RenderPassCacheKey RenderPassCacheKey; + +struct _PipelineCacheKey +{ + const GskGpuShaderOpClass *op_class; + GskGpuShaderClip clip; + VkFormat format; +}; + +struct _RenderPassCacheKey +{ + VkFormat format; + VkImageLayout from_layout; + VkImageLayout to_layout; +}; + +static guint +pipeline_cache_key_hash (gconstpointer data) +{ + const PipelineCacheKey *key = data; + + return GPOINTER_TO_UINT (key->op_class) ^ + key->clip ^ + (key->format << 2); +} + +static gboolean +pipeline_cache_key_equal (gconstpointer a, + gconstpointer b) +{ + const PipelineCacheKey *keya = a; + const PipelineCacheKey *keyb = b; + + return keya->op_class == keyb->op_class && + keya->clip == keyb->clip && + keya->format == keyb->format; +} + +static guint +render_pass_cache_key_hash (gconstpointer data) +{ + const RenderPassCacheKey *key = data; + + return (key->from_layout << 20) ^ + (key->to_layout << 16) ^ + (key->format); +} + +static gboolean +render_pass_cache_key_equal (gconstpointer a, + gconstpointer b) +{ + const RenderPassCacheKey *keya = a; + const RenderPassCacheKey *keyb = b; + + return keya->from_layout == keyb->from_layout && + keya->to_layout == keyb->to_layout && + keya->format == keyb->format; +} + static GskGpuImage * gsk_vulkan_device_create_offscreen_image (GskGpuDevice *device, GdkMemoryDepth depth, @@ -57,16 +129,49 @@ gsk_vulkan_device_finalize (GObject *object) GskVulkanDevice *self = GSK_VULKAN_DEVICE (object); GskGpuDevice *device = GSK_GPU_DEVICE (self); GdkDisplay *display; + GHashTableIter iter; + gpointer key, value; gsize i; g_object_steal_data (G_OBJECT (gsk_gpu_device_get_display (device)), "-gsk-vulkan-device"); display = gsk_gpu_device_get_display (device); + g_hash_table_iter_init (&iter, self->pipeline_cache); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + g_free (key); + vkDestroyPipeline (display->vk_device, value, NULL); + } + g_hash_table_unref (self->pipeline_cache); + + g_hash_table_iter_init (&iter, self->render_pass_cache); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + g_free (key); + vkDestroyRenderPass (display->vk_device, value, NULL); + } + g_hash_table_unref (self->render_pass_cache); + + for (i = 0; i < G_N_ELEMENTS (self->vk_samplers); i++) + { + vkDestroySampler (display->vk_device, + self->vk_samplers[i], + NULL); + } + + vkDestroyPipelineLayout (display->vk_device, + self->vk_pipeline_layout, + NULL); vkDestroyCommandPool (display->vk_device, self->vk_command_pool, NULL); + for (i = 0; i < GSK_VULKAN_N_DESCRIPTOR_SETS; i++) + vkDestroyDescriptorSetLayout (display->vk_device, + self->vk_descriptor_set_layouts[i], + NULL); + for (i = 0; i < VK_MAX_MEMORY_TYPES; i++) g_clear_pointer (&self->allocators[i], gsk_vulkan_allocator_unref); @@ -90,6 +195,8 @@ gsk_vulkan_device_class_init (GskVulkanDeviceClass *klass) static void gsk_vulkan_device_init (GskVulkanDevice *self) { + self->pipeline_cache = g_hash_table_new (pipeline_cache_key_hash, pipeline_cache_key_equal); + self->render_pass_cache = g_hash_table_new (render_pass_cache_key_hash, render_pass_cache_key_equal); } static void @@ -99,6 +206,75 @@ gsk_vulkan_device_setup (GskVulkanDevice *self) display = gsk_gpu_device_get_display (GSK_GPU_DEVICE (self)); + GSK_VK_CHECK (vkCreateDescriptorSetLayout, display->vk_device, + &(VkDescriptorSetLayoutCreateInfo) { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = 1, + .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT, + .pBindings = (VkDescriptorSetLayoutBinding[1]) { + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = DESCRIPTOR_POOL_MAXITEMS, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT + } + }, + .pNext = &(VkDescriptorSetLayoutBindingFlagsCreateInfo) { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .bindingCount = 1, + .pBindingFlags = (VkDescriptorBindingFlags[1]) { + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT + | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT + | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT, + }, + } + }, + NULL, + &self->vk_descriptor_set_layouts[GSK_VULKAN_IMAGE_SET_LAYOUT]); + + GSK_VK_CHECK (vkCreateDescriptorSetLayout, display->vk_device, + &(VkDescriptorSetLayoutCreateInfo) { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = 1, + .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT, + .pBindings = (VkDescriptorSetLayoutBinding[1]) { + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = DESCRIPTOR_POOL_MAXITEMS, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT + }, + }, + .pNext = &(VkDescriptorSetLayoutBindingFlagsCreateInfo) { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .bindingCount = 1, + .pBindingFlags = (VkDescriptorBindingFlags[1]) { + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT + | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT + | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT, + }, + } + }, + NULL, + &self->vk_descriptor_set_layouts[GSK_VULKAN_BUFFER_SET_LAYOUT]); + + GSK_VK_CHECK (vkCreatePipelineLayout, display->vk_device, + &(VkPipelineLayoutCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = G_N_ELEMENTS (self->vk_descriptor_set_layouts), + .pSetLayouts = self->vk_descriptor_set_layouts, + .pushConstantRangeCount = 1, + .pPushConstantRanges = (VkPushConstantRange[1]) { + { + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + .offset = 0, + .size = sizeof (GskGpuGlobalsInstance) + } + } + }, + NULL, + &self->vk_pipeline_layout); + GSK_VK_CHECK (vkCreateCommandPool, display->vk_device, &(const VkCommandPoolCreateInfo) { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, @@ -107,6 +283,51 @@ gsk_vulkan_device_setup (GskVulkanDevice *self) }, NULL, &self->vk_command_pool); + + GSK_VK_CHECK (vkCreateSampler, display->vk_device, + &(VkSamplerCreateInfo) { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .magFilter = VK_FILTER_LINEAR, + .minFilter = VK_FILTER_LINEAR, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, + .unnormalizedCoordinates = VK_FALSE, + .maxAnisotropy = 1.0, + }, + NULL, + &self->vk_samplers[GSK_GPU_SAMPLER_DEFAULT]); + + GSK_VK_CHECK (vkCreateSampler, display->vk_device, + &(VkSamplerCreateInfo) { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .magFilter = VK_FILTER_LINEAR, + .minFilter = VK_FILTER_LINEAR, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, + .unnormalizedCoordinates = VK_FALSE, + .maxAnisotropy = 1.0, + }, + NULL, + &self->vk_samplers[GSK_GPU_SAMPLER_REPEAT]); + + GSK_VK_CHECK (vkCreateSampler, display->vk_device, + &(VkSamplerCreateInfo) { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .magFilter = VK_FILTER_NEAREST, + .minFilter = VK_FILTER_NEAREST, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, + .unnormalizedCoordinates = VK_FALSE, + .maxAnisotropy = 1.0, + }, + NULL, + &self->vk_samplers[GSK_GPU_SAMPLER_NEAREST]); } GskGpuDevice * @@ -132,6 +353,12 @@ gsk_vulkan_device_get_for_display (GdkDisplay *display, return GSK_GPU_DEVICE (self); } +gsize +gsk_vulkan_device_get_max_descriptors (GskVulkanDevice *self) +{ + return DESCRIPTOR_POOL_MAXITEMS; +} + VkDevice gsk_vulkan_device_get_vk_device (GskVulkanDevice *self) { @@ -150,12 +377,256 @@ gsk_vulkan_device_get_vk_queue (GskVulkanDevice *self) return gsk_gpu_device_get_display (GSK_GPU_DEVICE (self))->vk_queue; } +VkDescriptorSetLayout +gsk_vulkan_device_get_vk_image_set_layout (GskVulkanDevice *self) +{ + return self->vk_descriptor_set_layouts[GSK_VULKAN_IMAGE_SET_LAYOUT]; +} + +VkDescriptorSetLayout +gsk_vulkan_device_get_vk_buffer_set_layout (GskVulkanDevice *self) +{ + return self->vk_descriptor_set_layouts[GSK_VULKAN_BUFFER_SET_LAYOUT]; +} + +VkPipelineLayout +gsk_vulkan_device_get_vk_pipeline_layout (GskVulkanDevice *self) +{ + return self->vk_pipeline_layout; +} + VkCommandPool gsk_vulkan_device_get_vk_command_pool (GskVulkanDevice *self) { return self->vk_command_pool; } +VkSampler +gsk_vulkan_device_get_vk_sampler (GskVulkanDevice *self, + GskGpuSampler sampler) +{ + return self->vk_samplers[sampler]; +} + +VkRenderPass +gsk_vulkan_device_get_vk_render_pass (GskVulkanDevice *self, + VkFormat format, + VkImageLayout from_layout, + VkImageLayout to_layout) +{ + RenderPassCacheKey cache_key; + VkRenderPass render_pass; + GdkDisplay *display; + + cache_key = (RenderPassCacheKey) { + .format = format, + .from_layout = from_layout, + .to_layout = to_layout, + }; + render_pass = g_hash_table_lookup (self->render_pass_cache, &cache_key); + if (render_pass) + return render_pass; + + display = gsk_gpu_device_get_display (GSK_GPU_DEVICE (self)); + + GSK_VK_CHECK (vkCreateRenderPass, display->vk_device, + &(VkRenderPassCreateInfo) { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .attachmentCount = 1, + .pAttachments = (VkAttachmentDescription[]) { + { + .format = format, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .initialLayout = from_layout, + .finalLayout = to_layout + } + }, + .subpassCount = 1, + .pSubpasses = (VkSubpassDescription []) { + { + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .inputAttachmentCount = 0, + .colorAttachmentCount = 1, + .pColorAttachments = (VkAttachmentReference []) { + { + .attachment = 0, + .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + } + }, + .pResolveAttachments = (VkAttachmentReference []) { + { + .attachment = VK_ATTACHMENT_UNUSED, + .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + } + }, + .pDepthStencilAttachment = NULL, + } + }, + .dependencyCount = 0, + }, + NULL, + &render_pass); + + g_hash_table_insert (self->render_pass_cache, g_memdup (&cache_key, sizeof (RenderPassCacheKey)), render_pass); + + return render_pass; +} + +typedef struct _GskVulkanShaderSpecialization GskVulkanShaderSpecialization; +struct _GskVulkanShaderSpecialization +{ + guint32 clip; +}; + +VkPipeline +gsk_vulkan_device_get_vk_pipeline (GskVulkanDevice *self, + const GskGpuShaderOpClass *op_class, + GskGpuShaderClip clip, + VkFormat format, + VkRenderPass render_pass) +{ + PipelineCacheKey cache_key; + VkPipeline pipeline; + GdkDisplay *display; + char *vertex_shader_name, *fragment_shader_name; + + cache_key = (PipelineCacheKey) { + .op_class = op_class, + .clip = clip, + .format = format, + }; + pipeline = g_hash_table_lookup (self->pipeline_cache, &cache_key); + if (pipeline) + return pipeline; + + display = gsk_gpu_device_get_display (GSK_GPU_DEVICE (self)); + vertex_shader_name = g_strconcat ("/org/gtk/libgsk/shaders/vulkan/", op_class->shader_name, ".vert.spv", NULL); + fragment_shader_name = g_strconcat ("/org/gtk/libgsk/shaders/vulkan/", op_class->shader_name, ".frag.spv", NULL); + + GSK_VK_CHECK (vkCreateGraphicsPipelines, display->vk_device, + display->vk_pipeline_cache, + 1, + &(VkGraphicsPipelineCreateInfo) { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .stageCount = 2, + .pStages = (VkPipelineShaderStageCreateInfo[2]) { + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .module = gdk_display_get_vk_shader_module (display, vertex_shader_name), + .pName = "main", + .pSpecializationInfo = &(VkSpecializationInfo) { + .mapEntryCount = 1, + .pMapEntries = (VkSpecializationMapEntry[1]) { + { + .constantID = 0, + .offset = G_STRUCT_OFFSET (GskVulkanShaderSpecialization, clip), + .size = sizeof (guint32), + }, + }, + .dataSize = sizeof (GskVulkanShaderSpecialization), + .pData = &(GskVulkanShaderSpecialization) { + .clip = clip, + }, + }, + }, + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .module = gdk_display_get_vk_shader_module (display, fragment_shader_name), + .pName = "main", + .pSpecializationInfo = &(VkSpecializationInfo) { + .mapEntryCount = 1, + .pMapEntries = (VkSpecializationMapEntry[1]) { + { + .constantID = 0, + .offset = G_STRUCT_OFFSET (GskVulkanShaderSpecialization, clip), + .size = sizeof (guint32), + } + }, + .dataSize = sizeof (GskVulkanShaderSpecialization), + .pData = &(GskVulkanShaderSpecialization) { + .clip = clip, + }, + }, + }, + }, + + .pVertexInputState = op_class->vertex_input_state, + .pInputAssemblyState = &(VkPipelineInputAssemblyStateCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + .primitiveRestartEnable = VK_FALSE, + }, + .pTessellationState = NULL, + .pViewportState = &(VkPipelineViewportStateCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .viewportCount = 1, + .scissorCount = 1 + }, + .pRasterizationState = &(VkPipelineRasterizationStateCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .depthClampEnable = VK_FALSE, + .rasterizerDiscardEnable = VK_FALSE, + .polygonMode = VK_POLYGON_MODE_FILL, + .cullMode = VK_CULL_MODE_NONE, + .frontFace = VK_FRONT_FACE_CLOCKWISE, + .lineWidth = 1.0f, + }, + .pMultisampleState = &(VkPipelineMultisampleStateCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .rasterizationSamples = 1, + }, + .pDepthStencilState = &(VkPipelineDepthStencilStateCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO + }, + .pColorBlendState = &(VkPipelineColorBlendStateCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .attachmentCount = 1, + .pAttachments = (VkPipelineColorBlendAttachmentState []) { + { + .blendEnable = VK_TRUE, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcColorBlendFactor = VK_BLEND_FACTOR_ONE, + .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + .alphaBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + .colorWriteMask = VK_COLOR_COMPONENT_A_BIT + | VK_COLOR_COMPONENT_R_BIT + | VK_COLOR_COMPONENT_G_BIT + | VK_COLOR_COMPONENT_B_BIT + }, + } + }, + .pDynamicState = &(VkPipelineDynamicStateCreateInfo) { + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .dynamicStateCount = 2, + .pDynamicStates = (VkDynamicState[2]) { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }, + }, + .layout = self->vk_pipeline_layout, + .renderPass = render_pass, + .subpass = 0, + .basePipelineHandle = VK_NULL_HANDLE, + .basePipelineIndex = -1, + }, + NULL, + &pipeline); + + g_free (fragment_shader_name); + g_free (vertex_shader_name); + + g_hash_table_insert (self->pipeline_cache, g_memdup (&cache_key, sizeof (PipelineCacheKey)), pipeline); + //gdk_vulkan_context_pipeline_cache_updated (self->vulkan); + + return pipeline; +} + static GskVulkanAllocator * gsk_vulkan_device_get_allocator (GskVulkanDevice *self, gsize index, diff --git a/gsk/gpu/gskvulkandeviceprivate.h b/gsk/gpu/gskvulkandeviceprivate.h index 277e332651..84a369d9e9 100644 --- a/gsk/gpu/gskvulkandeviceprivate.h +++ b/gsk/gpu/gskvulkandeviceprivate.h @@ -3,12 +3,21 @@ #include "gskgpudeviceprivate.h" #include "gskdebugprivate.h" +#include "gskgpuclipprivate.h" #include "gskvulkanmemoryprivate.h" #include G_BEGIN_DECLS +/* also used by gskvulkanframe.c */ +enum { + GSK_VULKAN_IMAGE_SET_LAYOUT, + GSK_VULKAN_BUFFER_SET_LAYOUT, + + GSK_VULKAN_N_DESCRIPTOR_SETS +}; + #define GSK_TYPE_VULKAN_DEVICE (gsk_vulkan_device_get_type ()) G_DECLARE_FINAL_TYPE(GskVulkanDevice, gsk_vulkan_device, GSK, VULKAN_DEVICE, GskGpuDevice) @@ -16,10 +25,27 @@ G_DECLARE_FINAL_TYPE(GskVulkanDevice, gsk_vulkan_device, GSK, VULKAN_DEVICE, Gsk GskGpuDevice * gsk_vulkan_device_get_for_display (GdkDisplay *display, GError **error); -VkDevice gsk_vulkan_device_get_vk_device (GskVulkanDevice *self); -VkPhysicalDevice gsk_vulkan_device_get_vk_physical_device (GskVulkanDevice *self); -VkQueue gsk_vulkan_device_get_vk_queue (GskVulkanDevice *self); -VkCommandPool gsk_vulkan_device_get_vk_command_pool (GskVulkanDevice *self); +gsize gsk_vulkan_device_get_max_descriptors (GskVulkanDevice *self) G_GNUC_PURE; + +VkDevice gsk_vulkan_device_get_vk_device (GskVulkanDevice *self) G_GNUC_PURE; +VkPhysicalDevice gsk_vulkan_device_get_vk_physical_device (GskVulkanDevice *self) G_GNUC_PURE; +VkQueue gsk_vulkan_device_get_vk_queue (GskVulkanDevice *self) G_GNUC_PURE; +VkDescriptorSetLayout gsk_vulkan_device_get_vk_image_set_layout (GskVulkanDevice *self) G_GNUC_PURE; +VkDescriptorSetLayout gsk_vulkan_device_get_vk_buffer_set_layout (GskVulkanDevice *self) G_GNUC_PURE; +VkPipelineLayout gsk_vulkan_device_get_vk_pipeline_layout (GskVulkanDevice *self) G_GNUC_PURE; +VkCommandPool gsk_vulkan_device_get_vk_command_pool (GskVulkanDevice *self) G_GNUC_PURE; +VkSampler gsk_vulkan_device_get_vk_sampler (GskVulkanDevice *self, + GskGpuSampler sampler) G_GNUC_PURE; + +VkRenderPass gsk_vulkan_device_get_vk_render_pass (GskVulkanDevice *self, + VkFormat format, + VkImageLayout from_layout, + VkImageLayout to_layout); +VkPipeline gsk_vulkan_device_get_vk_pipeline (GskVulkanDevice *self, + const GskGpuShaderOpClass *op_class, + GskGpuShaderClip clip, + VkFormat format, + VkRenderPass render_pass); GskVulkanAllocator * gsk_vulkan_device_find_allocator (GskVulkanDevice *self, uint32_t allowed_types, diff --git a/gsk/gpu/gskvulkanframe.c b/gsk/gpu/gskvulkanframe.c index 81d5e07cfa..4b3f7f3b2a 100644 --- a/gsk/gpu/gskvulkanframe.c +++ b/gsk/gpu/gskvulkanframe.c @@ -3,16 +3,38 @@ #include "gskvulkanframeprivate.h" #include "gskgpuopprivate.h" +#include "gskvulkanbufferprivate.h" #include "gskvulkandeviceprivate.h" +#include "gskvulkanimageprivate.h" #include "gdk/gdkdisplayprivate.h" +#define GDK_ARRAY_NAME gsk_descriptor_image_infos +#define GDK_ARRAY_TYPE_NAME GskDescriptorImageInfos +#define GDK_ARRAY_ELEMENT_TYPE VkDescriptorImageInfo +#define GDK_ARRAY_BY_VALUE 1 +#define GDK_ARRAY_PREALLOC 128 +#define GDK_ARRAY_NO_MEMSET 1 +#include "gdk/gdkarrayimpl.c" + +#define GDK_ARRAY_NAME gsk_descriptor_buffer_infos +#define GDK_ARRAY_TYPE_NAME GskDescriptorBufferInfos +#define GDK_ARRAY_ELEMENT_TYPE VkDescriptorBufferInfo +#define GDK_ARRAY_BY_VALUE 1 +#define GDK_ARRAY_PREALLOC 32 +#define GDK_ARRAY_NO_MEMSET 1 +#include "gdk/gdkarrayimpl.c" + struct _GskVulkanFrame { GskGpuFrame parent_instance; + GskDescriptorImageInfos descriptor_images; + GskDescriptorBufferInfos descriptor_buffers; + VkFence vk_fence; VkCommandBuffer vk_command_buffer; + VkDescriptorPool vk_descriptor_pool; }; struct _GskVulkanFrameClass @@ -61,35 +83,168 @@ gsk_vulkan_frame_setup (GskGpuFrame *frame) }, NULL, &self->vk_fence); + + GSK_VK_CHECK (vkCreateDescriptorPool, vk_device, + &(VkDescriptorPoolCreateInfo) { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT, + .maxSets = GSK_VULKAN_N_DESCRIPTOR_SETS, + .poolSizeCount = GSK_VULKAN_N_DESCRIPTOR_SETS, + .pPoolSizes = (VkDescriptorPoolSize[GSK_VULKAN_N_DESCRIPTOR_SETS]) { + { + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = gsk_vulkan_device_get_max_descriptors (device), + }, + { + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = gsk_vulkan_device_get_max_descriptors (device), + } + } + }, + NULL, + &self->vk_descriptor_pool); + } static void gsk_vulkan_frame_cleanup (GskGpuFrame *frame) { GskVulkanFrame *self = GSK_VULKAN_FRAME (frame); - VkDevice device; + VkDevice vk_device; - device = gsk_vulkan_device_get_vk_device (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame))); + vk_device = gsk_vulkan_device_get_vk_device (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame))); - GSK_VK_CHECK (vkWaitForFences, device, + GSK_VK_CHECK (vkWaitForFences, vk_device, 1, &self->vk_fence, VK_TRUE, INT64_MAX); - GSK_VK_CHECK (vkResetFences, device, + GSK_VK_CHECK (vkResetFences, vk_device, 1, &self->vk_fence); GSK_VK_CHECK (vkResetCommandBuffer, self->vk_command_buffer, 0); + GSK_VK_CHECK (vkResetDescriptorPool, vk_device, + self->vk_descriptor_pool, + 0); + gsk_descriptor_image_infos_set_size (&self->descriptor_images, 0); + gsk_descriptor_buffer_infos_set_size (&self->descriptor_buffers, 0); + GSK_GPU_FRAME_CLASS (gsk_vulkan_frame_parent_class)->cleanup (frame); } +static guint32 +gsk_vulkan_frame_get_image_descriptor (GskGpuFrame *frame, + GskGpuImage *image, + GskGpuSampler sampler) +{ + GskVulkanFrame *self = GSK_VULKAN_FRAME (frame); + GskVulkanDevice *device; + guint32 result; + + device = GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame)); + + result = gsk_descriptor_image_infos_get_size (&self->descriptor_images); + g_assert (result < gsk_vulkan_device_get_max_descriptors (device)); + + gsk_descriptor_image_infos_append (&self->descriptor_images, + &(VkDescriptorImageInfo) { + .sampler = gsk_vulkan_device_get_vk_sampler (device, sampler), + .imageView = gsk_vulkan_image_get_vk_image_view (GSK_VULKAN_IMAGE (image)), + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + }); + + + return result; +} + static void -gsk_vulkan_frame_submit (GskGpuFrame *frame, - GskGpuOp *op) +gsk_vulkan_frame_prepare_descriptor_sets (GskVulkanFrame *self) +{ + GskVulkanDevice *device; + VkDevice vk_device; + VkWriteDescriptorSet write_descriptor_sets[GSK_VULKAN_N_DESCRIPTOR_SETS]; + gsize n_descriptor_sets; + VkDescriptorSet descriptor_sets[GSK_VULKAN_N_DESCRIPTOR_SETS]; + + device = GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (GSK_GPU_FRAME (self))); + vk_device = gsk_vulkan_device_get_vk_device (device); + + GSK_VK_CHECK (vkAllocateDescriptorSets, vk_device, + &(VkDescriptorSetAllocateInfo) { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = self->vk_descriptor_pool, + .descriptorSetCount = GSK_VULKAN_N_DESCRIPTOR_SETS, + .pSetLayouts = (VkDescriptorSetLayout[GSK_VULKAN_N_DESCRIPTOR_SETS]) { + gsk_vulkan_device_get_vk_image_set_layout (device), + gsk_vulkan_device_get_vk_buffer_set_layout (device), + }, + .pNext = &(VkDescriptorSetVariableDescriptorCountAllocateInfo) { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO, + .descriptorSetCount = GSK_VULKAN_N_DESCRIPTOR_SETS, + .pDescriptorCounts = (uint32_t[GSK_VULKAN_N_DESCRIPTOR_SETS]) { + MAX (1, gsk_descriptor_image_infos_get_size (&self->descriptor_images)), + MAX (1, gsk_descriptor_buffer_infos_get_size (&self->descriptor_buffers)) + } + } + }, + descriptor_sets); + + n_descriptor_sets = 0; + if (gsk_descriptor_image_infos_get_size (&self->descriptor_images) > 0) + { + write_descriptor_sets[n_descriptor_sets++] = (VkWriteDescriptorSet) { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptor_sets[GSK_VULKAN_IMAGE_SET_LAYOUT], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = gsk_descriptor_image_infos_get_size (&self->descriptor_images), + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = gsk_descriptor_image_infos_get_data (&self->descriptor_images) + }; + } + if (gsk_descriptor_buffer_infos_get_size (&self->descriptor_buffers) > 0) + { + write_descriptor_sets[n_descriptor_sets++] = (VkWriteDescriptorSet) { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptor_sets[GSK_VULKAN_BUFFER_SET_LAYOUT], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = gsk_descriptor_buffer_infos_get_size (&self->descriptor_buffers), + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .pBufferInfo = gsk_descriptor_buffer_infos_get_data (&self->descriptor_buffers) + }; + } + + vkUpdateDescriptorSets (vk_device, + n_descriptor_sets, + write_descriptor_sets, + 0, NULL); + + vkCmdBindDescriptorSets (self->vk_command_buffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + gsk_vulkan_device_get_vk_pipeline_layout (device), + 0, + GSK_VULKAN_N_DESCRIPTOR_SETS, + descriptor_sets, + 0, + NULL); +} + +static GskGpuBuffer * +gsk_vulkan_frame_create_vertex_buffer (GskGpuFrame *frame, + gsize size) +{ + return gsk_vulkan_buffer_new_vertex (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame)), size); +} + +static void +gsk_vulkan_frame_submit (GskGpuFrame *frame, + GskGpuBuffer *vertex_buffer, + GskGpuOp *op) { GskVulkanFrame *self = GSK_VULKAN_FRAME (frame); @@ -99,9 +254,20 @@ gsk_vulkan_frame_submit (GskGpuFrame *frame, .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }); + gsk_vulkan_frame_prepare_descriptor_sets (self); + + if (vertex_buffer) + vkCmdBindVertexBuffers (self->vk_command_buffer, + 0, + 1, + (VkBuffer[1]) { + gsk_vulkan_buffer_get_vk_buffer (GSK_VULKAN_BUFFER (vertex_buffer)) + }, + (VkDeviceSize[1]) { 0 }); + while (op) { - op = gsk_gpu_op_vk_command (op, frame, VK_NULL_HANDLE, self->vk_command_buffer); + op = gsk_gpu_op_vk_command (op, frame, VK_NULL_HANDLE, VK_FORMAT_UNDEFINED, self->vk_command_buffer); } GSK_VK_CHECK (vkEndCommandBuffer, self->vk_command_buffer); @@ -128,6 +294,12 @@ gsk_vulkan_frame_finalize (GObject *object) vk_device = gsk_vulkan_device_get_vk_device (device); vk_command_pool = gsk_vulkan_device_get_vk_command_pool (device); + vkDestroyDescriptorPool (vk_device, + self->vk_descriptor_pool, + NULL); + gsk_descriptor_image_infos_clear (&self->descriptor_images); + gsk_descriptor_buffer_infos_clear (&self->descriptor_buffers); + vkFreeCommandBuffers (vk_device, vk_command_pool, 1, &self->vk_command_buffer); @@ -147,6 +319,8 @@ gsk_vulkan_frame_class_init (GskVulkanFrameClass *klass) gpu_frame_class->is_busy = gsk_vulkan_frame_is_busy; gpu_frame_class->setup = gsk_vulkan_frame_setup; gpu_frame_class->cleanup = gsk_vulkan_frame_cleanup; + gpu_frame_class->get_image_descriptor = gsk_vulkan_frame_get_image_descriptor; + gpu_frame_class->create_vertex_buffer = gsk_vulkan_frame_create_vertex_buffer; gpu_frame_class->submit = gsk_vulkan_frame_submit; object_class->finalize = gsk_vulkan_frame_finalize; @@ -155,6 +329,8 @@ gsk_vulkan_frame_class_init (GskVulkanFrameClass *klass) static void gsk_vulkan_frame_init (GskVulkanFrame *self) { + gsk_descriptor_image_infos_init (&self->descriptor_images); + gsk_descriptor_buffer_infos_init (&self->descriptor_buffers); } VkFence diff --git a/gsk/gpu/gskvulkanimage.c b/gsk/gpu/gskvulkanimage.c index 45dd6f8c3a..f186f49028 100644 --- a/gsk/gpu/gskvulkanimage.c +++ b/gsk/gpu/gskvulkanimage.c @@ -805,6 +805,7 @@ gsk_vulkan_image_finalize (GObject *object) { vkDestroyImage (device, self->vk_image, NULL); gsk_vulkan_free (self->allocator, &self->allocation); + gsk_vulkan_allocator_unref (self->allocator); } gdk_display_unref_vulkan (self->display); @@ -825,8 +826,8 @@ gsk_vulkan_image_init (GskVulkanImage *self) } VkFramebuffer -gsk_vulkan_image_get_framebuffer (GskVulkanImage *self, - VkRenderPass render_pass) +gsk_vulkan_image_get_vk_framebuffer (GskVulkanImage *self, + VkRenderPass render_pass) { if (self->vk_framebuffer) return self->vk_framebuffer; @@ -862,7 +863,7 @@ gsk_vulkan_image_get_vk_image (GskVulkanImage *self) } VkImageView -gsk_vulkan_image_get_image_view (GskVulkanImage *self) +gsk_vulkan_image_get_vk_image_view (GskVulkanImage *self) { return self->vk_image_view; } diff --git a/gsk/gpu/gskvulkanimageprivate.h b/gsk/gpu/gskvulkanimageprivate.h index 72056beb39..85fba730c8 100644 --- a/gsk/gpu/gskvulkanimageprivate.h +++ b/gsk/gpu/gskvulkanimageprivate.h @@ -55,9 +55,9 @@ void gsk_vulkan_image_transition (GskVulk VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT) VkImage gsk_vulkan_image_get_vk_image (GskVulkanImage *self); -VkImageView gsk_vulkan_image_get_image_view (GskVulkanImage *self); +VkImageView gsk_vulkan_image_get_vk_image_view (GskVulkanImage *self); VkFormat gsk_vulkan_image_get_vk_format (GskVulkanImage *self); -VkFramebuffer gsk_vulkan_image_get_framebuffer (GskVulkanImage *self, +VkFramebuffer gsk_vulkan_image_get_vk_framebuffer (GskVulkanImage *self, VkRenderPass pass); G_END_DECLS diff --git a/gsk/gpu/shaders/common-gl.glsl b/gsk/gpu/shaders/common-gl.glsl new file mode 100644 index 0000000000..7ec09cacb0 --- /dev/null +++ b/gsk/gpu/shaders/common-gl.glsl @@ -0,0 +1,49 @@ +precision highp float; + +#if defined(GSK_GLES) && __VERSION__ < 310 +layout(std140) +#else +layout(std140, binding = 0) +#endif +uniform PushConstants +{ + mat4 mvp; + vec4 clip_bounds; + vec4 clip_widths; + vec4 clip_heights; + vec2 scale; +} push; + +#define GSK_VERTEX_INDEX gl_VertexID + + +#ifdef GSK_VERTEX_SHADER +#define IN(_loc) in +#define PASS(_loc) out +#define PASS_FLAT(_loc) flat out +#endif + + +#ifdef GSK_FRAGMENT_SHADER +#define PASS(_loc) in +#define PASS_FLAT(_loc) flat in + +uniform sampler2D textures[16]; +#define gsk_get_texture(id) textures[id] + +#ifdef GSK_GLES +void +gsk_set_output_color (vec4 color) +{ + gl_FragColor = color; +} +#else +layout(location = 0) out vec4 out_color; +void +gsk_set_output_color (vec4 color) +{ + out_color = color; +} +#endif + +#endif diff --git a/gsk/gpu/shaders/common-vulkan.glsl b/gsk/gpu/shaders/common-vulkan.glsl new file mode 100644 index 0000000000..e5b7620b16 --- /dev/null +++ b/gsk/gpu/shaders/common-vulkan.glsl @@ -0,0 +1,47 @@ +#extension GL_EXT_nonuniform_qualifier : enable + +#include "enums.glsl" + +layout(push_constant) uniform PushConstants { + mat4 mvp; + vec4 clip_bounds; + vec4 clip_widths; + vec4 clip_heights; + vec2 scale; +} push; + +layout(constant_id=0) const uint GSK_SHADER_CLIP = GSK_GPU_SHADER_CLIP_NONE; + +#define GSK_VERTEX_INDEX gl_VertexIndex + +#ifdef GSK_VERTEX_SHADER +#define IN(_loc) layout(location = _loc) in +#define PASS(_loc) layout(location = _loc) out +#define PASS_FLAT(_loc) layout(location = _loc) flat out +#endif + + +#ifdef GSK_FRAGMENT_SHADER +#define PASS(_loc) layout(location = _loc) in +#define PASS_FLAT(_loc) layout(location = _loc) flat in + +layout(set = 0, binding = 0) uniform sampler2D textures[50000]; +layout(set = 1, binding = 0) readonly buffer FloatBuffers { + float floats[]; +} buffers[50000]; + +layout(location = 0) out vec4 out_color; + +#define gsk_get_texture(id) textures[nonuniformEXT (id)] +#if 0 +#define get_buffer(id) buffers[nonuniformEXT (id)] +#define get_float(id) get_buffer(0).floats[nonuniformEXT (id)] +#endif + +void +gsk_set_output_color (vec4 color) +{ + out_color = color; +} + +#endif diff --git a/gsk/gpu/shaders/common.glsl b/gsk/gpu/shaders/common.glsl new file mode 100644 index 0000000000..d31d3bfb3f --- /dev/null +++ b/gsk/gpu/shaders/common.glsl @@ -0,0 +1,139 @@ +#ifndef _COMMON_ +#define _COMMON_ + +void main_clip_none (void); +void main_clip_rect (void); +void main_clip_rounded (void); + +#include "enums.glsl" + +#ifdef VULKAN +#include "common-vulkan.glsl" +#else +#include "common-gl.glsl" +#endif + +#include "rect.glsl" +#include "roundedrect.glsl" + +Rect +rect_clip (Rect r) +{ + if (GSK_SHADER_CLIP == GSK_GPU_SHADER_CLIP_NONE) + return r; + else + return rect_intersect (r, rect_from_gsk (push.clip_bounds)); +} + +#ifdef GSK_VERTEX_SHADER + +const vec2 offsets[6] = vec2[6](vec2(0.0, 0.0), + vec2(1.0, 0.0), + vec2(0.0, 1.0), + vec2(0.0, 1.0), + vec2(1.0, 0.0), + vec2(1.0, 1.0)); + +void +gsk_set_position (vec2 pos) +{ + gl_Position = push.mvp * vec4 (pos, 0.0, 1.0); +} + +vec2 +rect_get_position (Rect rect) +{ + Rect r = rect_round_larger (rect_clip (rect)); + + vec2 pos = mix (r.bounds.xy, r.bounds.zw, offsets[GSK_VERTEX_INDEX]); + + return pos; +} + +vec2 +scale_tex_coord (vec2 in_pos, + Rect in_rect, + vec4 tex_rect) +{ + return tex_rect.xy + (in_pos - in_rect.bounds.xy) / rect_size (in_rect) * tex_rect.zw; +} + +void run (out vec2 pos); + +void +main (void) +{ + vec2 pos; + + run (pos); + + gsk_set_position (pos); +} + +#endif /* GSK_VERTEX_SHADER */ + + +#ifdef GSK_FRAGMENT_SHADER + +void run (out vec4 color, + out vec2 pos); + +void +main_clip_none (void) +{ + vec4 color; + vec2 pos; + + run (color, pos); + + gsk_set_output_color (color); +} + +void +main_clip_rect (void) +{ + vec4 color; + vec2 pos; + + run (color, pos); + + Rect clip = rect_from_gsk (push.clip_bounds); + + float coverage = rect_coverage (clip, pos); + color *= coverage; + + gsk_set_output_color (color); +} + +void +main_clip_rounded (void) +{ + vec4 color; + vec2 pos; + + run (color, pos); + + RoundedRect clip = RoundedRect(vec4(push.clip_bounds.xy, push.clip_bounds.xy + push.clip_bounds.zw), push.clip_widths, push.clip_heights); + clip = rounded_rect_scale (clip, push.scale); + + float coverage = rounded_rect_coverage (clip, pos); + color *= coverage; + + gsk_set_output_color (color); +} + +void +main (void) +{ + if (GSK_SHADER_CLIP == GSK_GPU_SHADER_CLIP_NONE) + main_clip_none (); + else if (GSK_SHADER_CLIP == GSK_GPU_SHADER_CLIP_RECT) + main_clip_rect (); + else if (GSK_SHADER_CLIP == GSK_GPU_SHADER_CLIP_ROUNDED) + main_clip_rounded (); +} + +#endif /* GSK_FRAGMENT_SHADER */ + +#endif + diff --git a/gsk/gpu/shaders/ellipse.glsl b/gsk/gpu/shaders/ellipse.glsl new file mode 100644 index 0000000000..08986de731 --- /dev/null +++ b/gsk/gpu/shaders/ellipse.glsl @@ -0,0 +1,38 @@ +#ifndef _ELLIPSE_ +#define _ELLIPSE_ + +struct Ellipse +{ + vec2 center; + vec2 radius; +}; + +float +ellipse_distance (Ellipse r, vec2 p) +{ + vec2 e = r.radius; + p = p - r.center; + + if (e.x == e.y) + return length (p) - e.x; + + /* from https://www.shadertoy.com/view/tt3yz7 */ + vec2 pAbs = abs(p); + vec2 ei = 1.0 / e; + vec2 e2 = e*e; + vec2 ve = ei * vec2(e2.x - e2.y, e2.y - e2.x); + + vec2 t = vec2(0.70710678118654752, 0.70710678118654752); + for (int i = 0; i < 3; i++) { + vec2 v = ve*t*t*t; + vec2 u = normalize(pAbs - v) * length(t * e - v); + vec2 w = ei * (v + u); + t = normalize(clamp(w, 0.0, 1.0)); + } + + vec2 nearestAbs = t * e; + float dist = length(pAbs - nearestAbs); + return dot(pAbs, pAbs) < dot(nearestAbs, nearestAbs) ? -dist : dist; +} + +#endif diff --git a/gsk/gpu/shaders/enums.glsl b/gsk/gpu/shaders/enums.glsl new file mode 100644 index 0000000000..3e825b427c --- /dev/null +++ b/gsk/gpu/shaders/enums.glsl @@ -0,0 +1,8 @@ +#ifndef _ENUMS_ +#define _ENUMS_ + +#define GSK_GPU_SHADER_CLIP_NONE 0u +#define GSK_GPU_SHADER_CLIP_RECT 1u +#define GSK_GPU_SHADER_CLIP_ROUNDED 2u + +#endif diff --git a/gsk/gpu/shaders/generate-header.py b/gsk/gpu/shaders/generate-header.py new file mode 100644 index 0000000000..a5f8e9e6f7 --- /dev/null +++ b/gsk/gpu/shaders/generate-header.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python3 + +import sys +import re +import os + +name = os.path.splitext(os.path.splitext(os.path.basename(sys.argv[1]))[0])[0][6:] +var_name = "gsk_gpu_" + name.replace('-', '_') +struct_name = "GskGpu" + name.title().replace('-', '') + "Instance" + +with open(sys.argv[1]) as f: + lines = f.readlines() + matches = [] + + for line in lines: + match = re.search(r"^IN\(([0-9]+)\) ([a-z0-9]+) ([a-zA-Z0-9_]+);$", line) + if not match: + if re.search(r"layout.*\sin\s.*", line): + raise Exception("Failed to parse file") + continue + if not match.group(3).startswith('in'): + raise Exception("Variable doesn't start with 'in'") + matches.append({'name': ''.join('_' + char.lower() if char.isupper() else char for char in match.group(3))[3:], + 'location': int(match.group(1)), + 'type': match.group(2)}) + +print(f'''/* This file is auto-generated; any change will not be preserved */ +#pragma once + +typedef struct _{struct_name} {struct_name}; + +struct _{struct_name} {{''') + +expected = 0 +for match in matches: + if expected != int(match['location']): + raise Exception(f"Should be layout location {expected} but is {match['location']}") # noqa + + if match['type'] == 'float': + print(f" float {match['name']};") + expected += 1 + elif match['type'] == 'int': + print(f" gint32 {match['name']};") + expected += 1 + elif match['type'] == 'uint': + print(f" guint32 {match['name']};") + expected += 1 + elif match['type'] == 'uvec2': + print(f" guint32 {match['name']}[2];") + expected += 1 + elif match['type'] == 'vec2': + print(f" float {match['name']}[2];") + expected += 1 + elif match['type'] == 'vec4': + print(f" float {match['name']}[4];") + expected += 1 + elif match['type'] == 'mat3x4': + print(f" float {match['name']}[12];") + expected += 3 + elif match['type'] == 'mat4': + print(f" float {match['name']}[16];") + expected += 4 + else: + raise Exception(f"Don't know what a {match['type']} is") + +print(f'''}}; +''') + +print(f'''static inline void +{var_name}_setup_vao (gsize offset) +{{''') +for i, match in enumerate(matches): + if match['type'] == 'float': + print(f''' glEnableVertexAttribArray ({match['location']}); + glVertexAttribDivisor ({match['location']}, 1); + glVertexAttribPointer ({match['location']}, + 1, + GL_FLOAT, + GL_FALSE, + sizeof ({struct_name}), + GSIZE_TO_POINTER (offset + G_STRUCT_OFFSET({struct_name}, {match['name']})));'''); + elif match['type'] == 'uint': + print(f''' glEnableVertexAttribArray ({match['location']}); + glVertexAttribDivisor ({match['location']}, 1); + glVertexAttribIPointer ({match['location']}, + 1, + GL_UNSIGNED_INT, + sizeof ({struct_name}), + GSIZE_TO_POINTER (offset + G_STRUCT_OFFSET({struct_name}, {match['name']})));'''); + elif match['type'] == 'uvec2': + print(f''' glEnableVertexAttribArray ({match['location']}); + glVertexAttribDivisor ({match['location']}, 1); + glVertexAttribIPointer ({match['location']}, + 2, + GL_UNSIGNED_INT, + sizeof ({struct_name}), + GSIZE_TO_POINTER (offset + G_STRUCT_OFFSET({struct_name}, {match['name']})));'''); + elif match['type'] == 'vec2': + print(f''' glEnableVertexAttribArray ({match['location']}); + glVertexAttribDivisor ({match['location']}, 1); + glVertexAttribPointer ({match['location']}, + 2, + GL_FLOAT, + GL_FALSE, + sizeof ({struct_name}), + GSIZE_TO_POINTER (offset + G_STRUCT_OFFSET({struct_name}, {match['name']})));'''); + elif match['type'] == 'vec4': + print(f''' glEnableVertexAttribArray ({match['location']}); + glVertexAttribDivisor ({match['location']}, 1); + glVertexAttribPointer ({match['location']}, + 4, + GL_FLOAT, + GL_FALSE, + sizeof ({struct_name}), + GSIZE_TO_POINTER (offset + G_STRUCT_OFFSET({struct_name}, {match['name']})));'''); + elif match['type'] == 'mat3x4': + print(f''' glEnableVertexAttribArray ({match['location']}); + glVertexAttribDivisor ({match['location']}, 1); + glVertexAttribPointer ({match['location']}, + 4, + GL_FLOAT, + GL_FALSE, + sizeof ({struct_name}), + GSIZE_TO_POINTER (offset + G_STRUCT_OFFSET({struct_name}, {match['name']}))); + glEnableVertexAttribArray ({int(match['location'] + 1)}); + glVertexAttribDivisor ({int(match['location'] + 1)}, 1); + glVertexAttribPointer ({int(match['location']) + 1}, + 4, + GL_FLOAT, + GL_FALSE, + sizeof ({struct_name}), + GSIZE_TO_POINTER (offset + G_STRUCT_OFFSET({struct_name}, {match['name']}) + sizeof (float) * 4)); + glEnableVertexAttribArray ({int(match['location'] + 2)}); + glVertexAttribDivisor ({int(match['location'] + 2)}, 1); + glVertexAttribPointer ({int(match['location']) + 2}, + 4, + GL_FLOAT, + GL_FALSE, + sizeof ({struct_name}), + GSIZE_TO_POINTER (offset + G_STRUCT_OFFSET({struct_name}, {match['name']}) + sizeof (float) * 8));''') + elif match['type'] == 'mat4': + print(f''' glEnableVertexAttribArray ({match['location']}); + glVertexAttribDivisor ({match['location']}, 1); + glVertexAttribPointer ({match['location']}, + 4, + GL_FLOAT, + GL_FALSE, + sizeof ({struct_name}), + GSIZE_TO_POINTER (offset + G_STRUCT_OFFSET({struct_name}, {match['name']}))); + glEnableVertexAttribArray ({int(match['location'] + 1)}); + glVertexAttribDivisor ({int(match['location'] + 1)}, 1); + glVertexAttribPointer ({int(match['location']) + 1}, + 4, + GL_FLOAT, + GL_FALSE, + sizeof ({struct_name}), + GSIZE_TO_POINTER (offset + G_STRUCT_OFFSET({struct_name}, {match['name']}) + sizeof (float) * 4)); + glEnableVertexAttribArray ({int(match['location'] + 2)}); + glVertexAttribDivisor ({int(match['location'] + 2)}, 1); + glVertexAttribPointer ({int(match['location']) + 2}, + 4, + GL_FLOAT, + GL_FALSE, + sizeof ({struct_name}), + GSIZE_TO_POINTER (offset + G_STRUCT_OFFSET({struct_name}, {match['name']}) + sizeof (float) * 8)); + glEnableVertexAttribArray ({int(match['location'] + 3)}); + glVertexAttribDivisor ({int(match['location'] + 3)}, 1); + glVertexAttribPointer ({int(match['location']) + 3}, + 4, + GL_FLOAT, + GL_FALSE, + sizeof ({struct_name}), + GSIZE_TO_POINTER (offset + G_STRUCT_OFFSET({struct_name}, {match['name']}) + sizeof (float) * 12));''') + else: + raise Exception(f"Don't know what a {match['type']} is") + +print(f'''}} + +'''); + +print(f'''#ifdef GDK_RENDERING_VULKAN + +static const VkPipelineVertexInputStateCreateInfo {var_name}_info = {{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = (VkVertexInputBindingDescription[1]) {{ + {{ + .binding = 0, + .stride = sizeof ({struct_name}), + .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE + }} + }}, + .vertexAttributeDescriptionCount = {expected}, + .pVertexAttributeDescriptions = (VkVertexInputAttributeDescription[{expected}]) {{''') + +for match in matches: + if match['type'] == 'float': + print(f''' {{ + .location = {match['location']}, + .binding = 0, + .format = VK_FORMAT_R32_SFLOAT, + .offset = G_STRUCT_OFFSET({struct_name}, {match['name']}), + }},''') + elif match['type'] == 'int': + print(f''' {{ + .location = {match['location']}, + .binding = 0, + .format = VK_FORMAT_R32_SINT, + .offset = G_STRUCT_OFFSET({struct_name}, {match['name']}), + }},''') + + elif match['type'] == 'uint': + print(f''' {{ + .location = {match['location']}, + .binding = 0, + .format = VK_FORMAT_R32_UINT, + .offset = G_STRUCT_OFFSET({struct_name}, {match['name']}), + }},''') + elif match['type'] == 'uvec2': + print(f''' {{ + .location = {match['location']}, + .binding = 0, + .format = VK_FORMAT_R32G32_UINT, + .offset = G_STRUCT_OFFSET({struct_name}, {match['name']}), + }},''') + elif match['type'] == 'vec2': + print(f''' {{ + .location = {match['location']}, + .binding = 0, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = G_STRUCT_OFFSET({struct_name}, {match['name']}), + }},''') + elif match['type'] == 'vec4': + print(f''' {{ + .location = {match['location']}, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = G_STRUCT_OFFSET({struct_name}, {match['name']}), + }},''') + elif match['type'] == 'mat3x4': + print(f''' {{ + .location = {match['location']}, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = G_STRUCT_OFFSET({struct_name}, {match['name']}), + }}, + {{ + .location = {int(match['location']) + 1}, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = G_STRUCT_OFFSET({struct_name}, {match['name']}) + sizeof (float) * 4, + }}, + {{ + .location = {int(match['location']) + 2}, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = G_STRUCT_OFFSET({struct_name}, {match['name']}) + sizeof (float) * 8, + }},''') + elif match['type'] == 'mat4': + print(f''' {{ + .location = {match['location']}, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = G_STRUCT_OFFSET({struct_name}, {match['name']}), + }}, + {{ + .location = {int(match['location']) + 1}, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = G_STRUCT_OFFSET({struct_name}, {match['name']}) + sizeof (float) * 4, + }}, + {{ + .location = {int(match['location']) + 2}, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = G_STRUCT_OFFSET({struct_name}, {match['name']}) + sizeof (float) * 8, + }}, + {{ + .location = {int(match['location']) + 3}, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = G_STRUCT_OFFSET({struct_name}, {match['name']}) + sizeof (float) * 12, + }},''') + else: + raise Exception(f"Don't know what a {match['type']} is") + +print(f''' }}, +}}; + +#endif +'''); diff --git a/gsk/gpu/shaders/gskgputexture.glsl b/gsk/gpu/shaders/gskgputexture.glsl new file mode 100644 index 0000000000..7ba2b93f12 --- /dev/null +++ b/gsk/gpu/shaders/gskgputexture.glsl @@ -0,0 +1,44 @@ +#include "common.glsl" + +PASS(0) vec2 _pos; +PASS_FLAT(1) Rect _rect; +PASS(2) vec2 _tex_coord; +PASS_FLAT(3) uint _tex_id; + + + +#ifdef GSK_VERTEX_SHADER + +IN(0) vec4 in_rect; +IN(1) vec4 in_tex_rect; +IN(2) uint in_tex_id; + +void +run (out vec2 pos) +{ + Rect r = rect_from_gsk (in_rect); + + pos = rect_get_position (r); + + _pos = pos; + _rect = r; + _tex_coord = rect_get_coord (rect_from_gsk (in_tex_rect), pos); + _tex_id = in_tex_id; +} + +#endif + + + +#ifdef GSK_FRAGMENT_SHADER + +void +run (out vec4 color, + out vec2 position) +{ + color = texture (gsk_get_texture (_tex_id), _tex_coord) * + rect_coverage (_rect, _pos); + position = _pos; +} + +#endif diff --git a/gsk/gpu/shaders/meson.build b/gsk/gpu/shaders/meson.build new file mode 100644 index 0000000000..7d96d17cc5 --- /dev/null +++ b/gsk/gpu/shaders/meson.build @@ -0,0 +1,82 @@ +gsk_private_gpu_include_shaders = files([ + 'common.glsl', + 'common-gl.glsl', + 'common-vulkan.glsl', + 'ellipse.glsl', + 'enums.glsl', + 'rect.glsl', + 'roundedrect.glsl', +]) + +gsk_private_gpu_shaders = files([ + 'gskgputexture.glsl', +]) + +gsk_private_gpu_shader_headers = [] +gsk_private_gpu_gl_shaders = [] +gsk_private_gpu_vulkan_vertex_shaders = [] +gsk_private_gpu_vulkan_fragment_shaders = [] + +generate_header = find_program('generate-header.py') +process_includes = find_program('process-glsl-includes.py') + +foreach shader: gsk_private_gpu_shaders + instance = fs.name (fs.replace_suffix (shader, '')) + 'instance.h' + shader_header = custom_target(instance, + output: instance, + input: shader, + command: [ + generate_header, + '@INPUT@', + ], + capture: true) + gsk_private_gpu_shader_headers += shader_header + + gl_shader_name = fs.name (shader) + gl_shader = custom_target (gl_shader_name, + output: gl_shader_name, + input: shader, + depend_files: gsk_private_gpu_include_shaders, + command: [ + process_includes, + '@INPUT@', + ], + capture: true) + gsk_private_gpu_gl_shaders += gl_shader + + if (have_vulkan) + vert_spv = fs.name (fs.replace_suffix (shader, '')) + '.vert.spv' + frag_spv = fs.name (fs.replace_suffix (shader, '')) + '.frag.spv' + vert_target = custom_target(vert_spv, + input: shader, + output: vert_spv, + depend_files: gsk_private_gpu_include_shaders, + command: [ + glslc, + '-std=450', + '--target-env=vulkan1.2', + '-fshader-stage=vertex', + '-DGSK_VERTEX_SHADER=1', + '-O', + '@INPUT@', + '-o', '@OUTPUT@' + ]) + frag_target = custom_target(frag_spv, + input: shader, + output: frag_spv, + depend_files: gsk_private_gpu_include_shaders, + command: [ + glslc, + '-std=450', + '--target-env=vulkan1.2', + '-fshader-stage=fragment', + '-DGSK_FRAGMENT_SHADER=1', + '-O', + '@INPUT@', + '-o', '@OUTPUT@' + ]) + gsk_private_gpu_vulkan_vertex_shaders += vert_target + gsk_private_gpu_vulkan_fragment_shaders += frag_target + endif +endforeach + diff --git a/gsk/gpu/shaders/process-glsl-includes.py b/gsk/gpu/shaders/process-glsl-includes.py new file mode 100644 index 0000000000..07f1200827 --- /dev/null +++ b/gsk/gpu/shaders/process-glsl-includes.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +import sys +import re +import os + +loaded_files = [] + +def load (path): + if (path in loaded_files): + return + + loaded_files.append (path) + + with open(path) as f: + lines = f.readlines() + for line in lines: + match = re.search (r"^#include \"(.*)\"$", line) + if (match): + load (os.path.join (os.path.dirname(path), match.group(1))) + else: + print (line, end="") + +load (sys.argv[1]) + diff --git a/gsk/gpu/shaders/rect.glsl b/gsk/gpu/shaders/rect.glsl new file mode 100644 index 0000000000..49629b6489 --- /dev/null +++ b/gsk/gpu/shaders/rect.glsl @@ -0,0 +1,92 @@ +#ifndef _RECT_ +#define _RECT_ + +struct Rect +{ + /* x,y and y,w make up the 2 points of this rect, + note that this is not containing width or height */ + vec4 bounds; +}; + +Rect +rect_new_size (vec4 coords) +{ + return Rect (coords + vec4 (0.0, 0.0, coords.xy)); +} + +Rect +rect_from_gsk (vec4 coords) +{ + Rect result = rect_new_size (coords); + result.bounds *= push.scale.xyxy; + return result; +} + +float +rect_distance (Rect r, vec2 p) +{ + vec4 distance = (r.bounds - p.xyxy) * vec4(1.0, 1.0, -1.0, -1.0); + vec2 max2 = max (distance.xy, distance.zw); + return length (max (max2, 0.0)) + min (max(max2.x, max2.y), 0.0); +} + +vec2 +rect_size (Rect r) +{ + return r.bounds.zw - r.bounds.xy; +} + +Rect +rect_round_larger (Rect r) +{ + return Rect (vec4 (floor(r.bounds.xy), ceil (r.bounds.zw))); +} + +Rect +rect_round_larger_smaller (Rect r) +{ + return Rect (mix (floor(r.bounds), ceil (r.bounds), bvec4(0, 1, 1, 0))); +} + +Rect +rect_round_smaller_larger (Rect r) +{ + return Rect (mix (floor(r.bounds), ceil (r.bounds), bvec4(1, 0, 0, 1))); +} + +Rect +rect_intersect (Rect a, Rect b) +{ + vec4 result = vec4(max(a.bounds.xy, b.bounds.xy), min(a.bounds.zw, b.bounds.zw)); + if (any (greaterThanEqual (result.xy, result.zw))) + return Rect (vec4(0.0)); + return Rect(result); +} + +Rect +rect_union (Rect a, Rect b) +{ + return Rect (vec4 (min (a.bounds.xy, b.bounds.xy), max (a.bounds.zw, b.bounds.zw))); +} + +vec2 +rect_get_coord (Rect r, vec2 pt) +{ + return (pt - r.bounds.xy) / rect_size (r); +} + +#ifdef GSK_FRAGMENT_SHADER + +float +rect_coverage (Rect r, vec2 p) +{ + vec2 dFdp = abs(fwidth (p)); + Rect prect = Rect(vec4(p - 0.5 * dFdp, p + 0.5 * dFdp)); + Rect coverect = rect_intersect (r, prect); + vec2 coverage = rect_size(coverect) / dFdp; + return coverage.x * coverage.y; +} + +#endif + +#endif diff --git a/gsk/gpu/shaders/roundedrect.glsl b/gsk/gpu/shaders/roundedrect.glsl new file mode 100644 index 0000000000..8d8f79bd75 --- /dev/null +++ b/gsk/gpu/shaders/roundedrect.glsl @@ -0,0 +1,90 @@ +#ifndef _ROUNDED_RECT_ +#define _ROUNDED_RECT_ + +#include "ellipse.glsl" +#include "rect.glsl" + +struct RoundedRect +{ + vec4 bounds; + vec4 corner_widths; + vec4 corner_heights; +}; + +RoundedRect +rounded_rect_from_gsk (mat3x4 gsk_rounded_rect) +{ + return RoundedRect ((gsk_rounded_rect[0].xyxy + vec4 (0.0, 0.0, gsk_rounded_rect[0].zw)) * push.scale.xyxy, + gsk_rounded_rect[1] * push.scale.xxxx, + gsk_rounded_rect[2] * push.scale.yyyy); +} + +float +rounded_rect_distance (RoundedRect r, vec2 p) +{ + Rect bounds = Rect(vec4(r.bounds)); + + float bounds_distance = rect_distance (bounds, p); + + Ellipse tl = Ellipse (r.bounds.xy + vec2( r.corner_widths.x, r.corner_heights.x), + vec2(r.corner_widths.x, r.corner_heights.x)); + Ellipse tr = Ellipse (r.bounds.zy + vec2(-r.corner_widths.y, r.corner_heights.y), + vec2(r.corner_widths.y, r.corner_heights.y)); + Ellipse br = Ellipse (r.bounds.zw + vec2(-r.corner_widths.z, -r.corner_heights.z), + vec2(r.corner_widths.z, r.corner_heights.z)); + Ellipse bl = Ellipse (r.bounds.xw + vec2( r.corner_widths.w, -r.corner_heights.w), + vec2(r.corner_widths.w, r.corner_heights.w)); + + vec4 distances = vec4(ellipse_distance (tl, p), + ellipse_distance (tr, p), + ellipse_distance (br, p), + ellipse_distance (bl, p)); + + bvec4 is_out = bvec4(p.x < tl.center.x && p.y < tl.center.y, + p.x > tr.center.x && p.y < tr.center.y, + p.x > br.center.x && p.y > br.center.y, + p.x < bl.center.x && p.y > bl.center.y); + distances = mix (vec4(bounds_distance), distances, is_out); + + vec2 max2 = max (distances.xy, distances.zw); + return max (max2.x, max2.y); +} + +RoundedRect +rounded_rect_scale (RoundedRect r, vec2 scale) +{ + r.bounds *= scale.xyxy; + r.corner_widths *= scale.xxxx; + r.corner_heights *= scale.yyyy; + + return r; +} + +RoundedRect +rounded_rect_shrink (RoundedRect r, vec4 amount) +{ + vec4 new_bounds = r.bounds + vec4(1.0,1.0,-1.0,-1.0) * amount.wxyz; + vec4 new_widths = max (r.corner_widths - sign (r.corner_widths) * amount.wyyw, 0.0); + vec4 new_heights = max (r.corner_heights - sign (r.corner_heights) * amount.xxzz, 0.0); + new_widths = min (new_widths, new_bounds.z - new_bounds.x); + new_heights = min (new_heights, new_bounds.w - new_bounds.y); + + return RoundedRect (new_bounds, new_widths, new_heights); +} + +#ifdef GSK_FRAGMENT_SHADER + +float +rounded_rect_coverage (RoundedRect r, vec2 p) +{ + vec2 fw = abs (fwidth (p)); + float distance_scale = max (fw.x, fw.y); + float distance = rounded_rect_distance (r, p) / distance_scale; + float coverage = 0.5 - distance; + + return clamp (coverage, 0.0, 1.0); +} + +#endif + +#endif diff --git a/gsk/gskrectprivate.h b/gsk/gskrectprivate.h index 1b07709c45..f9aa706b37 100644 --- a/gsk/gskrectprivate.h +++ b/gsk/gskrectprivate.h @@ -94,3 +94,15 @@ gsk_rect_equal (const graphene_rect_t *r1, r1->size.width == r2->size.width && r1->size.height == r2->size.height; } + +static inline void +gsk_gpu_rect_to_float (const graphene_rect_t *rect, + const graphene_point_t *offset, + float values[4]) +{ + values[0] = rect->origin.x + offset->x; + values[1] = rect->origin.y + offset->y; + values[2] = rect->size.width; + values[3] = rect->size.height; +} + diff --git a/gsk/meson.build b/gsk/meson.build index fdec9b7738..18f9f88787 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -67,18 +67,25 @@ gsk_private_sources = files([ 'gl/gskglprofiler.c', 'gl/stb_rect_pack.c', 'gl/fp16.c', + 'gpu/gskglbuffer.c', 'gpu/gskgldevice.c', 'gpu/gskglframe.c', 'gpu/gskglimage.c', 'gpu/gskgpublitop.c', + 'gpu/gskgpubuffer.c', + 'gpu/gskgpuclip.c', 'gpu/gskgpudownloadop.c', 'gpu/gskgpudevice.c', 'gpu/gskgpuframe.c', + 'gpu/gskgpuglobalsop.c', 'gpu/gskgpuimage.c', 'gpu/gskgpunodeprocessor.c', 'gpu/gskgpuop.c', 'gpu/gskgpuprint.c', 'gpu/gskgpurenderer.c', + 'gpu/gskgpurenderpassop.c', + 'gpu/gskgpushaderop.c', + 'gpu/gskgputextureop.c', 'gpu/gskgpuuploadop.c', 'gpu/gsknglrenderer.c', ]) @@ -142,6 +149,8 @@ if have_vulkan ]) endif # have_vulkan +subdir('gpu/shaders') + if get_variable('broadway_enabled') gsk_public_sources += files([ 'broadway/gskbroadwayrenderer.c', @@ -155,7 +164,10 @@ gsk_resources_xml = custom_target(output: 'gsk.resources.xml', '@OUTPUT@', gsk_private_gl_shaders, gsk_private_vulkan_compiled_shaders, - gsk_private_vulkan_shaders + gsk_private_vulkan_shaders, + gsk_private_gpu_gl_shaders, + gsk_private_gpu_vulkan_vertex_shaders, + gsk_private_gpu_vulkan_fragment_shaders, ], ) @@ -171,7 +183,11 @@ gskenum_h = gsk_enums[1] gskresources = gnome.compile_resources('gskresources', gsk_resources_xml, - dependencies: gsk_private_vulkan_compiled_shaders_deps, + dependencies: [ + gsk_private_vulkan_compiled_shaders_deps, + gsk_private_gpu_vulkan_fragment_shaders, + gsk_private_gpu_vulkan_vertex_shaders, + ], source_dir: [meson.current_build_dir(), meson.current_source_dir()], c_name: '_gsk', extra_args: [ '--manual-register', ], @@ -205,6 +221,7 @@ libgsk = static_library('gsk', gsk_enums, gskresources, gsk_private_vulkan_shader_headers, + gsk_private_gpu_shader_headers, ], dependencies: gsk_deps, include_directories: [ confinc, ],