diff --git a/src/shader.wgsl b/src/shader.wgsl index d7f7d86..3d1ba4f 100644 --- a/src/shader.wgsl +++ b/src/shader.wgsl @@ -23,13 +23,13 @@ struct Params { @group(0) @binding(0) var params: Params; -@group(0) @binding(1) +@group(1) @binding(0) var color_atlas_texture: texture_2d; -@group(0) @binding(2) +@group(1) @binding(1) var mask_atlas_texture: texture_2d; -@group(0) @binding(3) +@group(1) @binding(2) var atlas_sampler: sampler; fn srgb_to_linear(c: f32) -> f32 { diff --git a/src/text_atlas.rs b/src/text_atlas.rs index 4110554..44e017c 100644 --- a/src/text_atlas.rs +++ b/src/text_atlas.rs @@ -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)] @@ -255,8 +255,6 @@ 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, @@ -264,6 +262,7 @@ pub struct TextAtlas { )>, pub(crate) bind_group: Arc, 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, @@ -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 { @@ -351,6 +350,21 @@ impl TextAtlas { min_binding_size: NonZeroU64::new(size_of::() 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: 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, @@ -364,37 +378,12 @@ impl TextAtlas { }, BindGroupLayoutEntry { 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, 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::() 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( @@ -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, @@ -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), }, ], diff --git a/src/text_render.rs b/src/text_render.rs index 9818bd1..81dd5f5 100644 --- a/src/text_render.rs +++ b/src/text_render.rs @@ -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, + bind_group: wgpu::BindGroup, } impl TextRenderer { @@ -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::() 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, } } @@ -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::(), ) }); @@ -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);