use std::{ffi::CString, num::NonZeroU32, sync::Arc};
use glutin::display::DisplayApiPreference;
use glutin::{
config::{ConfigTemplateBuilder, GetGlConfig, GlConfig},
context::{ContextAttributesBuilder, PossiblyCurrentContext},
display::{Display, GetGlDisplay},
prelude::{GlDisplay, NotCurrentGlContext, PossiblyCurrentGlContext},
surface::{GlSurface, SurfaceAttributesBuilder, WindowSurface},
};
use skia_safe::{
Surface,
gpu::{
DirectContext, direct_contexts,
gl::{FramebufferInfo, Interface},
},
};
use crate::window_renderer::SkiaBackend;
pub(crate) struct OpenGLBackend {
surface: Option<Surface>,
gr_context: DirectContext,
gl_surface: glutin::surface::Surface<WindowSurface>,
gl_context: PossiblyCurrentContext,
fb_info: FramebufferInfo,
}
impl OpenGLBackend {
pub(crate) fn new(
window: Arc<dyn anyrender::WindowHandle>,
width: u32,
height: u32,
) -> OpenGLBackend {
let raw_display_handle = window.display_handle().unwrap().as_raw();
let raw_window_handle = window.window_handle().unwrap().as_raw();
let gl_display = unsafe {
Display::new(
raw_display_handle,
#[cfg(any(target_os = "macos", target_os = "ios"))]
DisplayApiPreference::Cgl,
#[cfg(target_os = "windows")]
DisplayApiPreference::Wgl(Some(raw_window_handle.clone())),
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))]
DisplayApiPreference::Egl,
)
.unwrap()
};
let gl_config_template = ConfigTemplateBuilder::new().with_transparency(true).build();
let gl_config = unsafe {
gl_display
.find_configs(gl_config_template)
.unwrap()
.reduce(|accum, config| {
let transparency_check = config.supports_transparency().unwrap_or(false)
& !accum.supports_transparency().unwrap_or(false);
if transparency_check || config.num_samples() < accum.num_samples() {
config
} else {
accum
}
})
.unwrap()
};
let gl_context_attrs = ContextAttributesBuilder::new().build(Some(raw_window_handle));
let gl_surface_attrs = SurfaceAttributesBuilder::<WindowSurface>::new().build(
raw_window_handle,
NonZeroU32::new(width).expect("width should be a positive value"),
NonZeroU32::new(height).expect("height should be a positive value"),
);
let gl_not_current_context = unsafe {
gl_display
.create_context(&gl_config, &gl_context_attrs)
.unwrap()
};
let gl_surface = unsafe {
gl_config
.display()
.create_window_surface(&gl_config, &gl_surface_attrs)
.unwrap()
};
let gl_context = gl_not_current_context.make_current(&gl_surface).unwrap();
gl::load_with(|s| {
gl_config
.display()
.get_proc_address(CString::new(s).unwrap().as_c_str())
});
let interface = Interface::new_load_with(|name| {
if name == "eglGetCurrentDisplay" {
return std::ptr::null();
}
gl_config
.display()
.get_proc_address(CString::new(name).unwrap().as_c_str())
})
.unwrap();
let mut gr_context = direct_contexts::make_gl(interface, None).unwrap();
let mut fb_info = {
let mut fboid: gl::types::GLint = 0;
unsafe {
gl::GetIntegerv(gl::FRAMEBUFFER_BINDING, &mut fboid);
}
FramebufferInfo {
fboid: fboid.try_into().unwrap(),
format: skia_safe::gpu::gl::Format::RGBA8.into(),
..Default::default()
}
};
OpenGLBackend {
surface: Some(Self::create_surface(
width,
height,
&mut gr_context,
&gl_surface,
&gl_context,
&mut fb_info,
)),
gr_context,
gl_surface,
gl_context,
fb_info,
}
}
fn create_surface(
width: u32,
height: u32,
gr_context: &mut DirectContext,
gl_surface: &glutin::surface::Surface<WindowSurface>,
gl_context: &PossiblyCurrentContext,
fb_info: &mut FramebufferInfo,
) -> Surface {
gl_surface.resize(
gl_context,
NonZeroU32::new(width).unwrap(),
NonZeroU32::new(height).unwrap(),
);
let backend_render_target = skia_safe::gpu::backend_render_targets::make_gl(
(width as i32, height as i32),
gl_context.config().num_samples() as usize,
gl_context.config().stencil_size() as usize,
*fb_info,
);
skia_safe::gpu::surfaces::wrap_backend_render_target(
gr_context,
&backend_render_target,
skia_safe::gpu::SurfaceOrigin::BottomLeft,
skia_safe::ColorType::RGBA8888,
None,
None,
)
.unwrap()
}
}
impl SkiaBackend for OpenGLBackend {
fn set_size(&mut self, width: u32, height: u32) {
self.surface = Some(Self::create_surface(
width,
height,
&mut self.gr_context,
&self.gl_surface,
&self.gl_context,
&mut self.fb_info,
));
}
fn prepare(&mut self) -> Option<Surface> {
self.gl_context.make_current(&self.gl_surface).unwrap();
self.surface.take()
}
fn flush(&mut self, mut surface: Surface) {
self.gr_context.flush_and_submit();
self.gl_surface.swap_buffers(&self.gl_context).unwrap();
surface.canvas().discard();
self.surface = Some(surface);
}
}