-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #115 from robbert-vdh/feature/merge-raw-gl-context
Merge raw-gl-context into baseview to allow OpenGL contexts to be created during window creation
- Loading branch information
Showing
19 changed files
with
1,307 additions
and
229 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,13 +9,18 @@ authors = [ | |
"Billy Messenger <[email protected]>", | ||
"Anton Lazarev <https://antonok.com>", | ||
"Joakim Frostegård <[email protected]>", | ||
"Robbert van der Helm <[email protected]>", | ||
] | ||
edition = "2018" | ||
license = "MIT OR Apache-2.0" | ||
|
||
[features] | ||
default = [] | ||
opengl = ["uuid", "x11/glx"] | ||
|
||
[dependencies] | ||
keyboard-types = { version = "0.6.1", default-features = false } | ||
raw-window-handle = "0.3.3" | ||
raw-window-handle = "0.4.2" | ||
|
||
[target.'cfg(target_os="linux")'.dependencies] | ||
xcb = { version = "0.9", features = ["thread", "xlib_xcb", "dri2"] } | ||
|
@@ -25,6 +30,7 @@ nix = "0.22.0" | |
|
||
[target.'cfg(target_os="windows")'.dependencies] | ||
winapi = { version = "0.3.8", features = ["libloaderapi", "winuser", "windef", "minwindef", "guiddef", "combaseapi", "wingdi", "errhandlingapi"] } | ||
uuid = { version = "0.8", features = ["v4"], optional = true } | ||
|
||
[target.'cfg(target_os="macos")'.dependencies] | ||
cocoa = "0.24.0" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
use std::ffi::c_void; | ||
use std::str::FromStr; | ||
|
||
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; | ||
|
||
use cocoa::appkit::{ | ||
NSOpenGLContext, NSOpenGLContextParameter, NSOpenGLPFAAccelerated, NSOpenGLPFAAlphaSize, | ||
NSOpenGLPFAColorSize, NSOpenGLPFADepthSize, NSOpenGLPFADoubleBuffer, NSOpenGLPFAMultisample, | ||
NSOpenGLPFAOpenGLProfile, NSOpenGLPFASampleBuffers, NSOpenGLPFASamples, NSOpenGLPFAStencilSize, | ||
NSOpenGLPixelFormat, NSOpenGLProfileVersion3_2Core, NSOpenGLProfileVersion4_1Core, | ||
NSOpenGLProfileVersionLegacy, NSOpenGLView, NSView, | ||
}; | ||
use cocoa::base::{id, nil, YES}; | ||
|
||
use core_foundation::base::TCFType; | ||
use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; | ||
use core_foundation::string::CFString; | ||
|
||
use objc::{msg_send, sel, sel_impl}; | ||
|
||
use super::{GlConfig, GlError, Profile}; | ||
|
||
pub type CreationFailedError = (); | ||
pub struct GlContext { | ||
view: id, | ||
context: id, | ||
} | ||
|
||
impl GlContext { | ||
pub unsafe fn create( | ||
parent: &impl HasRawWindowHandle, config: GlConfig, | ||
) -> Result<GlContext, GlError> { | ||
let handle = if let RawWindowHandle::AppKit(handle) = parent.raw_window_handle() { | ||
handle | ||
} else { | ||
return Err(GlError::InvalidWindowHandle); | ||
}; | ||
|
||
if handle.ns_view.is_null() { | ||
return Err(GlError::InvalidWindowHandle); | ||
} | ||
|
||
let parent_view = handle.ns_view as id; | ||
|
||
let version = if config.version < (3, 2) && config.profile == Profile::Compatibility { | ||
NSOpenGLProfileVersionLegacy | ||
} else if config.version == (3, 2) && config.profile == Profile::Core { | ||
NSOpenGLProfileVersion3_2Core | ||
} else if config.version > (3, 2) && config.profile == Profile::Core { | ||
NSOpenGLProfileVersion4_1Core | ||
} else { | ||
return Err(GlError::VersionNotSupported); | ||
}; | ||
|
||
#[rustfmt::skip] | ||
let mut attrs = vec![ | ||
NSOpenGLPFAOpenGLProfile as u32, version as u32, | ||
NSOpenGLPFAColorSize as u32, (config.red_bits + config.blue_bits + config.green_bits) as u32, | ||
NSOpenGLPFAAlphaSize as u32, config.alpha_bits as u32, | ||
NSOpenGLPFADepthSize as u32, config.depth_bits as u32, | ||
NSOpenGLPFAStencilSize as u32, config.stencil_bits as u32, | ||
NSOpenGLPFAAccelerated as u32, | ||
]; | ||
|
||
if config.samples.is_some() { | ||
#[rustfmt::skip] | ||
attrs.extend_from_slice(&[ | ||
NSOpenGLPFAMultisample as u32, | ||
NSOpenGLPFASampleBuffers as u32, 1, | ||
NSOpenGLPFASamples as u32, config.samples.unwrap() as u32, | ||
]); | ||
} | ||
|
||
if config.double_buffer { | ||
attrs.push(NSOpenGLPFADoubleBuffer as u32); | ||
} | ||
|
||
attrs.push(0); | ||
|
||
let pixel_format = NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attrs); | ||
|
||
if pixel_format == nil { | ||
return Err(GlError::CreationFailed(())); | ||
} | ||
|
||
let view = | ||
NSOpenGLView::alloc(nil).initWithFrame_pixelFormat_(parent_view.frame(), pixel_format); | ||
|
||
if view == nil { | ||
return Err(GlError::CreationFailed(())); | ||
} | ||
|
||
view.setWantsBestResolutionOpenGLSurface_(YES); | ||
|
||
let () = msg_send![view, retain]; | ||
NSOpenGLView::display_(view); | ||
parent_view.addSubview_(view); | ||
|
||
let context: id = msg_send![view, openGLContext]; | ||
let () = msg_send![context, retain]; | ||
|
||
context.setValues_forParameter_( | ||
&(config.vsync as i32), | ||
NSOpenGLContextParameter::NSOpenGLCPSwapInterval, | ||
); | ||
|
||
let () = msg_send![pixel_format, release]; | ||
|
||
Ok(GlContext { view, context }) | ||
} | ||
|
||
pub unsafe fn make_current(&self) { | ||
self.context.makeCurrentContext(); | ||
} | ||
|
||
pub unsafe fn make_not_current(&self) { | ||
NSOpenGLContext::clearCurrentContext(self.context); | ||
} | ||
|
||
pub fn get_proc_address(&self, symbol: &str) -> *const c_void { | ||
let symbol_name = CFString::from_str(symbol).unwrap(); | ||
let framework_name = CFString::from_str("com.apple.opengl").unwrap(); | ||
let framework = | ||
unsafe { CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) }; | ||
let addr = unsafe { | ||
CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef()) | ||
}; | ||
addr as *const c_void | ||
} | ||
|
||
pub fn swap_buffers(&self) { | ||
unsafe { | ||
self.context.flushBuffer(); | ||
let () = msg_send![self.view, setNeedsDisplay: YES]; | ||
} | ||
} | ||
} | ||
|
||
impl Drop for GlContext { | ||
fn drop(&mut self) { | ||
unsafe { | ||
let () = msg_send![self.context, release]; | ||
let () = msg_send![self.view, release]; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
use std::ffi::c_void; | ||
use std::marker::PhantomData; | ||
|
||
// On X11 creating the context is a two step process | ||
#[cfg(not(target_os = "linux"))] | ||
use raw_window_handle::HasRawWindowHandle; | ||
|
||
#[cfg(target_os = "windows")] | ||
mod win; | ||
#[cfg(target_os = "windows")] | ||
use win as platform; | ||
|
||
// We need to use this directly within the X11 window creation to negotiate the correct visual | ||
#[cfg(target_os = "linux")] | ||
pub(crate) mod x11; | ||
#[cfg(target_os = "linux")] | ||
pub(crate) use self::x11 as platform; | ||
|
||
#[cfg(target_os = "macos")] | ||
mod macos; | ||
#[cfg(target_os = "macos")] | ||
use macos as platform; | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct GlConfig { | ||
pub version: (u8, u8), | ||
pub profile: Profile, | ||
pub red_bits: u8, | ||
pub blue_bits: u8, | ||
pub green_bits: u8, | ||
pub alpha_bits: u8, | ||
pub depth_bits: u8, | ||
pub stencil_bits: u8, | ||
pub samples: Option<u8>, | ||
pub srgb: bool, | ||
pub double_buffer: bool, | ||
pub vsync: bool, | ||
} | ||
|
||
impl Default for GlConfig { | ||
fn default() -> Self { | ||
GlConfig { | ||
version: (3, 2), | ||
profile: Profile::Core, | ||
red_bits: 8, | ||
blue_bits: 8, | ||
green_bits: 8, | ||
alpha_bits: 8, | ||
depth_bits: 24, | ||
stencil_bits: 8, | ||
samples: None, | ||
srgb: true, | ||
double_buffer: true, | ||
vsync: false, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
pub enum Profile { | ||
Compatibility, | ||
Core, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum GlError { | ||
InvalidWindowHandle, | ||
VersionNotSupported, | ||
CreationFailed(platform::CreationFailedError), | ||
} | ||
|
||
pub struct GlContext { | ||
context: platform::GlContext, | ||
phantom: PhantomData<*mut ()>, | ||
} | ||
|
||
impl GlContext { | ||
#[cfg(not(target_os = "linux"))] | ||
pub(crate) unsafe fn create( | ||
parent: &impl HasRawWindowHandle, config: GlConfig, | ||
) -> Result<GlContext, GlError> { | ||
platform::GlContext::create(parent, config) | ||
.map(|context| GlContext { context, phantom: PhantomData }) | ||
} | ||
|
||
/// The X11 version needs to be set up in a different way compared to the Windows and macOS | ||
/// versions. So the platform-specific versions should be used to construct the context within | ||
/// baseview, and then this object can be passed to the user. | ||
#[cfg(target_os = "linux")] | ||
pub(crate) fn new(context: platform::GlContext) -> GlContext { | ||
GlContext { context, phantom: PhantomData } | ||
} | ||
|
||
pub unsafe fn make_current(&self) { | ||
self.context.make_current(); | ||
} | ||
|
||
pub unsafe fn make_not_current(&self) { | ||
self.context.make_not_current(); | ||
} | ||
|
||
pub fn get_proc_address(&self, symbol: &str) -> *const c_void { | ||
self.context.get_proc_address(symbol) | ||
} | ||
|
||
pub fn swap_buffers(&self) { | ||
self.context.swap_buffers(); | ||
} | ||
} |
Oops, something went wrong.