session 2: pmacs-gpu workspace + wgpu/winit/glyphon hello-world
Adds the pmacs-gpu binary crate to the workspace. wgpu 29.0 + winit 0.30 + glyphon 0.11 (cosmic-text 0.18 via re-export) + pollster + env_logger; pmacs-protocol in the dep graph but not consumed yet (session 3 wires the attach loop). The binary opens an 800x200 window titled 'pmacs-gpu hello-world', sets up wgpu against its surface, configures glyphon with the bundled JetBrains Mono Regular, and renders 'hello, pmacs' once per redraw. Close button or Escape exits. Resize re-configures the surface and glyphon viewport. Surface acquisition matches wgpu 29's CurrentSurfaceTexture enum (success/suboptimal render through; lost/ outdated re-configure; timeout/occluded skip the frame). Bundled assets: pmacs-gpu/fonts/JetBrainsMono-Regular.ttf (268 KB) and pmacs-gpu/fonts/OFL.txt. Font shipped as required by the SIL Open Font License 1.1. One finding surfaced during the move and absorbed under rule (iii) of the framing pass (small / no structural change): the design doc recorded JetBrains Mono as Apache 2.0; the actual license has been OFL since the family's open-source release. Doc corrected in docs/pmacs-gpu-design.md. Gates: cargo fmt + cargo clippy --all-targets -D warnings clean for the whole workspace; cargo test --lib still 1314 (pmacs main crate untouched); m4_acceptance 83; m11_5_semantic_acceptance --features crdt 2. Visual confirmation pending — agent environment is headless, so 'window opens, text renders' is user-side validation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
dab2a48bb0
commit
a706864e04
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
|
|
@ -1,10 +1,10 @@
|
|||
[workspace]
|
||||
# Session 1 of the pmacs-gpu arc: `pmacs-protocol` is the wire-types
|
||||
# crate the post-v1.0 frontend (`pmacs-gpu`) will consume directly. The
|
||||
# root `pmacs` package stays a workspace member (no file moves); the
|
||||
# new crate lives under `pmacs-protocol/`. See
|
||||
# `docs/pmacs-gpu-design.md`.
|
||||
members = [".", "pmacs-protocol"]
|
||||
# Session 1 of the pmacs-gpu arc landed `pmacs-protocol` as a workspace
|
||||
# member (the wire-types crate). Session 2 (this) adds `pmacs-gpu` as
|
||||
# the GPU/GUI frontend binary — wgpu/winit/cosmic-text/glyphon — with
|
||||
# hello-world rendering. Protocol consumption arrives in session 3.
|
||||
# See `docs/pmacs-gpu-design.md`.
|
||||
members = [".", "pmacs-protocol", "pmacs-gpu"]
|
||||
|
||||
[workspace.dependencies]
|
||||
# Shared between `pmacs` and `pmacs-protocol`. Pinned here so both
|
||||
|
|
|
|||
|
|
@ -273,8 +273,15 @@ Out of scope:
|
|||
Stance (γ): **bundle JetBrains Mono as default; expose Lua hook for
|
||||
override.**
|
||||
|
||||
- Bundled font: JetBrains Mono (Apache 2.0; broad glyph coverage;
|
||||
designed for code; ~1.5MB; dwarfed by `wgpu`'s footprint).
|
||||
- Bundled font: JetBrains Mono (SIL Open Font License 1.1; broad
|
||||
glyph coverage; designed for code; ~270 KB for the Regular weight,
|
||||
dwarfed by `wgpu`'s footprint). **Correction:** the framing pass
|
||||
recorded the license as Apache 2.0, but the actual license has
|
||||
been OFL since the family's open-source release. Session 2
|
||||
surfaced this when fetching the real asset for the bundle —
|
||||
classified as a small finding under rule (iii) and absorbed; the
|
||||
bundled `fonts/OFL.txt` is shipped alongside the TTF as required
|
||||
by the OFL.
|
||||
- Lua override: `pmacs.gpu.set_font(path)` or similar (precise binding
|
||||
shape decided session 2).
|
||||
- Missing-glyph fallback: tofu (replacement character `U+FFFD`).
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
[package]
|
||||
name = "pmacs-gpu"
|
||||
version = "0.0.1"
|
||||
edition = "2024"
|
||||
rust-version = "1.95"
|
||||
description = "GPU/GUI frontend for pmacs (session 2: hello-world). See docs/pmacs-gpu-design.md."
|
||||
license = "MIT OR Apache-2.0"
|
||||
authors = ["Pmacs contributors"]
|
||||
readme = "../README.md"
|
||||
repository = "https://git.levineuwirth.org/neuwirth/pmacs"
|
||||
homepage = "https://levineuwirth.org/essays/pmacs"
|
||||
keywords = ["editor", "emacs", "gpu", "gui"]
|
||||
categories = ["text-editors"]
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
name = "pmacs-gpu"
|
||||
path = "src/main.rs"
|
||||
|
||||
[lints.rust]
|
||||
unsafe_code = "forbid"
|
||||
missing_docs = "warn"
|
||||
|
||||
[lints.clippy]
|
||||
all = { level = "warn", priority = -1 }
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
# Allows: same set as the root `pmacs` crate, mirrored so the GPU
|
||||
# code doesn't trip lints the rest of the project doesn't either.
|
||||
module_name_repetitions = "allow"
|
||||
must_use_candidate = "allow"
|
||||
missing_errors_doc = "allow"
|
||||
missing_panics_doc = "allow"
|
||||
cast_possible_truncation = "allow"
|
||||
cast_sign_loss = "allow"
|
||||
cast_precision_loss = "allow"
|
||||
similar_names = "allow"
|
||||
multiple_crate_versions = "allow"
|
||||
|
||||
[dependencies]
|
||||
env_logger = "0.11.10"
|
||||
# Text shaping + GPU rendering. `glyphon` re-exports the `cosmic-text`
|
||||
# types it pins (`Buffer`, `Attrs`, `Family`, `FontSystem`,
|
||||
# `Metrics`, `Shaping`, `SwashCache`, ...); we use those re-exports
|
||||
# rather than depending on `cosmic-text` directly so the build never
|
||||
# ends up with two cosmic-text versions resolving to the same name.
|
||||
glyphon = "0.11.0"
|
||||
# Session 1's wire-types crate. Pulled in now so the dep graph is
|
||||
# settled from session 2 forward; protocol consumption itself lands
|
||||
# in session 3.
|
||||
pmacs-protocol = { version = "1.0.0", path = "../pmacs-protocol" }
|
||||
pollster = "0.4.0"
|
||||
wgpu = "29.0.3"
|
||||
winit = "0.30.13"
|
||||
Binary file not shown.
|
|
@ -0,0 +1,93 @@
|
|||
Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
|
|
@ -0,0 +1,350 @@
|
|||
//! pmacs-gpu — GPU/GUI frontend for pmacs.
|
||||
//!
|
||||
//! Session 2 of the pmacs-gpu arc (`docs/pmacs-gpu-design.md`):
|
||||
//! **hello-world binary**. Opens a window via `winit`, initializes
|
||||
//! `wgpu` against its surface, sets up `glyphon` text rendering with
|
||||
//! the bundled `JetBrains` Mono font, and renders "hello, pmacs" once
|
||||
//! per frame. No protocol consumption yet — that arrives in session
|
||||
//! 3 (the attach loop). No editor state, no input handling beyond
|
||||
//! close + Escape.
|
||||
//!
|
||||
//! The bundled font is `JetBrains` Mono Regular, distributed under
|
||||
//! the SIL Open Font License 1.1 (see `fonts/OFL.txt`). The design
|
||||
//! note incorrectly recorded the license as Apache 2.0; the actual
|
||||
//! license has been OFL since the family's open-source release. The
|
||||
//! design-doc note will be corrected as part of this session.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use glyphon::{
|
||||
Attrs, Buffer, Cache, Color, Family, FontSystem, Metrics, Resolution, Shaping, SwashCache,
|
||||
TextArea, TextAtlas, TextBounds, TextRenderer, Viewport,
|
||||
};
|
||||
use wgpu::MultisampleState;
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{ElementState, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::keyboard::{Key, NamedKey};
|
||||
use winit::window::{Window, WindowId};
|
||||
|
||||
/// Bundled font (SIL Open Font License 1.1 — see `fonts/OFL.txt`).
|
||||
const JETBRAINS_MONO: &[u8] = include_bytes!("../fonts/JetBrainsMono-Regular.ttf");
|
||||
|
||||
/// Initial window size in logical pixels. Session 2 is fixed-size for
|
||||
/// simplicity; resizes still work, this is just the boot dimension.
|
||||
const INITIAL_WIDTH: u32 = 800;
|
||||
const INITIAL_HEIGHT: u32 = 200;
|
||||
|
||||
/// Color the surface clears to before text renders.
|
||||
const BG: wgpu::Color = wgpu::Color {
|
||||
r: 0.05,
|
||||
g: 0.05,
|
||||
b: 0.07,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
/// Hello-world payload. Stays inert here — session 3 wires this to
|
||||
/// the daemon's `BufferSnapshot` instead.
|
||||
const HELLO_TEXT: &str = "hello, pmacs";
|
||||
|
||||
fn main() {
|
||||
// wgpu emits useful trace output on adapter selection / surface
|
||||
// configuration. The default `RUST_LOG=info` is fine for
|
||||
// development.
|
||||
env_logger::init();
|
||||
|
||||
let event_loop = EventLoop::new().expect("create winit event loop");
|
||||
let mut app = App { state: None };
|
||||
event_loop
|
||||
.run_app(&mut app)
|
||||
.expect("winit event loop run_app");
|
||||
}
|
||||
|
||||
/// Top-level application handler. Holds an `Option<State>` because
|
||||
/// winit 0.30 requires the window + GPU resources to be created
|
||||
/// *after* `resumed()` fires, not at `main()` start.
|
||||
struct App {
|
||||
state: Option<State>,
|
||||
}
|
||||
|
||||
/// All resources owned by a single running pmacs-gpu instance: the
|
||||
/// window, the wgpu device/queue/surface, the glyphon stack, and the
|
||||
/// shaped text buffer.
|
||||
struct State {
|
||||
window: Arc<Window>,
|
||||
|
||||
// wgpu plumbing.
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
surface: wgpu::Surface<'static>,
|
||||
config: wgpu::SurfaceConfiguration,
|
||||
|
||||
// glyphon plumbing.
|
||||
font_system: FontSystem,
|
||||
swash_cache: SwashCache,
|
||||
viewport: Viewport,
|
||||
atlas: TextAtlas,
|
||||
text_renderer: TextRenderer,
|
||||
buffer: Buffer,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
if self.state.is_some() {
|
||||
// `resumed` can fire more than once on platforms that
|
||||
// suspend/restore (e.g. mobile). The hello-world doesn't
|
||||
// reinitialize on resume; first call wins.
|
||||
return;
|
||||
}
|
||||
self.state = Some(State::new(event_loop));
|
||||
}
|
||||
|
||||
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
|
||||
let Some(state) = self.state.as_mut() else {
|
||||
return;
|
||||
};
|
||||
match event {
|
||||
WindowEvent::CloseRequested
|
||||
| WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: Key::Named(NamedKey::Escape),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => event_loop.exit(),
|
||||
WindowEvent::Resized(size) => state.resize(size.width.max(1), size.height.max(1)),
|
||||
WindowEvent::RedrawRequested => state.render(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn new(event_loop: &ActiveEventLoop) -> Self {
|
||||
let window = Arc::new(
|
||||
event_loop
|
||||
.create_window(
|
||||
Window::default_attributes()
|
||||
.with_title("pmacs-gpu hello-world")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(
|
||||
f64::from(INITIAL_WIDTH),
|
||||
f64::from(INITIAL_HEIGHT),
|
||||
)),
|
||||
)
|
||||
.expect("create window"),
|
||||
);
|
||||
|
||||
// wgpu instance + surface.
|
||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_without_display_handle());
|
||||
let surface = instance
|
||||
.create_surface(window.clone())
|
||||
.expect("create surface");
|
||||
|
||||
// Pick an adapter that supports our surface. Power preference
|
||||
// = LowPower because the hello-world has no GPU appetite;
|
||||
// saves laptop battery during development.
|
||||
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::LowPower,
|
||||
compatible_surface: Some(&surface),
|
||||
force_fallback_adapter: false,
|
||||
}))
|
||||
.expect("request_adapter");
|
||||
|
||||
let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
|
||||
label: Some("pmacs-gpu device"),
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits::default(),
|
||||
..wgpu::DeviceDescriptor::default()
|
||||
}))
|
||||
.expect("request_device");
|
||||
|
||||
// Configure the surface. Pick the first format the surface
|
||||
// and adapter both like; glyphon handles colorspace conversion
|
||||
// internally, so sRGB vs UNORM is the renderer's concern, not
|
||||
// ours at this layer.
|
||||
let inner_size = window.inner_size();
|
||||
let surface_caps = surface.get_capabilities(&adapter);
|
||||
let surface_format = surface_caps
|
||||
.formats
|
||||
.iter()
|
||||
.copied()
|
||||
.find(wgpu::TextureFormat::is_srgb)
|
||||
.unwrap_or(surface_caps.formats[0]);
|
||||
let config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: surface_format,
|
||||
width: inner_size.width.max(1),
|
||||
height: inner_size.height.max(1),
|
||||
present_mode: wgpu::PresentMode::Fifo,
|
||||
desired_maximum_frame_latency: 2,
|
||||
alpha_mode: surface_caps.alpha_modes[0],
|
||||
view_formats: vec![],
|
||||
};
|
||||
surface.configure(&device, &config);
|
||||
|
||||
// glyphon plumbing — `FontSystem` owns the font database and
|
||||
// shaper state; we register the bundled JetBrains Mono before
|
||||
// anything tries to shape with it.
|
||||
let mut font_system = FontSystem::new();
|
||||
font_system.db_mut().load_font_data(JETBRAINS_MONO.to_vec());
|
||||
let swash_cache = SwashCache::new();
|
||||
let cache = Cache::new(&device);
|
||||
let mut viewport = Viewport::new(&device, &cache);
|
||||
viewport.update(
|
||||
&queue,
|
||||
Resolution {
|
||||
width: config.width,
|
||||
height: config.height,
|
||||
},
|
||||
);
|
||||
let mut atlas = TextAtlas::new(&device, &queue, &cache, surface_format);
|
||||
let text_renderer =
|
||||
TextRenderer::new(&mut atlas, &device, MultisampleState::default(), None);
|
||||
|
||||
// Shape "hello, pmacs" with JetBrains Mono at a comfortable
|
||||
// hello-world size. cosmic-text 0.18 (glyphon's pinned
|
||||
// version) threads `&mut FontSystem` through every Buffer
|
||||
// mutator that needs to re-shape; the 5th `None` on `set_text`
|
||||
// is the optional `Align` (we let cosmic-text default it).
|
||||
let mut buffer = Buffer::new(&mut font_system, Metrics::new(48.0, 56.0));
|
||||
buffer.set_size(
|
||||
&mut font_system,
|
||||
Some(config.width as f32),
|
||||
Some(config.height as f32),
|
||||
);
|
||||
buffer.set_text(
|
||||
&mut font_system,
|
||||
HELLO_TEXT,
|
||||
&Attrs::new().family(Family::Name("JetBrains Mono")),
|
||||
Shaping::Advanced,
|
||||
None,
|
||||
);
|
||||
buffer.shape_until_scroll(&mut font_system, false);
|
||||
|
||||
Self {
|
||||
window,
|
||||
device,
|
||||
queue,
|
||||
surface,
|
||||
config,
|
||||
font_system,
|
||||
swash_cache,
|
||||
viewport,
|
||||
atlas,
|
||||
text_renderer,
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reconfigure surface + glyphon viewport on window-size change.
|
||||
/// The shaped text buffer also gets a new max-size so wrap and
|
||||
/// scroll align with the new viewport.
|
||||
fn resize(&mut self, width: u32, height: u32) {
|
||||
self.config.width = width;
|
||||
self.config.height = height;
|
||||
self.surface.configure(&self.device, &self.config);
|
||||
self.viewport
|
||||
.update(&self.queue, Resolution { width, height });
|
||||
self.buffer.set_size(
|
||||
&mut self.font_system,
|
||||
Some(width as f32),
|
||||
Some(height as f32),
|
||||
);
|
||||
self.window.request_redraw();
|
||||
}
|
||||
|
||||
/// One frame: clear the surface to `BG`, render the text buffer,
|
||||
/// present. Acquisition failures cause a re-configure and skip
|
||||
/// the frame (a typical recovery for transient surface losses).
|
||||
fn render(&mut self) {
|
||||
// wgpu 29 collapses success/error into a single enum
|
||||
// (`CurrentSurfaceTexture`), not `Result<SurfaceTexture, SurfaceError>`
|
||||
// as earlier versions did. Lost / Outdated trigger a
|
||||
// re-configure and skip the frame; Suboptimal is rendered
|
||||
// through but flagged for the next configure cycle (we don't
|
||||
// act on it in the hello-world).
|
||||
let frame = match self.surface.get_current_texture() {
|
||||
wgpu::CurrentSurfaceTexture::Success(frame)
|
||||
| wgpu::CurrentSurfaceTexture::Suboptimal(frame) => frame,
|
||||
wgpu::CurrentSurfaceTexture::Lost | wgpu::CurrentSurfaceTexture::Outdated => {
|
||||
self.surface.configure(&self.device, &self.config);
|
||||
return;
|
||||
}
|
||||
wgpu::CurrentSurfaceTexture::Timeout | wgpu::CurrentSurfaceTexture::Occluded => {
|
||||
// `Timeout`: transient acquisition stall — drop this
|
||||
// frame, try again next redraw.
|
||||
// `Occluded`: window minimized / behind another
|
||||
// window — skip the frame, save the GPU work.
|
||||
return;
|
||||
}
|
||||
wgpu::CurrentSurfaceTexture::Validation => {
|
||||
eprintln!("surface acquisition raised a validation error");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let view = frame
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
self.text_renderer
|
||||
.prepare(
|
||||
&self.device,
|
||||
&self.queue,
|
||||
&mut self.font_system,
|
||||
&mut self.atlas,
|
||||
&self.viewport,
|
||||
[TextArea {
|
||||
buffer: &self.buffer,
|
||||
left: 24.0,
|
||||
top: 60.0,
|
||||
scale: 1.0,
|
||||
bounds: TextBounds {
|
||||
left: 0,
|
||||
top: 0,
|
||||
// Surface dimensions are u32 but `TextBounds`
|
||||
// is i32; `cast_signed` keeps the bit pattern
|
||||
// and is correct for typical window sizes well
|
||||
// below 2^31.
|
||||
right: self.config.width.cast_signed(),
|
||||
bottom: self.config.height.cast_signed(),
|
||||
},
|
||||
default_color: Color::rgb(230, 230, 235),
|
||||
custom_glyphs: &[],
|
||||
}],
|
||||
&mut self.swash_cache,
|
||||
)
|
||||
.expect("text_renderer prepare");
|
||||
|
||||
let mut encoder = self
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("pmacs-gpu frame encoder"),
|
||||
});
|
||||
{
|
||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("pmacs-gpu hello-world pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
depth_slice: None,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(BG),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
occlusion_query_set: None,
|
||||
timestamp_writes: None,
|
||||
multiview_mask: None,
|
||||
});
|
||||
self.text_renderer
|
||||
.render(&self.atlas, &self.viewport, &mut pass)
|
||||
.expect("text_renderer render");
|
||||
}
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
frame.present();
|
||||
self.atlas.trim();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue