bevyengine / bevyengine/bevy

Text looks blurry/unsharp/bad unless the font size is absurdly large

Open
#25,453 6 comments 0 reactions 0 assignees View on GitHub
A-Rendering A-Text C-Bug S-Blocked
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 22h
Merged PRs (30d)
161

Description

I'm not sure if this is a issue with bevy or a dependency, or both,
I don't know how text is rendered in bevy.

I used alacritty as a comparison because i use it every time i program and its text rendering (but not shaping, it seems to struggle with complicated scripts, but that's unrelated to the issue) is amazing so it was the first program that came to mind, firefox/chrome would have likely made for better comparisons.

the issue is noticable without having any other program to compare the text rendering with.

## Bevy version and features

- The release number or commit hash of the version you're using: `0.19.1`
- If you're not using default features, the combination of bevy's cargo features you are using: `system_font_discovery`

## \[Optional\] Relevant system information

If your bug is rendering-related, copy the adapter info that appears when you run Bevy:
```
AdapterInfo { name: "AMD Radeon RX 6800 (RADV NAVI21)", vendor: 4098, device: 29631, device_type: DiscreteGpu, device_pci_bus_id: "0000:03:00.0", driver: "radv", driver_info: "Mesa 26.1.4", backend: Vulkan, subgroup_min_size: 32, subgroup_max_size: 64, transient_saves_memory: false }
```

## What you did

i tried to display text on my monitor, using the following code (i encountered the issue in a more complex app, i wrote a simpler example so that its easier to diagnose):

```rust
//! Render 4 areas of text at different sizes, each with a different combination of hinting and anti-aliasing,
//! the last column of text in each area uses jetbrains mono, the same font as my terminal.
//! different fonts will be used on different computers as i used system fonts.

use bevy::{prelude::*, text::FontSourceTemplate};
// unrelated to the issue, but useful for debugging.
use bevy_inspector_egui::{bevy_egui::EguiPlugin, quick::WorldInspectorPlugin};

fn main() -> AppExit {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins((
EguiPlugin::default(),
WorldInspectorPlugin::new(),
))
.add_systems(Startup, setup)
.run()
}

fn setup(mut commands: Commands) {
commands.spawn(Camera2d);

let root = commands.spawn_scene(bsn! {
Node {
display: Display::Flex,
flex_direction: FlexDirection::Row,
column_gap: px(8.0),
}
}).id();

for hinting in [FontHinting::Disabled, FontHinting::Enabled] {
let entity = commands.spawn_scene(bsn! {
ChildOf(root)

Node {
display: Display::Flex,
flex_direction: FlexDirection::Column,
column_gap: px(8.0),
}
}).id();

commands.spawn_scene(bsn! {
ChildOf(entity)

Node
Text(format!("hinting: {hinting:?}"))
template_value(hinting)
});

for font_smoothing in [FontSmoothing::None, FontSmoothing::AntiAliased] {
commands.spawn_scene(bsn! {
ChildOf(entity)

Node
Text(format!("smoothing: {font_smoothing:?}"))
template_value(hinting)
});

for font in [FontSourceTemplate::default(), FontSourceTemplate::SystemUi, FontSourceTemplate::Monospace] {
for size in [14.0, 16.0, 18.0, 24.0, 32.0] {
let font = font.clone_template();

commands.spawn_scene(bsn! {
ChildOf(entity)

Node
Text("The quick brown fox jumps over the lazy dog")
TextFont {
font,
font_size: size,
font_smoothing,
}
template_value(hinting)
});
}
}
}
}
}
```

## What went wrong

If it's not clear, break this out into:

- what were you expecting:
sharp, non-blurry plesent-to-look-at text
- what actually happened:
the text is blurry; i compared the text to alacritty (both with and without subpixel rendering on allacritty),
and bevy's text looks more blurry.

the monospace font used for the comparison is jetbrains mono, i did not compare non-monospace font rendering with other applications, but it's also blurry.

## Additional information

even 32px text, which i would expect to look amazing given the size, it still looks a bit blurry.

using a absurdly large font size such as 150 results in non-blurry text.

Other information that can be used to further reproduce or isolate the problem.
This commonly includes:

- screenshots:

(**NOTE**: the image needs to be viewed at 100% size with no upscaling or compression, open image in new tab does **NOT** display the image at 100% size)
Image

- theories and observations about what might be going wrong:
my original theory was that alacritty uses sub-pixel rendering and bevy doesn't, that turned out to be false as i had subpixel rendering disabled on my entire computer for **YEARS** without noticing, neither had subpixel rendering until i enabled and added a third comparison, *subpixel vs normal rendering is not the issue*.

bevy's anti-aliasing seems to be much more "aggressive" and less aware of what the font is trying to draw

bevy often uses way more pixels than alacritty, and the anti-aliasing is less transparent in bevy.

the w is much blurrier, filling almost all "empty" space (i counted 2 empty pixels) with gradiants, while alacritty cuts the gradients off at some point (although alacritty's w is also a bit weird, but much less bad).

the r has a clearly-defined 1-pixel line with some anti-aliasing next to it in alacritty, while it looks more like a blurry 2-pixel line in bevy, a lot of other letters are the same.

the dots above i and j look nearly identical in alacritty, both being a 2x2 square with a bit of antialiasing next to them, while bevy's i dot looks very blurry and has much more anti-aliasing, and the j dot is stretched 1 pixel wider for unknown reasons (the font defines a circle, but alacritty's square is a much better-looking approximation of a circle at this scale)

the m in "jumps" has 0 empty pixels in bevy, while alacritty leaves the 2 center columns empty

- workarounds that you used:
set the text size to something way too large like 150.

Contributor guide

Open the contributing guide

Research direction

Start by running the supplied minimal Rust example with Bevy 0.19.1 and inspect the text-rendering paths exercised by FontSourceTemplate, FontHinting, FontSmoothing, and TextFont. Compare small-font output at 100% against the included observations and adapter information; done means the blur is explained and a verified rendering or dependency finding is documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
computer-graphics
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
56/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.