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
// web_gpu -- what the UI's output through the GPU needs of the browser (lib/ui/gpu_out.rs, lib/gpu
// web_*.rs, web/gpu.js, web/loader.js; docs/GPU.md "Ausgabe im Browser")
//
// win_gpu_child(w) a canvas above the page's #screen as an OffscreenCanvas in this
// worker, the analogue of Android's SurfaceView: only for window 0
// while nothing else shows on #screen (tool windows and popups are
// composed on the CPU, below it). None: the page was asked, the canvas
// is on its way; each surface takes a new one
// win_gpu_child_later(w) no canvas now, it comes: no reason to give up, nothing to poll for
// win_gpu_child_alone(w) the window still shows on it alone
// win_gpu_child_show(w, show) show: the frame is on the canvas, #screen rests with its last frame
// under it. hide: the frame is on its way to #screen, the page
// takes the canvas away after it (after_flush): no gap, no wrong frame
// win_release_kept(w) the copy of the window for #screen goes, and the worker's pooled
// pixel buffers with it
// win_gpu_child_blank() the page found that the canvas shows nothing (once)
//
// A canvas that has shown nothing yet shows nothing: until the first frame reaches the compositor the
// page's #screen is seen through it, the same pixels (the switch needs no message and no timing). A
// new period on the GPU takes a new canvas, so it can never show an old frame. The worker's thread is the
// only one that touches any of this: there are no job threads here, in none of the three bundles (the
// compute workers of the threads bundle cannot own WebGPU objects); gpu_out.rs runs its jobs on this
// thread when the browser has answered.
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
/// (browser) the output wants a canvas above #screen; the page answers with jfgui_gpu_canvas
pub const REQUEST_GPU_CANVAS: u32 = 28;
/// (browser) the canvas above #screen goes (after the frame that replaces it)
pub const REQUEST_GPU_HIDE: u32 = 29;
/// (browser) the canvas above #screen shows: the worker's pixel buffers for the page may go
pub const REQUEST_GPU_SHOWN: u32 = 30;
// the canvas that came, 0: none
static CANVAS: AtomicU32 = AtomicU32::new(0);
// the page was asked, its answer is on the way
static ASKED: AtomicBool = AtomicBool::new(false);
// the next flush takes the canvas away
static HIDE: AtomicBool = AtomicBool::new(false);
// the page's watcher found that the canvas shows nothing
static BLANK: AtomicBool = AtomicBool::new(false);
/// (worker.js) the page's canvas, as a number of web/gpu.js's table
#[unsafe(no_mangle)]
pub extern "C" fn jfgui_gpu_canvas(handle: u32) {
CANVAS.store(handle, Ordering::Release);
ASKED.store(false, Ordering::Release);
super::wake();
}
/// (worker.js) the page's watcher (watchGpuCanvas in loader.js): the canvas shows nothing
#[unsafe(no_mangle)]
pub extern "C" fn jfgui_gpu_blank() {
BLANK.store(true, Ordering::Release);
super::wake();
}
/// the page found that the canvas shows nothing, though everything computes right; asked once
pub fn win_gpu_child_blank() -> bool {
BLANK.swap(false, Ordering::AcqRel)
}
pub(super) fn after_flush() {
if HIDE.swap(false, Ordering::AcqRel) {
super::notice(REQUEST_GPU_HIDE, &[]);
}
}
/// a target for swapchains: the OffscreenCanvas of the page's canvas, a number of web/gpu.js's table
#[derive(Debug)]
pub struct NativeWindow {
canvas: u32,
}
impl NativeWindow {
/// the number, once (lib/gpu's Surface owns it then)
pub fn take(self) -> u32 {
self.canvas
}
}
/// how the output came up: nothing keeps it across runs here
pub fn gpu_out_state(_state: u32) {}
/// the canvas for window w; None: see win_gpu_child_later
pub fn win_gpu_child(w: i32) -> Option<NativeWindow> {
if w != 0 || !super::windows::only_main() {
return None;
}
match CANVAS.swap(0, Ordering::AcqRel) {
0 => {
if !ASKED.swap(true, Ordering::AcqRel) {
super::notice(REQUEST_GPU_CANVAS, &[]);
}
None
}
canvas => Some(NativeWindow { canvas }),
}
}
/// no canvas for window w now, but it may come: the page or the next frame wakes the loop
pub fn win_gpu_child_later(w: i32) -> bool {
w != 0 || !super::windows::only_main() || (CANVAS.load(Ordering::Acquire) == 0 && ASKED.load(Ordering::Acquire))
}
/// the window still shows on its canvas alone: nothing else came on #screen
pub fn win_gpu_child_alone(w: i32) -> bool {
w == 0 && super::windows::only_main()
}
/// show: the window shows on the canvas above; hide: on #screen, the canvas above goes after that frame
pub fn win_gpu_child_show(w: i32, show: bool) {
if w == 0 {
super::windows::gpu_shows(show);
if show {
super::notice(REQUEST_GPU_SHOWN, &[]);
} else {
HIDE.store(true, Ordering::Release);
}
}
}
/// the frame is on the canvas above: the copy for #screen goes (it comes back whole)
pub fn win_release_kept(w: i32) {
super::windows::release_pixels(w);
}
/// no modal size loop here
pub fn win_in_sizemove() -> bool {
false
}
/// the browser chooses the GPU
pub fn win_display_adapter(_w: i32) -> Option<(u32, u32)> {
None
}
/// no remote sessions to look for here
pub fn win_remote_session() -> bool {
false
}
// the script command `gpu` (a script's real windows) has nothing to work with here: scripts run on the
// desktop, this target has its own test (apps/gallery/tests/gpu_web.mjs)
pub const WT_NOANIM: u32 = 0;
pub const WT_FROM_POINT: u32 = 0;
pub fn win_test(_op: u32, _w: i32, _a: [i32; 4]) -> i64 {
0
}
pub fn win_test_image(_w: i32) -> Option<(i32, i32, alloc::vec::Vec<u8>)> {
None
}
pub fn win_test_screen(_x: i32, _y: i32, _w: i32, _h: i32) -> Option<alloc::vec::Vec<u8>> {
None
}
pub fn gpu_wait(_deadline_us: u64) {}