From 39f5c5bf499bf8ea361ae71859ef85242bb1786f Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Fri, 12 Jul 2024 11:32:58 +0200 Subject: [PATCH] gpu: Implement tiling for texture nodes Use the new cache feature to split oversized textures into tiles the size given by the new device API. Then number those tiles from left to right and top to bottom and use that number as the tile id. --- gsk/gpu/gskgpunodeprocessor.c | 142 ++++++++++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 7 deletions(-) diff --git a/gsk/gpu/gskgpunodeprocessor.c b/gsk/gpu/gskgpunodeprocessor.c index 2d420a1a25..ef6482999b 100644 --- a/gsk/gpu/gskgpunodeprocessor.c +++ b/gsk/gpu/gskgpunodeprocessor.c @@ -43,8 +43,10 @@ #include "gskprivate.h" #include "gdk/gdkcolorstateprivate.h" +#include "gdk/gdkmemorytextureprivate.h" #include "gdk/gdkrgbaprivate.h" #include "gdk/gdksubsurfaceprivate.h" +#include "gdk/gdktextureprivate.h" /* the epsilon we allow pixels to be off due to rounding errors. * Chosen rather randomly. @@ -1725,6 +1727,110 @@ gsk_gpu_lookup_texture (GskGpuFrame *frame, return image; } +static GskGpuImage * +gsk_gpu_get_texture_tiles_as_image (GskGpuFrame *frame, + GdkColorState *ccs, + const graphene_rect_t *clip_bounds, + const graphene_vec2_t *scale, + const graphene_rect_t *texture_bounds, + GdkTexture *texture) +{ + GskGpuNodeProcessor self; + GskGpuCache *cache; + GskGpuDevice *device; + gint64 timestamp; + GskGpuImage *image, *tile; + GdkColorState *tile_cs; + GdkMemoryTexture *memtex; + GdkTexture *subtex; + float scaled_tile_width, scaled_tile_height; + gsize tile_size, width, height, n_width, n_height, x, y; + + device = gsk_gpu_frame_get_device (frame); + cache = gsk_gpu_device_get_cache (device); + timestamp = gsk_gpu_frame_get_timestamp (frame); + tile_size = gsk_gpu_device_get_tile_size (device); + width = gdk_texture_get_width (texture); + height = gdk_texture_get_height (texture); + n_width = (width + tile_size - 1) / tile_size; + n_height = (height + tile_size - 1) / tile_size; + scaled_tile_width = texture_bounds->size.width * tile_size / width; + scaled_tile_height = texture_bounds->size.height * tile_size / height; + + image = gsk_gpu_node_processor_init_draw (&self, + frame, + ccs, + gdk_texture_get_depth (texture), + scale, + clip_bounds); + if (image == NULL) + return NULL; + + self.blend = GSK_GPU_BLEND_ADD; + self.pending_globals |= GSK_GPU_GLOBAL_BLEND; + gsk_gpu_node_processor_sync_globals (&self, 0); + + memtex = NULL; + for (y = 0; y < n_height; y++) + { + for (x = 0; x < n_width; x++) + { + graphene_rect_t tile_rect = GRAPHENE_RECT_INIT (texture_bounds->origin.x + scaled_tile_width * x, + texture_bounds->origin.y + scaled_tile_height * y, + scaled_tile_width, + scaled_tile_height); + if (!gsk_rect_intersection (&tile_rect, texture_bounds, &tile_rect) || + !gsk_rect_intersects (clip_bounds, &tile_rect)) + continue; + + tile = gsk_gpu_cache_lookup_tile (cache, texture, y * n_width + x, timestamp); + if (tile == NULL) + { + if (memtex == NULL) + memtex = gdk_memory_texture_from_texture (texture); + subtex = gdk_memory_texture_new_subtexture (memtex, + x * tile_size, + y * tile_size, + MIN (tile_size, width - x * tile_size), + MIN (tile_size, height - y * tile_size)); + tile = gsk_gpu_upload_texture_op_try (self.frame, FALSE, subtex); + g_object_unref (subtex); + if (tile == NULL) + { + g_warning ("failed to create %zux%zu tile for %zux%zu texture. Out of memory?", + tile_size, tile_size, width, height); + goto out; + } + + gsk_gpu_cache_cache_tile (cache, timestamp, texture, y * n_width + x, tile); + } + + tile_cs = gdk_texture_get_color_state (texture); + if (gsk_gpu_image_get_flags (tile) & GSK_GPU_IMAGE_SRGB) + { + tile_cs = gdk_color_state_get_no_srgb_tf (tile_cs); + g_assert (tile_cs); + } + + gsk_gpu_node_processor_image_op (&self, + tile, + tile_cs, + GSK_GPU_SAMPLER_DEFAULT, + &tile_rect, + &tile_rect); + + g_object_unref (tile); + } + } + +out: + gsk_gpu_node_processor_finish_draw (&self, image); + + g_clear_object (&memtex); + + return image; +} + static void gsk_gpu_node_processor_add_texture_node (GskGpuNodeProcessor *self, GskRenderNode *node) @@ -1741,11 +1847,25 @@ gsk_gpu_node_processor_add_texture_node (GskGpuNodeProcessor *self, if (image == NULL) { - GSK_DEBUG (FALLBACK, "Unsupported texture format %u for size %dx%d", - gdk_texture_get_format (texture), - gdk_texture_get_width (texture), - gdk_texture_get_height (texture)); - gsk_gpu_node_processor_add_cairo_node (self, node); + graphene_rect_t clip, rounded_clip; + + if (!gsk_gpu_node_processor_clip_node_bounds (self, node, &clip)) + return; + rect_round_to_pixels (&clip, &self->scale, &self->offset, &rounded_clip); + + image = gsk_gpu_get_texture_tiles_as_image (self->frame, + self->ccs, + &rounded_clip, + &self->scale, + &node->bounds, + texture); + gsk_gpu_node_processor_image_op (self, + image, + self->ccs, + GSK_GPU_SAMPLER_DEFAULT, + &clip, + &rounded_clip); + g_object_unref (image); return; } @@ -1803,9 +1923,17 @@ gsk_gpu_get_texture_node_as_image (GskGpuFrame *frame, image = gsk_gpu_lookup_texture (frame, ccs, texture, FALSE, &image_cs); - /* Happens ie for oversized textures */ if (image == NULL) - return gsk_gpu_get_node_as_image_via_offscreen (frame, ccs, clip_bounds, scale, node, out_bounds); + { + image = gsk_gpu_get_texture_tiles_as_image (frame, + ccs, + clip_bounds, + scale, + &node->bounds, + gsk_texture_node_get_texture (node)); + *out_bounds = *clip_bounds; + return image; + } if (!gdk_color_state_equal (ccs, image_cs) || gsk_gpu_image_get_flags (image) & GSK_GPU_IMAGE_STRAIGHT_ALPHA)