Compare commits
No commits in common. "da3b02cf8af74058f7a07e68cc21980afaec9e54" and "40861cd9339fb56359b1f9551d06da37da1f265a" have entirely different histories.
da3b02cf8a
...
40861cd933
11 changed files with 221 additions and 578 deletions
1
.envrc
1
.envrc
|
@ -1 +0,0 @@
|
||||||
use flake .
|
|
|
@ -2,14 +2,14 @@
|
||||||
name = "cryoglyph"
|
name = "cryoglyph"
|
||||||
description = "Fast, simple 2D text rendering for wgpu"
|
description = "Fast, simple 2D text rendering for wgpu"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2021"
|
||||||
repository = "https://github.com/iced-rs/cryoglyph"
|
repository = "https://github.com/iced-rs/cryoglyph"
|
||||||
license = "MIT OR Apache-2.0 OR Zlib"
|
license = "MIT OR Apache-2.0 OR Zlib"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wgpu = { version = "24", default-features = false, features = ["wgsl"] }
|
wgpu = { version = "24", default-features = false, features = ["wgsl"] }
|
||||||
etagere = "0.2.10"
|
etagere = "0.2.10"
|
||||||
cosmic-text = "0.14"
|
cosmic-text = "0.12"
|
||||||
lru = { version = "0.12.1", default-features = false }
|
lru = { version = "0.12.1", default-features = false }
|
||||||
rustc-hash = "2.0"
|
rustc-hash = "2.0"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use cosmic_text::{Attrs, Buffer, Color, Family, FontSystem, Metrics, Shaping, SwashCache};
|
use cosmic_text::{Attrs, Buffer, Color, Family, FontSystem, Metrics, Shaping, SwashCache};
|
||||||
use criterion::{Criterion, criterion_group, criterion_main};
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
use cryoglyph::{
|
use cryoglyph::{
|
||||||
Cache, ColorMode, Resolution, TextArea, TextAtlas, TextBounds, TextRenderer, Viewport, Weight,
|
Cache, ColorMode, Resolution, TextArea, TextAtlas, TextBounds, TextRenderer, Viewport, Weight,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use cryoglyph::{
|
use cryoglyph::{
|
||||||
cosmic_text::LetterSpacing, Attrs, AttrsOwned, Buffer, Cache, Color, Family, FontSystem,
|
Attrs, Buffer, Cache, Color, Family, FontSystem, Metrics, Resolution, Shaping, SwashCache,
|
||||||
Metrics, Resolution, Shaping, SwashCache, TextArea, TextAtlas, TextBounds, TextRenderer,
|
TextArea, TextAtlas, TextBounds, TextRenderer, Viewport,
|
||||||
Viewport,
|
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
|
@ -88,7 +87,7 @@ impl WindowState {
|
||||||
Some(physical_width),
|
Some(physical_width),
|
||||||
Some(physical_height),
|
Some(physical_height),
|
||||||
);
|
);
|
||||||
text_buffer.set_text(&mut font_system, "Hello world! 👋\nThis is rendered with 🦅 glyphon 🦁\nThe text below should be partially clipped.\na b c d e f g h i j k l m n o p q r s t u v w x y z", &Attrs::new().family(Family::SansSerif), Shaping::Advanced);
|
text_buffer.set_text(&mut font_system, "Hello world! 👋\nThis is rendered with 🦅 glyphon 🦁\nThe text below should be partially clipped.\na b c d e f g h i j k l m n o p q r s t u v w x y z", Attrs::new().family(Family::SansSerif), Shaping::Advanced);
|
||||||
text_buffer.shape_until_scroll(&mut font_system, false);
|
text_buffer.shape_until_scroll(&mut font_system, false);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -170,21 +169,6 @@ impl winit::application::ApplicationHandler for Application {
|
||||||
|
|
||||||
let mut encoder =
|
let mut encoder =
|
||||||
device.create_command_encoder(&CommandEncoderDescriptor { label: None });
|
device.create_command_encoder(&CommandEncoderDescriptor { label: None });
|
||||||
let text_area = TextArea {
|
|
||||||
buffer: text_buffer,
|
|
||||||
left: 10.0,
|
|
||||||
top: 10.0,
|
|
||||||
scale: 3.5,
|
|
||||||
bounds: TextBounds {
|
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
right: 600,
|
|
||||||
bottom: 160,
|
|
||||||
},
|
|
||||||
default_color: Color::rgb(255, 255, 255),
|
|
||||||
stroke_size: 5.5,
|
|
||||||
stroke_color: Color::rgb(50, 50, 255),
|
|
||||||
};
|
|
||||||
|
|
||||||
text_renderer
|
text_renderer
|
||||||
.prepare(
|
.prepare(
|
||||||
|
@ -194,7 +178,19 @@ impl winit::application::ApplicationHandler for Application {
|
||||||
font_system,
|
font_system,
|
||||||
atlas,
|
atlas,
|
||||||
viewport,
|
viewport,
|
||||||
[text_area],
|
[TextArea {
|
||||||
|
buffer: text_buffer,
|
||||||
|
left: 10.0,
|
||||||
|
top: 10.0,
|
||||||
|
scale: 1.0,
|
||||||
|
bounds: TextBounds {
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
right: 600,
|
||||||
|
bottom: 160,
|
||||||
|
},
|
||||||
|
default_color: Color::rgb(255, 255, 255),
|
||||||
|
}],
|
||||||
swash_cache,
|
swash_cache,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
149
flake.lock
generated
149
flake.lock
generated
|
@ -1,149 +0,0 @@
|
||||||
{
|
|
||||||
"nodes": {
|
|
||||||
"fenix": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": "nixpkgs",
|
|
||||||
"rust-analyzer-src": "rust-analyzer-src"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1746081462,
|
|
||||||
"narHash": "sha256-WmJBaktb33WwqNn5BwdJghAoiBnvnPhgHSBksTrF5K8=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "fenix",
|
|
||||||
"rev": "e3be528e4f03538852ba49b413ec4ac843edeb60",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "fenix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"flake-utils": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1731533236,
|
|
||||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"naersk": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": "nixpkgs_2"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1745925850,
|
|
||||||
"narHash": "sha256-cyAAMal0aPrlb1NgzMxZqeN1mAJ2pJseDhm2m6Um8T0=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "naersk",
|
|
||||||
"rev": "38bc60bbc157ae266d4a0c96671c6c742ee17a5f",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "naersk",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1745930157,
|
|
||||||
"narHash": "sha256-y3h3NLnzRSiUkYpnfvnS669zWZLoqqI6NprtLQ+5dck=",
|
|
||||||
"owner": "nixos",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "46e634be05ce9dc6d4db8e664515ba10b78151ae",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nixos",
|
|
||||||
"ref": "nixos-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_2": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1746061036,
|
|
||||||
"narHash": "sha256-OxYwCGJf9VJ2KnUO+w/hVJVTjOgscdDg/lPv8Eus07Y=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "3afd19146cac33ed242fc0fc87481c67c758a59e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixpkgs-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_3": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1745930157,
|
|
||||||
"narHash": "sha256-y3h3NLnzRSiUkYpnfvnS669zWZLoqqI6NprtLQ+5dck=",
|
|
||||||
"owner": "nixos",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "46e634be05ce9dc6d4db8e664515ba10b78151ae",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nixos",
|
|
||||||
"ref": "nixos-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"fenix": "fenix",
|
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"naersk": "naersk",
|
|
||||||
"nixpkgs": "nixpkgs_3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"rust-analyzer-src": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1746024678,
|
|
||||||
"narHash": "sha256-Q5J7+RoTPH4zPeu0Ka7iSXtXty228zKjS0Ed4R+ohpA=",
|
|
||||||
"owner": "rust-lang",
|
|
||||||
"repo": "rust-analyzer",
|
|
||||||
"rev": "5d66d45005fef79751294419ab9a9fa304dfdf5c",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "rust-lang",
|
|
||||||
"ref": "nightly",
|
|
||||||
"repo": "rust-analyzer",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "root",
|
|
||||||
"version": 7
|
|
||||||
}
|
|
84
flake.nix
84
flake.nix
|
@ -1,84 +0,0 @@
|
||||||
{
|
|
||||||
description = "A fast, simple 2D text renderer for wgpu";
|
|
||||||
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
|
||||||
naersk.url = "github:nix-community/naersk";
|
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
|
||||||
fenix.url = "github:nix-community/fenix";
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = inputs: with inputs;
|
|
||||||
flake-utils.lib.eachDefaultSystem
|
|
||||||
(system:
|
|
||||||
let
|
|
||||||
pkgs = import nixpkgs {
|
|
||||||
inherit system;
|
|
||||||
overlays = [fenix.overlays.default];
|
|
||||||
# overlays = [cargo2nix.overlays.default];
|
|
||||||
};
|
|
||||||
naersk' = pkgs.callPackage naersk {};
|
|
||||||
nbi = with pkgs; [
|
|
||||||
# Rust tools
|
|
||||||
alejandra
|
|
||||||
(pkgs.fenix.stable.withComponents [
|
|
||||||
"cargo"
|
|
||||||
"clippy"
|
|
||||||
"rust-src"
|
|
||||||
"rustc"
|
|
||||||
"rustfmt"
|
|
||||||
])
|
|
||||||
rust-analyzer
|
|
||||||
vulkan-loader
|
|
||||||
wayland
|
|
||||||
wayland-protocols
|
|
||||||
libxkbcommon
|
|
||||||
pkg-config
|
|
||||||
];
|
|
||||||
|
|
||||||
bi = with pkgs; [
|
|
||||||
gcc
|
|
||||||
stdenv
|
|
||||||
gnumake
|
|
||||||
gdb
|
|
||||||
cmake
|
|
||||||
makeWrapper
|
|
||||||
vulkan-headers
|
|
||||||
vulkan-loader
|
|
||||||
vulkan-tools
|
|
||||||
harfbuzz
|
|
||||||
libGL
|
|
||||||
cargo-flamegraph
|
|
||||||
fontconfig
|
|
||||||
just
|
|
||||||
cargo-watch
|
|
||||||
];
|
|
||||||
in rec
|
|
||||||
{
|
|
||||||
devShell = pkgs.mkShell.override {
|
|
||||||
stdenv = pkgs.stdenvAdapters.useMoldLinker pkgs.clangStdenv;
|
|
||||||
} {
|
|
||||||
nativeBuildInputs = nbi;
|
|
||||||
buildInputs = bi;
|
|
||||||
LD_LIBRARY_PATH = "$LD_LIBRARY_PATH:${
|
|
||||||
with pkgs;
|
|
||||||
pkgs.lib.makeLibraryPath [
|
|
||||||
pkgs.vulkan-loader
|
|
||||||
pkgs.wayland
|
|
||||||
pkgs.wayland-protocols
|
|
||||||
pkgs.libxkbcommon
|
|
||||||
]
|
|
||||||
}";
|
|
||||||
DATABASE_URL = "sqlite:///home/chris/.local/share/lumina/library-db.sqlite3";
|
|
||||||
};
|
|
||||||
defaultPackage = naersk'.buildPackage {
|
|
||||||
src = ./.;
|
|
||||||
};
|
|
||||||
packages = {
|
|
||||||
default = naersk'.buildPackage {
|
|
||||||
src = ./.;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
29
src/cache.rs
29
src/cache.rs
|
@ -1,4 +1,5 @@
|
||||||
use crate::{GlyphToRender, Params};
|
use crate::{GlyphToRender, Params};
|
||||||
|
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutEntry,
|
BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutEntry,
|
||||||
BindingResource, BindingType, BlendState, Buffer, BufferBindingType, ColorTargetState,
|
BindingResource, BindingType, BlendState, Buffer, BufferBindingType, ColorTargetState,
|
||||||
|
@ -13,7 +14,7 @@ use std::borrow::Cow;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Cache(Arc<Inner>);
|
pub struct Cache(Arc<Inner>);
|
||||||
|
@ -26,12 +27,12 @@ struct Inner {
|
||||||
atlas_layout: BindGroupLayout,
|
atlas_layout: BindGroupLayout,
|
||||||
uniforms_layout: BindGroupLayout,
|
uniforms_layout: BindGroupLayout,
|
||||||
pipeline_layout: PipelineLayout,
|
pipeline_layout: PipelineLayout,
|
||||||
cache: Mutex<
|
cache: RwLock<
|
||||||
Vec<(
|
Vec<(
|
||||||
TextureFormat,
|
TextureFormat,
|
||||||
MultisampleState,
|
MultisampleState,
|
||||||
Option<DepthStencilState>,
|
Option<DepthStencilState>,
|
||||||
RenderPipeline,
|
Arc<RenderPipeline>,
|
||||||
)>,
|
)>,
|
||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
|
@ -87,16 +88,6 @@ impl Cache {
|
||||||
offset: mem::size_of::<u32>() as u64 * 6,
|
offset: mem::size_of::<u32>() as u64 * 6,
|
||||||
shader_location: 5,
|
shader_location: 5,
|
||||||
},
|
},
|
||||||
wgpu::VertexAttribute {
|
|
||||||
format: VertexFormat::Uint32,
|
|
||||||
offset: mem::size_of::<u32>() as u64 * 7,
|
|
||||||
shader_location: 6,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
format: VertexFormat::Float32,
|
|
||||||
offset: mem::size_of::<u32>() as u64 * 8,
|
|
||||||
shader_location: 7,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -159,7 +150,7 @@ impl Cache {
|
||||||
uniforms_layout,
|
uniforms_layout,
|
||||||
atlas_layout,
|
atlas_layout,
|
||||||
pipeline_layout,
|
pipeline_layout,
|
||||||
cache: Mutex::new(Vec::new()),
|
cache: RwLock::new(Vec::new()),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +197,7 @@ impl Cache {
|
||||||
format: TextureFormat,
|
format: TextureFormat,
|
||||||
multisample: MultisampleState,
|
multisample: MultisampleState,
|
||||||
depth_stencil: Option<DepthStencilState>,
|
depth_stencil: Option<DepthStencilState>,
|
||||||
) -> RenderPipeline {
|
) -> Arc<RenderPipeline> {
|
||||||
let Inner {
|
let Inner {
|
||||||
cache,
|
cache,
|
||||||
pipeline_layout,
|
pipeline_layout,
|
||||||
|
@ -215,14 +206,14 @@ impl Cache {
|
||||||
..
|
..
|
||||||
} = self.0.deref();
|
} = self.0.deref();
|
||||||
|
|
||||||
let mut cache = cache.lock().expect("Write pipeline cache");
|
let mut cache = cache.write().expect("Write pipeline cache");
|
||||||
|
|
||||||
cache
|
cache
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(fmt, ms, ds, _)| fmt == &format && ms == &multisample && ds == &depth_stencil)
|
.find(|(fmt, ms, ds, _)| fmt == &format && ms == &multisample && ds == &depth_stencil)
|
||||||
.map(|(_, _, _, p)| p.clone())
|
.map(|(_, _, _, p)| Arc::clone(p))
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
|
let pipeline = Arc::new(device.create_render_pipeline(&RenderPipelineDescriptor {
|
||||||
label: Some("glyphon pipeline"),
|
label: Some("glyphon pipeline"),
|
||||||
layout: Some(pipeline_layout),
|
layout: Some(pipeline_layout),
|
||||||
vertex: VertexState {
|
vertex: VertexState {
|
||||||
|
@ -249,7 +240,7 @@ impl Cache {
|
||||||
multisample,
|
multisample,
|
||||||
multiview: None,
|
multiview: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
});
|
}));
|
||||||
|
|
||||||
cache.push((format, multisample, depth_stencil, pipeline.clone()));
|
cache.push((format, multisample, depth_stencil, pipeline.clone()));
|
||||||
|
|
||||||
|
|
|
@ -57,8 +57,6 @@ pub(crate) struct GlyphToRender {
|
||||||
color: u32,
|
color: u32,
|
||||||
content_type_with_srgb: [u16; 2],
|
content_type_with_srgb: [u16; 2],
|
||||||
depth: f32,
|
depth: f32,
|
||||||
stroke_color: u32,
|
|
||||||
stroke_size: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The screen resolution to use when rendering text.
|
/// The screen resolution to use when rendering text.
|
||||||
|
@ -119,8 +117,4 @@ pub struct TextArea<'a> {
|
||||||
pub bounds: TextBounds,
|
pub bounds: TextBounds,
|
||||||
// The default color of the text area.
|
// The default color of the text area.
|
||||||
pub default_color: Color,
|
pub default_color: Color,
|
||||||
/// The stroke (outline) size
|
|
||||||
pub stroke_size: f32,
|
|
||||||
/// The stroke (outline) color
|
|
||||||
pub stroke_color: Color,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@ struct VertexInput {
|
||||||
@location(3) color: u32,
|
@location(3) color: u32,
|
||||||
@location(4) content_type_with_srgb: u32,
|
@location(4) content_type_with_srgb: u32,
|
||||||
@location(5) depth: f32,
|
@location(5) depth: f32,
|
||||||
@location(6) stroke_color: u32,
|
|
||||||
@location(7) stroke_size: f32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VertexOutput {
|
struct VertexOutput {
|
||||||
|
@ -15,8 +13,6 @@ struct VertexOutput {
|
||||||
@location(0) color: vec4<f32>,
|
@location(0) color: vec4<f32>,
|
||||||
@location(1) uv: vec2<f32>,
|
@location(1) uv: vec2<f32>,
|
||||||
@location(2) @interpolate(flat) content_type: u32,
|
@location(2) @interpolate(flat) content_type: u32,
|
||||||
@location(3) stroke_color: vec4<f32>,
|
|
||||||
@location(4) stroke_size: f32,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Params {
|
struct Params {
|
||||||
|
@ -46,13 +42,11 @@ fn srgb_to_linear(c: f32) -> f32 {
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vs_main(in_vert: VertexInput) -> VertexOutput {
|
fn vs_main(in_vert: VertexInput) -> VertexOutput {
|
||||||
var stroke = in_vert.stroke_size;
|
|
||||||
var width = in_vert.dim & 0xffffu;
|
|
||||||
var height = (in_vert.dim & 0xffff0000u) >> 16u;
|
|
||||||
var pos = in_vert.pos;
|
var pos = in_vert.pos;
|
||||||
var uv = vec2<u32>(in_vert.uv & 0xffffu, (in_vert.uv & 0xffff0000u) >> 16u);
|
let width = in_vert.dim & 0xffffu;
|
||||||
|
let height = (in_vert.dim & 0xffff0000u) >> 16u;
|
||||||
let color = in_vert.color;
|
let color = in_vert.color;
|
||||||
let stroke_color = in_vert.stroke_color;
|
var uv = vec2<u32>(in_vert.uv & 0xffffu, (in_vert.uv & 0xffff0000u) >> 16u);
|
||||||
let v = in_vert.vertex_idx;
|
let v = in_vert.vertex_idx;
|
||||||
|
|
||||||
let corner_position = vec2<u32>(
|
let corner_position = vec2<u32>(
|
||||||
|
@ -86,12 +80,6 @@ fn vs_main(in_vert: VertexInput) -> VertexOutput {
|
||||||
f32(color & 0x000000ffu) / 255.0,
|
f32(color & 0x000000ffu) / 255.0,
|
||||||
f32((color & 0xff000000u) >> 24u) / 255.0,
|
f32((color & 0xff000000u) >> 24u) / 255.0,
|
||||||
);
|
);
|
||||||
vert_output.stroke_color = vec4<f32>(
|
|
||||||
f32((stroke_color & 0x00ff0000u) >> 16u) / 255.0,
|
|
||||||
f32((stroke_color & 0x0000ff00u) >> 8u) / 255.0,
|
|
||||||
f32(stroke_color & 0x000000ffu) / 255.0,
|
|
||||||
f32((stroke_color & 0xff000000u) >> 24u) / 255.0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
case 1u: {
|
case 1u: {
|
||||||
vert_output.color = vec4<f32>(
|
vert_output.color = vec4<f32>(
|
||||||
|
@ -100,12 +88,6 @@ fn vs_main(in_vert: VertexInput) -> VertexOutput {
|
||||||
srgb_to_linear(f32(color & 0x000000ffu) / 255.0),
|
srgb_to_linear(f32(color & 0x000000ffu) / 255.0),
|
||||||
f32((color & 0xff000000u) >> 24u) / 255.0,
|
f32((color & 0xff000000u) >> 24u) / 255.0,
|
||||||
);
|
);
|
||||||
vert_output.stroke_color = vec4<f32>(
|
|
||||||
srgb_to_linear(f32((stroke_color & 0x00ff0000u) >> 16u) / 255.0),
|
|
||||||
srgb_to_linear(f32((stroke_color & 0x0000ff00u) >> 8u) / 255.0),
|
|
||||||
srgb_to_linear(f32(stroke_color & 0x000000ffu) / 255.0),
|
|
||||||
f32((stroke_color & 0xff000000u) >> 24u) / 255.0,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
default: {}
|
default: {}
|
||||||
}
|
}
|
||||||
|
@ -126,54 +108,20 @@ fn vs_main(in_vert: VertexInput) -> VertexOutput {
|
||||||
vert_output.content_type = content_type;
|
vert_output.content_type = content_type;
|
||||||
|
|
||||||
vert_output.uv = vec2<f32>(uv) / vec2<f32>(dim);
|
vert_output.uv = vec2<f32>(uv) / vec2<f32>(dim);
|
||||||
vert_output.stroke_size = in_vert.stroke_size;
|
|
||||||
|
|
||||||
return vert_output;
|
return vert_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fs_main(in_frag: VertexOutput) -> @location(0) vec4<f32> {
|
fn fs_main(in_frag: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
let dims = vec2<f32>(textureDimensions(mask_atlas_texture));
|
|
||||||
var bit: u32 = 1;
|
|
||||||
|
|
||||||
let current = textureSample(mask_atlas_texture, atlas_sampler, dims);
|
|
||||||
var closest_dist = distance(vec2<f32>(in_frag.position.xy), vec2<f32>(current.xy));
|
|
||||||
var result = current;
|
|
||||||
|
|
||||||
for (var step: u32 = 0; step <= 32; step++) {
|
|
||||||
bit = 1u << step;
|
|
||||||
for (var dy = -1; dy <= 1; dy++) {
|
|
||||||
for (var dx = -1; dx <= 1; dx++) {
|
|
||||||
if (dx == 0 && dy == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let neighbour_coord = in_frag.position.xy + vec2<f32>(f32(dx * bit), f32(dy * bit));
|
|
||||||
let neighbour = textureSample(mask_atlas_texture, atlas_sampler, neighbour_coord / dims);
|
|
||||||
let dist = distance(vec2<f32>(in_frag.position.xy), vec2<f32>(neighbour.xy));
|
|
||||||
|
|
||||||
if (dist < closest_dist) {
|
|
||||||
closest_dist = dist;
|
|
||||||
result = vec4<f32>(in_frag.stroke_color.rgb, in_frag.stroke_color.a * textureSampleLevel(mask_atlas_texture, atlas_sampler, neighbour.xy, 0.0).x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
switch in_frag.content_type {
|
switch in_frag.content_type {
|
||||||
case 0u: {
|
case 0u: {
|
||||||
return textureSampleLevel(color_atlas_texture, atlas_sampler, in_frag.uv, 0.0);
|
return textureSampleLevel(color_atlas_texture, atlas_sampler, in_frag.uv, 0.0);
|
||||||
}
|
}
|
||||||
case 1u: {
|
case 1u: {
|
||||||
if in_frag.stroke_size > 0.0 {
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
return vec4<f32>(in_frag.color.rgb, in_frag.color.a * textureSampleLevel(mask_atlas_texture, atlas_sampler, in_frag.uv, 0.0).x);
|
return vec4<f32>(in_frag.color.rgb, in_frag.color.a * textureSampleLevel(mask_atlas_texture, atlas_sampler, in_frag.uv, 0.0).x);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
default: {
|
default: {
|
||||||
// return result;
|
|
||||||
return vec4<f32>(0.0);
|
return vec4<f32>(0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
use etagere::{size2, Allocation, BucketedAtlasAllocator};
|
use etagere::{size2, Allocation, BucketedAtlasAllocator};
|
||||||
use lru::LruCache;
|
use lru::LruCache;
|
||||||
use rustc_hash::FxHasher;
|
use rustc_hash::FxHasher;
|
||||||
use std::{collections::HashSet, hash::BuildHasherDefault};
|
use std::{collections::HashSet, hash::BuildHasherDefault, sync::Arc};
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
BindGroup, DepthStencilState, Device, Extent3d, MultisampleState, Origin3d, Queue,
|
BindGroup, DepthStencilState, Device, Extent3d, MultisampleState, Origin3d, Queue,
|
||||||
RenderPipeline, TexelCopyBufferLayout, TexelCopyTextureInfo, Texture, TextureAspect,
|
RenderPipeline, TexelCopyBufferLayout, TexelCopyTextureInfo, Texture, TextureAspect,
|
||||||
|
@ -329,7 +329,7 @@ impl TextAtlas {
|
||||||
device: &Device,
|
device: &Device,
|
||||||
multisample: MultisampleState,
|
multisample: MultisampleState,
|
||||||
depth_stencil: Option<DepthStencilState>,
|
depth_stencil: Option<DepthStencilState>,
|
||||||
) -> RenderPipeline {
|
) -> Arc<RenderPipeline> {
|
||||||
self.cache
|
self.cache
|
||||||
.get_or_create_pipeline(device, self.format, multisample, depth_stencil)
|
.get_or_create_pipeline(device, self.format, multisample, depth_stencil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,7 @@ use crate::{
|
||||||
ColorMode, FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus, PrepareError, RenderError,
|
ColorMode, FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus, PrepareError, RenderError,
|
||||||
SwashCache, SwashContent, TextArea, TextAtlas, Viewport,
|
SwashCache, SwashContent, TextArea, TextAtlas, Viewport,
|
||||||
};
|
};
|
||||||
use cosmic_text::LayoutRun;
|
use std::{num::NonZeroU64, slice, sync::Arc};
|
||||||
use std::{num::NonZeroU64, slice};
|
|
||||||
use wgpu::util::StagingBelt;
|
use wgpu::util::StagingBelt;
|
||||||
use wgpu::{
|
use wgpu::{
|
||||||
Buffer, BufferDescriptor, BufferUsages, CommandEncoder, DepthStencilState, Device, Extent3d,
|
Buffer, BufferDescriptor, BufferUsages, CommandEncoder, DepthStencilState, Device, Extent3d,
|
||||||
|
@ -16,7 +15,7 @@ pub struct TextRenderer {
|
||||||
staging_belt: StagingBelt,
|
staging_belt: StagingBelt,
|
||||||
vertex_buffer: Buffer,
|
vertex_buffer: Buffer,
|
||||||
vertex_buffer_size: u64,
|
vertex_buffer_size: u64,
|
||||||
pipeline: RenderPipeline,
|
pipeline: Arc<RenderPipeline>,
|
||||||
glyph_vertices: Vec<GlyphToRender>,
|
glyph_vertices: Vec<GlyphToRender>,
|
||||||
glyphs_to_render: u32,
|
glyphs_to_render: u32,
|
||||||
}
|
}
|
||||||
|
@ -74,11 +73,10 @@ impl TextRenderer {
|
||||||
let bounds_max_y = text_area.bounds.bottom.min(resolution.height as i32);
|
let bounds_max_y = text_area.bounds.bottom.min(resolution.height as i32);
|
||||||
|
|
||||||
let is_run_visible = |run: &cosmic_text::LayoutRun| {
|
let is_run_visible = |run: &cosmic_text::LayoutRun| {
|
||||||
let start_y_physical = (text_area.top + (run.line_top * text_area.scale)) as i32;
|
let start_y = (text_area.top + run.line_top) as i32;
|
||||||
let end_y_physical = start_y_physical + (run.line_height * text_area.scale) as i32;
|
let end_y = (text_area.top + run.line_top + run.line_height) as i32;
|
||||||
|
|
||||||
start_y_physical <= text_area.bounds.bottom
|
start_y <= bounds_max_y && bounds_min_y <= end_y
|
||||||
&& text_area.bounds.top <= end_y_physical
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let layout_runs = text_area
|
let layout_runs = text_area
|
||||||
|
@ -88,164 +86,23 @@ impl TextRenderer {
|
||||||
.take_while(is_run_visible);
|
.take_while(is_run_visible);
|
||||||
|
|
||||||
for run in layout_runs {
|
for run in layout_runs {
|
||||||
if text_area.stroke_size > 0.0 {
|
|
||||||
for i in 0..2 {
|
|
||||||
self.prepare_glyphs(
|
|
||||||
&run,
|
|
||||||
&text_area,
|
|
||||||
atlas,
|
|
||||||
cache,
|
|
||||||
font_system,
|
|
||||||
device,
|
|
||||||
queue,
|
|
||||||
bounds_max_x,
|
|
||||||
bounds_max_y,
|
|
||||||
bounds_min_y,
|
|
||||||
bounds_min_x,
|
|
||||||
i == 0,
|
|
||||||
&mut metadata_to_depth,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.prepare_glyphs(
|
|
||||||
&run,
|
|
||||||
&text_area,
|
|
||||||
atlas,
|
|
||||||
cache,
|
|
||||||
font_system,
|
|
||||||
device,
|
|
||||||
queue,
|
|
||||||
bounds_max_x,
|
|
||||||
bounds_max_y,
|
|
||||||
bounds_min_y,
|
|
||||||
bounds_min_x,
|
|
||||||
false,
|
|
||||||
&mut metadata_to_depth,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.glyphs_to_render = self.glyph_vertices.len() as u32;
|
|
||||||
|
|
||||||
let will_render = !self.glyph_vertices.is_empty();
|
|
||||||
if !will_render {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let vertices = self.glyph_vertices.as_slice();
|
|
||||||
let vertices_raw = unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
vertices as *const _ as *const u8,
|
|
||||||
std::mem::size_of_val(vertices),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.vertex_buffer_size >= vertices_raw.len() as u64 {
|
|
||||||
self.staging_belt
|
|
||||||
.write_buffer(
|
|
||||||
encoder,
|
|
||||||
&self.vertex_buffer,
|
|
||||||
0,
|
|
||||||
NonZeroU64::new(vertices_raw.len() as u64).expect("Non-empty vertices"),
|
|
||||||
device,
|
|
||||||
)
|
|
||||||
.copy_from_slice(vertices_raw);
|
|
||||||
} else {
|
|
||||||
self.vertex_buffer.destroy();
|
|
||||||
|
|
||||||
let (buffer, buffer_size) = create_oversized_buffer(
|
|
||||||
device,
|
|
||||||
Some("glyphon vertices"),
|
|
||||||
vertices_raw,
|
|
||||||
BufferUsages::VERTEX | BufferUsages::COPY_DST,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.vertex_buffer = buffer;
|
|
||||||
self.vertex_buffer_size = buffer_size;
|
|
||||||
|
|
||||||
self.staging_belt.finish();
|
|
||||||
self.staging_belt = StagingBelt::new(buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.staging_belt.finish();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prepare<'a>(
|
|
||||||
&mut self,
|
|
||||||
device: &Device,
|
|
||||||
queue: &Queue,
|
|
||||||
encoder: &mut CommandEncoder,
|
|
||||||
font_system: &mut FontSystem,
|
|
||||||
atlas: &mut TextAtlas,
|
|
||||||
viewport: &Viewport,
|
|
||||||
text_areas: impl IntoIterator<Item = TextArea<'a>>,
|
|
||||||
cache: &mut SwashCache,
|
|
||||||
) -> Result<(), PrepareError> {
|
|
||||||
self.prepare_with_depth(
|
|
||||||
device,
|
|
||||||
queue,
|
|
||||||
encoder,
|
|
||||||
font_system,
|
|
||||||
atlas,
|
|
||||||
viewport,
|
|
||||||
text_areas,
|
|
||||||
cache,
|
|
||||||
zero_depth,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Renders all layouts that were previously provided to `prepare`.
|
|
||||||
pub fn render(
|
|
||||||
&self,
|
|
||||||
atlas: &TextAtlas,
|
|
||||||
viewport: &Viewport,
|
|
||||||
pass: &mut RenderPass<'_>,
|
|
||||||
) -> Result<(), RenderError> {
|
|
||||||
if self.glyphs_to_render == 0 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
pass.set_pipeline(&self.pipeline);
|
|
||||||
pass.set_bind_group(0, &atlas.bind_group, &[]);
|
|
||||||
pass.set_bind_group(1, &viewport.bind_group, &[]);
|
|
||||||
pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
|
|
||||||
pass.draw(0..4, 0..self.glyphs_to_render);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prepare_glyphs(
|
|
||||||
&mut self,
|
|
||||||
run: &LayoutRun,
|
|
||||||
text_area: &TextArea,
|
|
||||||
atlas: &mut TextAtlas,
|
|
||||||
cache: &mut SwashCache,
|
|
||||||
font_system: &mut FontSystem,
|
|
||||||
device: &Device,
|
|
||||||
queue: &Queue,
|
|
||||||
bounds_max_x: i32,
|
|
||||||
bounds_max_y: i32,
|
|
||||||
bounds_min_y: i32,
|
|
||||||
bounds_min_x: i32,
|
|
||||||
stroke: bool,
|
|
||||||
mut metadata_to_depth: impl FnMut(usize) -> f32,
|
|
||||||
) -> Result<(), PrepareError> {
|
|
||||||
for glyph in run.glyphs.iter() {
|
for glyph in run.glyphs.iter() {
|
||||||
let physical_glyph = glyph.physical((text_area.left, text_area.top), text_area.scale);
|
let physical_glyph =
|
||||||
|
glyph.physical((text_area.left, text_area.top), text_area.scale);
|
||||||
|
|
||||||
let cache_key = physical_glyph.cache_key;
|
let cache_key = physical_glyph.cache_key;
|
||||||
|
|
||||||
let details = if let Some(details) = atlas.mask_atlas.glyph_cache.get(&cache_key) {
|
let details = if let Some(details) =
|
||||||
|
atlas.mask_atlas.glyph_cache.get(&cache_key)
|
||||||
|
{
|
||||||
atlas.mask_atlas.glyphs_in_use.insert(cache_key);
|
atlas.mask_atlas.glyphs_in_use.insert(cache_key);
|
||||||
details
|
details
|
||||||
} else if let Some(details) = atlas.color_atlas.glyph_cache.get(&cache_key) {
|
} else if let Some(details) = atlas.color_atlas.glyph_cache.get(&cache_key) {
|
||||||
atlas.color_atlas.glyphs_in_use.insert(cache_key);
|
atlas.color_atlas.glyphs_in_use.insert(cache_key);
|
||||||
details
|
details
|
||||||
} else {
|
} else {
|
||||||
let Some(image) = cache.get_image_uncached(font_system, physical_glyph.cache_key)
|
let Some(image) =
|
||||||
|
cache.get_image_uncached(font_system, physical_glyph.cache_key)
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
@ -272,7 +129,13 @@ impl TextRenderer {
|
||||||
match inner.try_allocate(width, height) {
|
match inner.try_allocate(width, height) {
|
||||||
Some(a) => break a,
|
Some(a) => break a,
|
||||||
None => {
|
None => {
|
||||||
if !atlas.grow(device, queue, font_system, cache, content_type) {
|
if !atlas.grow(
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
font_system,
|
||||||
|
cache,
|
||||||
|
content_type,
|
||||||
|
) {
|
||||||
return Err(PrepareError::AtlasFull);
|
return Err(PrepareError::AtlasFull);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,13 +247,9 @@ impl TextRenderer {
|
||||||
height = bounds_max_y - y;
|
height = bounds_max_y - y;
|
||||||
}
|
}
|
||||||
|
|
||||||
let color = if stroke {
|
let color = match glyph.color_opt {
|
||||||
text_area.stroke_color
|
|
||||||
} else {
|
|
||||||
match glyph.color_opt {
|
|
||||||
Some(some) => some,
|
Some(some) => some,
|
||||||
None => text_area.default_color,
|
None => text_area.default_color,
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let depth = metadata_to_depth(glyph.metadata);
|
let depth = metadata_to_depth(glyph.metadata);
|
||||||
|
@ -408,10 +267,99 @@ impl TextRenderer {
|
||||||
} as u16,
|
} as u16,
|
||||||
],
|
],
|
||||||
depth,
|
depth,
|
||||||
stroke_color: text_area.stroke_color.0,
|
|
||||||
stroke_size: if stroke { text_area.stroke_size } else { 0.0 },
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.glyphs_to_render = self.glyph_vertices.len() as u32;
|
||||||
|
|
||||||
|
let will_render = !self.glyph_vertices.is_empty();
|
||||||
|
if !will_render {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let vertices = self.glyph_vertices.as_slice();
|
||||||
|
let vertices_raw = unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
vertices as *const _ as *const u8,
|
||||||
|
std::mem::size_of_val(vertices),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.vertex_buffer_size >= vertices_raw.len() as u64 {
|
||||||
|
self.staging_belt
|
||||||
|
.write_buffer(
|
||||||
|
encoder,
|
||||||
|
&self.vertex_buffer,
|
||||||
|
0,
|
||||||
|
NonZeroU64::new(vertices_raw.len() as u64).expect("Non-empty vertices"),
|
||||||
|
device,
|
||||||
|
)
|
||||||
|
.copy_from_slice(vertices_raw);
|
||||||
|
} else {
|
||||||
|
self.vertex_buffer.destroy();
|
||||||
|
|
||||||
|
let (buffer, buffer_size) = create_oversized_buffer(
|
||||||
|
device,
|
||||||
|
Some("glyphon vertices"),
|
||||||
|
vertices_raw,
|
||||||
|
BufferUsages::VERTEX | BufferUsages::COPY_DST,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.vertex_buffer = buffer;
|
||||||
|
self.vertex_buffer_size = buffer_size;
|
||||||
|
|
||||||
|
self.staging_belt.finish();
|
||||||
|
self.staging_belt = StagingBelt::new(buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.staging_belt.finish();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepare<'a>(
|
||||||
|
&mut self,
|
||||||
|
device: &Device,
|
||||||
|
queue: &Queue,
|
||||||
|
encoder: &mut CommandEncoder,
|
||||||
|
font_system: &mut FontSystem,
|
||||||
|
atlas: &mut TextAtlas,
|
||||||
|
viewport: &Viewport,
|
||||||
|
text_areas: impl IntoIterator<Item = TextArea<'a>>,
|
||||||
|
cache: &mut SwashCache,
|
||||||
|
) -> Result<(), PrepareError> {
|
||||||
|
self.prepare_with_depth(
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
encoder,
|
||||||
|
font_system,
|
||||||
|
atlas,
|
||||||
|
viewport,
|
||||||
|
text_areas,
|
||||||
|
cache,
|
||||||
|
zero_depth,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders all layouts that were previously provided to `prepare`.
|
||||||
|
pub fn render(
|
||||||
|
&self,
|
||||||
|
atlas: &TextAtlas,
|
||||||
|
viewport: &Viewport,
|
||||||
|
pass: &mut RenderPass<'_>,
|
||||||
|
) -> Result<(), RenderError> {
|
||||||
|
if self.glyphs_to_render == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
pass.set_pipeline(&self.pipeline);
|
||||||
|
pass.set_bind_group(0, &atlas.bind_group, &[]);
|
||||||
|
pass.set_bind_group(1, &viewport.bind_group, &[]);
|
||||||
|
pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
|
||||||
|
pass.draw(0..4, 0..self.glyphs_to_render);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue