three_d/window/winit_window/
windowed_context.rs1use crate::Context;
2use crate::SurfaceSettings;
3use crate::WindowError;
4use std::sync::Arc;
5use winit::window::Window;
6
7#[cfg(target_arch = "wasm32")]
8mod inner {
9 use crate::HardwareAcceleration;
10 use serde::{Deserialize, Serialize};
11 use wasm_bindgen::JsCast;
12 use winit::platform::web::WindowExtWebSys;
13
14 use super::*;
15 #[allow(non_snake_case)]
16 #[derive(Serialize, Deserialize)]
17 struct ContextOpt {
18 pub antialias: bool,
19 pub depth: bool,
20 pub stencil: bool,
21 pub willReadFrequently: bool,
22 pub alpha: bool,
23 }
24
25 pub struct WindowedContext {
27 pub(super) context: Context,
28 }
29
30 impl WindowedContext {
31 pub fn from_winit_window(
33 window: &Window,
34 settings: SurfaceSettings,
35 ) -> Result<Self, WindowError> {
36 let canvas = window.canvas();
37
38 let webgl_context = canvas
40 .get_context_with_context_options(
41 "webgl2",
42 &serde_wasm_bindgen::to_value(&ContextOpt {
43 antialias: settings.multisamples > 0,
44 depth: settings.depth_buffer > 0,
45 stencil: settings.stencil_buffer > 0,
46 willReadFrequently: match settings.hardware_acceleration {
47 HardwareAcceleration::Required => false,
48 HardwareAcceleration::Preferred => false,
49 HardwareAcceleration::Off => true,
50 },
51 alpha: false,
52 })
53 .unwrap(),
54 )
55 .map_err(|e| WindowError::WebGL2NotSupported(format!(": {:?}", e)))?
56 .ok_or(WindowError::WebGL2NotSupported("".to_string()))?
57 .dyn_into::<web_sys::WebGl2RenderingContext>()
58 .map_err(|e| WindowError::WebGL2NotSupported(format!(": {:?}", e)))?;
59 webgl_context
60 .get_extension("EXT_color_buffer_float")
61 .map_err(|e| WindowError::ColorBufferFloatNotSupported(format!("{:?}", e)))?;
62 webgl_context
63 .get_extension("OES_texture_float_linear")
64 .map_err(|e| WindowError::OESTextureFloatNotSupported(format!(": {:?}", e)))?;
65 webgl_context
66 .get_extension("OES_texture_half_float_linear")
67 .map_err(|e| WindowError::OESTextureFloatNotSupported(format!(": {:?}", e)))?;
68
69 Ok(Self {
70 context: Context::from_gl_context(Arc::new(
71 crate::context::Context::from_webgl2_context(webgl_context),
72 ))?,
73 })
74 }
75
76 pub fn resize(&self, _physical_size: winit::dpi::PhysicalSize<u32>) {}
78
79 pub fn make_current(&self) -> Result<(), WindowError> {
81 Ok(())
82 }
83
84 pub fn swap_buffers(&self) -> Result<(), WindowError> {
86 Ok(())
87 }
88 }
89}
90
91#[cfg(not(target_arch = "wasm32"))]
92mod inner {
93 use glutin::{prelude::PossiblyCurrentContextGlSurfaceAccessor, surface::*};
94
95 use super::*;
96 pub struct WindowedContext {
101 pub(super) context: Context,
102 surface: Surface<WindowSurface>,
103 glutin_context: glutin::context::PossiblyCurrentContext,
104 }
105
106 impl WindowedContext {
107 #[allow(unsafe_code)]
109 pub fn from_winit_window(
110 window: &Window,
111 settings: SurfaceSettings,
112 ) -> Result<Self, WindowError> {
113 if settings.multisamples > 0 && !settings.multisamples.is_power_of_two() {
114 Err(WindowError::InvalidNumberOfMSAASamples)?;
115 }
116 use glutin::prelude::*;
117 use raw_window_handle::*;
118 let raw_display_handle = window.raw_display_handle();
119 let raw_window_handle = window.raw_window_handle();
120
121 #[cfg(target_os = "windows")]
129 let preference =
130 glutin::display::DisplayApiPreference::WglThenEgl(Some(raw_window_handle));
131 #[cfg(target_os = "linux")]
133 let preference = glutin::display::DisplayApiPreference::EglThenGlx(Box::new(
134 winit::platform::x11::register_xlib_error_hook,
135 ));
136 #[cfg(target_os = "macos")]
137 let preference = glutin::display::DisplayApiPreference::Cgl;
138 #[cfg(target_os = "android")]
139 let preference = glutin::display::DisplayApiPreference::Egl;
140
141 let gl_display =
142 unsafe { glutin::display::Display::new(raw_display_handle, preference)? };
143 let swap_interval = if settings.vsync {
144 glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap())
145 } else {
146 glutin::surface::SwapInterval::DontWait
147 };
148
149 let hardware_acceleration = match settings.hardware_acceleration {
150 crate::HardwareAcceleration::Required => Some(true),
151 crate::HardwareAcceleration::Preferred => None,
152 crate::HardwareAcceleration::Off => Some(false),
153 };
154 let config_template = glutin::config::ConfigTemplateBuilder::new()
155 .prefer_hardware_accelerated(hardware_acceleration)
156 .with_depth_size(settings.depth_buffer);
157 let config_template = if settings.multisamples > 0 {
159 config_template.with_multisampling(settings.multisamples)
160 } else {
161 config_template
162 };
163 let config_template = config_template
164 .with_stencil_size(settings.stencil_buffer)
165 .compatible_with_native_window(raw_window_handle)
166 .build();
167 let config = unsafe {
172 gl_display
173 .find_configs(config_template)?
174 .next()
175 .ok_or(WindowError::SurfaceCreationError)?
176 };
177
178 let context_attributes =
179 glutin::context::ContextAttributesBuilder::new().build(Some(raw_window_handle));
180 let (width, height): (u32, u32) = window.inner_size().into();
182 let width = std::num::NonZeroU32::new(width.max(1)).unwrap();
183 let height = std::num::NonZeroU32::new(height.max(1)).unwrap();
184 let surface_attributes =
185 glutin::surface::SurfaceAttributesBuilder::<glutin::surface::WindowSurface>::new()
186 .build(raw_window_handle, width, height);
187 let gl_context = unsafe { gl_display.create_context(&config, &context_attributes)? };
189
190 let gl_surface =
191 unsafe { gl_display.create_window_surface(&config, &surface_attributes)? };
192 let gl_context = gl_context.make_current(&gl_surface)?;
193 gl_surface.set_swap_interval(&gl_context, swap_interval)?;
194
195 Ok(Self {
196 context: Context::from_gl_context(Arc::new(unsafe {
197 crate::context::Context::from_loader_function(|s| {
198 let s = std::ffi::CString::new(s)
199 .expect("failed to construct C string from string for gl proc address");
200
201 gl_display.get_proc_address(&s)
202 })
203 }))?,
204 glutin_context: gl_context,
205 surface: gl_surface,
206 })
207 }
208
209 pub fn resize(&self, physical_size: winit::dpi::PhysicalSize<u32>) {
211 let width = std::num::NonZeroU32::new(physical_size.width.max(1)).unwrap();
212 let height = std::num::NonZeroU32::new(physical_size.height.max(1)).unwrap();
213 self.surface.resize(&self.glutin_context, width, height);
214 }
215
216 pub fn make_current(&self) -> Result<(), WindowError> {
218 Ok(self.glutin_context.make_current(&self.surface)?)
219 }
220
221 pub fn swap_buffers(&self) -> Result<(), WindowError> {
223 Ok(self.surface.swap_buffers(&self.glutin_context)?)
224 }
225
226 pub fn set_vsync(&self, enabled: bool) -> Result<(), WindowError> {
228 let swap_interval = if enabled {
229 glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap())
230 } else {
231 glutin::surface::SwapInterval::DontWait
232 };
233 Ok(self
234 .surface
235 .set_swap_interval(&self.glutin_context, swap_interval)?)
236 }
237 }
238}
239
240pub use inner::*;
241
242impl std::ops::Deref for WindowedContext {
243 type Target = Context;
244
245 fn deref(&self) -> &Self::Target {
246 &self.context
247 }
248}