use std::cell::RefCell;
use std::ffi::c_void;
use skia_safe::gpu::{self, DirectContext, graphite};
use skia_safe::{Canvas, ColorType, Surface};
unsafe extern "C" {
fn editor_graphite_make_dawn(instance: *mut c_void, device: *mut c_void, queue: *mut c_void) -> *mut c_void;
fn editor_graphite_dawn_texture(out: *mut graphite::BackendTexture, texture: *mut c_void);
fn editor_graphite_make_recorder(context: *mut c_void) -> *mut c_void;
fn wgpuCreateInstance(descriptor: *const c_void) -> *mut c_void;
fn wgpuTextureRelease(texture: *mut c_void);
}
struct Graphite {
context: graphite::Context,
recorder: graphite::Recorder,
}
struct Ganesh {
context: DirectContext,
surface: Option<Surface>,
}
#[derive(Default)]
struct State {
graphite: Option<Graphite>,
ganesh: std::collections::HashMap<u64, Ganesh>,
}
thread_local! {
static STATE: RefCell<State> = RefCell::default();
}
pub fn webgpu_instance() -> usize {
unsafe { wgpuCreateInstance(std::ptr::null()) as usize }
}
pub unsafe fn attach_webgpu(instance: usize, device: usize, queue: usize) -> bool {
let (instance, device, queue) = (instance as *mut c_void, device as *mut c_void, queue as *mut c_void);
let context: Option<graphite::Context> = unsafe { std::mem::transmute(editor_graphite_make_dawn(instance, device, queue)) };
let Some(context) = context else { return false };
let recorder: Option<graphite::Recorder> = unsafe { std::mem::transmute(editor_graphite_make_recorder(std::mem::transmute_copy(&context))) };
let Some(recorder) = recorder else { return false };
STATE.with_borrow_mut(|s| s.graphite = Some(Graphite { context, recorder }));
true
}
pub unsafe fn webgpu_frame(texture: usize, bgra: bool, draw: impl FnOnce(&Canvas)) -> bool {
let texture = texture as *mut c_void;
let painted = STATE.with_borrow_mut(|s| {
let Some(g) = &mut s.graphite else { return false };
let mut target = graphite::BackendTexture::new();
unsafe { editor_graphite_dawn_texture(&mut target, texture) };
let color = if bgra { ColorType::BGRA8888 } else { ColorType::RGBA8888 };
let Some(mut surface) = graphite::surfaces::wrap_backend_texture(&mut g.recorder, &target, color, None, None) else {
return false;
};
draw(surface.canvas());
drop(surface);
let Some(mut recording) = g.recorder.snap() else { return false };
g.context.insert_recording(&graphite::InsertRecordingInfo::new(&mut recording)) == graphite::InsertStatus::Success && g.context.submit(None)
});
unsafe { wgpuTextureRelease(texture) };
painted
}
pub fn attach_gl(key: u64) -> bool {
let Some(context) = gpu::gl::Interface::new_native().and_then(|i| gpu::direct_contexts::make_gl(i, None)) else {
return false;
};
STATE.with_borrow_mut(|s| s.ganesh.insert(key, Ganesh { context, surface: None }));
true
}
pub fn detach_gl(key: u64) {
STATE.with_borrow_mut(|s| s.ganesh.remove(&key));
}
pub fn gl_frame(key: u64, width: i32, height: i32, draw: impl FnOnce(&Canvas)) -> bool {
STATE.with_borrow_mut(|s| {
let Some(g) = s.ganesh.get_mut(&key) else { return false };
if g.surface.as_ref().is_none_or(|surface| (surface.width(), surface.height()) != (width, height)) {
let fb = gpu::gl::FramebufferInfo { fboid: 0, format: gpu::gl::Format::RGBA8.into(), protected: gpu::Protected::No };
let target = gpu::backend_render_targets::make_gl((width, height), 0, 8, fb);
g.surface =
gpu::surfaces::wrap_backend_render_target(&mut g.context, &target, gpu::SurfaceOrigin::BottomLeft, ColorType::RGBA8888, None, None);
}
let Some(surface) = &mut g.surface else { return false };
draw(surface.canvas());
g.context.flush_and_submit_surface(surface, None);
true
})
}