mirror of
https://github.com/brl/mutter.git
synced 2024-12-25 20:32:16 +00:00
[cogl-primitives] Split the journal out from cogl-primitives.c
The Journal can be considered a standalone component, so even though it's currently only used to log quads, it seems better to split it out into its own file.
This commit is contained in:
parent
5387aa0e9e
commit
861766f4ad
@ -120,6 +120,8 @@ libclutter_cogl_la_SOURCES = \
|
||||
cogl-texture-2d-sliced.c \
|
||||
cogl-spans.h \
|
||||
cogl-spans.c \
|
||||
cogl-journal-private.h \
|
||||
cogl-journal.c \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST += stb_image.c cogl-enum-types.h.in cogl-enum-types.c.in
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "cogl-internal.h"
|
||||
#include "cogl-util.h"
|
||||
#include "cogl-context.h"
|
||||
#include "cogl-journal-private.h"
|
||||
#include "cogl-texture-private.h"
|
||||
#include "cogl-material-private.h"
|
||||
|
||||
|
57
cogl/cogl-journal-private.h
Normal file
57
cogl/cogl-journal-private.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Cogl
|
||||
*
|
||||
* An object oriented GL/GLES Abstraction/Utility Layer
|
||||
*
|
||||
* Copyright (C) 2007,2008,2009 Intel Corporation.
|
||||
*
|
||||
* 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 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 library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __COGL_JOURNAL_PRIVATE_H
|
||||
#define __COGL_JOURNAL_PRIVATE_H
|
||||
|
||||
#include "cogl-handle.h"
|
||||
|
||||
/* To improve batching of geometry when submitting vertices to OpenGL we
|
||||
* log the texture rectangles we want to draw to a journal, so when we
|
||||
* later flush the journal we aim to batch data, and gl draw calls. */
|
||||
typedef struct _CoglJournalEntry
|
||||
{
|
||||
CoglHandle material;
|
||||
int n_layers;
|
||||
CoglMaterialFlushOptions flush_options;
|
||||
CoglMatrix model_view;
|
||||
/* XXX: These entries are pretty big now considering the padding in
|
||||
* CoglMaterialFlushOptions and CoglMatrix, so we might need to optimize this
|
||||
* later. */
|
||||
} CoglJournalEntry;
|
||||
|
||||
void _cogl_journal_flush (void);
|
||||
|
||||
void
|
||||
_cogl_journal_log_quad (float x_1,
|
||||
float y_1,
|
||||
float x_2,
|
||||
float y_2,
|
||||
CoglHandle material,
|
||||
int n_layers,
|
||||
guint32 fallback_layers,
|
||||
GLuint layer0_override_texture,
|
||||
float *tex_coords,
|
||||
unsigned int tex_coords_len);
|
||||
|
||||
#endif /* __COGL_JOURNAL_PRIVATE_H */
|
744
cogl/cogl-journal.c
Normal file
744
cogl/cogl-journal.c
Normal file
@ -0,0 +1,744 @@
|
||||
/*
|
||||
* Cogl
|
||||
*
|
||||
* An object oriented GL/GLES Abstraction/Utility Layer
|
||||
*
|
||||
* Copyright (C) 2007,2008,2009 Intel Corporation.
|
||||
*
|
||||
* 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 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 library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "cogl.h"
|
||||
#include "cogl-internal.h"
|
||||
#include "cogl-context.h"
|
||||
#include "cogl-journal-private.h"
|
||||
#include "cogl-texture-private.h"
|
||||
#include "cogl-material-private.h"
|
||||
#include "cogl-vertex-buffer-private.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <gmodule.h>
|
||||
#include <math.h>
|
||||
|
||||
#define _COGL_MAX_BEZ_RECURSE_DEPTH 16
|
||||
|
||||
#ifdef HAVE_COGL_GL
|
||||
|
||||
#define glGenBuffers ctx->drv.pf_glGenBuffersARB
|
||||
#define glBindBuffer ctx->drv.pf_glBindBufferARB
|
||||
#define glBufferData ctx->drv.pf_glBufferDataARB
|
||||
#define glBufferSubData ctx->drv.pf_glBufferSubDataARB
|
||||
#define glDeleteBuffers ctx->drv.pf_glDeleteBuffersARB
|
||||
#define glClientActiveTexture ctx->drv.pf_glClientActiveTexture
|
||||
|
||||
#elif defined (HAVE_COGL_GLES2)
|
||||
|
||||
#include "../gles/cogl-gles2-wrapper.h"
|
||||
|
||||
#endif
|
||||
|
||||
/* XXX NB:
|
||||
* Our journal's vertex data is arranged as follows:
|
||||
* 4 vertices per quad:
|
||||
* 2 or 3 GLfloats per position (3 when doing software transforms)
|
||||
* 4 RGBA GLubytes,
|
||||
* 2 GLfloats per tex coord * n_layers
|
||||
*
|
||||
* Where n_layers corresponds to the number of material layers enabled
|
||||
*
|
||||
* To avoid frequent changes in the stride of our vertex data we always pad
|
||||
* n_layers to be >= 2
|
||||
*
|
||||
* When we are transforming quads in software we need to also track the z
|
||||
* coordinate of transformed vertices.
|
||||
*
|
||||
* So for a given number of layers this gets the stride in 32bit words:
|
||||
*/
|
||||
#define SW_TRANSFORM (!(cogl_debug_flags & \
|
||||
COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))
|
||||
#define POS_STRIDE (SW_TRANSFORM ? 3 : 2) /* number of 32bit words */
|
||||
#define N_POS_COMPONENTS POS_STRIDE
|
||||
#define COLOR_STRIDE 1 /* number of 32bit words */
|
||||
#define TEX_STRIDE 2 /* number of 32bit words */
|
||||
#define MIN_LAYER_PADING 2
|
||||
#define GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS(N_LAYERS) \
|
||||
(POS_STRIDE + COLOR_STRIDE + \
|
||||
TEX_STRIDE * (N_LAYERS < MIN_LAYER_PADING ? MIN_LAYER_PADING : N_LAYERS))
|
||||
|
||||
typedef CoglVertexBufferIndices CoglJournalIndices;
|
||||
|
||||
typedef struct _CoglJournalFlushState
|
||||
{
|
||||
size_t stride;
|
||||
/* Note: this is a pointer to handle fallbacks. It normally holds a VBO
|
||||
* offset, but when the driver doesn't support VBOs then this points into
|
||||
* our GArray of logged vertices. */
|
||||
char * vbo_offset;
|
||||
GLuint vertex_offset;
|
||||
#ifndef HAVE_COGL_GL
|
||||
CoglJournalIndices *indices;
|
||||
size_t indices_type_size;
|
||||
#endif
|
||||
} CoglJournalFlushState;
|
||||
|
||||
typedef void (*CoglJournalBatchCallback) (CoglJournalEntry *start,
|
||||
int n_entries,
|
||||
void *data);
|
||||
typedef gboolean (*CoglJournalBatchTest) (CoglJournalEntry *entry0,
|
||||
CoglJournalEntry *entry1);
|
||||
|
||||
void
|
||||
_cogl_journal_dump_quad_vertices (guint8 *data, int n_layers)
|
||||
{
|
||||
size_t stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers);
|
||||
int i;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
g_print ("n_layers = %d; stride = %d; pos stride = %d; color stride = %d; "
|
||||
"tex stride = %d; stride in bytes = %d\n",
|
||||
n_layers, (int)stride, POS_STRIDE, COLOR_STRIDE,
|
||||
TEX_STRIDE, (int)stride * 4);
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
float *v = (float *)data + (i * stride);
|
||||
guint8 *c = data + (POS_STRIDE * 4) + (i * stride * 4);
|
||||
int j;
|
||||
|
||||
if (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)
|
||||
g_print ("v%d: x = %f, y = %f, rgba=0x%02X%02X%02X%02X",
|
||||
i, v[0], v[1], c[0], c[1], c[2], c[3]);
|
||||
else
|
||||
g_print ("v%d: x = %f, y = %f, z = %f, rgba=0x%02X%02X%02X%02X",
|
||||
i, v[0], v[1], v[2], c[0], c[1], c[2], c[3]);
|
||||
for (j = 0; j < n_layers; j++)
|
||||
{
|
||||
float *t = v + POS_STRIDE + COLOR_STRIDE + TEX_STRIDE * j;
|
||||
g_print (", tx%d = %f, ty%d = %f", j, t[0], j, t[1]);
|
||||
}
|
||||
g_print ("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_cogl_journal_dump_quad_batch (guint8 *data, int n_layers, int n_quads)
|
||||
{
|
||||
size_t byte_stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4;
|
||||
int i;
|
||||
|
||||
g_print ("_cogl_journal_dump_quad_batch: n_layers = %d, n_quads = %d\n",
|
||||
n_layers, n_quads);
|
||||
for (i = 0; i < n_quads; i++)
|
||||
_cogl_journal_dump_quad_vertices (data + byte_stride * 4 * i, n_layers);
|
||||
}
|
||||
|
||||
static void
|
||||
batch_and_call (CoglJournalEntry *entries,
|
||||
int n_entries,
|
||||
CoglJournalBatchTest can_batch_callback,
|
||||
CoglJournalBatchCallback batch_callback,
|
||||
void *data)
|
||||
{
|
||||
int i;
|
||||
int batch_len = 1;
|
||||
CoglJournalEntry *batch_start = entries;
|
||||
|
||||
for (i = 1; i < n_entries; i++)
|
||||
{
|
||||
CoglJournalEntry *entry0 = &entries[i - 1];
|
||||
CoglJournalEntry *entry1 = entry0 + 1;
|
||||
|
||||
if (can_batch_callback (entry0, entry1))
|
||||
{
|
||||
batch_len++;
|
||||
continue;
|
||||
}
|
||||
|
||||
batch_callback (batch_start, batch_len, data);
|
||||
|
||||
batch_start = entry1;
|
||||
batch_len = 1;
|
||||
}
|
||||
|
||||
/* The last batch... */
|
||||
batch_callback (batch_start, batch_len, data);
|
||||
}
|
||||
|
||||
static void
|
||||
_cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start,
|
||||
int batch_len,
|
||||
void *data)
|
||||
{
|
||||
CoglJournalFlushState *state = data;
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING))
|
||||
g_print ("BATCHING: modelview batch len = %d\n", batch_len);
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))
|
||||
GE (glLoadMatrixf ((GLfloat *)&batch_start->model_view));
|
||||
|
||||
#ifdef HAVE_COGL_GL
|
||||
|
||||
GE (glDrawArrays (GL_QUADS, state->vertex_offset, batch_len * 4));
|
||||
|
||||
#else /* HAVE_COGL_GL */
|
||||
|
||||
if (batch_len > 1)
|
||||
{
|
||||
int indices_offset = (state->vertex_offset / 4) * 6;
|
||||
GE (glDrawElements (GL_TRIANGLES,
|
||||
6 * batch_len,
|
||||
state->indices->type,
|
||||
(GLvoid*)(indices_offset * state->indices_type_size)));
|
||||
}
|
||||
else
|
||||
{
|
||||
GE (glDrawArrays (GL_TRIANGLE_FAN,
|
||||
state->vertex_offset, /* first */
|
||||
4)); /* n vertices */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* DEBUGGING CODE XXX:
|
||||
* This path will cause all rectangles to be drawn with a red, green
|
||||
* or blue outline with no blending. This may e.g. help with debugging
|
||||
* texture slicing issues or blending issues, plus it looks quite cool.
|
||||
*/
|
||||
if (cogl_debug_flags & COGL_DEBUG_RECTANGLES)
|
||||
{
|
||||
static CoglHandle outline = COGL_INVALID_HANDLE;
|
||||
static int color = 0;
|
||||
int i;
|
||||
if (outline == COGL_INVALID_HANDLE)
|
||||
outline = cogl_material_new ();
|
||||
|
||||
cogl_enable (COGL_ENABLE_VERTEX_ARRAY);
|
||||
for (i = 0; i < batch_len; i++, color = (color + 1) % 3)
|
||||
{
|
||||
cogl_material_set_color4ub (outline,
|
||||
color == 0 ? 0xff : 0x00,
|
||||
color == 1 ? 0xff : 0x00,
|
||||
color == 2 ? 0xff : 0x00,
|
||||
0xff);
|
||||
_cogl_material_flush_gl_state (outline, NULL);
|
||||
GE( glDrawArrays (GL_LINE_LOOP, 4 * i, 4) );
|
||||
}
|
||||
}
|
||||
|
||||
state->vertex_offset += (4 * batch_len);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
compare_entry_modelviews (CoglJournalEntry *entry0,
|
||||
CoglJournalEntry *entry1)
|
||||
{
|
||||
/* Batch together quads with the same model view matrix */
|
||||
|
||||
/* FIXME: this is nasty, there are much nicer ways to track this
|
||||
* (at the add_quad_vertices level) without resorting to a memcmp!
|
||||
*
|
||||
* E.g. If the cogl-current-matrix code maintained an "age" for
|
||||
* the modelview matrix we could simply check in add_quad_vertices
|
||||
* if the age has increased, and if so record the change as a
|
||||
* boolean in the journal.
|
||||
*/
|
||||
|
||||
if (memcmp (&entry0->model_view, &entry1->model_view,
|
||||
sizeof (GLfloat) * 16) == 0)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* At this point we have a run of quads that we know have compatible
|
||||
* materials, but they may not all have the same modelview matrix */
|
||||
static void
|
||||
_cogl_journal_flush_material_and_entries (CoglJournalEntry *batch_start,
|
||||
gint batch_len,
|
||||
void *data)
|
||||
{
|
||||
gulong enable_flags = 0;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING))
|
||||
g_print ("BATCHING: material batch len = %d\n", batch_len);
|
||||
|
||||
_cogl_material_flush_gl_state (batch_start->material,
|
||||
&batch_start->flush_options);
|
||||
|
||||
/* FIXME: This api is a bit yukky, ideally it will be removed if we
|
||||
* re-work the cogl_enable mechanism */
|
||||
enable_flags |= _cogl_material_get_cogl_enable_flags (batch_start->material);
|
||||
|
||||
if (ctx->enable_backface_culling)
|
||||
enable_flags |= COGL_ENABLE_BACKFACE_CULLING;
|
||||
|
||||
enable_flags |= COGL_ENABLE_VERTEX_ARRAY;
|
||||
enable_flags |= COGL_ENABLE_COLOR_ARRAY;
|
||||
cogl_enable (enable_flags);
|
||||
|
||||
/* If we haven't transformed the quads in software then we need to also break
|
||||
* up batches according to changes in the modelview matrix... */
|
||||
if (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)
|
||||
{
|
||||
batch_and_call (batch_start,
|
||||
batch_len,
|
||||
compare_entry_modelviews,
|
||||
_cogl_journal_flush_modelview_and_entries,
|
||||
data);
|
||||
}
|
||||
else
|
||||
_cogl_journal_flush_modelview_and_entries (batch_start, batch_len, data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
compare_entry_materials (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
|
||||
{
|
||||
/* batch rectangles using compatible materials */
|
||||
|
||||
/* XXX: _cogl_material_equal may give false negatives since it avoids
|
||||
* deep comparisons as an optimization. It aims to compare enough so
|
||||
* that we that we are able to batch the 90% common cases, but may not
|
||||
* look at less common differences. */
|
||||
if (_cogl_material_equal (entry0->material,
|
||||
&entry0->flush_options,
|
||||
entry1->material,
|
||||
&entry1->flush_options))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Since the stride may not reflect the number of texture layers in use
|
||||
* (due to padding) we deal with texture coordinate offsets separately
|
||||
* from vertex and color offsets... */
|
||||
static void
|
||||
_cogl_journal_flush_texcoord_vbo_offsets_and_entries (
|
||||
CoglJournalEntry *batch_start,
|
||||
gint batch_len,
|
||||
void *data)
|
||||
{
|
||||
CoglJournalFlushState *state = data;
|
||||
int prev_n_texcoord_arrays_enabled;
|
||||
int i;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
for (i = 0; i < batch_start->n_layers; i++)
|
||||
{
|
||||
GE (glClientActiveTexture (GL_TEXTURE0 + i));
|
||||
GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY));
|
||||
/* XXX NB:
|
||||
* Our journal's vertex data is arranged as follows:
|
||||
* 4 vertices per quad:
|
||||
* 2 or 3 GLfloats per position (3 when doing software transforms)
|
||||
* 4 RGBA GLubytes,
|
||||
* 2 GLfloats per tex coord * n_layers
|
||||
* (though n_layers may be padded; see definition of
|
||||
* GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details)
|
||||
*/
|
||||
GE (glTexCoordPointer (2, GL_FLOAT, state->stride,
|
||||
(void *)(state->vbo_offset +
|
||||
(POS_STRIDE + COLOR_STRIDE) * 4 +
|
||||
TEX_STRIDE * 4 * i)));
|
||||
}
|
||||
prev_n_texcoord_arrays_enabled =
|
||||
ctx->n_texcoord_arrays_enabled;
|
||||
ctx->n_texcoord_arrays_enabled = batch_start->n_layers;
|
||||
for (; i < prev_n_texcoord_arrays_enabled; i++)
|
||||
{
|
||||
GE (glClientActiveTexture (GL_TEXTURE0 + i));
|
||||
GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY));
|
||||
}
|
||||
|
||||
batch_and_call (batch_start,
|
||||
batch_len,
|
||||
compare_entry_materials,
|
||||
_cogl_journal_flush_material_and_entries,
|
||||
data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
compare_entry_n_layers (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
|
||||
{
|
||||
if (entry0->n_layers == entry1->n_layers)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* At this point we know the stride has changed from the previous batch
|
||||
* of journal entries */
|
||||
static void
|
||||
_cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start,
|
||||
gint batch_len,
|
||||
void *data)
|
||||
{
|
||||
CoglJournalFlushState *state = data;
|
||||
size_t stride;
|
||||
#ifndef HAVE_COGL_GL
|
||||
int needed_indices = batch_len * 6;
|
||||
CoglHandle indices_handle;
|
||||
CoglVertexBufferIndices *indices;
|
||||
#endif
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING))
|
||||
g_print ("BATCHING: vbo offset batch len = %d\n", batch_len);
|
||||
|
||||
/* XXX NB:
|
||||
* Our journal's vertex data is arranged as follows:
|
||||
* 4 vertices per quad:
|
||||
* 2 or 3 GLfloats per position (3 when doing software transforms)
|
||||
* 4 RGBA GLubytes,
|
||||
* 2 GLfloats per tex coord * n_layers
|
||||
* (though n_layers may be padded; see definition of
|
||||
* GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details)
|
||||
*/
|
||||
stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (batch_start->n_layers);
|
||||
stride *= sizeof (GLfloat);
|
||||
state->stride = stride;
|
||||
|
||||
GE (glVertexPointer (N_POS_COMPONENTS, GL_FLOAT, stride,
|
||||
(void *)state->vbo_offset));
|
||||
GE (glColorPointer (4, GL_UNSIGNED_BYTE, stride,
|
||||
(void *)(state->vbo_offset + (POS_STRIDE * 4))));
|
||||
|
||||
#ifndef HAVE_COGL_GL
|
||||
indices_handle = cogl_vertex_buffer_indices_get_for_quads (needed_indices);
|
||||
indices = _cogl_vertex_buffer_indices_pointer_from_handle (indices_handle);
|
||||
state->indices = indices;
|
||||
|
||||
if (indices->type == GL_UNSIGNED_BYTE)
|
||||
state->indices_type_size = 1;
|
||||
else if (indices->type == GL_UNSIGNED_SHORT)
|
||||
state->indices_type_size = 2;
|
||||
else
|
||||
g_critical ("unknown indices type %d", indices->type);
|
||||
|
||||
GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER,
|
||||
GPOINTER_TO_UINT (indices->vbo_name)));
|
||||
#endif
|
||||
|
||||
/* We only call gl{Vertex,Color,Texture}Pointer when the stride within
|
||||
* the VBO changes. (due to a change in the number of material layers)
|
||||
* While the stride remains constant we walk forward through the above
|
||||
* VBO using a vertex offset passed to glDraw{Arrays,Elements} */
|
||||
state->vertex_offset = 0;
|
||||
|
||||
if (cogl_debug_flags & COGL_DEBUG_JOURNAL)
|
||||
{
|
||||
guint8 *verts;
|
||||
|
||||
if (cogl_get_features () & COGL_FEATURE_VBOS)
|
||||
verts = ((guint8 *)ctx->logged_vertices->data) +
|
||||
(size_t)state->vbo_offset;
|
||||
else
|
||||
verts = (guint8 *)state->vbo_offset;
|
||||
_cogl_journal_dump_quad_batch (verts,
|
||||
batch_start->n_layers,
|
||||
batch_len);
|
||||
}
|
||||
|
||||
batch_and_call (batch_start,
|
||||
batch_len,
|
||||
compare_entry_n_layers,
|
||||
_cogl_journal_flush_texcoord_vbo_offsets_and_entries,
|
||||
data);
|
||||
|
||||
/* progress forward through the VBO containing all our vertices */
|
||||
state->vbo_offset += (stride * 4 * batch_len);
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_JOURNAL))
|
||||
g_print ("new vbo offset = %lu\n", (gulong)state->vbo_offset);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
compare_entry_strides (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
|
||||
{
|
||||
/* Currently the only thing that affects the stride for our vertex arrays
|
||||
* is the number of material layers. We need to update our VBO offsets
|
||||
* whenever the stride changes. */
|
||||
/* TODO: We should be padding the n_layers == 1 case as if it were
|
||||
* n_layers == 2 so we can reduce the need to split batches. */
|
||||
if (entry0->n_layers == entry1->n_layers ||
|
||||
(entry0->n_layers <= MIN_LAYER_PADING &&
|
||||
entry1->n_layers <= MIN_LAYER_PADING))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GLuint
|
||||
upload_vertices_to_vbo (GArray *vertices, CoglJournalFlushState *state)
|
||||
{
|
||||
size_t needed_vbo_len;
|
||||
GLuint journal_vbo;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, 0);
|
||||
|
||||
needed_vbo_len = vertices->len * sizeof (GLfloat);
|
||||
|
||||
g_assert (needed_vbo_len);
|
||||
GE (glGenBuffers (1, &journal_vbo));
|
||||
GE (glBindBuffer (GL_ARRAY_BUFFER, journal_vbo));
|
||||
GE (glBufferData (GL_ARRAY_BUFFER,
|
||||
needed_vbo_len,
|
||||
vertices->data,
|
||||
GL_STATIC_DRAW));
|
||||
|
||||
/* As we flush the journal entries in batches we walk forward through the
|
||||
* above VBO starting at offset 0... */
|
||||
state->vbo_offset = 0;
|
||||
|
||||
return journal_vbo;
|
||||
}
|
||||
|
||||
/* XXX NB: When _cogl_journal_flush() returns all state relating
|
||||
* to materials, all glEnable flags and current matrix state
|
||||
* is undefined.
|
||||
*/
|
||||
void
|
||||
_cogl_journal_flush (void)
|
||||
{
|
||||
CoglJournalFlushState state;
|
||||
int i;
|
||||
GLuint journal_vbo;
|
||||
gboolean vbo_fallback =
|
||||
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
if (ctx->journal->len == 0)
|
||||
return;
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING))
|
||||
g_print ("BATCHING: journal len = %d\n", ctx->journal->len);
|
||||
|
||||
/* Load all the vertex data we have accumulated so far into a single VBO
|
||||
* to minimize memory management costs within the GL driver. */
|
||||
if (!vbo_fallback)
|
||||
journal_vbo = upload_vertices_to_vbo (ctx->logged_vertices, &state);
|
||||
else
|
||||
state.vbo_offset = (char *)ctx->logged_vertices->data;
|
||||
|
||||
/* Since the journal deals with emitting the modelview matrices manually
|
||||
* we need to dirty our client side modelview matrix stack cache... */
|
||||
_cogl_current_matrix_state_dirty ();
|
||||
|
||||
/* And explicitly flush other matrix stacks... */
|
||||
_cogl_set_current_matrix (COGL_MATRIX_PROJECTION);
|
||||
_cogl_current_matrix_state_flush ();
|
||||
|
||||
/* If we have transformed all our quads at log time then the whole journal
|
||||
* then we ensure no further model transform is applied by loading the
|
||||
* identity matrix here...*/
|
||||
if (!(cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))
|
||||
{
|
||||
GE (glMatrixMode (GL_MODELVIEW));
|
||||
glLoadIdentity ();
|
||||
}
|
||||
|
||||
/* batch_and_call() batches a list of journal entries according to some
|
||||
* given criteria and calls a callback once for each determined batch.
|
||||
*
|
||||
* The process of flushing the journal is staggered to reduce the amount
|
||||
* of driver/GPU state changes necessary:
|
||||
* 1) We split the entries according to the stride of the vertices:
|
||||
* Each time the stride of our vertex data changes we need to call
|
||||
* gl{Vertex,Color}Pointer to inform GL of new VBO offsets.
|
||||
* Currently the only thing that affects the stride of our vertex data
|
||||
* is the number of material layers.
|
||||
* 2) We split the entries explicitly by the number of material layers:
|
||||
* We pad our vertex data when the number of layers is < 2 so that we
|
||||
* can minimize changes in stride. Each time the number of layers
|
||||
* changes we need to call glTexCoordPointer to inform GL of new VBO
|
||||
* offsets.
|
||||
* 3) We then split according to compatible Cogl materials:
|
||||
* This is where we flush material state
|
||||
* 4) Finally we split according to modelview matrix changes:
|
||||
* This is when we finally tell GL to draw something.
|
||||
* Note: Splitting by modelview changes is skipped when are doing the
|
||||
* vertex transformation in software at log time.
|
||||
*/
|
||||
batch_and_call ((CoglJournalEntry *)ctx->journal->data, /* first entry */
|
||||
ctx->journal->len, /* max number of entries to consider */
|
||||
compare_entry_strides,
|
||||
_cogl_journal_flush_vbo_offsets_and_entries, /* callback */
|
||||
&state); /* data */
|
||||
|
||||
for (i = 0; i < ctx->journal->len; i++)
|
||||
{
|
||||
CoglJournalEntry *entry =
|
||||
&g_array_index (ctx->journal, CoglJournalEntry, i);
|
||||
_cogl_material_journal_unref (entry->material);
|
||||
}
|
||||
|
||||
if (!vbo_fallback)
|
||||
GE (glDeleteBuffers (1, &journal_vbo));
|
||||
|
||||
g_array_set_size (ctx->journal, 0);
|
||||
g_array_set_size (ctx->logged_vertices, 0);
|
||||
}
|
||||
|
||||
void
|
||||
_cogl_journal_log_quad (float x_1,
|
||||
float y_1,
|
||||
float x_2,
|
||||
float y_2,
|
||||
CoglHandle material,
|
||||
int n_layers,
|
||||
guint32 fallback_layers,
|
||||
GLuint layer0_override_texture,
|
||||
float *tex_coords,
|
||||
unsigned int tex_coords_len)
|
||||
{
|
||||
size_t stride;
|
||||
size_t byte_stride;
|
||||
int next_vert;
|
||||
GLfloat *v;
|
||||
GLubyte *c;
|
||||
GLubyte *src_c;
|
||||
int i;
|
||||
int next_entry;
|
||||
guint32 disable_layers;
|
||||
CoglJournalEntry *entry;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
/* The vertex data is logged into a separate array in a layout that can be
|
||||
* directly passed to OpenGL
|
||||
*/
|
||||
|
||||
/* XXX: See definition of GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details
|
||||
* about how we pack our vertex data */
|
||||
stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers);
|
||||
/* NB: stride is in 32bit words */
|
||||
byte_stride = stride * 4;
|
||||
|
||||
next_vert = ctx->logged_vertices->len;
|
||||
g_array_set_size (ctx->logged_vertices, next_vert + 4 * stride);
|
||||
v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert);
|
||||
c = (GLubyte *)(v + POS_STRIDE);
|
||||
|
||||
/* XXX: All the jumping around to fill in this strided buffer doesn't
|
||||
* seem ideal. */
|
||||
|
||||
/* XXX: we could defer expanding the vertex data for GL until we come
|
||||
* to flushing the journal. */
|
||||
|
||||
/* FIXME: This is a hacky optimization, since it will break if we
|
||||
* change the definition of CoglColor: */
|
||||
_cogl_material_get_colorubv (material, c);
|
||||
src_c = c;
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
c += byte_stride;
|
||||
memcpy (c, src_c, 4);
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))
|
||||
{
|
||||
v[0] = x_1; v[1] = y_1;
|
||||
v += stride;
|
||||
v[0] = x_1; v[1] = y_2;
|
||||
v += stride;
|
||||
v[0] = x_2; v[1] = y_2;
|
||||
v += stride;
|
||||
v[0] = x_2; v[1] = y_1;
|
||||
}
|
||||
else
|
||||
{
|
||||
CoglMatrix mv;
|
||||
float x, y, z, w;
|
||||
|
||||
cogl_get_modelview_matrix (&mv);
|
||||
|
||||
x = x_1, y = y_1, z = 0; w = 1;
|
||||
cogl_matrix_transform_point (&mv, &x, &y, &z, &w);
|
||||
v[0] = x; v[1] = y; v[2] = z;
|
||||
v += stride;
|
||||
x = x_1, y = y_2, z = 0; w = 1;
|
||||
cogl_matrix_transform_point (&mv, &x, &y, &z, &w);
|
||||
v[0] = x; v[1] = y; v[2] = z;
|
||||
v += stride;
|
||||
x = x_2, y = y_2, z = 0; w = 1;
|
||||
cogl_matrix_transform_point (&mv, &x, &y, &z, &w);
|
||||
v[0] = x; v[1] = y; v[2] = z;
|
||||
v += stride;
|
||||
x = x_2, y = y_1, z = 0; w = 1;
|
||||
cogl_matrix_transform_point (&mv, &x, &y, &z, &w);
|
||||
v[0] = x; v[1] = y; v[2] = z;
|
||||
}
|
||||
|
||||
for (i = 0; i < n_layers; i++)
|
||||
{
|
||||
/* XXX: See definition of GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details
|
||||
* about how we pack our vertex data */
|
||||
GLfloat *t = &g_array_index (ctx->logged_vertices, GLfloat,
|
||||
next_vert + POS_STRIDE +
|
||||
COLOR_STRIDE + TEX_STRIDE * i);
|
||||
|
||||
t[0] = tex_coords[0]; t[1] = tex_coords[1];
|
||||
t += stride;
|
||||
t[0] = tex_coords[0]; t[1] = tex_coords[3];
|
||||
t += stride;
|
||||
t[0] = tex_coords[2]; t[1] = tex_coords[3];
|
||||
t += stride;
|
||||
t[0] = tex_coords[2]; t[1] = tex_coords[1];
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_JOURNAL))
|
||||
{
|
||||
g_print ("Logged new quad:\n");
|
||||
v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert);
|
||||
_cogl_journal_dump_quad_vertices ((guint8 *)v, n_layers);
|
||||
}
|
||||
|
||||
next_entry = ctx->journal->len;
|
||||
g_array_set_size (ctx->journal, next_entry + 1);
|
||||
entry = &g_array_index (ctx->journal, CoglJournalEntry, next_entry);
|
||||
|
||||
disable_layers = (1 << n_layers) - 1;
|
||||
disable_layers = ~disable_layers;
|
||||
|
||||
entry->material = _cogl_material_journal_ref (material);
|
||||
entry->n_layers = n_layers;
|
||||
entry->flush_options.flags =
|
||||
COGL_MATERIAL_FLUSH_FALLBACK_MASK |
|
||||
COGL_MATERIAL_FLUSH_DISABLE_MASK |
|
||||
COGL_MATERIAL_FLUSH_SKIP_GL_COLOR;
|
||||
entry->flush_options.fallback_layers = fallback_layers;
|
||||
entry->flush_options.disable_layers = disable_layers;
|
||||
if (layer0_override_texture)
|
||||
{
|
||||
entry->flush_options.flags |= COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE;
|
||||
entry->flush_options.layer0_override_texture = layer0_override_texture;
|
||||
}
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))
|
||||
cogl_get_modelview_matrix (&entry->model_view);
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING
|
||||
|| cogl_debug_flags & COGL_DEBUG_RECTANGLES))
|
||||
_cogl_journal_flush ();
|
||||
}
|
||||
|
@ -28,81 +28,16 @@
|
||||
#include "cogl.h"
|
||||
#include "cogl-internal.h"
|
||||
#include "cogl-context.h"
|
||||
#include "cogl-journal-private.h"
|
||||
#include "cogl-texture-private.h"
|
||||
#include "cogl-material-private.h"
|
||||
#include "cogl-vertex-buffer-private.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <gmodule.h>
|
||||
#include <math.h>
|
||||
|
||||
#define _COGL_MAX_BEZ_RECURSE_DEPTH 16
|
||||
|
||||
#ifdef HAVE_COGL_GL
|
||||
|
||||
#define glGenBuffers ctx->drv.pf_glGenBuffersARB
|
||||
#define glBindBuffer ctx->drv.pf_glBindBufferARB
|
||||
#define glBufferData ctx->drv.pf_glBufferDataARB
|
||||
#define glBufferSubData ctx->drv.pf_glBufferSubDataARB
|
||||
#define glDeleteBuffers ctx->drv.pf_glDeleteBuffersARB
|
||||
#define glClientActiveTexture ctx->drv.pf_glClientActiveTexture
|
||||
|
||||
#elif defined (HAVE_COGL_GLES2)
|
||||
|
||||
#include "../gles/cogl-gles2-wrapper.h"
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* XXX NB:
|
||||
* Our journal's vertex data is arranged as follows:
|
||||
* 4 vertices per quad:
|
||||
* 2 or 3 GLfloats per position (3 when doing software transforms)
|
||||
* 4 RGBA GLubytes,
|
||||
* 2 GLfloats per tex coord * n_layers
|
||||
*
|
||||
* Where n_layers corresponds to the number of material layers enabled
|
||||
*
|
||||
* To avoid frequent changes in the stride of our vertex data we always pad
|
||||
* n_layers to be >= 2
|
||||
*
|
||||
* When we are transforming quads in software we need to also track the z
|
||||
* coordinate of transformed vertices.
|
||||
*
|
||||
* So for a given number of layers this gets the stride in 32bit words:
|
||||
*/
|
||||
#define SW_TRANSFORM (!(cogl_debug_flags & \
|
||||
COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))
|
||||
#define POS_STRIDE (SW_TRANSFORM ? 3 : 2) /* number of 32bit words */
|
||||
#define N_POS_COMPONENTS POS_STRIDE
|
||||
#define COLOR_STRIDE 1 /* number of 32bit words */
|
||||
#define TEX_STRIDE 2 /* number of 32bit words */
|
||||
#define MIN_LAYER_PADING 2
|
||||
#define GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS(N_LAYERS) \
|
||||
(POS_STRIDE + COLOR_STRIDE + \
|
||||
TEX_STRIDE * (N_LAYERS < MIN_LAYER_PADING ? MIN_LAYER_PADING : N_LAYERS))
|
||||
|
||||
|
||||
typedef void (*CoglJournalBatchCallback) (CoglJournalEntry *start,
|
||||
int n_entries,
|
||||
void *data);
|
||||
typedef gboolean (*CoglJournalBatchTest) (CoglJournalEntry *entry0,
|
||||
CoglJournalEntry *entry1);
|
||||
typedef CoglVertexBufferIndices CoglJournalIndices;
|
||||
|
||||
typedef struct _CoglJournalFlushState
|
||||
{
|
||||
size_t stride;
|
||||
/* Note: this is a pointer to handle fallbacks. It normally holds a VBO
|
||||
* offset, but when the driver doesn't support VBOs then this points into
|
||||
* our GArray of logged vertices. */
|
||||
char * vbo_offset;
|
||||
GLuint vertex_offset;
|
||||
#ifndef HAVE_COGL_GL
|
||||
CoglJournalIndices *indices;
|
||||
size_t indices_type_size;
|
||||
#endif
|
||||
} CoglJournalFlushState;
|
||||
|
||||
typedef struct _TextureSlicedQuadState
|
||||
{
|
||||
@ -133,644 +68,6 @@ void _cogl_path_add_node (gboolean new_sub_path,
|
||||
void _cogl_path_fill_nodes ();
|
||||
void _cogl_path_stroke_nodes ();
|
||||
|
||||
void
|
||||
_cogl_journal_dump_quad_vertices (guint8 *data, int n_layers)
|
||||
{
|
||||
size_t stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers);
|
||||
int i;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
g_print ("n_layers = %d; stride = %d; pos stride = %d; color stride = %d; "
|
||||
"tex stride = %d; stride in bytes = %d\n",
|
||||
n_layers, (int)stride, POS_STRIDE, COLOR_STRIDE,
|
||||
TEX_STRIDE, (int)stride * 4);
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
float *v = (float *)data + (i * stride);
|
||||
guint8 *c = data + (POS_STRIDE * 4) + (i * stride * 4);
|
||||
int j;
|
||||
|
||||
if (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)
|
||||
g_print ("v%d: x = %f, y = %f, rgba=0x%02X%02X%02X%02X",
|
||||
i, v[0], v[1], c[0], c[1], c[2], c[3]);
|
||||
else
|
||||
g_print ("v%d: x = %f, y = %f, z = %f, rgba=0x%02X%02X%02X%02X",
|
||||
i, v[0], v[1], v[2], c[0], c[1], c[2], c[3]);
|
||||
for (j = 0; j < n_layers; j++)
|
||||
{
|
||||
float *t = v + POS_STRIDE + COLOR_STRIDE + TEX_STRIDE * j;
|
||||
g_print (", tx%d = %f, ty%d = %f", j, t[0], j, t[1]);
|
||||
}
|
||||
g_print ("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_cogl_journal_dump_quad_batch (guint8 *data, int n_layers, int n_quads)
|
||||
{
|
||||
size_t byte_stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4;
|
||||
int i;
|
||||
|
||||
g_print ("_cogl_journal_dump_quad_batch: n_layers = %d, n_quads = %d\n",
|
||||
n_layers, n_quads);
|
||||
for (i = 0; i < n_quads; i++)
|
||||
_cogl_journal_dump_quad_vertices (data + byte_stride * 4 * i, n_layers);
|
||||
}
|
||||
|
||||
static void
|
||||
batch_and_call (CoglJournalEntry *entries,
|
||||
int n_entries,
|
||||
CoglJournalBatchTest can_batch_callback,
|
||||
CoglJournalBatchCallback batch_callback,
|
||||
void *data)
|
||||
{
|
||||
int i;
|
||||
int batch_len = 1;
|
||||
CoglJournalEntry *batch_start = entries;
|
||||
|
||||
for (i = 1; i < n_entries; i++)
|
||||
{
|
||||
CoglJournalEntry *entry0 = &entries[i - 1];
|
||||
CoglJournalEntry *entry1 = entry0 + 1;
|
||||
|
||||
if (can_batch_callback (entry0, entry1))
|
||||
{
|
||||
batch_len++;
|
||||
continue;
|
||||
}
|
||||
|
||||
batch_callback (batch_start, batch_len, data);
|
||||
|
||||
batch_start = entry1;
|
||||
batch_len = 1;
|
||||
}
|
||||
|
||||
/* The last batch... */
|
||||
batch_callback (batch_start, batch_len, data);
|
||||
}
|
||||
|
||||
static void
|
||||
_cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start,
|
||||
int batch_len,
|
||||
void *data)
|
||||
{
|
||||
CoglJournalFlushState *state = data;
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING))
|
||||
g_print ("BATCHING: modelview batch len = %d\n", batch_len);
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))
|
||||
GE (glLoadMatrixf ((GLfloat *)&batch_start->model_view));
|
||||
|
||||
#ifdef HAVE_COGL_GL
|
||||
|
||||
GE (glDrawArrays (GL_QUADS, state->vertex_offset, batch_len * 4));
|
||||
|
||||
#else /* HAVE_COGL_GL */
|
||||
|
||||
if (batch_len > 1)
|
||||
{
|
||||
int indices_offset = (state->vertex_offset / 4) * 6;
|
||||
GE (glDrawElements (GL_TRIANGLES,
|
||||
6 * batch_len,
|
||||
state->indices->type,
|
||||
(GLvoid*)(indices_offset * state->indices_type_size)));
|
||||
}
|
||||
else
|
||||
{
|
||||
GE (glDrawArrays (GL_TRIANGLE_FAN,
|
||||
state->vertex_offset, /* first */
|
||||
4)); /* n vertices */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* DEBUGGING CODE XXX:
|
||||
* This path will cause all rectangles to be drawn with a red, green
|
||||
* or blue outline with no blending. This may e.g. help with debugging
|
||||
* texture slicing issues or blending issues, plus it looks quite cool.
|
||||
*/
|
||||
if (cogl_debug_flags & COGL_DEBUG_RECTANGLES)
|
||||
{
|
||||
static CoglHandle outline = COGL_INVALID_HANDLE;
|
||||
static int color = 0;
|
||||
int i;
|
||||
if (outline == COGL_INVALID_HANDLE)
|
||||
outline = cogl_material_new ();
|
||||
|
||||
cogl_enable (COGL_ENABLE_VERTEX_ARRAY);
|
||||
for (i = 0; i < batch_len; i++, color = (color + 1) % 3)
|
||||
{
|
||||
cogl_material_set_color4ub (outline,
|
||||
color == 0 ? 0xff : 0x00,
|
||||
color == 1 ? 0xff : 0x00,
|
||||
color == 2 ? 0xff : 0x00,
|
||||
0xff);
|
||||
_cogl_material_flush_gl_state (outline, NULL);
|
||||
GE( glDrawArrays (GL_LINE_LOOP, 4 * i, 4) );
|
||||
}
|
||||
}
|
||||
|
||||
state->vertex_offset += (4 * batch_len);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
compare_entry_modelviews (CoglJournalEntry *entry0,
|
||||
CoglJournalEntry *entry1)
|
||||
{
|
||||
/* Batch together quads with the same model view matrix */
|
||||
|
||||
/* FIXME: this is nasty, there are much nicer ways to track this
|
||||
* (at the add_quad_vertices level) without resorting to a memcmp!
|
||||
*
|
||||
* E.g. If the cogl-current-matrix code maintained an "age" for
|
||||
* the modelview matrix we could simply check in add_quad_vertices
|
||||
* if the age has increased, and if so record the change as a
|
||||
* boolean in the journal.
|
||||
*/
|
||||
|
||||
if (memcmp (&entry0->model_view, &entry1->model_view,
|
||||
sizeof (GLfloat) * 16) == 0)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* At this point we have a run of quads that we know have compatible
|
||||
* materials, but they may not all have the same modelview matrix */
|
||||
static void
|
||||
_cogl_journal_flush_material_and_entries (CoglJournalEntry *batch_start,
|
||||
gint batch_len,
|
||||
void *data)
|
||||
{
|
||||
gulong enable_flags = 0;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING))
|
||||
g_print ("BATCHING: material batch len = %d\n", batch_len);
|
||||
|
||||
_cogl_material_flush_gl_state (batch_start->material,
|
||||
&batch_start->flush_options);
|
||||
|
||||
/* FIXME: This api is a bit yukky, ideally it will be removed if we
|
||||
* re-work the cogl_enable mechanism */
|
||||
enable_flags |= _cogl_material_get_cogl_enable_flags (batch_start->material);
|
||||
|
||||
if (ctx->enable_backface_culling)
|
||||
enable_flags |= COGL_ENABLE_BACKFACE_CULLING;
|
||||
|
||||
enable_flags |= COGL_ENABLE_VERTEX_ARRAY;
|
||||
enable_flags |= COGL_ENABLE_COLOR_ARRAY;
|
||||
cogl_enable (enable_flags);
|
||||
|
||||
/* If we haven't transformed the quads in software then we need to also break
|
||||
* up batches according to changes in the modelview matrix... */
|
||||
if (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)
|
||||
{
|
||||
batch_and_call (batch_start,
|
||||
batch_len,
|
||||
compare_entry_modelviews,
|
||||
_cogl_journal_flush_modelview_and_entries,
|
||||
data);
|
||||
}
|
||||
else
|
||||
_cogl_journal_flush_modelview_and_entries (batch_start, batch_len, data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
compare_entry_materials (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
|
||||
{
|
||||
/* batch rectangles using compatible materials */
|
||||
|
||||
/* XXX: _cogl_material_equal may give false negatives since it avoids
|
||||
* deep comparisons as an optimization. It aims to compare enough so
|
||||
* that we that we are able to batch the 90% common cases, but may not
|
||||
* look at less common differences. */
|
||||
if (_cogl_material_equal (entry0->material,
|
||||
&entry0->flush_options,
|
||||
entry1->material,
|
||||
&entry1->flush_options))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Since the stride may not reflect the number of texture layers in use
|
||||
* (due to padding) we deal with texture coordinate offsets separately
|
||||
* from vertex and color offsets... */
|
||||
static void
|
||||
_cogl_journal_flush_texcoord_vbo_offsets_and_entries (
|
||||
CoglJournalEntry *batch_start,
|
||||
gint batch_len,
|
||||
void *data)
|
||||
{
|
||||
CoglJournalFlushState *state = data;
|
||||
int prev_n_texcoord_arrays_enabled;
|
||||
int i;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
for (i = 0; i < batch_start->n_layers; i++)
|
||||
{
|
||||
GE (glClientActiveTexture (GL_TEXTURE0 + i));
|
||||
GE (glEnableClientState (GL_TEXTURE_COORD_ARRAY));
|
||||
/* XXX NB:
|
||||
* Our journal's vertex data is arranged as follows:
|
||||
* 4 vertices per quad:
|
||||
* 2 or 3 GLfloats per position (3 when doing software transforms)
|
||||
* 4 RGBA GLubytes,
|
||||
* 2 GLfloats per tex coord * n_layers
|
||||
* (though n_layers may be padded; see definition of
|
||||
* GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details)
|
||||
*/
|
||||
GE (glTexCoordPointer (2, GL_FLOAT, state->stride,
|
||||
(void *)(state->vbo_offset +
|
||||
(POS_STRIDE + COLOR_STRIDE) * 4 +
|
||||
TEX_STRIDE * 4 * i)));
|
||||
}
|
||||
prev_n_texcoord_arrays_enabled =
|
||||
ctx->n_texcoord_arrays_enabled;
|
||||
ctx->n_texcoord_arrays_enabled = batch_start->n_layers;
|
||||
for (; i < prev_n_texcoord_arrays_enabled; i++)
|
||||
{
|
||||
GE (glClientActiveTexture (GL_TEXTURE0 + i));
|
||||
GE (glDisableClientState (GL_TEXTURE_COORD_ARRAY));
|
||||
}
|
||||
|
||||
batch_and_call (batch_start,
|
||||
batch_len,
|
||||
compare_entry_materials,
|
||||
_cogl_journal_flush_material_and_entries,
|
||||
data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
compare_entry_n_layers (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
|
||||
{
|
||||
if (entry0->n_layers == entry1->n_layers)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* At this point we know the stride has changed from the previous batch
|
||||
* of journal entries */
|
||||
static void
|
||||
_cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start,
|
||||
gint batch_len,
|
||||
void *data)
|
||||
{
|
||||
CoglJournalFlushState *state = data;
|
||||
size_t stride;
|
||||
#ifndef HAVE_COGL_GL
|
||||
int needed_indices = batch_len * 6;
|
||||
CoglHandle indices_handle;
|
||||
CoglVertexBufferIndices *indices;
|
||||
#endif
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING))
|
||||
g_print ("BATCHING: vbo offset batch len = %d\n", batch_len);
|
||||
|
||||
/* XXX NB:
|
||||
* Our journal's vertex data is arranged as follows:
|
||||
* 4 vertices per quad:
|
||||
* 2 or 3 GLfloats per position (3 when doing software transforms)
|
||||
* 4 RGBA GLubytes,
|
||||
* 2 GLfloats per tex coord * n_layers
|
||||
* (though n_layers may be padded; see definition of
|
||||
* GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details)
|
||||
*/
|
||||
stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (batch_start->n_layers);
|
||||
stride *= sizeof (GLfloat);
|
||||
state->stride = stride;
|
||||
|
||||
GE (glVertexPointer (N_POS_COMPONENTS, GL_FLOAT, stride,
|
||||
(void *)state->vbo_offset));
|
||||
GE (glColorPointer (4, GL_UNSIGNED_BYTE, stride,
|
||||
(void *)(state->vbo_offset + (POS_STRIDE * 4))));
|
||||
|
||||
#ifndef HAVE_COGL_GL
|
||||
indices_handle = cogl_vertex_buffer_indices_get_for_quads (needed_indices);
|
||||
indices = _cogl_vertex_buffer_indices_pointer_from_handle (indices_handle);
|
||||
state->indices = indices;
|
||||
|
||||
if (indices->type == GL_UNSIGNED_BYTE)
|
||||
state->indices_type_size = 1;
|
||||
else if (indices->type == GL_UNSIGNED_SHORT)
|
||||
state->indices_type_size = 2;
|
||||
else
|
||||
g_critical ("unknown indices type %d", indices->type);
|
||||
|
||||
GE (glBindBuffer (GL_ELEMENT_ARRAY_BUFFER,
|
||||
GPOINTER_TO_UINT (indices->vbo_name)));
|
||||
#endif
|
||||
|
||||
/* We only call gl{Vertex,Color,Texture}Pointer when the stride within
|
||||
* the VBO changes. (due to a change in the number of material layers)
|
||||
* While the stride remains constant we walk forward through the above
|
||||
* VBO using a vertex offset passed to glDraw{Arrays,Elements} */
|
||||
state->vertex_offset = 0;
|
||||
|
||||
if (cogl_debug_flags & COGL_DEBUG_JOURNAL)
|
||||
{
|
||||
guint8 *verts;
|
||||
|
||||
if (cogl_get_features () & COGL_FEATURE_VBOS)
|
||||
verts = ((guint8 *)ctx->logged_vertices->data) +
|
||||
(size_t)state->vbo_offset;
|
||||
else
|
||||
verts = (guint8 *)state->vbo_offset;
|
||||
_cogl_journal_dump_quad_batch (verts,
|
||||
batch_start->n_layers,
|
||||
batch_len);
|
||||
}
|
||||
|
||||
batch_and_call (batch_start,
|
||||
batch_len,
|
||||
compare_entry_n_layers,
|
||||
_cogl_journal_flush_texcoord_vbo_offsets_and_entries,
|
||||
data);
|
||||
|
||||
/* progress forward through the VBO containing all our vertices */
|
||||
state->vbo_offset += (stride * 4 * batch_len);
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_JOURNAL))
|
||||
g_print ("new vbo offset = %lu\n", (gulong)state->vbo_offset);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
compare_entry_strides (CoglJournalEntry *entry0, CoglJournalEntry *entry1)
|
||||
{
|
||||
/* Currently the only thing that affects the stride for our vertex arrays
|
||||
* is the number of material layers. We need to update our VBO offsets
|
||||
* whenever the stride changes. */
|
||||
/* TODO: We should be padding the n_layers == 1 case as if it were
|
||||
* n_layers == 2 so we can reduce the need to split batches. */
|
||||
if (entry0->n_layers == entry1->n_layers ||
|
||||
(entry0->n_layers <= MIN_LAYER_PADING &&
|
||||
entry1->n_layers <= MIN_LAYER_PADING))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static GLuint
|
||||
upload_vertices_to_vbo (GArray *vertices, CoglJournalFlushState *state)
|
||||
{
|
||||
size_t needed_vbo_len;
|
||||
GLuint journal_vbo;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, 0);
|
||||
|
||||
needed_vbo_len = vertices->len * sizeof (GLfloat);
|
||||
|
||||
g_assert (needed_vbo_len);
|
||||
GE (glGenBuffers (1, &journal_vbo));
|
||||
GE (glBindBuffer (GL_ARRAY_BUFFER, journal_vbo));
|
||||
GE (glBufferData (GL_ARRAY_BUFFER,
|
||||
needed_vbo_len,
|
||||
vertices->data,
|
||||
GL_STATIC_DRAW));
|
||||
|
||||
/* As we flush the journal entries in batches we walk forward through the
|
||||
* above VBO starting at offset 0... */
|
||||
state->vbo_offset = 0;
|
||||
|
||||
return journal_vbo;
|
||||
}
|
||||
|
||||
/* XXX NB: When _cogl_journal_flush() returns all state relating
|
||||
* to materials, all glEnable flags and current matrix state
|
||||
* is undefined.
|
||||
*/
|
||||
void
|
||||
_cogl_journal_flush (void)
|
||||
{
|
||||
CoglJournalFlushState state;
|
||||
int i;
|
||||
GLuint journal_vbo;
|
||||
gboolean vbo_fallback =
|
||||
(cogl_get_features () & COGL_FEATURE_VBOS) ? FALSE : TRUE;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
if (ctx->journal->len == 0)
|
||||
return;
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_BATCHING))
|
||||
g_print ("BATCHING: journal len = %d\n", ctx->journal->len);
|
||||
|
||||
/* Load all the vertex data we have accumulated so far into a single VBO
|
||||
* to minimize memory management costs within the GL driver. */
|
||||
if (!vbo_fallback)
|
||||
journal_vbo = upload_vertices_to_vbo (ctx->logged_vertices, &state);
|
||||
else
|
||||
state.vbo_offset = (char *)ctx->logged_vertices->data;
|
||||
|
||||
/* Since the journal deals with emitting the modelview matrices manually
|
||||
* we need to dirty our client side modelview matrix stack cache... */
|
||||
_cogl_current_matrix_state_dirty ();
|
||||
|
||||
/* And explicitly flush other matrix stacks... */
|
||||
_cogl_set_current_matrix (COGL_MATRIX_PROJECTION);
|
||||
_cogl_current_matrix_state_flush ();
|
||||
|
||||
/* If we have transformed all our quads at log time then the whole journal
|
||||
* then we ensure no further model transform is applied by loading the
|
||||
* identity matrix here...*/
|
||||
if (!(cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))
|
||||
{
|
||||
GE (glMatrixMode (GL_MODELVIEW));
|
||||
glLoadIdentity ();
|
||||
}
|
||||
|
||||
/* batch_and_call() batches a list of journal entries according to some
|
||||
* given criteria and calls a callback once for each determined batch.
|
||||
*
|
||||
* The process of flushing the journal is staggered to reduce the amount
|
||||
* of driver/GPU state changes necessary:
|
||||
* 1) We split the entries according to the stride of the vertices:
|
||||
* Each time the stride of our vertex data changes we need to call
|
||||
* gl{Vertex,Color}Pointer to inform GL of new VBO offsets.
|
||||
* Currently the only thing that affects the stride of our vertex data
|
||||
* is the number of material layers.
|
||||
* 2) We split the entries explicitly by the number of material layers:
|
||||
* We pad our vertex data when the number of layers is < 2 so that we
|
||||
* can minimize changes in stride. Each time the number of layers
|
||||
* changes we need to call glTexCoordPointer to inform GL of new VBO
|
||||
* offsets.
|
||||
* 3) We then split according to compatible Cogl materials:
|
||||
* This is where we flush material state
|
||||
* 4) Finally we split according to modelview matrix changes:
|
||||
* This is when we finally tell GL to draw something.
|
||||
* Note: Splitting by modelview changes is skipped when are doing the
|
||||
* vertex transformation in software at log time.
|
||||
*/
|
||||
batch_and_call ((CoglJournalEntry *)ctx->journal->data, /* first entry */
|
||||
ctx->journal->len, /* max number of entries to consider */
|
||||
compare_entry_strides,
|
||||
_cogl_journal_flush_vbo_offsets_and_entries, /* callback */
|
||||
&state); /* data */
|
||||
|
||||
for (i = 0; i < ctx->journal->len; i++)
|
||||
{
|
||||
CoglJournalEntry *entry =
|
||||
&g_array_index (ctx->journal, CoglJournalEntry, i);
|
||||
_cogl_material_journal_unref (entry->material);
|
||||
}
|
||||
|
||||
if (!vbo_fallback)
|
||||
GE (glDeleteBuffers (1, &journal_vbo));
|
||||
|
||||
g_array_set_size (ctx->journal, 0);
|
||||
g_array_set_size (ctx->logged_vertices, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
_cogl_journal_log_quad (float x_1,
|
||||
float y_1,
|
||||
float x_2,
|
||||
float y_2,
|
||||
CoglHandle material,
|
||||
int n_layers,
|
||||
guint32 fallback_layers,
|
||||
GLuint layer0_override_texture,
|
||||
float *tex_coords,
|
||||
guint tex_coords_len)
|
||||
{
|
||||
size_t stride;
|
||||
size_t byte_stride;
|
||||
int next_vert;
|
||||
GLfloat *v;
|
||||
GLubyte *c;
|
||||
GLubyte *src_c;
|
||||
int i;
|
||||
int next_entry;
|
||||
guint32 disable_layers;
|
||||
CoglJournalEntry *entry;
|
||||
|
||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
||||
|
||||
/* The vertex data is logged into a separate array in a layout that can be
|
||||
* directly passed to OpenGL
|
||||
*/
|
||||
|
||||
/* XXX: See definition of GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details
|
||||
* about how we pack our vertex data */
|
||||
stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers);
|
||||
/* NB: stride is in 32bit words */
|
||||
byte_stride = stride * 4;
|
||||
|
||||
next_vert = ctx->logged_vertices->len;
|
||||
g_array_set_size (ctx->logged_vertices, next_vert + 4 * stride);
|
||||
v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert);
|
||||
c = (GLubyte *)(v + POS_STRIDE);
|
||||
|
||||
/* XXX: All the jumping around to fill in this strided buffer doesn't
|
||||
* seem ideal. */
|
||||
|
||||
/* XXX: we could defer expanding the vertex data for GL until we come
|
||||
* to flushing the journal. */
|
||||
|
||||
/* FIXME: This is a hacky optimization, since it will break if we
|
||||
* change the definition of CoglColor: */
|
||||
_cogl_material_get_colorubv (material, c);
|
||||
src_c = c;
|
||||
for (i = 0; i < 3; i++)
|
||||
{
|
||||
c += byte_stride;
|
||||
memcpy (c, src_c, 4);
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))
|
||||
{
|
||||
v[0] = x_1; v[1] = y_1;
|
||||
v += stride;
|
||||
v[0] = x_1; v[1] = y_2;
|
||||
v += stride;
|
||||
v[0] = x_2; v[1] = y_2;
|
||||
v += stride;
|
||||
v[0] = x_2; v[1] = y_1;
|
||||
}
|
||||
else
|
||||
{
|
||||
CoglMatrix mv;
|
||||
float x, y, z, w;
|
||||
|
||||
cogl_get_modelview_matrix (&mv);
|
||||
|
||||
x = x_1, y = y_1, z = 0; w = 1;
|
||||
cogl_matrix_transform_point (&mv, &x, &y, &z, &w);
|
||||
v[0] = x; v[1] = y; v[2] = z;
|
||||
v += stride;
|
||||
x = x_1, y = y_2, z = 0; w = 1;
|
||||
cogl_matrix_transform_point (&mv, &x, &y, &z, &w);
|
||||
v[0] = x; v[1] = y; v[2] = z;
|
||||
v += stride;
|
||||
x = x_2, y = y_2, z = 0; w = 1;
|
||||
cogl_matrix_transform_point (&mv, &x, &y, &z, &w);
|
||||
v[0] = x; v[1] = y; v[2] = z;
|
||||
v += stride;
|
||||
x = x_2, y = y_1, z = 0; w = 1;
|
||||
cogl_matrix_transform_point (&mv, &x, &y, &z, &w);
|
||||
v[0] = x; v[1] = y; v[2] = z;
|
||||
}
|
||||
|
||||
for (i = 0; i < n_layers; i++)
|
||||
{
|
||||
/* XXX: See definition of GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details
|
||||
* about how we pack our vertex data */
|
||||
GLfloat *t = &g_array_index (ctx->logged_vertices, GLfloat,
|
||||
next_vert + POS_STRIDE +
|
||||
COLOR_STRIDE + TEX_STRIDE * i);
|
||||
|
||||
t[0] = tex_coords[0]; t[1] = tex_coords[1];
|
||||
t += stride;
|
||||
t[0] = tex_coords[0]; t[1] = tex_coords[3];
|
||||
t += stride;
|
||||
t[0] = tex_coords[2]; t[1] = tex_coords[3];
|
||||
t += stride;
|
||||
t[0] = tex_coords[2]; t[1] = tex_coords[1];
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_JOURNAL))
|
||||
{
|
||||
g_print ("Logged new quad:\n");
|
||||
v = &g_array_index (ctx->logged_vertices, GLfloat, next_vert);
|
||||
_cogl_journal_dump_quad_vertices ((guint8 *)v, n_layers);
|
||||
}
|
||||
|
||||
next_entry = ctx->journal->len;
|
||||
g_array_set_size (ctx->journal, next_entry + 1);
|
||||
entry = &g_array_index (ctx->journal, CoglJournalEntry, next_entry);
|
||||
|
||||
disable_layers = (1 << n_layers) - 1;
|
||||
disable_layers = ~disable_layers;
|
||||
|
||||
entry->material = _cogl_material_journal_ref (material);
|
||||
entry->n_layers = n_layers;
|
||||
entry->flush_options.flags =
|
||||
COGL_MATERIAL_FLUSH_FALLBACK_MASK |
|
||||
COGL_MATERIAL_FLUSH_DISABLE_MASK |
|
||||
COGL_MATERIAL_FLUSH_SKIP_GL_COLOR;
|
||||
entry->flush_options.fallback_layers = fallback_layers;
|
||||
entry->flush_options.disable_layers = disable_layers;
|
||||
if (layer0_override_texture)
|
||||
{
|
||||
entry->flush_options.flags |= COGL_MATERIAL_FLUSH_LAYER0_OVERRIDE;
|
||||
entry->flush_options.layer0_override_texture = layer0_override_texture;
|
||||
}
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))
|
||||
cogl_get_modelview_matrix (&entry->model_view);
|
||||
|
||||
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_BATCHING
|
||||
|| cogl_debug_flags & COGL_DEBUG_RECTANGLES))
|
||||
_cogl_journal_flush ();
|
||||
}
|
||||
|
||||
static void
|
||||
log_quad_sub_textures_cb (CoglHandle texture_handle,
|
||||
GLuint gl_handle,
|
||||
|
@ -55,20 +55,6 @@ struct _CoglTexture
|
||||
gboolean mipmaps_dirty;
|
||||
};
|
||||
|
||||
/* To improve batching of geometry when submitting vertices to OpenGL we
|
||||
* log the texture rectangles we want to draw to a journal, so when we
|
||||
* later flush the journal we aim to batch data, and gl draw calls. */
|
||||
typedef struct _CoglJournalEntry
|
||||
{
|
||||
CoglHandle material;
|
||||
int n_layers;
|
||||
CoglMaterialFlushOptions flush_options;
|
||||
CoglMatrix model_view;
|
||||
/* XXX: These entries are pretty big now considering the padding in
|
||||
* CoglMaterialFlushOptions and CoglMatrix, so we might need to optimize this
|
||||
* later. */
|
||||
} CoglJournalEntry;
|
||||
|
||||
typedef void (*CoglTextureSliceCallback) (CoglHandle handle,
|
||||
GLuint gl_handle,
|
||||
GLenum gl_target,
|
||||
|
Loading…
Reference in New Issue
Block a user