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 {
100 pub(super) context: Context,
101 surface: Surface<WindowSurface>,
102 glutin_context: glutin::context::PossiblyCurrentContext,
103 }
104
105 impl WindowedContext {
106 #[allow(unsafe_code)]
108 pub fn from_winit_window(
109 window: &Window,
110 settings: SurfaceSettings,
111 ) -> Result<Self, WindowError> {
112 if settings.multisamples > 0 && !settings.multisamples.is_power_of_two() {
113 Err(WindowError::InvalidNumberOfMSAASamples)?;
114 }
115 use glutin::prelude::*;
116 use raw_window_handle::*;
117 let raw_display_handle = window.raw_display_handle();
118 let raw_window_handle = window.raw_window_handle();
119
120 #[cfg(target_os = "windows")]
128 let preference =
129 glutin::display::DisplayApiPreference::WglThenEgl(Some(raw_window_handle));
130 #[cfg(target_os = "linux")]
132 let preference = glutin::display::DisplayApiPreference::EglThenGlx(Box::new(
133 winit::platform::x11::register_xlib_error_hook,
134 ));
135 #[cfg(target_os = "macos")]
136 let preference = glutin::display::DisplayApiPreference::Cgl;
137 #[cfg(target_os = "android")]
138 let preference = glutin::display::DisplayApiPreference::Egl;
139
140 let gl_display =
141 unsafe { glutin::display::Display::new(raw_display_handle, preference)? };
142 let swap_interval = if settings.vsync {
143 glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap())
144 } else {
145 glutin::surface::SwapInterval::DontWait
146 };
147
148 let hardware_acceleration = match settings.hardware_acceleration {
149 crate::HardwareAcceleration::Required => Some(true),
150 crate::HardwareAcceleration::Preferred => None,
151 crate::HardwareAcceleration::Off => Some(false),
152 };
153 let config_template = glutin::config::ConfigTemplateBuilder::new()
154 .prefer_hardware_accelerated(hardware_acceleration)
155 .with_depth_size(settings.depth_buffer);
156 let config_template = if settings.multisamples > 0 {
158 config_template.with_multisampling(settings.multisamples)
159 } else {
160 config_template
161 };
162 let config_template = config_template
163 .with_stencil_size(settings.stencil_buffer)
164 .compatible_with_native_window(raw_window_handle)
165 .build();
166 let config = unsafe {
171 gl_display
172 .find_configs(config_template)?
173 .next()
174 .ok_or(WindowError::SurfaceCreationError)?
175 };
176
177 let context_attributes =
178 glutin::context::ContextAttributesBuilder::new().build(Some(raw_window_handle));
179 let (width, height): (u32, u32) = window.inner_size().into();
181 let width = std::num::NonZeroU32::new(width.max(1)).unwrap();
182 let height = std::num::NonZeroU32::new(height.max(1)).unwrap();
183 let surface_attributes =
184 glutin::surface::SurfaceAttributesBuilder::<glutin::surface::WindowSurface>::new()
185 .build(raw_window_handle, width, height);
186 let gl_context = unsafe { gl_display.create_context(&config, &context_attributes)? };
188
189 let gl_surface =
190 unsafe { gl_display.create_window_surface(&config, &surface_attributes)? };
191 let gl_context = gl_context.make_current(&gl_surface)?;
192 gl_surface.set_swap_interval(&gl_context, swap_interval)?;
193
194 Ok(Self {
195 context: Context::from_gl_context(Arc::new(unsafe {
196 crate::context::Context::from_loader_function(|s| {
197 let s = std::ffi::CString::new(s)
198 .expect("failed to construct C string from string for gl proc address");
199
200 gl_display.get_proc_address(&s)
201 })
202 }))?,
203 glutin_context: gl_context,
204 surface: gl_surface,
205 })
206 }
207
208 pub fn resize(&self, physical_size: winit::dpi::PhysicalSize<u32>) {
210 let width = std::num::NonZeroU32::new(physical_size.width.max(1)).unwrap();
211 let height = std::num::NonZeroU32::new(physical_size.height.max(1)).unwrap();
212 self.surface.resize(&self.glutin_context, width, height);
213 }
214
215 pub fn make_current(&self) -> Result<(), WindowError> {
217 Ok(self.glutin_context.make_current(&self.surface)?)
218 }
219
220 pub fn swap_buffers(&self) -> Result<(), WindowError> {
222 Ok(self.surface.swap_buffers(&self.glutin_context)?)
223 }
224
225 pub fn set_vsync(&self, enabled: bool) -> Result<(), WindowError> {
227 let swap_interval = if enabled {
228 glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap())
229 } else {
230 glutin::surface::SwapInterval::DontWait
231 };
232 Ok(self
233 .surface
234 .set_swap_interval(&self.glutin_context, swap_interval)?)
235 }
236 }
237}
238
239pub use inner::*;
240
241impl std::ops::Deref for WindowedContext {
242 type Target = Context;
243
244 fn deref(&self) -> &Self::Target {
245 &self.context
246 }
247}