From 3dd504fcd11168e2041fd273a21735d96f7b64fe Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sat, 8 Feb 2025 19:28:19 -0500 Subject: [PATCH] Lots of refactoring, new api --- platform/rust/build.rs | 65 ++++++++++++------------ platform/rust/build_support/src/lib.rs | 16 +++--- platform/rust/src/bin/render.rs | 8 ++- platform/rust/src/deps_parser.rs | 68 -------------------------- platform/rust/src/lib.rs | 2 +- platform/rust/src/map_renderer.rs | 64 ++++++++++++++++++------ 6 files changed, 91 insertions(+), 132 deletions(-) delete mode 100644 platform/rust/src/deps_parser.rs diff --git a/platform/rust/build.rs b/platform/rust/build.rs index 6a7cb8a12df..93abb3c430c 100644 --- a/platform/rust/build.rs +++ b/platform/rust/build.rs @@ -4,6 +4,16 @@ use std::{env, fs}; use build_support::parse_deps; use walkdir::WalkDir; +trait CfgBool { + fn define_bool(&mut self, key: &str, value: bool); +} + +impl CfgBool for cmake::Config { + fn define_bool(&mut self, key: &str, value: bool) { + self.define(key, if value { "ON" } else { "OFF" }); + } +} + /// Helper that returns a new cmake::Config with common settings. /// It selects the renderer based on Cargo features: the user must enable exactly one of: /// "metal", "opengl", or "vulkan". If none are explicitly enabled, on iOS/macOS the default is metal, @@ -13,44 +23,29 @@ fn create_cmake_config(project_root: &Path) -> cmake::Config { cfg.generator("Ninja"); cfg.define("CMAKE_C_COMPILER_LAUNCHER", "ccache"); cfg.define("CMAKE_CXX_COMPILER_LAUNCHER", "ccache"); - cfg.define("MLN_DRAWABLE_RENDERER", "ON"); - cfg.define("MLN_WITH_OPENGL", "OFF"); + cfg.define_bool("MLN_DRAWABLE_RENDERER", true); + cfg.define_bool("MLN_WITH_OPENGL", false); - let (metal_enabled, opengl_enabled, vulkan_enabled) = { - let metal = env::var("CARGO_FEATURE_METAL").is_ok(); - let opengl = env::var("CARGO_FEATURE_OPENGL").is_ok(); - let vulkan = env::var("CARGO_FEATURE_VULKAN").is_ok(); - if !metal && !opengl && !vulkan { - if cfg!(target_os = "ios") || cfg!(target_os = "macos") { - (true, false, false) - } else { - (false, false, true) - } + let with_opengl = env::var("CARGO_FEATURE_OPENGL").is_ok(); + let mut with_metal = env::var("CARGO_FEATURE_METAL").is_ok(); + let mut with_vulkan = env::var("CARGO_FEATURE_VULKAN").is_ok(); + + if !with_opengl && !with_metal && !with_vulkan { + if cfg!(any(target_os = "ios", target_os = "macos")) { + with_metal = true; } else { - (metal, opengl, vulkan) + with_vulkan = true; } - }; - - let num_enabled = (metal_enabled as u8) + (opengl_enabled as u8) + (vulkan_enabled as u8); - if num_enabled > 1 { - panic!("Features 'metal', 'opengl', and 'vulkan' are mutually exclusive. Please enable only one."); } - if opengl_enabled { - cfg.define("MLN_WITH_OPENGL", "ON"); - cfg.define("MLN_WITH_METAL", "OFF"); - cfg.define("MLN_WITH_VULKAN", "OFF"); - } else if metal_enabled { - cfg.define("MLN_WITH_OPENGL", "OFF"); - cfg.define("MLN_WITH_METAL", "ON"); - cfg.define("MLN_WITH_VULKAN", "OFF"); - } else if vulkan_enabled { - cfg.define("MLN_WITH_OPENGL", "OFF"); - cfg.define("MLN_WITH_METAL", "OFF"); - cfg.define("MLN_WITH_VULKAN", "ON"); + if ((with_metal as u8) + (with_opengl as u8) + (with_vulkan as u8)) > 1 { + panic!("Features 'metal', 'opengl', and 'vulkan' are mutually exclusive. Please enable only one."); } - cfg.define("MLN_WITH_WERROR", "OFF"); + cfg.define_bool("MLN_WITH_OPENGL", with_opengl); + cfg.define_bool("MLN_WITH_METAL", with_metal); + cfg.define_bool("MLN_WITH_VULKAN", with_vulkan); + cfg.define_bool("MLN_WITH_WERROR", false); cfg } @@ -73,13 +68,12 @@ fn main() { .unwrap_or_else(|_| panic!("Failed to read {}", deps_file.display())); // Parse the deps file into a list of Cargo instructions. - let instructions = parse_deps(&deps_contents, &deps_build_dir.join("build")); - for instr in instructions { - println!("{}", instr); + for instr in parse_deps(&deps_contents, &deps_build_dir.join("build")) { + println!("{instr}"); } // ------------------------------------------------------------------------ - // 2. Build the actual "mbgl-core" target. + // 2. Build the actual "mbgl-core" static library target. // ------------------------------------------------------------------------ let core_build_dir = create_cmake_config(&project_root) .build_target("mbgl-core") @@ -93,6 +87,7 @@ fn main() { // ------------------------------------------------------------------------ // 3. Gather include directories and build the C++ bridge using cxx_build. // ------------------------------------------------------------------------ + // TODO: This is a temporary solution. We should get this list from CMake as well. let mut include_dirs = vec![ project_root.join("include"), project_root.join("platform/default/include"), diff --git a/platform/rust/build_support/src/lib.rs b/platform/rust/build_support/src/lib.rs index 17441a18a29..e8e1d6ebf0d 100644 --- a/platform/rust/build_support/src/lib.rs +++ b/platform/rust/build_support/src/lib.rs @@ -10,19 +10,18 @@ use std::path::Path; pub fn parse_deps(deps_contents: &str, static_lib_base: &Path) -> Vec { let mut instructions = Vec::new(); let mut added_search_paths = HashSet::new(); - let tokens: Vec<&str> = deps_contents.split_whitespace().collect(); - let mut token_iter = tokens.iter().peekable(); + let mut token_iter = deps_contents.split_whitespace().peekable(); - while let Some(&token) = token_iter.next() { + while let Some(token) = token_iter.next() { if token == "-framework" { - if let Some(&framework) = token_iter.next() { - instructions.push(format!("cargo:rustc-link-lib=framework={}", framework)); + if let Some(framework) = token_iter.next() { + instructions.push(format!("cargo:rustc-link-lib=framework={framework}")); } else { panic!("Expected a framework name after '-framework'"); } } else if token.starts_with("-l") { let libname = &token[2..]; - instructions.push(format!("cargo:rustc-link-lib={}", libname)); + instructions.push(format!("cargo:rustc-link-lib={libname}")); } else if token.ends_with(".a") { let lib_path = Path::new(token); let file_stem = lib_path.file_stem().expect("Library file has no stem"); @@ -41,9 +40,10 @@ pub fn parse_deps(deps_contents: &str, static_lib_base: &Path) -> Vec { search_dir.display() )); } - instructions.push(format!("cargo:rustc-link-lib=static={}", lib_name)); + instructions.push(format!("cargo:rustc-link-lib=static={lib_name}")); } else { - instructions.push(format!("cargo:rustc-link-arg={}", token)); + // FIXME: should not use args by default, maybe with a feature flag? + instructions.push(format!("cargo:rustc-link-arg={token}")); } } instructions diff --git a/platform/rust/src/bin/render.rs b/platform/rust/src/bin/render.rs index 66508cacd22..b1d55b16529 100644 --- a/platform/rust/src/bin/render.rs +++ b/platform/rust/src/bin/render.rs @@ -1,7 +1,7 @@ use std::fs; use clap::Parser; -use maplibre_native::MapRenderer; +use maplibre_native::ImageRendererOptions; /// MapLibre Native render tool #[derive(Parser, Debug)] @@ -79,9 +79,7 @@ fn main() { // let val = s.source_version_prefix(); // println!("Parsed arguments: {:?}", val); - let mut map = MapRenderer::new(); - map.set_size(512, 512); - let data = map.render(); - + let mut map = ImageRendererOptions::new().build_static_renderer(); + let data = map.render_static(512, 512); fs::write("out.png", data.as_slice()).unwrap(); } diff --git a/platform/rust/src/deps_parser.rs b/platform/rust/src/deps_parser.rs deleted file mode 100644 index 80ecbab8faf..00000000000 --- a/platform/rust/src/deps_parser.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::collections::HashSet; -use std::path::{Path, PathBuf}; - -/// Given the contents of mbgl-core-deps.txt and the base directory where static libraries are found, -/// parse the whitespace-separated tokens and return a vector of Cargo linker instructions. -pub fn parse_deps(deps_contents: &str, static_lib_base: &Path) -> Vec { - let mut instructions = Vec::new(); - let mut added_search_paths = HashSet::new(); - let tokens: Vec<&str> = deps_contents.split_whitespace().collect(); - let mut token_iter = tokens.iter().peekable(); - - while let Some(&token) = token_iter.next() { - if token == "-framework" { - if let Some(&framework) = token_iter.next() { - instructions.push(format!("cargo:rustc-link-lib=framework={}", framework)); - } else { - panic!("Expected a framework name after '-framework'"); - } - } else if token.starts_with("-l") { - let libname = &token[2..]; - instructions.push(format!("cargo:rustc-link-lib={}", libname)); - } else if token.ends_with(".a") { - let lib_path = Path::new(token); - let file_stem = lib_path.file_stem().expect("Library file has no stem"); - let file_stem = file_stem.to_str().expect("Library file stem is not valid UTF-8"); - let lib_name = file_stem.strip_prefix("lib").unwrap_or(file_stem); - - let search_dir = match lib_path.parent() { - Some(parent) if !parent.as_os_str().is_empty() => static_lib_base.join(parent), - _ => static_lib_base.to_path_buf(), - }; - if added_search_paths.insert(search_dir.clone()) { - instructions.push(format!("cargo:rustc-link-search=native={}", search_dir.display())); - } - instructions.push(format!("cargo:rustc-link-lib=static={}", lib_name)); - } else { - instructions.push(format!("cargo:rustc-link-arg={}", token)); - } - } - instructions -} - -#[cfg(test)] -mod tests { - use super::*; - use std::path::PathBuf; - - #[test] - fn test_parse_deps() { - // This sample mimics a deps file with: - // - a dynamic library flag (-lsqlite3) - // - a static library file (libmbgl-core.a) - // - a framework flag (-framework AppKit) - // - an extra linker argument ("some_arg") - let deps_content = "-lsqlite3 libmbgl-core.a -framework AppKit some_arg"; - // Assume that the static libraries are found in "/build_dir/build" - let base_dir = PathBuf::from("/build_dir/build"); - let instructions = parse_deps(deps_content, &base_dir); - let expected = vec![ - "cargo:rustc-link-lib=sqlite3".to_string(), - format!("cargo:rustc-link-search=native={}", base_dir.display()), - "cargo:rustc-link-lib=static=mbgl-core".to_string(), - "cargo:rustc-link-lib=framework=AppKit".to_string(), - "cargo:rustc-link-arg=some_arg".to_string(), - ]; - assert_eq!(instructions, expected); - } -} diff --git a/platform/rust/src/lib.rs b/platform/rust/src/lib.rs index 05402cba103..d8694e8d505 100644 --- a/platform/rust/src/lib.rs +++ b/platform/rust/src/lib.rs @@ -1,7 +1,7 @@ mod map_renderer; mod tile_server_options; -pub use map_renderer::MapRenderer; +pub use map_renderer::{ImageRenderer, ImageRendererOptions, Static, Tile, Image}; pub use tile_server_options::TileServerOptions; #[cxx::bridge(namespace = "ml::bridge")] diff --git a/platform/rust/src/map_renderer.rs b/platform/rust/src/map_renderer.rs index af9ddc8cc6a..b7fe8dc0c11 100644 --- a/platform/rust/src/map_renderer.rs +++ b/platform/rust/src/map_renderer.rs @@ -1,31 +1,65 @@ +use std::marker::PhantomData; use cxx::{CxxVector, UniquePtr}; use crate::ffi; +use crate::ffi::MapRenderer_setSize; -/// Configuration options for a tile server. -pub struct MapRenderer(UniquePtr); +#[derive(Debug, Clone, Default)] +pub struct ImageRendererOptions { + pixel_ratio: f32, +} -impl MapRenderer { - /// Create a new default configuration +impl ImageRendererOptions { pub fn new() -> Self { - Self(ffi::MapRenderer_new()) + Self::default() + } + + pub fn build_static_renderer(self) -> ImageRenderer { + ImageRenderer::new(self.pixel_ratio) } - pub fn set_size(&mut self, width: u32, height: u32) -> &mut Self { - ffi::MapRenderer_setSize(self.0.pin_mut(), width, height); - self + pub fn build_tile_renderer(self) -> ImageRenderer { + ImageRenderer::new(self.pixel_ratio) + } +} + +/// Internal state type to render a static map image. +pub struct Static; +/// Internal state type to render a map tile. +pub struct Tile; + +/// Configuration options for a tile server. +pub struct ImageRenderer(UniquePtr, PhantomData); + +impl ImageRenderer { + /// Private constructor. + fn new(_pixel_ratio: f32/*, mode: Mode*/) -> Self { + let map = ffi::MapRenderer_new(); + // map.set_pixel_ratio(self.pixel_ratio); + // map.set_mode(self.mode); + Self(map, PhantomData) } +} - pub fn render(&mut self) -> UniquePtr> { - ffi::MapRenderer_render(self.0.pin_mut()) +impl ImageRenderer { + pub fn render_static(&mut self, width: u32, height: u32) -> Image { + MapRenderer_setSize(self.0.pin_mut(), width, height); + Image(ffi::MapRenderer_render(self.0.pin_mut()) ) } } -impl Default for MapRenderer { - fn default() -> Self { - Self::new() +impl ImageRenderer { + pub fn render_tile(&mut self, zoom: f64, x: u64, y: u64) -> Image { + MapRenderer_setSize(self.0.pin_mut(), 512, 512); + // TODO: set tile location + Image(ffi::MapRenderer_render(self.0.pin_mut()) ) } } -#[cfg(test)] -mod tests {} +pub struct Image(UniquePtr>); + +impl Image { + pub fn as_slice(&self) -> &[u8] { + self.0.as_ref().unwrap().as_slice() + } +}