-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathrender.rs
155 lines (129 loc) · 4.9 KB
/
render.rs
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
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::OnceLock;
use parking_lot::Mutex;
use tracing::{debug, error, trace};
use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM};
#[cfg(target_arch = "x86")]
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongA;
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrA;
use windows::Win32::UI::WindowsAndMessaging::{DefWindowProcW, GWLP_WNDPROC};
use crate::hooks::input::{imgui_wnd_proc_impl, WndProcType};
use crate::renderer::dx12::RenderEngine;
use crate::ImguiRenderLoop;
static mut GAME_HWND: OnceLock<HWND> = OnceLock::new();
static mut WND_PROC: OnceLock<WndProcType> = OnceLock::new();
static mut RENDER_ENGINE: OnceLock<Mutex<RenderEngine>> = OnceLock::new();
static mut RENDER_LOOP: OnceLock<Box<dyn ImguiRenderLoop + Send + Sync>> = OnceLock::new();
static RENDER_LOCK: AtomicBool = AtomicBool::new(false);
pub(super) struct RenderState;
impl RenderState {
pub(super) fn setup<F: Fn() -> HWND>(f: F) -> HWND {
let hwnd = unsafe { *GAME_HWND.get_or_init(f) };
unsafe {
WND_PROC.get_or_init(|| {
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
let wnd_proc = mem::transmute(SetWindowLongPtrA(
hwnd,
GWLP_WNDPROC,
imgui_wnd_proc as usize as isize,
));
#[cfg(target_arch = "x86")]
let wnd_proc = mem::transmute(SetWindowLongA(
hwnd,
GWLP_WNDPROC,
imgui_wnd_proc as usize as i32,
));
wnd_proc
})
};
hwnd
}
pub(super) fn set_render_loop<T: ImguiRenderLoop + Send + Sync + 'static>(t: T) {
unsafe { RENDER_LOOP.get_or_init(|| Box::new(t)) };
}
pub(super) fn is_locked() -> bool {
RENDER_LOCK.load(Ordering::SeqCst)
}
pub(super) fn render(hwnd: HWND) {
RENDER_LOCK.store(true, Ordering::SeqCst);
let Some(render_loop) = (unsafe { RENDER_LOOP.get_mut() }) else {
error!("Could not obtain render loop");
return;
};
let render_engine = unsafe {
RENDER_ENGINE.get_or_init(|| {
let mut render_engine = RenderEngine::new(hwnd).unwrap();
render_loop.initialize(&mut render_engine.ctx());
Mutex::new(render_engine)
})
};
let Some(mut render_engine) = render_engine.try_lock() else {
error!("Could not lock render engine");
return;
};
render_loop.before_render(&mut render_engine.ctx());
if let Err(e) = render_engine.render(|ui| render_loop.render(ui)) {
error!("Render: {e:?}");
}
RENDER_LOCK.store(false, Ordering::SeqCst);
}
pub(super) fn resize() {
// TODO sometimes it doesn't lock.
if let Some(Some(mut render_engine)) = unsafe { RENDER_ENGINE.get().map(Mutex::try_lock) } {
trace!("Resizing");
if let Err(e) = render_engine.resize() {
error!("Couldn't resize: {e:?}");
}
}
}
pub(super) fn cleanup() {
unsafe {
if let (Some(wnd_proc), Some(hwnd)) = (WND_PROC.take(), GAME_HWND.take()) {
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
SetWindowLongPtrA(hwnd, GWLP_WNDPROC, wnd_proc as usize as isize);
#[cfg(target_arch = "x86")]
SetWindowLongA(hwnd, GWLP_WNDPROC, wnd_proc as usize as i32);
}
RENDER_ENGINE.take();
RENDER_LOOP.take();
RENDER_LOCK.store(false, Ordering::SeqCst);
}
}
}
unsafe extern "system" fn imgui_wnd_proc(
hwnd: HWND,
umsg: u32,
WPARAM(wparam): WPARAM,
LPARAM(lparam): LPARAM,
) -> LRESULT {
let render_engine = match RENDER_ENGINE.get().map(Mutex::try_lock) {
Some(Some(render_engine)) => render_engine,
Some(None) => {
debug!("Could not lock in WndProc");
return DefWindowProcW(hwnd, umsg, WPARAM(wparam), LPARAM(lparam));
},
None => {
debug!("WndProc called before hook was set");
return DefWindowProcW(hwnd, umsg, WPARAM(wparam), LPARAM(lparam));
},
};
let Some(render_loop) = RENDER_LOOP.get() else {
debug!("Could not get render loop");
return DefWindowProcW(hwnd, umsg, WPARAM(wparam), LPARAM(lparam));
};
let Some(&wnd_proc) = WND_PROC.get() else {
debug!("Could not get original WndProc");
return DefWindowProcW(hwnd, umsg, WPARAM(wparam), LPARAM(lparam));
};
imgui_wnd_proc_impl(
hwnd,
umsg,
WPARAM(wparam),
LPARAM(lparam),
wnd_proc,
render_engine,
render_loop,
)
}