gsk: Add more flexible texture upload

Add a command queue function to create a
GL texture by uploading several GdkTextures
at given positions.
This lets us 'piece together' textures.
This commit is contained in:
Matthias Clasen
2023-03-21 22:55:03 -04:00
parent 24720307bb
commit c3ee8d2699
2 changed files with 97 additions and 36 deletions

View File

@@ -1412,9 +1412,41 @@ gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self)
return fbo_id;
}
static GdkMemoryFormat
memory_format_gl_format (GdkMemoryFormat data_format,
gboolean use_es,
guint *gl_internalformat,
guint *gl_format,
guint *gl_type)
{
if (!gdk_memory_format_gl_format (data_format,
use_es,
gl_internalformat,
gl_format,
gl_type))
{
if (gdk_memory_format_prefers_high_depth (data_format))
data_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
else
data_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
if (!gdk_memory_format_gl_format (data_format,
use_es,
gl_internalformat,
gl_format,
gl_type))
{
g_assert_not_reached ();
}
}
return data_format;
}
static void
gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
GdkTexture *texture)
gsk_gl_command_queue_do_upload_texture_chunk (GskGLCommandQueue *self,
GdkTexture *texture,
int x,
int y)
{
GdkGLContext *context;
const guchar *data;
@@ -1435,25 +1467,11 @@ gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
if (!gdk_memory_format_gl_format (data_format,
use_es,
&gl_internalformat,
&gl_format,
&gl_type))
{
if (gdk_memory_format_prefers_high_depth (data_format))
data_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
else
data_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
if (!gdk_memory_format_gl_format (data_format,
use_es,
&gl_internalformat,
&gl_format,
&gl_type))
{
g_assert_not_reached ();
}
}
data_format = memory_format_gl_format (data_format,
use_es,
&gl_internalformat,
&gl_format,
&gl_type);
gdk_texture_downloader_init (&downloader, texture);
gdk_texture_downloader_set_format (&downloader, data_format);
@@ -1469,43 +1487,53 @@ gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
*/
if (stride == width * bpp)
{
glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
glTexSubImage2D (GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, data);
}
else if (stride % bpp == 0 &&
(gdk_gl_context_check_version (context, 0, 0, 3, 0) || gdk_gl_context_has_unpack_subimage (context)))
{
glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp);
glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
glTexSubImage2D (GL_TEXTURE_2D, 0, x, y, width, height, gl_format, gl_type, data);
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
}
else
{
int i;
glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, NULL);
for (i = 0; i < height; i++)
glTexSubImage2D (GL_TEXTURE_2D, 0, 0, i, width, 1, gl_format, gl_type, data + (i * stride));
for (int i = 0; i < height; i++)
glTexSubImage2D (GL_TEXTURE_2D, 0, x, y + i, width, 1, gl_format, gl_type, data + (i * stride));
}
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
g_bytes_unref (bytes);
}
int
gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
GdkTexture *texture)
gsk_gl_command_queue_upload_texture_chunks (GskGLCommandQueue *self,
unsigned int n_chunks,
GskGLTextureChunk *chunks)
{
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
cairo_surface_t *surface = NULL;
int width, height;
GdkMemoryFormat data_format;
GLenum gl_internalformat;
GLenum gl_format;
GLenum gl_type;
gboolean use_es;
int texture_id;
g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
g_assert (!GDK_IS_GL_TEXTURE (texture));
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
width = height = 0;
for (unsigned int i = 0; i < n_chunks; i++)
{
GskGLTextureChunk *c = &chunks[i];
width = MAX (width, c->x + gdk_texture_get_width (c->texture));
height = MAX (height, c->y + gdk_texture_get_height (c->texture));
}
if (width > self->max_texture_size || height > self->max_texture_size)
{
g_warning ("Attempt to create texture of size %ux%u but max size is %d. "
@@ -1514,6 +1542,7 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
width = MIN (width, self->max_texture_size);
height = MIN (height, self->max_texture_size);
}
texture_id = gsk_gl_command_queue_create_texture (self, width, height, GL_RGBA8);
if (texture_id == -1)
return texture_id;
@@ -1524,15 +1553,28 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, texture_id);
gsk_gl_command_queue_do_upload_texture (self, texture);
/* Initialize the texture */
use_es = gdk_gl_context_get_use_es (gdk_gl_context_get_current ());
data_format = gdk_texture_get_format (chunks[0].texture);
memory_format_gl_format (data_format,
use_es,
&gl_internalformat,
&gl_format,
&gl_type);
glTexImage2D (GL_TEXTURE_2D, 0, gl_internalformat, width, height, 0, gl_format, gl_type, NULL);
for (unsigned int i = 0; i < n_chunks; i++)
{
GskGLTextureChunk *c = &chunks[i];
gsk_gl_command_queue_do_upload_texture_chunk (self, c->texture, c->x, c->y);
}
/* Restore previous texture state if any */
if (self->attachments->textures[0].id > 0)
glBindTexture (self->attachments->textures[0].target,
self->attachments->textures[0].id);
g_clear_pointer (&surface, cairo_surface_destroy);
if (gdk_profiler_is_running ())
gdk_profiler_add_markf (start_time, GDK_PROFILER_CURRENT_TIME-start_time,
"Upload Texture",
@@ -1541,6 +1583,13 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
return texture_id;
}
int
gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
GdkTexture *texture)
{
return gsk_gl_command_queue_upload_texture_chunks (self, 1, &(GskGLTextureChunk){ texture, 0, 0});
}
void
gsk_gl_command_queue_set_profiler (GskGLCommandQueue *self,
GskProfiler *profiler)

View File

@@ -298,6 +298,18 @@ int gsk_gl_command_queue_create_texture (GskGLCommandQueue
int width,
int height,
int format);
typedef struct {
GdkTexture *texture;
int x;
int y;
} GskGLTextureChunk;
int gsk_gl_command_queue_upload_texture_chunks(GskGLCommandQueue *self,
unsigned int n_chunks,
GskGLTextureChunk *chunks);
guint gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self);
gboolean gsk_gl_command_queue_create_render_target (GskGLCommandQueue *self,
int width,