Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multi viewport rendering with reusable text atlas #88

Merged
merged 1 commit into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/shader.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ struct Params {
@group(0) @binding(0)
var<uniform> params: Params;

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

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

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

fn srgb_to_linear(c: f32) -> f32 {
Expand Down
80 changes: 30 additions & 50 deletions src/text_atlas.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
use crate::{
text_render::ContentType, CacheKey, FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus,
Params, Resolution, SwashCache,
Params, SwashCache,
};
use etagere::{size2, Allocation, BucketedAtlasAllocator};
use lru::LruCache;
use std::{borrow::Cow, collections::HashSet, mem::size_of, num::NonZeroU64, sync::Arc};
use wgpu::{
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutEntry,
BindingResource, BindingType, BlendState, Buffer, BufferBindingType, BufferDescriptor,
BufferUsages, ColorTargetState, ColorWrites, DepthStencilState, Device, Extent3d, FilterMode,
FragmentState, ImageCopyTexture, ImageDataLayout, MultisampleState, Origin3d, PipelineLayout,
PipelineLayoutDescriptor, PrimitiveState, Queue, RenderPipeline, RenderPipelineDescriptor,
Sampler, SamplerBindingType, SamplerDescriptor, ShaderModule, ShaderModuleDescriptor,
ShaderSource, ShaderStages, Texture, TextureAspect, TextureDescriptor, TextureDimension,
TextureFormat, TextureSampleType, TextureUsages, TextureView, TextureViewDescriptor,
TextureViewDimension, VertexFormat, VertexState,
BindingResource, BindingType, BlendState, BufferBindingType, ColorTargetState, ColorWrites,
DepthStencilState, Device, Extent3d, FilterMode, FragmentState, ImageCopyTexture,
ImageDataLayout, MultisampleState, Origin3d, PipelineLayout, PipelineLayoutDescriptor,
PrimitiveState, Queue, RenderPipeline, RenderPipelineDescriptor, Sampler, SamplerBindingType,
SamplerDescriptor, ShaderModule, ShaderModuleDescriptor, ShaderSource, ShaderStages, Texture,
TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType,
TextureUsages, TextureView, TextureViewDescriptor, TextureViewDimension, VertexFormat,
VertexState,
};

#[allow(dead_code)]
Expand Down Expand Up @@ -255,15 +255,14 @@ pub enum ColorMode {

/// An atlas containing a cache of rasterized glyphs that can be rendered.
pub struct TextAtlas {
pub(crate) params: Params,
pub(crate) params_buffer: Buffer,
pub(crate) cached_pipelines: Vec<(
MultisampleState,
Option<DepthStencilState>,
Arc<RenderPipeline>,
)>,
pub(crate) bind_group: Arc<BindGroup>,
pub(crate) bind_group_layout: BindGroupLayout,
pub(crate) text_render_bind_group_layout: BindGroupLayout,
pub(crate) sampler: Sampler,
pub(crate) color_atlas: InnerAtlas,
pub(crate) mask_atlas: InnerAtlas,
Expand Down Expand Up @@ -340,9 +339,9 @@ impl TextAtlas {
],
}];

let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
BindGroupLayoutEntry {
let text_render_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX,
ty: BindingType::Buffer {
Expand All @@ -351,9 +350,14 @@ impl TextAtlas {
min_binding_size: NonZeroU64::new(size_of::<Params>() as u64),
},
count: None,
},
}],
label: Some("glyphon text render bind group layout"),
});

let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
BindGroupLayoutEntry {
binding: 1,
binding: 0,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
Expand All @@ -363,7 +367,7 @@ impl TextAtlas {
count: None,
},
BindGroupLayoutEntry {
binding: 2,
binding: 1,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
Expand All @@ -373,28 +377,13 @@ impl TextAtlas {
count: None,
},
BindGroupLayoutEntry {
binding: 3,
binding: 2,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(SamplerBindingType::Filtering),
count: None,
},
],
label: Some("glyphon bind group layout"),
});

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,
label: Some("glyphon text atlas bind group layout"),
});

let color_atlas = InnerAtlas::new(
Expand All @@ -414,34 +403,29 @@ impl TextAtlas {
entries: &[
BindGroupEntry {
binding: 0,
resource: params_buffer.as_entire_binding(),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::TextureView(&color_atlas.texture_view),
},
BindGroupEntry {
binding: 2,
binding: 1,
resource: BindingResource::TextureView(&mask_atlas.texture_view),
},
BindGroupEntry {
binding: 3,
binding: 2,
resource: BindingResource::Sampler(&sampler),
},
],
label: Some("glyphon bind group"),
label: Some("glyphon text atlas bind group"),
}));

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

Self {
params,
params_buffer,
cached_pipelines: Vec::new(),
text_render_bind_group_layout,
bind_group,
bind_group_layout,
sampler,
Expand Down Expand Up @@ -540,18 +524,14 @@ impl TextAtlas {
entries: &[
BindGroupEntry {
binding: 0,
resource: self.params_buffer.as_entire_binding(),
},
BindGroupEntry {
binding: 1,
resource: BindingResource::TextureView(&self.color_atlas.texture_view),
},
BindGroupEntry {
binding: 2,
binding: 1,
resource: BindingResource::TextureView(&self.mask_atlas.texture_view),
},
BindGroupEntry {
binding: 3,
binding: 2,
resource: BindingResource::Sampler(&self.sampler),
},
],
Expand Down
63 changes: 39 additions & 24 deletions src/text_render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@ use crate::{
};
use std::{iter, mem::size_of, slice, sync::Arc};
use wgpu::{
Buffer, BufferDescriptor, BufferUsages, DepthStencilState, Device, Extent3d, ImageCopyTexture,
ImageDataLayout, IndexFormat, MultisampleState, Origin3d, Queue, RenderPass, RenderPipeline,
TextureAspect, COPY_BUFFER_ALIGNMENT,
BindGroupDescriptor, BindGroupEntry, Buffer, BufferDescriptor, BufferUsages, DepthStencilState,
Device, Extent3d, ImageCopyTexture, ImageDataLayout, IndexFormat, MultisampleState, Origin3d,
Queue, RenderPass, RenderPipeline, TextureAspect, COPY_BUFFER_ALIGNMENT,
};

/// A text renderer that uses cached glyphs to render text into an existing render pass.
pub struct TextRenderer {
params: Params,
params_buffer: Buffer,
vertex_buffer: Buffer,
vertex_buffer_size: u64,
index_buffer: Buffer,
index_buffer_size: u64,
vertices_to_render: u32,
screen_resolution: Resolution,
pipeline: Arc<RenderPipeline>,
bind_group: wgpu::BindGroup,
}

impl TextRenderer {
Expand Down Expand Up @@ -46,17 +48,40 @@ impl TextRenderer {

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

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 = device.create_bind_group(&BindGroupDescriptor {
layout: &atlas.text_render_bind_group_layout,
entries: &[BindGroupEntry {
binding: 0,
resource: params_buffer.as_entire_binding(),
}],
label: Some("glyphon text render bind group"),
});

Self {
params,
params_buffer,
vertex_buffer,
vertex_buffer_size,
index_buffer,
index_buffer_size,
vertices_to_render: 0,
screen_resolution: Resolution {
width: 0,
height: 0,
},
pipeline,
bind_group,
}
}

Expand All @@ -72,15 +97,11 @@ impl TextRenderer {
cache: &mut SwashCache,
mut metadata_to_depth: impl FnMut(usize) -> f32,
) -> Result<(), PrepareError> {
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 @@ -394,15 +415,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(0, &self.bind_group, &[]);
pass.set_bind_group(1, &atlas.bind_group, &[]);
pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
pass.set_index_buffer(self.index_buffer.slice(..), IndexFormat::Uint32);
pass.draw_indexed(0..self.vertices_to_render, 0, 0..1);
Expand Down
Loading