Skip to content

Commit

Permalink
Support sharing Pipeline state between TextAtlas (#95)
Browse files Browse the repository at this point in the history
* Support sharing `Pipeline` state between `TextAtlas`

* Keep using `Vec` for pipeline cache

* Use `OnceCell` to keep `Pipeline` private

* Revert "Use `OnceCell` to keep `Pipeline` private"

This reverts commit 4112732.

* Rename `Pipeline` type to `Cache`
  • Loading branch information
hecrj authored May 8, 2024
1 parent 670140e commit 5aed9e1
Show file tree
Hide file tree
Showing 6 changed files with 296 additions and 238 deletions.
11 changes: 6 additions & 5 deletions examples/hello-world.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use glyphon::{
Attrs, Buffer, Color, Family, FontSystem, Metrics, Resolution, Shaping, SwashCache, TextArea,
TextAtlas, TextBounds, TextRenderer,
Attrs, Buffer, Cache, Color, Family, FontSystem, Metrics, Resolution, Shaping, SwashCache,
TextArea, TextAtlas, TextBounds, TextRenderer,
};
use wgpu::{
CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Features, Instance,
Expand Down Expand Up @@ -71,8 +71,9 @@ async fn run() {

// Set up text renderer
let mut font_system = FontSystem::new();
let mut cache = SwashCache::new();
let mut atlas = TextAtlas::new(&device, &queue, swapchain_format);
let mut swash_cache = SwashCache::new();
let cache = Cache::new(&device);
let mut atlas = TextAtlas::new(&device, &queue, &cache, swapchain_format);
let mut text_renderer =
TextRenderer::new(&mut atlas, &device, MultisampleState::default(), None);
let mut buffer = Buffer::new(&mut font_system, Metrics::new(30.0, 42.0));
Expand Down Expand Up @@ -122,7 +123,7 @@ async fn run() {
},
default_color: Color::rgb(255, 255, 255),
}],
&mut cache,
&mut swash_cache,
)
.unwrap();

Expand Down
247 changes: 247 additions & 0 deletions src/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
use crate::{GlyphToRender, Params};

use wgpu::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutEntry,
BindingResource, BindingType, BlendState, Buffer, BufferBindingType, ColorTargetState,
ColorWrites, DepthStencilState, Device, FilterMode, FragmentState, MultisampleState,
PipelineCompilationOptions, PipelineLayout, PipelineLayoutDescriptor, PrimitiveState,
RenderPipeline, RenderPipelineDescriptor, Sampler, SamplerBindingType, SamplerDescriptor,
ShaderModule, ShaderModuleDescriptor, ShaderSource, ShaderStages, TextureFormat,
TextureSampleType, TextureView, TextureViewDimension, VertexFormat, VertexState,
};

use std::borrow::Cow;
use std::mem;
use std::num::NonZeroU64;
use std::ops::Deref;
use std::sync::{Arc, RwLock};

#[derive(Debug, Clone)]
pub struct Cache(Arc<Inner>);

#[derive(Debug)]
struct Inner {
sampler: Sampler,
shader: ShaderModule,
vertex_buffers: [wgpu::VertexBufferLayout<'static>; 1],
atlas_layout: BindGroupLayout,
uniforms_layout: BindGroupLayout,
pipeline_layout: PipelineLayout,
cache: RwLock<
Vec<(
TextureFormat,
MultisampleState,
Option<DepthStencilState>,
Arc<RenderPipeline>,
)>,
>,
}

impl Cache {
pub fn new(device: &Device) -> Self {
let sampler = device.create_sampler(&SamplerDescriptor {
label: Some("glyphon sampler"),
min_filter: FilterMode::Nearest,
mag_filter: FilterMode::Nearest,
mipmap_filter: FilterMode::Nearest,
lod_min_clamp: 0f32,
lod_max_clamp: 0f32,
..Default::default()
});

let shader = device.create_shader_module(ShaderModuleDescriptor {
label: Some("glyphon shader"),
source: ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
});

let vertex_buffer_layout = wgpu::VertexBufferLayout {
array_stride: mem::size_of::<GlyphToRender>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
format: VertexFormat::Sint32x2,
offset: 0,
shader_location: 0,
},
wgpu::VertexAttribute {
format: VertexFormat::Uint32,
offset: mem::size_of::<u32>() as u64 * 2,
shader_location: 1,
},
wgpu::VertexAttribute {
format: VertexFormat::Uint32,
offset: mem::size_of::<u32>() as u64 * 3,
shader_location: 2,
},
wgpu::VertexAttribute {
format: VertexFormat::Uint32,
offset: mem::size_of::<u32>() as u64 * 4,
shader_location: 3,
},
wgpu::VertexAttribute {
format: VertexFormat::Uint32,
offset: mem::size_of::<u32>() as u64 * 5,
shader_location: 4,
},
wgpu::VertexAttribute {
format: VertexFormat::Float32,
offset: mem::size_of::<u32>() as u64 * 6,
shader_location: 5,
},
],
};

let atlas_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
view_dimension: TextureViewDimension::D2,
sample_type: TextureSampleType::Float { filterable: true },
},
count: None,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
view_dimension: TextureViewDimension::D2,
sample_type: TextureSampleType::Float { filterable: true },
},
count: None,
},
BindGroupLayoutEntry {
binding: 2,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
],
label: Some("glyphon atlas bind group layout"),
});

let uniforms_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: NonZeroU64::new(mem::size_of::<Params>() as u64),
},
count: None,
}],
label: Some("glyphon uniforms bind group layout"),
});

let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[&atlas_layout, &uniforms_layout],
push_constant_ranges: &[],
});

Self(Arc::new(Inner {
sampler,
shader,
vertex_buffers: [vertex_buffer_layout],
uniforms_layout,
atlas_layout,
pipeline_layout,
cache: RwLock::new(Vec::new()),
}))
}

pub(crate) fn create_atlas_bind_group(
&self,
device: &Device,
color_atlas: &TextureView,
mask_atlas: &TextureView,
) -> BindGroup {
device.create_bind_group(&BindGroupDescriptor {
layout: &self.0.atlas_layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: BindingResource::TextureView(color_atlas),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::TextureView(mask_atlas),
},
BindGroupEntry {
binding: 2,
resource: BindingResource::Sampler(&self.0.sampler),
},
],
label: Some("glyphon atlas bind group"),
})
}

pub(crate) fn create_uniforms_bind_group(&self, device: &Device, buffer: &Buffer) -> BindGroup {
device.create_bind_group(&BindGroupDescriptor {
layout: &self.0.uniforms_layout,
entries: &[BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
}],
label: Some("glyphon uniforms bind group"),
})
}

pub(crate) fn get_or_create_pipeline(
&self,
device: &Device,
format: TextureFormat,
multisample: MultisampleState,
depth_stencil: Option<DepthStencilState>,
) -> Arc<RenderPipeline> {
let Inner {
cache,
pipeline_layout,
shader,
vertex_buffers,
..
} = self.0.deref();

let mut cache = cache.write().expect("Write pipeline cache");

cache
.iter()
.find(|(fmt, ms, ds, _)| fmt == &format && ms == &multisample && ds == &depth_stencil)
.map(|(_, _, _, p)| Arc::clone(p))
.unwrap_or_else(|| {
let pipeline = Arc::new(device.create_render_pipeline(&RenderPipelineDescriptor {
label: Some("glyphon pipeline"),
layout: Some(pipeline_layout),
vertex: VertexState {
module: shader,
entry_point: "vs_main",
buffers: vertex_buffers,
compilation_options: PipelineCompilationOptions::default(),
},
fragment: Some(FragmentState {
module: shader,
entry_point: "fs_main",
targets: &[Some(ColorTargetState {
format,
blend: Some(BlendState::ALPHA_BLENDING),
write_mask: ColorWrites::default(),
})],
compilation_options: PipelineCompilationOptions::default(),
}),
primitive: PrimitiveState::default(),
depth_stencil: depth_stencil.clone(),
multisample,
multiview: None,
}));

cache.push((format, multisample, depth_stencil, pipeline.clone()));

pipeline
})
.clone()
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
//! [cosmic-text]: https://github.com/pop-os/cosmic-text
//! [etagere]: https://github.com/nical/etagere
mod cache;
mod error;
mod text_atlas;
mod text_render;

pub use cache::Cache;
pub use error::{PrepareError, RenderError};
pub use text_atlas::{ColorMode, TextAtlas};
pub use text_render::TextRenderer;
Expand Down
10 changes: 5 additions & 5 deletions src/shader.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,17 @@ struct Params {
};

@group(0) @binding(0)
var<uniform> params: Params;

@group(1) @binding(0)
var color_atlas_texture: texture_2d<f32>;

@group(1) @binding(1)
@group(0) @binding(1)
var mask_atlas_texture: texture_2d<f32>;

@group(1) @binding(2)
@group(0) @binding(2)
var atlas_sampler: sampler;

@group(1) @binding(0)
var<uniform> params: Params;

fn srgb_to_linear(c: f32) -> f32 {
if c <= 0.04045 {
return c / 12.92;
Expand Down
Loading

0 comments on commit 5aed9e1

Please sign in to comment.