1 | //! Everything related to `GLXContext`. |
2 | |
3 | use std::fmt; |
4 | use std::marker::PhantomData; |
5 | use std::ops::Deref; |
6 | use std::os::raw::c_int; |
7 | |
8 | use glutin_glx_sys::glx::types::GLXContext; |
9 | use glutin_glx_sys::{glx, glx_extra}; |
10 | |
11 | use crate::config::GetGlConfig; |
12 | use crate::context::{ |
13 | self, AsRawContext, ContextApi, ContextAttributes, GlProfile, RawContext, ReleaseBehavior, |
14 | Robustness, Version, |
15 | }; |
16 | use crate::display::{DisplayFeatures, GetGlDisplay}; |
17 | use crate::error::{ErrorKind, Result}; |
18 | use crate::prelude::*; |
19 | use crate::private::Sealed; |
20 | use crate::surface::SurfaceTypeTrait; |
21 | |
22 | use super::config::Config; |
23 | use super::display::Display; |
24 | use super::surface::Surface; |
25 | |
26 | impl Display { |
27 | pub(crate) unsafe fn create_context( |
28 | &self, |
29 | config: &Config, |
30 | context_attributes: &ContextAttributes, |
31 | ) -> Result<NotCurrentContext> { |
32 | let shared_context = if let Some(shared_context) = |
33 | context_attributes.shared_context.as_ref() |
34 | { |
35 | match shared_context { |
36 | RawContext::Glx(shared_context) => *shared_context, |
37 | #[allow (unreachable_patterns)] |
38 | _ => return Err(ErrorKind::NotSupported("incompatible context was passed" ).into()), |
39 | } |
40 | } else { |
41 | std::ptr::null() |
42 | }; |
43 | |
44 | let context = if self.inner.client_extensions.contains("GLX_ARB_create_context" ) |
45 | && self.inner.glx_extra.is_some() |
46 | { |
47 | self.create_context_arb(config, context_attributes, shared_context)? |
48 | } else { |
49 | self.create_context_legacy(config, shared_context)? |
50 | }; |
51 | |
52 | // Failed to create the context. |
53 | if context.is_null() { |
54 | return Err(ErrorKind::BadContext.into()); |
55 | } |
56 | |
57 | let config = config.clone(); |
58 | let is_gles = matches!(context_attributes.api, Some(ContextApi::Gles(_))); |
59 | let inner = |
60 | ContextInner { display: self.clone(), config, raw: GlxContext(context), is_gles }; |
61 | |
62 | Ok(NotCurrentContext::new(inner)) |
63 | } |
64 | |
65 | fn create_context_arb( |
66 | &self, |
67 | config: &Config, |
68 | context_attributes: &ContextAttributes, |
69 | shared_context: GLXContext, |
70 | ) -> Result<GLXContext> { |
71 | let extra = self.inner.glx_extra.as_ref().unwrap(); |
72 | let mut attrs = Vec::<c_int>::with_capacity(16); |
73 | |
74 | // Check whether the ES context creation is supported. |
75 | let supports_es = self.inner.features.contains(DisplayFeatures::CREATE_ES_CONTEXT); |
76 | |
77 | let (profile, version) = match context_attributes.api { |
78 | api @ Some(ContextApi::OpenGl(_)) | api @ None => { |
79 | let version = api.and_then(|api| api.version()); |
80 | let (profile, version) = context::pick_profile(context_attributes.profile, version); |
81 | let profile = match profile { |
82 | GlProfile::Core => glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB, |
83 | GlProfile::Compatibility => glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, |
84 | }; |
85 | |
86 | (Some(profile), Some(version)) |
87 | }, |
88 | Some(ContextApi::Gles(version)) if supports_es => ( |
89 | Some(glx_extra::CONTEXT_ES2_PROFILE_BIT_EXT), |
90 | Some(version.unwrap_or(Version::new(2, 0))), |
91 | ), |
92 | _ => { |
93 | return Err(ErrorKind::NotSupported( |
94 | "extension to create ES context with glx is not present." , |
95 | ) |
96 | .into()) |
97 | }, |
98 | }; |
99 | |
100 | // Set the profile. |
101 | if let Some(profile) = profile { |
102 | attrs.push(glx_extra::CONTEXT_PROFILE_MASK_ARB as c_int); |
103 | attrs.push(profile as c_int); |
104 | } |
105 | |
106 | // Add version. |
107 | if let Some(version) = version { |
108 | attrs.push(glx_extra::CONTEXT_MAJOR_VERSION_ARB as c_int); |
109 | attrs.push(version.major as c_int); |
110 | attrs.push(glx_extra::CONTEXT_MINOR_VERSION_ARB as c_int); |
111 | attrs.push(version.minor as c_int); |
112 | } |
113 | |
114 | if let Some(profile) = context_attributes.profile { |
115 | let profile = match profile { |
116 | GlProfile::Core => glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB, |
117 | GlProfile::Compatibility => glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, |
118 | }; |
119 | |
120 | attrs.push(glx_extra::CONTEXT_PROFILE_MASK_ARB as c_int); |
121 | attrs.push(profile as c_int); |
122 | } |
123 | |
124 | let mut flags: c_int = 0; |
125 | let mut requested_no_error = false; |
126 | if self.inner.features.contains(DisplayFeatures::CONTEXT_ROBUSTNESS) { |
127 | match context_attributes.robustness { |
128 | Robustness::NotRobust => (), |
129 | Robustness::RobustNoResetNotification => { |
130 | attrs.push(glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int); |
131 | attrs.push(glx_extra::NO_RESET_NOTIFICATION_ARB as c_int); |
132 | flags |= glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int; |
133 | }, |
134 | Robustness::RobustLoseContextOnReset => { |
135 | attrs.push(glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int); |
136 | attrs.push(glx_extra::LOSE_CONTEXT_ON_RESET_ARB as c_int); |
137 | flags |= glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int; |
138 | }, |
139 | Robustness::NoError => { |
140 | if !self.inner.features.contains(DisplayFeatures::CONTEXT_NO_ERROR) { |
141 | return Err(ErrorKind::NotSupported( |
142 | "GLX_ARB_create_context_no_error not supported" , |
143 | ) |
144 | .into()); |
145 | } |
146 | |
147 | attrs.push(glx_extra::CONTEXT_OPENGL_NO_ERROR_ARB as c_int); |
148 | attrs.push(1); |
149 | requested_no_error = true; |
150 | }, |
151 | } |
152 | } else if context_attributes.robustness != Robustness::NotRobust { |
153 | return Err(ErrorKind::NotSupported( |
154 | "GLX_ARB_create_context_robustness is not supported" , |
155 | ) |
156 | .into()); |
157 | } |
158 | |
159 | // Debug flag. |
160 | if context_attributes.debug && !requested_no_error { |
161 | flags |= glx_extra::CONTEXT_DEBUG_BIT_ARB as c_int; |
162 | } |
163 | |
164 | if flags != 0 { |
165 | attrs.push(glx_extra::CONTEXT_FLAGS_ARB as c_int); |
166 | attrs.push(flags as c_int); |
167 | } |
168 | |
169 | // Flush control. |
170 | if self.inner.features.contains(DisplayFeatures::CONTEXT_RELEASE_BEHAVIOR) { |
171 | match context_attributes.release_behavior { |
172 | // This is the default behavior in specification. |
173 | // |
174 | // XXX passing it explicitly causing issues with older mesa versions. |
175 | ReleaseBehavior::Flush => (), |
176 | ReleaseBehavior::None => { |
177 | attrs.push(glx_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int); |
178 | attrs.push(glx_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as c_int); |
179 | }, |
180 | } |
181 | } else if context_attributes.release_behavior != ReleaseBehavior::Flush { |
182 | return Err(ErrorKind::NotSupported( |
183 | "flush control behavior GLX_ARB_context_flush_control" , |
184 | ) |
185 | .into()); |
186 | } |
187 | |
188 | // Terminate list with zero. |
189 | attrs.push(0); |
190 | |
191 | super::last_glx_error(|| unsafe { |
192 | extra.CreateContextAttribsARB( |
193 | self.inner.raw.cast(), |
194 | *config.inner.raw, |
195 | shared_context, |
196 | // Direct context |
197 | 1, |
198 | attrs.as_ptr(), |
199 | ) |
200 | }) |
201 | } |
202 | |
203 | fn create_context_legacy( |
204 | &self, |
205 | config: &Config, |
206 | shared_context: GLXContext, |
207 | ) -> Result<GLXContext> { |
208 | let render_type = |
209 | if config.float_pixels() { glx_extra::RGBA_FLOAT_TYPE_ARB } else { glx::RGBA_TYPE }; |
210 | |
211 | super::last_glx_error(|| unsafe { |
212 | self.inner.glx.CreateNewContext( |
213 | self.inner.raw.cast(), |
214 | *config.inner.raw, |
215 | render_type as c_int, |
216 | shared_context, |
217 | // Direct context. |
218 | 1, |
219 | ) |
220 | }) |
221 | } |
222 | } |
223 | |
224 | /// A wrapper around `GLXContext` that is known to be not current. |
225 | #[derive (Debug)] |
226 | pub struct NotCurrentContext { |
227 | inner: ContextInner, |
228 | } |
229 | |
230 | impl NotCurrentContext { |
231 | fn new(inner: ContextInner) -> Self { |
232 | Self { inner } |
233 | } |
234 | } |
235 | |
236 | impl NotCurrentGlContext for NotCurrentContext { |
237 | type PossiblyCurrentContext = PossiblyCurrentContext; |
238 | type Surface<T: SurfaceTypeTrait> = Surface<T>; |
239 | |
240 | fn treat_as_possibly_current(self) -> PossiblyCurrentContext { |
241 | PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData } |
242 | } |
243 | |
244 | fn make_current<T: SurfaceTypeTrait>( |
245 | self, |
246 | surface: &Self::Surface<T>, |
247 | ) -> Result<Self::PossiblyCurrentContext> { |
248 | self.inner.make_current_draw_read(surface, surface)?; |
249 | Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) |
250 | } |
251 | |
252 | fn make_current_draw_read<T: SurfaceTypeTrait>( |
253 | self, |
254 | surface_draw: &Self::Surface<T>, |
255 | surface_read: &Self::Surface<T>, |
256 | ) -> Result<Self::PossiblyCurrentContext> { |
257 | self.inner.make_current_draw_read(surface_draw, surface_read)?; |
258 | Ok(PossiblyCurrentContext { inner: self.inner, _nosendsync: PhantomData }) |
259 | } |
260 | } |
261 | |
262 | impl GlContext for NotCurrentContext { |
263 | fn context_api(&self) -> ContextApi { |
264 | self.inner.context_api() |
265 | } |
266 | } |
267 | |
268 | impl GetGlConfig for NotCurrentContext { |
269 | type Target = Config; |
270 | |
271 | fn config(&self) -> Self::Target { |
272 | self.inner.config.clone() |
273 | } |
274 | } |
275 | |
276 | impl GetGlDisplay for NotCurrentContext { |
277 | type Target = Display; |
278 | |
279 | fn display(&self) -> Self::Target { |
280 | self.inner.display.clone() |
281 | } |
282 | } |
283 | |
284 | impl AsRawContext for NotCurrentContext { |
285 | fn raw_context(&self) -> RawContext { |
286 | RawContext::Glx(*self.inner.raw) |
287 | } |
288 | } |
289 | |
290 | impl Sealed for NotCurrentContext {} |
291 | |
292 | /// A wrapper around `GLXContext` that could be current for the current thread. |
293 | #[derive (Debug)] |
294 | pub struct PossiblyCurrentContext { |
295 | inner: ContextInner, |
296 | // The context could be current only on the one thread. |
297 | _nosendsync: PhantomData<GLXContext>, |
298 | } |
299 | |
300 | impl PossiblyCurrentGlContext for PossiblyCurrentContext { |
301 | type NotCurrentContext = NotCurrentContext; |
302 | type Surface<T: SurfaceTypeTrait> = Surface<T>; |
303 | |
304 | fn make_not_current(self) -> Result<Self::NotCurrentContext> { |
305 | self.inner.make_not_current()?; |
306 | Ok(NotCurrentContext::new(self.inner)) |
307 | } |
308 | |
309 | fn is_current(&self) -> bool { |
310 | unsafe { self.inner.display.inner.glx.GetCurrentContext() == *self.inner.raw } |
311 | } |
312 | |
313 | fn make_current<T: SurfaceTypeTrait>(&self, surface: &Self::Surface<T>) -> Result<()> { |
314 | self.inner.make_current_draw_read(surface, surface) |
315 | } |
316 | |
317 | fn make_current_draw_read<T: SurfaceTypeTrait>( |
318 | &self, |
319 | surface_draw: &Self::Surface<T>, |
320 | surface_read: &Self::Surface<T>, |
321 | ) -> Result<()> { |
322 | self.inner.make_current_draw_read(surface_draw, surface_read) |
323 | } |
324 | } |
325 | |
326 | impl GlContext for PossiblyCurrentContext { |
327 | fn context_api(&self) -> ContextApi { |
328 | self.inner.context_api() |
329 | } |
330 | } |
331 | |
332 | impl GetGlConfig for PossiblyCurrentContext { |
333 | type Target = Config; |
334 | |
335 | fn config(&self) -> Self::Target { |
336 | self.inner.config.clone() |
337 | } |
338 | } |
339 | |
340 | impl GetGlDisplay for PossiblyCurrentContext { |
341 | type Target = Display; |
342 | |
343 | fn display(&self) -> Self::Target { |
344 | self.inner.display.clone() |
345 | } |
346 | } |
347 | |
348 | impl AsRawContext for PossiblyCurrentContext { |
349 | fn raw_context(&self) -> RawContext { |
350 | RawContext::Glx(*self.inner.raw) |
351 | } |
352 | } |
353 | |
354 | impl Sealed for PossiblyCurrentContext {} |
355 | |
356 | struct ContextInner { |
357 | display: Display, |
358 | config: Config, |
359 | raw: GlxContext, |
360 | is_gles: bool, |
361 | } |
362 | |
363 | impl ContextInner { |
364 | fn make_current_draw_read<T: SurfaceTypeTrait>( |
365 | &self, |
366 | surface_draw: &Surface<T>, |
367 | surface_read: &Surface<T>, |
368 | ) -> Result<()> { |
369 | super::last_glx_error(|| unsafe { |
370 | self.display.inner.glx.MakeContextCurrent( |
371 | self.display.inner.raw.cast(), |
372 | surface_draw.raw, |
373 | surface_read.raw, |
374 | *self.raw, |
375 | ); |
376 | }) |
377 | } |
378 | |
379 | fn make_not_current(&self) -> Result<()> { |
380 | super::last_glx_error(|| unsafe { |
381 | self.display.inner.glx.MakeContextCurrent( |
382 | self.display.inner.raw.cast(), |
383 | 0, |
384 | 0, |
385 | std::ptr::null(), |
386 | ); |
387 | }) |
388 | } |
389 | |
390 | fn context_api(&self) -> ContextApi { |
391 | if self.is_gles { |
392 | ContextApi::Gles(None) |
393 | } else { |
394 | ContextApi::OpenGl(None) |
395 | } |
396 | } |
397 | } |
398 | |
399 | impl Drop for ContextInner { |
400 | fn drop(&mut self) { |
401 | let _ = super::last_glx_error(|| unsafe { |
402 | self.display.inner.glx.DestroyContext(self.display.inner.raw.cast(), *self.raw); |
403 | }); |
404 | } |
405 | } |
406 | |
407 | impl fmt::Debug for ContextInner { |
408 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
409 | f&mut DebugStruct<'_, '_>.debug_struct("Context" ) |
410 | .field("display" , &self.display.inner.raw) |
411 | .field("config" , &self.config.inner.raw) |
412 | .field(name:"raw" , &self.raw) |
413 | .finish() |
414 | } |
415 | } |
416 | |
417 | #[derive (Debug)] |
418 | struct GlxContext(GLXContext); |
419 | |
420 | unsafe impl Send for GlxContext {} |
421 | |
422 | impl Deref for GlxContext { |
423 | type Target = GLXContext; |
424 | |
425 | fn deref(&self) -> &Self::Target { |
426 | &self.0 |
427 | } |
428 | } |
429 | |