Something went wrong. Try again.
a servo-backed webview component for gpui
Something went wrong. Try again.
7.3 kB · 222 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223use std::{ cell::{Cell, RefCell}, ffi::c_void, rc::Rc, sync::Arc,};
use core_foundation::base::TCFType;use core_video::pixel_buffer::CVPixelBuffer;use dpi::PhysicalSize;use euclid::{Size2D, default::Size2D as UntypedSize2D};use gleam::gl::{self, Gl};use servo::{DeviceIntRect, RenderingContext, RgbaImage};use surfman::{ Connection, Context, ContextAttributeFlags, ContextAttributes, Device, Error, GLApi, Surface, SurfaceAccess, SurfaceInfo, SurfaceTexture, SurfaceType, chains::{PreserveBuffer, SwapChain, SwapChainAPI},};
/// A surfman-backed offscreen GL surface, exposed to gpui as a `CVPixelBuffer`.////// Servo renders into this surface; each frame is presented to an `IOSurface`/// that gpui's `Window::paint_surface` can composite into the window.pub struct IoSurfaceBridge { size: Cell<PhysicalSize<u32>>, gleam_gl: Rc<dyn Gl>, glow_gl: Arc<glow::Context>, device: RefCell<Device>, context: RefCell<Context>, swap_chain: SwapChain<Device>, pixel_buffer: RefCell<Option<CVPixelBuffer>>,}
impl IoSurfaceBridge { pub fn new(size: PhysicalSize<u32>) -> Result<Rc<Self>, Error> { assert!( size.width > 0 && size.height > 0, "size must be at least 1x1" );
let connection = Connection::new()?; let adapter = connection.create_hardware_adapter()?; let device = connection.create_device(&adapter)?;
let flags = ContextAttributeFlags::ALPHA | ContextAttributeFlags::DEPTH | ContextAttributeFlags::STENCIL; let version = match connection.gl_api() { GLApi::GLES => surfman::GLVersion { major: 3, minor: 0 }, GLApi::GL => surfman::GLVersion { major: 3, minor: 2 }, }; let descriptor = device.create_context_descriptor(&ContextAttributes { flags, version })?; let mut context = device.create_context(&descriptor, None)?;
#[allow(unsafe_code)] let gleam_gl = match connection.gl_api() { GLApi::GL => unsafe { gl::GlFns::load_with(|name| device.get_proc_address(&context, name)) }, GLApi::GLES => unsafe { gl::GlesFns::load_with(|name| device.get_proc_address(&context, name)) }, }; #[allow(unsafe_code)] let glow_gl = unsafe { glow::Context::from_loader_function(|name| device.get_proc_address(&context, name)) };
let surfman_size = Size2D::new(size.width as i32, size.height as i32); let surface = device.create_surface( &context, SurfaceAccess::GPUOnly, SurfaceType::Generic { size: surfman_size }, )?; device .bind_surface_to_context(&mut context, surface) .map_err(|(err, mut surface)| { let _ = device.destroy_surface(&mut context, &mut surface); err })?; device.make_context_current(&context)?;
let swap_chain = SwapChain::create_attached(&device, &mut context, SurfaceAccess::GPUOnly)?;
Ok(Rc::new(Self { size: Cell::new(size), gleam_gl, glow_gl: Arc::new(glow_gl), device: RefCell::new(device), context: RefCell::new(context), swap_chain, pixel_buffer: RefCell::new(None), })) }
pub fn current_pixel_buffer(&self) -> Option<CVPixelBuffer> { self.pixel_buffer.borrow().clone() }
fn framebuffer_id(&self) -> u32 { let device = self.device.borrow(); let context = self.context.borrow(); device .context_surface_info(&context) .unwrap_or(None) .and_then(|info| info.framebuffer_object) .map_or(0, |fb| fb.0.get()) }}
impl Drop for IoSurfaceBridge { fn drop(&mut self) { let device = &mut self.device.borrow_mut(); let context = &mut self.context.borrow_mut(); let _ = self.swap_chain.destroy(device, context); let _ = device.destroy_context(context); }}
impl RenderingContext for IoSurfaceBridge { fn prepare_for_rendering(&self) { self.gleam_gl .bind_framebuffer(gl::FRAMEBUFFER, self.framebuffer_id()); }
fn read_to_image(&self, _source_rectangle: DeviceIntRect) -> Option<RgbaImage> { None }
fn size(&self) -> PhysicalSize<u32> { self.size.get() }
fn resize(&self, size: PhysicalSize<u32>) { if size.width == 0 || size.height == 0 || self.size.get() == size { return; } self.size.set(size); let device = &mut self.device.borrow_mut(); let context = &mut self.context.borrow_mut(); let size = Size2D::new(size.width as i32, size.height as i32); let _ = self.swap_chain.resize(device, context, size); }
fn present(&self) { { let device = self.device.borrow(); let context = &mut self.context.borrow_mut(); let _ = self .swap_chain .swap_buffers(&device, context, PreserveBuffer::No); }
if let Some(surface) = self.swap_chain.take_surface() { let device = self.device.borrow(); let native = device.native_surface(&surface); #[allow(unsafe_code, deprecated)] let pixel_buffer = unsafe { let raw = (&*native.0 as *const _) as *const c_void; let io_surface = io_surface::IOSurface::wrap_under_get_rule(raw as io_surface::IOSurfaceRef); CVPixelBuffer::from_io_surface(&io_surface, None).ok() }; if pixel_buffer.is_some() { *self.pixel_buffer.borrow_mut() = pixel_buffer; } drop(device); self.swap_chain.recycle_surface(surface); } }
fn make_current(&self) -> Result<(), Error> { let device = self.device.borrow(); device.make_context_current(&self.context.borrow()) }
fn gleam_gl_api(&self) -> Rc<dyn Gl> { Rc::clone(&self.gleam_gl) }
fn glow_gl_api(&self) -> Arc<glow::Context> { Arc::clone(&self.glow_gl) }
fn connection(&self) -> Option<Connection> { Some(self.device.borrow().connection()) }
fn create_texture( &self, surface: Surface, ) -> Option<(SurfaceTexture, u32, UntypedSize2D<i32>)> { let device = self.device.borrow(); let context = &mut self.context.borrow_mut(); let SurfaceInfo { size, .. } = device.surface_info(&surface); let surface_texture = device.create_surface_texture(context, surface).ok()?; let gl_texture = device .surface_texture_object(&surface_texture) .map(|tex| tex.0.get()) .unwrap_or(0); Some((surface_texture, gl_texture, size)) }
fn destroy_texture(&self, surface_texture: SurfaceTexture) -> Option<Surface> { let device = self.device.borrow(); let context = &mut self.context.borrow_mut(); device .destroy_surface_texture(context, surface_texture) .map_err(|(error, _)| error) .ok() }}