mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-10 19:00:08 +00:00
71a231b71d
When the command queue is out of batches, there is no point in doing further work like allocating uniforms. This helps us avoid assertions in the uniform code that we would hit when we run out of uniform space too.
417 lines
15 KiB
C
417 lines
15 KiB
C
/* gskglcommandqueueprivate.h
|
|
*
|
|
* Copyright 2020 Christian Hergert <chergert@redhat.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <gsk/gskprofilerprivate.h>
|
|
|
|
#include "gskgltypesprivate.h"
|
|
#include "gskglbufferprivate.h"
|
|
#include "gskglattachmentstateprivate.h"
|
|
#include "gskgluniformstateprivate.h"
|
|
|
|
#include "inlinearray.h"
|
|
|
|
#include "gskglprofilerprivate.h"
|
|
|
|
G_BEGIN_DECLS
|
|
|
|
#define GSK_TYPE_GL_COMMAND_QUEUE (gsk_gl_command_queue_get_type())
|
|
|
|
G_DECLARE_FINAL_TYPE (GskGLCommandQueue, gsk_gl_command_queue, GSK, GL_COMMAND_QUEUE, GObject)
|
|
|
|
typedef enum _GskGLCommandKind
|
|
{
|
|
/* The batch will perform a glClear() */
|
|
GSK_GL_COMMAND_KIND_CLEAR,
|
|
|
|
/* The batch will perform a glDrawArrays() */
|
|
GSK_GL_COMMAND_KIND_DRAW,
|
|
} GskGLCommandKind;
|
|
|
|
typedef struct _GskGLCommandBind
|
|
{
|
|
/* @texture is the value passed to glActiveTexture(), the "slot" the
|
|
* texture will be placed into. We always use GL_TEXTURE_2D so we don't
|
|
* waste any bits here to indicate that.
|
|
*/
|
|
guint texture : 4;
|
|
|
|
guint sampler : 4;
|
|
|
|
/* The identifier for the texture created with glGenTextures(). */
|
|
guint id: 24;
|
|
} GskGLCommandBind;
|
|
|
|
G_STATIC_ASSERT (sizeof (GskGLCommandBind) == 4);
|
|
|
|
typedef struct _GskGLCommandBatchAny
|
|
{
|
|
/* A GskGLCommandKind indicating what the batch will do */
|
|
guint kind : 8;
|
|
|
|
/* The program's identifier to use for determining if we can merge two
|
|
* batches together into a single set of draw operations. We put this
|
|
* here instead of the GskGLCommandDraw so that we can use the extra
|
|
* bits here without making the structure larger.
|
|
*/
|
|
guint program : 24;
|
|
|
|
/* The index of the next batch following this one. This is used
|
|
* as a sort of integer-based linked list to simplify out-of-order
|
|
* batching without moving memory around. -1 indicates last batch.
|
|
*/
|
|
gint16 next_batch_index;
|
|
|
|
/* Same but for reverse direction as we sort in reverse to get the
|
|
* batches ordered by framebuffer.
|
|
*/
|
|
gint16 prev_batch_index;
|
|
|
|
/* The viewport size of the batch. We check this as we process
|
|
* batches to determine if we need to resize the viewport.
|
|
*/
|
|
struct {
|
|
guint16 width;
|
|
guint16 height;
|
|
} viewport;
|
|
} GskGLCommandBatchAny;
|
|
|
|
G_STATIC_ASSERT (sizeof (GskGLCommandBatchAny) == 12);
|
|
|
|
typedef struct _GskGLCommandDraw
|
|
{
|
|
GskGLCommandBatchAny head;
|
|
|
|
/* There doesn't seem to be a limit on the framebuffer identifier that
|
|
* can be returned, so we have to use a whole unsigned for the framebuffer
|
|
* we are drawing to. When processing batches, we check to see if this
|
|
* changes and adjust the render target accordingly. Some sorting is
|
|
* performed to reduce the amount we change framebuffers.
|
|
*/
|
|
guint framebuffer;
|
|
|
|
/* The number of uniforms to change. This must be less than or equal to
|
|
* GL_MAX_UNIFORM_LOCATIONS but only guaranteed up to 1024 by any OpenGL
|
|
* implementation to be conformant.
|
|
*/
|
|
guint uniform_count : 11;
|
|
|
|
/* The number of textures to bind, which is only guaranteed up to 16
|
|
* by the OpenGL specification to be conformant.
|
|
*/
|
|
guint bind_count : 5;
|
|
|
|
/* GL_MAX_ELEMENTS_VERTICES specifies 33000 for this which requires 16-bit
|
|
* to address all possible counts <= GL_MAX_ELEMENTS_VERTICES.
|
|
*/
|
|
guint vbo_count : 16;
|
|
|
|
/* The offset within the VBO containing @vbo_count vertices to send with
|
|
* glDrawArrays().
|
|
*/
|
|
guint vbo_offset;
|
|
|
|
/* The offset within the array of uniform changes to be made containing
|
|
* @uniform_count `GskGLCommandUniform` elements to apply.
|
|
*/
|
|
guint uniform_offset;
|
|
|
|
/* The offset within the array of bind changes to be made containing
|
|
* @bind_count `GskGLCommandBind` elements to apply.
|
|
*/
|
|
guint bind_offset;
|
|
} GskGLCommandDraw;
|
|
|
|
G_STATIC_ASSERT (sizeof (GskGLCommandDraw) == 32);
|
|
|
|
typedef struct _GskGLCommandClear
|
|
{
|
|
GskGLCommandBatchAny any;
|
|
guint bits;
|
|
guint framebuffer;
|
|
} GskGLCommandClear;
|
|
|
|
G_STATIC_ASSERT (sizeof (GskGLCommandClear) == 20);
|
|
|
|
typedef struct _GskGLCommandUniform
|
|
{
|
|
GskGLUniformInfo info;
|
|
guint location;
|
|
} GskGLCommandUniform;
|
|
|
|
G_STATIC_ASSERT (sizeof (GskGLCommandUniform) == 8);
|
|
|
|
typedef union _GskGLCommandBatch
|
|
{
|
|
GskGLCommandBatchAny any;
|
|
GskGLCommandDraw draw;
|
|
GskGLCommandClear clear;
|
|
} GskGLCommandBatch;
|
|
|
|
G_STATIC_ASSERT (sizeof (GskGLCommandBatch) == 32);
|
|
|
|
typedef struct _GskGLSync {
|
|
guint id;
|
|
gpointer sync;
|
|
} GskGLSync;
|
|
|
|
DEFINE_INLINE_ARRAY (GskGLCommandBatches, gsk_gl_command_batches, GskGLCommandBatch)
|
|
DEFINE_INLINE_ARRAY (GskGLCommandBinds, gsk_gl_command_binds, GskGLCommandBind)
|
|
DEFINE_INLINE_ARRAY (GskGLCommandUniforms, gsk_gl_command_uniforms, GskGLCommandUniform)
|
|
DEFINE_INLINE_ARRAY (GskGLSyncs, gsk_gl_syncs, GskGLSync)
|
|
|
|
struct _GskGLCommandQueue
|
|
{
|
|
GObject parent_instance;
|
|
|
|
/* The GdkGLContext we make current before executing GL commands. */
|
|
GdkGLContext *context;
|
|
|
|
/* Array of GskGLCommandBatch which is a fixed size structure that will
|
|
* point into offsets of other arrays so that all similar data is stored
|
|
* together. The idea here is that we reduce the need for pointers so that
|
|
* using g_realloc()'d arrays is fine.
|
|
*/
|
|
GskGLCommandBatches batches;
|
|
|
|
/* Contains array of vertices and some wrapper code to help upload them
|
|
* to the GL driver. We can also tweak this to use double buffered arrays
|
|
* if we find that to be faster on some hardware and/or drivers.
|
|
*/
|
|
GskGLBuffer vertices;
|
|
|
|
/* The GskGLAttachmentState contains information about our FBO and texture
|
|
* attachments as we process incoming operations. We snapshot them into
|
|
* various batches so that we can compare differences between merge
|
|
* candidates.
|
|
*/
|
|
GskGLAttachmentState *attachments;
|
|
|
|
/* The uniform state across all programs. We snapshot this into batches so
|
|
* that we can compare uniform state between batches to give us more
|
|
* chances at merging draw commands.
|
|
*/
|
|
GskGLUniformState *uniforms;
|
|
|
|
/* Current program if we are in a draw so that we can send commands
|
|
* to the uniform state as needed.
|
|
*/
|
|
GskGLUniformProgram *program_info;
|
|
|
|
/* The profiler instance to deliver timing/etc data */
|
|
GskProfiler *profiler;
|
|
GskGLProfiler *gl_profiler;
|
|
|
|
/* Array of GskGLCommandBind which denote what textures need to be attached
|
|
* to which slot. GskGLCommandDraw.bind_offset and bind_count reference this
|
|
* array to determine what to attach.
|
|
*/
|
|
GskGLCommandBinds batch_binds;
|
|
|
|
/* Array of GskGLCommandUniform denoting which uniforms must be updated
|
|
* before the glDrawArrays() may be called. These are referenced from the
|
|
* GskGLCommandDraw.uniform_offset and uniform_count fields.
|
|
*/
|
|
GskGLCommandUniforms batch_uniforms;
|
|
|
|
/* Array of samplers that we use for mag/min filter handling. It is indexed
|
|
* by the sampler_index() function.
|
|
* Note that when samplers are not supported (hello GLES), we fall back to
|
|
* setting the texture filter, but that needs to be done for every texture.
|
|
*/
|
|
GLuint samplers[GSK_GL_N_FILTERS * GSK_GL_N_FILTERS];
|
|
|
|
/* Array of sync objects to wait on.
|
|
*/
|
|
GskGLSyncs syncs;
|
|
|
|
/* Discovered max texture size when loading the command queue so that we
|
|
* can either scale down or slice textures to fit within this size. Assumed
|
|
* to be both height and width.
|
|
*/
|
|
int max_texture_size;
|
|
|
|
/* The index of the last batch in @batches, which may not be the element
|
|
* at the end of the array, as batches can be reordered. This is used to
|
|
* update the "next" index when adding a new batch.
|
|
*/
|
|
gint16 tail_batch_index;
|
|
gint16 head_batch_index;
|
|
|
|
/* Max framebuffer we used, so we can sort items faster */
|
|
guint fbo_max;
|
|
|
|
/* Various GSK and GDK metric counter ids */
|
|
struct {
|
|
GQuark n_frames;
|
|
GQuark cpu_time;
|
|
GQuark gpu_time;
|
|
guint n_binds;
|
|
guint n_fbos;
|
|
guint n_uniforms;
|
|
guint n_uploads;
|
|
guint n_programs;
|
|
guint queue_depth;
|
|
} metrics;
|
|
|
|
/* Counter for uploads on the frame */
|
|
guint n_uploads;
|
|
|
|
/* If the GL context is new enough for sampler support */
|
|
guint has_samplers : 1;
|
|
|
|
/* If we're inside a begin/end_frame pair */
|
|
guint in_frame : 1;
|
|
|
|
/* If we're inside of a begin_draw()/end_draw() pair. */
|
|
guint in_draw : 1;
|
|
|
|
/* If we've warned about truncating batches */
|
|
guint have_truncated : 1;
|
|
};
|
|
|
|
GskGLCommandQueue *gsk_gl_command_queue_new (GdkGLContext *context,
|
|
GskGLUniformState *uniforms);
|
|
void gsk_gl_command_queue_set_profiler (GskGLCommandQueue *self,
|
|
GskProfiler *profiler);
|
|
GdkGLContext *gsk_gl_command_queue_get_context (GskGLCommandQueue *self);
|
|
void gsk_gl_command_queue_make_current (GskGLCommandQueue *self);
|
|
void gsk_gl_command_queue_begin_frame (GskGLCommandQueue *self);
|
|
void gsk_gl_command_queue_end_frame (GskGLCommandQueue *self);
|
|
void gsk_gl_command_queue_execute (GskGLCommandQueue *self,
|
|
guint surface_height,
|
|
float scale,
|
|
const cairo_region_t *scissor,
|
|
guint default_framebuffer);
|
|
int gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
|
|
GdkTexture *texture);
|
|
int gsk_gl_command_queue_create_texture (GskGLCommandQueue *self,
|
|
int width,
|
|
int height,
|
|
int format);
|
|
|
|
|
|
typedef struct {
|
|
GdkTexture *texture;
|
|
int x;
|
|
int y;
|
|
} GskGLTextureChunk;
|
|
|
|
int gsk_gl_command_queue_upload_texture_chunks(GskGLCommandQueue *self,
|
|
unsigned int n_chunks,
|
|
GskGLTextureChunk *chunks);
|
|
|
|
guint gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self);
|
|
gboolean gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self,
|
|
int width,
|
|
int height,
|
|
int format,
|
|
guint *out_fbo_id,
|
|
guint *out_texture_id);
|
|
void gsk_gl_command_queue_delete_program (GskGLCommandQueue *self,
|
|
guint program_id);
|
|
void gsk_gl_command_queue_clear (GskGLCommandQueue *self,
|
|
guint clear_bits,
|
|
const graphene_rect_t *viewport);
|
|
gboolean gsk_gl_command_queue_begin_draw (GskGLCommandQueue *self,
|
|
GskGLUniformProgram *program_info,
|
|
guint width,
|
|
guint height);
|
|
void gsk_gl_command_queue_end_draw (GskGLCommandQueue *self);
|
|
void gsk_gl_command_queue_split_draw (GskGLCommandQueue *self);
|
|
|
|
static inline GskGLCommandBatch *
|
|
gsk_gl_command_queue_get_batch (GskGLCommandQueue *self)
|
|
{
|
|
return gsk_gl_command_batches_tail (&self->batches);
|
|
}
|
|
|
|
static inline GskGLDrawVertex *
|
|
gsk_gl_command_queue_add_vertices (GskGLCommandQueue *self)
|
|
{
|
|
gsk_gl_command_queue_get_batch (self)->draw.vbo_count += GSK_GL_N_VERTICES;
|
|
return gsk_gl_buffer_advance (&self->vertices, GSK_GL_N_VERTICES);
|
|
}
|
|
|
|
static inline GskGLDrawVertex *
|
|
gsk_gl_command_queue_add_n_vertices (GskGLCommandQueue *self,
|
|
guint count)
|
|
{
|
|
/* This is a batch form of gsk_gl_command_queue_add_vertices(). Note that
|
|
* it does *not* add the count to .draw.vbo_count as the caller is responsible
|
|
* for that.
|
|
*/
|
|
return gsk_gl_buffer_advance (&self->vertices, GSK_GL_N_VERTICES * count);
|
|
}
|
|
|
|
static inline void
|
|
gsk_gl_command_queue_retract_n_vertices (GskGLCommandQueue *self,
|
|
guint count)
|
|
{
|
|
/* Like gsk_gl_command_queue_add_n_vertices(), this does not tweak
|
|
* the draw vbo_count.
|
|
*/
|
|
gsk_gl_buffer_retract (&self->vertices, GSK_GL_N_VERTICES * count);
|
|
}
|
|
|
|
static inline guint
|
|
gsk_gl_command_queue_bind_framebuffer (GskGLCommandQueue *self,
|
|
guint framebuffer)
|
|
{
|
|
guint ret = self->attachments->fbo.id;
|
|
gsk_gl_attachment_state_bind_framebuffer (self->attachments, framebuffer);
|
|
return ret;
|
|
}
|
|
|
|
static inline GskGLSync *
|
|
gsk_gl_syncs_get_sync (GskGLSyncs *syncs,
|
|
guint id)
|
|
{
|
|
for (unsigned int i = 0; i < syncs->len; i++)
|
|
{
|
|
GskGLSync *sync = &syncs->items[i];
|
|
if (sync->id == id)
|
|
return sync;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static inline void
|
|
gsk_gl_syncs_add_sync (GskGLSyncs *syncs,
|
|
guint id,
|
|
gpointer sync)
|
|
{
|
|
GskGLSync *s;
|
|
|
|
s = gsk_gl_syncs_get_sync (syncs, id);
|
|
if (s)
|
|
g_assert (s->sync == sync);
|
|
else
|
|
{
|
|
s = gsk_gl_syncs_append (syncs);
|
|
s->id = id;
|
|
s->sync = sync;
|
|
}
|
|
}
|
|
|
|
G_END_DECLS
|
|
|