mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-26 21:51:08 +00:00
be2ff60787
This function does not use the standard __cdecl calling convention on Windows, meaning using g_clear_pointer() on it directly will cause crashes on 32-bit Windows. Just call it directly if the GLsync it uses exists.
357 lines
12 KiB
C
357 lines
12 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 "gskvulkanbufferprivate.h"
|
|
#include "gskvulkanframeprivate.h"
|
|
#include "gskvulkanimageprivate.h"
|
|
#endif
|
|
|
|
#include "gdk/gdkdmabuftextureprivate.h"
|
|
#include "gdk/gdkglcontextprivate.h"
|
|
|
|
#ifdef HAVE_DMABUF
|
|
#include <linux/dma-buf.h>
|
|
#endif
|
|
|
|
typedef struct _GskGpuDownloadOp GskGpuDownloadOp;
|
|
|
|
typedef void (* GdkGpuDownloadOpCreateFunc) (GskGpuDownloadOp *);
|
|
|
|
struct _GskGpuDownloadOp
|
|
{
|
|
GskGpuOp op;
|
|
|
|
GskGpuImage *image;
|
|
gboolean allow_dmabuf;
|
|
GdkGpuDownloadOpCreateFunc create_func;
|
|
GskGpuDownloadFunc func;
|
|
gpointer user_data;
|
|
|
|
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);
|
|
|
|
self->func (self->user_data, self->texture);
|
|
|
|
g_object_unref (self->texture);
|
|
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
|
|
|
|
#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;
|
|
|
|
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);
|
|
self->texture = gdk_memory_texture_new (width,
|
|
height,
|
|
format,
|
|
bytes,
|
|
stride);
|
|
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;
|
|
gsize width, height, stride;
|
|
|
|
#ifdef HAVE_DMABUF
|
|
if (self->allow_dmabuf)
|
|
self->texture = gsk_vulkan_image_to_dmabuf_texture (GSK_VULKAN_IMAGE (self->image));
|
|
if (self->texture)
|
|
{
|
|
GskVulkanDevice *device = GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame));
|
|
VkDevice vk_device = gsk_vulkan_device_get_vk_device (device);
|
|
|
|
gsk_gpu_device_cache_texture_image (GSK_GPU_DEVICE (device), self->texture, gsk_gpu_frame_get_timestamp (frame), self->image);
|
|
|
|
if (gsk_vulkan_device_has_feature (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
|
|
|
|
width = gsk_gpu_image_get_width (self->image);
|
|
height = gsk_gpu_image_get_height (self->image);
|
|
stride = width * gdk_memory_format_bytes_per_pixel (gsk_gpu_image_get_format (self->image));
|
|
self->buffer = gsk_vulkan_buffer_new_read (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame)),
|
|
height * stride);
|
|
|
|
gsk_vulkan_image_transition (GSK_VULKAN_IMAGE (self->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 (self->image)),
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
gsk_vulkan_buffer_get_vk_buffer (GSK_VULKAN_BUFFER (self->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 (self->buffer)),
|
|
.offset = 0,
|
|
.size = VK_WHOLE_SIZE,
|
|
},
|
|
0, NULL);
|
|
|
|
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);
|
|
}
|
|
|
|
static GskGpuOp *
|
|
gsk_gpu_download_op_gl_command (GskGpuOp *op,
|
|
GskGpuFrame *frame,
|
|
GskGLCommandState *state)
|
|
{
|
|
GskGpuDownloadOp *self = (GskGpuDownloadOp *) op;
|
|
GdkGLTextureBuilder *builder;
|
|
GskGLTextureData *data;
|
|
GdkGLContext *context;
|
|
|
|
context = GDK_GL_CONTEXT (gsk_gpu_frame_get_context (frame));
|
|
|
|
data = g_new (GskGLTextureData, 1);
|
|
data->context = g_object_ref (context);
|
|
data->texture_id = gsk_gl_image_steal_texture (GSK_GL_IMAGE (self->image));
|
|
|
|
if (gdk_gl_context_has_feature (context, GDK_GL_FEATURE_SYNC))
|
|
data->sync = glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
|
|
builder = gdk_gl_texture_builder_new ();
|
|
gdk_gl_texture_builder_set_context (builder, 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_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);
|
|
|
|
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,
|
|
gboolean allow_dmabuf,
|
|
GskGpuDownloadFunc func,
|
|
gpointer user_data)
|
|
{
|
|
GskGpuDownloadOp *self;
|
|
|
|
self = (GskGpuDownloadOp *) gsk_gpu_op_alloc (frame, &GSK_GPU_DOWNLOAD_OP_CLASS);
|
|
|
|
self->image = g_object_ref (image);
|
|
self->allow_dmabuf = allow_dmabuf;
|
|
self->func = func,
|
|
self->user_data = user_data;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_download_save_png_cb (gpointer filename,
|
|
GdkTexture *texture)
|
|
{
|
|
gdk_texture_save_to_png (texture, filename);
|
|
|
|
g_free (filename);
|
|
}
|
|
|
|
void
|
|
gsk_gpu_download_png_op (GskGpuFrame *frame,
|
|
GskGpuImage *image,
|
|
const char *filename_format,
|
|
...)
|
|
{
|
|
va_list args;
|
|
char *filename;
|
|
|
|
va_start (args, filename_format);
|
|
filename = g_strdup_vprintf (filename_format, args);
|
|
va_end (args);
|
|
|
|
gsk_gpu_download_op (frame,
|
|
image,
|
|
FALSE,
|
|
gsk_gpu_download_save_png_cb,
|
|
filename);
|
|
}
|