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
|
use std::{
hint,
sync::Arc,
thread,
time::{Duration, Instant},
};
use winit::{
application::ApplicationHandler,
dpi::LogicalSize,
event::{ElementState, WindowEvent},
event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
window::{Window, WindowId},
};
mod graphics;
use graphics::{AcquireSurfaceTextureResult, Renderer};
#[derive(Default)]
struct App {
window: Option<Arc<Window>>,
renderer: Option<Renderer>,
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
// ウィンドウメッセージをpollで捌く
// キューが空になる度に`about_to_wait`メソッドが呼ばれる
event_loop.set_control_flow(ControlFlow::Poll);
let attributes = Window::default_attributes()
.with_title("君は呪われている!")
.with_resizable(false)
.with_inner_size(LogicalSize::new(1280, 720))
.with_fullscreen(None);
let window = match event_loop.create_window(attributes) {
Ok(v) => Arc::new(v),
Err(e) => panic!("error: {e}"),
};
let renderer = pollster::block_on(Renderer::new(
Box::new(event_loop.owned_display_handle()),
window.clone(),
));
let renderer = match renderer {
Ok(v) => v,
Err(e) => panic!("error: {e}"),
};
self.window = Some(window);
self.renderer = Some(renderer);
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::MouseInput { state, button, .. } => match state {
ElementState::Pressed => {
println!("pressed {:?}", button);
}
ElementState::Released => {
println!("released {:?}", button);
}
},
WindowEvent::CursorMoved { position, .. } => {
let Some(window) = self.window.as_ref() else {
return;
};
println!(
"moved {:?}",
position.to_logical::<f32>(window.scale_factor())
);
}
WindowEvent::CursorLeft { .. } => {
println!("left");
}
WindowEvent::Resized(size) => {
if let Some(renderer) = self.renderer.as_ref() {
renderer.configure_surface(size);
}
}
WindowEvent::ScaleFactorChanged { .. } => {
let (Some(window), Some(renderer)) = (&self.window, &self.renderer) else {
return;
};
renderer.configure_surface(window.inner_size());
}
WindowEvent::Focused(false) | WindowEvent::Occluded(true) => {
println!("focused or occluded");
}
_ => {}
}
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
let start = Instant::now();
let (Some(window), Some(renderer)) = (&self.window, &self.renderer) else {
// NOTE: `window`が`Some`になる`resumed`メソッドで`ControlFlow::Poll`を指定しているため、
// ここで`ControlFlow::WaitUntil`を指定しても問題ない。
event_loop.set_control_flow(ControlFlow::WaitUntil(start + Duration::from_millis(16)));
return;
};
// NOTE: Presentation modeがFIFOの場合はここでスレッドが停止する。
let current_texture = match renderer.get_current_texture(window) {
AcquireSurfaceTextureResult::Ok(v) => v,
AcquireSurfaceTextureResult::ShouldSkip => {
event_loop
.set_control_flow(ControlFlow::WaitUntil(start + Duration::from_millis(16)));
return;
}
// TODO: 一応対応する。
AcquireSurfaceTextureResult::Lost => panic!("fatal: device or surface lost."),
};
renderer.render(window, current_texture);
if !renderer.is_presentation_mode_fifo() {
sync_60fps(start);
}
}
}
/// 60fpsになるようにスレッドを停止する関数
///
/// * start - フレーム開始時刻
///
/// 15.66...msをsleepし、不足時間をスピンウェイトする。
/// 正直アニメーションの強いゲームではないので厳密さは要らないのだけれど、
/// 一応アニメーションのあるゲームだからね多少はね。
fn sync_60fps(start: Instant) {
const TARGET_FRAME_TIME: Duration = Duration::from_nanos(1_000_000_000 / 60); // 60FPS
const SPIN_THRESHOLD: Duration = Duration::from_millis(1);
let elapsed = start.elapsed();
if elapsed < TARGET_FRAME_TIME {
let remaining = TARGET_FRAME_TIME - elapsed;
if remaining > SPIN_THRESHOLD {
thread::sleep(remaining - SPIN_THRESHOLD);
}
while start.elapsed() < TARGET_FRAME_TIME {
hint::spin_loop();
}
}
}
fn main() {
let event_loop = match EventLoop::new() {
Ok(v) => v,
Err(e) => panic!("error: {e}"),
};
match event_loop.run_app(&mut App::default()) {
Ok(()) => {}
Err(e) => panic!("error: {e}"),
}
}
|