Skip to content

Render terminal texture with direct Vello path#107

Open
gold-silver-copper wants to merge 17 commits into
orhun:mainfrom
gold-silver-copper:direct-gpu-rendering-clean
Open

Render terminal texture with direct Vello path#107
gold-silver-copper wants to merge 17 commits into
orhun:mainfrom
gold-silver-copper:direct-gpu-rendering-clean

Conversation

@gold-silver-copper

@gold-silver-copper gold-silver-copper commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR replaces Ratty's CPU-readback terminal rendering with a direct Vello→Bevy GPU texture path: parley_ratatui records a Vello scene, the scene is handed to Bevy's render world through a double-buffered exchange, and Vello rasterizes it straight into a Bevy-owned GPU texture with no CPU readback. On top of that core change it reworks the scale/zoom model to render at native display scale, makes the terminal systems Send so they run continuously and in parallel, gates per-frame work behind dirty/state checks, and fixes a series of color, scale, shutdown, and GPU-validation bugs.

Updates parley_ratatui to 0.3.1 and Bevy to 0.19.0-rc.2.

Fixes #97.


1. New direct Vello→Bevy render path (replaces CPU readback)

The old path rendered the terminal on a private offscreen wgpu device, read the pixels back to the CPU, and uploaded them into a Bevy Image every frame. That whole bridge is gone.

src/direct_render.rs (new file) — DirectTerminalRenderPlugin:

  • DirectTerminalSceneExchange — an Arc-backed Resource shared between the main world and the RenderApp. Its inner state is two single-slot, mutex-guarded mailboxes: pending: Mutex<Option<DirectTerminalFrame>> (latest main→render frame, newest wins) and recycled: Mutex<Option<Scene>> (one spare Vello Scene flowing render→main). publish_frame replaces the pending slot and recycles any displaced frame's scene; take_recycled_scene/recycle_scene (which calls scene.reset() and keeps at most one) implement double-buffered scene ownership so neither side reallocates a scene per frame.
  • update_direct_terminal_frame (main side) — does the double-buffer dance: take a recycled scene, swap it into the TerminalRenderer, build the scene from the Ratatui buffer (build_scene_with_elapsed), swap the spare back, and publish_frame with both image handles, size, and base_color.
  • extract_terminal_frame (ExtractSchedule) — moves the pending frame into the render world's ExtractedDirectTerminalFrame, recycling any frame it supersedes.
  • render_terminal_frame (RenderGraph, set RenderGraphSystems::Begin) — lazily builds the Vello GpuRenderer, rasterizes into the render texture, and copies into the present texture. Placed in Begin so Vello's submission is ordered before the camera passes that sample the terminal texture.
  • Shutdown fix — the Vello renderer lives in DirectTerminalRenderState { renderer: SyncCell<Option<GpuRenderer>> }. vello::Renderer is Send but not Sync, so a plain resource is impossible; the previous non-send resource pinned render_terminal_frame to a thread and deadlocked the pipelined render app's final update during shutdown (World::clear_all waited forever). SyncCell makes it a normal Send resource.

src/terminal.rs — deletes the entire OffscreenGpu struct (its own wgpu::Device/Queue, GpuRenderer, TextureTarget, TextureReadback, CPU rgba: Vec<u8>), the pollster::block_on init, and the render_to_rgba8…image.data.copy_from_slice readback. TerminalSurface becomes a regular #[derive(Resource)].

Cargo.toml — removes pollster (no CPU readback to block on). README.md — rewrites the pipeline section: the terminal image is now "fully GPU-resident; the only data crossing from the main world to the render world each frame is the recorded scene, not pixels." src/lib.rs — adds mod direct_render;.


2. Color correctness & GPU-validation safety (sRGB / storage split)

Vello writes sRGB-encoded, display-ready bytes into its Rgba8Unorm storage target (confirmed by a readback probe). Two problems had to be solved, and the final design uses two textures:

  • new_terminal_render_image — the texture Vello rasterizes into: Rgba8Unorm, STORAGE_BINDING | COPY_SRC | COPY_DST, no sRGB view. Vello binds it as a compute storage target, which requires a plain (non-sRGB) view.
  • new_terminal_image — the present texture materials/sprite sample: Rgba8Unorm, TEXTURE_BINDING | COPY_DST (no storage), with view_formats = [Rgba8UnormSrgb] and a texture_view_descriptor so it is sampled through an sRGB view that decodes the bytes on read (rather than treating them as linear and re-encoding at the swapchain, which washed out colors).

render_terminal_frame rasterizes into the render texture, then copy_texture_to_texture (a plain same-format Rgba8UnormRgba8Unorm texel copy) into the present texture each frame.

Why two textures: earlier the present texture was the storage texture with an sRGB view bolted on. wgpu rejects an sRGB view of a storage texture, so on stricter backends bind-group creation failed (TextureView with '' label is invalid) and the app quit. Splitting them keeps the sRGB view on a non-storage texture (Bevy's standard render-target pattern, valid on every backend); the storage texture has no sRGB view. Inspired by linebender/bevy_vello, which keeps sRGB off the storage texture.

Frame retry & zero-fill — frames that arrive before the GPU image is prepared, or while its size lags a resize, are now retained and retried on the next render frame (a newer published frame supersedes a retained one during extraction) instead of being silently dropped, and both textures are zero-filled so a frame sampled before the first copy reads transparent black rather than uninitialized memory.

src/rendering.rssync_plane_texture only takes materials.get_mut(...) when the base_color_texture handle actually differs (checked via an immutable get), because get_mut marks the material modified and forces a GPU re-prepare.


3. Scale & zoom model (fixes #97)

Previously cell metrics were floored to whole physical pixels at a forced scale factor of 1.0, so a font-size step whose advance delta was under one pixel changed only the cell height — zoom collapsed into a vertical-only stretch.

  • Native scale by default. WindowConfig.scale_factor (src/config.rs) changes from f32 (default 1.0) to Option<f32> (default None = "use the display's scale factor"). src/main.rs's new window_resolution() helper applies with_scale_factor_override(...) only when the config sets it; otherwise the window renders at the display's real scale factor. config/ratty.toml comments out scale_factor with documentation.
  • Don't double-count the override. render_scale_for_window() (src/terminal.rs) chooses its base scale factor as scale_factor when a Bevy override is set, else base_scale_factor() — "a scale-factor override already defines the physical/logical ratio; applying the backend factor on top of it over-sizes fullscreen terminal textures."
  • Fractional cell quantization. build_terminal_renderer builds the parley_ratatui renderer with CellQuantization::Fractional and new_scaled(..., render_scale), so both axes grow on every font-size step. A regression test (font_size_steps_scale_cells_on_both_axes) asserts cell_width/cell_height strictly increase across sizes 8..=24 at scales 1.0 and 2.0.
  • Font size in points. FontConfig.size is now interpreted as points; the renderer applies PT_TO_PX = 96.0/72.0 before handing the size to Parley (so the default 18 renders at 18pt). config/ratty.toml documents this.
  • TerminalLayout (new) bundles cols, rows, texture_size: UVec2 (physical px), logical_size: Vec2 (Bevy world units), and render_scale; pty_pixels() clamps to portable-pty's u16 API. resize_to_fit(logical_size, render_scale) recomputes the grid from renderer.logical_metrics(render_scale) (logical metrics keep the column/row count stable across HiDPI scales) and returns a layout that the resize/render/input systems all consume. set_render_scale rebuilds the renderer; char_dimensions now returns fractional Vec2 logical metrics instead of a ceiled UVec2; snapped_translation() snaps the sprite to the physical pixel grid (the "align zoom resize layout" fix).

4. Scene & presentation

src/scene/mod.rs:

  • Two images in setup_scene — creates new_terminal_render_image (storage, terminal.render_image_handle) and new_terminal_image (present, terminal.image_handle); the legacy create_terminal_image is kept only for the 3D "back" debug image.
  • Layout-driven sizingsetup_scene becomes a SetupSceneParams system param that reads the real PrimaryWindow and TerminalRuntime, computes layout = terminal.resize_to_fit(window_size, render_scale_for_window(window)), resizes the PTY to layout.pty_pixels(), and derives all image sizes, the TerminalViewport, the sprite custom_size/snapped translation, and both plane transform scales from layout instead of app_config.window.{width,height}.
  • Presentation entitiesTerminalSprite (2D, samples the present texture), TerminalPlane + TerminalPlaneBack (3D meshes, StandardMaterial { unlit: true, alpha_mode: Blend }; front samples the present texture, back samples the debug image). Two cameras: Camera2d (order: 0) and TerminalPlaneCamera (Camera3d, order: 1, orthographic, clear_color: None by default).
  • apply_terminal_presentation only mutates GPU state when it changes — front-material cull_mode (None for Mobius's double-sided ribbon, else Some(Face::Back)) is only taken via get_mut when it differs; the 2D camera's is_active = !is_3d and the 3D camera's clear_color (the 3D camera stays active everywhere — it renders the cursor model/RGP objects even in 2D — so "whichever camera renders first owns the screen clear") are only assigned when changed.
  • sync_terminal_layout (new) re-applies a TerminalLayout to live entities (viewport, sprite size/translation, plane scales) on resize, backed by new TerminalSpriteLayoutQuery / TerminalPlaneLayoutQuery / TerminalPlaneBackLayoutQuery aliases.

src/config.rs — adds TERMINAL_RENDER_TEXTURE_LABEL for Vello's storage texture and clarifies TERMINAL_TEXTURE_LABEL as the sampled present texture.


5. Scheduling, parallelism & performance

  • Send resources. src/runtime.rs: TerminalRuntime becomes #[derive(Resource)] by wrapping its !Sync PTY handles in SyncCell (rx: SyncCell<Receiver<Vec<u8>>>, master: SyncCell<Option<Box<dyn MasterPty + Send>>>) and exposing try_recv(&mut self). With TerminalRuntime and TerminalSurface both Send, src/main.rs inserts them with insert_resource (was insert_non_send_resource) and every consuming system in src/systems.rs flips NonSend*Res*, so Bevy can schedule them off the main thread / in parallel. pump_pty_output now calls runtime.try_recv().
  • Continuous updates. src/main.rs replaces the per-focus reactive throttle (UpdateMode::reactive_low_power(FOCUSED_UPDATE_INTERVAL)) with WinitSettings::continuous() — Bevy's default switches unfocused windows to reactive mode, which would delay background PTY output. The Instant-based redraw throttle in TerminalRedrawState is removed.
  • TerminalRedrawSet pipeline. The old redraw_soft_terminal god-system (which redrew, uploaded, refreshed the back texture, synced materials, and drove cursor-model loading) is split into three .chain()-ed systems in a new TerminalRedrawSet: render_terminal_widgetsync_terminal_materialsfinish_terminal_model_load, ordered .after() input/resize/PTY systems.
  • Frame-dirty + blink-tick gating. render_terminal_widget computes a shared TerminalFrameDirty from needs_redraw || blink_ticked || !model_loaded, where blink_ticked uses a Local<u64> bucket over BLINK_TICK_SECS = 0.25 (rapid blink half-period; slow blink is a multiple). Texture content only changes with terminal state or blink phase (warp/camera motion is mesh-/camera-side), so an idle terminal re-rasterizes only ~4×/s instead of every frame, and the downstream pipeline systems early-return on clean frames. sync_image likewise only takes get_mut on each image when its size changes.
  • Run conditions (src/plugin.rs) so continuous mode doesn't burn work: apply_terminal_presentation runs only when presentation/plane-view/Mobius state is_changed(); apply_inline_objects only when presentation changed or inline-object entities were Added; sync_rgp_objects only when RGP objects exist; animate_mobius_transition only in Mobius mode or during a transition; animate_terminal_plane_warp only when not Flat2d; sync_asset_to_terminal_cursor only when the cursor model is visible.
  • src/systems.rs handle_window_resize is simplified to call terminal.resize_to_fit(...) + sync_terminal_layout(...) instead of inlining grid/viewport math, and the redraw pipeline (not the resize system) uploads the resized image.

6. Input handling under the new layout

  • src/keyboard.rs — font-zoom bindings now resize through the window-based layout model: instead of computing the grid from the viewport and integer char dimensions, it calls terminal.resize_to_fit(window_size, render_scale_for_window(window)), resizes the PTY from layout.pty_pixels(), and propagates geometry with sync_terminal_layout(...). This keeps zoom proportional under fractional cell metrics. Params switch NonSendMutResMut and gain the window + layout queries.
  • src/mouse.rs — mouse-to-cell mapping is corrected for a viewport that is smaller than and centered in the window (letterbox/pillarbox under the new scale model). position_to_cell gains a window_size parameter, subtracts the half-margin ((window_size - viewport.size) * 0.5), returns None when the cursor is outside the terminal area, and then clamps within the viewport — previously it clamped raw window coordinates, assuming the viewport filled the window from the origin. The wheel Pixel branch drops a now-unneeded .max(1) as f32 since char_dimensions() returns fractional Vec2.

7. Dependency & Bevy 0.19 migration

  • Cargo.tomlbevy 0.18.10.19.0-rc.2; adds the bevy_scene feature; parley_ratatui 0.2.10.3.1; removes pollster.
  • parley_ratatui 0.3.1 also fixes terminal-cell rendering upstream: it snaps cell background fills to the pixel grid (no base-color bleed between cells with fractional cell sizes) and renders block elements such as the half block () as exact geometric fills instead of font glyphs (which only filled the em box and left gaps in the taller line-height cell). The present texture keeps linear filtering because it is mapped onto transformable/scaled 3D geometry where point sampling would pixelate it; the seam fixes are made at authoring time in parley_ratatui, not by the sampler.
  • Scene type rename — Bevy 0.19 renamed SceneWorldAsset / SceneRootWorldAssetRoot: src/inline.rs (Handle<Scene>Handle<WorldAsset>) and src/model.rs (SceneRootWorldAssetRoot) are mechanical migrations.
  • Other API renamesinsert_non_send_resource/init_non_send_resourceinsert_non_send/init_non_send (AppWindowIcon, TerminalClipboard); StandardMaterial.shadows_enabledshadow_maps_enabled; apply_plane_warp takes Option<AssetMut<'_, Mesh>>.

Bug fixes in this PR (by commit)

  • font zoom proportional with fractional cell metrics (Wonky font scaling #97) — vertical-only zoom stretch.
  • avoid double-counting fullscreen scale — explicit scale override was multiplied by the backend factor, oversizing fullscreen textures.
  • align zoom resize layout — sprite/plane sizing and pixel-snapped translation on resize.
  • unblock shutdown by making render state a Send resource — non-send Vello renderer deadlocked shutdown.
  • decode terminal texture as sRGB and retry dropped frames — washed-out colors and stale/uninitialized frames.
  • give Vello a storage texture and copy into an sRGB present textureTextureView … is invalid crash on strict backends (sRGB view on a storage texture).
  • update parley_ratatui to 0.3.1 — inter-cell seams and block-glyph gaps.

@orhun

orhun commented Jun 12, 2026

Copy link
Copy Markdown
Owner

Can you also update the rendering pipeline docs? I think it will help with understanding this PR better as well.

@gold-silver-copper

Copy link
Copy Markdown
Contributor Author

will do

@gold-silver-copper gold-silver-copper force-pushed the direct-gpu-rendering-clean branch from 5ff72be to aa1392e Compare June 12, 2026 21:26
Cell metrics were floored to whole physical pixels at render scale 1.0
(the forced default scale-factor override), so font-size steps whose
advance delta was under one pixel changed only the cell height and zoom
collapsed into a vertical-only stretch.

- update to parley_ratatui 0.3.0 and opt into fractional cell
  quantization so both axes scale on every font-size step
- stop overriding the display scale factor by default; windowed
  rendering now measures and draws at native scale, and the config
  scale_factor remains available as an explicit override
- interpret the configured font size as points (1pt = 4/3px), so the
  default 18 renders at 18pt
The direct render path stored the Vello GpuRenderer as a non-send
render-world resource, which pinned render_terminal_frame to a specific
thread. During shutdown the pipelined render app runs one final update
with the main thread already gone from the rendezvous, so the Render
schedule parked forever waiting for the non-send system and the main
thread deadlocked in World::clear_all waiting for the render app to
return.

vello::Renderer is Send but not Sync (a RefCell inside its WgpuEngine),
so wrap it in SyncCell to qualify as a regular resource with exclusive
access.
Vello writes sRGB-encoded, display-ready bytes into its Rgba8Unorm
storage target (verified by readback probe), but Bevy sampled that
texture as linear and re-encoded at the swapchain, washing out colors
relative to the old Rgba8UnormSrgb readback path. Sample through a
separate Rgba8UnormSrgb view (via view_formats and the image's
texture_view_descriptor) while Vello keeps rendering through a plain
Rgba8Unorm storage view.

Frames that arrive before the GPU image is prepared, or while its size
lags a resize, were silently recycled, leaving the texture stale or
uninitialized. Retain such frames and retry on the next render frame
(newer published frames supersede retained ones during extraction), and
zero-fill the texture so it can never present uninitialized memory.
- Make TerminalRuntime and TerminalSurface regular Send resources
  (SyncCell around the PTY receiver and master) so systems using them
  are no longer pinned to the main thread.
- Split the redraw god-system into an ordered TerminalRedrawSet
  pipeline (widget render, material sync, deferred model load) gated by
  a shared frame-dirty flag.
- Render the terminal Vello scene inside the RenderGraph schedule's
  Begin set instead of RenderSystems::Prepare, ordering its submission
  with the frame's GPU work.
- Run the winit loop with WinitSettings::continuous() in both focus
  states and drop the Instant-based redraw throttle, so PTY output and
  animations are never paced by timers. Dirty-flag coalescing stays:
  each update drains all pending PTY chunks into one scene build.
@orhun

orhun commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Getting this while trying to run:

2026-06-14T22:48:36.481488Z ERROR bevy_render::error_handler: Caught rendering error: Validation Error

Caused by:
  In Device::create_bind_group, label = 'StandardMaterial'
    TextureView with '' label is invalid

2026-06-14T22:48:36.481532Z ERROR bevy_render::error_handler: Caught rendering error: Validation Error

Caused by:
  In Device::create_bind_group, label = 'sprite_material_bind_group'
    TextureView with '' label is invalid

2026-06-14T22:48:36.482422Z  INFO vello::wgpu_engine: Initialising shader modules in parallel using 14 threads
2026-06-14T22:48:36.643692Z ERROR bevy_render::error_handler: Quitting the application due to Validation RenderError
2026-06-14T22:48:36.702724Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702749Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702763Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702768Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702774Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702778Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702782Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702793Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702802Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702807Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702814Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702828Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702832Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702837Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702842Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702848Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702854Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702860Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702866Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702869Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702874Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702880Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702886Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702912Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702920Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702930Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702935Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702941Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?
2026-06-14T22:48:36.702950Z  WARN bevy_ecs::world::command_queue: CommandQueue has un-applied commands being dropped. Did you forget to call SystemState::apply?

…hanges

Rebuild the terminal texture on terminal-state changes or blink ticks
(4Hz) rather than every frame, and only take `get_mut` on the terminal
image and plane materials when the size, texture handle, or cull mode
actually changes, so Bevy does not re-extract and re-upload unchanged
assets. Gate the presentation, inline-object, Mobius, plane-warp, and
cursor-sync systems behind run conditions so they only run when their
state changes or for the relevant presentation mode.
0.3.1 snaps cell background fills to the pixel grid (fixing base-colour
bleed between cells with fractional cell sizes) and renders block
elements such as the half block as exact geometric fills instead of font
glyphs, which only filled the em box and left gaps in the taller cell.

Document why the terminal texture keeps linear filtering: it is mapped
onto transformable/scaled 3D geometry, so point sampling would pixelate
it; the seam fixes are now made at authoring time in parley_ratatui.
…esent texture

The terminal texture was a single Rgba8Unorm storage texture that also
exposed an Rgba8UnormSrgb view for sampling. wgpu rejects an sRGB view of
a storage texture, so on stricter backends bind-group creation failed
("TextureView with '' label is invalid") and the app quit.

Split it into two textures: Vello rasterizes into a plain Rgba8Unorm
storage render texture (no sRGB view), and each frame that is copied into
a separate Rgba8Unorm present texture that carries the Rgba8UnormSrgb view
the plane material and sprite sample. The present texture has no storage
usage, so the sRGB view is valid on every backend, and the copy is a
same-format texel copy. Colors are unchanged; the invalid combination is
gone by construction.

Inspired by linebender/bevy_vello, which keeps sRGB off the storage
texture.
@gold-silver-copper

Copy link
Copy Markdown
Contributor Author

@orhun

The error you got should be fixed, was a Vulkan vs Metal wgpu issue. Could you try rerunning to test, i dont have a decent linux machine here for it

The render/present texture split added an eighth parameter to
update_direct_terminal_frame, tripping clippy::too_many_arguments (8/7)
and failing `cargo clippy --all-targets -- -D warnings`. Group the two
handles into a TerminalImages struct, which also reads better at the call
site. No behavior change.
Both are only called internally by resize_to_fit; they don't need to be
part of the crate's public surface.
@gold-silver-copper

Copy link
Copy Markdown
Contributor Author

PR is ready for review here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wonky font scaling

2 participants