diff --git a/frontend/render_v2/rs/src/main.rs b/frontend/render_v2/rs/src/main.rs index 027bdba541..37877f9095 100644 --- a/frontend/render_v2/rs/src/main.rs +++ b/frontend/render_v2/rs/src/main.rs @@ -1,19 +1,19 @@ use std::boxed::Box; use skia_safe::{ - gpu::{self, gl::FramebufferInfo, DirectContext}, - Color, Paint, PaintStyle, Surface, Rect + gpu::{self, gl::FramebufferInfo, DirectContext}, + Color, Paint, PaintStyle, Rect, Surface, }; extern "C" { - pub fn emscripten_GetProcAddress( - name: *const ::std::os::raw::c_char, - ) -> *const ::std::os::raw::c_void; + pub fn emscripten_GetProcAddress( + name: *const ::std::os::raw::c_char, + ) -> *const ::std::os::raw::c_void; } struct GpuState { - context: DirectContext, - framebuffer_info: FramebufferInfo, + context: DirectContext, + framebuffer_info: FramebufferInfo, } /// This struct holds the state of the Rust application between JS calls. @@ -21,18 +21,18 @@ struct GpuState { /// It is created by [init] and passed to the other exported functions. Note that rust-skia data /// structures are not thread safe, so a state must not be shared between different Web Workers. pub struct State { - gpu_state: GpuState, - surface: Surface, + gpu_state: GpuState, + surface: Surface, } impl State { - fn new(gpu_state: GpuState, surface: Surface) -> Self { - State { gpu_state, surface } - } + fn new(gpu_state: GpuState, surface: Surface) -> Self { + State { gpu_state, surface } + } - fn set_surface(&mut self, surface: Surface) { - self.surface = surface; - } + fn set_surface(&mut self, surface: Surface) { + self.surface = surface; + } } #[no_mangle] @@ -43,57 +43,59 @@ pub fn add(left: f32, right: f32) -> f32 { } fn init_gl() { - unsafe { - gl::load_with(|addr| { - let addr = std::ffi::CString::new(addr).unwrap(); - emscripten_GetProcAddress(addr.into_raw() as *const _) as *const _ - }); - } + unsafe { + gl::load_with(|addr| { + let addr = std::ffi::CString::new(addr).unwrap(); + emscripten_GetProcAddress(addr.into_raw() as *const _) as *const _ + }); + } } /// This needs to be done once per WebGL context. fn create_gpu_state() -> GpuState { - let interface = skia_safe::gpu::gl::Interface::new_native().unwrap(); - let context = skia_safe::gpu::direct_contexts::make_gl(interface, None).unwrap(); - let framebuffer_info = { - let mut fboid: gl::types::GLint = 0; - unsafe { gl::GetIntegerv(gl::FRAMEBUFFER_BINDING, &mut fboid) }; + let interface = skia_safe::gpu::gl::Interface::new_native().unwrap(); + let context = skia_safe::gpu::direct_contexts::make_gl(interface, None).unwrap(); + let framebuffer_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(), - protected: skia_safe::gpu::Protected::No, - } - }; + FramebufferInfo { + fboid: fboid.try_into().unwrap(), + format: skia_safe::gpu::gl::Format::RGBA8.into(), + protected: skia_safe::gpu::Protected::No, + } + }; - GpuState { - context, - framebuffer_info, - } + GpuState { + context, + framebuffer_info, + } } /// Create the Skia surface that will be used for rendering. fn create_surface(gpu_state: &mut GpuState, width: i32, height: i32) -> Surface { - let backend_render_target = - gpu::backend_render_targets::make_gl((width, height), 1, 8, gpu_state.framebuffer_info); + let backend_render_target = + gpu::backend_render_targets::make_gl((width, height), 1, 8, gpu_state.framebuffer_info); - gpu::surfaces::wrap_backend_render_target( - &mut gpu_state.context, - &backend_render_target, - skia_safe::gpu::SurfaceOrigin::BottomLeft, - skia_safe::ColorType::RGBA8888, - None, - None, - ) - .unwrap() + gpu::surfaces::wrap_backend_render_target( + &mut gpu_state.context, + &backend_render_target, + skia_safe::gpu::SurfaceOrigin::BottomLeft, + skia_safe::ColorType::RGBA8888, + None, + None, + ) + .unwrap() } fn render_rect(surface: &mut Surface, left: f32, top: f32, right: f32, bottom: f32) { - let mut paint = Paint::default(); - paint.set_style(PaintStyle::Fill); - paint.set_color(Color::BLACK); - paint.set_anti_alias(true); - surface.canvas().draw_rect(Rect::new(left, top, right, bottom), &paint); + let mut paint = Paint::default(); + paint.set_style(PaintStyle::Fill); + paint.set_color(Color::BLACK); + paint.set_anti_alias(true); + surface + .canvas() + .draw_rect(Rect::new(left, top, right, bottom), &paint); } /// This is called from JS after the WebGL context has been created. @@ -117,10 +119,22 @@ pub unsafe extern "C" fn resize_surface(state: *mut State, width: i32, height: i /// Draw a black rect at the specified coordinates. /// # Safety #[no_mangle] -pub unsafe extern "C" fn draw_rect(state: *mut State, left: i32, right: i32, top: i32, bottom: i32) { +pub unsafe extern "C" fn draw_rect( + state: *mut State, + left: i32, + right: i32, + top: i32, + bottom: i32, +) { let state = unsafe { state.as_mut() }.expect("got an invalid state pointer"); //state.surface.canvas().clear(Color::WHITE); - render_rect(&mut state.surface, left as f32, right as f32, top as f32, bottom as f32); + render_rect( + &mut state.surface, + left as f32, + right as f32, + top as f32, + bottom as f32, + ); state .gpu_state .context @@ -129,7 +143,7 @@ pub unsafe extern "C" fn draw_rect(state: *mut State, left: i32, right: i32, top #[no_mangle] pub unsafe extern "C" fn translate(state: *mut State, dx: f32, dy: f32) { - (*state).surface.canvas().translate((dx, dy)); + (*state).surface.canvas().translate((dx, dy)); } #[no_mangle] @@ -137,6 +151,11 @@ pub unsafe extern "C" fn scale(state: *mut State, sx: f32, sy: f32) { (*state).surface.canvas().scale((sx, sy)); } +#[no_mangle] +pub unsafe extern "C" fn reset_canvas(state: *mut State) { + (*state).surface.canvas().reset_matrix(); +} + fn main() { init_gl(); } @@ -150,4 +169,4 @@ mod tests { let result = add(2, 2); assert_eq!(result, 4); } -} \ No newline at end of file +} diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index d7f3772356..0e78958451 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -271,13 +271,19 @@ rule-area-size (/ rulers/ruler-area-size zoom)] (when (render-v2/is-enabled?) + ;; set up canvas and first render (mf/with-effect [canvas-ref vbox' @canvas-set? base-objects] (let [canvas (mf/ref-val canvas-ref)] (when (and (some? vbox') (not @canvas-set?)) - (swap! canvas-set? true) - (p/then (render-v2/init) - #(render-v2/set-canvas canvas vbox' base-objects)))))) + (p/then (render-v2/init) (fn [] + (render-v2/set-canvas canvas vbox' base-objects) + (swap! canvas-set? true)))))) + ;; redraw when vbox or shapes change + (mf/with-effect + [vbox' base-objects canvas-set?] + (when @canvas-set? + (render-v2/draw-canvas vbox' base-objects)))) (hooks/setup-dom-events zoom disable-paste in-viewport? workspace-read-only? drawing-tool drawing-path?) (hooks/setup-viewport-size vport viewport-ref) diff --git a/frontend/src/app/render_v2.cljs b/frontend/src/app/render_v2.cljs index 7645d8a1b3..4333b4bdf0 100644 --- a/frontend/src/app/render_v2.cljs +++ b/frontend/src/app/render_v2.cljs @@ -36,3 +36,9 @@ ;; Rust (contains? cf/flags :render-v2-rs) (render-v2-rs/set-canvas canvas vbox base-objects))) + +(defn draw-canvas [vbox base-objects] + (cond + ;; Rust + (contains? cf/flags :render-v2-rs) + (render-v2-rs/draw-canvas vbox base-objects))) diff --git a/frontend/src/app/render_v2/rs.cljs b/frontend/src/app/render_v2/rs.cljs index efce70405f..9b7cded952 100644 --- a/frontend/src/app/render_v2/rs.cljs +++ b/frontend/src/app/render_v2/rs.cljs @@ -17,16 +17,17 @@ (defonce ^:dynamic gpu-state #js {}) (defn draw-canvas [vbox objects] - (let [draw_rect (gobj/get ^js internal-module "_draw_rect") + (let [draw-rect (gobj/get ^js internal-module "_draw_rect") translate (gobj/get ^js internal-module "_translate") - _ (js/console.log "vbox " (clj->js vbox)) + reset-canvas (gobj/get ^js internal-module "_reset_canvas") scale (gobj/get ^js internal-module "_scale")] + (reset-canvas gpu-state) (translate gpu-state (- (:x vbox)) (- (:y vbox))) (doseq [shape (vals objects)] (let [sr (:selrect shape)] (println "*****" (:x1 sr) (:y1 sr) (:x2 sr) (:y2 sr)) - (draw_rect gpu-state (:x1 sr) (:y1 sr) (:x2 sr) (:y2 sr)))))) + (draw-rect gpu-state (:x1 sr) (:y1 sr) (:x2 sr) (:y2 sr)))))) (defn set-canvas [canvas vbox objects] diff --git a/frontend/src/app/render_v2/rs.js b/frontend/src/app/render_v2/rs.js index 7f6f4abc7b..33bc8d707f 100644 --- a/frontend/src/app/render_v2/rs.js +++ b/frontend/src/app/render_v2/rs.js @@ -28,7 +28,7 @@ var readyPromise = new Promise((resolve, reject) => { readyPromiseResolve = resolve; readyPromiseReject = reject; }); -["_add","_draw_rect","_init","_main","_resize_surface","_scale","_translate","getExceptionMessage","incrementExceptionRefcount","decrementExceptionRefcount","_memory","___indirect_function_table","onRuntimeInitialized"].forEach((prop) => { +["_add","_draw_rect","_init","_main","_reset_canvas","_resize_surface","_scale","_translate","getExceptionMessage","incrementExceptionRefcount","decrementExceptionRefcount","_memory","___indirect_function_table","onRuntimeInitialized"].forEach((prop) => { if (!Object.getOwnPropertyDescriptor(readyPromise, prop)) { Object.defineProperty(readyPromise, prop, { get: () => abort('You are getting ' + prop + ' on the Promise object, instead of the instance. Use .then() to get called back with the instance, see the MODULARIZE docs in src/settings.js'), @@ -8567,6 +8567,7 @@ var _resize_surface = Module['_resize_surface'] = createExportWrapper('resize_su var _draw_rect = Module['_draw_rect'] = createExportWrapper('draw_rect', 5); var _translate = Module['_translate'] = createExportWrapper('translate', 3); var _scale = Module['_scale'] = createExportWrapper('scale', 3); +var _reset_canvas = Module['_reset_canvas'] = createExportWrapper('reset_canvas', 1); var _main = Module['_main'] = createExportWrapper('main', 2); var _fflush = createExportWrapper('fflush', 1); var _free = createExportWrapper('free', 1); @@ -8612,10 +8613,10 @@ var dynCall_iiiiij = Module['dynCall_iiiiij'] = createExportWrapper('dynCall_iii var dynCall_iiiiijj = Module['dynCall_iiiiijj'] = createExportWrapper('dynCall_iiiiijj', 9); var dynCall_iiiiiijj = Module['dynCall_iiiiiijj'] = createExportWrapper('dynCall_iiiiiijj', 10); -function invoke_vii(index,a1,a2) { +function invoke_iii(index,a1,a2) { var sp = stackSave(); try { - getWasmTableEntry(index)(a1,a2); + return getWasmTableEntry(index)(a1,a2); } catch(e) { stackRestore(sp); if (!(e instanceof EmscriptenEH)) throw e; @@ -8634,6 +8635,17 @@ function invoke_ii(index,a1) { } } +function invoke_vii(index,a1,a2) { + var sp = stackSave(); + try { + getWasmTableEntry(index)(a1,a2); + } catch(e) { + stackRestore(sp); + if (!(e instanceof EmscriptenEH)) throw e; + _setThrew(1, 0); + } +} + function invoke_viii(index,a1,a2,a3) { var sp = stackSave(); try { @@ -8656,10 +8668,10 @@ function invoke_vi(index,a1) { } } -function invoke_iii(index,a1,a2) { +function invoke_iiii(index,a1,a2,a3) { var sp = stackSave(); try { - return getWasmTableEntry(index)(a1,a2); + return getWasmTableEntry(index)(a1,a2,a3); } catch(e) { stackRestore(sp); if (!(e instanceof EmscriptenEH)) throw e; @@ -8700,17 +8712,6 @@ function invoke_viffff(index,a1,a2,a3,a4,a5) { } } -function invoke_iiii(index,a1,a2,a3) { - var sp = stackSave(); - try { - return getWasmTableEntry(index)(a1,a2,a3); - } catch(e) { - stackRestore(sp); - if (!(e instanceof EmscriptenEH)) throw e; - _setThrew(1, 0); - } -} - function invoke_iiiii(index,a1,a2,a3,a4) { var sp = stackSave(); try {