mirage-engine 0.1.1

Mirage, an immediate-mode 3D engine for simple games on desktop and the browser
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
use core::time::Duration;
use std::collections::HashMap;
#[cfg(feature = "ui")]
use std::sync::Arc;

#[cfg(feature = "ui")]
use crate::Error;
use crate::animation::{AnimationStates, Animator};
use crate::assets::Assets;
#[cfg(feature = "ui")]
use crate::assets::font;
use crate::input::{
    Axis2Binding, AxisBinding, ButtonBinding, Cursor, Input, InputAction, InputAxis2Action,
    InputAxisAction, InputButtonAction, Ticking,
};
use crate::math::{UVec2, Vec2};
use crate::mesh::{Instance, Mesh, Part};
use crate::post_effect::{PostEffect, PostEffectId, PostEffects};
use crate::renderer::Renderer;
use crate::renderer::mesh_cache::MeshCache;
use crate::save::{SaveKey, Saved};
use crate::sound::{Audio, SoundCue};
use crate::surface_style::{SurfaceStyle, SurfaceStyleId, SurfaceStyles};
use crate::time::{FrameTime, TickInterval};
use crate::ui::{Claims, Layer, Overlay};
use crate::{Camera, Config, Game, Holds, Light, View};

/// The work the startup closure [`run`](crate::run) takes may do.
pub struct InitContext<'a, G: Game> {
    startup: Startup<'a>,
    renderer: &'a mut Renderer<G::Meshes, G::Skyboxes>,
    audio: &'a mut Audio<G::Sounds>,
}

impl<'a, G: Game> InitContext<'a, G> {
    pub(crate) fn new(
        engine: Engine<'a>,
        renderer: &'a mut Renderer<G::Meshes, G::Skyboxes>,
        audio: &'a mut Audio<G::Sounds>,
        saves: &'a Saved,
        assets: &'a Assets,
        overlay: &'a Overlay,
    ) -> Self {
        Self {
            startup: Startup::new(engine, saves, assets, overlay),
            renderer,
            audio,
        }
    }

    /// The startup work that is typed by no vocabulary of the game's own.
    ///
    /// Required if you want to start two games the same way: `Startup` is
    /// one type whatever the game, so code shared between them takes
    /// `&mut Startup<'_>`.
    pub fn startup(&mut self) -> &mut Startup<'a> {
        &mut self.startup
    }

    /// How long `sound` plays for at its own pitch: the whole clip, before
    /// any trim.
    ///
    /// Required if you want to time a game against a sound. Every value the
    /// vocabulary catalogs is built before this runs, and a streamed one
    /// reports what the decode at startup measured.
    pub fn duration(&mut self, sound: G::Sounds) -> Duration {
        self.audio.duration(&sound)
    }

    /// How long each value that the vocabulary catalogs plays for, keyed by the
    /// value.
    ///
    /// Required if you want to time a game against a whole vocabulary. A
    /// value the catalog leaves out is absent from the map; read that one
    /// through [`duration`](Self::duration).
    pub fn durations(&mut self) -> HashMap<G::Sounds, Duration> {
        self.audio.durations()
    }

    /// Builds and uploads `mesh` now, instead of on its first draw; the
    /// copy is then held like any drawn mesh's under
    /// [`Config::with_mesh_memory`], and no longer than that.
    ///
    /// Takes a mesh of [`Game::Meshes`](crate::Game::Meshes) and no other.
    pub fn prepare<M>(&mut self, mesh: M)
    where
        G::Meshes: Holds<M> + From<M>,
    {
        let engine = &self.startup.engine;
        self.renderer
            .prepare(engine.device, engine.queue, mesh.into());
    }
}

/// The part of [`InitContext`] that is typed by no vocabulary of the game's
/// own, which [`InitContext::startup`] returns.
pub struct Startup<'a> {
    engine: Engine<'a>,
    saves: &'a Saved,
    #[cfg(feature = "ui")]
    assets: &'a Assets,
    #[cfg(feature = "ui")]
    overlay: &'a Overlay,
}

impl<'a> Startup<'a> {
    /// What a startup reads: the values a run saved, the store its sources
    /// loaded into, and the UI.
    #[cfg(feature = "ui")]
    pub(crate) fn new(
        engine: Engine<'a>,
        saves: &'a Saved,
        assets: &'a Assets,
        overlay: &'a Overlay,
    ) -> Self {
        Self {
            engine,
            saves,
            assets,
            overlay,
        }
    }

    /// The same without the UI, which is the one reader of a font.
    #[cfg(not(feature = "ui"))]
    pub(crate) fn new(
        engine: Engine<'a>,
        saves: &'a Saved,
        _assets: &'a Assets,
        _overlay: &'a Overlay,
    ) -> Self {
        Self { engine, saves }
    }

    /// The value the last run to save `key` kept, or its fallback where
    /// none did, or where what was kept no longer reads as the key's own
    /// value, with a debug log.
    pub fn saved<K: SaveKey>(&self, key: K) -> K::Value {
        self.saves.read(key)
    }

    /// The window's drawing area, in physical pixels; zero while minimized.
    pub fn window_size(&self) -> UVec2 {
        self.engine.window_size
    }

    /// The configuration the engine started with.
    pub fn config(&self) -> &Config {
        self.engine.config
    }

    /// The font a source loaded under `name`, to name in an
    /// `egui::FontDefinitions`.
    ///
    /// Required if you want the UI to draw in a game's own font: a source is
    /// a `.ttf` or an `.otf` [`Config::with_assets`] loads, named by its
    /// stem. A name no source loaded a font under, or one two sources share,
    /// is an error. The `ui` feature's own.
    #[cfg(feature = "ui")]
    pub fn font(&self, name: &str) -> Result<egui::FontData, Error> {
        let bytes = self.assets.font(name)?;

        Ok(egui::FontData::from_owned(bytes.to_vec()))
    }

    /// Draws every frame's UI in `fonts`, from the first frame on.
    ///
    /// Required if you want the UI to draw in a game's own font. Fails,
    /// naming the font, where an entry of `font_data` does not decode at its
    /// own index, and where a family lists a name `font_data` does not hold.
    /// egui's own fonts stay wherever `fonts` keeps them, and the last call
    /// is what draws. The `ui` feature's own.
    #[cfg(feature = "ui")]
    pub fn set_fonts(&mut self, fonts: egui::FontDefinitions) -> Result<(), Error> {
        for (name, data) in &fonts.font_data {
            font::decode_at(&data.font, data.index)
                .map_err(|error| Error::msg(format!("the font `{name}` {error}")))?;
        }
        for (family, names) in &fonts.families {
            for name in names {
                if !fonts.font_data.contains_key(name) {
                    return Err(Error::msg(format!(
                        "the font family `{family}` lists `{name}`, which `font_data` does \
                         not hold"
                    )));
                }
            }
        }
        self.overlay.set_fonts(fonts);

        Ok(())
    }
}

/// The work [`Game::tick`](crate::Game::tick) may do: simulate, never draw.
pub struct TickContext<'a, G: Game> {
    engine: Engine<'a>,
    meshes: &'a mut MeshCache<G::Meshes>,
    audio: &'a mut Audio<G::Sounds>,
    ticking: &'a Ticking<'a>,
    saves: &'a mut Saved,
    run: &'a mut Run,
    dt: Duration,
    /// The run clock at the frame these ticks belong to, which every draw
    /// of that frame is posed at too.
    elapsed: Duration,
    claims: Claims,
}

impl<'a, G: Game> TickContext<'a, G> {
    /// Whether `action` is held right now.
    ///
    /// Every tick of one drawn frame reads the same controls.
    pub fn down<A: InputButtonAction>(&self, action: A) -> bool
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.ticking.down(action)
    }

    /// Whether `action` went down since the last frame whose ticks ran.
    ///
    /// A frame that runs no ticks holds its edges for the ticks that follow,
    /// so every press is read by the ticks of exactly one frame.
    pub fn pressed<A: InputButtonAction>(&self, action: A) -> bool
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.ticking.pressed(action)
    }

    /// Whether `action` came up since the last frame whose ticks ran.
    ///
    /// A release is held for the ticks that follow, the same as a press.
    pub fn released<A: InputButtonAction>(&self, action: A) -> bool
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.ticking.released(action)
    }

    /// Presses of `action` in a row, counting the one these ticks read:
    /// `1` for a single click, `2` for a double, `0` where they read no
    /// press; see [`FrameContext::clicks`].
    ///
    /// The ticks of one frame read one press however many landed in them,
    /// so a control pressed twice inside one frame reads one press of two
    /// clicks.
    pub fn clicks<A: InputButtonAction>(&self, action: A) -> u32
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.ticking.clicks(action)
    }

    /// Analog reading of `action`: a fraction in `-1..=1` from a pad axis,
    /// a joystick axis or a button composite, of which a trigger reads
    /// `0..=1`, and the scaled distance, which nothing clamps, from a
    /// [`PointerDelta`](crate::PointerDelta) or
    /// [`WheelDelta`](crate::WheelDelta) lane.
    ///
    /// Read those two in [`Game::frame`](crate::Game::frame): the ticks of
    /// one frame each read the whole distance that frame moved.
    pub fn axis<A: InputAxisAction>(&self, action: A) -> f32
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.ticking.axis(action)
    }

    /// `action`'s reading: a vector no longer than `1` from a stick or a
    /// button composite, and the scaled distance, which nothing clamps,
    /// from [`Axis2Binding::pointer`].
    ///
    /// Read the pointer in [`Game::frame`](crate::Game::frame): the ticks
    /// of one frame each read the whole distance that frame moved.
    pub fn axis2<A: InputAxis2Action>(&self, action: A) -> Vec2
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.ticking.axis2(action)
    }

    /// Pointer position, in physical pixels from the drawing area's top
    /// left; the origin until it is first seen.
    ///
    /// The mouse and the first touch share it, and
    /// [`window_size`](Self::window_size) is in the same pixels, so
    /// [`Camera::ray_through`] takes it as it is.
    pub fn pointer(&self) -> Vec2 {
        self.ticking.pointer()
    }

    /// Plays `sound` once, keeping wherever it is placed as of this tick.
    ///
    /// Every call is a voice of its own, so the same sound twice over is
    /// heard twice; the ticks of one frame are played together at the end
    /// of it.
    pub fn play(&mut self, sound: impl Into<SoundCue<G::Sounds>>) {
        self.audio.play(sound.into());
    }

    /// The value the last run to save `key` kept, or its fallback where
    /// none did, or where what was kept no longer reads as the key's own
    /// value, with a debug log.
    pub fn saved<K: SaveKey>(&self, key: K) -> K::Value {
        self.saves.read(key)
    }

    /// Keeps `value` under `key`, for the rest of this run and the runs
    /// after it.
    ///
    /// The store is written once the frame these ticks belong to is drawn,
    /// and only where a value changed, so saving every tick costs nothing.
    pub fn save<K: SaveKey>(&mut self, key: K, value: K::Value) {
        self.saves.write(key, value);
    }

    /// Whether the platform allows sound to start right now; see
    /// [`FrameContext::sound_unlocked`].
    pub fn sound_unlocked(&self) -> bool {
        self.audio.unlocked()
    }

    /// Runs `animator` up to this tick against `input`, over the clips
    /// `mesh` holds: where its state goes from here, and what the state it
    /// lands in plays.
    ///
    /// Takes a mesh of [`Game::Meshes`](crate::Game::Meshes) and a machine
    /// typed by that mesh, so a machine runs on the clips of the value the
    /// game draws and no other. Every tick of one drawn
    /// frame runs at one instant, as every one of them reads one snapshot
    /// of the controls, and the frame draws at that same instant.
    pub fn animate<M, P, S>(&mut self, mesh: M, animator: &mut Animator<M, S>, input: &S::Input)
    where
        G::Meshes: Holds<M> + From<M>,
        M: Mesh<P, S::Clip>,
        P: Part,
        S: AnimationStates,
    {
        let now = self.elapsed;
        animator.animate(input, now, self.meshes.clips(mesh));
    }

    /// This tick's fixed time step: [`Config::tick_interval`] until
    /// [`set_tick_interval`](Self::set_tick_interval) changes it.
    pub fn dt(&self) -> Duration {
        self.dt
    }

    /// Sets the simulated time every later tick covers; see
    /// [`FrameContext::set_tick_interval`].
    pub fn set_tick_interval(&mut self, interval: Duration) {
        self.run.tick_interval.set(interval);
    }

    /// Ends the run once the frame these ticks belong to is drawn; see
    /// [`FrameContext::close`].
    pub fn close(&mut self) {
        self.run.closing = true;
    }

    /// Whether the UI took the pointer last frame. Always false without the
    /// `ui` feature.
    pub fn ui_wants_pointer(&self) -> bool {
        self.claims.pointer
    }

    /// Whether the UI took the keyboard last frame. Always false without the
    /// `ui` feature.
    pub fn ui_wants_keyboard(&self) -> bool {
        self.claims.keyboard
    }

    /// The window's drawing area, in physical pixels; zero while minimized.
    pub fn window_size(&self) -> UVec2 {
        self.engine.window_size
    }

    /// The camera the last drawn frame was viewed from; [`Camera::default`]
    /// before the first frame.
    ///
    /// Required if you want a ray through a pixel: the player points at what
    /// was last drawn.
    pub fn last_camera(&self) -> Camera {
        self.engine.last_camera
    }

    /// The configuration the engine started with.
    pub fn config(&self) -> &Config {
        self.engine.config
    }
}

/// The work [`Game::frame`](crate::Game::frame) may do.
pub struct FrameContext<'a, G: Game> {
    engine: Engine<'a>,
    renderer: &'a mut Renderer<G::Meshes, G::Skyboxes>,
    audio: &'a mut Audio<G::Sounds>,
    input: &'a mut Input,
    saves: &'a mut Saved,
    run: &'a mut Run,
    time: FrameTime,
    layer: Layer<'a>,
}

impl<'a, G: Game> FrameContext<'a, G> {
    /// Whether `action` is held right now.
    pub fn down<A: InputButtonAction>(&self, action: A) -> bool
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.input.down(action)
    }

    /// Whether `action` went down during this frame.
    pub fn pressed<A: InputButtonAction>(&self, action: A) -> bool
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.input.pressed(action)
    }

    /// Whether `action` came up during this frame.
    pub fn released<A: InputButtonAction>(&self, action: A) -> bool
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.input.released(action)
    }

    /// Presses of `action` in a row, counting this frame's: `1` for a
    /// single click, `2` for a double, and `0` on a frame where `action`
    /// was not pressed.
    ///
    /// A press counts with the one before it where the same control took
    /// both, no later than the double click interval after it — the
    /// platform's own until
    /// [`Config::with_double_click_interval`](crate::Config::with_double_click_interval)
    /// sets one. The engine counts one control at a time, so an action
    /// whose press lands in the same frame as another control's reads `0`
    /// where the count is on that other one. A headless session counts
    /// against the clock its caller drives, so presses at one instant of it
    /// count together.
    pub fn clicks<A: InputButtonAction>(&self, action: A) -> u32
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.input.clicks(action)
    }

    /// Analog reading of `action`: a fraction in `-1..=1` from a pad axis,
    /// a joystick axis or a button composite, of which a trigger reads
    /// `0..=1`, and the scaled distance, which nothing clamps, from a
    /// [`PointerDelta`](crate::PointerDelta) or
    /// [`WheelDelta`](crate::WheelDelta) lane.
    pub fn axis<A: InputAxisAction>(&self, action: A) -> f32
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.input.axis(action)
    }

    /// `action`'s reading: a vector no longer than `1` from a stick or a
    /// button composite, and the scaled distance, which nothing clamps,
    /// from [`Axis2Binding::pointer`].
    pub fn axis2<A: InputAxis2Action>(&self, action: A) -> Vec2
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.input.axis2(action)
    }

    /// Pointer position, in physical pixels from the drawing area's top
    /// left; the origin until it is first seen.
    ///
    /// The mouse and the first touch share it, and
    /// [`window_size`](Self::window_size) is in the same pixels, so
    /// [`Camera::ray_through`] takes it as it is.
    pub fn pointer(&self) -> Vec2 {
        self.input.pointer()
    }

    /// Draws the pointer as `cursor` this frame; the last call in a frame
    /// is the one it draws.
    ///
    /// A frame that never calls this draws it as [`Cursor::Arrow`]. Where
    /// the UI sets a cursor of its own, the UI's is drawn instead.
    ///
    /// [`Cursor::Held`] holds the pointer in place, and the UI's own
    /// cursor does not take `Held` over: [`pointer`](Self::pointer) reads
    /// the place it was held at, a [`PointerDelta`](crate::PointerDelta)
    /// binding keeps reading how far it moves, and the first frame to set
    /// another cursor releases it. A browser takes the pointer lock only from
    /// inside a gesture of the player's, so there the hold is taken on the
    /// player's next press or touch. A window loses the hold as it loses
    /// focus, and takes it again on the first frame to set `Held` after the
    /// focus returns.
    pub fn set_cursor(&mut self, cursor: Cursor) {
        *self.layer.cursor = cursor;
    }

    /// Binds `action` to `bindings` for the rest of the run, and keeps it
    /// for the runs after that — written when the frame ends, and only
    /// when these bindings are not already what `action` is bound to.
    ///
    /// The kind of binding follows the action, so a stick cannot be bound to
    /// a button.
    pub fn rebind<A: InputAction>(&mut self, action: A, bindings: Vec<A::Binding>)
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.input.rebind(action, bindings);
    }

    /// The bindings `action` reads through right now, which a controls menu
    /// shows through each binding's text.
    pub fn bindings<A: InputAction>(&self, action: A) -> Vec<A::Binding>
    where
        G::InputActions: Holds<A, A::Binding>,
    {
        self.input.bindings(action)
    }

    /// The button control the player pressed this frame, for a controls menu
    /// listening for one to bind.
    ///
    /// Nothing where the player pressed nothing new; polling is the whole
    /// mechanism, so a menu that stops calling this stops listening.
    pub fn actuated_button(&self) -> Option<ButtonBinding> {
        self.input.actuated_button()
    }

    /// The analog control the player pushed this frame, past the deadzone a
    /// binding starts with.
    ///
    /// The pointer and the wheel are never returned: they would take the
    /// smallest nudge for a choice.
    pub fn actuated_axis(&self) -> Option<AxisBinding> {
        self.input.actuated_axis()
    }

    /// The stick the player pushed this frame, past the deadzone a binding
    /// starts with.
    pub fn actuated_axis2(&self) -> Option<Axis2Binding> {
        self.input.actuated_axis2()
    }

    /// Duration of the previous frame: this frame's own variable time
    /// step, distinct from the fixed one `tick` runs at.
    pub fn dt(&self) -> Duration {
        self.time.dt
    }

    /// Duration the game has been running.
    pub fn elapsed(&self) -> Duration {
        self.time.elapsed
    }

    /// This frame's position into the next simulation step, a fraction in
    /// `0.0..1.0`; used to draw between two tick states.
    pub fn alpha(&self) -> f32 {
        self.time.alpha
    }

    /// Sets the simulated time every later tick covers, from the next frame
    /// on; held to at least `Duration::from_micros(1)`.
    ///
    /// Required if you want to pace the simulation against something outside
    /// the engine, such as a program on another machine. The ticks a frame
    /// already runs keep the step they started with.
    pub fn set_tick_interval(&mut self, interval: Duration) {
        self.run.tick_interval.set(interval);
    }

    /// Ends the run once this frame is drawn: what the frame saved is
    /// written, and no tick or frame runs after it.
    ///
    /// On the desktop the window closes and [`run`](crate::run) returns. In
    /// the browser there is no program to end: the loop stops, and the
    /// canvas the engine created is dropped from the page, while one the
    /// game named through [`Config::with_canvas_id`] stays as the page left
    /// it.
    pub fn close(&mut self) {
        self.run.closing = true;
    }

    /// Views the rest of the frame from `camera`. A later call in the same
    /// frame replaces this one; without any, [`Camera::default`] is used.
    pub fn set_camera(&mut self, camera: Camera) {
        self.renderer.set_camera(camera);
    }

    /// Draws `instance` this frame. The engine decides the order and which
    /// draws share GPU work.
    ///
    /// Takes a draw of a mesh of [`Game::Meshes`](crate::Game::Meshes) and
    /// no other.
    pub fn draw<M>(&mut self, instance: Instance<M, G::SurfaceStyles>)
    where
        G::Meshes: Holds<M> + From<M>,
    {
        self.renderer.draw(instance.record().into_set());
    }

    /// Passes the style `T` the values its WGSL reads this frame; the last
    /// call for a style is the one it reads.
    ///
    /// A frame that never calls this for a style leaves it reading the
    /// default value of every field. Takes a style of
    /// [`Game::SurfaceStyles`](crate::Game::SurfaceStyles) and no other.
    pub fn set_surface_style<T: SurfaceStyle>(&mut self, style: T)
    where
        G::SurfaceStyles: Holds<T> + From<T>,
    {
        let style = G::SurfaceStyles::from(style);
        self.renderer
            .set_surface_style(SurfaceStyleId(style.seat()), &style);
    }

    /// Runs the post effect `T` over this frame with the values its WGSL
    /// reads; the last call for an effect is the one it runs with.
    ///
    /// A frame that never calls this for an effect runs no pass for it.
    /// Takes an effect of
    /// [`Game::PostEffects`](crate::Game::PostEffects) and no other.
    pub fn set_post_effect<T: PostEffect>(&mut self, effect: T)
    where
        G::PostEffects: Holds<T> + From<T>,
    {
        let effect = G::PostEffects::from(effect);
        self.renderer
            .set_post_effect(PostEffectId(effect.seat()), &effect);
    }

    /// Draws and lights this frame by `sky`: what it draws where nothing
    /// else was drawn, and the ambient — the light every surface takes from
    /// every direction.
    ///
    /// Takes a value of [`Game::Skyboxes`](crate::Game::Skyboxes) and no
    /// other; startup built every one of them. The last call in a frame is
    /// the one it draws, and a frame that never calls this draws and is lit
    /// by the default sky.
    pub fn set_skybox(&mut self, sky: G::Skyboxes) {
        self.renderer
            .set_skybox(self.engine.device, self.engine.queue, &sky);
    }

    /// Lights the frame with `light`, in addition to any already submitted.
    ///
    /// The first call replaces the default environment's light; past
    /// [`MAX_LIGHTS`](crate::MAX_LIGHTS), excess is ignored — warned the
    /// first frame, a debug log after.
    pub fn light(&mut self, light: Light) {
        self.renderer.light(light);
    }

    /// Scales the frame's light before the curve, as a fraction of it; a
    /// value under `0.0` is clamped to it, and `1.0` is used where a frame
    /// never calls this.
    ///
    /// The last call in a frame replaces the rest.
    pub fn set_exposure(&mut self, exposure: f32) {
        self.renderer.set_exposure(exposure);
    }

    /// Spreads the frame's brightest light over what is around it; the
    /// value is clamped into `0.0..=1.0`, and `0.0` is used where a frame
    /// never calls this.
    ///
    /// The value is the fraction of the frame the spread replaces, and `0.0`
    /// is no work at all. The last call in a frame replaces the rest.
    pub fn set_bloom(&mut self, amount: f32) {
        self.renderer.set_bloom(amount);
    }

    /// Plays `sound` once, keeping wherever it is placed as of this frame.
    ///
    /// Every call is a voice of its own, so the same sound twice over is
    /// heard twice.
    pub fn play(&mut self, sound: impl Into<SoundCue<G::Sounds>>) {
        self.audio.play(sound.into());
    }

    /// Keeps `sound` playing while frames go on declaring it, and fades it
    /// out over its fade once one does not.
    ///
    /// What a frame declares is the whole of what it wants sounding. One
    /// voice per value, whose knobs follow what each frame declares; the
    /// last call for a value in a frame is the one that counts. A value a
    /// frame stops declaring is dropped once it has faded, so declaring it
    /// again after that starts it at its window start.
    pub fn sustain(&mut self, sound: impl Into<SoundCue<G::Sounds>>) {
        self.audio.sustain(sound.into());
    }

    /// Hears the frame from `listener`, in place of the frame's camera.
    pub fn set_listener(&mut self, listener: View) {
        self.audio.set_listener(listener);
    }

    /// Plays everything this frame at `volume`, the fraction of each
    /// sound's own gain it multiplies; `1.0` where a frame never calls this.
    ///
    /// The mix slides to it over [`SoundCue::DEFAULT_GLIDE`], so a slider a
    /// player moves never steps the sound.
    pub fn set_volume(&mut self, volume: f32) {
        self.audio.set_volume(volume);
    }

    /// Whether the platform allows sound to start right now.
    ///
    /// True from the first frame on the desktop, with or without an audio
    /// device: what holds it false is the browser, which plays nothing until
    /// the player has done something. A one-shot played while it is false is
    /// dropped; a sustain declared then starts when it turns true.
    pub fn sound_unlocked(&self) -> bool {
        self.audio.unlocked()
    }

    /// The value the last run to save `key` kept, or its fallback where
    /// none did, or where what was kept no longer reads as the key's own
    /// value, with a debug log.
    pub fn saved<K: SaveKey>(&self, key: K) -> K::Value {
        self.saves.read(key)
    }

    /// Keeps `value` under `key`, for the rest of this run and the runs
    /// after it.
    ///
    /// The store is written once the frame is drawn, and only where a value
    /// changed, so saving every frame costs nothing.
    pub fn save<K: SaveKey>(&mut self, key: K, value: K::Value) {
        self.saves.write(key, value);
    }

    /// Builds this frame's UI, drawn over the scene.
    ///
    /// Calls append to one layer, which is placed `8` points clear of the
    /// window's edges; floating windows go through `ui.ctx()`, and
    /// `egui::Panel::left` and the three beside it hold a panel against one
    /// side of that layer, shown in the `ui` this call takes.
    #[cfg(feature = "ui")]
    pub fn ui(&mut self, build: impl FnOnce(&mut egui::Ui)) {
        build(&mut *self.layer.ui);
    }

    /// Physical pixels per logical point of the UI this frame, a fraction
    /// over `1.0` on a dense screen: what a pixel [`Camera::pixel_of`]
    /// returns is divided by before the UI draws at it.
    #[cfg(feature = "ui")]
    pub fn pixels_per_point(&self) -> f32 {
        self.layer.pixels_per_point
    }

    /// `text` laid out in `font` at no width, so a row ends only where a
    /// `\n` starts the next one.
    ///
    /// Required if you want to size or place what you draw against text: a
    /// box a name has to fit in, a line as wide as the word over it. Its
    /// `size()` is the width and the height the text takes, in logical
    /// points. One kept past a change of
    /// [`pixels_per_point`](Self::pixels_per_point) still reports the size
    /// the frame that laid it out measured. The `ui` feature's own.
    #[cfg(feature = "ui")]
    pub fn text_layout(&self, text: &str, font: egui::FontId) -> Arc<egui::Galley> {
        self.layer.ui.ctx().fonts_mut(|fonts| {
            fonts.layout_no_wrap(text.to_owned(), font, egui::Color32::PLACEHOLDER)
        })
    }

    /// Whether the UI took the pointer last frame. Always false without the
    /// `ui` feature.
    pub fn ui_wants_pointer(&self) -> bool {
        self.layer.claims.pointer
    }

    /// Whether the UI took the keyboard last frame. Always false without the
    /// `ui` feature.
    pub fn ui_wants_keyboard(&self) -> bool {
        self.layer.claims.keyboard
    }

    /// The window's drawing area, in physical pixels; zero while minimized.
    pub fn window_size(&self) -> UVec2 {
        self.engine.window_size
    }

    /// The camera the last drawn frame was viewed from; [`Camera::default`]
    /// before the first frame.
    ///
    /// Required if you want a ray through a pixel: the player points at what
    /// was last drawn, not at what this frame has set since.
    pub fn last_camera(&self) -> Camera {
        self.engine.last_camera
    }

    /// The configuration the engine started with.
    pub fn config(&self) -> &Config {
        self.engine.config
    }

    /// Builds and uploads `mesh` now, instead of on its first draw; the
    /// copy is then held like any drawn mesh's under
    /// [`Config::with_mesh_memory`], and no longer than that.
    ///
    /// Takes a mesh of [`Game::Meshes`](crate::Game::Meshes) and no other.
    pub fn prepare<M>(&mut self, mesh: M)
    where
        G::Meshes: Holds<M> + From<M>,
    {
        self.renderer
            .prepare(self.engine.device, self.engine.queue, mesh.into());
    }
}

/// Everything a tick is recorded against: what it plays through, keeps
/// in, and the run it paces and ends.
pub(crate) struct Simulating<'a, G: Game> {
    pub(crate) audio: &'a mut Audio<G::Sounds>,
    pub(crate) saves: &'a mut Saved,
    pub(crate) run: &'a mut Run,
}

impl<'a, G: Game> Simulating<'a, G> {
    /// The context one tick of `dt` runs with, reading `ticking`'s controls
    /// and running every machine it is passed at `elapsed` on the run
    /// clock.
    pub(crate) fn tick_context(
        self,
        engine: Engine<'a>,
        meshes: &'a mut MeshCache<G::Meshes>,
        ticking: &'a Ticking<'a>,
        dt: Duration,
        elapsed: Duration,
        claims: Claims,
    ) -> TickContext<'a, G> {
        let Self { audio, saves, run } = self;
        TickContext {
            engine,
            meshes,
            audio,
            ticking,
            saves,
            run,
            dt,
            elapsed,
            claims,
        }
    }
}

/// Everything a frame is recorded against: what a tick is, and what it
/// draws through and reads its controls from.
pub(crate) struct Recording<'a, G: Game> {
    pub(crate) renderer: &'a mut Renderer<G::Meshes, G::Skyboxes>,
    pub(crate) input: &'a mut Input,
    pub(crate) simulating: Simulating<'a, G>,
}

impl<'a, G: Game> Recording<'a, G> {
    /// The context one frame at `time` runs with, building its UI in
    /// `layer`.
    ///
    /// The frame starts at the instant `time` reads, so every draw it
    /// records is posed at that one instant.
    pub(crate) fn frame_context(
        self,
        engine: Engine<'a>,
        time: FrameTime,
        layer: Layer<'a>,
    ) -> FrameContext<'a, G> {
        let Self {
            renderer,
            input,
            simulating: Simulating { audio, saves, run },
        } = self;
        renderer.start_frame(time.elapsed);

        FrameContext {
            engine,
            renderer,
            audio,
            input,
            saves,
            run,
            time,
            layer,
        }
    }
}

/// What a game changes about the run itself: how long its ticks are, and
/// whether it goes on.
pub(crate) struct Run {
    tick_interval: TickInterval,
    closing: bool,
}

impl Run {
    /// A run that ticks every `tick_interval` until the game closes it.
    pub(crate) fn new(tick_interval: Duration) -> Self {
        Self {
            tick_interval: TickInterval::new(tick_interval),
            closing: false,
        }
    }

    /// Starts a batch of ticks, and returns the step each one takes.
    pub(crate) fn start_ticks(&mut self) -> Duration {
        self.tick_interval.advance();
        self.tick_interval.current()
    }

    /// The step the ticks running now take.
    pub(crate) fn tick_interval(&self) -> Duration {
        self.tick_interval.current()
    }

    /// Whether the game has requested that the run end.
    pub(crate) fn closing(&self) -> bool {
        self.closing
    }
}

/// Everything every context uses: the GPU meshes are built on, the area
/// being drawn to, the settings the engine started with, and the camera the
/// last frame was drawn from.
pub(crate) struct Engine<'a> {
    device: &'a wgpu::Device,
    queue: &'a wgpu::Queue,
    window_size: UVec2,
    config: &'a Config,
    last_camera: Camera,
}

impl<'a> Engine<'a> {
    pub(crate) fn new(
        device: &'a wgpu::Device,
        queue: &'a wgpu::Queue,
        window_size: UVec2,
        config: &'a Config,
        last_camera: Camera,
    ) -> Self {
        Self {
            device,
            queue,
            window_size,
            config,
            last_camera,
        }
    }
}