sciter-sdk / rust-sciter

Rust bindings for Sciter
https://sciter.com
MIT License
806 stars 76 forks source link

Render into bitmap callback is too slow. #147

Open LaineZ opened 4 months ago

LaineZ commented 4 months ago

I'm using a sciter.lite in ggez game framework. And I need draw a UI into the bitmap. There was a problem is low performance when render is goes into reasonable resolution like 2560×1440. The bitmap render callback renders one frame around 169-180 ms on my PC (Ryzen 9 5900X, RTX 3060, Windows 10).

Here is minimal-example, without any render output, just a generating bitmap

//! Windowless mode example (for Sciter.Lite build).
extern crate sciter;
extern crate winit;
extern crate winapi;
extern crate raw_window_handle;

use std::time::Instant;

use winit::event::{Event, WindowEvent};
use winit::event_loop::EventLoop;
use winit::window::WindowBuilder;

fn main() {
    if let Some(arg) = std::env::args().nth(1) {
        println!("loading sciter from {:?}", arg);
        if let Err(_) = sciter::set_options(sciter::RuntimeOptions::LibraryPath(&arg)) {
            panic!("Invalid sciter-lite dll specified.");
        }
    } else {
        panic!("usage: cargo run -p windowless -- sciter-sdk/bin.win/x64lite/sciter.dll")
    }

    // prepare and create a new window
    println!("create window");
    let events = EventLoop::new();

    use raw_window_handle::HasRawWindowHandle;
    let wnd = WindowBuilder::new();
    let wnd = wnd.build(&events).expect("Failed to create window");
    let window_handle = wnd.raw_window_handle();

    // configure Sciter
    println!("create sciter instance");
    sciter::set_options(sciter::RuntimeOptions::UxTheming(true)).unwrap();
    sciter::set_options(sciter::RuntimeOptions::DebugMode(true)).unwrap();
    sciter::set_options(sciter::RuntimeOptions::ScriptFeatures(0xFF)).unwrap();

    // create an engine instance with an opaque pointer as an identifier
    use sciter::windowless::{Message, handle_message};
    let scwnd = { &wnd as *const _ as sciter::types::HWINDOW };
    handle_message(scwnd, Message::Create { backend: sciter::types::GFX_LAYER::SKIA_OPENGL, transparent: false, });

    #[cfg(windows)]
    {
        // Windows-specific: we need to redraw window in response to a corresponding notification.
        // winit 0.20 has an explicit `Window::request_redraw` method,
        // here we use `winapi::InvalidateRect` for this.
        struct WindowlessHandler {
            hwnd: winapi::shared::windef::HWND,
        }

        impl sciter::HostHandler for WindowlessHandler {
            fn on_invalidate(&mut self, pnm: &sciter::host::SCN_INVALIDATE_RECT) {
                unsafe {
                    let rc = &pnm.invalid_rect;
                    let dst = winapi::shared::windef::RECT {
                        left: rc.left,
                        top: rc.top,
                        right: rc.right,
                        bottom: rc.bottom,
                    };
                    winapi::um::winuser::InvalidateRect(self.hwnd, &dst as *const _, 0);
                    // println!("- {} {}", rc.width(), rc.height());
                }
            }
        }

        let handler = WindowlessHandler {
            hwnd: match window_handle {
                raw_window_handle::RawWindowHandle::Windows(data) => data.hwnd as winapi::shared::windef::HWND,
                _ => unreachable!(),
            },
        };

        let instance = sciter::Host::attach_with(scwnd, handler);

        let html = include_bytes!("../../minimal.htm");
        instance.load_html(html, Some("example://minimal.htm"));
    }

    // events processing
    use sciter::windowless::RenderEvent;

    println!("running...");

    let startup = std::time::Instant::now();

    // release CPU a bit, hackish
    std::thread::sleep(std::time::Duration::from_millis(0));

    handle_message(scwnd, Message::Heartbit {
        milliseconds: std::time::Instant::now().duration_since(startup).as_millis() as u32,
    });
    handle_message(scwnd, Message::Size { width: 2560, height: 1440 });

    // the actual event loop polling

    events.run(move |event, _, control_flow| {
        match event {
            Event::RedrawRequested(_) => {
                let on_render = move |bitmap_area: &sciter::types::RECT, bitmap_data: &[u8]|
                {
                };

                let render_time = Instant::now();
                let cb = RenderEvent {
                    layer: None,
                    callback: Box::new(on_render),
                };

                handle_message(scwnd, Message::RenderTo(cb));

                println!("Render took {} ms", render_time.elapsed().as_millis());
            },
            _ => (),
        }
    });
}

output:

loading sciter from "F:\\rust-sciter\\sciter-js-sdk-main\\sciter-js-sdk-main\\bin.lite\\windows\\x64\\sciter.dll"
create window
create sciter instance
running...
Render took 169 ms