gtk/gsk/gpu/gskglimage.c
Benjamin Otte 32d35b1d01 gpu: Handle flags for images
For now, the flags are just there because, and nobody uses them yet.
The only flag is EXTERNAL, which for now I'm using for YUV buffers,
though it's a bit undefined what that means.
2024-01-07 07:22:51 +01:00

254 lines
6.6 KiB
C

#include "config.h"
#include "gskglimageprivate.h"
#include "gdk/gdkdisplayprivate.h"
#include "gdk/gdkglcontextprivate.h"
struct _GskGLImage
{
GskGpuImage parent_instance;
guint texture_id;
guint framebuffer_id;
GLint gl_internal_format;
GLenum gl_format;
GLenum gl_type;
guint owns_texture : 1;
};
struct _GskGLImageClass
{
GskGpuImageClass parent_class;
};
G_DEFINE_TYPE (GskGLImage, gsk_gl_image, GSK_TYPE_GPU_IMAGE)
static void
gsk_gl_image_get_projection_matrix (GskGpuImage *image,
graphene_matrix_t *out_projection)
{
GskGLImage *self = GSK_GL_IMAGE (image);
GSK_GPU_IMAGE_CLASS (gsk_gl_image_parent_class)->get_projection_matrix (image, out_projection);
if (self->texture_id == 0)
graphene_matrix_scale (out_projection, 1.f, -1.f, 1.f);
}
static void
gsk_gl_image_finalize (GObject *object)
{
GskGLImage *self = GSK_GL_IMAGE (object);
if (self->framebuffer_id)
glDeleteFramebuffers (1, &self->framebuffer_id);
if (self->owns_texture)
glDeleteTextures (1, &self->texture_id);
G_OBJECT_CLASS (gsk_gl_image_parent_class)->finalize (object);
}
static void
gsk_gl_image_class_init (GskGLImageClass *klass)
{
GskGpuImageClass *image_class = GSK_GPU_IMAGE_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
image_class->get_projection_matrix = gsk_gl_image_get_projection_matrix;
object_class->finalize = gsk_gl_image_finalize;
}
static void
gsk_gl_image_init (GskGLImage *self)
{
}
GskGpuImage *
gsk_gl_image_new_backbuffer (GskGLDevice *device,
GdkMemoryFormat format,
gsize width,
gsize height)
{
GskGLImage *self;
GLint swizzle[4];
self = g_object_new (GSK_TYPE_GL_IMAGE, NULL);
/* We only do this so these variables get initialized */
gsk_gl_device_find_gl_format (device,
format,
&format,
&self->gl_internal_format,
&self->gl_format,
&self->gl_type,
swizzle);
gsk_gpu_image_setup (GSK_GPU_IMAGE (self), 0, format, width, height);
/* texture_id == 0 means backbuffer */
return GSK_GPU_IMAGE (self);
}
GskGpuImage *
gsk_gl_image_new (GskGLDevice *device,
GdkMemoryFormat format,
gsize width,
gsize height)
{
GskGLImage *self;
GLint swizzle[4];
gsize max_size;
max_size = gsk_gpu_device_get_max_image_size (GSK_GPU_DEVICE (device));
if (width > max_size || height > max_size)
return NULL;
self = g_object_new (GSK_TYPE_GL_IMAGE, NULL);
gsk_gl_device_find_gl_format (device,
format,
&format,
&self->gl_internal_format,
&self->gl_format,
&self->gl_type,
swizzle);
gsk_gpu_image_setup (GSK_GPU_IMAGE (self), 0, format, width, height);
glGenTextures (1, &self->texture_id);
self->owns_texture = TRUE;
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, self->texture_id);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D (GL_TEXTURE_2D, 0, self->gl_internal_format, width, height, 0, self->gl_format, self->gl_type, NULL);
/* Only apply swizzle if really needed, might not even be
* supported if default values are set
*/
if (swizzle[0] != GL_RED || swizzle[1] != GL_GREEN || swizzle[2] != GL_BLUE || swizzle[3] != GL_ALPHA)
{
/* Set each channel independently since GLES 3.0 doesn't support the iv method */
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, swizzle[0]);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, swizzle[1]);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, swizzle[2]);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, swizzle[3]);
}
return GSK_GPU_IMAGE (self);
}
void
gsk_gl_image_bind_texture (GskGLImage *self)
{
glBindTexture (GL_TEXTURE_2D, self->texture_id);
}
void
gsk_gl_image_bind_framebuffer_target (GskGLImage *self,
GLenum target)
{
GLenum status;
if (self->framebuffer_id)
{
glBindFramebuffer (target, self->framebuffer_id);
return;
}
/* We're the renderbuffer */
if (self->texture_id == 0)
{
glBindFramebuffer (target, 0);
return;
}
glGenFramebuffers (1, &self->framebuffer_id);
glBindFramebuffer (target, self->framebuffer_id);
glFramebufferTexture2D (target, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->texture_id, 0);
status = glCheckFramebufferStatus (target);
switch (status)
{
case GL_FRAMEBUFFER_COMPLETE:
break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
g_critical ("glCheckFramebufferStatus() returned GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT. Expect broken rendering.");
break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
g_critical ("glCheckFramebufferStatus() returned GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS. Expect broken rendering.");
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
g_critical ("glCheckFramebufferStatus() returned GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT. Expect broken rendering.");
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
g_critical ("glCheckFramebufferStatus() returned GL_FRAMEBUFFER_UNSUPPORTED. Expect broken rendering.");
break;
default:
g_critical ("glCheckFramebufferStatus() returned %u (0x%x). Expect broken rendering.", status, status);
break;
}
}
void
gsk_gl_image_bind_framebuffer (GskGLImage *self)
{
gsk_gl_image_bind_framebuffer_target (self, GL_FRAMEBUFFER);
}
gboolean
gsk_gl_image_is_flipped (GskGLImage *self)
{
return self->texture_id == 0;
}
GLint
gsk_gl_image_get_gl_internal_format (GskGLImage *self)
{
return self->gl_internal_format;
}
GLenum
gsk_gl_image_get_gl_format (GskGLImage *self)
{
return self->gl_format;
}
GLenum
gsk_gl_image_get_gl_type (GskGLImage *self)
{
return self->gl_type;
}
GLuint
gsk_gl_image_steal_texture (GskGLImage *self)
{
g_assert (self->owns_texture);
if (self->framebuffer_id)
{
glDeleteFramebuffers (1, &self->framebuffer_id);
self->framebuffer_id = 0;
}
self->owns_texture = FALSE;
return self->texture_id;
}