grovesnl / glyphon Goto Github PK
View Code? Open in Web Editor NEWπ¦ π¦ Fast, simple 2D text renderer for wgpu
π¦ π¦ Fast, simple 2D text renderer for wgpu
The hello-world example uses wgpu::Limits::downlevel_defaults()
, which creates a set of limits with max_texture_dimension_2d set to 2048. In my wgpu application, I'm using wgpu::Limits::downlevel_webgl2_defaults().using_resolution(adapter.limits())
which is what many of the wgpu examples use. This results in a max_texture_dimension_2d of 32,768.
When the texture atlas is initialized against this device, it attempts to allocate a 32kx32k texture which is 4 gigs with the SrgbUnorm texture format. This causes an out of memory error on my machine.
I can of course restrict my limits further, but it seems like TextAtlas shouldn't be attempting to create the largest possible texture. Ideally it would start small and dynamically grow as needed. However, I know that's really complicated, so as an alternative, perhaps allow the developer a way to specify the atlas size explicitly?
I'm pretty sure I'm obtuse:
I'm looking at the hello-world.rs
file and can't figure out how to unclip the text in the buffer. Changing the width and height parameters don't change the clipping, only the size of the spawned window.
Is there a default setting somewhere I'm unable to grasp? I've tried looking through both cosmic-text's and glyphon's docs to no avail.
Any help would be appreciated!
You can test this by setting default_color in the hello-world example to #5fafff for example, and then taking a screenshot of the resulting window. You will measure #a4d8ff instead, which means that the default_color is being interpreted as though it is in the linear color space instead of sRGB.
cosmic-text intends for the colors used to be sRGB, and glyphon should also interpret them that way, doing any conversions necessary inside of glyphon.
Hey, I like that this is using the latest shader format. You might want to add this to Cargo.toml or make mention of it:
[patch.crates-io]
wgpu = { git = "https://github.com/gfx-rs/wgpu" }
I have tried using glyphon in a project with wgpu 0.18.0, but it appears that the crate still depends on wgpu 0.16.3, breaking a lot of things. I have seen the update wgpu to 0.18
commit, but this change doesn't seem to have landed at https://crates.io/crates/glyphon yet (And not on my end either).
I apologize if this is intentional, but it seems strange to me. Thank you!
Consider handling scale, shear, rotation, etc., at least for typical 2D use cases
Also related to #3 because pixel snapping wouldn't work well depending on the transformation.
It's a bit difficult to organize fonts/layouts and pass them to renderer.
Consider some other way to handle fonts, layouts, etc., possibly wrapping all of fontdue to remove fontdue from the public API.
If I'm not mistaken, there is currently no changelog for this crate. This makes it really hard to update from one version to the next, or even just check what version works with a specific version of wgpu. Adding a changelog would be great, thanks!
Currently we snap to the nearest pixel so kind of avoid this.
We probably want to have a strategy for handling subpixel positioning and AA generally. This can be difficult with glyphs because we might need to generate multiple glyphs for various subpixel positions.
I have a render pass with MSAA enabled and would like to render text in this pass. Would you be willing to accept a PR to set the sample count as part of the atlasβ new
function? Is there any reason not to do render text to an MSAA target?
Documentation on https://docs.rs/glyphon/0.3.0/glyphon/struct.BufferLine.html#method.set_align claims that it resets shape and layout but the implementation appears to only reset layout.
Hi,
I was wondering if there was a way to obtain (line, character) information from a rendered text area in a window? I can't think of another way to highlight words using the cursor, for example.
What would it take for such a calculation to be done?
Thanks.
Would it be feasible to provide a C API of glyphon so it can be easily used by libraries outside Rust?
If its feasible and acceptable, then with some guidance I'm potentially willing to put time into this effort
Hello!
I want to get the size of the rendered text rather than the whole buffer dimensions, because I'm setting it to the whole width of the window (probably shouldn't do that(?)), but if there is a way to measure the text with a function or something that would solve a couple of issues I'm having
can this be done in this crate or cosmic-text
?
Ideally we could have an easy way to have multiple text renderers that share the same atlas to avoid extra texture allocations and share glyphs when possible.
We could do this by passing the atlas to prepare
, sharing internally, etc. Alternatively we could share a text renderer between all layers, and the text renderer could model layers directly somehow.
Hey there, Im working on an IDE and running the example code in this repo, I get an error in the browser:
panicked at /Users/user/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cosmic-text-0.10.0/src/shape.rs:217:33: no default font found
What is the best way to deal with loading fonts for Glyphon in the browser?
Thanks :)
At the moment text area holds reference for a buffer. This create lifetimes issues when storing text areas somewhere.
Are there any ways to change this API?
Because at the moment it is hard to organize text area storage, and the only usable option is to recreate text areas on every redraw call.
I'm looking to migrate from wgpu_glyph
to Glyphon
, but I'm getting stuck on one thing: I can't define a text area's depth value.
In wgpu_glyph
I could do something like this:
let section = Section {
screen_position: pos,
bounds: size,
text: vec![Text::new(text)
.with_color([c[0], c[1], c[2], 1.0])
.with_scale(appearance.font_size)
.with_z(z_order.calculate_position())],
layout: Layout::default_wrap(),
};
where with_z
would decide the Z written into the depth buffer, a must when doing a UI.
I've seen prepare_with_depth
but I do not think that lets me set a depth value per textarea ?
When I run the example on my machine I get this panic:
~2/Projects/Crates/glyphon> cargo run --example hello-world
Finished dev [unoptimized + debuginfo] target(s) in 0.07s
Running `target/debug/examples/hello-world`
thread 'main' panicked at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-0.19.3/src/backend/wgpu_core.rs:724:18:
Error in Surface::configure: Validation Error
Caused by:
`Surface` width and height must be within the maximum supported texture size. Requested was (1276, 2119), maximum extent is 2048.
stack backtrace:
0: rust_begin_unwind
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:645:5
1: core::panicking::panic_fmt
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:72:14
2: wgpu::backend::wgpu_core::ContextWgpuCore::handle_error_fatal
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-0.19.3/src/backend/wgpu_core.rs:283:9
3: <wgpu::backend::wgpu_core::ContextWgpuCore as wgpu::context::Context>::surface_configure
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-0.19.3/src/backend/wgpu_core.rs:724:13
4: <T as wgpu::context::DynContext>::surface_configure
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-0.19.3/src/context.rs:2124:9
5: wgpu::Surface::configure
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-0.19.3/src/lib.rs:4818:9
6: hello_world::run::{{closure}}::{{closure}}
at ./examples/hello-world.rs:94:25
7: core::ops::function::impls::<impl core::ops::function::FnMut<A> for &mut F>::call_mut
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:294:13
8: winit::platform_impl::platform::x11::EventLoop<T>::drain_events::{{closure}}
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.14/src/platform_impl/linux/x11/mod.rs:636:25
9: core::ops::function::impls::<impl core::ops::function::FnMut<A> for &mut F>::call_mut
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:294:13
10: core::ops::function::impls::<impl core::ops::function::FnMut<A> for &mut F>::call_mut
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:294:13
11: winit::platform_impl::platform::x11::event_processor::EventProcessor<T>::configure_notify
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.14/src/platform_impl/linux/x11/event_processor.rs:778:13
12: winit::platform_impl::platform::x11::event_processor::EventProcessor<T>::process_xevent
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.14/src/platform_impl/linux/x11/event_processor.rs:169:38
13: winit::platform_impl::platform::x11::event_processor::EventProcessor<T>::process_event
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.14/src/platform_impl/linux/x11/event_processor.rs:80:9
14: winit::platform_impl::platform::x11::EventLoop<T>::drain_events
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.14/src/platform_impl/linux/x11/mod.rs:626:13
15: winit::platform_impl::platform::x11::EventLoop<T>::single_iteration
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.14/src/platform_impl/linux/x11/mod.rs:557:9
16: winit::platform_impl::platform::x11::EventLoop<T>::pump_events
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.14/src/platform_impl/linux/x11/mod.rs:441:13
17: winit::platform_impl::platform::x11::EventLoop<T>::run_on_demand
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.14/src/platform_impl/linux/x11/mod.rs:408:19
18: winit::platform_impl::platform::EventLoop<T>::run_on_demand
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.14/src/platform_impl/linux/mod.rs:829:56
19: winit::platform_impl::platform::EventLoop<T>::run
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.14/src/platform_impl/linux/mod.rs:822:9
20: winit::event_loop::EventLoop<T>::run
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/winit-0.29.14/src/event_loop.rs:249:9
21: hello_world::run::{{closure}}
at ./examples/hello-world.rs:83:5
22: pollster::block_on
at /home/rukai/.cargo/registry/src/index.crates.io-6f17d22bba15001f/pollster-0.3.0/src/lib.rs:128:15
23: hello_world::main
at ./examples/hello-world.rs:21:5
24: core::ops::function::FnOnce::call_once
at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
My machine is running arch linux with i3 wm.
Since its using i3, the example automatically fills up the entire monitor height when launched.
Even for non-i3 users, they could trigger this issue by resizing the window to fill more of their monitor.
Hi!
I'm making an application with a custom WGPU renderer (I'm still learning), and I tried to integrate glyphon with it, but it doesn't render anything, even when I comment out my renderer and pass the render pass to the text renderer, it still doesn't render anything to the window
relevant code:
pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
let output = self.surface.get_current_texture()?;
let view = output
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
}),
store: true,
},
})],
depth_stencil_attachment: None,
});
// my renderer
self.renderer
.render(&mut render_pass, self.painter.meshes());
// glyphon renderer
self.text_renderer.render(&mut render_pass).unwrap();
}
// submit will accept anything that implements IntoIter
self.queue.submit(std::iter::once(encoder.finish()));
output.present();
self.text_renderer.trim();
Ok(())
}
pub struct TextRenderer {
pub buffer: glyphon::Buffer,
pub renderer: glyphon::TextRenderer,
pub cache: SwashCache,
pub font_system: glyphon::FontSystem,
pub atlas: glyphon::TextAtlas,
}
impl TextRenderer {
pub fn prepare(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
width: u32,
height: u32,
) -> Result<(), wgpu::SurfaceError> {
self.renderer
.prepare(
device,
queue,
&mut self.font_system,
&mut self.atlas,
Resolution { width, height },
[TextArea {
buffer: &self.buffer,
left: 0.0,
top: 0.0,
scale: 1.0,
bounds: TextBounds {
left: 1000,
top: 1000,
right: 1000,
bottom: 1000,
},
default_color: glyphon::Color::rgb(255, 255, 255),
}],
&mut self.cache,
)
.unwrap();
Ok(())
}
pub fn render<'rp>(
&'rp self,
render_pass: &mut wgpu::RenderPass<'rp>,
) -> Result<(), crate::Error> {
self.renderer.render(&self.atlas, render_pass)?;
Ok(())
}
pub fn trim(&mut self) {
self.atlas.trim();
}
}
if more context is needed I will be happy to provide it :)
Handle text overflow outside of layouts through culling and clipping.
Culling:
Clipping:
render
, clipping with scissor could be handled outside of glyphon or by glyphon setting the scissorSo in small or large projects it does tend to be a good Idea to split structure and data types into their own parts. This makes certain things Easier to find and can improve readability and Adaptability. Keeping most of everything in a Single file generally is a huge turn off to programmers who are willing to help as it makes it harder to find Certain things. This is just a Suggestion not really a Must do.
TextAtlas and Text Renderer can be split into their own files. The major Data structures at the top Could either Stay since they are small or be moved into a single separate file to remove code form the lib.rs. Some internal Changes Could also be split into Functions as well and just made inlined, which can make Code easier to read if the function names Make sense for what it does.
Splitting up the Code can also help later on with new Feature Additions to this library. Anyways What do you think?
When storage buffers area available (e.g. not WebGL), we could use storage buffers and vertex pulling as an optimization
Rendering a short string with a large font size and large TextArea.scale
(still small in 4k monitors) causes AtlasFull
.
Relaxing the texture limit to x4, TextAtlas keeps growing until OOM.
let mut limits = wgpu::Limits::default();
limits.max_texture_dimension_2d = 8192 * 4;
let mut buffer =
glyphon::Buffer::new(&mut font_system, glyphon::Metrics::new(30.0, 42.0));
buffer.set_size(&mut font_system, 600.0, 500.0);
buffer.set_text(
&mut font_system,
" π¦
glyphon π¦\nThe text x y z",
glyphon::Attrs::new().family(glyphon::Family::SansSerif),
glyphon::Shaping::Advanced,
);
buffer.shape_until_scroll(&mut font_system);
text_renderer.prepare(
device,
queue,
&mut font_system,
&mut atlas,
glyphon::Resolution {
width: 800,
height: 600,
},
[glyphon::TextArea {
buffer: &buffer,
left: 100.0,
top: 100.0,
scale: 10.0,
bounds: glyphon::TextBounds {
left: 100,
top: 100,
right: 600,
bottom: 600,
},
default_color: glyphon::Color::rgb(0, 0, 0),
}],
&mut cache,
)
.unwrap();
text_renderer.render(&atlas, &mut render_pass).unwrap();
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:474] content_type = Color
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:475] did_grow = true
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:130] self.size = 26880
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:131] self.max_texture_dimension_2d = 32768
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:474] content_type = Color
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:475] did_grow = true
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:130] self.size = 27136
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:131] self.max_texture_dimension_2d = 32768
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:474] content_type = Color
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:475] did_grow = true
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:130] self.size = 27392
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:131] self.max_texture_dimension_2d = 32768
thread 'main' panicked at .cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-0.17.1/src/backend/direct.rs:3056:5:
wgpu error: Validation Error
Caused by:
In Device::create_texture
note: label = `glyphon atlas`
Not enough memory left
But then reducing TextArea.scale from 10 to 5 renders fine with only size=256.
Why does scale=10 use x100 more memory than scale=5?
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:130] self.size = 256
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:131] self.max_texture_dimension_2d = 32768
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:474] content_type = Color
[.cargo/git/checkouts/glyphon-bce1d8fb6a1f7cb1/9e25714/src/text_atlas.rs:475] did_grow = true
How would I center the text in for example the middle of my window?
I currently do
let x = self.size.width / 2;
let y = self.size.height / 2;
self.text_renderer
.prepare(
&self.device,
&self.queue,
&mut self.font_system,
&mut self.atlas,
Resolution {
width: self.size.width,
height: self.size.height,
},
[TextArea {
buffer: &self.buffer,
left: x as f32,
top: y as f32,
scale: 1.0,
bounds: TextBounds {
left: x as i32,
top: y as i32,
right: self.size.width as i32,
bottom: self.size.height as i32,
},
default_color: glyphon::Color::rgb(0, 0, 0),
}],
&mut self.cache,
)
.unwrap();
But that does it with a offset. Now i would need something like the worlds famous translate(-50%) css trick...
But I can't seem to find any API to get the size of the text. Also the TextBounds
API is kinda weird because provides extra left and top values which are absolute so I need to pass the left and top in there as well...
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.