Skip to content

Commit

Permalink
Support multi viewport rendering with reusable text atlas (grovesNL#88)
Browse files Browse the repository at this point in the history
  • Loading branch information
PPakalns authored and hecrj committed May 8, 2024
1 parent cd66a24 commit 5a4fc2c
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 79 deletions.
62 changes: 37 additions & 25 deletions src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ struct Inner {
sampler: Sampler,
shader: ShaderModule,
vertex_buffers: [wgpu::VertexBufferLayout<'static>; 1],
bind_group_layout: BindGroupLayout,
atlas_layout: BindGroupLayout,
uniforms_layout: BindGroupLayout,
pipeline_layout: PipelineLayout,
cache: RwLock<
HashMap<(TextureFormat, MultisampleState, Option<DepthStencilState>), Arc<RenderPipeline>>,
Expand Down Expand Up @@ -86,20 +87,10 @@ impl Pipeline {
],
};

let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
let atlas_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,
},
BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
Expand All @@ -109,7 +100,7 @@ impl Pipeline {
count: None,
},
BindGroupLayoutEntry {
binding: 2,
binding: 1,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
Expand All @@ -119,7 +110,7 @@ impl Pipeline {
count: None,
},
BindGroupLayoutEntry {
binding: 3,
binding: 2,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
Expand All @@ -128,53 +119,74 @@ impl Pipeline {
label: Some("glyphon 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: &[&bind_group_layout],
bind_group_layouts: &[&atlas_layout, &uniforms_layout],
push_constant_ranges: &[],
});

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

pub(crate) fn create_bind_group(
pub(crate) fn create_atlas_bind_group(
&self,
device: &Device,
params_buffer: &Buffer,
color_atlas: &TextureView,
mask_atlas: &TextureView,
) -> BindGroup {
device.create_bind_group(&BindGroupDescriptor {
layout: &self.0.bind_group_layout,
layout: &self.0.atlas_layout,
entries: &[
BindGroupEntry {
binding: 0,
resource: params_buffer.as_entire_binding(),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::TextureView(color_atlas),
},
BindGroupEntry {
binding: 2,
binding: 1,
resource: BindingResource::TextureView(mask_atlas),
},
BindGroupEntry {
binding: 3,
binding: 2,
resource: BindingResource::Sampler(&self.0.sampler),
},
],
label: Some("glyphon 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(
&self,
device: &Device,
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(0) @binding(1)
var color_atlas_texture: texture_2d<f32>;

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

@group(0) @binding(3)
@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
42 changes: 12 additions & 30 deletions src/text_atlas.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use crate::{
text_render::ContentType, CacheKey, FontSystem, GlyphDetails, GpuCacheStatus, Params, Pipeline,
Resolution, SwashCache,
text_render::ContentType, CacheKey, FontSystem, GlyphDetails, GpuCacheStatus, Pipeline,
SwashCache,
};
use etagere::{size2, Allocation, BucketedAtlasAllocator};
use lru::LruCache;
use rustc_hash::FxHasher;
use std::{collections::HashSet, hash::BuildHasherDefault, mem::size_of, sync::Arc};
use std::{collections::HashSet, hash::BuildHasherDefault, sync::Arc};
use wgpu::{
BindGroup, Buffer, BufferDescriptor, BufferUsages, DepthStencilState, Device, Extent3d,
ImageCopyTexture, ImageDataLayout, MultisampleState, Origin3d, Queue, RenderPipeline, Texture,
TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView,
TextureViewDescriptor,
BindGroup, Buffer, DepthStencilState, Device, Extent3d, ImageCopyTexture, ImageDataLayout,
MultisampleState, Origin3d, Queue, RenderPipeline, Texture, TextureAspect, TextureDescriptor,
TextureDimension, TextureFormat, TextureUsages, TextureView, TextureViewDescriptor,
};

type Hasher = BuildHasherDefault<FxHasher>;
Expand Down Expand Up @@ -254,8 +253,6 @@ pub enum ColorMode {
/// An atlas containing a cache of rasterized glyphs that can be rendered.
pub struct TextAtlas {
pipeline: Pipeline,
pub(crate) params: Params,
pub(crate) params_buffer: Buffer,
pub(crate) bind_group: Arc<BindGroup>,
pub(crate) color_atlas: InnerAtlas,
pub(crate) mask_atlas: InnerAtlas,
Expand All @@ -277,21 +274,6 @@ impl TextAtlas {
format: TextureFormat,
color_mode: ColorMode,
) -> Self {
let params = Params {
screen_resolution: Resolution {
width: 0,
height: 0,
},
_pad: [0, 0],
};

let params_buffer = device.create_buffer(&BufferDescriptor {
label: Some("glyphon params"),
size: size_of::<Params>() as u64,
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
mapped_at_creation: false,
});

let color_atlas = InnerAtlas::new(
device,
queue,
Expand All @@ -304,17 +286,14 @@ impl TextAtlas {
);
let mask_atlas = InnerAtlas::new(device, queue, Kind::Mask);

let bind_group = Arc::new(pipeline.create_bind_group(
let bind_group = Arc::new(pipeline.create_atlas_bind_group(
device,
&params_buffer,
&color_atlas.texture_view,
&mask_atlas.texture_view,
));

Self {
pipeline: pipeline.clone(),
params,
params_buffer,
bind_group,
color_atlas,
mask_atlas,
Expand Down Expand Up @@ -372,10 +351,13 @@ impl TextAtlas {
.get_or_create(device, self.format, multisample, depth_stencil)
}

pub(crate) fn create_uniforms_bind_group(&self, device: &Device, buffer: &Buffer) -> BindGroup {
self.pipeline.create_uniforms_bind_group(device, buffer)
}

fn rebind(&mut self, device: &wgpu::Device) {
self.bind_group = Arc::new(self.pipeline.create_bind_group(
self.bind_group = Arc::new(self.pipeline.create_atlas_bind_group(
device,
&self.params_buffer,
&self.color_atlas.texture_view,
&self.mask_atlas.texture_view,
));
Expand Down
47 changes: 28 additions & 19 deletions src/text_render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ use wgpu::{
/// A text renderer that uses cached glyphs to render text into an existing render pass.
pub struct TextRenderer {
staging_belt: StagingBelt,
params: Params,
params_buffer: Buffer,
bind_group: wgpu::BindGroup,
vertex_buffer: Buffer,
vertex_buffer_size: u64,
screen_resolution: Resolution,
pipeline: Arc<RenderPipeline>,
glyph_vertices: Vec<GlyphToRender>,
glyphs_to_render: u32,
Expand All @@ -39,14 +41,30 @@ impl TextRenderer {

let pipeline = atlas.get_or_create_pipeline(device, multisample, depth_stencil);

Self {
staging_belt: StagingBelt::new(vertex_buffer_size),
vertex_buffer,
vertex_buffer_size,
let params = Params {
screen_resolution: Resolution {
width: 0,
height: 0,
},
_pad: [0, 0],
};

let params_buffer = device.create_buffer(&BufferDescriptor {
label: Some("glyphon params"),
size: size_of::<Params>() as u64,
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
mapped_at_creation: false,
});

let bind_group = atlas.create_uniforms_bind_group(device, &params_buffer);

Self {
staging_belt: StagingBelt::new(vertex_buffer_size),
params,
params_buffer,
bind_group,
vertex_buffer,
vertex_buffer_size,
pipeline,
glyph_vertices: Vec::new(),
glyphs_to_render: 0,
Expand All @@ -67,15 +85,12 @@ impl TextRenderer {
mut metadata_to_depth: impl FnMut(usize) -> f32,
) -> Result<(), PrepareError> {
self.staging_belt.recall();
self.screen_resolution = screen_resolution;

let atlas_current_resolution = { atlas.params.screen_resolution };

if screen_resolution != atlas_current_resolution {
atlas.params.screen_resolution = screen_resolution;
queue.write_buffer(&atlas.params_buffer, 0, unsafe {
if self.params.screen_resolution != screen_resolution {
self.params.screen_resolution = screen_resolution;
queue.write_buffer(&self.params_buffer, 0, unsafe {
slice::from_raw_parts(
&atlas.params as *const Params as *const u8,
&self.params as *const Params as *const u8,
size_of::<Params>(),
)
});
Expand Down Expand Up @@ -362,15 +377,9 @@ impl TextRenderer {
return Ok(());
}

{
// Validate that screen resolution hasn't changed since `prepare`
if self.screen_resolution != atlas.params.screen_resolution {
return Err(RenderError::ScreenResolutionChanged);
}
}

pass.set_pipeline(&self.pipeline);
pass.set_bind_group(0, &atlas.bind_group, &[]);
pass.set_bind_group(1, &self.bind_group, &[]);
pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
pass.draw(0..4, 0..self.glyphs_to_render);

Expand Down

0 comments on commit 5a4fc2c

Please sign in to comment.