Files
gtk/gsk/gpu/gskgpudownloadop.c
Benjamin Otte 52df3481d6 gpu: Add a gsk_gpu_download_into_op()
... and use it for the dmabuf downloader

Splits the download op into 2 separate ops: One for downloading textures
and one for downloading into preallocated memory.

The download into memory is the fallback for the texture downloading op,
so they need to share code.

But keeping them separate ensures that the different codepaths for
dmabuf download and render_texture() don't get mixed up in weird ways
that potentially call into each other.

By passing the emory down into the op we can also avoid an extra memcpy
which can lead to quite large speedups for big textures.
2024-09-26 22:06:17 +02:00

548 lines
18 KiB
C

#include "config.h"
#include "gskgpudownloadopprivate.h"
#include "gskgpuframeprivate.h"
#include "gskglimageprivate.h"
#include "gskgpuimageprivate.h"
#include "gskgpuprintprivate.h"
#ifdef GDK_RENDERING_VULKAN
#include "gskgpucacheprivate.h"
#include "gskvulkanbufferprivate.h"
#include "gskvulkanframeprivate.h"
#include "gskvulkanimageprivate.h"
#endif
#include <glib/gstdio.h>
#include "gdk/gdkdisplayprivate.h"
#include "gdk/gdkdmabuftexturebuilderprivate.h"
#include "gdk/gdkdmabuftextureprivate.h"
#include "gdk/gdkglcontextprivate.h"
#ifdef HAVE_DMABUF
#include <glib-unix.h>
#include <linux/dma-buf.h>
#endif
typedef struct _GskGpuDownloadOp GskGpuDownloadOp;
typedef void (* GdkGpuDownloadOpCreateFunc) (GskGpuDownloadOp *);
struct _GskGpuDownloadOp
{
GskGpuOp op;
GskGpuImage *image;
GdkColorState *color_state;
gboolean allow_dmabuf;
GdkGpuDownloadOpCreateFunc create_func;
GdkTexture **texture;
GskGpuBuffer *buffer;
#ifdef GDK_RENDERING_VULKAN
VkSemaphore vk_semaphore;
#endif
};
static void
gsk_gpu_download_op_finish (GskGpuOp *op)
{
GskGpuDownloadOp *self = (GskGpuDownloadOp *) op;
if (self->create_func)
self->create_func (self);
g_object_unref (self->image);
g_clear_object (&self->buffer);
}
static void
gsk_gpu_download_op_print (GskGpuOp *op,
GskGpuFrame *frame,
GString *string,
guint indent)
{
GskGpuDownloadOp *self = (GskGpuDownloadOp *) op;
gsk_gpu_print_op (string, indent, "download");
gsk_gpu_print_image (string, self->image);
gsk_gpu_print_newline (string);
}
#ifdef GDK_RENDERING_VULKAN
static GskGpuBuffer *
gsk_gpu_download_vk_start (GskGpuFrame *frame,
GskVulkanCommandState *state,
GskGpuImage *image)
{
gsize width, height, stride;
GskGpuBuffer *buffer;
width = gsk_gpu_image_get_width (image);
height = gsk_gpu_image_get_height (image);
stride = width * gdk_memory_format_bytes_per_pixel (gsk_gpu_image_get_format (image));
buffer = gsk_vulkan_buffer_new_read (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame)),
height * stride);
gsk_vulkan_image_transition (GSK_VULKAN_IMAGE (image),
state->semaphores,
state->vk_command_buffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_ACCESS_TRANSFER_READ_BIT);
vkCmdCopyImageToBuffer (state->vk_command_buffer,
gsk_vulkan_image_get_vk_image (GSK_VULKAN_IMAGE (image)),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
gsk_vulkan_buffer_get_vk_buffer (GSK_VULKAN_BUFFER (buffer)),
1,
(VkBufferImageCopy[1]) {
{
.bufferOffset = 0,
.bufferRowLength = width,
.bufferImageHeight = height,
.imageSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1
},
.imageOffset = {
.x = 0,
.y = 0,
.z = 0
},
.imageExtent = {
.width = width,
.height = height,
.depth = 1
}
}
});
vkCmdPipelineBarrier (state->vk_command_buffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_HOST_BIT,
0,
0, NULL,
1, &(VkBufferMemoryBarrier) {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_HOST_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = gsk_vulkan_buffer_get_vk_buffer (GSK_VULKAN_BUFFER (buffer)),
.offset = 0,
.size = VK_WHOLE_SIZE,
},
0, NULL);
return buffer;
}
#ifdef HAVE_DMABUF
/* The code needs to run here because vkGetSemaphoreFdKHR() may
* only be called after the semaphore has been submitted via
* vkQueueSubmit().
*/
static void
gsk_gpu_download_op_vk_sync_semaphore (GskGpuDownloadOp *self)
{
PFN_vkGetSemaphoreFdKHR func_vkGetSemaphoreFdKHR;
GdkDisplay *display;
int fd, sync_file_fd;
/* Don't look at where I store my variables plz */
display = gdk_dmabuf_texture_get_display (GDK_DMABUF_TEXTURE (*self->texture));
fd = gdk_dmabuf_texture_get_dmabuf (GDK_DMABUF_TEXTURE (*self->texture))->planes[0].fd;
func_vkGetSemaphoreFdKHR = (PFN_vkGetSemaphoreFdKHR) vkGetDeviceProcAddr (display->vk_device, "vkGetSemaphoreFdKHR");
/* vkGetSemaphoreFdKHR implicitly resets the semaphore.
* But who cares, we're about to delete it. */
if (GSK_VK_CHECK (func_vkGetSemaphoreFdKHR, display->vk_device,
&(VkSemaphoreGetFdInfoKHR) {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
.semaphore = self->vk_semaphore,
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
},
&sync_file_fd) == VK_SUCCESS)
{
gdk_dmabuf_import_sync_file (fd, DMA_BUF_SYNC_WRITE, sync_file_fd);
close (sync_file_fd);
}
vkDestroySemaphore (display->vk_device, self->vk_semaphore, NULL);
}
#endif
static void
gsk_gpu_download_op_vk_create (GskGpuDownloadOp *self)
{
GBytes *bytes;
guchar *data;
gsize width, height, stride;
GdkMemoryFormat format;
GdkMemoryTextureBuilder *builder;
data = gsk_gpu_buffer_map (self->buffer);
width = gsk_gpu_image_get_width (self->image);
height = gsk_gpu_image_get_height (self->image);
format = gsk_gpu_image_get_format (self->image);
stride = width * gdk_memory_format_bytes_per_pixel (format);
bytes = g_bytes_new (data, stride * height);
builder = gdk_memory_texture_builder_new ();
gdk_memory_texture_builder_set_width (builder, width);
gdk_memory_texture_builder_set_height (builder, height);
gdk_memory_texture_builder_set_format (builder, format);
gdk_memory_texture_builder_set_bytes (builder, bytes);
gdk_memory_texture_builder_set_stride (builder, stride);
gdk_memory_texture_builder_set_color_state (builder, self->color_state);
*self->texture = gdk_memory_texture_builder_build (builder);
g_object_unref (builder);
g_bytes_unref (bytes);
gsk_gpu_buffer_unmap (self->buffer, 0);
}
static GskGpuOp *
gsk_gpu_download_op_vk_command (GskGpuOp *op,
GskGpuFrame *frame,
GskVulkanCommandState *state)
{
GskGpuDownloadOp *self = (GskGpuDownloadOp *) op;
#ifdef HAVE_DMABUF
if (self->allow_dmabuf)
*self->texture = gsk_vulkan_image_to_dmabuf_texture (GSK_VULKAN_IMAGE (self->image),
self->color_state);
if (*self->texture)
{
GskGpuDevice *device = gsk_gpu_frame_get_device (frame);
GskGpuCache *cache = gsk_gpu_device_get_cache (device);
VkDevice vk_device = gsk_vulkan_device_get_vk_device (GSK_VULKAN_DEVICE (device));
gsk_gpu_cache_cache_texture_image (cache, *self->texture, self->image, NULL);
if (gsk_vulkan_device_has_feature (GSK_VULKAN_DEVICE (device), GDK_VULKAN_FEATURE_SEMAPHORE_EXPORT))
{
GSK_VK_CHECK (vkCreateSemaphore, vk_device,
&(VkSemaphoreCreateInfo) {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &(VkExportSemaphoreCreateInfo) {
.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
},
},
NULL,
&self->vk_semaphore);
gsk_vulkan_semaphores_add_signal (state->semaphores, self->vk_semaphore);
self->create_func = gsk_gpu_download_op_vk_sync_semaphore;
}
return op->next;
}
#endif
self->buffer = gsk_gpu_download_vk_start (frame, state, self->image);
self->create_func = gsk_gpu_download_op_vk_create;
return op->next;
}
#endif
typedef struct _GskGLTextureData GskGLTextureData;
struct _GskGLTextureData
{
GdkGLContext *context;
GLuint texture_id;
GLsync sync;
};
static void
gsk_gl_texture_data_free (gpointer user_data)
{
GskGLTextureData *data = user_data;
gdk_gl_context_make_current (data->context);
/* can't use g_clear_pointer() on glDeleteSync(), see MR !7294 */
if (data->sync)
{
glDeleteSync (data->sync);
data->sync = NULL;
}
glDeleteTextures (1, &data->texture_id);
g_object_unref (data->context);
g_free (data);
}
#ifdef HAVE_DMABUF
typedef struct
{
GdkDmabuf dmabuf;
} Texture;
static void
release_dmabuf_texture (gpointer data)
{
Texture *texture = data;
gdk_dmabuf_close_fds (&texture->dmabuf);
g_free (texture);
}
#endif
static GskGpuOp *
gsk_gpu_download_op_gl_command (GskGpuOp *op,
GskGpuFrame *frame,
GskGLCommandState *state)
{
GskGpuDownloadOp *self = (GskGpuDownloadOp *) op;
GdkGLTextureBuilder *builder;
GskGLTextureData *data;
guint texture_id;
texture_id = gsk_gl_image_get_texture_id (GSK_GL_IMAGE (self->image));
#ifdef HAVE_DMABUF
if (self->allow_dmabuf)
{
GdkGLContext *context;
Texture *texture;
context = GDK_GL_CONTEXT (gsk_gpu_frame_get_context (frame));
texture = g_new0 (Texture, 1);
if (gdk_gl_context_export_dmabuf (context, texture_id, &texture->dmabuf))
{
GdkDmabufTextureBuilder *db;
db = gdk_dmabuf_texture_builder_new ();
gdk_dmabuf_texture_builder_set_display (db, gdk_gl_context_get_display (context));
gdk_dmabuf_texture_builder_set_dmabuf (db, &texture->dmabuf);
gdk_dmabuf_texture_builder_set_premultiplied (db, gdk_memory_format_get_premultiplied (gsk_gpu_image_get_format (self->image)));
gdk_dmabuf_texture_builder_set_width (db, gsk_gpu_image_get_width (self->image));
gdk_dmabuf_texture_builder_set_height (db, gsk_gpu_image_get_height (self->image));
gdk_dmabuf_texture_builder_set_color_state (db, self->color_state);
*self->texture = gdk_dmabuf_texture_builder_build (db, release_dmabuf_texture, texture, NULL);
g_object_unref (db);
if (*self->texture)
return op->next;
gdk_dmabuf_close_fds (&texture->dmabuf);
}
g_free (texture);
}
#endif
data = g_new (GskGLTextureData, 1);
/* Don't use the renderer context for the texture,
* the texture might survive the frame and its surface */
data->context = g_object_ref (gdk_display_get_gl_context (gsk_gpu_device_get_display (gsk_gpu_frame_get_device (frame))));
data->texture_id = texture_id;
data->sync = glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
builder = gdk_gl_texture_builder_new ();
gdk_gl_texture_builder_set_context (builder, data->context);
gdk_gl_texture_builder_set_id (builder, data->texture_id);
gdk_gl_texture_builder_set_format (builder, gsk_gpu_image_get_format (self->image));
gdk_gl_texture_builder_set_color_state (builder, self->color_state);
gdk_gl_texture_builder_set_width (builder, gsk_gpu_image_get_width (self->image));
gdk_gl_texture_builder_set_height (builder, gsk_gpu_image_get_height (self->image));
gdk_gl_texture_builder_set_sync (builder, data->sync);
*self->texture = gdk_gl_texture_builder_build (builder,
gsk_gl_texture_data_free,
data);
gsk_gpu_image_toggle_ref_texture (self->image, *self->texture);
gsk_gl_image_steal_texture_ownership (GSK_GL_IMAGE (self->image));
g_object_unref (builder);
return op->next;
}
static const GskGpuOpClass GSK_GPU_DOWNLOAD_OP_CLASS = {
GSK_GPU_OP_SIZE (GskGpuDownloadOp),
GSK_GPU_STAGE_COMMAND,
gsk_gpu_download_op_finish,
gsk_gpu_download_op_print,
#ifdef GDK_RENDERING_VULKAN
gsk_gpu_download_op_vk_command,
#endif
gsk_gpu_download_op_gl_command
};
void
gsk_gpu_download_op (GskGpuFrame *frame,
GskGpuImage *image,
GdkColorState *color_state,
GdkTexture **out_texture)
{
GskGpuDownloadOp *self;
g_assert (out_texture != NULL && *out_texture == NULL);
self = (GskGpuDownloadOp *) gsk_gpu_op_alloc (frame, &GSK_GPU_DOWNLOAD_OP_CLASS);
self->image = g_object_ref (image);
self->color_state = gdk_color_state_ref (color_state);
self->texture = out_texture;
}
typedef struct _GskGpuDownloadIntoOp GskGpuDownloadIntoOp;
typedef void (* GdkGpuDownloadIntoOpCreateFunc) (GskGpuDownloadIntoOp *);
struct _GskGpuDownloadIntoOp
{
GskGpuOp op;
GdkGpuDownloadIntoOpCreateFunc create_func;
GskGpuBuffer *buffer;
GskGpuImage *image;
GdkColorState *image_color_state;
GdkMemoryFormat format;
GdkColorState *color_state;
guchar *data;
gsize stride;
};
static void
gsk_gpu_download_into_op_finish (GskGpuOp *op)
{
GskGpuDownloadIntoOp *self = (GskGpuDownloadIntoOp *) op;
if (self->create_func)
self->create_func (self);
g_object_unref (self->image);
gdk_color_state_unref (self->image_color_state);
gdk_color_state_unref (self->color_state);
g_clear_object (&self->buffer);
}
static void
gsk_gpu_download_into_op_print (GskGpuOp *op,
GskGpuFrame *frame,
GString *string,
guint indent)
{
GskGpuDownloadIntoOp *self = (GskGpuDownloadIntoOp *) op;
gsk_gpu_print_op (string, indent, "download-into");
gsk_gpu_print_image (string, self->image);
g_string_append_printf (string, "%s ", gdk_color_state_get_name (self->image_color_state));
g_string_append_printf (string, "%s ", gdk_memory_format_get_name (self->format));
g_string_append_printf (string, "%s ", gdk_color_state_get_name (self->color_state));
gsk_gpu_print_newline (string);
}
#ifdef GDK_RENDERING_VULKAN
static void
gsk_gpu_download_into_op_vk_create (GskGpuDownloadIntoOp *self)
{
guchar *data;
GdkMemoryFormat format;
gsize width, height;
data = gsk_gpu_buffer_map (self->buffer);
format = gsk_gpu_image_get_format (self->image);
width = gsk_gpu_image_get_width (self->image);
height = gsk_gpu_image_get_height (self->image);
gdk_memory_convert (self->data,
self->stride,
self->format,
self->color_state,
data,
width * gdk_memory_format_bytes_per_pixel (format),
format,
self->image_color_state,
width,
height);
}
static GskGpuOp *
gsk_gpu_download_into_op_vk_command (GskGpuOp *op,
GskGpuFrame *frame,
GskVulkanCommandState *state)
{
GskGpuDownloadIntoOp *self = (GskGpuDownloadIntoOp *) op;
self->buffer = gsk_gpu_download_vk_start (frame, state, self->image);
self->create_func = gsk_gpu_download_into_op_vk_create;
return op->next;
}
#endif
static GskGpuOp *
gsk_gpu_download_into_op_gl_command (GskGpuOp *op,
GskGpuFrame *frame,
GskGLCommandState *state)
{
GskGpuDownloadIntoOp *self = (GskGpuDownloadIntoOp *) op;
gdk_gl_context_download (GDK_GL_CONTEXT (gsk_gpu_frame_get_context (frame)),
gsk_gl_image_get_texture_id (GSK_GL_IMAGE (self->image)),
gsk_gpu_image_get_format (self->image),
self->image_color_state,
self->data,
self->stride,
self->format,
self->color_state,
gsk_gpu_image_get_width (self->image),
gsk_gpu_image_get_height (self->image));
return op->next;
}
static const GskGpuOpClass GSK_GPU_DOWNLOAD_INTO_OP_CLASS = {
GSK_GPU_OP_SIZE (GskGpuDownloadIntoOp),
GSK_GPU_STAGE_COMMAND,
gsk_gpu_download_into_op_finish,
gsk_gpu_download_into_op_print,
#ifdef GDK_RENDERING_VULKAN
gsk_gpu_download_into_op_vk_command,
#endif
gsk_gpu_download_into_op_gl_command
};
void
gsk_gpu_download_into_op (GskGpuFrame *frame,
GskGpuImage *image,
GdkColorState *image_color_state,
GdkMemoryFormat format,
GdkColorState *color_state,
guchar *data,
gsize stride)
{
GskGpuDownloadIntoOp *self;
self = (GskGpuDownloadIntoOp *) gsk_gpu_op_alloc (frame, &GSK_GPU_DOWNLOAD_INTO_OP_CLASS);
self->image = g_object_ref (image);
self->image_color_state = gdk_color_state_ref (image_color_state);
self->format = format;
self->color_state = gdk_color_state_ref (color_state);
self->data = data;
self->stride = stride;
}