From b83533739581c35333e694f41398c9a5cce1e9f3 Mon Sep 17 00:00:00 2001 From: Zakor Gyula Date: Wed, 22 May 2019 10:09:56 +0200 Subject: [PATCH 1/2] Add IOSurface support --- Cargo.toml | 1 + src/draw_buffer.rs | 220 ++++++++++++++++++++++++++++++++++++++++++++- src/gl_context.rs | 43 ++++++++- src/lib.rs | 2 + 4 files changed, 260 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 98f90dfd..d1368be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ serde = { version = "1.0", optional = true } [target.x86_64-apple-darwin.dependencies] core-foundation = "0.6" cgl = "0.2.3" +io-surface = "0.12.0" [target.'cfg(target_os = "ios")'.dependencies] objc = "0.2" diff --git a/src/draw_buffer.rs b/src/draw_buffer.rs index 4e59142..8201b9c 100644 --- a/src/draw_buffer.rs +++ b/src/draw_buffer.rs @@ -1,15 +1,21 @@ use euclid::Size2D; use gleam::gl; use gleam::gl::types::{GLuint, GLenum, GLint}; +#[cfg(target_os="macos")] +use io_surface::{IOSurface, IOSurfaceID}; use std::rc::Rc; +use std::mem; use crate::GLContext; use crate::NativeGLContextMethods; -#[derive(Debug)] + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum ColorAttachmentType { Texture, Renderbuffer, + #[cfg(target_os="macos")] + IOSurface, } impl Default for ColorAttachmentType { @@ -18,6 +24,10 @@ impl Default for ColorAttachmentType { } } +#[cfg(target_os="macos")] +const SURFACE_COUNT: usize = 3; +#[cfg(target_os="macos")] +const BYTES_PER_PIXEL: i32 = 4; /// We either have a color renderbuffer, or a surface bound to a texture bound /// to a framebuffer as a color attachment. @@ -28,6 +38,13 @@ impl Default for ColorAttachmentType { pub enum ColorAttachment { Renderbuffer(GLuint), Texture(GLuint), + #[cfg(target_os="macos")] + IOSurface { + surfaces: [(GLuint, IOSurfaceID); SURFACE_COUNT], + wr_visible: usize, + complete: usize, + active: usize, + }, } impl ColorAttachment { @@ -35,6 +52,8 @@ impl ColorAttachment { match *self { ColorAttachment::Renderbuffer(_) => ColorAttachmentType::Renderbuffer, ColorAttachment::Texture(_) => ColorAttachmentType::Texture, + #[cfg(target_os="macos")] + ColorAttachment::IOSurface{..} => ColorAttachmentType::IOSurface, } } @@ -42,6 +61,64 @@ impl ColorAttachment { match self { ColorAttachment::Renderbuffer(id) => gl.delete_renderbuffers(&[id]), ColorAttachment::Texture(tex_id) => gl.delete_textures(&[tex_id]), + #[cfg(target_os="macos")] + ColorAttachment::IOSurface{ surfaces, .. } => { + for (text, _) in surfaces.iter() { + gl.delete_textures(&[*text]); + } + } + + } + } + + #[cfg(target_os="macos")] + fn active_texture(&self) -> GLuint { + match *self { + ColorAttachment::Renderbuffer(_) => panic!("no texture for renderbuffer attachment"), + ColorAttachment::Texture(active) => active, + ColorAttachment::IOSurface{ surfaces, wr_visible: _, complete: _, active } => { + surfaces[active].0 + } + } + } + + #[cfg(target_os="macos")] + fn complete_surface(&self) -> Option { + match *self { + ColorAttachment::IOSurface{ surfaces, wr_visible: _, complete, active: _ } => { + Some(surfaces[complete].1) + } + _ => None, + } + } + + #[cfg(target_os="macos")] + fn wr_visible_surface(&self) -> Option { + match *self { + ColorAttachment::IOSurface{ surfaces, wr_visible, complete: _, active: _ } => { + Some(surfaces[wr_visible].1) + } + _ => None, + } + } + + #[cfg(target_os="macos")] + fn swap_active_texture(&mut self) { + match *self { + ColorAttachment::IOSurface{ surfaces: _, wr_visible: _, ref mut complete, ref mut active } => { + mem::swap(complete, active); + } + _ => (), + } + } + + #[cfg(target_os="macos")] + fn swap_wr_visible_texture(&mut self) { + match *self { + ColorAttachment::IOSurface{ surfaces: _, ref mut wr_visible, ref mut complete, active: _ } => { + mem::swap(complete, wr_visible); + } + _ => (), } } } @@ -60,6 +137,8 @@ pub struct DrawBuffer { depth_renderbuffer: GLuint, packed_depth_stencil_renderbuffer: GLuint, // samples: GLsizei, + #[cfg(target_os="macos")] + io_surfaces: Vec, } /// Helper function to create a render buffer @@ -114,6 +193,8 @@ impl DrawBuffer { depth_renderbuffer: 0, packed_depth_stencil_renderbuffer: 0, // samples: 0, + #[cfg(target_os="macos")] + io_surfaces: vec![], }; context.make_current()?; @@ -156,6 +237,21 @@ impl DrawBuffer { match self.color_attachment.as_ref().unwrap() { &ColorAttachment::Renderbuffer(_) => None, &ColorAttachment::Texture(id) => Some(id), + #[cfg(target_os="macos")] + &ColorAttachment::IOSurface{ surfaces, wr_visible: _, complete: _, active } => { + Some(surfaces[active].0) + } + } + } + + #[cfg(target_os="macos")] + pub fn get_active_io_surface_id(&self) -> Option { + match self.color_attachment.as_ref().unwrap() { + &ColorAttachment::Renderbuffer(_) => None, + &ColorAttachment::Texture(_) => None, + &ColorAttachment::IOSurface{ surfaces, wr_visible: _, complete: _, active } => { + Some(surfaces[active].1) + } } } @@ -188,8 +284,17 @@ impl DrawBuffer { debug_assert!(texture != 0); self.gl().bind_texture(gl::TEXTURE_2D, texture); - self.gl().tex_image_2d(gl::TEXTURE_2D, 0, - formats.texture_internal as GLint, self.size.width, self.size.height, 0, formats.texture, formats.texture_type, None); + self.gl().tex_image_2d( + gl::TEXTURE_2D, + 0, + formats.texture_internal as GLint, + self.size.width, + self.size.height, + 0, + formats.texture, + formats.texture_type, + None + ); // Low filtering to allow rendering self.gl().tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint); @@ -205,6 +310,69 @@ impl DrawBuffer { Some(ColorAttachment::Texture(texture)) }, + #[cfg(target_os="macos")] + ColorAttachmentType::IOSurface => { + use core_foundation::base::TCFType; + use core_foundation::dictionary::CFDictionary; + use core_foundation::string::CFString; + use core_foundation::number::CFNumber; + use core_foundation::boolean::CFBoolean; + + let mut create_texture = || { + let texture = self.gl().gen_textures(1)[0]; + debug_assert!(texture != 0); + + self.gl().bind_texture(gl::TEXTURE_RECTANGLE_ARB, texture); + let has_alpha = match formats.texture { + gl::RGB => false, + gl::RGBA => true, + _ => unimplemented!(), + }; + let io_surface = unsafe { + let props = CFDictionary::from_CFType_pairs( + &[ + (CFString::wrap_under_get_rule(io_surface::kIOSurfaceWidth),CFNumber::from(self.size.width).as_CFType()), + (CFString::wrap_under_get_rule(io_surface::kIOSurfaceHeight),CFNumber::from(self.size.height).as_CFType()), + (CFString::wrap_under_get_rule(io_surface::kIOSurfaceBytesPerElement),CFNumber::from(BYTES_PER_PIXEL).as_CFType()), + (CFString::wrap_under_get_rule(io_surface::kIOSurfaceBytesPerRow),CFNumber::from(self.size.width * BYTES_PER_PIXEL).as_CFType()), + (CFString::wrap_under_get_rule(io_surface::kIOSurfaceIsGlobal),CFBoolean::from(true).as_CFType()), + ] + ); + io_surface::new(&props) + }; + + io_surface.bind_to_gl_texture(self.size.width, self.size.height, has_alpha); + + // Low filtering to allow rendering + self.gl().tex_parameter_i(gl::TEXTURE_RECTANGLE_ARB, gl::TEXTURE_MAG_FILTER, gl::NEAREST as GLint); + self.gl().tex_parameter_i(gl::TEXTURE_RECTANGLE_ARB, gl::TEXTURE_MIN_FILTER, gl::NEAREST as GLint); + + // TODO(emilio): Check if these two are neccessary, probably not + self.gl().tex_parameter_i(gl::TEXTURE_RECTANGLE_ARB, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as GLint); + self.gl().tex_parameter_i(gl::TEXTURE_RECTANGLE_ARB, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as GLint); + + self.gl().bind_texture(gl::TEXTURE_RECTANGLE_ARB, 0); + + debug_assert_eq!(self.gl().get_error(), gl::NO_ERROR); + + let surface_id = io_surface.get_id(); + + self.io_surfaces.push(io_surface); + + (texture, surface_id) + }; + + let wr_visible = create_texture(); + let complete = create_texture(); + let active = create_texture(); + + Some(ColorAttachment::IOSurface { + surfaces: [wr_visible, complete, active], + wr_visible: 0, + complete: 1, + active: 2, + }) + }, }; // After this we check if we need stencil and depth buffers @@ -230,6 +398,45 @@ impl DrawBuffer { self.attach_to_framebuffer() } + /// Swap the internal read and draw textures, returning the id of the texture + /// now used for reading. + #[cfg(target_os="macos")] + pub fn swap_framebuffer_texture(&mut self) -> Option { + self.gl().finish(); + let (active_texture_id, complete_surface_id) = match self.color_attachment { + Some(ref mut attachment) => { + attachment.swap_active_texture(); + ( + attachment.active_texture(), + attachment.complete_surface(), + ) + } + None => return None, + }; + self.gl().bind_framebuffer(gl::FRAMEBUFFER, self.framebuffer); + self.gl().framebuffer_texture_2d( + gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_RECTANGLE_ARB, + active_texture_id, + 0 + ); + complete_surface_id + } + + /// Swap the WR visible and complete texture, returning the id of + /// the IOSurface which we will send to the WR thread + #[cfg(target_os="macos")] + pub fn swap_wr_visible_texture(&mut self) -> Option { + match self.color_attachment { + Some(ref mut attachment) => { + attachment.swap_wr_visible_texture(); + attachment.wr_visible_surface() + } + None => None, + } + } + fn attach_to_framebuffer(&mut self) -> Result<(), &'static str> { self.gl().bind_framebuffer(gl::FRAMEBUFFER, self.framebuffer); // NOTE: The assertion fails if the framebuffer is not bound @@ -249,6 +456,13 @@ impl DrawBuffer { gl::TEXTURE_2D, texture_id, 0); }, + #[cfg(target_os="macos")] + ColorAttachment::IOSurface{ surfaces, wr_visible: _, complete: _, active } => { + self.gl().framebuffer_texture_2d(gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::TEXTURE_RECTANGLE_ARB, + surfaces[active].0, 0); + } } if self.packed_depth_stencil_renderbuffer != 0 { diff --git a/src/gl_context.rs b/src/gl_context.rs index 487ccd2..97ccce0 100644 --- a/src/gl_context.rs +++ b/src/gl_context.rs @@ -188,6 +188,35 @@ impl GLContext &self.limits } + /// Swap the backing textures for the draw buffer, returning the id of + /// the IOSurface now used for reading. Resets the new active texture to an + /// appropriate initial state; + #[cfg(target_os="macos")] + pub fn swap_draw_buffer( + &mut self, + clear_color: (f32, f32, f32, f32), + mask: u32, + ) -> Option { + let surface_id = match self.draw_buffer { + Some(ref mut db) => db.swap_framebuffer_texture(), + None => return None, + }; + // TODO: support preserveDrawBuffer instead of clearing new frame + self.reset_draw_buffer_contents(Some(clear_color), Some(mask)); + surface_id + } + + /// Swap the WR visible and complete texture, returning the id of + /// the IOSurface which we will send to the WR thread + #[cfg(target_os="macos")] + pub fn handle_lock(&mut self) -> Option { + let surface_id = match self.draw_buffer { + Some(ref mut db) => db.swap_wr_visible_texture(), + None => return None, + }; + surface_id + } + pub fn borrow_draw_buffer(&self) -> Option<&DrawBuffer> { self.draw_buffer.as_ref() } @@ -224,13 +253,21 @@ impl GLContext self.extensions.clone() } + fn reset_draw_buffer_contents( + &self, + clear_color: Option<(f32, f32, f32, f32)>, + mask: Option, + ) { + let cc = clear_color.unwrap_or((0., 0., 0., 0.)); + self.gl().clear_color(cc.0, cc.1, cc.2, if !self.attributes.alpha { 1.0 } else { cc.3 }); + self.gl().clear(mask.unwrap_or(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT)); + } + fn init_offscreen(&mut self, size: Size2D, color_attachment_type: ColorAttachmentType) -> Result<(), &'static str> { self.create_draw_buffer(size, color_attachment_type)?; debug_assert!(self.is_current()); - - self.gl().clear_color(0.0, 0.0, 0.0, 0.0); - self.gl().clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT); + self.reset_draw_buffer_contents(None, None); self.gl().scissor(0, 0, size.width, size.height); self.gl().viewport(0, 0, size.width, size.height); diff --git a/src/lib.rs b/src/lib.rs index 45e1fd8..40386a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,8 @@ extern crate lazy_static; #[cfg(target_os = "ios")] #[macro_use] extern crate objc; +#[cfg(target_os="macos")] +extern crate io_surface; mod platform; pub use platform::{NativeGLContext, NativeGLContextMethods, NativeGLContextHandle}; From 165d683a2f30514169f4b007d1fc2b1db7bbac79 Mon Sep 17 00:00:00 2001 From: Zakor Gyula Date: Wed, 24 Jul 2019 14:00:10 +0200 Subject: [PATCH 2/2] Update to 0.23 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d1368be..2646b78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "offscreen_gl_context" license = "MIT / Apache-2.0" edition = "2018" -version = "0.22.4" +version = "0.23.0" authors = ["Emilio Cobos Álvarez ", "The Servo Project Developers"] description = "Creation and manipulation of HW accelerated offscreen rendering contexts in multiple platforms. Originally intended for the Servo project's WebGL implementation." repository = "https://github.com/servo/rust-offscreen-rendering-context"