212 lines
5.8 KiB
Rust
212 lines
5.8 KiB
Rust
use euclid::Size2D;
|
|
use femtovg::{renderer::OpenGl, Canvas};
|
|
use gl;
|
|
use gl::types::{GLchar, GLenum, GLint, GLuint, GLvoid};
|
|
use glow::HasContext;
|
|
use std::borrow::{Borrow, BorrowMut};
|
|
use std::fmt::{Debug, Formatter};
|
|
use std::num::NonZeroU32;
|
|
use std::ops::{Deref, DerefMut};
|
|
use std::sync::{Mutex, RwLock};
|
|
use std::{cell::RefCell, sync::Arc};
|
|
use surfman::{
|
|
device, Adapter, Connection, Context, ContextAttributeFlags, Device, Error, GLApi,
|
|
NativeConnection, NativeDevice,
|
|
};
|
|
|
|
use super::Target;
|
|
|
|
pub struct OffscreenRenderer {
|
|
context: Arc<RwLock<Context>>,
|
|
device: Device,
|
|
fbo: NonZeroU32,
|
|
glow_ctx: Option<glow::Context>,
|
|
size: (i32, i32), // canvas: Arc<Mutex<CanvasWrapper>>,
|
|
}
|
|
|
|
impl Debug for OffscreenRenderer {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("OffscreenRenderer").finish()
|
|
}
|
|
}
|
|
|
|
impl OffscreenRenderer {
|
|
pub fn new(width: i32, height: i32) -> Result<Self, surfman::Error> {
|
|
let connection = Connection::new()?;
|
|
let adapter = connection.create_adapter()?;
|
|
let mut device = connection.create_device(&adapter)?;
|
|
|
|
let descriptor = device.create_context_descriptor(&surfman::ContextAttributes {
|
|
version: surfman::GLVersion::new(4, 1),
|
|
flags: ContextAttributeFlags::ALPHA
|
|
.union(ContextAttributeFlags::DEPTH)
|
|
.union(ContextAttributeFlags::STENCIL),
|
|
})?;
|
|
|
|
let mut context = device.create_context(&descriptor, None)?;
|
|
|
|
let surface = device.create_surface(
|
|
&context,
|
|
surfman::SurfaceAccess::GPUOnly,
|
|
surfman::SurfaceType::Generic {
|
|
size: euclid::Size2D::new(width, height),
|
|
},
|
|
)?;
|
|
|
|
device
|
|
.bind_surface_to_context(&mut context, surface)
|
|
.expect("Failed to bind surface to context");
|
|
|
|
device.make_context_current(&context).unwrap();
|
|
|
|
let surface_info = device.context_surface_info(&context).unwrap().unwrap();
|
|
gl::load_with(|symbol_name| device.get_proc_address(&context, symbol_name));
|
|
|
|
unsafe {
|
|
gl::BindFramebuffer(gl::FRAMEBUFFER, surface_info.framebuffer_object);
|
|
gl::Viewport(0, 0, width, height);
|
|
debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
|
|
}
|
|
|
|
Ok(Self {
|
|
context: Arc::new(RwLock::new(context)),
|
|
device,
|
|
fbo: NonZeroU32::new(surface_info.framebuffer_object).unwrap(),
|
|
glow_ctx: None,
|
|
size: (width, height),
|
|
})
|
|
}
|
|
|
|
pub fn create_canvas(&mut self) -> CanvasWrapper {
|
|
let (w, h) = self.size;
|
|
let (mut renderer, fbo) = unsafe {
|
|
let renderer = OpenGl::new_from_function(|s| {
|
|
self.device
|
|
.get_proc_address(&self.context.read().unwrap(), s) as *const _
|
|
})
|
|
.expect("Cannot create renderer");
|
|
|
|
let ctx = glow::Context::from_loader_function(|s| {
|
|
self.device
|
|
.get_proc_address(&self.context.read().unwrap(), s) as *const _
|
|
});
|
|
|
|
let surface_info = self
|
|
.device
|
|
.context_surface_info(&self.context.read().unwrap())
|
|
.unwrap()
|
|
.unwrap();
|
|
|
|
let fbo =
|
|
glow::NativeFramebuffer(NonZeroU32::new(surface_info.framebuffer_object).unwrap());
|
|
ctx.bind_framebuffer(glow::FRAMEBUFFER, None);
|
|
(renderer, fbo)
|
|
};
|
|
|
|
renderer.set_screen_target(Some(fbo));
|
|
let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");
|
|
canvas.set_size(w as u32, h as u32, 1.0);
|
|
|
|
CanvasWrapper::new(canvas)
|
|
}
|
|
|
|
pub fn get_img(&self, target: Target) {}
|
|
|
|
pub fn get_mem_img(&self) -> Vec<u8> {
|
|
let (w, h) = self.size;
|
|
let mut pixels: Vec<u8> = vec![0; w as usize * h as usize * 4];
|
|
|
|
unsafe {
|
|
gl::ReadPixels(
|
|
0,
|
|
0,
|
|
w,
|
|
h,
|
|
gl::RGBA,
|
|
gl::UNSIGNED_BYTE,
|
|
pixels.as_mut_ptr() as *mut GLvoid,
|
|
);
|
|
|
|
debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
|
|
}
|
|
|
|
pixels
|
|
}
|
|
}
|
|
|
|
impl Drop for OffscreenRenderer {
|
|
fn drop(&mut self) {
|
|
let mut context = self.context.write().unwrap();
|
|
self.device.destroy_context(&mut context).unwrap();
|
|
let _ = self;
|
|
}
|
|
}
|
|
|
|
unsafe impl Send for OffscreenRenderer {}
|
|
unsafe impl Sync for OffscreenRenderer {}
|
|
pub struct CanvasWrapper(femtovg::Canvas<OpenGl>);
|
|
|
|
impl Debug for CanvasWrapper {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("CanvasWrapper").finish()
|
|
}
|
|
}
|
|
|
|
impl CanvasWrapper {
|
|
fn new(canvas: femtovg::Canvas<OpenGl>) -> Self {
|
|
Self(canvas)
|
|
}
|
|
}
|
|
|
|
impl Deref for CanvasWrapper {
|
|
type Target = femtovg::Canvas<OpenGl>;
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl DerefMut for CanvasWrapper {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
unsafe impl Send for CanvasWrapper {}
|
|
unsafe impl Sync for CanvasWrapper {}
|
|
|
|
impl Drop for CanvasWrapper {
|
|
fn drop(&mut self) {
|
|
let _ = self;
|
|
}
|
|
}
|
|
|
|
impl From<Canvas<OpenGl>> for CanvasWrapper {
|
|
fn from(canvas: Canvas<OpenGl>) -> Self {
|
|
Self(canvas)
|
|
}
|
|
}
|
|
|
|
impl Borrow<Canvas<OpenGl>> for CanvasWrapper {
|
|
fn borrow(&self) -> &Canvas<OpenGl> {
|
|
&self
|
|
}
|
|
}
|
|
|
|
impl BorrowMut<Canvas<OpenGl>> for CanvasWrapper {
|
|
fn borrow_mut(&mut self) -> &mut Canvas<OpenGl> {
|
|
self
|
|
}
|
|
}
|
|
|
|
impl AsRef<Canvas<OpenGl>> for CanvasWrapper {
|
|
fn as_ref(&self) -> &Canvas<OpenGl> {
|
|
&self
|
|
}
|
|
}
|
|
|
|
impl AsMut<Canvas<OpenGl>> for CanvasWrapper {
|
|
fn as_mut(&mut self) -> &mut Canvas<OpenGl> {
|
|
self
|
|
}
|
|
}
|