EvanZhu0721 / 公开仓库
SBMS
SBMS complete the calculation rules for the default logical desktop topology relationship and the physical dimensions of the displays in the Windows multi-screen collaboration system.
文件
../ bininputuiconfig.rs81.2 KBcontrol.rs4.1 KBcontroller.rs43.6 KBdiagnostics.rs24.2 KBdisplay.rs29.8 KBgeometry.rs21.4 KBgpu_renderer.rs37.4 KBinput.rs79.7 KBlaunch_broker.rs10.3 KBlib.rs350 Blimits.rs714 Bmain.rs24.4 KBmapping.rs46.5 KBnetwork.rs9.2 KBrenderer.rs20.6 KBsession_gate.rs14.9 KBsunshine.rs12.2 KBui.rs68.5 KBvirtual_display.rs7.2 KBwin32_flyout.rs3.4 KBwin32_tray.rs22.3 KBwindow_migration.rs43.8 KBsrc/renderer.rs20.6 KB
1
use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle};2
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};3
use std::sync::{Arc, mpsc};4
use std::thread::{self, JoinHandle};5
use std::time::{Duration, Instant};6
7
use windows::Win32::Foundation::{8
HANDLE, HINSTANCE, HWND, LPARAM, LRESULT, RECT, WAIT_FAILED, WPARAM,9
};10
use windows::Win32::System::LibraryLoader::GetModuleHandleW;11
use windows::Win32::System::Threading::{CreateEventW, INFINITE, SetEvent};12
use windows::Win32::UI::WindowsAndMessaging::{13
CS_HREDRAW, CS_VREDRAW, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, MSG,14
MWMO_INPUTAVAILABLE, MsgWaitForMultipleObjectsEx, PM_REMOVE, PeekMessageW, PostQuitMessage,15
QS_ALLINPUT, RegisterClassW, SW_SHOW, ShowWindow, TranslateMessage, UnregisterClassW,16
WINDOW_EX_STYLE, WM_CLOSE, WM_DESTROY, WM_QUIT, WNDCLASSW, WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW,17
WS_EX_TOPMOST, WS_POPUP,18
};19
use windows::core::{PCWSTR, w};20
21
use crate::display::Display;22
use crate::gpu_renderer::{GpuRendererConfig, GpuRendererError, run_gpu_renderer};23
use crate::input::{InputGuard, flush_movement, handle_message};24
25
const START_TIMEOUT: Duration = Duration::from_secs(10);26
const START_CANCEL_POLL_INTERVAL: Duration = Duration::from_millis(50);27
#[cfg(not(test))]28
const STOP_TIMEOUT: Duration = Duration::from_secs(2);29
#[cfg(test)]30
const STOP_TIMEOUT: Duration = Duration::from_millis(20);31
static WINDOW_CLASS_SEQUENCE: AtomicU64 = AtomicU64::new(1);32
33
#[derive(Clone, Debug)]34
pub enum RendererEvent {35
Fps(u32),36
TopologyLost,37
Failed(String),38
}39
40
pub type RendererReporter = Arc<dyn Fn(RendererEvent) + Send + Sync + 'static>;41
42
pub struct Renderer {43
stop: Arc<AtomicBool>,44
wake_event: Arc<OwnedHandle>,45
done: mpsc::Receiver<Result<(), String>>,46
thread: Option<JoinHandle<()>>,47
}48
49
#[derive(Debug, Eq, PartialEq)]50
enum StartWait {51
Ready,52
Failed(String),53
Cancelled,54
TimedOut,55
Disconnected,56
}57
58
#[derive(Debug, Eq, PartialEq)]59
pub(crate) enum RendererStartError {60
Cancelled { cleanup_error: Option<String> },61
Failed(String),62
}63
64
impl RendererStartError {65
fn into_message(self) -> String {66
match self {67
Self::Cancelled {68
cleanup_error: None,69
} => "renderer startup was cancelled".into(),70
Self::Cancelled {71
cleanup_error: Some(error),72
} => format!("renderer startup was cancelled; cleanup was incomplete: {error}"),73
Self::Failed(error) => error,74
}75
}76
}77
78
impl Renderer {79
pub fn start_with_reporter(80
target: Display,81
source: Display,82
reporter: RendererReporter,83
) -> Result<Self, String> {84
let cancel = AtomicBool::new(false);85
Self::start_with_reporter_cancellable(target, source, reporter, &cancel)86
.map_err(RendererStartError::into_message)87
}88
89
pub(crate) fn start_with_reporter_cancellable(90
target: Display,91
source: Display,92
reporter: RendererReporter,93
cancel: &AtomicBool,94
) -> Result<Self, RendererStartError> {95
let stop = Arc::new(AtomicBool::new(false));96
let worker_stop = Arc::clone(&stop);97
let wake_event = Arc::new(create_wake_event().map_err(RendererStartError::Failed)?);98
let worker_wake_event = Arc::clone(&wake_event);99
let (ready_tx, ready_rx) = mpsc::channel();100
let (done_tx, done_rx) = mpsc::sync_channel(1);101
let thread = thread::spawn(move || {102
let result = run(103
target.rect,104
source.rect,105
&worker_stop,106
&worker_wake_event,107
&ready_tx,108
&reporter,109
);110
if let Err(error) = &result {111
let _ = ready_tx.send(Err(error.clone()));112
}113
let failure = result.as_ref().err().cloned();114
if let Some(error) = failure {115
reporter(RendererEvent::Failed(error));116
}117
let _ = done_tx.send(result);118
});119
120
match wait_for_start(&ready_rx, cancel, START_TIMEOUT) {121
StartWait::Ready => Ok(Self {122
stop,123
wake_event,124
done: done_rx,125
thread: Some(thread),126
}),127
StartWait::Failed(error) => {128
stop.store(true, Ordering::Release);129
signal_event(&wake_event);130
let _ = thread.join();131
Err(RendererStartError::Failed(error))132
}133
StartWait::Cancelled => Err(RendererStartError::Cancelled {134
cleanup_error: abort_start(&stop, &wake_event, &done_rx, thread).err(),135
}),136
StartWait::TimedOut => {137
let cleanup = abort_start(&stop, &wake_event, &done_rx, thread)138
.err()139
.map(|error| format!("; cleanup was incomplete: {error}"))140
.unwrap_or_default();141
Err(RendererStartError::Failed(format!(142
"renderer did not receive and draw a first frame within 10 seconds{cleanup}"143
)))144
}145
StartWait::Disconnected => {146
let cleanup = abort_start(&stop, &wake_event, &done_rx, thread)147
.err()148
.map(|error| format!("; cleanup was incomplete: {error}"))149
.unwrap_or_default();150
Err(RendererStartError::Failed(format!(151
"renderer exited before drawing its first frame{cleanup}"152
)))153
}154
}155
}156
157
pub fn request_stop(&self) {158
self.stop.store(true, Ordering::Release);159
signal_event(&self.wake_event);160
}161
162
pub fn stop(&mut self) -> Result<(), String> {163
if self.thread.is_none() {164
return Ok(());165
}166
self.request_stop();167
let (result, exceeded_timeout) = match self.done.recv_timeout(STOP_TIMEOUT) {168
Ok(result) => (result, false),169
Err(mpsc::RecvTimeoutError::Timeout) => {170
// A live renderer owns HWND, input and D3D resources. Keep waiting171
// rather than detaching it and allowing the display topology to be172
// torn down underneath the worker.173
let result = self174
.done175
.recv()176
.map_err(|_| "renderer stopped without reporting a result".to_string())?;177
(result, true)178
}179
Err(mpsc::RecvTimeoutError::Disconnected) => {180
let thread = self.thread.take().expect("renderer thread disappeared");181
thread182
.join()183
.map_err(|_| "renderer thread panicked".to_string())?;184
return Err("renderer stopped without reporting a result".into());185
}186
};187
let thread = self.thread.take().expect("renderer thread disappeared");188
thread189
.join()190
.map_err(|_| "renderer thread panicked".to_string())?;191
match (result, exceeded_timeout) {192
(Ok(()), false) => Ok(()),193
(Ok(()), true) => Err("renderer needed more than 2 seconds to stop".into()),194
(Err(error), false) => Err(error),195
(Err(error), true) => Err(format!(196
"renderer needed more than 2 seconds to stop; renderer failed: {error}"197
)),198
}199
}200
}201
202
fn wait_for_start(203
ready: &mpsc::Receiver<Result<(), String>>,204
cancel: &AtomicBool,205
timeout: Duration,206
) -> StartWait {207
let deadline = Instant::now() + timeout;208
loop {209
match ready.try_recv() {210
Ok(Ok(())) => return StartWait::Ready,211
Ok(Err(error)) => return StartWait::Failed(error),212
Err(mpsc::TryRecvError::Disconnected) => return StartWait::Disconnected,213
Err(mpsc::TryRecvError::Empty) => {}214
}215
if cancel.load(Ordering::Acquire) {216
return StartWait::Cancelled;217
}218
let now = Instant::now();219
if now >= deadline {220
return StartWait::TimedOut;221
}222
let wait = (deadline - now).min(START_CANCEL_POLL_INTERVAL);223
match ready.recv_timeout(wait) {224
Ok(Ok(())) => return StartWait::Ready,225
Ok(Err(error)) => return StartWait::Failed(error),226
Err(mpsc::RecvTimeoutError::Disconnected) => return StartWait::Disconnected,227
Err(mpsc::RecvTimeoutError::Timeout) => {}228
}229
}230
}231
232
fn abort_start(233
stop: &AtomicBool,234
wake_event: &OwnedHandle,235
done: &mpsc::Receiver<Result<(), String>>,236
thread: JoinHandle<()>,237
) -> Result<(), String> {238
stop.store(true, Ordering::Release);239
signal_event(wake_event);240
let exceeded_timeout = match done.recv_timeout(STOP_TIMEOUT) {241
Ok(_) | Err(mpsc::RecvTimeoutError::Disconnected) => false,242
Err(mpsc::RecvTimeoutError::Timeout) => {243
// Startup failure cannot hand an unowned live renderer to the caller.244
// Wait for definitive termination before mapping rollback changes the245
// virtual display or physical topology.246
let _ = done.recv();247
true248
}249
};250
thread251
.join()252
.map_err(|_| "renderer thread panicked while stopping".to_string())?;253
if exceeded_timeout {254
Err("renderer needed more than 2 seconds to stop".into())255
} else {256
Ok(())257
}258
}259
260
impl Drop for Renderer {261
fn drop(&mut self) {262
let _ = self.stop();263
}264
}265
266
fn run(267
target: RECT,268
source: RECT,269
stop: &AtomicBool,270
wake_event: &OwnedHandle,271
ready: &mpsc::Sender<Result<(), String>>,272
reporter: &RendererReporter,273
) -> Result<(), String> {274
let instance = unsafe { GetModuleHandleW(None) }275
.map_err(|error| format!("GetModuleHandleW failed: {error}"))?;276
let instance = HINSTANCE(instance.0);277
let class_name_storage =278
window_class_name(WINDOW_CLASS_SEQUENCE.fetch_add(1, Ordering::Relaxed));279
let class_name = PCWSTR(class_name_storage.as_ptr());280
let class = WNDCLASSW {281
style: CS_HREDRAW | CS_VREDRAW,282
lpfnWndProc: Some(window_proc),283
hInstance: instance,284
lpszClassName: class_name,285
..Default::default()286
};287
if unsafe { RegisterClassW(&class) } == 0 {288
return Err(format!(289
"RegisterClassW failed: {}",290
windows::core::Error::from_thread()291
));292
}293
294
let width = target.right - target.left;295
let height = target.bottom - target.top;296
let window = unsafe {297
CreateWindowExW(298
WINDOW_EX_STYLE(WS_EX_TOPMOST.0 | WS_EX_TOOLWINDOW.0 | WS_EX_NOACTIVATE.0),299
class_name,300
w!("SBMS"),301
WS_POPUP,302
target.left,303
target.top,304
width,305
height,306
None,307
None,308
Some(instance),309
None,310
)311
}312
.map_err(|error| {313
unsafe {314
let _ = UnregisterClassW(class_name, Some(instance));315
}316
format!("CreateWindowExW failed: {error}")317
})?;318
unsafe {319
let _ = ShowWindow(window, SW_SHOW);320
}321
322
let input = match InputGuard::start(window, target, source, instance) {323
Ok(input) => input,324
Err(error) => {325
unsafe {326
let _ = DestroyWindow(window);327
let _ = UnregisterClassW(class_name, Some(instance));328
}329
return Err(error);330
}331
};332
let result = thread::scope(|scope| {333
let (draw_done_tx, draw_done_rx) = mpsc::sync_channel(1);334
let window_value = window.0 as usize;335
let draw_thread = scope.spawn(move || {336
let window = HWND(window_value as *mut _);337
let result = draw_frames(window, width, height, source, stop, ready, reporter);338
let _ = draw_done_tx.send(result);339
signal_event(wake_event);340
});341
let draw_result = loop {342
if let Ok(result) = draw_done_rx.try_recv() {343
break result;344
}345
if stop.load(Ordering::Acquire) {346
break draw_done_rx347
.recv()348
.map_err(|_| "renderer draw worker exited without a result".to_string())?;349
}350
if !pump_messages() {351
stop.store(true, Ordering::Release);352
continue;353
}354
flush_movement();355
if let Err(error) = wait_for_message(wake_event) {356
stop.store(true, Ordering::Release);357
let _ = draw_done_rx.recv();358
break Err(error);359
}360
};361
draw_thread362
.join()363
.map_err(|_| "renderer draw worker panicked".to_string())?;364
draw_result365
});366
drop(input);367
unsafe {368
let _ = DestroyWindow(window);369
let _ = UnregisterClassW(class_name, Some(instance));370
}371
result372
}373
374
fn create_wake_event() -> Result<OwnedHandle, String> {375
let handle = unsafe { CreateEventW(None, false, false, PCWSTR::null()) }376
.map_err(|error| format!("CreateEventW(renderer wake) failed: {error}"))?;377
Ok(unsafe { OwnedHandle::from_raw_handle(handle.0) })378
}379
380
fn wait_for_message(wake_event: &OwnedHandle) -> Result<(), String> {381
let handle = HANDLE(wake_event.as_raw_handle());382
let result = unsafe {383
MsgWaitForMultipleObjectsEx(384
Some(std::slice::from_ref(&handle)),385
INFINITE,386
QS_ALLINPUT,387
MWMO_INPUTAVAILABLE,388
)389
};390
if result == WAIT_FAILED {391
return Err(format!(392
"MsgWaitForMultipleObjectsEx failed: {}",393
windows::core::Error::from_thread()394
));395
}396
Ok(())397
}398
399
fn signal_event(wake_event: &OwnedHandle) {400
let _ = unsafe { SetEvent(HANDLE(wake_event.as_raw_handle())) };401
}402
403
fn draw_frames(404
window: HWND,405
target_width: i32,406
target_height: i32,407
source: RECT,408
stop: &AtomicBool,409
ready: &mpsc::Sender<Result<(), String>>,410
reporter: &RendererReporter,411
) -> Result<(), String> {412
let mut first_frame = true;413
let mut frames = 0_u32;414
let mut sample_started = Instant::now();415
let result = run_gpu_renderer(416
GpuRendererConfig {417
target_window: window,418
target_width: target_width as u32,419
target_height: target_height as u32,420
source_rect: source,421
},422
stop,423
|| {424
frames = frames.saturating_add(1);425
if first_frame {426
ready427
.send(Ok(()))428
.map_err(|_| "mapping start was cancelled".to_string())?;429
first_frame = false;430
}431
let elapsed = sample_started.elapsed();432
if elapsed >= Duration::from_secs(1) {433
reporter(RendererEvent::Fps(measured_fps(frames, elapsed)));434
frames = 0;435
sample_started = Instant::now();436
}437
Ok(())438
},439
);440
finish_gpu_renderer(result, reporter)441
}442
443
fn finish_gpu_renderer(444
result: Result<(), GpuRendererError>,445
reporter: &RendererReporter,446
) -> Result<(), String> {447
match result {448
Ok(()) => Ok(()),449
Err(GpuRendererError::AccessLost) => {450
reporter(RendererEvent::TopologyLost);451
Ok(())452
}453
Err(error) => Err(error.to_string()),454
}455
}456
457
fn measured_fps(frames: u32, elapsed: Duration) -> u32 {458
if elapsed.is_zero() {459
return 0;460
}461
((frames as f64 / elapsed.as_secs_f64()).round()).clamp(0.0, u32::MAX as f64) as u32462
}463
464
fn window_class_name(sequence: u64) -> Vec<u16> {465
format!("SBMSMirrorWindow-{}-{sequence}", std::process::id())466
.encode_utf16()467
.chain(Some(0))468
.collect()469
}470
471
fn pump_messages() -> bool {472
let mut message = MSG::default();473
while unsafe { PeekMessageW(&mut message, None, 0, 0, PM_REMOVE) }.as_bool() {474
if message.message == WM_QUIT {475
return false;476
}477
unsafe {478
let _ = TranslateMessage(&message);479
DispatchMessageW(&message);480
}481
}482
true483
}484
485
unsafe extern "system" fn window_proc(486
window: HWND,487
message: u32,488
wparam: WPARAM,489
lparam: LPARAM,490
) -> LRESULT {491
if let Some(result) = handle_message(message, wparam, lparam) {492
return result;493
}494
match message {495
WM_CLOSE => {496
unsafe {497
let _ = DestroyWindow(window);498
}499
LRESULT(0)500
}501
WM_DESTROY => {502
unsafe { PostQuitMessage(0) };503
LRESULT(0)504
}505
_ => unsafe { DefWindowProcW(window, message, wparam, lparam) },506
}507
}508
509
#[cfg(test)]510
mod tests {511
use super::{512
Renderer, RendererEvent, RendererReporter, StartWait, create_wake_event,513
finish_gpu_renderer, measured_fps, signal_event, wait_for_message, wait_for_start,514
window_class_name,515
};516
use crate::gpu_renderer::GpuRendererError;517
use std::sync::Arc;518
use std::sync::atomic::{AtomicBool, Ordering};519
use std::sync::mpsc;520
use std::thread;521
use std::time::Duration;522
523
#[test]524
fn fps_uses_actual_sample_duration() {525
assert_eq!(measured_fps(60, Duration::from_secs(1)), 60);526
assert_eq!(measured_fps(90, Duration::from_millis(1500)), 60);527
}528
529
#[test]530
fn fps_reports_idle_and_handles_zero_duration() {531
assert_eq!(measured_fps(0, Duration::from_secs(1)), 0);532
assert_eq!(measured_fps(1, Duration::ZERO), 0);533
}534
535
#[test]536
fn access_lost_reports_recoverable_topology_loss() {537
let (event_tx, event_rx) = mpsc::channel();538
let reporter: RendererReporter = Arc::new(move |event| {539
event_tx.send(event).unwrap();540
});541
542
assert!(finish_gpu_renderer(Err(GpuRendererError::AccessLost), &reporter).is_ok());543
assert!(matches!(544
event_rx.recv().unwrap(),545
RendererEvent::TopologyLost546
));547
}548
549
#[test]550
fn renderer_window_classes_are_unique_and_null_terminated() {551
let first = window_class_name(7);552
let second = window_class_name(8);553
assert_ne!(first, second);554
assert_eq!(first.last(), Some(&0));555
assert_eq!(second.last(), Some(&0));556
}557
558
#[test]559
fn ready_frame_wins_a_simultaneous_start_cancellation() {560
let (ready_tx, ready_rx) = mpsc::channel();561
ready_tx.send(Ok(())).unwrap();562
let cancel = AtomicBool::new(true);563
assert_eq!(564
wait_for_start(&ready_rx, &cancel, Duration::from_secs(1)),565
StartWait::Ready566
);567
}568
569
#[test]570
fn renderer_start_wait_distinguishes_cancel_timeout_and_disconnect() {571
let (_ready_tx, ready_rx) = mpsc::channel();572
let cancel = AtomicBool::new(true);573
assert_eq!(574
wait_for_start(&ready_rx, &cancel, Duration::from_secs(1)),575
StartWait::Cancelled576
);577
578
let (_ready_tx, ready_rx) = mpsc::channel();579
let cancel = AtomicBool::new(false);580
assert_eq!(581
wait_for_start(&ready_rx, &cancel, Duration::from_millis(1)),582
StartWait::TimedOut583
);584
585
let (ready_tx, ready_rx) = mpsc::channel::<Result<(), String>>();586
drop(ready_tx);587
assert_eq!(588
wait_for_start(&ready_rx, &cancel, Duration::from_secs(1)),589
StartWait::Disconnected590
);591
}592
593
#[test]594
fn event_releases_message_wait() {595
let wake_event = Arc::new(create_wake_event().unwrap());596
let waiting_event = Arc::clone(&wake_event);597
let (ready_tx, ready_rx) = mpsc::sync_channel(1);598
let (done_tx, done_rx) = mpsc::sync_channel(1);599
let thread = thread::spawn(move || {600
ready_tx.send(()).unwrap();601
done_tx.send(wait_for_message(&waiting_event)).unwrap();602
});603
604
ready_rx.recv().unwrap();605
signal_event(&wake_event);606
assert_eq!(607
done_rx.recv_timeout(Duration::from_millis(200)).unwrap(),608
Ok(())609
);610
thread.join().unwrap();611
}612
613
#[test]614
fn stop_waits_for_actual_thread_exit_after_reporting_timeout() {615
let stop = Arc::new(AtomicBool::new(false));616
let worker_stop = Arc::clone(&stop);617
let wake_event = Arc::new(create_wake_event().unwrap());618
let (done_tx, done_rx) = mpsc::sync_channel(1);619
let thread = thread::spawn(move || {620
while !worker_stop.load(Ordering::Acquire) {621
thread::yield_now();622
}623
thread::sleep(Duration::from_millis(40));624
done_tx.send(Ok(())).unwrap();625
});626
let mut renderer = Renderer {627
stop,628
wake_event,629
done: done_rx,630
thread: Some(thread),631
};632
633
let error = renderer.stop().unwrap_err();634
assert!(error.contains("needed more than"));635
assert!(renderer.thread.is_none());636
}637
}638