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.)
This commit is contained in:
Benjamin Otte 2023-09-24 08:40:05 +02:00
parent 8271687ef6
commit 9045431bde
7 changed files with 65 additions and 19 deletions

View File

@ -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));
}

View File

@ -25,6 +25,8 @@ static const GdkDebugKey gsk_gpu_optimization_keys[] = {
{ "uber", GSK_GPU_OPTIMIZE_UBER, "Don't use the uber shader" },
{ "clear", GSK_GPU_OPTIMIZE_CLEAR, "Use shaders instead of vkCmdClearAttachment()/glClear()" },
{ "merge", GSK_GPU_OPTIMIZE_MERGE, "Use one vkCmdDraw()/glDrawArrays() per operation" },
{ "gl-baseinstance", GSK_GPU_OPTIMIZE_GL_BASE_INSTANCE, "Assume no ARB/EXT_base_instance support" },
};
typedef struct _GskGpuRendererPrivate GskGpuRendererPrivate;
@ -33,6 +35,7 @@ struct _GskGpuRendererPrivate
{
GskGpuDevice *device;
GdkDrawContext *context;
GskGpuOptimizations optimizations;
GskGpuFrame *frames[GSK_GPU_MAX_FRAMES];
};
@ -83,7 +86,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;
}
@ -119,6 +122,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)
@ -130,13 +134,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;
}
@ -287,6 +293,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 *

View File

@ -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);

View File

@ -106,12 +106,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;
}

View File

@ -58,5 +58,7 @@ typedef enum {
GSK_GPU_OPTIMIZE_UBER = 1 << 0,
GSK_GPU_OPTIMIZE_CLEAR = 1 << 1,
GSK_GPU_OPTIMIZE_MERGE = 1 << 2,
/* These require hardware support */
GSK_GPU_OPTIMIZE_GL_BASE_INSTANCE = 1 << 3,
} GskGpuOptimizations;

View File

@ -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);
}

View File

@ -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);
}