From 51f805b0fe25e20ca894e071d8511c848772099a Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 24 Jun 2013 18:54:02 -0700 Subject: [PATCH] Add the interface for safe GL context wrapping and implement on Mac. Linux implementation will come next. --- context.rs | 29 +++++++ dummy.rs => platform/dummy.rs | 0 macos.rs => platform/macos.rs | 154 ++++++++++++++++++++++------------ sharegl.rc | 14 +++- 4 files changed, 139 insertions(+), 58 deletions(-) create mode 100644 context.rs rename dummy.rs => platform/dummy.rs (100%) rename macos.rs => platform/macos.rs (54%) diff --git a/context.rs b/context.rs new file mode 100644 index 0000000..5e74c37 --- /dev/null +++ b/context.rs @@ -0,0 +1,29 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A platform-independent interface to 3D graphics contexts. + +/// Platform-independent interface to 3D graphics contexts. +pub trait GraphicsContextMethods { + /// Returns the current 3D graphics context, incrementing its reference count. + pub fn current() -> Self; + + /// Wraps the given instance of the native 3D context, incrementing its reference count. + pub fn wrap(instance: NativeContextType) -> Self; + + /// Returns the underlying native 3D context without modifying its reference count. + pub fn native(&self) -> NativeContextType; + + /// Creates a new offscreen 3D graphics context. + pub fn new() -> Self; + + /// Makes this context the current context, so that all graphics operations will go here. + pub fn make_current(&self); +} + diff --git a/dummy.rs b/platform/dummy.rs similarity index 100% rename from dummy.rs rename to platform/dummy.rs diff --git a/macos.rs b/platform/macos.rs similarity index 54% rename from macos.rs rename to platform/macos.rs index 071a0e0..1a8b988 100644 --- a/macos.rs +++ b/platform/macos.rs @@ -7,37 +7,102 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub extern mod core_foundation; -pub extern mod io_surface; -pub extern mod opengles; - -use platform::opengles::gl2; +use base::ShareContext; +use context::GraphicsContextMethods; use core::cast::transmute; use core::ptr::{null, to_unsafe_ptr}; use geom::size::Size2D; -use platform::io_surface::{IOSurface, kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow}; -use platform::io_surface::{kIOSurfaceHeight, kIOSurfaceIsGlobal, kIOSurfaceWidth}; -use platform::io_surface::IOSurfaceMethods; -use platform::opengles::cgl::{CGLChoosePixelFormat, CGLContextObj, CGLCreateContext}; -use platform::opengles::cgl::{CGLLockContext, CGLSetCurrentContext, CGLTexImageIOSurface2D}; -use platform::opengles::cgl::{kCGLNoError, kCGLPFACompliant, kCGLPFADoubleBuffer}; -use platform::opengles::gl2::{BGRA, CLAMP_TO_EDGE, COLOR_ATTACHMENT0, FRAMEBUFFER}; -use platform::opengles::gl2::{FRAMEBUFFER_COMPLETE, GLenum, GLint, GLsizei, GLuint, LINEAR}; -use platform::opengles::gl2::{NEAREST, RGBA, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER}; -use platform::opengles::gl2::{TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, TEXTURE_WRAP_T}; -use platform::opengles::gl2::{UNSIGNED_INT_8_8_8_8_REV}; - -use base::ShareContext; +use io_surface::{IOSurface, kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow}; +use io_surface::{kIOSurfaceHeight, kIOSurfaceIsGlobal, kIOSurfaceWidth}; +use io_surface::IOSurfaceMethods; +use opengles::cgl::{CGLChoosePixelFormat, CGLContextObj, CGLCreateContext, CGLGetCurrentContext}; +use opengles::cgl::{CGLReleaseContext, CGLRetainContext, CGLSetCurrentContext}; +use opengles::cgl::{CGLTexImageIOSurface2D, kCGLNoError, kCGLPFACompliant, kCGLPFADoubleBuffer}; +use opengles::gl2::{BGRA, CLAMP_TO_EDGE, COLOR_ATTACHMENT0, FRAMEBUFFER}; +use opengles::gl2::{FRAMEBUFFER_COMPLETE, GLenum, GLint, GLsizei, GLuint, LINEAR}; +use opengles::gl2::{NEAREST, RGBA, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER}; +use opengles::gl2::{TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, TEXTURE_WRAP_T}; +use opengles::gl2::{UNSIGNED_INT_8_8_8_8_REV}; +use opengles::gl2; // FIXME: This is not good. #[link_args="-framework IOSurface -framework CoreFoundation"] #[nolink] extern {} -pub type Context = MacContext; +/// Mac-specific interface to 3D graphics contexts. +pub struct GraphicsContext { + contents: CGLContextObj +} + +impl GraphicsContextMethods for GraphicsContext { + /// Returns the current graphics context. + fn current() -> GraphicsContext { + unsafe { + GraphicsContextMethods::wrap(CGLGetCurrentContext()) + } + } -pub struct MacContext { + /// Wraps the given instance of the native Core OpenGL graphics context. + fn wrap(instance: CGLContextObj) -> GraphicsContext { + unsafe { + GraphicsContext { + contents: CGLRetainContext(instance) + } + } + } + + /// Returns the underlying native 3D context without modifying its reference count. + fn native(&self) -> CGLContextObj { + self.contents + } + + /// Creates a new offscreen 3D graphics context. + fn new() -> GraphicsContext { + unsafe { + // Choose a pixel format. + let attributes = [ kCGLPFADoubleBuffer, kCGLPFACompliant, 0 ]; + let pixel_format = null(); + let pixel_format_count = 1; + let gl_error = CGLChoosePixelFormat(transmute(&attributes[0]), + to_unsafe_ptr(&pixel_format), + to_unsafe_ptr(&pixel_format_count)); + assert!(gl_error == kCGLNoError); + + // Create the context. + let cgl_context = null(); + let gl_error = CGLCreateContext(pixel_format, null(), to_unsafe_ptr(&cgl_context)); + assert!(gl_error == kCGLNoError); + + GraphicsContextMethods::wrap(cgl_context) + } + } + + /// Makes this context the current context. + fn make_current(&self) { + unsafe { + let gl_error = CGLSetCurrentContext(self.contents); + assert!(gl_error == kCGLNoError) + } + } +} + +impl Drop for GraphicsContext { + fn finalize(&self) { + unsafe { + CGLReleaseContext(self.native()) + } + } +} + +impl Clone for GraphicsContext { + fn clone(&self) -> GraphicsContext { + GraphicsContextMethods::wrap(self.native()) + } +} + +pub struct Context { surface: IOSurface, framebuffer: GLuint, texture: GLuint @@ -45,40 +110,19 @@ pub struct MacContext { // FIXME: Needs drop. } -pub fn init_cgl() -> CGLContextObj { - // FIXME: We should expose some higher-level, safe abstractions inside the CGL module. - unsafe { - // Choose a pixel format. - let attributes = [ kCGLPFADoubleBuffer, kCGLPFACompliant, 0 ]; - let pixel_format = null(); - let pixel_format_count = 1; - let gl_error = CGLChoosePixelFormat(transmute(&attributes[0]), - to_unsafe_ptr(&pixel_format), - to_unsafe_ptr(&pixel_format_count)); - assert!(gl_error == kCGLNoError); - - // Create the context. - let cgl_context = null(); - let gl_error = CGLCreateContext(pixel_format, null(), to_unsafe_ptr(&cgl_context)); - assert!(gl_error == kCGLNoError); - - // Set the context. - let gl_error = CGLSetCurrentContext(cgl_context); - assert!(gl_error == kCGLNoError); - - // Lock the context. - let gl_error = CGLLockContext(cgl_context); - assert!(gl_error == kCGLNoError); - - return cgl_context; - } +pub fn init_cgl() -> GraphicsContext { + let context: GraphicsContext = GraphicsContextMethods::new(); + context.make_current(); + context } pub fn init_surface(size: Size2D) -> IOSurface { - use platform::core_foundation::boolean::CFBoolean; - use number = platform::core_foundation::number::CFNumber::new; - use string = platform::core_foundation::string::CFString::wrap_shared; + use core_foundation::boolean::CFBoolean; + use core_foundation; + use io_surface; + use number = core_foundation::number::CFNumber::new; + use string = core_foundation::string::CFString::wrap_shared; // TODO: dictionary constructor should be less ridiculous. // Or, we could add bindings for mutable dictionaries. @@ -119,10 +163,10 @@ pub fn init_texture() -> GLuint { } // Assumes the texture is already bound via gl2::bind_texture(). -pub fn bind_surface_to_texture(context: &CGLContextObj, surface: &IOSurface, size: Size2D) { +pub fn bind_surface_to_texture(context: &GraphicsContext, surface: &IOSurface, size: Size2D) { // FIXME: There should be safe wrappers for this. unsafe { - let gl_error = CGLTexImageIOSurface2D(*context, + let gl_error = CGLTexImageIOSurface2D(context.native(), TEXTURE_RECTANGLE_ARB, RGBA as GLenum, size.width as GLsizei, @@ -141,8 +185,8 @@ pub fn bind_texture_to_framebuffer(texture: GLuint) { assert!(gl2::check_framebuffer_status(FRAMEBUFFER) == FRAMEBUFFER_COMPLETE); } -impl ShareContext for MacContext { - fn new(size: Size2D) -> MacContext { +impl ShareContext for Context { + fn new(size: Size2D) -> Context { // Initialize CGL. let context = init_cgl(); @@ -160,7 +204,7 @@ impl ShareContext for MacContext { // Bind the texture to the framebuffer. bind_texture_to_framebuffer(texture); - MacContext { + Context { surface: surface, framebuffer: framebuffer, texture: texture diff --git a/sharegl.rc b/sharegl.rc index 9caac6e..ded19d6 100644 --- a/sharegl.rc +++ b/sharegl.rc @@ -14,16 +14,24 @@ extern mod std; extern mod geom; +#[cfg(target_os="macos")] +extern mod core_foundation; +#[cfg(target_os="macos")] +extern mod io_surface; +#[cfg(target_os="macos")] +extern mod opengles; + pub mod base; +pub mod context; #[cfg(target_os="macos")] -#[path="macos.rs"] +#[path="platform/macos.rs"] pub mod platform; #[cfg(target_os="linux")] -#[path="dummy.rs"] +#[path="platform/dummy.rs"] pub mod platform; #[cfg(target_os="windows")] -#[path="dummy.rs"] +#[path="platform/dummy.rs"] pub mod platform;