diff options
| author | Valentin Popov <valentin@popov.link> | 2026-07-18 06:29:31 +0300 |
|---|---|---|
| committer | Valentin Popov <valentin@popov.link> | 2026-07-18 06:29:31 +0300 |
| commit | 6e6496eebf4057ceb4a09869b0df21b1f0260e81 (patch) | |
| tree | d6e019a6f653c07b2b75579bc7c25e3bf45cfc90 /apps | |
| parent | 283b48e8c512dd251d4617cc988985e3be2a0c5e (diff) | |
| download | fparkan-6e6496eebf4057ceb4a09869b0df21b1f0260e81.tar.xz fparkan-6e6496eebf4057ceb4a09869b0df21b1f0260e81.zip | |
feat(render): upload original TEXM to Vulkan
Diffstat (limited to 'apps')
| -rw-r--r-- | apps/fparkan-vulkan-smoke/src/main.rs | 140 |
1 files changed, 138 insertions, 2 deletions
diff --git a/apps/fparkan-vulkan-smoke/src/main.rs b/apps/fparkan-vulkan-smoke/src/main.rs index 90c59c9..baa678b 100644 --- a/apps/fparkan-vulkan-smoke/src/main.rs +++ b/apps/fparkan-vulkan-smoke/src/main.rs @@ -16,7 +16,7 @@ use fparkan_platform_winit::{window_native_handles, WinitWindowPlan}; use fparkan_render_vulkan::{ project_msh_to_static_mesh, VulkanSmokeBootstrapProgress, VulkanSmokeFrameOutcome, VulkanSmokeRenderer, VulkanSmokeRendererCreateInfo, VulkanSmokeRendererReport, - VulkanSmokeShutdownReport, VulkanStaticMesh, VulkanValidationReport, + VulkanSmokeShutdownReport, VulkanStaticMesh, VulkanStaticTexture, VulkanValidationReport, }; use serde::Serialize; use std::path::PathBuf; @@ -56,6 +56,7 @@ fn main() { fn run(args: &[String]) -> Result<String, String> { let options = SmokeOptions::parse(args)?; let mesh = options.load_mesh()?; + let texture = options.load_texture()?; remove_stale_output(&options)?; let event_loop = EventLoop::new().map_err(|err| format!("winit event loop: {err}"))?; event_loop.set_control_flow(ControlFlow::Poll); @@ -66,7 +67,7 @@ fn run(args: &[String]) -> Result<String, String> { Arc::clone(&completed), Arc::clone(&progress), ); - let mut app = SmokeApp::new(options, mesh, completed, progress); + let mut app = SmokeApp::new(options, mesh, texture, completed, progress); if let Err(err) = event_loop.run_app(&mut app) { app.error = Some(format!("winit event loop: {err}")); } @@ -112,6 +113,14 @@ struct SmokeOptions { resize_frame: u32, timeout_seconds: u64, mesh_input: MeshInput, + texture_input: Option<ResourceInput>, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +struct ResourceInput { + root: PathBuf, + archive: String, + name: String, } #[derive(Clone, Debug, Eq, PartialEq)] @@ -148,6 +157,7 @@ impl MeshInput { } impl SmokeOptions { + #[allow(clippy::too_many_lines)] fn parse(args: &[String]) -> Result<Self, String> { let mut out = None; let mut frames = DEFAULT_TARGET_FRAMES; @@ -156,6 +166,9 @@ impl SmokeOptions { let mut model_root = None; let mut model_archive = None; let mut model_name = None; + let mut texture_root = None; + let mut texture_archive = None; + let mut texture_name = None; let mut iter = args.iter(); while let Some(arg) = iter.next() { match arg.as_str() { @@ -208,6 +221,27 @@ impl SmokeOptions { .ok_or_else(|| "--model-name requires a value".to_string())?, ); } + "--texture-root" => { + texture_root = Some( + iter.next() + .map(PathBuf::from) + .ok_or_else(|| "--texture-root requires a path".to_string())?, + ); + } + "--texture-archive" => { + texture_archive = Some( + iter.next() + .cloned() + .ok_or_else(|| "--texture-archive requires a value".to_string())?, + ); + } + "--texture-name" => { + texture_name = Some( + iter.next() + .cloned() + .ok_or_else(|| "--texture-name requires a value".to_string())?, + ); + } _ => return Err(format!("unknown native smoke option: {arg}")), } } @@ -234,12 +268,27 @@ impl SmokeOptions { ); } }; + let texture_input = match (texture_root, texture_archive, texture_name) { + (None, None, None) => None, + (Some(root), Some(archive), Some(name)) => Some(ResourceInput { + root, + archive, + name, + }), + _ => { + return Err( + "--texture-root, --texture-archive and --texture-name must be supplied together" + .to_string(), + ); + } + }; Ok(Self { out, frames, resize_frame, timeout_seconds, mesh_input, + texture_input, }) } @@ -256,11 +305,27 @@ impl SmokeOptions { } } } + + fn load_texture(&self) -> Result<Option<VulkanStaticTexture>, String> { + self.texture_input.as_ref().map_or(Ok(None), |input| { + let image = fparkan_inspection::load_texture_mip0_rgba8_from_root( + &input.root, + &input.archive, + &input.name, + )?; + Ok(Some(VulkanStaticTexture { + width: image.width, + height: image.height, + rgba8: image.rgba8, + })) + }) + } } struct SmokeApp { options: SmokeOptions, mesh: VulkanStaticMesh, + texture: Option<VulkanStaticTexture>, completed: Arc<AtomicBool>, progress: Arc<SharedSmokeProgress>, window_id: Option<WindowId>, @@ -305,12 +370,14 @@ impl SmokeApp { fn new( options: SmokeOptions, mesh: VulkanStaticMesh, + texture: Option<VulkanStaticTexture>, completed: Arc<AtomicBool>, progress: Arc<SharedSmokeProgress>, ) -> Self { Self { options, mesh, + texture, completed, progress, window_id: None, @@ -407,6 +474,23 @@ impl SmokeApp { mesh_name: self.options.mesh_input.name(), mesh_vertex_count: self.mesh.vertices.len(), mesh_index_count: self.mesh.indices.len(), + texture_source: self + .options + .texture_input + .as_ref() + .map_or("none", |_| "original-texm"), + texture_archive: self + .options + .texture_input + .as_ref() + .map_or("", |input| input.archive.as_str()), + texture_name: self + .options + .texture_input + .as_ref() + .map_or("", |input| input.name.as_str()), + texture_width: self.texture.as_ref().map_or(0, |texture| texture.width), + texture_height: self.texture.as_ref().map_or(0, |texture| texture.height), shader_manifest_hash: renderer .as_ref() .map_or("", |snapshot| snapshot.report.shader_manifest_hash.as_str()), @@ -611,6 +695,20 @@ fn render_timeout_failure_report( mesh_name: options.mesh_input.name(), mesh_vertex_count: 0, mesh_index_count: 0, + texture_source: options + .texture_input + .as_ref() + .map_or("none", |_| "original-texm"), + texture_archive: options + .texture_input + .as_ref() + .map_or("", |input| input.archive.as_str()), + texture_name: options + .texture_input + .as_ref() + .map_or("", |input| input.name.as_str()), + texture_width: 0, + texture_height: 0, shader_manifest_hash: "", vulkan_loader_status: if bootstrap.loader_available { "available" @@ -706,6 +804,7 @@ impl ApplicationHandler for SmokeApp { render_request: RenderRequest::conservative(), enable_validation: true, mesh: self.mesh.clone(), + texture: self.texture.clone(), bootstrap_progress: Some(Arc::clone(&self.progress.bootstrap)), }) { Ok(renderer) => renderer, @@ -838,6 +937,11 @@ struct SmokeReport<'a> { mesh_name: &'a str, mesh_vertex_count: usize, mesh_index_count: usize, + texture_source: &'a str, + texture_archive: &'a str, + texture_name: &'a str, + texture_width: u32, + texture_height: u32, shader_manifest_hash: &'a str, vulkan_loader_status: &'a str, vulkan_instance_status: &'a str, @@ -1080,6 +1184,7 @@ mod tests { resize_frame: DEFAULT_RESIZE_FRAME, timeout_seconds: DEFAULT_TIMEOUT_SECONDS, mesh_input: MeshInput::SmokeTriangle, + texture_input: None, }) ); } @@ -1116,6 +1221,7 @@ mod tests { resize_frame: DEFAULT_RESIZE_FRAME, timeout_seconds: 45, mesh_input: MeshInput::SmokeTriangle, + texture_input: None, }) ); } @@ -1176,6 +1282,28 @@ mod tests { } #[test] + fn parses_original_texm_input_as_one_atomic_request() { + let parsed = SmokeOptions::parse(&[ + "--out".to_string(), + "target/report.json".to_string(), + "--texture-root".to_string(), + "C:/GOG".to_string(), + "--texture-archive".to_string(), + "Textures.lib".to_string(), + "--texture-name".to_string(), + "DEFAULT.0".to_string(), + ]); + + assert!(matches!( + parsed, + Ok(SmokeOptions { + texture_input: Some(ResourceInput { archive, name, .. }), + .. + }) if archive == "Textures.lib" && name == "DEFAULT.0" + )); + } + + #[test] fn rejects_deprecated_self_assertion_flags() { for flag in [ "--status", @@ -1239,6 +1367,11 @@ mod tests { mesh_name: "MTCHECK.MSH", mesh_vertex_count: 128, mesh_index_count: 252, + texture_source: "original-texm", + texture_archive: "Textures.lib", + texture_name: "DEFAULT.0", + texture_width: 16, + texture_height: 16, shader_manifest_hash: "deadbeef", vulkan_loader_status: "available", vulkan_instance_status: "created", @@ -1264,6 +1397,7 @@ mod tests { assert!(json.contains("\"vulkan_device_name\": \"Windows test GPU\"")); assert!(json.contains("\"runner_architecture\": \"x86_64\"")); assert!(json.contains("\"mesh_source\": \"original-msh\"")); + assert!(json.contains("\"texture_source\": \"original-texm\"")); } #[test] @@ -1286,8 +1420,10 @@ mod tests { resize_frame: DEFAULT_RESIZE_FRAME, timeout_seconds: 7, mesh_input: MeshInput::SmokeTriangle, + texture_input: None, }, mesh: VulkanStaticMesh::smoke_triangle(), + texture: None, completed: Arc::new(AtomicBool::new(false)), progress: Arc::new(SharedSmokeProgress::default()), window_id: None, |
