From 998ede9e87f631107622ea2817f73903c4daea9b Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Sun, 24 Sep 2023 08:40:05 +0200 Subject: [PATCH] gpu: Add a GL optimization Use glDrawArraysInstancedBaseInstance() to draw. (Yay for GL naming.) That allows setting up the offset in the vertex array without having to glVertexAttribPointer() everything again. However, this is only supported since GL 4.2 and not at all in stock GLES, so we need to have code that can work without it. Fortunately, it is mandatory in Vulkan, so every recent GPU supports it. And if that GPU has a proper driver, it will also expose the GL extension for it. (Hint: You can check https://opengles.gpuinfo.org/listextensions.php for how many proper drivers exist outside of Mesa.) --- gsk/gpu/gskglframe.c | 15 ++++++++++++--- gsk/gpu/gskgpurenderer.c | 13 +++++++++++-- gsk/gpu/gskgpurendererprivate.h | 3 ++- gsk/gpu/gskgpushaderop.c | 21 ++++++++++++++++----- gsk/gpu/gskgputypesprivate.h | 2 ++ gsk/gpu/gsknglrenderer.c | 19 +++++++++++++++---- gsk/gpu/gskvulkanrenderer.c | 11 +++++++---- 7 files changed, 65 insertions(+), 19 deletions(-) diff --git a/gsk/gpu/gskglframe.c b/gsk/gpu/gskglframe.c index a5f2a84f6f..367e2e1ffa 100644 --- a/gsk/gpu/gskglframe.c +++ b/gsk/gpu/gskglframe.c @@ -4,6 +4,7 @@ #include "gskgpuglobalsopprivate.h" #include "gskgpuopprivate.h" +#include "gskgpushaderopprivate.h" #include "gskglbufferprivate.h" #include "gskgldeviceprivate.h" @@ -66,6 +67,13 @@ static GskGpuBuffer * gsk_gl_frame_create_vertex_buffer (GskGpuFrame *frame, gsize size) { + GskGLFrame *self = GSK_GL_FRAME (frame); + + /* We could also reassign them all to the new buffer here? + * Is that faster? + */ + g_hash_table_remove_all (self->vaos); + return gsk_gl_buffer_new (GL_ARRAY_BUFFER, size, GL_WRITE_ONLY); } @@ -166,11 +174,12 @@ gsk_gl_frame_use_program (GskGLFrame *self, vao = GPOINTER_TO_UINT (g_hash_table_lookup (self->vaos, op_class)); if (vao) { - glBindVertexArray(vao); + glBindVertexArray (vao); return; } - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); + glGenVertexArrays (1, &vao); + glBindVertexArray (vao); + op_class->setup_vao (0); g_hash_table_insert (self->vaos, (gpointer) op_class, GUINT_TO_POINTER (vao)); } diff --git a/gsk/gpu/gskgpurenderer.c b/gsk/gpu/gskgpurenderer.c index ed7002e14a..8f41e435a4 100644 --- a/gsk/gpu/gskgpurenderer.c +++ b/gsk/gpu/gskgpurenderer.c @@ -24,6 +24,8 @@ static const GdkDebugKey gsk_gpu_optimization_keys[] = { { "uber", GSK_GPU_OPTIMIZE_UBER, "Don't use the uber shader" }, { "clear", GSK_GPU_OPTIMIZE_CLEAR, "Don't vkCmdClearAttachment()/glClear() instead of shaders" }, + + { "gl-baseinstance", GSK_GPU_OPTIMIZE_GL_BASE_INSTANCE, "Assume no ARB/EXT_base_instance support" }, }; typedef struct _GskGpuRendererPrivate GskGpuRendererPrivate; @@ -32,6 +34,7 @@ struct _GskGpuRendererPrivate { GskGpuDevice *device; GdkDrawContext *context; + GskGpuOptimizations optimizations; GskGpuFrame *frames[GSK_GPU_MAX_FRAMES]; }; @@ -82,7 +85,7 @@ gsk_gpu_renderer_create_frame (GskGpuRenderer *self) result = g_object_new (klass->frame_type, NULL); - gsk_gpu_frame_setup (result, self, priv->device, klass->optimizations); + gsk_gpu_frame_setup (result, self, priv->device, priv->optimizations); return result; } @@ -118,6 +121,7 @@ gsk_gpu_renderer_realize (GskRenderer *renderer, { GskGpuRenderer *self = GSK_GPU_RENDERER (renderer); GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self); + GskGpuOptimizations context_optimizations; GdkDisplay *display; if (surface) @@ -129,13 +133,15 @@ gsk_gpu_renderer_realize (GskRenderer *renderer, if (priv->device == NULL) return FALSE; - priv->context = GSK_GPU_RENDERER_GET_CLASS (self)->create_context (self, display, surface, error); + priv->context = GSK_GPU_RENDERER_GET_CLASS (self)->create_context (self, display, surface, &context_optimizations, error); if (priv->context == NULL) { g_clear_object (&priv->device); return FALSE; } + priv->optimizations &= context_optimizations; + return TRUE; } @@ -286,6 +292,9 @@ gsk_gpu_renderer_class_init (GskGpuRendererClass *klass) static void gsk_gpu_renderer_init (GskGpuRenderer *self) { + GskGpuRendererPrivate *priv = gsk_gpu_renderer_get_instance_private (self); + + priv->optimizations = GSK_GPU_RENDERER_GET_CLASS (self)->optimizations; } GdkDrawContext * diff --git a/gsk/gpu/gskgpurendererprivate.h b/gsk/gpu/gskgpurendererprivate.h index 8a408c6fac..8d2c73221a 100644 --- a/gsk/gpu/gskgpurendererprivate.h +++ b/gsk/gpu/gskgpurendererprivate.h @@ -23,13 +23,14 @@ struct _GskGpuRendererClass GskRendererClass parent_class; GType frame_type; - GskGpuOptimizations optimizations; + GskGpuOptimizations optimizations; /* subclasses cannot override this */ GskGpuDevice * (* get_device) (GdkDisplay *display, GError **error); GdkDrawContext * (* create_context) (GskGpuRenderer *self, GdkDisplay *display, GdkSurface *surface, + GskGpuOptimizations *supported, GError **error); void (* make_current) (GskGpuRenderer *self); diff --git a/gsk/gpu/gskgpushaderop.c b/gsk/gpu/gskgpushaderop.c index 7422ae36a7..f61ddd1edf 100644 --- a/gsk/gpu/gskgpushaderop.c +++ b/gsk/gpu/gskgpushaderop.c @@ -96,12 +96,23 @@ gsk_gpu_shader_op_gl_command_n (GskGpuOp *op, gsk_gl_device_get_sampler_id (device, images[i].sampler)); } - shader_op_class->setup_vao (self->vertex_offset); + if (gsk_gpu_frame_should_optimize (frame, GSK_GPU_OPTIMIZE_GL_BASE_INSTANCE)) + { + glDrawArraysInstancedBaseInstance (GL_TRIANGLES, + 0, + 6 * instance_scale, + 1, + self->vertex_offset / shader_op_class->vertex_size); + } + else + { + shader_op_class->setup_vao (self->vertex_offset); - glDrawArraysInstanced (GL_TRIANGLES, - 0, - 6 * instance_scale, - 1); + glDrawArraysInstanced (GL_TRIANGLES, + 0, + 6 * instance_scale, + 1); + } return op->next; } diff --git a/gsk/gpu/gskgputypesprivate.h b/gsk/gpu/gskgputypesprivate.h index 0d83ed9bc4..d3e56d7650 100644 --- a/gsk/gpu/gskgputypesprivate.h +++ b/gsk/gpu/gskgputypesprivate.h @@ -57,5 +57,7 @@ typedef enum { typedef enum { GSK_GPU_OPTIMIZE_UBER = 1 << 0, GSK_GPU_OPTIMIZE_CLEAR = 1 << 1, + /* These require hardware support */ + GSK_GPU_OPTIMIZE_GL_BASE_INSTANCE = 1 << 2, } GskGpuOptimizations; diff --git a/gsk/gpu/gsknglrenderer.c b/gsk/gpu/gsknglrenderer.c index 4319fc1061..a0b5478757 100644 --- a/gsk/gpu/gsknglrenderer.c +++ b/gsk/gpu/gsknglrenderer.c @@ -8,6 +8,8 @@ #include "gskglframeprivate.h" #include "gskglimageprivate.h" +#include "gdk/gdkglcontextprivate.h" + struct _GskNglRenderer { GskGpuRenderer parent_instance; @@ -23,10 +25,11 @@ struct _GskNglRendererClass G_DEFINE_TYPE (GskNglRenderer, gsk_ngl_renderer, GSK_TYPE_GPU_RENDERER) static GdkDrawContext * -gsk_ngl_renderer_create_context (GskGpuRenderer *renderer, - GdkDisplay *display, - GdkSurface *surface, - GError **error) +gsk_ngl_renderer_create_context (GskGpuRenderer *renderer, + GdkDisplay *display, + GdkSurface *surface, + GskGpuOptimizations *supported, + GError **error) { GdkGLContext *context; @@ -47,6 +50,14 @@ gsk_ngl_renderer_create_context (GskGpuRenderer *renderer, return NULL; } + gdk_gl_context_make_current (context); + + *supported = -1; + if (!gdk_gl_context_check_version (context, "4.2", "9.9") && + !epoxy_has_gl_extension ("GL_EXT_base_instance") && + !epoxy_has_gl_extension ("GL_ARB_base_instance")) + *supported &= ~GSK_GPU_OPTIMIZE_GL_BASE_INSTANCE; + return GDK_DRAW_CONTEXT (context); } diff --git a/gsk/gpu/gskvulkanrenderer.c b/gsk/gpu/gskvulkanrenderer.c index afe33d2478..b3fdd1cb38 100644 --- a/gsk/gpu/gskvulkanrenderer.c +++ b/gsk/gpu/gskvulkanrenderer.c @@ -73,10 +73,11 @@ gsk_vulkan_renderer_update_images_cb (GdkVulkanContext *context, } static GdkDrawContext * -gsk_vulkan_renderer_create_context (GskGpuRenderer *renderer, - GdkDisplay *display, - GdkSurface *surface, - GError **error) +gsk_vulkan_renderer_create_context (GskGpuRenderer *renderer, + GdkDisplay *display, + GdkSurface *surface, + GskGpuOptimizations *supported, + GError **error) { GskVulkanRenderer *self = GSK_VULKAN_RENDERER (renderer); GdkVulkanContext *context; @@ -95,6 +96,8 @@ gsk_vulkan_renderer_create_context (GskGpuRenderer *renderer, self); gsk_vulkan_renderer_update_images_cb (context, self); + *supported = -1; + return GDK_DRAW_CONTEXT (context); }