From d428807d5258d85f41cc446c59572d8f3f46fc2a Mon Sep 17 00:00:00 2001 From: Paris DOUADY Date: Wed, 20 Dec 2023 12:35:33 +0100 Subject: [PATCH] Add bindgroup cache in wgpu avoid recreating them every frame --- crates/yakui-wgpu/src/bindgroup_cache.rs | 74 ++++++++++++++++++++++++ crates/yakui-wgpu/src/lib.rs | 73 +++++++++++------------ 2 files changed, 111 insertions(+), 36 deletions(-) create mode 100644 crates/yakui-wgpu/src/bindgroup_cache.rs diff --git a/crates/yakui-wgpu/src/bindgroup_cache.rs b/crates/yakui-wgpu/src/bindgroup_cache.rs new file mode 100644 index 00000000..f3f189c1 --- /dev/null +++ b/crates/yakui-wgpu/src/bindgroup_cache.rs @@ -0,0 +1,74 @@ +use crate::samplers::Samplers; +use std::collections::HashMap; +use wgpu::{FilterMode, TextureView}; +use yakui_core::TextureId; + +#[derive(Copy, Clone, Hash, PartialEq, Eq)] +pub(crate) struct TextureBindgroupCacheEntry { + pub id: TextureId, + pub min_filter: FilterMode, + pub mag_filter: FilterMode, +} + +pub(crate) struct TextureBindgroupCache { + cache: HashMap, + layout: wgpu::BindGroupLayout, + pub default: wgpu::BindGroup, +} + +impl TextureBindgroupCache { + pub fn new(layout: wgpu::BindGroupLayout, default: wgpu::BindGroup) -> Self { + Self { + cache: HashMap::new(), + layout, + default, + } + } + + pub fn update( + &mut self, + device: &wgpu::Device, + entry: TextureBindgroupCacheEntry, + view: &TextureView, + samplers: &Samplers, + ) { + self.cache.entry(entry).or_insert_with(|| { + bindgroup( + device, + &self.layout, + samplers, + view, + entry.min_filter, + entry.mag_filter, + ) + }); + } + + pub fn get(&self, entry: &TextureBindgroupCacheEntry) -> &wgpu::BindGroup { + self.cache.get(entry).unwrap_or(&self.default) + } +} + +pub fn bindgroup( + device: &wgpu::Device, + layout: &wgpu::BindGroupLayout, + samplers: &Samplers, + view: &TextureView, + min_filter: FilterMode, + mag_filter: FilterMode, +) -> wgpu::BindGroup { + device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("yakui Bind Group"), + layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(samplers.get(min_filter, mag_filter)), + }, + ], + }) +} diff --git a/crates/yakui-wgpu/src/lib.rs b/crates/yakui-wgpu/src/lib.rs index 0a3a4e44..5bb6c977 100644 --- a/crates/yakui-wgpu/src/lib.rs +++ b/crates/yakui-wgpu/src/lib.rs @@ -1,5 +1,6 @@ #![allow(clippy::new_without_default)] +mod bindgroup_cache; mod buffer; mod pipeline_cache; mod samplers; @@ -18,6 +19,8 @@ use yakui_core::geometry::{Rect, Vec2, Vec4}; use yakui_core::paint::{PaintDom, Pipeline, Texture, TextureChange, TextureFormat}; use yakui_core::{ManagedTextureId, TextureId}; +use self::bindgroup_cache::TextureBindgroupCache; +use self::bindgroup_cache::TextureBindgroupCacheEntry; use self::pipeline_cache::PipelineCache; use self::samplers::Samplers; use self::texture::{GpuManagedTexture, GpuTexture}; @@ -25,11 +28,10 @@ use self::texture::{GpuManagedTexture, GpuTexture}; pub struct YakuiWgpu { main_pipeline: PipelineCache, text_pipeline: PipelineCache, - layout: wgpu::BindGroupLayout, - default_texture: GpuManagedTexture, samplers: Samplers, textures: Arena, managed_textures: HashMap, + texture_bindgroup_cache: TextureBindgroupCache, vertices: Buffer, indices: Buffer, @@ -109,16 +111,23 @@ impl YakuiWgpu { let default_texture_data = Texture::new(TextureFormat::Rgba8Srgb, UVec2::new(1, 1), vec![255; 4]); let default_texture = GpuManagedTexture::new(device, queue, &default_texture_data); + let default_bindgroup = bindgroup_cache::bindgroup( + device, + &layout, + &samplers, + &default_texture.view, + default_texture.min_filter, + default_texture.mag_filter, + ); Self { main_pipeline, text_pipeline, - layout, - default_texture, samplers, textures: Arena::new(), managed_textures: HashMap::new(), + texture_bindgroup_cache: TextureBindgroupCache::new(layout, default_bindgroup), vertices: Buffer::new(wgpu::BufferUsages::VERTEX), indices: Buffer::new(wgpu::BufferUsages::INDEX), commands: Vec::new(), @@ -277,7 +286,12 @@ impl YakuiWgpu { } } - render_pass.set_bind_group(0, &command.bind_group, &[]); + let bindgroup = command + .bind_group_entry + .map(|entry| self.texture_bindgroup_cache.get(&entry)) + .unwrap_or(&self.texture_bindgroup_cache.default); + + render_pass.set_bind_group(0, bindgroup, &[]); render_pass.draw_indexed(command.index_range.clone(), 0, 0..1); } } @@ -310,45 +324,33 @@ impl YakuiWgpu { self.vertices.extend(vertices); self.indices.extend(indices); - let (view, min_filter, mag_filter) = call + let bind_group_entry = call .texture .and_then(|id| match id { TextureId::Managed(managed) => { let texture = self.managed_textures.get(&managed)?; - Some((&texture.view, texture.min_filter, texture.mag_filter)) + Some((id, &texture.view, texture.min_filter, texture.mag_filter)) } TextureId::User(bits) => { let index = Index::from_bits(bits)?; let texture = self.textures.get(index)?; - Some((&texture.view, texture.min_filter, texture.mag_filter)) + Some((id, &texture.view, texture.min_filter, texture.mag_filter)) } }) - .unwrap_or(( - &self.default_texture.view, - self.default_texture.min_filter, - self.default_texture.mag_filter, - )); - - let sampler = self.samplers.get(min_filter, mag_filter); - - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("yakui Bind Group"), - layout: &self.layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(sampler), - }, - ], - }); + .map(|(id, view, min_filter, mag_filter)| { + let entry = TextureBindgroupCacheEntry { + id, + min_filter, + mag_filter, + }; + self.texture_bindgroup_cache + .update(device, entry, view, &self.samplers); + entry + }); DrawCommand { index_range: start..end, - bind_group, + bind_group_entry, pipeline: call.pipeline, clip: call.clip, } @@ -361,10 +363,9 @@ impl YakuiWgpu { profiling::scope!("update_textures"); for (id, texture) in paint.textures() { - if !self.managed_textures.contains_key(&id) { - self.managed_textures - .insert(id, GpuManagedTexture::new(device, queue, texture)); - } + self.managed_textures + .entry(id) + .or_insert_with(|| GpuManagedTexture::new(device, queue, texture)); } for (id, change) in paint.texture_edits() { @@ -392,7 +393,7 @@ impl YakuiWgpu { struct DrawCommand { index_range: Range, - bind_group: wgpu::BindGroup, + bind_group_entry: Option, pipeline: Pipeline, clip: Option, }