From: Benjamin Otte Date: Fri, 14 Jul 2023 21:22:35 +0000 (+0200) Subject: vulkan: Add GskVulkanDownloadOp X-Git-Tag: archive/raspbian/4.12.3+ds-1+rpi1~1^2^2^2~22^2~1^2~60^2~7 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=db2029d931658263318b5c2d120696b5eab89cca;p=gtk4.git vulkan: Add GskVulkanDownloadOp This op queues a download of an image. The image will only be available once the commands finished executing, so it requires waiting for the render to finish, which makes the API a bit awkward. Included is also a download_png_op() useful for debugging. --- diff --git a/gsk/meson.build b/gsk/meson.build index 260ed72c01..74cf758ca3 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -117,6 +117,7 @@ if have_vulkan 'vulkan/gskvulkancolorop.c', 'vulkan/gskvulkancommandpool.c', 'vulkan/gskvulkancrossfadeop.c', + 'vulkan/gskvulkandownloadop.c', 'vulkan/gskvulkanglyphcache.c', 'vulkan/gskvulkanglyphop.c', 'vulkan/gskvulkanimage.c', diff --git a/gsk/vulkan/gskvulkandownloadop.c b/gsk/vulkan/gskvulkandownloadop.c new file mode 100644 index 0000000000..9396721f7f --- /dev/null +++ b/gsk/vulkan/gskvulkandownloadop.c @@ -0,0 +1,279 @@ +#include "config.h" + +#include "gskvulkandownloadopprivate.h" + +#include "gskvulkanprivate.h" + +#include "gdk/gdkmemoryformatprivate.h" + +static gsize +gsk_vulkan_download_op_count_vertex_data (GskVulkanOp *op, + gsize n_bytes) +{ + return n_bytes; +} + +static void +gsk_vulkan_download_op_collect_vertex_data (GskVulkanOp *op, + guchar *data) +{ +} + +static void +gsk_vulkan_download_op_reserve_descriptor_sets (GskVulkanOp *op, + GskVulkanRender *render) +{ +} + +static GskVulkanOp * +gsk_vulkan_download_op_command_with_area (GskVulkanOp *op, + GskVulkanRender *render, + VkPipelineLayout pipeline_layout, + VkCommandBuffer command_buffer, + GskVulkanImage *image, + const cairo_rectangle_int_t *area, + GskVulkanBuffer **buffer) +{ + VkImageLayout image_layout; + VkAccessFlags access; + gsize stride; + + image_layout = gsk_vulkan_image_get_vk_image_layout (image); + access = gsk_vulkan_image_get_vk_access (image); + stride = area->width * gdk_memory_format_bytes_per_pixel (gsk_vulkan_image_get_format (image)); + *buffer = gsk_vulkan_buffer_new_map (gsk_vulkan_render_get_context (render), + area->height * stride, + GSK_VULKAN_READ); + + vkCmdPipelineBarrier (command_buffer, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, NULL, + 1, &(VkBufferMemoryBarrier) { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + .srcAccessMask = VK_ACCESS_HOST_READ_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = gsk_vulkan_buffer_get_buffer (*buffer), + .offset = 0, + .size = VK_WHOLE_SIZE, + }, + 1, &(VkImageMemoryBarrier) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = access, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .oldLayout = image_layout, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = gsk_vulkan_image_get_vk_image (image), + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + }, + }); + gsk_vulkan_image_set_vk_image_layout (image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_ACCESS_TRANSFER_READ_BIT); + + vkCmdCopyImageToBuffer (command_buffer, + gsk_vulkan_image_get_vk_image (image), + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + gsk_vulkan_buffer_get_buffer (*buffer), + 1, + (VkBufferImageCopy[1]) { + { + .bufferOffset = 0, + .bufferRowLength = area->width, + .bufferImageHeight = area->height, + .imageSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1 + }, + .imageOffset = { + .x = area->x, + .y = area->y, + .z = 0 + }, + .imageExtent = { + .width = area->width, + .height = area->height, + .depth = 1 + } + } + }); + + vkCmdPipelineBarrier (command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + 0, + 0, NULL, + 0, NULL, + 1, &(VkImageMemoryBarrier) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = gsk_vulkan_image_get_vk_access (image), + .dstAccessMask = access, + .oldLayout = gsk_vulkan_image_get_vk_image_layout (image), + .newLayout = image_layout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = gsk_vulkan_image_get_vk_image (image), + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + }, + }); + + gsk_vulkan_image_set_vk_image_layout (image, image_layout, access); + + return op->next; +} + +typedef struct _GskVulkanDownloadOp GskVulkanDownloadOp; + +struct _GskVulkanDownloadOp +{ + GskVulkanOp op; + + GskVulkanImage *image; + GskVulkanDownloadFunc func; + gpointer user_data; + + GskVulkanBuffer *buffer; +}; + +static void +gsk_vulkan_download_op_finish (GskVulkanOp *op) +{ + GskVulkanDownloadOp *self = (GskVulkanDownloadOp *) op; + guchar *data; + gsize stride; + + data = gsk_vulkan_buffer_map (self->buffer); + stride = gsk_vulkan_image_get_width (self->image) * + gdk_memory_format_bytes_per_pixel (gsk_vulkan_image_get_format (self->image)); + self->func (self->user_data, + gsk_vulkan_image_get_format (self->image), + data, + gsk_vulkan_image_get_width (self->image), + gsk_vulkan_image_get_height (self->image), + stride); + gsk_vulkan_buffer_unmap (self->buffer); + + g_object_unref (self->image); + g_clear_pointer (&self->buffer, gsk_vulkan_buffer_free); +} + +static void +gsk_vulkan_download_op_print (GskVulkanOp *op, + GString *string, + guint indent) +{ + GskVulkanDownloadOp *self = (GskVulkanDownloadOp *) op; + + print_indent (string, indent); + g_string_append (string, "download "); + print_image (string, self->image); + print_newline (string); +} + +static GskVulkanOp * +gsk_vulkan_download_op_command (GskVulkanOp *op, + GskVulkanRender *render, + VkPipelineLayout pipeline_layout, + VkCommandBuffer command_buffer) +{ + GskVulkanDownloadOp *self = (GskVulkanDownloadOp *) op; + + return gsk_vulkan_download_op_command_with_area (op, + render, + pipeline_layout, + command_buffer, + self->image, + &(cairo_rectangle_int_t) { + 0, 0, + gsk_vulkan_image_get_width (self->image), + gsk_vulkan_image_get_height (self->image) + }, + &self->buffer); +} + +static const GskVulkanOpClass GSK_VULKAN_DOWNLOAD_OP_CLASS = { + GSK_VULKAN_OP_SIZE (GskVulkanDownloadOp), + GSK_VULKAN_STAGE_COMMAND, + NULL, + NULL, + gsk_vulkan_download_op_finish, + gsk_vulkan_download_op_print, + gsk_vulkan_download_op_count_vertex_data, + gsk_vulkan_download_op_collect_vertex_data, + gsk_vulkan_download_op_reserve_descriptor_sets, + gsk_vulkan_download_op_command +}; + +void +gsk_vulkan_download_op (GskVulkanRender *render, + GskVulkanImage *image, + GskVulkanDownloadFunc func, + gpointer user_data) +{ + GskVulkanDownloadOp *self; + + self = (GskVulkanDownloadOp *) gsk_vulkan_op_alloc (render, &GSK_VULKAN_DOWNLOAD_OP_CLASS); + + self->image = g_object_ref (image); + self->func = func, + self->user_data = user_data; +} + +static void +gsk_vulkan_download_save_png_cb (gpointer filename, + GdkMemoryFormat format, + const guchar *data, + int width, + int height, + gsize stride) +{ + GdkTexture *texture; + GBytes *bytes; + + bytes = g_bytes_new_static (data, stride * height); + texture = gdk_memory_texture_new (width, height, + format, + bytes, + stride); + gdk_texture_save_to_png (texture, filename); + + g_object_unref (texture); + g_bytes_unref (bytes); + g_free (filename); +} + +void +gsk_vulkan_download_png_op (GskVulkanRender *render, + GskVulkanImage *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_vulkan_download_op (render, + image, + gsk_vulkan_download_save_png_cb, + filename); +} diff --git a/gsk/vulkan/gskvulkandownloadopprivate.h b/gsk/vulkan/gskvulkandownloadopprivate.h new file mode 100644 index 0000000000..1c7d3010d9 --- /dev/null +++ b/gsk/vulkan/gskvulkandownloadopprivate.h @@ -0,0 +1,18 @@ +#pragma once + +#include "gskvulkanopprivate.h" + +G_BEGIN_DECLS + +void gsk_vulkan_download_op (GskVulkanRender *render, + GskVulkanImage *image, + GskVulkanDownloadFunc func, + gpointer user_data); + +void gsk_vulkan_download_png_op (GskVulkanRender *render, + GskVulkanImage *image, + const char *filename_format, + ...) G_GNUC_PRINTF(3, 4); + +G_END_DECLS + diff --git a/gsk/vulkan/gskvulkanrender.c b/gsk/vulkan/gskvulkanrender.c index 17cd2daf5a..e70bd813e1 100644 --- a/gsk/vulkan/gskvulkanrender.c +++ b/gsk/vulkan/gskvulkanrender.c @@ -7,6 +7,7 @@ #include "gskrendererprivate.h" #include "gskvulkanbufferprivate.h" #include "gskvulkancommandpoolprivate.h" +#include "gskvulkandownloadopprivate.h" #include "gskvulkanglyphcacheprivate.h" #include "gskvulkanprivate.h" #include "gskvulkanpushconstantsopprivate.h" @@ -485,8 +486,10 @@ gsk_vulkan_render_sort_ops (GskVulkanRender *self) } static void -gsk_vulkan_render_add_node (GskVulkanRender *self, - GskRenderNode *node) +gsk_vulkan_render_add_node (GskVulkanRender *self, + GskRenderNode *node, + GskVulkanDownloadFunc download_func, + gpointer download_data) { graphene_vec2_t scale; @@ -502,6 +505,9 @@ gsk_vulkan_render_add_node (GskVulkanRender *self, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + if (download_func) + gsk_vulkan_download_op (self, self->target, download_func, download_data); + gsk_vulkan_render_seal_ops (self); gsk_vulkan_render_verbose_print (self, "start of frame"); gsk_vulkan_render_sort_ops (self); @@ -1073,13 +1079,15 @@ gsk_vulkan_render_render (GskVulkanRender *self, GskVulkanImage *target, const graphene_rect_t *rect, const cairo_region_t *clip, - GskRenderNode *node) + GskRenderNode *node, + GskVulkanDownloadFunc download_func, + gpointer download_data) { gsk_vulkan_render_cleanup (self); gsk_vulkan_render_setup (self, target, rect, clip); - gsk_vulkan_render_add_node (self, node); + gsk_vulkan_render_add_node (self, node, download_func, download_data); gsk_vulkan_render_submit (self); } diff --git a/gsk/vulkan/gskvulkanrenderer.c b/gsk/vulkan/gskvulkanrenderer.c index 40eb796b7a..c10588436c 100644 --- a/gsk/vulkan/gskvulkanrenderer.c +++ b/gsk/vulkan/gskvulkanrenderer.c @@ -256,6 +256,25 @@ gsk_vulkan_renderer_unrealize (GskRenderer *renderer) g_clear_object (&self->vulkan); } +static void +gsk_vulkan_renderer_download_texture_cb (gpointer user_data, + GdkMemoryFormat format, + const guchar *data, + int width, + int height, + gsize stride) +{ + GdkTexture **texture = (GdkTexture **) user_data; + GBytes *bytes; + + bytes = g_bytes_new (data, stride * height); + *texture = gdk_memory_texture_new (width, height, + format, + bytes, + stride); + g_bytes_unref (bytes); +} + static GdkTexture * gsk_vulkan_renderer_render_texture (GskRenderer *renderer, GskRenderNode *root, @@ -292,12 +311,20 @@ gsk_vulkan_renderer_render_texture (GskRenderer *renderer, rounded_viewport.size.width, rounded_viewport.size.height); - gsk_vulkan_render_render (render, image, &rounded_viewport, NULL, root); + texture = NULL; + gsk_vulkan_render_render (render, + image, + &rounded_viewport, + NULL, + root, + gsk_vulkan_renderer_download_texture_cb, + &texture); - texture = gsk_vulkan_render_download_target (render); - - g_object_unref (image); gsk_vulkan_render_free (render); + g_object_unref (image); + + /* check that callback setting texture was actually called, as its technically async */ + g_assert (texture); #ifdef G_ENABLE_DEBUG start_time = gsk_profiler_timer_get_start (profiler, self->profile_timers.cpu_time); @@ -349,7 +376,12 @@ gsk_vulkan_renderer_render (GskRenderer *renderer, render_region = get_render_region (self); draw_index = gdk_vulkan_context_get_draw_index (self->vulkan); - gsk_vulkan_render_render (render, self->targets[draw_index], NULL, render_region, root); + gsk_vulkan_render_render (render, + self->targets[draw_index], + NULL, + render_region, + root, + NULL, NULL); #ifdef G_ENABLE_DEBUG gsk_profiler_counter_inc (profiler, self->profile_counters.frames); diff --git a/gsk/vulkan/gskvulkanrenderprivate.h b/gsk/vulkan/gskvulkanrenderprivate.h index 9e24e155fb..c9322edd9a 100644 --- a/gsk/vulkan/gskvulkanrenderprivate.h +++ b/gsk/vulkan/gskvulkanrenderprivate.h @@ -15,6 +15,13 @@ typedef enum { GSK_VULKAN_SAMPLER_NEAREST } GskVulkanRenderSampler; +typedef void (* GskVulkanDownloadFunc) (gpointer user_data, + GdkMemoryFormat format, + const guchar *data, + int width, + int height, + gsize stride); + GskVulkanRender * gsk_vulkan_render_new (GskRenderer *renderer, GdkVulkanContext *context); void gsk_vulkan_render_free (GskVulkanRender *self); @@ -24,7 +31,9 @@ void gsk_vulkan_render_render (GskVulk GskVulkanImage *target, const graphene_rect_t *rect, const cairo_region_t *clip, - GskRenderNode *node); + GskRenderNode *node, + GskVulkanDownloadFunc download_func, + gpointer download_data); GskRenderer * gsk_vulkan_render_get_renderer (GskVulkanRender *self); GdkVulkanContext * gsk_vulkan_render_get_context (GskVulkanRender *self);