From 23fa98b7e076fe10e0b240c9c5bfcf5c65508b5f Mon Sep 17 00:00:00 2001 From: Laurenz Stampfl Date: Thu, 1 Oct 2026 07:41:20 +0200 Subject: [PATCH] vello_gpu: Revert to only binding 1 external texture per draw --- vello_gpu/src/draw.rs | 130 ++++++++++---------------- vello_gpu/src/render/webgl/mod.rs | 18 ++-- vello_gpu/src/render/wgpu/mod.rs | 29 +----- vello_gpu_shaders/shaders/render.wesl | 54 ++--------- 4 files changed, 70 insertions(+), 161 deletions(-) diff --git a/vello_gpu/src/draw.rs b/vello_gpu/src/draw.rs index cd725265e..8e826f005 100644 --- a/vello_gpu/src/draw.rs +++ b/vello_gpu/src/draw.rs @@ -447,7 +447,7 @@ impl LayerTextureRegion { } /// Number of external textures that can be sampled by one strip draw. -pub(crate) const EXTERNAL_TEXTURE_SLOT_COUNT: usize = 4; +pub(crate) const EXTERNAL_TEXTURE_SLOT_COUNT: usize = 1; /// External texture bindings for one strip draw. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] @@ -461,7 +461,7 @@ impl ExternalTextureBindings { }; /// Return the existing slot for `texture_source`, or insert it into the first empty slot. - /// Returns `None` when all four slots are occupied by other textures. + /// Returns `None` when the slot is occupied by another texture. #[inline] fn get_or_insert(&mut self, texture_source: TextureSourceId) -> Option { // This iteration order assumes slots are assigned without leaving "holes" in-between, @@ -684,7 +684,12 @@ mod tests { texture_ids().map(TextureSourceId::External) } - fn run_states(runs: &[ExternalTextureRun]) -> Vec<([Option; 4], usize)> { + fn run_states( + runs: &[ExternalTextureRun], + ) -> Vec<( + [Option; super::EXTERNAL_TEXTURE_SLOT_COUNT], + usize, + )> { runs.iter() .map(|run| (run.bindings.as_array(), run.strips_start)) .collect() @@ -754,15 +759,11 @@ mod tests { assert_eq!( run_states(&draw.external_texture_runs), - [( - [ - Some(TextureSourceId::External(texture_a)), - Some(TextureSourceId::External(texture_b)), - None, - None, - ], - 0, - )] + [ + ([Some(TextureSourceId::External(texture_a))], 0), + ([Some(TextureSourceId::External(texture_b))], 2), + ([Some(TextureSourceId::External(texture_a))], 4), + ] ); } @@ -787,24 +788,12 @@ mod tests { assert_eq!( run_states(&draw.external_texture_runs), [ - ( - [ - Some(TextureSourceId::External(textures[0])), - Some(TextureSourceId::External(textures[1])), - Some(TextureSourceId::External(textures[2])), - Some(TextureSourceId::External(textures[3])) - ], - 0 - ), - ( - [ - Some(TextureSourceId::External(textures[4])), - Some(TextureSourceId::External(textures[0])), - None, - None, - ], - 4, - ), + ([Some(TextureSourceId::External(textures[0]))], 0), + ([Some(TextureSourceId::External(textures[1]))], 1), + ([Some(TextureSourceId::External(textures[2]))], 2), + ([Some(TextureSourceId::External(textures[3]))], 3), + ([Some(TextureSourceId::External(textures[4]))], 4), + ([Some(TextureSourceId::External(textures[0]))], 5), ] ); assert_eq!( @@ -813,7 +802,7 @@ mod tests { .iter() .map(|strip| (strip.paint_and_rect_flag >> EXTERNAL_TEXTURE_SLOT_SHIFT) & 0x3) .collect::>(), - [0, 1, 2, 3, 0, 1] + [0, 0, 0, 0, 0, 0] ); } @@ -830,10 +819,7 @@ mod tests { assert_eq!( run_states(&draw.external_texture_runs), - [( - [Some(TextureSourceId::External(texture)), None, None, None], - 0, - )] + [([Some(TextureSourceId::External(texture))], 0)] ); } @@ -853,15 +839,13 @@ mod tests { assert_eq!(draw.strip_ranges.len(), 3); assert_eq!( - draw.external_texture_runs[0].bindings.as_array(), + run_states(&draw.external_texture_runs), [ - Some(TextureSourceId::External(texture)), - Some(TextureSourceId::Atlas(AtlasId::new(0))), - None, - None, + ([Some(TextureSourceId::External(texture))], 0), + ([Some(TextureSourceId::Atlas(AtlasId::new(0)))], 1), + ([Some(TextureSourceId::External(texture))], 2), ] ); - assert_eq!(draw.external_texture_runs[0].strips_start, 0); } #[test] @@ -908,25 +892,19 @@ mod tests { assert_eq!( run_states(draw.external_texture_runs()), [ - ( - [ - Some(textures[0]), - Some(textures[1]), - Some(textures[2]), - Some(textures[3]), - ], - 0, - ), - ( - [ - Some(textures[4]), - Some(textures[0]), - Some(textures[5]), - Some(textures[6]), - ], - 19, - ), - ([Some(textures[7]), Some(textures[1]), None, None], 28,), + ([Some(textures[0])], 0), + ([Some(textures[1])], 6), + ([Some(textures[2])], 8), + ([Some(textures[0])], 13), + ([Some(textures[1])], 15), + ([Some(textures[3])], 16), + ([Some(textures[0])], 18), + ([Some(textures[4])], 19), + ([Some(textures[0])], 22), + ([Some(textures[5])], 23), + ([Some(textures[6])], 26), + ([Some(textures[7])], 28), + ([Some(textures[1])], 30), ] ); let original_slots = external_texture_slots(&draw); @@ -941,25 +919,19 @@ mod tests { assert_eq!( run_states(draw.external_texture_runs()), [ - ([Some(textures[7]), Some(textures[1]), None, None], 0,), - ( - [ - Some(textures[4]), - Some(textures[0]), - Some(textures[5]), - Some(textures[6]), - ], - 4, - ), - ( - [ - Some(textures[0]), - Some(textures[1]), - Some(textures[2]), - Some(textures[3]), - ], - 13, - ), + ([Some(textures[1])], 0), + ([Some(textures[7])], 2), + ([Some(textures[6])], 4), + ([Some(textures[5])], 6), + ([Some(textures[0])], 9), + ([Some(textures[4])], 10), + ([Some(textures[0])], 13), + ([Some(textures[3])], 14), + ([Some(textures[1])], 16), + ([Some(textures[0])], 17), + ([Some(textures[2])], 19), + ([Some(textures[1])], 24), + ([Some(textures[0])], 26), ] ); } diff --git a/vello_gpu/src/render/webgl/mod.rs b/vello_gpu/src/render/webgl/mod.rs index 496618e71..52fb657a3 100644 --- a/vello_gpu/src/render/webgl/mod.rs +++ b/vello_gpu/src/render/webgl/mod.rs @@ -2128,12 +2128,7 @@ fn get_strip_uniforms( let encoded_paints_texture_vs_name = render::vertex::ENCODED_PAINTS_TEXTURE; let gradient_texture_name = render::fragment::GRADIENT_TEXTURE; // TODO: Change it so this is based on `EXTERNAL_TEXTURE_SLOT_COUNT`. - let external_texture_names = [ - render::fragment::EXTERNAL_TEXTURE_0, - render::fragment::EXTERNAL_TEXTURE_1, - render::fragment::EXTERNAL_TEXTURE_2, - render::fragment::EXTERNAL_TEXTURE_3, - ]; + let external_texture_names = [render::fragment::EXTERNAL_TEXTURE_0]; Ok(StripUniforms { config_vs_block_index, @@ -2151,12 +2146,11 @@ fn get_strip_uniforms( encoded_paints_texture_vs_name, )?, gradient_texture: required_uniform_location(gl, program, gradient_texture_name)?, - external_textures: [ - required_uniform_location(gl, program, external_texture_names[0])?, - required_uniform_location(gl, program, external_texture_names[1])?, - required_uniform_location(gl, program, external_texture_names[2])?, - required_uniform_location(gl, program, external_texture_names[3])?, - ], + external_textures: [required_uniform_location( + gl, + program, + external_texture_names[0], + )?], }) } diff --git a/vello_gpu/src/render/wgpu/mod.rs b/vello_gpu/src/render/wgpu/mod.rs index ace1ba486..1bcb269ef 100644 --- a/vello_gpu/src/render/wgpu/mod.rs +++ b/vello_gpu/src/render/wgpu/mod.rs @@ -1138,12 +1138,7 @@ impl Programs { }, count: None, }; - let external_texture_layout_entries = [ - external_texture_layout_entry(0), - external_texture_layout_entry(1), - external_texture_layout_entry(2), - external_texture_layout_entry(3), - ]; + let external_texture_layout_entries = [external_texture_layout_entry(0)]; let external_texture_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("External Texture Bind Group Layout"), @@ -2033,24 +2028,10 @@ impl Programs { external_texture_bind_group_layout: &BindGroupLayout, texture_views: [&TextureView; EXTERNAL_TEXTURE_SLOT_COUNT], ) -> BindGroup { - let entries = [ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(texture_views[0]), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::TextureView(texture_views[1]), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::TextureView(texture_views[2]), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: wgpu::BindingResource::TextureView(texture_views[3]), - }, - ]; + let entries = [wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(texture_views[0]), + }]; device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("External Texture Bind Group"), layout: external_texture_bind_group_layout, diff --git a/vello_gpu_shaders/shaders/render.wesl b/vello_gpu_shaders/shaders/render.wesl index eb0841987..a909af54b 100644 --- a/vello_gpu_shaders/shaders/render.wesl +++ b/vello_gpu_shaders/shaders/render.wesl @@ -123,7 +123,7 @@ struct Config { // - Bits 0-25: // - If paint_type = 0: unused // - Bits 0-23: `paint_texture_idx` for every non-solid paint -// - Bits 24-25: external texture slot for external images; unused otherwise +// - Bits 24-25: reserved // // When color_source = 1 (COLOR_SOURCE_LAYER): // - Bits 0-7: opacity (0-255) @@ -216,15 +216,6 @@ var config: Config; @group(1) @binding(0) var external_texture_0: texture_2d; -@group(1) @binding(1) -var external_texture_1: texture_2d; - -@group(1) @binding(2) -var external_texture_2: texture_2d; - -@group(1) @binding(3) -var external_texture_3: texture_2d; - @group(2) @binding(0) var encoded_paints_texture: texture_2d; @@ -412,43 +403,14 @@ fn fs_main( // TODO: add a fast path for images where we are using bilinear sampling and want transparent pixels, // using GPU-native bilinear sampling - var sample_color: vec4; - let external_texture_slot = - (paint_and_rect_flag >> EXTERNAL_TEXTURE_SLOT_SHIFT) & 0x3u; let final_xy = image_offset + extended_xy; - if external_texture_slot == 0u { - sample_color = sample_external_image( - external_texture_0, - image_quality, - final_xy, - image_offset, - image_size, - ); - } else if external_texture_slot == 1u { - sample_color = sample_external_image( - external_texture_1, - image_quality, - final_xy, - image_offset, - image_size, - ); - } else if external_texture_slot == 2u { - sample_color = sample_external_image( - external_texture_2, - image_quality, - final_xy, - image_offset, - image_size, - ); - } else { - sample_color = sample_external_image( - external_texture_3, - image_quality, - final_xy, - image_offset, - image_size, - ); - } + let sample_color = sample_external_image( + external_texture_0, + image_quality, + final_xy, + image_offset, + image_size, + ); final_color = alpha * select( image_tint * sample_color.a,