ferroui_browser/rendering/
browser_software_render_target.rs1use super::web_render_target::update_size;
2use super::{BrowserSurfaceShared, RenderStatistics};
3use crate::interop::canvas_helper::RENDER_TARGET_KIND_SOFTWARE;
4use crate::interop::{thread_proxy, JsObject};
5use ferroui_base::platform::surfaces::{
6 FramebufferLockProperties, IFramebufferRenderTarget, IPlatformRenderSurfaceRenderTarget,
7};
8use ferroui_base::platform::{
9 AlphaFormat, ILockedFramebuffer, PixelFormats, RenderTargetSceneInfo, RetainedFramebuffer,
10};
11use ferroui_base::Vector;
12use std::cell::RefCell;
13use std::rc::Rc;
14use std::sync::Arc;
15use wasm_bindgen::prelude::*;
16
17#[wasm_bindgen(raw_module = "./ferroui.js")]
18extern "C" {
19 #[wasm_bindgen(js_namespace = SoftwareRenderTarget, js_name = staticPutPixelData)]
20 fn put_pixel_data(js: &JsObject, address: u32, size: i32, width: i32, height: i32);
21}
22
23pub struct BrowserSoftwareRenderTarget {
35 js: JsObject,
36}
37
38impl BrowserSoftwareRenderTarget {
39 pub fn new(js: JsObject) -> Rc<Self> {
43 Rc::new(Self { js })
44 }
45
46 pub fn create_framebuffer_render_target(
49 self: &Rc<Self>,
50 shared: Arc<BrowserSurfaceShared>,
51 ) -> Rc<dyn IFramebufferRenderTarget> {
52 Rc::new(FramebufferRenderTarget { parent: self.clone(), shared, fb: RefCell::new(None) })
53 }
54
55 fn blit(&self, fb: &RetainedFramebuffer) {
56 let size = fb.size();
57 put_pixel_data(&self.js, fb.address() as usize as u32, size.width * size.height * 4, size.width, size.height);
58 RenderStatistics::frame_presented(
61 thread_proxy::current_thread(),
62 RENDER_TARGET_KIND_SOFTWARE,
63 0,
64 size.width,
65 size.height,
66 );
67 }
68}
69
70struct FramebufferRenderTarget {
71 parent: Rc<BrowserSoftwareRenderTarget>,
72 shared: Arc<BrowserSurfaceShared>,
73 fb: RefCell<Option<Rc<RetainedFramebuffer>>>,
74}
75
76impl IPlatformRenderSurfaceRenderTarget for FramebufferRenderTarget {}
77
78impl IFramebufferRenderTarget for FramebufferRenderTarget {
79 fn lock(&self, _scene_info: &RenderTargetSceneInfo) -> (Rc<dyn ILockedFramebuffer>, FramebufferLockProperties) {
80 let properties = FramebufferLockProperties::default();
81 let (size, scaling) = self.shared.size();
85 update_size(&self.parent.js, size);
86
87 let mut fb = self.fb.borrow_mut();
88 if fb.as_ref().is_none_or(|fb| fb.size() != size) {
89 if let Some(old) = fb.take() {
90 old.dispose();
91 }
92 *fb = Some(RetainedFramebuffer::new(size, PixelFormats::RGBA8888, AlphaFormat::Premul));
93 }
94
95 let blit_target = Rc::downgrade(&self.parent);
96 let shared = self.shared.clone();
97 let locked = fb.as_ref().expect("the framebuffer was just created").lock(
98 Vector::new(scaling * 96.0, scaling * 96.0),
99 move |fb| {
100 if let Some(parent) = blit_target.upgrade() {
101 parent.blit(fb);
102 shared.frame_presented();
105 }
106 },
107 );
108 (locked, properties)
109 }
110
111 fn dispose(&self) {
112 if let Some(fb) = self.fb.borrow_mut().take() {
113 fb.dispose();
114 }
115 }
116}