gtk2/gsk/ngl/gsknglcommandqueueprivate.h
Matthias Clasen bfe4aea981 ngl: Add a program change counter
Count how often we change programs during a frame.
2021-04-09 00:52:18 -04:00

361 lines
14 KiB
C

/* gsknglcommandqueueprivate.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
*/
#ifndef __GSK_NGL_COMMAND_QUEUE_PRIVATE_H__
#define __GSK_NGL_COMMAND_QUEUE_PRIVATE_H__
#include <gsk/gskprofilerprivate.h>
#include "gskngltypesprivate.h"
#include "gsknglbufferprivate.h"
#include "gsknglattachmentstateprivate.h"
#include "gskngluniformstateprivate.h"
#include "inlinearray.h"
#include "../gl/gskglprofilerprivate.h"
G_BEGIN_DECLS
#define GSK_TYPE_GL_COMMAND_QUEUE (gsk_ngl_command_queue_get_type())
G_DECLARE_FINAL_TYPE (GskNglCommandQueue, gsk_ngl_command_queue, GSK, NGL_COMMAND_QUEUE, GObject)
typedef enum _GskNglCommandKind
{
/* The batch will perform a glClear() */
GSK_NGL_COMMAND_KIND_CLEAR,
/* The batch will perform a glDrawArrays() */
GSK_NGL_COMMAND_KIND_DRAW,
} GskNglCommandKind;
typedef struct _GskNglCommandBind
{
/* @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 : 5;
/* The identifier for the texture created with glGenTextures(). */
guint id : 27;
} GskNglCommandBind;
G_STATIC_ASSERT (sizeof (GskNglCommandBind) == 4);
typedef struct _GskNglCommandBatchAny
{
/* A GskNglCommandKind 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 GskNglCommandDraw 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;
} GskNglCommandBatchAny;
G_STATIC_ASSERT (sizeof (GskNglCommandBatchAny) == 12);
typedef struct _GskNglCommandDraw
{
GskNglCommandBatchAny 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 #GskNglCommandUniform elements to apply.
*/
guint uniform_offset;
/* The offset within the array of bind changes to be made containing
* @bind_count #GskNglCommandBind elements to apply.
*/
guint bind_offset;
} GskNglCommandDraw;
G_STATIC_ASSERT (sizeof (GskNglCommandDraw) == 32);
typedef struct _GskNglCommandClear
{
GskNglCommandBatchAny any;
guint bits;
guint framebuffer;
} GskNglCommandClear;
G_STATIC_ASSERT (sizeof (GskNglCommandClear) == 20);
typedef struct _GskNglCommandUniform
{
GskNglUniformInfo info;
guint location;
} GskNglCommandUniform;
G_STATIC_ASSERT (sizeof (GskNglCommandUniform) == 8);
typedef union _GskNglCommandBatch
{
GskNglCommandBatchAny any;
GskNglCommandDraw draw;
GskNglCommandClear clear;
} GskNglCommandBatch;
G_STATIC_ASSERT (sizeof (GskNglCommandBatch) == 32);
DEFINE_INLINE_ARRAY (GskNglCommandBatches, gsk_ngl_command_batches, GskNglCommandBatch)
DEFINE_INLINE_ARRAY (GskNglCommandBinds, gsk_ngl_command_binds, GskNglCommandBind)
DEFINE_INLINE_ARRAY (GskNglCommandUniforms, gsk_ngl_command_uniforms, GskNglCommandUniform)
struct _GskNglCommandQueue
{
GObject parent_instance;
/* The GdkGLContext we make current before executing GL commands. */
GdkGLContext *context;
/* Array of GskNglCommandBatch 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.
*/
GskNglCommandBatches 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.
*/
GskNglBuffer vertices;
/* The GskNglAttachmentState 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.
*/
GskNglAttachmentState *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.
*/
GskNglUniformState *uniforms;
/* Current program if we are in a draw so that we can send commands
* to the uniform state as needed.
*/
GskNglUniformProgram *program_info;
/* The profiler instance to deliver timing/etc data */
GskProfiler *profiler;
GskGLProfiler *gl_profiler;
/* Array of GskNglCommandBind which denote what textures need to be attached
* to which slot. GskNglCommandDraw.bind_offset and bind_count reference this
* array to determine what to attach.
*/
GskNglCommandBinds batch_binds;
/* Array of GskNglCommandUniform denoting which uniforms must be updated
* before the glDrawArrays() may be called. These are referenced from the
* GskNglCommandDraw.uniform_offset and uniform_count fields.
*/
GskNglCommandUniforms batch_uniforms;
/* 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 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;
};
GskNglCommandQueue *gsk_ngl_command_queue_new (GdkGLContext *context,
GskNglUniformState *uniforms);
void gsk_ngl_command_queue_set_profiler (GskNglCommandQueue *self,
GskProfiler *profiler);
GdkGLContext *gsk_ngl_command_queue_get_context (GskNglCommandQueue *self);
void gsk_ngl_command_queue_make_current (GskNglCommandQueue *self);
void gsk_ngl_command_queue_begin_frame (GskNglCommandQueue *self);
void gsk_ngl_command_queue_end_frame (GskNglCommandQueue *self);
void gsk_ngl_command_queue_execute (GskNglCommandQueue *self,
guint surface_height,
guint scale_factor,
const cairo_region_t *scissor);
int gsk_ngl_command_queue_upload_texture (GskNglCommandQueue *self,
GdkTexture *texture,
guint x_offset,
guint y_offset,
guint width,
guint height,
int min_filter,
int mag_filter);
int gsk_ngl_command_queue_create_texture (GskNglCommandQueue *self,
int width,
int height,
int min_filter,
int mag_filter);
guint gsk_ngl_command_queue_create_framebuffer (GskNglCommandQueue *self);
gboolean gsk_ngl_command_queue_create_render_target (GskNglCommandQueue *self,
int width,
int height,
int min_filter,
int mag_filter,
guint *out_fbo_id,
guint *out_texture_id);
void gsk_ngl_command_queue_delete_program (GskNglCommandQueue *self,
guint program_id);
void gsk_ngl_command_queue_clear (GskNglCommandQueue *self,
guint clear_bits,
const graphene_rect_t *viewport);
void gsk_ngl_command_queue_begin_draw (GskNglCommandQueue *self,
GskNglUniformProgram *program_info,
guint width,
guint height);
void gsk_ngl_command_queue_end_draw (GskNglCommandQueue *self);
void gsk_ngl_command_queue_split_draw (GskNglCommandQueue *self);
static inline GskNglCommandBatch *
gsk_ngl_command_queue_get_batch (GskNglCommandQueue *self)
{
return gsk_ngl_command_batches_tail (&self->batches);
}
static inline GskNglDrawVertex *
gsk_ngl_command_queue_add_vertices (GskNglCommandQueue *self)
{
gsk_ngl_command_queue_get_batch (self)->draw.vbo_count += GSK_NGL_N_VERTICES;
return gsk_ngl_buffer_advance (&self->vertices, GSK_NGL_N_VERTICES);
}
static inline GskNglDrawVertex *
gsk_ngl_command_queue_add_n_vertices (GskNglCommandQueue *self,
guint count)
{
/* This is a batch form of gsk_ngl_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_ngl_buffer_advance (&self->vertices, GSK_NGL_N_VERTICES * count);
}
static inline void
gsk_ngl_command_queue_retract_n_vertices (GskNglCommandQueue *self,
guint count)
{
/* Like gsk_ngl_command_queue_add_n_vertices(), this does not tweak
* the draw vbo_count.
*/
gsk_ngl_buffer_retract (&self->vertices, GSK_NGL_N_VERTICES * count);
}
static inline guint
gsk_ngl_command_queue_bind_framebuffer (GskNglCommandQueue *self,
guint framebuffer)
{
guint ret = self->attachments->fbo.id;
gsk_ngl_attachment_state_bind_framebuffer (self->attachments, framebuffer);
return ret;
}
G_END_DECLS
#endif /* __GSK_NGL_COMMAND_QUEUE_PRIVATE_H__ */