Reimplement checkered background rendering (#4034)

* Reimplement background checkerboard rendering
This commit is contained in:
Timon
2026-05-01 18:45:32 +02:00
parent 7cd5531730
commit 29f6e686ee
17 changed files with 936 additions and 459 deletions

View File

@@ -6,7 +6,7 @@ use core_types::{CloneVarArgs, Context, Ctx, ExtractAll, ExtractAnimationTime, E
use glam::{DAffine2, DVec2, IVec2, UVec2};
use graph_craft::application_io::PlatformEditorApi;
use graph_craft::document::value::RenderOutput;
use graphene_application_io::ApplicationIo;
use graphene_application_io::{ApplicationIo, ImageTexture};
use rendering::{RenderOutputType as RenderOutputTypeRequest, RenderParams};
use std::collections::HashSet;
use std::hash::Hash;
@@ -26,7 +26,7 @@ pub struct TileCoord {
#[derive(Debug, Clone)]
pub struct CachedRegion {
pub texture: wgpu::Texture,
pub texture: ImageTexture,
pub texture_size: UVec2,
pub tiles: Vec<TileCoord>,
pub metadata: rendering::RenderMetadata,
@@ -41,7 +41,6 @@ pub struct CacheKey {
pub device_scale: u64,
pub zoom: u64,
pub rotation: u64,
pub hide_artboards: bool,
pub for_export: bool,
pub for_mask: bool,
pub thumbnail: bool,
@@ -60,7 +59,6 @@ impl CacheKey {
device_scale: f64,
zoom: f64,
rotation: f64,
hide_artboards: bool,
for_export: bool,
for_mask: bool,
thumbnail: bool,
@@ -87,7 +85,6 @@ impl CacheKey {
device_scale: device_scale.to_bits(),
zoom: zoom.to_bits(),
rotation: quantized_rotation.to_bits(),
hide_artboards,
for_export,
for_mask,
thumbnail,
@@ -100,23 +97,27 @@ impl CacheKey {
}
}
#[derive(Clone, Default, dyn_any::DynAny, Debug)]
pub struct TileCache(Arc<Mutex<TileCacheImpl>>);
impl TileCache {
pub fn query(&self, viewport_bounds: &AxisAlignedBbox, cache_key: &CacheKey, max_region_area: u32) -> CacheQuery {
self.0.lock().unwrap().query(viewport_bounds, cache_key, max_region_area)
}
pub fn store_regions(&self, regions: Vec<CachedRegion>) {
self.0.lock().unwrap().store_regions(regions);
}
}
#[derive(Default, Debug)]
struct TileCacheImpl {
regions: Vec<CachedRegion>,
timestamp: u64,
total_memory: usize,
cache_key: CacheKey,
texture_cache_resolution: UVec2,
/// Pool of textures of the same size: `texture_cache_resolution`.
/// Reusing textures reduces the wgpu allocation pressure,
/// which is a problem on web since we have to wait for
/// the browser to garbage collect unused textures, eating up memory.
texture_cache: Vec<Arc<wgpu::Texture>>,
}
#[derive(Clone, Default, dyn_any::DynAny, Debug)]
pub struct TileCache(Arc<Mutex<TileCacheImpl>>);
#[derive(Debug, Clone)]
pub struct RenderRegion {
pub tiles: Vec<TileCoord>,
@@ -205,7 +206,6 @@ impl TileCacheImpl {
while self.total_memory > MAX_CACHE_MEMORY_BYTES && !self.regions.is_empty() {
if let Some((oldest_idx, _)) = self.regions.iter().enumerate().min_by_key(|(_, r)| r.last_access) {
let removed = self.regions.remove(oldest_idx);
removed.texture.destroy();
self.total_memory = self.total_memory.saturating_sub(removed.memory_size);
} else {
break;
@@ -214,56 +214,9 @@ impl TileCacheImpl {
}
fn invalidate_all(&mut self) {
for region in &self.regions {
region.texture.destroy();
}
self.regions.clear();
self.total_memory = 0;
}
pub fn request_texture(&mut self, size: UVec2, device: &wgpu::Device) -> Arc<wgpu::Texture> {
if self.texture_cache_resolution != size {
self.texture_cache_resolution = size;
self.texture_cache.clear();
}
self.texture_cache.truncate(5);
for texture in &self.texture_cache {
if Arc::strong_count(texture) == 1 {
return Arc::clone(texture);
}
}
let texture = Arc::new(device.create_texture(&wgpu::TextureDescriptor {
label: Some("viewport_output"),
size: wgpu::Extent3d {
width: size.x,
height: size.y,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
}));
self.texture_cache.push(texture.clone());
texture
}
}
impl TileCache {
pub fn query(&self, viewport_bounds: &AxisAlignedBbox, cache_key: &CacheKey, max_region_area: u32) -> CacheQuery {
self.0.lock().unwrap().query(viewport_bounds, cache_key, max_region_area)
}
pub fn store_regions(&self, regions: Vec<CachedRegion>) {
self.0.lock().unwrap().store_regions(regions);
}
pub fn request_texture(&self, size: UVec2, device: &wgpu::Device) -> Arc<wgpu::Texture> {
self.0.lock().unwrap().request_texture(size, device)
}
}
fn group_into_regions(tiles: &[TileCoord], max_region_area: u32) -> Vec<RenderRegion> {
@@ -411,7 +364,6 @@ pub async fn render_output_cache<'a: 'n>(
device_scale,
zoom,
rotation,
render_params.hide_artboards,
render_params.for_export,
render_params.for_mask,
render_params.thumbnail,
@@ -454,10 +406,9 @@ pub async fn render_output_cache<'a: 'n>(
let exec = editor_api.application_io.as_ref().unwrap().gpu_executor().unwrap();
let device = &exec.context.device;
let output_texture = tile_cache.request_texture(physical_resolution, device);
let output_texture = exec.request_texture(physical_resolution).await;
let combined_metadata = composite_cached_regions(&all_regions, output_texture.as_ref(), &device_origin_offset, &footprint.transform, exec);
let combined_metadata = composite_cached_regions(&all_regions, &output_texture, &device_origin_offset, &footprint.transform, exec);
RenderOutput {
data: RenderOutputType::Texture(output_texture.into()),
@@ -496,7 +447,7 @@ where
let region_ctx = OwnedContextImpl::from(ctx).with_footprint(region_footprint).with_vararg(Box::new(region_params)).into_context();
let mut result = render_fn(region_ctx).await;
let RenderOutputType::Texture(rendered_texture) = result.data else {
let RenderOutputType::Texture(texture) = result.data else {
unreachable!("render_missing_region: expected texture output from Vello render");
};
@@ -506,7 +457,7 @@ where
let memory_size = (region_pixel_size.x * region_pixel_size.y) as usize * BYTES_PER_PIXEL;
CachedRegion {
texture: rendered_texture.as_ref().clone(),
texture,
texture_size: region_pixel_size,
tiles: region.tiles.clone(),
metadata: result.metadata,
@@ -552,7 +503,7 @@ fn composite_cached_regions(
if width > 0 && height > 0 {
encoder.copy_texture_to_texture(
wgpu::TexelCopyTextureInfo {
texture: &region.texture,
texture: region.texture.as_ref(),
mip_level: 0,
origin: wgpu::Origin3d { x: src_x, y: src_y, z: 0 },
aspect: wgpu::TextureAspect::All,