forked from AuroraMiddleware/gtk
05928608ec
Try to handle two different values for scale in horizontal and vertical direction better. Fixes #3431
853 lines
23 KiB
C
853 lines
23 KiB
C
#include "config.h"
|
|
|
|
#include "gskgldriverprivate.h"
|
|
|
|
#include "gskdebugprivate.h"
|
|
#include "gskprofilerprivate.h"
|
|
#include "gdk/gdkglcontextprivate.h"
|
|
#include "gdk/gdktextureprivate.h"
|
|
#include "gdk/gdkgltextureprivate.h"
|
|
#include "gdkmemorytextureprivate.h"
|
|
|
|
#include <gdk/gdk.h>
|
|
#include <epoxy/gl.h>
|
|
|
|
typedef struct {
|
|
GLuint fbo_id;
|
|
GLuint depth_stencil_id;
|
|
} Fbo;
|
|
|
|
typedef struct {
|
|
GLuint texture_id;
|
|
int width;
|
|
int height;
|
|
GLuint min_filter;
|
|
GLuint mag_filter;
|
|
Fbo fbo;
|
|
GdkTexture *user;
|
|
guint in_use : 1;
|
|
guint permanent : 1;
|
|
|
|
/* TODO: Make this optional and not for every texture... */
|
|
TextureSlice *slices;
|
|
guint n_slices;
|
|
} Texture;
|
|
|
|
struct _GskGLDriver
|
|
{
|
|
GObject parent_instance;
|
|
|
|
GdkGLContext *gl_context;
|
|
GskProfiler *profiler;
|
|
struct {
|
|
GQuark created_textures;
|
|
GQuark reused_textures;
|
|
GQuark surface_uploads;
|
|
} counters;
|
|
|
|
Fbo default_fbo;
|
|
|
|
GHashTable *textures; /* texture_id -> Texture */
|
|
GHashTable *pointer_textures; /* pointer -> texture_id */
|
|
|
|
const Texture *bound_source_texture;
|
|
|
|
int max_texture_size;
|
|
|
|
gboolean in_frame : 1;
|
|
};
|
|
|
|
G_DEFINE_TYPE (GskGLDriver, gsk_gl_driver, G_TYPE_OBJECT)
|
|
|
|
static void
|
|
upload_gdk_texture (GdkTexture *source_texture,
|
|
int target,
|
|
int x_offset,
|
|
int y_offset,
|
|
int width,
|
|
int height)
|
|
{
|
|
cairo_surface_t *surface = NULL;
|
|
GdkMemoryFormat data_format;
|
|
const guchar *data;
|
|
gsize data_stride;
|
|
gsize bpp;
|
|
|
|
g_return_if_fail (source_texture != NULL);
|
|
g_return_if_fail (x_offset + width <= gdk_texture_get_width (source_texture));
|
|
g_return_if_fail (y_offset + height <= gdk_texture_get_height (source_texture));
|
|
|
|
/* Note: GdkGLTextures are already handled before we reach this and reused as-is */
|
|
|
|
if (GDK_IS_MEMORY_TEXTURE (source_texture))
|
|
{
|
|
GdkMemoryTexture *memory_texture = GDK_MEMORY_TEXTURE (source_texture);
|
|
data = gdk_memory_texture_get_data (memory_texture);
|
|
data_format = gdk_memory_texture_get_format (memory_texture);
|
|
data_stride = gdk_memory_texture_get_stride (memory_texture);
|
|
}
|
|
else
|
|
{
|
|
/* Fall back to downloading to a surface */
|
|
surface = gdk_texture_download_surface (source_texture);
|
|
cairo_surface_flush (surface);
|
|
data = cairo_image_surface_get_data (surface);
|
|
data_format = GDK_MEMORY_DEFAULT;
|
|
data_stride = cairo_image_surface_get_stride (surface);
|
|
}
|
|
|
|
bpp = gdk_memory_format_bytes_per_pixel (data_format);
|
|
|
|
gdk_gl_context_upload_texture (gdk_gl_context_get_current (),
|
|
data + x_offset * bpp + y_offset * data_stride,
|
|
width, height, data_stride,
|
|
data_format, target);
|
|
|
|
if (surface)
|
|
cairo_surface_destroy (surface);
|
|
}
|
|
|
|
static Texture *
|
|
texture_new (void)
|
|
{
|
|
return g_slice_new0 (Texture);
|
|
}
|
|
|
|
static inline void
|
|
fbo_clear (const Fbo *f)
|
|
{
|
|
if (f->depth_stencil_id != 0)
|
|
glDeleteRenderbuffers (1, &f->depth_stencil_id);
|
|
|
|
glDeleteFramebuffers (1, &f->fbo_id);
|
|
}
|
|
|
|
static void
|
|
texture_free (gpointer data)
|
|
{
|
|
Texture *t = data;
|
|
guint i;
|
|
|
|
if (t->user)
|
|
gdk_texture_clear_render_data (t->user);
|
|
|
|
if (t->fbo.fbo_id != 0)
|
|
fbo_clear (&t->fbo);
|
|
|
|
if (t->texture_id != 0)
|
|
{
|
|
glDeleteTextures (1, &t->texture_id);
|
|
}
|
|
else
|
|
{
|
|
g_assert_cmpint (t->n_slices, >, 0);
|
|
|
|
for (i = 0; i < t->n_slices; i ++)
|
|
glDeleteTextures (1, &t->slices[i].texture_id);
|
|
}
|
|
|
|
g_slice_free (Texture, t);
|
|
}
|
|
|
|
static void
|
|
gsk_gl_driver_set_texture_parameters (GskGLDriver *self,
|
|
int min_filter,
|
|
int mag_filter)
|
|
{
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
|
|
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
}
|
|
|
|
static void
|
|
gsk_gl_driver_finalize (GObject *gobject)
|
|
{
|
|
GskGLDriver *self = GSK_GL_DRIVER (gobject);
|
|
|
|
gdk_gl_context_make_current (self->gl_context);
|
|
|
|
g_clear_pointer (&self->textures, g_hash_table_unref);
|
|
g_clear_pointer (&self->pointer_textures, g_hash_table_unref);
|
|
g_clear_object (&self->profiler);
|
|
|
|
if (self->gl_context == gdk_gl_context_get_current ())
|
|
gdk_gl_context_clear_current ();
|
|
|
|
G_OBJECT_CLASS (gsk_gl_driver_parent_class)->finalize (gobject);
|
|
}
|
|
|
|
static void
|
|
gsk_gl_driver_class_init (GskGLDriverClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->finalize = gsk_gl_driver_finalize;
|
|
}
|
|
|
|
static void
|
|
gsk_gl_driver_init (GskGLDriver *self)
|
|
{
|
|
self->textures = g_hash_table_new_full (NULL, NULL, NULL, texture_free);
|
|
|
|
self->max_texture_size = -1;
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
self->profiler = gsk_profiler_new ();
|
|
self->counters.created_textures = gsk_profiler_add_counter (self->profiler,
|
|
"created_textures",
|
|
"Textures created this frame",
|
|
TRUE);
|
|
self->counters.reused_textures = gsk_profiler_add_counter (self->profiler,
|
|
"reused_textures",
|
|
"Textures reused this frame",
|
|
TRUE);
|
|
self->counters.surface_uploads = gsk_profiler_add_counter (self->profiler,
|
|
"surface_uploads",
|
|
"Texture uploads from surfaces this frame",
|
|
TRUE);
|
|
#endif
|
|
}
|
|
|
|
GskGLDriver *
|
|
gsk_gl_driver_new (GdkGLContext *context)
|
|
{
|
|
GskGLDriver *self;
|
|
g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), NULL);
|
|
|
|
self = (GskGLDriver *) g_object_new (GSK_TYPE_GL_DRIVER, NULL);
|
|
self->gl_context = context;
|
|
|
|
return self;
|
|
}
|
|
|
|
void
|
|
gsk_gl_driver_begin_frame (GskGLDriver *self)
|
|
{
|
|
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
|
g_return_if_fail (!self->in_frame);
|
|
|
|
self->in_frame = TRUE;
|
|
|
|
if (self->max_texture_size < 0)
|
|
{
|
|
glGetIntegerv (GL_MAX_TEXTURE_SIZE, (GLint *) &self->max_texture_size);
|
|
GSK_NOTE (OPENGL, g_message ("GL max texture size: %d", self->max_texture_size));
|
|
}
|
|
|
|
glBindFramebuffer (GL_FRAMEBUFFER, 0);
|
|
|
|
glActiveTexture (GL_TEXTURE0);
|
|
glBindTexture (GL_TEXTURE_2D, 0);
|
|
|
|
glActiveTexture (GL_TEXTURE0 + 1);
|
|
glBindTexture (GL_TEXTURE_2D, 0);
|
|
|
|
glBindVertexArray (0);
|
|
glUseProgram (0);
|
|
|
|
glActiveTexture (GL_TEXTURE0);
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
gsk_profiler_reset (self->profiler);
|
|
#endif
|
|
}
|
|
|
|
gboolean
|
|
gsk_gl_driver_in_frame (GskGLDriver *self)
|
|
{
|
|
return self->in_frame;
|
|
}
|
|
|
|
void
|
|
gsk_gl_driver_end_frame (GskGLDriver *self)
|
|
{
|
|
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
|
g_return_if_fail (self->in_frame);
|
|
|
|
self->bound_source_texture = NULL;
|
|
|
|
self->default_fbo.fbo_id = 0;
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
GSK_NOTE (OPENGL,
|
|
g_message ("Textures created: %" G_GINT64_FORMAT "\n"
|
|
" Textures reused: %" G_GINT64_FORMAT "\n"
|
|
" Surface uploads: %" G_GINT64_FORMAT,
|
|
gsk_profiler_counter_get (self->profiler, self->counters.created_textures),
|
|
gsk_profiler_counter_get (self->profiler, self->counters.reused_textures),
|
|
gsk_profiler_counter_get (self->profiler, self->counters.surface_uploads)));
|
|
#endif
|
|
|
|
GSK_NOTE (OPENGL,
|
|
g_message ("*** Frame end: textures=%d",
|
|
g_hash_table_size (self->textures)));
|
|
|
|
self->in_frame = FALSE;
|
|
}
|
|
|
|
int
|
|
gsk_gl_driver_collect_textures (GskGLDriver *self)
|
|
{
|
|
GHashTableIter iter;
|
|
gpointer value_p = NULL;
|
|
int old_size;
|
|
|
|
g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0);
|
|
g_return_val_if_fail (!self->in_frame, 0);
|
|
|
|
old_size = g_hash_table_size (self->textures);
|
|
|
|
g_hash_table_iter_init (&iter, self->textures);
|
|
while (g_hash_table_iter_next (&iter, NULL, &value_p))
|
|
{
|
|
Texture *t = value_p;
|
|
|
|
if (t->user || t->permanent)
|
|
continue;
|
|
|
|
if (t->in_use)
|
|
{
|
|
t->in_use = FALSE;
|
|
|
|
if (t->fbo.fbo_id != 0)
|
|
{
|
|
fbo_clear (&t->fbo);
|
|
t->fbo.fbo_id = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Remove from self->pointer_textures. */
|
|
/* TODO: Is there a better way for this? */
|
|
if (self->pointer_textures)
|
|
{
|
|
GHashTableIter pointer_iter;
|
|
gpointer value;
|
|
gpointer p;
|
|
|
|
g_hash_table_iter_init (&pointer_iter, self->pointer_textures);
|
|
while (g_hash_table_iter_next (&pointer_iter, &p, &value))
|
|
{
|
|
if (GPOINTER_TO_INT (value) == t->texture_id)
|
|
{
|
|
g_hash_table_iter_remove (&pointer_iter);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_hash_table_iter_remove (&iter);
|
|
}
|
|
}
|
|
|
|
return old_size - g_hash_table_size (self->textures);
|
|
}
|
|
|
|
|
|
GdkGLContext *
|
|
gsk_gl_driver_get_gl_context (GskGLDriver *self)
|
|
{
|
|
return self->gl_context;
|
|
}
|
|
|
|
int
|
|
gsk_gl_driver_get_max_texture_size (GskGLDriver *self)
|
|
{
|
|
if (self->max_texture_size < 0)
|
|
{
|
|
if (gdk_gl_context_get_use_es (self->gl_context))
|
|
return 2048;
|
|
|
|
return 1024;
|
|
}
|
|
|
|
return self->max_texture_size;
|
|
}
|
|
|
|
static Texture *
|
|
gsk_gl_driver_get_texture (GskGLDriver *self,
|
|
int texture_id)
|
|
{
|
|
Texture *t;
|
|
|
|
if (g_hash_table_lookup_extended (self->textures, GINT_TO_POINTER (texture_id), NULL, (gpointer *) &t))
|
|
return t;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static Texture *
|
|
create_texture (GskGLDriver *self,
|
|
float fwidth,
|
|
float fheight)
|
|
{
|
|
guint texture_id;
|
|
Texture *t;
|
|
int width = ceilf (fwidth);
|
|
int height = ceilf (fheight);
|
|
|
|
g_assert (width > 0);
|
|
g_assert (height > 0);
|
|
|
|
if (width > self->max_texture_size ||
|
|
height > self->max_texture_size)
|
|
{
|
|
g_critical ("Texture %d x %d is bigger than supported texture limit of %d; clipping...",
|
|
width, height,
|
|
self->max_texture_size);
|
|
|
|
width = MIN (width, self->max_texture_size);
|
|
height = MIN (height, self->max_texture_size);
|
|
}
|
|
|
|
glGenTextures (1, &texture_id);
|
|
t = texture_new ();
|
|
t->texture_id = texture_id;
|
|
t->width = width;
|
|
t->height = height;
|
|
t->min_filter = GL_NEAREST;
|
|
t->mag_filter = GL_NEAREST;
|
|
t->in_use = TRUE;
|
|
g_hash_table_insert (self->textures, GINT_TO_POINTER (texture_id), t);
|
|
#ifdef G_ENABLE_DEBUG
|
|
gsk_profiler_counter_inc (self->profiler, self->counters.created_textures);
|
|
#endif
|
|
|
|
return t;
|
|
}
|
|
|
|
static void
|
|
gsk_gl_driver_release_texture (gpointer data)
|
|
{
|
|
Texture *t = data;
|
|
|
|
t->user = NULL;
|
|
}
|
|
|
|
void
|
|
gsk_gl_driver_slice_texture (GskGLDriver *self,
|
|
GdkTexture *texture,
|
|
TextureSlice **out_slices,
|
|
guint *out_n_slices)
|
|
{
|
|
const int max_texture_size = gsk_gl_driver_get_max_texture_size (self) / 4; // XXX Too much?
|
|
const int tex_width = texture->width;
|
|
const int tex_height = texture->height;
|
|
const int cols = (texture->width / max_texture_size) + 1;
|
|
const int rows = (texture->height / max_texture_size) + 1;
|
|
int col, row;
|
|
int x = 0, y = 0; /* Position in the texture */
|
|
TextureSlice *slices;
|
|
Texture *tex;
|
|
|
|
g_assert (tex_width > max_texture_size || tex_height > max_texture_size);
|
|
|
|
|
|
tex = gdk_texture_get_render_data (texture, self);
|
|
|
|
if (tex != NULL)
|
|
{
|
|
g_assert (tex->n_slices > 0);
|
|
*out_slices = tex->slices;
|
|
*out_n_slices = tex->n_slices;
|
|
return;
|
|
}
|
|
|
|
slices = g_new0 (TextureSlice, cols * rows);
|
|
|
|
for (col = 0; col < cols; col ++)
|
|
{
|
|
const int slice_width = MIN (max_texture_size, texture->width - x);
|
|
|
|
for (row = 0; row < rows; row ++)
|
|
{
|
|
const int slice_height = MIN (max_texture_size, texture->height - y);
|
|
const int slice_index = (col * rows) + row;
|
|
guint texture_id;
|
|
|
|
glGenTextures (1, &texture_id);
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
gsk_profiler_counter_inc (self->profiler, self->counters.created_textures);
|
|
#endif
|
|
glBindTexture (GL_TEXTURE_2D, texture_id);
|
|
gsk_gl_driver_set_texture_parameters (self, GL_NEAREST, GL_NEAREST);
|
|
upload_gdk_texture (texture, GL_TEXTURE_2D, x, y, slice_width, slice_height);
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads);
|
|
#endif
|
|
|
|
slices[slice_index].rect = (GdkRectangle){x, y, slice_width, slice_height};
|
|
slices[slice_index].texture_id = texture_id;
|
|
|
|
y += slice_height;
|
|
}
|
|
|
|
y = 0;
|
|
x += slice_width;
|
|
}
|
|
|
|
/* Allocate one Texture for the entire thing. */
|
|
tex = texture_new ();
|
|
tex->width = texture->width;
|
|
tex->height = texture->height;
|
|
tex->min_filter = GL_NEAREST;
|
|
tex->mag_filter = GL_NEAREST;
|
|
tex->in_use = TRUE;
|
|
tex->slices = slices;
|
|
tex->n_slices = cols * rows;
|
|
|
|
/* Use texture_free as destroy notify here since we are not inserting this Texture
|
|
* into self->textures! */
|
|
gdk_texture_set_render_data (texture, self, tex, texture_free);
|
|
|
|
*out_slices = slices;
|
|
*out_n_slices = cols * rows;
|
|
}
|
|
|
|
int
|
|
gsk_gl_driver_get_texture_for_texture (GskGLDriver *self,
|
|
GdkTexture *texture,
|
|
int min_filter,
|
|
int mag_filter)
|
|
{
|
|
Texture *t;
|
|
GdkTexture *downloaded_texture = NULL;
|
|
GdkTexture *source_texture;
|
|
|
|
if (GDK_IS_GL_TEXTURE (texture))
|
|
{
|
|
GdkGLContext *texture_context = gdk_gl_texture_get_context ((GdkGLTexture *)texture);
|
|
GdkGLContext *shared_context = gdk_gl_context_get_shared_context (self->gl_context);
|
|
|
|
if (texture_context == self->gl_context ||
|
|
(gdk_gl_context_get_shared_context (texture_context) == shared_context && shared_context != NULL))
|
|
{
|
|
/* A GL texture from the same GL context is a simple task... */
|
|
return gdk_gl_texture_get_id ((GdkGLTexture *)texture);
|
|
}
|
|
else
|
|
{
|
|
cairo_surface_t *surface;
|
|
|
|
/* In this case, we have to temporarily make the texture's context the current one,
|
|
* download its data into our context and then create a texture from it. */
|
|
if (texture_context)
|
|
gdk_gl_context_make_current (texture_context);
|
|
|
|
surface = gdk_texture_download_surface (texture);
|
|
downloaded_texture = gdk_texture_new_for_surface (surface);
|
|
cairo_surface_destroy (surface);
|
|
|
|
gdk_gl_context_make_current (self->gl_context);
|
|
|
|
source_texture = downloaded_texture;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
t = gdk_texture_get_render_data (texture, self);
|
|
|
|
if (t)
|
|
{
|
|
if (t->min_filter == min_filter && t->mag_filter == mag_filter)
|
|
return t->texture_id;
|
|
}
|
|
|
|
source_texture = texture;
|
|
}
|
|
|
|
t = create_texture (self, gdk_texture_get_width (texture), gdk_texture_get_height (texture));
|
|
|
|
if (gdk_texture_set_render_data (texture, self, t, gsk_gl_driver_release_texture))
|
|
t->user = texture;
|
|
|
|
gsk_gl_driver_bind_source_texture (self, t->texture_id);
|
|
gsk_gl_driver_init_texture (self,
|
|
t->texture_id,
|
|
source_texture,
|
|
min_filter,
|
|
mag_filter);
|
|
gdk_gl_context_label_object_printf (self->gl_context, GL_TEXTURE, t->texture_id,
|
|
"GdkTexture<%p> %d", texture, t->texture_id);
|
|
|
|
if (downloaded_texture)
|
|
g_object_unref (downloaded_texture);
|
|
|
|
return t->texture_id;
|
|
}
|
|
|
|
static guint
|
|
texture_key_hash (gconstpointer v)
|
|
{
|
|
const GskTextureKey *k = (GskTextureKey *)v;
|
|
|
|
return GPOINTER_TO_UINT (k->pointer)
|
|
+ (guint)(k->scale_x * 100)
|
|
+ (guint)(k->scale_y * 100)
|
|
+ (guint)k->filter * 2 +
|
|
+ (guint)k->pointer_is_child;
|
|
}
|
|
|
|
static gboolean
|
|
texture_key_equal (gconstpointer v1, gconstpointer v2)
|
|
{
|
|
const GskTextureKey *k1 = (GskTextureKey *)v1;
|
|
const GskTextureKey *k2 = (GskTextureKey *)v2;
|
|
|
|
return k1->pointer == k2->pointer &&
|
|
k1->scale_x == k2->scale_x &&
|
|
k1->scale_y == k2->scale_y &&
|
|
k1->filter == k2->filter &&
|
|
k1->pointer_is_child == k2->pointer_is_child &&
|
|
(!k1->pointer_is_child || graphene_rect_equal (&k1->parent_rect, &k2->parent_rect));
|
|
}
|
|
|
|
int
|
|
gsk_gl_driver_get_texture_for_key (GskGLDriver *self,
|
|
GskTextureKey *key)
|
|
{
|
|
int id = 0;
|
|
|
|
if (G_UNLIKELY (self->pointer_textures == NULL))
|
|
self->pointer_textures = g_hash_table_new_full (texture_key_hash, texture_key_equal, g_free, NULL);
|
|
|
|
id = GPOINTER_TO_INT (g_hash_table_lookup (self->pointer_textures, key));
|
|
|
|
if (id != 0)
|
|
{
|
|
Texture *t;
|
|
|
|
t = g_hash_table_lookup (self->textures, GINT_TO_POINTER (id));
|
|
|
|
if (t != NULL)
|
|
t->in_use = TRUE;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
void
|
|
gsk_gl_driver_set_texture_for_key (GskGLDriver *self,
|
|
GskTextureKey *key,
|
|
int texture_id)
|
|
{
|
|
GskTextureKey *k;
|
|
|
|
if (G_UNLIKELY (self->pointer_textures == NULL))
|
|
self->pointer_textures = g_hash_table_new_full (texture_key_hash, texture_key_equal, g_free, NULL);
|
|
|
|
k = g_new (GskTextureKey, 1);
|
|
*k = *key;
|
|
|
|
g_hash_table_insert (self->pointer_textures, k, GINT_TO_POINTER (texture_id));
|
|
}
|
|
|
|
int
|
|
gsk_gl_driver_create_texture (GskGLDriver *self,
|
|
float width,
|
|
float height)
|
|
{
|
|
Texture *t;
|
|
|
|
g_return_val_if_fail (GSK_IS_GL_DRIVER (self), -1);
|
|
|
|
t = create_texture (self, width, height);
|
|
|
|
return t->texture_id;
|
|
}
|
|
|
|
void
|
|
gsk_gl_driver_create_render_target (GskGLDriver *self,
|
|
int width,
|
|
int height,
|
|
int min_filter,
|
|
int mag_filter,
|
|
int *out_texture_id,
|
|
int *out_render_target_id)
|
|
{
|
|
GLuint fbo_id;
|
|
Texture *texture;
|
|
|
|
g_return_if_fail (self->in_frame);
|
|
|
|
texture = create_texture (self, width, height);
|
|
gsk_gl_driver_bind_source_texture (self, texture->texture_id);
|
|
gsk_gl_driver_init_texture_empty (self, texture->texture_id, min_filter, mag_filter);
|
|
|
|
glGenFramebuffers (1, &fbo_id);
|
|
glBindFramebuffer (GL_FRAMEBUFFER, fbo_id);
|
|
glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->texture_id, 0);
|
|
|
|
#if 0
|
|
if (add_depth_buffer || add_stencil_buffer)
|
|
{
|
|
glGenRenderbuffersEXT (1, &depth_stencil_buffer_id);
|
|
gdk_gl_context_label_object_printf (self->gl_context, GL_RENDERBUFFER, depth_stencil_buffer_id,
|
|
"%s buffer for %d", add_depth_buffer ? "Depth" : "Stencil", texture_id);
|
|
}
|
|
else
|
|
depth_stencil_buffer_id = 0;
|
|
|
|
glBindRenderbuffer (GL_RENDERBUFFER, depth_stencil_buffer_id);
|
|
|
|
if (add_depth_buffer || add_stencil_buffer)
|
|
{
|
|
if (add_stencil_buffer)
|
|
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, t->width, t->height);
|
|
else
|
|
glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, t->width, t->height);
|
|
|
|
if (add_depth_buffer)
|
|
glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
|
GL_RENDERBUFFER, depth_stencil_buffer_id);
|
|
|
|
if (add_stencil_buffer)
|
|
glFramebufferRenderbufferEXT (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
|
|
GL_RENDERBUFFER, depth_stencil_buffer_id);
|
|
texture->fbo.depth_stencil_id = depth_stencil_buffer_id;
|
|
}
|
|
#endif
|
|
|
|
texture->fbo.fbo_id = fbo_id;
|
|
|
|
g_assert_cmphex (glCheckFramebufferStatus (GL_FRAMEBUFFER), ==, GL_FRAMEBUFFER_COMPLETE);
|
|
|
|
glBindFramebuffer (GL_FRAMEBUFFER, self->default_fbo.fbo_id);
|
|
|
|
*out_texture_id = texture->texture_id;
|
|
*out_render_target_id = fbo_id;
|
|
}
|
|
|
|
/* Mark the texture permanent, meaning it won'e be reused by the GLDriver.
|
|
* E.g. to store it in some other cache. */
|
|
void
|
|
gsk_gl_driver_mark_texture_permanent (GskGLDriver *self,
|
|
int texture_id)
|
|
{
|
|
Texture *t = gsk_gl_driver_get_texture (self, texture_id);
|
|
|
|
g_assert (t != NULL);
|
|
|
|
t->permanent = TRUE;
|
|
}
|
|
|
|
void
|
|
gsk_gl_driver_bind_source_texture (GskGLDriver *self,
|
|
int texture_id)
|
|
{
|
|
Texture *t;
|
|
|
|
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
|
g_return_if_fail (self->in_frame);
|
|
|
|
t = gsk_gl_driver_get_texture (self, texture_id);
|
|
if (t == NULL)
|
|
{
|
|
g_critical ("No texture %d found.", texture_id);
|
|
return;
|
|
}
|
|
|
|
if (self->bound_source_texture != t)
|
|
{
|
|
glActiveTexture (GL_TEXTURE0);
|
|
glBindTexture (GL_TEXTURE_2D, t->texture_id);
|
|
|
|
self->bound_source_texture = t;
|
|
}
|
|
}
|
|
|
|
void
|
|
gsk_gl_driver_destroy_texture (GskGLDriver *self,
|
|
int texture_id)
|
|
{
|
|
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
|
|
|
g_hash_table_remove (self->textures, GINT_TO_POINTER (texture_id));
|
|
}
|
|
|
|
|
|
void
|
|
gsk_gl_driver_init_texture_empty (GskGLDriver *self,
|
|
int texture_id,
|
|
int min_filter,
|
|
int mag_filter)
|
|
{
|
|
Texture *t;
|
|
|
|
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
|
|
|
t = gsk_gl_driver_get_texture (self, texture_id);
|
|
if (t == NULL)
|
|
{
|
|
g_critical ("No texture %d found.", texture_id);
|
|
return;
|
|
}
|
|
|
|
if (self->bound_source_texture != t)
|
|
{
|
|
g_critical ("You must bind the texture before initializing it.");
|
|
return;
|
|
}
|
|
|
|
t->min_filter = min_filter;
|
|
t->mag_filter = mag_filter;
|
|
|
|
gsk_gl_driver_set_texture_parameters (self, t->min_filter, t->mag_filter);
|
|
|
|
if (gdk_gl_context_get_use_es (self->gl_context))
|
|
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, t->width, t->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
else
|
|
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, t->width, t->height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glBindTexture (GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
static gboolean
|
|
filter_uses_mipmaps (int filter)
|
|
{
|
|
return filter != GL_NEAREST && filter != GL_LINEAR;
|
|
}
|
|
|
|
void
|
|
gsk_gl_driver_init_texture (GskGLDriver *self,
|
|
int texture_id,
|
|
GdkTexture *texture,
|
|
int min_filter,
|
|
int mag_filter)
|
|
{
|
|
Texture *t;
|
|
|
|
g_return_if_fail (GSK_IS_GL_DRIVER (self));
|
|
|
|
t = gsk_gl_driver_get_texture (self, texture_id);
|
|
if (t == NULL)
|
|
{
|
|
g_critical ("No texture %d found.", texture_id);
|
|
return;
|
|
}
|
|
|
|
if (self->bound_source_texture != t)
|
|
{
|
|
g_critical ("You must bind the texture before initializing it.");
|
|
return;
|
|
}
|
|
|
|
gsk_gl_driver_set_texture_parameters (self, min_filter, mag_filter);
|
|
|
|
upload_gdk_texture (texture, GL_TEXTURE_2D, 0, 0, t->width, t->height);
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
gsk_profiler_counter_inc (self->profiler, self->counters.surface_uploads);
|
|
#endif
|
|
|
|
t->min_filter = min_filter;
|
|
t->mag_filter = mag_filter;
|
|
|
|
if (filter_uses_mipmaps (t->min_filter))
|
|
glGenerateMipmap (GL_TEXTURE_2D);
|
|
}
|