Optimize rendering by pre-loading resources #231

Merged
savanni merged 2 commits from otg/resource-loader into main 2024-04-09 13:56:03 +00:00
3 changed files with 101 additions and 10 deletions
Showing only changes of commit baeb458126 - Show all commits

View File

@ -14,7 +14,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with On the Grid. If not, see <https://www.gnu.org/licenses/>. You should have received a copy of the GNU General Public License along with On the Grid. If not, see <https://www.gnu.org/licenses/>.
*/ */
use crate::CoreApi; use crate::{CoreApi, ResourceManager};
use adw::prelude::*; use adw::prelude::*;
use otg_core::{ use otg_core::{
@ -22,7 +22,7 @@ use otg_core::{
CoreRequest, CoreResponse, CoreRequest, CoreResponse,
}; };
use sgf::GameRecord; use sgf::GameRecord;
use std::sync::{Arc, RwLock}; use std::{rc::Rc, sync::{Arc, RwLock}};
use crate::views::{GameReview, HomeView, SettingsView}; use crate::views::{GameReview, HomeView, SettingsView};
@ -58,10 +58,12 @@ pub struct AppWindow {
// Not liking this, but I have to keep track of the settings view model separately from // Not liking this, but I have to keep track of the settings view model separately from
// anything else. I'll have to look into this later. // anything else. I'll have to look into this later.
settings_view_model: Arc<RwLock<Option<SettingsView>>>, settings_view_model: Arc<RwLock<Option<SettingsView>>>,
resources: Rc<ResourceManager>,
} }
impl AppWindow { impl AppWindow {
pub fn new(app: &adw::Application, core: CoreApi) -> Self { pub fn new(app: &adw::Application, core: CoreApi, resources: ResourceManager) -> Self {
let window = Self::setup_window(app); let window = Self::setup_window(app);
let overlay = Self::setup_overlay(); let overlay = Self::setup_overlay();
let stack = adw::NavigationView::new(); let stack = adw::NavigationView::new();
@ -77,6 +79,7 @@ impl AppWindow {
overlay, overlay,
core, core,
settings_view_model: Default::default(), settings_view_model: Default::default(),
resources: Rc::new(resources),
}; };
let home = s.setup_home(); let home = s.setup_home();

View File

@ -21,10 +21,12 @@ pub use app_window::AppWindow;
mod views; mod views;
use async_std::task::{yield_now}; use async_std::task::yield_now;
use otg_core::{Core, Observable, CoreRequest, CoreResponse}; use gio::resources_lookup_data;
use std::{rc::Rc}; use gtk::gdk_pixbuf::{Colorspace, InterpType, Pixbuf};
use image::{io::Reader as ImageReader, ImageError};
use otg_core::{Core, CoreRequest, CoreResponse, Observable};
use std::{cell::RefCell, collections::HashMap, io::Cursor, rc::Rc};
#[derive(Clone)] #[derive(Clone)]
pub struct CoreApi { pub struct CoreApi {
@ -37,6 +39,92 @@ impl CoreApi {
} }
} }
#[derive(Clone)]
pub enum Resource {
Image(Pixbuf),
}
pub struct ResourceManager {
resources: Rc<RefCell<HashMap<String, Resource>>>,
}
impl ResourceManager {
pub fn new() -> Self {
let mut resources = HashMap::new();
for (path, xres, yres, transparency) in [
(
"/com/luminescent-dreams/otg-gtk/wood_texture.jpg",
840,
840,
false,
),
(
"/com/luminescent-dreams/otg-gtk/black_stone.png",
40,
40,
true,
),
(
"/com/luminescent-dreams/otg-gtk/white_stone.png",
40,
40,
true,
),
] {
match perftrace(&format!("loading {}", path), || {
Self::load_image(path, transparency, xres, yres)
}) {
Ok(Some(image)) => {
resources.insert(path.to_owned(), Resource::Image(image));
}
Ok(None) => println!("no image in resource bundle for {}", path),
Err(err) => println!("failed to load image {}: {}", path, err),
}
}
Self {
resources: Rc::new(RefCell::new(resources)),
}
}
pub fn resource(&self, path: &str) -> Option<Resource> {
self.resources.borrow().get(path).cloned()
}
fn load_image(
path: &str,
transparency: bool,
width: i32,
height: i32,
) -> Result<Option<Pixbuf>, ImageError> {
let image_bytes = resources_lookup_data(path, gio::ResourceLookupFlags::NONE).unwrap();
let image = ImageReader::new(Cursor::new(image_bytes))
.with_guessed_format()
.unwrap()
.decode();
image.map(|image| {
let stride = if transparency {
image.to_rgba8().sample_layout().height_stride
} else {
image.to_rgb8().sample_layout().height_stride
};
Pixbuf::from_bytes(
&glib::Bytes::from(image.as_bytes()),
Colorspace::Rgb,
transparency,
8,
image.width() as i32,
image.height() as i32,
stride as i32,
)
.scale_simple(width, height, InterpType::Nearest)
})
}
}
pub fn perftrace<F, A>(trace_name: &str, f: F) -> A pub fn perftrace<F, A>(trace_name: &str, f: F) -> A
where where
F: FnOnce() -> A, F: FnOnce() -> A,

View File

@ -4,8 +4,7 @@ use async_std::task::spawn;
use gio::ActionEntry; use gio::ActionEntry;
use otg_core::{Config, ConfigOption, Core, CoreNotification, LibraryPath, Observable}; use otg_core::{Config, ConfigOption, Core, CoreNotification, LibraryPath, Observable};
use otg_gtk::{ use otg_gtk::{
AppWindow, AppWindow, CoreApi, ResourceManager
CoreApi,
}; };
@ -123,8 +122,9 @@ fn main() {
app.connect_activate({ app.connect_activate({
move |app| { move |app| {
let resources = ResourceManager::new();
let core_api = CoreApi { core: core.clone() }; let core_api = CoreApi { core: core.clone() };
let app_window = AppWindow::new(app, core_api); let app_window = AppWindow::new(app, core_api, resources);
setup_app_configuration_action(app, app_window.clone()); setup_app_configuration_action(app, app_window.clone());