Support multi viewport rendering with reusable text atlas (#88)
This commit is contained in:
parent
4700e54f16
commit
f95e66f612
3 changed files with 78 additions and 83 deletions
|
@ -23,13 +23,13 @@ struct Params {
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var<uniform> params: Params;
|
var<uniform> params: Params;
|
||||||
|
|
||||||
@group(0) @binding(1)
|
@group(1) @binding(0)
|
||||||
var color_atlas_texture: texture_2d<f32>;
|
var color_atlas_texture: texture_2d<f32>;
|
||||||
|
|
||||||
@group(0) @binding(2)
|
@group(1) @binding(1)
|
||||||
var mask_atlas_texture: texture_2d<f32>;
|
var mask_atlas_texture: texture_2d<f32>;
|
||||||
|
|
||||||
@group(0) @binding(3)
|
@group(1) @binding(2)
|
||||||
var atlas_sampler: sampler;
|
var atlas_sampler: sampler;
|
||||||
|
|
||||||
fn srgb_to_linear(c: f32) -> f32 {
|
fn srgb_to_linear(c: f32) -> f32 {
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
text_render::ContentType, CacheKey, FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus,
|
text_render::ContentType, CacheKey, FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus,
|
||||||
Params, Resolution, SwashCache,
|
Params, SwashCache,
|
||||||
};
|
};
|
||||||
use etagere::{size2, Allocation, BucketedAtlasAllocator};
|
use etagere::{size2, Allocation, BucketedAtlasAllocator};
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
use std::{borrow::Cow, collections::HashSet, mem::size_of, num::NonZeroU64, sync::Arc};
|
use std::{borrow::Cow, collections::HashSet, mem::size_of, num::NonZeroU64, sync::Arc};
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutEntry,
|
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutEntry,
|
||||||
BindingResource, BindingType, BlendState, Buffer, BufferBindingType, BufferDescriptor,
|
BindingResource, BindingType, BlendState, BufferBindingType, ColorTargetState, ColorWrites,
|
||||||
BufferUsages, ColorTargetState, ColorWrites, DepthStencilState, Device, Extent3d, FilterMode,
|
DepthStencilState, Device, Extent3d, FilterMode, FragmentState, ImageCopyTexture,
|
||||||
FragmentState, ImageCopyTexture, ImageDataLayout, MultisampleState, Origin3d, PipelineLayout,
|
ImageDataLayout, MultisampleState, Origin3d, PipelineLayout, PipelineLayoutDescriptor,
|
||||||
PipelineLayoutDescriptor, PrimitiveState, Queue, RenderPipeline, RenderPipelineDescriptor,
|
PrimitiveState, Queue, RenderPipeline, RenderPipelineDescriptor, Sampler, SamplerBindingType,
|
||||||
Sampler, SamplerBindingType, SamplerDescriptor, ShaderModule, ShaderModuleDescriptor,
|
SamplerDescriptor, ShaderModule, ShaderModuleDescriptor, ShaderSource, ShaderStages, Texture,
|
||||||
ShaderSource, ShaderStages, Texture, TextureAspect, TextureDescriptor, TextureDimension,
|
TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType,
|
||||||
TextureFormat, TextureSampleType, TextureUsages, TextureView, TextureViewDescriptor,
|
TextureUsages, TextureView, TextureViewDescriptor, TextureViewDimension, VertexFormat,
|
||||||
TextureViewDimension, VertexFormat, VertexState,
|
VertexState,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -255,8 +255,6 @@ pub enum ColorMode {
|
||||||
|
|
||||||
/// An atlas containing a cache of rasterized glyphs that can be rendered.
|
/// An atlas containing a cache of rasterized glyphs that can be rendered.
|
||||||
pub struct TextAtlas {
|
pub struct TextAtlas {
|
||||||
pub(crate) params: Params,
|
|
||||||
pub(crate) params_buffer: Buffer,
|
|
||||||
pub(crate) cached_pipelines: Vec<(
|
pub(crate) cached_pipelines: Vec<(
|
||||||
MultisampleState,
|
MultisampleState,
|
||||||
Option<DepthStencilState>,
|
Option<DepthStencilState>,
|
||||||
|
@ -264,6 +262,7 @@ pub struct TextAtlas {
|
||||||
)>,
|
)>,
|
||||||
pub(crate) bind_group: Arc<BindGroup>,
|
pub(crate) bind_group: Arc<BindGroup>,
|
||||||
pub(crate) bind_group_layout: BindGroupLayout,
|
pub(crate) bind_group_layout: BindGroupLayout,
|
||||||
|
pub(crate) text_render_bind_group_layout: BindGroupLayout,
|
||||||
pub(crate) sampler: Sampler,
|
pub(crate) sampler: Sampler,
|
||||||
pub(crate) color_atlas: InnerAtlas,
|
pub(crate) color_atlas: InnerAtlas,
|
||||||
pub(crate) mask_atlas: InnerAtlas,
|
pub(crate) mask_atlas: InnerAtlas,
|
||||||
|
@ -340,9 +339,9 @@ impl TextAtlas {
|
||||||
],
|
],
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
let text_render_bind_group_layout =
|
||||||
entries: &[
|
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
BindGroupLayoutEntry {
|
entries: &[BindGroupLayoutEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
visibility: ShaderStages::VERTEX,
|
visibility: ShaderStages::VERTEX,
|
||||||
ty: BindingType::Buffer {
|
ty: BindingType::Buffer {
|
||||||
|
@ -351,6 +350,21 @@ impl TextAtlas {
|
||||||
min_binding_size: NonZeroU64::new(size_of::<Params>() as u64),
|
min_binding_size: NonZeroU64::new(size_of::<Params>() as u64),
|
||||||
},
|
},
|
||||||
count: None,
|
count: None,
|
||||||
|
}],
|
||||||
|
label: Some("glyphon text render bind group layout"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group_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 {
|
BindGroupLayoutEntry {
|
||||||
binding: 1,
|
binding: 1,
|
||||||
|
@ -364,37 +378,12 @@ impl TextAtlas {
|
||||||
},
|
},
|
||||||
BindGroupLayoutEntry {
|
BindGroupLayoutEntry {
|
||||||
binding: 2,
|
binding: 2,
|
||||||
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
|
|
||||||
ty: BindingType::Texture {
|
|
||||||
multisampled: false,
|
|
||||||
view_dimension: TextureViewDimension::D2,
|
|
||||||
sample_type: TextureSampleType::Float { filterable: true },
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
BindGroupLayoutEntry {
|
|
||||||
binding: 3,
|
|
||||||
visibility: ShaderStages::FRAGMENT,
|
visibility: ShaderStages::FRAGMENT,
|
||||||
ty: BindingType::Sampler(SamplerBindingType::Filtering),
|
ty: BindingType::Sampler(SamplerBindingType::Filtering),
|
||||||
count: None,
|
count: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
label: Some("glyphon bind group layout"),
|
label: Some("glyphon text atlas 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,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let color_atlas = InnerAtlas::new(
|
let color_atlas = InnerAtlas::new(
|
||||||
|
@ -414,34 +403,29 @@ impl TextAtlas {
|
||||||
entries: &[
|
entries: &[
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
resource: params_buffer.as_entire_binding(),
|
|
||||||
},
|
|
||||||
BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: BindingResource::TextureView(&color_atlas.texture_view),
|
resource: BindingResource::TextureView(&color_atlas.texture_view),
|
||||||
},
|
},
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 2,
|
binding: 1,
|
||||||
resource: BindingResource::TextureView(&mask_atlas.texture_view),
|
resource: BindingResource::TextureView(&mask_atlas.texture_view),
|
||||||
},
|
},
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 3,
|
binding: 2,
|
||||||
resource: BindingResource::Sampler(&sampler),
|
resource: BindingResource::Sampler(&sampler),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
label: Some("glyphon bind group"),
|
label: Some("glyphon text atlas bind group"),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
|
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
bind_group_layouts: &[&bind_group_layout],
|
bind_group_layouts: &[&text_render_bind_group_layout, &bind_group_layout],
|
||||||
push_constant_ranges: &[],
|
push_constant_ranges: &[],
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
params,
|
|
||||||
params_buffer,
|
|
||||||
cached_pipelines: Vec::new(),
|
cached_pipelines: Vec::new(),
|
||||||
|
text_render_bind_group_layout,
|
||||||
bind_group,
|
bind_group,
|
||||||
bind_group_layout,
|
bind_group_layout,
|
||||||
sampler,
|
sampler,
|
||||||
|
@ -540,18 +524,14 @@ impl TextAtlas {
|
||||||
entries: &[
|
entries: &[
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
resource: self.params_buffer.as_entire_binding(),
|
|
||||||
},
|
|
||||||
BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: BindingResource::TextureView(&self.color_atlas.texture_view),
|
resource: BindingResource::TextureView(&self.color_atlas.texture_view),
|
||||||
},
|
},
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 2,
|
binding: 1,
|
||||||
resource: BindingResource::TextureView(&self.mask_atlas.texture_view),
|
resource: BindingResource::TextureView(&self.mask_atlas.texture_view),
|
||||||
},
|
},
|
||||||
BindGroupEntry {
|
BindGroupEntry {
|
||||||
binding: 3,
|
binding: 2,
|
||||||
resource: BindingResource::Sampler(&self.sampler),
|
resource: BindingResource::Sampler(&self.sampler),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -4,20 +4,22 @@ use crate::{
|
||||||
};
|
};
|
||||||
use std::{iter, mem::size_of, slice, sync::Arc};
|
use std::{iter, mem::size_of, slice, sync::Arc};
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
Buffer, BufferDescriptor, BufferUsages, DepthStencilState, Device, Extent3d, ImageCopyTexture,
|
BindGroupDescriptor, BindGroupEntry, Buffer, BufferDescriptor, BufferUsages, DepthStencilState,
|
||||||
ImageDataLayout, IndexFormat, MultisampleState, Origin3d, Queue, RenderPass, RenderPipeline,
|
Device, Extent3d, ImageCopyTexture, ImageDataLayout, IndexFormat, MultisampleState, Origin3d,
|
||||||
TextureAspect, COPY_BUFFER_ALIGNMENT,
|
Queue, RenderPass, RenderPipeline, TextureAspect, COPY_BUFFER_ALIGNMENT,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A text renderer that uses cached glyphs to render text into an existing render pass.
|
/// A text renderer that uses cached glyphs to render text into an existing render pass.
|
||||||
pub struct TextRenderer {
|
pub struct TextRenderer {
|
||||||
|
params: Params,
|
||||||
|
params_buffer: Buffer,
|
||||||
vertex_buffer: Buffer,
|
vertex_buffer: Buffer,
|
||||||
vertex_buffer_size: u64,
|
vertex_buffer_size: u64,
|
||||||
index_buffer: Buffer,
|
index_buffer: Buffer,
|
||||||
index_buffer_size: u64,
|
index_buffer_size: u64,
|
||||||
vertices_to_render: u32,
|
vertices_to_render: u32,
|
||||||
screen_resolution: Resolution,
|
|
||||||
pipeline: Arc<RenderPipeline>,
|
pipeline: Arc<RenderPipeline>,
|
||||||
|
bind_group: wgpu::BindGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextRenderer {
|
impl TextRenderer {
|
||||||
|
@ -46,17 +48,40 @@ impl TextRenderer {
|
||||||
|
|
||||||
let pipeline = atlas.get_or_create_pipeline(device, multisample, depth_stencil);
|
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 {
|
Self {
|
||||||
|
params,
|
||||||
|
params_buffer,
|
||||||
vertex_buffer,
|
vertex_buffer,
|
||||||
vertex_buffer_size,
|
vertex_buffer_size,
|
||||||
index_buffer,
|
index_buffer,
|
||||||
index_buffer_size,
|
index_buffer_size,
|
||||||
vertices_to_render: 0,
|
vertices_to_render: 0,
|
||||||
screen_resolution: Resolution {
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
},
|
|
||||||
pipeline,
|
pipeline,
|
||||||
|
bind_group,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,15 +97,11 @@ impl TextRenderer {
|
||||||
cache: &mut SwashCache,
|
cache: &mut SwashCache,
|
||||||
mut metadata_to_depth: impl FnMut(usize) -> f32,
|
mut metadata_to_depth: impl FnMut(usize) -> f32,
|
||||||
) -> Result<(), PrepareError> {
|
) -> Result<(), PrepareError> {
|
||||||
self.screen_resolution = screen_resolution;
|
if self.params.screen_resolution != screen_resolution {
|
||||||
|
self.params.screen_resolution = screen_resolution;
|
||||||
let atlas_current_resolution = { atlas.params.screen_resolution };
|
queue.write_buffer(&self.params_buffer, 0, unsafe {
|
||||||
|
|
||||||
if screen_resolution != atlas_current_resolution {
|
|
||||||
atlas.params.screen_resolution = screen_resolution;
|
|
||||||
queue.write_buffer(&atlas.params_buffer, 0, unsafe {
|
|
||||||
slice::from_raw_parts(
|
slice::from_raw_parts(
|
||||||
&atlas.params as *const Params as *const u8,
|
&self.params as *const Params as *const u8,
|
||||||
size_of::<Params>(),
|
size_of::<Params>(),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
@ -394,15 +415,9 @@ impl TextRenderer {
|
||||||
return Ok(());
|
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_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_vertex_buffer(0, self.vertex_buffer.slice(..));
|
||||||
pass.set_index_buffer(self.index_buffer.slice(..), IndexFormat::Uint32);
|
pass.set_index_buffer(self.index_buffer.slice(..), IndexFormat::Uint32);
|
||||||
pass.draw_indexed(0..self.vertices_to_render, 0, 0..1);
|
pass.draw_indexed(0..self.vertices_to_render, 0, 0..1);
|
||||||
|
|
Loading…
Reference in a new issue