use super::web_render_target::{get_render_target, BrowserRenderTarget};
use super::BrowserSurfaceShared;
use ferroui_base::platform::surfaces::{IFramebufferPlatformSurface, IFramebufferRenderTarget, IPlatformRenderSurface};
use ferroui_opengl::surfaces::{IGlPlatformSurface, IGlPlatformSurfaceRenderTarget};
use ferroui_opengl::IGlContext;
use std::any::{Any, TypeId};
use std::rc::Rc;
use std::sync::Arc;
pub struct BrowserRenderSurface {
shared: Arc<BrowserSurfaceShared>,
}
const _: fn() = || {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<BrowserRenderSurface>();
};
impl BrowserRenderSurface {
pub fn new(shared: Arc<BrowserSurfaceShared>) -> Arc<Self> {
Arc::new(Self { shared })
}
pub fn shared(&self) -> &Arc<BrowserSurfaceShared> {
&self.shared
}
fn target(&self) -> Option<BrowserRenderTarget> {
if !self.shared.has_target() || self.shared.is_disposed() {
return None;
}
get_render_target(self.shared.target_id())
}
}
impl IPlatformRenderSurface for BrowserRenderSurface {
fn is_ready(&self) -> bool {
self.shared.has_target() && !self.shared.is_disposed()
}
fn as_framebuffer_surface(&self) -> Option<&dyn IFramebufferPlatformSurface> {
match self.target() {
Some(BrowserRenderTarget::Software(_)) => Some(self as &dyn IFramebufferPlatformSurface),
_ => None,
}
}
fn try_get_surface_kind(&self, kind: TypeId) -> Option<Rc<dyn Any>> {
if kind == TypeId::of::<dyn IGlPlatformSurface>() {
if let Some(BrowserRenderTarget::WebGl(_)) = self.target() {
let this: Rc<dyn IGlPlatformSurface> = Rc::new(Self { shared: self.shared.clone() });
return Some(Rc::new(this));
}
}
None
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl IFramebufferPlatformSurface for BrowserRenderSurface {
fn create_framebuffer_render_target(&self) -> Rc<dyn IFramebufferRenderTarget> {
match self.target() {
Some(BrowserRenderTarget::Software(target)) => target.create_framebuffer_render_target(self.shared.clone()),
_ => panic!("the thread has no software render target of the canvas"),
}
}
}
impl IGlPlatformSurface for BrowserRenderSurface {
fn create_gl_render_target(&self, _context: &Rc<dyn IGlContext>) -> Rc<dyn IGlPlatformSurfaceRenderTarget> {
match self.target() {
Some(BrowserRenderTarget::WebGl(target)) => target.create_gl_render_target(self.shared.clone()),
_ => panic!("the thread has no WebGL render target of the canvas"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::interop::canvas_helper::{RENDER_TARGET_KIND_SOFTWARE, RENDER_TARGET_KIND_WEB_GL};
use crate::interop::JsObject;
use crate::rendering::web_render_target::set_script_render_targets_for_unit_tests;
use crate::rendering::BrowserSoftwareRenderTarget;
use ferroui_opengl::surfaces::try_get_gl_surface;
fn software_script(_id: i32) -> Option<BrowserRenderTarget> {
Some(BrowserRenderTarget::Software(BrowserSoftwareRenderTarget::new(JsObject::NULL)))
}
fn empty_script(_id: i32) -> Option<BrowserRenderTarget> {
None
}
fn surface(target_id: i32) -> Arc<BrowserRenderSurface> {
let shared = BrowserSurfaceShared::new();
shared.set_target_id(target_id);
BrowserRenderSurface::new(shared)
}
#[test]
fn a_surface_without_a_reported_target_is_not_ready_and_of_no_kind() {
set_script_render_targets_for_unit_tests(software_script);
let surface = surface(1);
assert!(!surface.is_ready());
assert!(surface.as_framebuffer_surface().is_none());
assert!(try_get_gl_surface(&*surface).is_none());
}
#[test]
fn the_thread_that_has_the_target_sees_a_framebuffer_surface() {
set_script_render_targets_for_unit_tests(software_script);
let surface = surface(2);
surface.shared().set_target_kind(RENDER_TARGET_KIND_SOFTWARE);
assert!(surface.is_ready());
assert!(surface.as_framebuffer_surface().is_some());
assert!(try_get_gl_surface(&*surface).is_none());
assert!(surface.try_get_surface_kind(TypeId::of::<dyn IPlatformRenderSurface>()).is_none());
}
#[test]
fn a_thread_without_the_target_reads_that_it_is_ready_and_sees_no_kind() {
set_script_render_targets_for_unit_tests(software_script);
let surface = surface(3);
surface.shared().set_target_kind(RENDER_TARGET_KIND_SOFTWARE);
assert!(surface.as_framebuffer_surface().is_some());
let seen = std::thread::spawn({
let surface = surface.clone();
move || {
set_script_render_targets_for_unit_tests(empty_script);
(
surface.is_ready(),
surface.as_framebuffer_surface().is_some(),
try_get_gl_surface(&*surface).is_some(),
)
}
})
.join()
.unwrap();
assert_eq!((true, false, false), seen);
}
#[test]
fn the_kind_is_the_one_of_the_target_of_the_thread() {
set_script_render_targets_for_unit_tests(software_script);
let surface = surface(4);
surface.shared().set_target_kind(RENDER_TARGET_KIND_WEB_GL);
assert!(try_get_gl_surface(&*surface).is_none());
assert!(surface.as_framebuffer_surface().is_some());
}
#[test]
fn a_disposed_surface_is_not_ready() {
let surface = surface(5);
surface.shared().set_target_kind(RENDER_TARGET_KIND_WEB_GL);
assert!(surface.is_ready());
surface.shared().dispose();
assert!(!surface.is_ready());
}
#[test]
#[should_panic(expected = "the thread has no software render target of the canvas")]
fn a_thread_without_the_target_cannot_create_a_render_target() {
set_script_render_targets_for_unit_tests(empty_script);
let surface = surface(6);
surface.shared().set_target_kind(RENDER_TARGET_KIND_SOFTWARE);
surface.create_framebuffer_render_target();
}
#[test]
fn the_thread_that_has_the_target_creates_a_render_target() {
set_script_render_targets_for_unit_tests(software_script);
let surface = surface(7);
surface.shared().set_target_kind(RENDER_TARGET_KIND_SOFTWARE);
let render_target =
surface.as_framebuffer_surface().expect("a framebuffer surface").create_framebuffer_render_target();
render_target.dispose();
}
}