|
1 use std::time::Duration; |
|
2 |
|
3 use futures::executor::block_on; |
1 use glutin::{ |
4 use glutin::{ |
2 dpi, ContextTrait, DeviceEvent, ElementState, Event, EventsLoop, GlProfile, GlRequest, |
5 dpi, ContextTrait, DeviceEvent, ElementState, Event, EventsLoop, GlProfile, GlRequest, |
3 MouseButton, MouseScrollDelta, WindowEvent, WindowedContext, |
6 MouseButton, MouseScrollDelta, Window, WindowBuilder, WindowEvent, WindowedContext, |
4 }; |
7 }; |
5 |
|
6 use hedgewars_engine::instance::EngineInstance; |
8 use hedgewars_engine::instance::EngineInstance; |
7 |
|
8 use integral_geometry::Point; |
9 use integral_geometry::Point; |
9 use std::time::Duration; |
10 use std::error::Error; |
10 |
11 use wgpu::{ |
11 fn init(event_loop: &EventsLoop, size: dpi::LogicalSize) -> WindowedContext { |
12 Adapter, BackendBit, Color, CommandEncoderDescriptor, Device, DeviceDescriptor, Features, |
12 use glutin::{ContextBuilder, WindowBuilder}; |
13 LoadOp, Operations, PowerPreference, PresentMode, Queue, RenderPassColorAttachmentDescriptor, |
13 |
14 RenderPassDescriptor, RequestAdapterOptions, Surface, SwapChain, SwapChainDescriptor, |
14 let window = WindowBuilder::new() |
15 TextureFormat, TextureUsage, |
|
16 }; |
|
17 |
|
18 type HwGlRendererContext = WindowedContext; |
|
19 |
|
20 struct HwWgpuRenderingContext { |
|
21 window: Window, |
|
22 surface: Surface, |
|
23 adapter: Adapter, |
|
24 device: Device, |
|
25 queue: Queue, |
|
26 swap_chain: SwapChain, |
|
27 } |
|
28 |
|
29 enum HwRendererContext { |
|
30 Gl(HwGlRendererContext), |
|
31 Wgpu(HwWgpuRenderingContext), |
|
32 } |
|
33 |
|
34 struct ErrorStub; |
|
35 |
|
36 impl<T: Error> From<T> for ErrorStub { |
|
37 fn from(_: T) -> Self { |
|
38 ErrorStub |
|
39 } |
|
40 } |
|
41 |
|
42 impl HwRendererContext { |
|
43 pub fn window(&self) -> &Window { |
|
44 match self { |
|
45 HwRendererContext::Gl(gl) => &gl.window(), |
|
46 HwRendererContext::Wgpu(wgpu) => &wgpu.window, |
|
47 } |
|
48 } |
|
49 |
|
50 pub fn update(&mut self, size: dpi::LogicalSize) { |
|
51 let phys = size.to_physical(self.window().get_hidpi_factor()); |
|
52 match self { |
|
53 HwRendererContext::Gl(context) => unsafe { |
|
54 gl::Viewport(0, 0, phys.width as i32, phys.height as i32); |
|
55 }, |
|
56 HwRendererContext::Wgpu(context) => { |
|
57 context.swap_chain = context.device.create_swap_chain( |
|
58 &context.surface, |
|
59 &SwapChainDescriptor { |
|
60 usage: TextureUsage::OUTPUT_ATTACHMENT, |
|
61 format: TextureFormat::Bgra8Unorm, |
|
62 width: phys.width as u32, |
|
63 height: phys.height as u32, |
|
64 present_mode: PresentMode::Fifo, |
|
65 }, |
|
66 ); |
|
67 } |
|
68 } |
|
69 } |
|
70 |
|
71 pub fn present(&mut self) -> Result<(), ErrorStub> { |
|
72 match self { |
|
73 HwRendererContext::Gl(context) => context.swap_buffers()?, |
|
74 HwRendererContext::Wgpu(context) => { |
|
75 let frame_view = &context.swap_chain.get_current_frame()?.output.view; |
|
76 |
|
77 let mut encoder = |
|
78 context |
|
79 .device |
|
80 .create_command_encoder(&CommandEncoderDescriptor { |
|
81 label: Some("Main encoder"), |
|
82 }); |
|
83 encoder.begin_render_pass(&RenderPassDescriptor { |
|
84 color_attachments: &[RenderPassColorAttachmentDescriptor { |
|
85 attachment: &frame_view, |
|
86 resolve_target: None, |
|
87 ops: Operations { |
|
88 load: LoadOp::Clear(Color::BLUE), |
|
89 store: false, |
|
90 }, |
|
91 }], |
|
92 depth_stencil_attachment: None, |
|
93 }); |
|
94 let buffer = encoder.finish(); |
|
95 context.queue.submit(std::iter::once(buffer)); |
|
96 } |
|
97 } |
|
98 Ok(()) |
|
99 } |
|
100 } |
|
101 |
|
102 fn init_wgpu(event_loop: &EventsLoop, size: dpi::LogicalSize) -> HwWgpuRenderingContext { |
|
103 let builder = WindowBuilder::new() |
15 .with_title("hwengine") |
104 .with_title("hwengine") |
16 .with_dimensions(size); |
105 .with_dimensions(size); |
17 |
106 let window = builder.build(event_loop).unwrap(); |
18 let cxt = ContextBuilder::new() |
107 |
|
108 let instance = wgpu::Instance::new(BackendBit::PRIMARY); |
|
109 |
|
110 let surface = unsafe { instance.create_surface(&window) }; |
|
111 |
|
112 let adapter = block_on(instance.request_adapter(&RequestAdapterOptions { |
|
113 power_preference: PowerPreference::HighPerformance, |
|
114 compatible_surface: Some(&surface), |
|
115 })) |
|
116 .unwrap(); |
|
117 |
|
118 let (device, queue) = block_on(adapter.request_device(&Default::default(), None)).unwrap(); |
|
119 |
|
120 let size = window.get_inner_size().unwrap(); |
|
121 |
|
122 let phys = size.to_physical(window.get_hidpi_factor()); |
|
123 |
|
124 let mut swap_chain = device.create_swap_chain( |
|
125 &surface, |
|
126 &SwapChainDescriptor { |
|
127 usage: TextureUsage::OUTPUT_ATTACHMENT, |
|
128 format: TextureFormat::Bgra8Unorm, |
|
129 width: phys.width as u32, |
|
130 height: phys.height as u32, |
|
131 present_mode: PresentMode::Fifo, |
|
132 }, |
|
133 ); |
|
134 |
|
135 HwWgpuRenderingContext { |
|
136 window, |
|
137 surface, |
|
138 adapter, |
|
139 device, |
|
140 queue, |
|
141 swap_chain, |
|
142 } |
|
143 } |
|
144 |
|
145 fn init_gl(event_loop: &EventsLoop, size: dpi::LogicalSize) -> HwGlRendererContext { |
|
146 use glutin::ContextBuilder; |
|
147 |
|
148 let builder = WindowBuilder::new() |
|
149 .with_title("hwengine") |
|
150 .with_dimensions(size); |
|
151 |
|
152 let context = ContextBuilder::new() |
19 .with_gl(GlRequest::Latest) |
153 .with_gl(GlRequest::Latest) |
20 .with_gl_profile(GlProfile::Core) |
154 .with_gl_profile(GlProfile::Core) |
21 .build_windowed(window, &event_loop) |
155 .build_windowed(builder, &event_loop) |
22 .ok() |
156 .ok() |
23 .unwrap(); |
157 .unwrap(); |
24 |
158 |
25 unsafe { |
159 unsafe { |
26 cxt.make_current().unwrap(); |
160 context.make_current().unwrap(); |
27 gl::load_with(|ptr| cxt.get_proc_address(ptr) as *const _); |
161 gl::load_with(|ptr| context.get_proc_address(ptr) as *const _); |
28 |
162 |
29 if let Some(sz) = cxt.get_inner_size() { |
163 if let Some(sz) = context.get_inner_size() { |
30 let phys = sz.to_physical(cxt.get_hidpi_factor()); |
164 let phys = sz.to_physical(context.get_hidpi_factor()); |
31 |
165 |
32 gl::Viewport(0, 0, phys.width as i32, phys.height as i32); |
166 gl::Viewport(0, 0, phys.width as i32, phys.height as i32); |
33 } |
167 } |
34 } |
168 } |
35 |
169 |
36 cxt |
170 context |
|
171 } |
|
172 |
|
173 fn init(event_loop: &EventsLoop, size: dpi::LogicalSize, use_wgpu: bool) -> HwRendererContext { |
|
174 if use_wgpu { |
|
175 HwRendererContext::Wgpu(init_wgpu(event_loop, size)) |
|
176 } else { |
|
177 HwRendererContext::Gl(init_gl(event_loop, size)) |
|
178 } |
37 } |
179 } |
38 |
180 |
39 fn main() { |
181 fn main() { |
|
182 let use_wgpu = false; |
40 let mut event_loop = EventsLoop::new(); |
183 let mut event_loop = EventsLoop::new(); |
41 let (w, h) = (1024.0, 768.0); |
184 let (w, h) = (1024.0, 768.0); |
42 let window = init(&event_loop, dpi::LogicalSize::new(w, h)); |
185 |
|
186 let mut context = init(&event_loop, dpi::LogicalSize::new(w, h), use_wgpu); |
43 |
187 |
44 let mut engine = EngineInstance::new(); |
188 let mut engine = EngineInstance::new(); |
45 engine.world.create_renderer(w as u16, h as u16); |
189 if !use_wgpu { |
|
190 engine.world.create_renderer(w as u16, h as u16); |
|
191 } |
46 |
192 |
47 let mut dragging = false; |
193 let mut dragging = false; |
48 |
194 |
49 use std::time::Instant; |
195 use std::time::Instant; |
50 |
196 |
51 let mut now = Instant::now(); |
197 let mut now = Instant::now(); |
52 let mut update = Instant::now(); |
198 let mut update_time = Instant::now(); |
53 let mut render = Instant::now(); |
199 let mut render_time = Instant::now(); |
54 |
200 |
55 unsafe { window.make_current().unwrap() }; |
|
56 let mut is_running = true; |
201 let mut is_running = true; |
57 |
202 |
58 while is_running { |
203 while is_running { |
59 let curr = Instant::now(); |
204 let current_time = Instant::now(); |
60 let delta = curr - now; |
205 let delta = current_time - now; |
61 now = curr; |
206 now = current_time; |
62 let ms = delta.as_secs() as f64 * 1000.0 + delta.subsec_millis() as f64; |
207 let ms = delta.as_secs() as f64 * 1000.0 + delta.subsec_millis() as f64; |
63 window.set_title(&format!("hwengine {:.3}ms", ms)); |
208 context.window().set_title(&format!("hwengine {:.3}ms", ms)); |
64 |
209 |
65 if update.elapsed() > Duration::from_millis(10) { |
210 if update_time.elapsed() > Duration::from_millis(10) { |
66 update = curr; |
211 update_time = current_time; |
67 engine.world.step() |
212 engine.world.step() |
68 } |
213 } |
69 |
214 |
70 event_loop.poll_events(|event| match event { |
215 event_loop.poll_events(|event| match event { |
71 Event::WindowEvent { event, .. } => match event { |
216 Event::WindowEvent { event, .. } => match event { |
72 WindowEvent::CloseRequested => { |
217 WindowEvent::CloseRequested => { |
73 is_running = false; |
218 is_running = false; |
74 } |
219 } |
|
220 WindowEvent::Resized(size) => context.update(size), |
|
221 |
75 WindowEvent::MouseInput { button, state, .. } => { |
222 WindowEvent::MouseInput { button, state, .. } => { |
76 if let MouseButton::Right = button { |
223 if let MouseButton::Right = button { |
77 dragging = state == ElementState::Pressed; |
224 dragging = state == ElementState::Pressed; |
78 } |
225 } |
79 } |
226 } |
80 |
227 |
81 WindowEvent::MouseWheel { delta, .. } => { |
228 WindowEvent::MouseWheel { delta, .. } => { |
82 let zoom_change = match delta { |
229 let zoom_change = match delta { |
83 MouseScrollDelta::LineDelta(x, y) => y as f32 * 0.1f32, |
230 MouseScrollDelta::LineDelta(x, y) => y as f32 * 0.1f32, |
84 MouseScrollDelta::PixelDelta(delta) => { |
231 MouseScrollDelta::PixelDelta(delta) => { |
85 let physical = delta.to_physical(window.get_hidpi_factor()); |
232 let physical = delta.to_physical(context.window().get_hidpi_factor()); |
86 physical.y as f32 * 0.1f32 |
233 physical.y as f32 * 0.1f32 |
87 } |
234 } |
88 }; |
235 }; |
89 engine.world.move_camera(Point::ZERO, zoom_change); |
236 engine.world.move_camera(Point::ZERO, zoom_change); |
90 } |
237 } |