diff --git a/Cargo.lock b/Cargo.lock index b21735f..1fa29c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -182,6 +182,17 @@ dependencies = [ "libloading 0.8.5", ] +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "autocfg" version = "1.4.0" @@ -653,6 +664,13 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "element_bridge" version = "0.1.0" +dependencies = [ + "flume", + "makepad-widgets", + "mp_elements", + "tokio", + "wgpu", +] [[package]] name = "encase" @@ -1693,6 +1711,9 @@ dependencies = [ name = "mp" version = "0.1.0" dependencies = [ + "async-trait", + "element_bridge", + "futures", "glam", "log", "makepad-widgets", @@ -1735,6 +1756,7 @@ dependencies = [ "flume", "glam", "image", + "log", "mp_core", "pollster", "quick_cache", diff --git a/backup.txt b/backup.txt new file mode 100644 index 0000000..2ae450c --- /dev/null +++ b/backup.txt @@ -0,0 +1,24 @@ +let data = DATAPOOL + .get_or_load( + "/Users/tsuki/Desktop/Z_RADR_I_X5775_20230726180000_O_DOR-XPD-CAP-FMT.BIN.zip", + ) + .unwrap(); + let first_block = data.get(0).unwrap(); + let first_block: &Data = &(*first_block); + + if let Ok(d) = first_block.try_into() { + let attachment = { + // Ctx + let mut pipelines = GIAPP.pipelines(); + let ppi = pipelines.ppi(); + + GIAPP.load_data( + ppi, + d, + &PPIConfig { + colormap: vec![[1.0, 1.0, 1.0, 1.0], [1.0, 1.0, 1.0, 1.0]], + color_range: [0.0, 1.0], + }, + ) + }; + } \ No newline at end of file diff --git a/element_bridge/Cargo.toml b/element_bridge/Cargo.toml index d1a8da9..026df2e 100644 --- a/element_bridge/Cargo.toml +++ b/element_bridge/Cargo.toml @@ -4,3 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +mp_elements = { path = "../mp_elements", version = "*" } +makepad-widgets = { git = "https://github.com/makepad/makepad", branch = "rik", version = "0.6.0" } +flume = "0.11.1" +wgpu = "23.0.0" +tokio = { version = "1.41.1", features = ["sync"] } diff --git a/element_bridge/src/lib.rs b/element_bridge/src/lib.rs index b93cf3f..4aa5dee 100644 --- a/element_bridge/src/lib.rs +++ b/element_bridge/src/lib.rs @@ -1,14 +1,70 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right +use makepad_widgets::{makepad_shader_compiler::builtin, Cx, Texture}; +use mp_elements::{ + app::{Ctx, DrawList, RenderWindow}, + App, +}; +use std::sync::{Arc, Mutex}; + +pub use mp_elements::app::Window; +#[derive(Clone)] +pub struct TextureBridge { + dirty: bool, + _buffer: Arc>>, + window: Arc, + _texture: Texture, } -#[cfg(test)] -mod tests { - use super::*; +impl TextureBridge { + pub fn new(texture: Texture, app: &App, window: Window) -> Self { + let window = app.create_window(window); + Self { + dirty: true, + _buffer: Arc::new(tokio::sync::Mutex::new(vec![0u8; 256 * 256 * 4])), + window: Arc::new(window), + _texture: texture, + } + } - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + pub fn texture(&self) -> &Texture { + &self._texture + } + + pub fn window(&self) -> &RenderWindow { + &self.window + } + + pub fn render_window(&self) -> Arc { + self.window.clone() + } + + pub async fn draw(&mut self, app: &App, draw_list: DrawList) { + app.draw(&self.window, draw_list).await; + } + + pub async fn load_data(buffer: Arc>>, ctx: &Ctx, render_window: &RenderWindow) { + let output = &render_window.output().output_buffer; + + let (sender, receiver) = flume::bounded(1); + let slice = output.slice(..); + slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap()); + + let device = &ctx.device; + device.poll(wgpu::Maintain::wait()).panic_on_timeout(); + receiver.recv_async().await.unwrap().unwrap(); + + { + let view = slice.get_mapped_range(); + let mut buffer = buffer.lock().unwrap(); + buffer.copy_from_slice(&view[..]); + } + output.unmap(); + } + + pub fn update_texture(&self, cx: &mut Cx, buffer: Vec) { + self._texture.put_back_vec_u8(cx, buffer, None); + } + + pub fn buffer(&self) -> Arc>> { + self._buffer.clone() } } diff --git a/mp/Cargo.toml b/mp/Cargo.toml index 2dcc5ac..2e69bcd 100644 --- a/mp/Cargo.toml +++ b/mp/Cargo.toml @@ -13,5 +13,9 @@ native-dialog = "0.7.0" once_cell = "1.20.2" tracing = "0.1.40" tracing-subscriber = "0.3.18" -mp_elements = { path = "../mp_elements", version = "*" } +mp_elements = { path = "../mp_elements", version = "*" } tokio = { version = "1.41.1", features = ["full"] } + +element_bridge = { path = "../element_bridge", version = "*" } +futures = "0.3.31" +async-trait = "0.1.83" diff --git a/mp/src/app.rs b/mp/src/app.rs index 278d211..ac36111 100644 --- a/mp/src/app.rs +++ b/mp/src/app.rs @@ -1,8 +1,17 @@ +use crate::widgets::area::TAreaWidgetRefExt; +use crate::windows_manager::WindowsManager; +use crate::{render_task::RenderTasks, PLUGIN_MANAGER, RUNTIME}; +use crate::{DATAPOOL, GIAPP}; +use ::log::info; use makepad_widgets::makepad_micro_serde::*; use makepad_widgets::*; -use mp_elements::elements::Element; +use mp_core::Data; +use mp_elements::elements::ppi::PPIConfig; +use mp_elements::elements::PPI; +use std::sync::Arc; +use window_menu::WindowMenuWidgetRefExt; -use crate::{DATAPOOL, GIAPP, PLUGIN_MANAGER}; +use tokio::sync::Mutex; live_design! { import makepad_widgets::base::*; @@ -48,6 +57,29 @@ live_design! { {} } + window_menu = { + main = Main {items: [app, file, window, help]} + + app = Sub {name: "MP", items: [about, line, settings, line, quit]} + about = Item {name: "About Makepad Studio", enabled: false} + settings = Item {name: "Settings", enabled: false} + quit = Item {name: "Quit Makepad Studio", key: KeyQ} + + file = Sub {name: "File", items: [open_file, open_window]} + open_file = Item {name: "Open File", enabled: true, shift: true, key: KeyO} + open_window = Item {name: "Open Folder", enabled: false, shift: true, key: KeyN} + + + window = Sub {name: "Window", items: [minimize, zoom, line, all_to_front]} + minimize = Item {name: "Minimize", enabled: false} + zoom = Item {name: "Zoom", enabled: false} + all_to_front = Item {name: "Bring All to Front", enabled: false} + + help = Sub {name: "Help", items: [about]} + + line = Line, + } + } } } @@ -58,6 +90,10 @@ live_design! { pub struct App { #[live] ui: WidgetRef, + #[rust] + render_tasks: Arc>, + #[rust] + windows_manager: WindowsManager, } impl LiveRegister for App { @@ -65,6 +101,7 @@ impl LiveRegister for App { crate::makepad_widgets::live_design(_cx); crate::app_ui::live_design(_cx); crate::widgets::area::live_design(_cx); + crate::widgets::renderer::live_design(_cx); } } @@ -74,31 +111,61 @@ struct AppStateRon { } impl MatchEvent for App { - fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) { - use native_dialog::FileDialog; - let ui = self.ui.clone(); - if ui.button(id!(open_modal)).clicked(&actions) { - let supported_extensions = PLUGIN_MANAGER.supported_extensions(); + // Start UP + fn handle_startup(&mut self, _cx: &mut Cx) {} - // let file = FileDialog::new() - // .add_filter("Supported files", &supported_extensions) - // .show_open_single_file() - // .unwrap(); - - // if let Some(file) = file { - // if let Ok(d) = DATAPOOL.get_or_load(file) {} - // } - - ui.modal(id!(modal)).open(cx); - // ui.popup_notification(id!(test_noti)).open(cx); - } - } + fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {} } impl AppMain for App { fn handle_event(&mut self, cx: &mut Cx, event: &Event) { self.match_event(cx, event); self.ui.handle_event(cx, event, &mut Scope::empty()); + spawn_background(self, event); + handle_menu(event); + } +} + +fn spawn_background(app: &mut App, event: &Event) { + match event { + Event::Draw(_) => { + // Render Task + + info!("Starting background task......"); + let tasks = app.render_tasks.clone(); + RUNTIME.spawn(async move { + let tasks = tasks.lock().await; + tasks.render().await; + }); + let tasks = app.render_tasks.clone(); + let all_buffers = app.windows_manager.buffer(); + RUNTIME.spawn(async move { + let mut tasks = tasks.lock().await; + tasks.listen(all_buffers).await; + }); + } + _ => {} + } +} + +fn handle_menu(event: &Event) { + match event { + Event::MacosMenuCommand(command) => { + if command == &LiveId::from_str("open_file") { + use native_dialog::FileDialog; + info!("Open File Dialog"); + let supported_extensions = PLUGIN_MANAGER.supported_extensions(); + let file = FileDialog::new() + .add_filter("Supported files", &supported_extensions) + .show_open_single_file() + .unwrap(); + + if let Some(file) = file { + info!("File: {:?}", file); + } + } + } + _ => {} } } diff --git a/mp/src/app_ui.rs b/mp/src/app_ui.rs index 8d38d64..1896f5a 100644 --- a/mp/src/app_ui.rs +++ b/mp/src/app_ui.rs @@ -3,6 +3,7 @@ live_design! { import makepad_widgets::base::*; import makepad_widgets::theme_desktop_dark::*; import crate::widgets::area::Area; + import crate::widgets::renderer::IRenderer; import makepad_draw::shader::std::*; @@ -59,28 +60,28 @@ live_design! { y: 0.5 }, quad = { - draw: { - // this example shader is ported from kishimisu's tutorial - fn pixel(self) -> vec4 { - // let uv = self.pos - 0.5; - // let uv0 = uv; - // let finalColor = vec3(0.0); + // draw: { + // // this example shader is ported from kishimisu's tutorial + // fn pixel(self) -> vec4 { + // // let uv = self.pos - 0.5; + // // let uv0 = uv; + // // let finalColor = vec3(0.0); - // let i = 0; - // for _i in 0..4 { // you cannot refer to _i inside the for loop; use i instead - // uv = fract(uv * -1.5) - 0.5; - // let d = length(uv) * exp(-length(uv0)); - // let col = Pal::iq2(length(uv0) + float(i) * .4 + self.time * .4); - // d = sin(d*8. + self.time) / 8.; - // d = abs(d); - // d = pow(0.01 / d, 1.2); - // finalColor += col * d; - // i = i+1; - // } + // // let i = 0; + // // for _i in 0..4 { // you cannot refer to _i inside the for loop; use i instead + // // uv = fract(uv * -1.5) - 0.5; + // // let d = length(uv) * exp(-length(uv0)); + // // let col = Pal::iq2(length(uv0) + float(i) * .4 + self.time * .4); + // // d = sin(d*8. + self.time) / 8.; + // // d = abs(d); + // // d = pow(0.01 / d, 1.2); + // // finalColor += col * d; + // // i = i+1; + // // } - // return vec4(finalColor ,1); - } - } + // // return vec4(finalColor ,1); + // } + // } } diff --git a/mp/src/handle_events.rs b/mp/src/handle_events.rs new file mode 100644 index 0000000..c941978 --- /dev/null +++ b/mp/src/handle_events.rs @@ -0,0 +1,44 @@ +use log::*; +use std::sync::{Arc, Mutex}; + +use crate::{app::App, render_task::RenderTasks, windows_manager::WindowsManager, GIAPP, RUNTIME}; + +pub async fn register_task(tasks: Arc>, windows_manager: &mut WindowsManager) { + let render_window = { + let bridge = windows_manager.get_bridge(0).unwrap(); + let b = bridge.lock().unwrap(); + b.render_window().clone() + }; + + if let Ok(data) = DATAPOOL.get_or_load_async(file).await { + // First Block + let first_block = data.get(0).unwrap(); + let first_block: &Data = &*first_block; + let pipelines = GIAPP.pipelines(); + let ppi = pipelines.ppi(); + let first_block = first_block.try_into().unwrap(); + + let attachment = { + let ppi: &PPI = &*ppi; + let attachment = GIAPP.load_data( + ppi, + first_block, + &PPIConfig { + colormap: vec![[1.0, 1.0, 1.0, 1.0], [1.0, 1.0, 1.0, 1.0]], + color_range: [0.0, 1.0], + }, + ); + + attachment + }; + + let mut tasks = tasks.lock().await; + let mut draw_list = mp_elements::app::DrawList::new(); + let attachment = Arc::new(attachment); + draw_list.push(ppi.clone(), attachment); + tasks.register_task(0, render_window, draw_list); + + info!("task registered, all {}", tasks.tasks.len()); + info!("Data loaded"); + } +} diff --git a/mp/src/lib.rs b/mp/src/lib.rs index 60034e8..31dfb2f 100644 --- a/mp/src/lib.rs +++ b/mp/src/lib.rs @@ -1,12 +1,12 @@ use mp_elements; use tokio::runtime::Runtime; pub mod app; +pub mod windows_manager; pub use makepad_widgets; pub use makepad_widgets::makepad_draw; pub use makepad_widgets::makepad_platform; pub mod app_ui; -pub mod render; -pub mod shaders; +pub mod render_task; pub mod widgets; use mp_core::{config::Setting, datapool::DataPool, plugin_system::PluginManager}; diff --git a/mp/src/render/camera.rs b/mp/src/render/camera.rs deleted file mode 100644 index 81ce860..0000000 --- a/mp/src/render/camera.rs +++ /dev/null @@ -1 +0,0 @@ -pub struct Camera {} diff --git a/mp/src/render/data.rs b/mp/src/render/data.rs deleted file mode 100644 index f5be4e4..0000000 --- a/mp/src/render/data.rs +++ /dev/null @@ -1,46 +0,0 @@ -use makepad_widgets::{Cx, Texture, TextureFormat}; -use mp_core::data::RadarGridData; - -pub enum Data { - GridData(GridData), -} -pub struct GridData { - texture: Texture, - clear_buffer: bool, - shape: Option>, -} - -impl GridData { - pub fn new(cx: &mut Cx) -> Self { - let mut texture = Texture::new_with_format(cx, TextureFormat::Unknown); - - texture.texture_id(); - - GridData { - texture, - clear_buffer: true, - shape: None, - } - } - - pub fn update(&mut self, cx: &mut Cx, data: &RadarGridData) { - let data = data.data.cast_to::(); - self.clear_buffer = false; - // self.shape = Some(data.dim().into()); - // self.texture.put_back_vec_f32(cx, data.into(), None); - } - - pub fn clear(&mut self) { - self.clear_buffer = true; - } - - pub fn texture(&self) -> &Texture { - &self.texture - } -} - -impl From for Data { - fn from(data: GridData) -> Self { - Data::GridData(data) - } -} diff --git a/mp/src/render/mod.rs b/mp/src/render/mod.rs deleted file mode 100644 index 2292b3a..0000000 --- a/mp/src/render/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod camera; -pub mod data; diff --git a/mp/src/render_task.rs b/mp/src/render_task.rs new file mode 100644 index 0000000..fd51969 --- /dev/null +++ b/mp/src/render_task.rs @@ -0,0 +1,123 @@ +use crate::{ + widgets::renderer, + windows_manager::{AllBuffers, WindowsManager}, + GIAPP, RUNTIME, +}; +use element_bridge::TextureBridge; +use futures::future::BoxFuture; +use log::info; +use makepad_widgets::{Cx, Texture}; +use mp_elements::{ + app::{DrawList, RenderWindow}, + App, +}; +use std::sync::{Arc, Mutex}; + +#[derive(Default)] +pub struct RenderTasks { + pub(crate) tasks: Vec<(tokio::sync::mpsc::Receiver>, RenderTask)>, +} + +impl RenderTasks { + pub fn new() -> Self { + Self { tasks: Vec::new() } + } + + fn push_task(&mut self, task: (tokio::sync::mpsc::Receiver>, RenderTask)) { + self.tasks.push(task); + } + + pub fn register_task( + &mut self, + bridge_id: usize, + bridge: Arc, + draw_list: DrawList, + ) { + let (task, receiver) = RenderTask::new(bridge_id, bridge, draw_list); + self.push_task((receiver, task)); + } + + pub fn clear(&mut self) { + self.tasks.clear(); + } + + pub async fn render(&self) { + // Draw all tasks + let futures: Vec<_> = self + .tasks + .iter() + .map(|(_, task)| async { + info!("Drawing task"); + task.draw().await; + }) + .collect(); + info!("all tasks: {:?}", futures.len()); + futures::future::join_all(futures).await; + } + + pub async fn listen(&mut self, manager: AllBuffers) { + let mut futures = Vec::new(); + + for (receiver, _) in self.tasks.iter_mut() { + let manager = manager.lock().unwrap(); + let buffer = manager.get(&0).unwrap().clone(); + let future = async move { + while let Some(data) = receiver.recv().await { + let mut buffer = buffer.lock().await; + info!("Received data"); + buffer.copy_from_slice(&data); + } + }; + futures.push(Box::pin(future)); + } + + futures::future::join_all(futures).await; + } +} + +pub struct RenderTask { + // bridge: element_bridge::TextureBridge, + bridge_id: usize, + sender: tokio::sync::mpsc::Sender>, + buffer: Arc>>, + render_window: Arc, + draw_list: DrawList, +} + +impl RenderTask { + pub fn new( + bridge_id: usize, + bridge_render_window: Arc, + draw_list: DrawList, + ) -> (Self, tokio::sync::mpsc::Receiver>) { + let (sender, receiver) = tokio::sync::mpsc::channel(1); + ( + Self { + bridge_id, + sender, + buffer: Arc::new(Mutex::new(vec![0u8; 256 * 256 * 4])), + render_window: bridge_render_window, + draw_list, + }, + receiver, + ) + } + + pub async fn draw(&self) { + GIAPP + .draw(&self.render_window, self.draw_list.clone()) + .await; + + let ctx = GIAPP.ctx(); + + TextureBridge::load_data(self.buffer.clone(), ctx, &self.render_window).await; + + let bf = { + let buffer = self.buffer.clone(); + let buffer = buffer.lock().unwrap(); + buffer.clone() + }; + + self.sender.send(bf).await.unwrap(); + } +} diff --git a/mp/src/shaders/colormap.rs b/mp/src/shaders/colormap.rs deleted file mode 100644 index 192e09d..0000000 --- a/mp/src/shaders/colormap.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::render::data::GridData; -use makepad_widgets::*; - -live_design! { - ColorMap = {{ColorMap}} { - - // Cameras - uniform view: mat4 - uniform projection: mat4 - uniform model: mat4 - - uniform conf: vec4 - - varing value: float - varing range: vec4 - - // Data - texture data: texture3d - // ColorMAPPER - textrue color_map: texture1d - - fn get_value_at(self, pos: vec3) -> float { - return sampler3d_rt(self.data, pos).r; - } - - fn vertex(self) -> vec4 { - let v = sampler2d_rt(self.data, ) - - } - - fn pixel(self) -> vec4 { - return vec4(1., 0., 0., 1.); - } - - } -} - -#[repr(C)] -#[derive(Live, LiveRegister)] -pub struct ColorMap { - #[deref] - draw_vars: DrawVars, - #[live] - geometry: GeometryQuad2D, - #[calc] - pub position: Vec3, - #[calc] - pub value: f32, -} - -impl LiveHook for ColorMap { - fn before_apply(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) { - self.draw_vars - .before_apply_init_shader(cx, apply, index, nodes, &self.geometry); - } - - fn after_apply(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) { - self.draw_vars - .after_apply_update_self(cx, apply, index, nodes, &self.geometry); - } -} - -impl ColorMap {} diff --git a/mp/src/shaders/ppi.rs b/mp/src/shaders/ppi.rs deleted file mode 100644 index 528f9b4..0000000 --- a/mp/src/shaders/ppi.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::render::camera::Camera; -use crate::render::data::GridData; -use makepad_widgets::*; - -live_design! { - PPI = {{PPI}} { - - // Cameras - uniform view: mat4 - uniform projection: mat4 - uniform model: mat4 - - uniform conf: vec4 - - varing value: float - varing range: vec4 - - // Data - texture data: texture2d - - // ColorMAPPER - textrue color_map: texture2d - - fn get_value_at(self, pos: vec3) -> float { - return sampler3d(self.data, pos).r; - } - - fn vertex(self) -> vec4 { - let v = sampler2d_rt(self.data, ) - - } - - fn pixel(self) -> vec4 { - return vec4(1., 0., 0., 1.); - } - - } -} - -#[repr(C)] -#[derive(Live, LiveRegister)] -pub struct PPI { - #[deref] - pub draw_vars: DrawVars, - #[live] - geometry: GeometryQuad2D, - #[calc] - pub position: Vec3, - #[calc] - pub value: f32, -} - -impl LiveHook for PPI { - fn before_apply(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) { - self.draw_vars - .before_apply_init_shader(cx, apply, index, nodes, &self.geometry); - } - - fn after_apply(&mut self, cx: &mut Cx, apply: &mut Apply, index: usize, nodes: &[LiveNode]) { - self.draw_vars - .after_apply_update_self(cx, apply, index, nodes, &self.geometry); - } -} - -impl PPI { - pub fn update_draw_call_vars(&mut self, camera: &Camera, data: &GridData) { - self.draw_vars.texture_slots[0] = Some(data.texture().clone()); - } -} diff --git a/mp/src/widgets/area.rs b/mp/src/widgets/area.rs index 130e97d..6efa848 100644 --- a/mp/src/widgets/area.rs +++ b/mp/src/widgets/area.rs @@ -1,6 +1,16 @@ +use std::cell::RefCell; +use std::sync::Arc; +use std::sync::Mutex; + +use crate::windows_manager::WindowId; +use crate::windows_manager::WindowsManager; +use crate::GIAPP; +use element_bridge::{TextureBridge, Window}; use makepad_widgets::makepad_derive_widget::*; use makepad_widgets::makepad_draw::*; use makepad_widgets::widget::*; +use mp_elements::renderer::camera::Camera; +use mp_elements::renderer::projection::Projection; live_design! { Area = {{TArea}} {} @@ -17,32 +27,81 @@ pub struct TArea { layout: Layout, #[live] time: f32, - // #[rust] - // next_frame: NextFrame, + + #[rust] + bridge: Option>>, } -impl LiveHook for TArea { - fn after_new_from_doc(&mut self, cx: &mut Cx) { - // starts the animation cycle on startup - // self.next_frame = cx.new_next_frame(); +#[derive(Debug, Clone)] +pub enum Status { + Share(std::rc::Rc>), + Own(T), +} + +#[derive(Clone)] +pub struct TAreaState { + pub time: f32, + pub camera: Status, + pub bridge: Option, + pub projection: Projection, +} + +impl Default for TAreaState { + fn default() -> Self { + Self { + time: 0.0, + camera: Status::Own(Camera::default()), + bridge: None, + projection: Projection::default(), + } } } +impl TAreaState { + pub fn set_camera(&mut self, f: F) { + match self.camera { + Status::Share(ref camera) => { + f(&mut camera.borrow_mut()); + } + Status::Own(ref mut camera) => { + f(camera); + } + } + } + + pub fn resize(&mut self, width: f32, height: f32) { + self.projection.resize(width as u32, height as u32); + } + + pub fn set_bridge(&mut self, bridge: TextureBridge) { + self.bridge = Some(bridge); + } + + pub fn new_bridge(&mut self, cx: &mut Cx, window: Window) { + let texture = Texture::new_with_format( + cx, + TextureFormat::VecRGBAf32 { + width: window.width as usize, + height: window.height as usize, + data: None, + updated: TextureUpdated::Full, + }, + ); + + let bridge = TextureBridge::new(texture, &GIAPP, window); + self.bridge = Some(bridge); + } +} + +impl LiveHook for TArea {} + #[derive(Clone, DefaultNone)] pub enum MyWidgetAction { None, } impl Widget for TArea { - fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) { - // if let Some(ne) = self.next_frame.is_event(event) { - // // update time to use for animation - // self.time = (ne.time * 0.001).fract() as f32; - // // force updates, so that we can animate in the absence of user-generated events - // self.redraw(cx); - // self.next_frame = cx.new_next_frame(); - // } - } + fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {} fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep { self.draw.begin(cx, walk, self.layout); @@ -50,3 +109,49 @@ impl Widget for TArea { DrawStep::done() } } + +impl TArea { + fn set_bridge(&mut self, bridge: Arc>) { + self.bridge = Some(bridge); + self.draw + .draw_vars + .set_texture(0, self.bridge.as_ref().unwrap().lock().unwrap().texture()); + } + + fn init_bridge(&mut self, cx: &mut Cx, manager: &mut WindowsManager) { + let window_id = WindowId::new("Primary"); + + // let height = self.walk.height. + // let width = self.walk.width.fixed_or_zero(); + let height = 256; + let width = 256; + + let window = Window { + height: height as u32, + width: width as u32, + }; + + let texture = Texture::new_with_format( + cx, + TextureFormat::VecRGBAf32 { + width: window.width as usize, + height: window.height as usize, + data: None, + updated: TextureUpdated::Full, + }, + ); + + let bridge = TextureBridge::new(texture, &GIAPP, window); + let (_, bridge) = manager.add_window(window_id, bridge); + + self.set_bridge(bridge); + } +} + +impl TAreaRef { + pub fn init_bridge(&self, cx: &mut Cx, manager: &mut WindowsManager) { + if let Some(mut bridge) = self.borrow_mut() { + bridge.init_bridge(cx, manager); + } + } +} diff --git a/mp/src/widgets/mod.rs b/mp/src/widgets/mod.rs index 31c7df9..5eaa31d 100644 --- a/mp/src/widgets/mod.rs +++ b/mp/src/widgets/mod.rs @@ -1 +1,2 @@ pub mod area; +pub mod renderer; diff --git a/mp/src/widgets/renderer.rs b/mp/src/widgets/renderer.rs new file mode 100644 index 0000000..06efdac --- /dev/null +++ b/mp/src/widgets/renderer.rs @@ -0,0 +1,110 @@ +use std::cell::RefCell; + +use crate::GIAPP; +use element_bridge::{TextureBridge, Window}; +use makepad_widgets::makepad_derive_widget::*; +use makepad_widgets::makepad_draw::*; +use makepad_widgets::widget::*; +use mp_elements::renderer::camera::Camera; +use mp_elements::renderer::projection::Projection; + +live_design! { + IRenderer = {{Renderer}} { + + import crate::widgets::area::Area; + { + draw: { + fn pixel(self) -> vec4 { + return mix(#7,#4,self.pos.y); + } + } + } + + } +} + +#[derive(Live, Widget)] +pub struct Renderer { + #[redraw] + #[live] + draw: DrawQuad, + #[walk] + walk: Walk, + #[layout] + layout: Layout, + #[rust] + state: RenderState, +} + +#[derive(Debug, Clone)] +pub enum Status { + Share(std::rc::Rc>), + Own(T), +} + +#[derive(Clone)] +pub struct RenderState { + pub time: f32, + pub camera: Status, + pub bridge: Option, + pub projection: Projection, +} + +impl Default for RenderState { + fn default() -> Self { + Self { + time: 0.0, + camera: Status::Own(Camera::default()), + bridge: None, + projection: Projection::default(), + } + } +} + +impl RenderState { + pub fn set_camera(&mut self, f: F) { + match self.camera { + Status::Share(ref camera) => { + f(&mut camera.borrow_mut()); + } + Status::Own(ref mut camera) => { + f(camera); + } + } + } + + pub fn resize(&mut self, width: f32, height: f32) { + self.projection.resize(width as u32, height as u32); + } + + pub fn set_bridge(&mut self, bridge: TextureBridge) { + self.bridge = Some(bridge); + } + + pub fn new_bridge(&mut self, cx: &mut Cx, window: Window) { + let texture = Texture::new_with_format( + cx, + TextureFormat::VecRGBAf32 { + width: window.width as usize, + height: window.height as usize, + data: None, + updated: TextureUpdated::Full, + }, + ); + + let bridge = TextureBridge::new(texture, &GIAPP, window); + self.bridge = Some(bridge); + } +} + +impl LiveHook for Renderer {} + +impl Widget for Renderer { + fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {} + + fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep { + self.draw.begin(cx, walk, self.layout); + self.draw.end(cx); + DrawStep::done() + } +} diff --git a/mp/src/windows_manager.rs b/mp/src/windows_manager.rs new file mode 100644 index 0000000..4d44389 --- /dev/null +++ b/mp/src/windows_manager.rs @@ -0,0 +1,73 @@ +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use element_bridge::TextureBridge; + +pub type AllBuffers = Arc>>>>>; + +#[derive(Eq, Hash, PartialEq)] +pub struct WindowId { + id: u64, + name: String, +} +#[derive(Default)] +pub struct WindowsManager { + inner: HashMap>>, + + all_bridges: Vec<(usize, Arc>)>, + + all_revelant_buffers: Arc>>>>>, +} + +impl WindowsManager { + pub fn new() -> Self { + Self { + inner: HashMap::new(), + all_bridges: Vec::new(), + all_revelant_buffers: Arc::new(Mutex::new(HashMap::new())), + } + } + + pub fn add_window( + &mut self, + id: WindowId, + bridge: TextureBridge, + ) -> (usize, Arc>) { + let bridge = Arc::new(Mutex::new(bridge)); + self.inner.insert(id, bridge.clone()); + + let new_id = self.all_bridges.len(); + self.all_bridges.push((new_id, bridge.clone())); + + let buffer = { + let bridge = bridge.lock().unwrap(); + bridge.buffer() + }; + + // self.all_revelant_buffers.push((new_id, buffer)); + self.all_revelant_buffers + .lock() + .unwrap() + .insert(new_id, buffer); + (new_id, bridge) + } + + pub fn get_bridge(&self, id: usize) -> Option>> { + self.all_bridges.get(id).map(|(_, bridge)| bridge.clone()) + } + + pub fn buffer(&self) -> AllBuffers { + self.all_revelant_buffers.clone() + } +} + +impl WindowId { + pub fn new>(name: S) -> Self { + Self { + id: 0, + name: name.into(), + } + } +} diff --git a/mp_elements/Cargo.toml b/mp_elements/Cargo.toml index 8c09113..a550f76 100644 --- a/mp_elements/Cargo.toml +++ b/mp_elements/Cargo.toml @@ -6,15 +6,17 @@ edition = "2021" [dependencies] bytemuck = { version = "1.19.0", features = ["derive"] } glam = { version = "0.29.2", features = ["bytemuck"] } -regex = "1.11.1" wgpu = "23.0.0" mp_core = { path = "../mp_core", version = "*" } -flume = "0.11.1" -pollster = "0.4.0" quick_cache = "0.6.9" encase = { version = "0.10.0", features = ["glam"] } image = "0.25.5" rust-embed = "8.5.0" +flume = "0.11.1" +log = "0.4.22" [build-dependencies] regex = "1.11.1" + +[dev-dependencies] +pollster = "0.4.0" diff --git a/mp_elements/build.rs b/mp_elements/build.rs index b7acf65..c40bb40 100644 --- a/mp_elements/build.rs +++ b/mp_elements/build.rs @@ -22,6 +22,11 @@ fn main() { println!("cargo:rerun-if-changed={}", path.display()); } + let path_name = path.file_stem().unwrap().to_str().unwrap(); + if path_name.contains("merged") { + continue; + } + let merged_shader = merge_shader(&path.display().to_string(), &shader_path_base); let out_path = Path::new(&crate_path) diff --git a/mp_elements/image.png b/mp_elements/image.png index c655438..182a8d0 100644 Binary files a/mp_elements/image.png and b/mp_elements/image.png differ diff --git a/mp_elements/shaders/colormap.wgsl b/mp_elements/shaders/colormap.wgsl index 4d02b5e..44c42a1 100644 --- a/mp_elements/shaders/colormap.wgsl +++ b/mp_elements/shaders/colormap.wgsl @@ -1,20 +1,20 @@ -@group(1) @binding(0) var color_map_texture: texture_2d; +@group(1) @binding(0) var color_map_texture: texture_1d; @group(1) @binding(1) var color_map_sampler: sampler; @group(1) @binding(2) var color_map_params: ColorMapParams; struct ColorMapParams { color_count: u32, value_min: f32, - value_max: f32 + value_max: f32, invalid_value: f32 } fn find_idx(ratio: f32) -> f32 { var sum = 0.0; var i = 0.0; - var count = (color_map_params.color_count - 1) as f32; + let count = f32(color_map_params.color_count - 1); while (ratio > sum) { - sum += textureSample(color_map_texture, color_map_sampler, vec2(i / count, 0.0)).r; + sum += textureSample(color_map_texture, color_map_sampler, i / count).r; i += 1.0; } return i / count; @@ -22,8 +22,8 @@ fn find_idx(ratio: f32) -> f32 { fn linear_colormap(value: f32) -> vec4f { - var v = clamp((value - color_map_params.value_min) / (color_map_params.value_max - color_map_params.value_min), 0.0, 1.0); - float idx = find_idx(v); - let c0: vec3f = textureSample(color_map_texture, color_map_sampler, vec2(idx, 0.0)).rgb; + let v = clamp((value - color_map_params.value_min) / (color_map_params.value_max - color_map_params.value_min), 0.0, 1.0); + let idx = find_idx(v); + let c0 = textureSample(color_map_texture, color_map_sampler, idx).rgb; return vec4f(c0, 1.0); } \ No newline at end of file diff --git a/mp_elements/shaders/elements/ppi.wgsl b/mp_elements/shaders/elements/ppi.wgsl index d8a3289..8da1a87 100644 --- a/mp_elements/shaders/elements/ppi.wgsl +++ b/mp_elements/shaders/elements/ppi.wgsl @@ -1,4 +1,5 @@ #include "constants.wgsl"; +#include "colormap.wgsl"; // Common Uniforms // common_tools // model_matrix: mat4, @@ -12,9 +13,9 @@ // light_intensity: float, // Uniforms -@group(1) @binding(0) var params: UniformParams; +@group(2) @binding(0) var params: UniformParams; // Data Buffer -@group(1) @binding(1) var data: array; +@group(2) @binding(1) var data: array; struct UniformParams { origin: vec4f @@ -36,8 +37,7 @@ fn vertex( var out: VertexOutput; // Transform position - out.position = common_tools.proj_matrix * common_tools.view_matrix * common_tools.model_matrix * vec4f(position, 1.0); - // out.position = vec4(position.xyz, 1.0); + out.position = vec4(position.xyz, 1.0); out.r_range = r_range; let idx = u32(position.w); out.idx = idx; @@ -57,8 +57,8 @@ fn fragment(input: VertexOutput) -> @location(0) vec4f { // Sample data texture let value = data[input.idx]; let ear = polar_forward(input.position.xyz); - // var color = linear_colormap(value); + // let color = linear_colormap(value); var color = clamp(value / 75.0, 0.0, 1.0); // let r = ear.x; diff --git a/mp_elements/shaders/elements/ppi_merged.wgsl b/mp_elements/shaders/elements/ppi_merged.wgsl index 0736c03..5830148 100644 --- a/mp_elements/shaders/elements/ppi_merged.wgsl +++ b/mp_elements/shaders/elements/ppi_merged.wgsl @@ -26,6 +26,35 @@ const HALF_PI:f32 = 1.57079632679489661923132169163975144; const LOG2:f32 = 0.693147180559945309417232121458176568; const LOG10:f32 = 2.30258509299404568401799145468436421; +@group(1) @binding(0) var color_map_texture: texture_1d; +@group(1) @binding(1) var color_map_sampler: sampler; +@group(1) @binding(2) var color_map_params: ColorMapParams; + +struct ColorMapParams { + color_count: u32, + value_min: f32, + value_max: f32, + invalid_value: f32 +} + +fn find_idx(ratio: f32) -> f32 { + var sum = 0.0; + var i = 0.0; + let count = f32(color_map_params.color_count - 1); + while (ratio > sum) { + sum += textureSample(color_map_texture, color_map_sampler, i / count).r; + i += 1.0; + } + return i / count; +} + + +fn linear_colormap(value: f32) -> vec4f { + let v = clamp((value - color_map_params.value_min) / (color_map_params.value_max - color_map_params.value_min), 0.0, 1.0); + let idx = find_idx(v); + let c0 = textureSample(color_map_texture, color_map_sampler, idx).rgb; + return vec4f(c0, 1.0); +} // Common Uniforms // common_tools // model_matrix: mat4, @@ -39,9 +68,9 @@ const LOG10:f32 = 2.30258509299404568401799145468436421; // light_intensity: float, // Uniforms -@group(1) @binding(0) var params: UniformParams; +@group(2) @binding(0) var params: UniformParams; // Data Buffer -@group(1) @binding(1) var data: array; +@group(2) @binding(1) var data: array; struct UniformParams { origin: vec4f @@ -63,8 +92,7 @@ fn vertex( var out: VertexOutput; // Transform position - out.position = common_tools.proj_matrix * common_tools.view_matrix * common_tools.model_matrix * vec4f(position, 1.0); - // out.position = vec4(position.xyz, 1.0); + out.position = vec4(position.xyz, 1.0); out.r_range = r_range; let idx = u32(position.w); out.idx = idx; @@ -84,8 +112,8 @@ fn fragment(input: VertexOutput) -> @location(0) vec4f { // Sample data texture let value = data[input.idx]; let ear = polar_forward(input.position.xyz); - // var color = linear_colormap(value); + // let color = linear_colormap(value); var color = clamp(value / 75.0, 0.0, 1.0); // let r = ear.x; diff --git a/mp_elements/src/app.rs b/mp_elements/src/app.rs index ea44e5d..9299fb3 100644 --- a/mp_elements/src/app.rs +++ b/mp_elements/src/app.rs @@ -1,12 +1,12 @@ +use std::cell::RefCell; +use std::sync::{Arc, Mutex, MutexGuard}; + +use crate::elements::{Element, ElementAttach, Elements, ElementsRef}; +use crate::elementvec::ElementVec; +use encase; use quick_cache::sync::Cache; use wgpu::util::DeviceExt; - -use crate::elements::{Element, ElementAttach, ElementsRef}; -use crate::elementvec::ElementVec; use wgpu::{Backends, Instance}; - -use encase; - type DB = std::sync::Arc; const BACKENDS_DEFAULT: u32 = Backends::DX12.bits() @@ -15,13 +15,8 @@ const BACKENDS_DEFAULT: u32 = Backends::DX12.bits() | Backends::BROWSER_WEBGPU.bits(); pub struct App { - _texture: wgpu::Texture, - _texture_size: wgpu::Extent3d, - texture_view: wgpu::TextureView, - output: Output, - pub pipelines: Option, - pub ctx: Ctx, - + pub(crate) pipelines: Option, + ctx: Ctx, buffer_pool: DataBufferPool, } @@ -59,44 +54,22 @@ impl App { // Create a new instance of the common utils struct. This struct contains the bind group layout, bind group, and uniform buffer. let common_utils = CommonUtils::new(&device); - // Create a new texture. This texture will be used as the output texture for the render pass. - let texture_size = wgpu::Extent3d { - width: 256, - height: 256, - depth_or_array_layers: 1, - }; - let texture = device.create_texture(&wgpu::TextureDescriptor { - label: Some("output texture"), - size: texture_size, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, - view_formats: &[], - }); - - // Create a texture view from the texture. This texture view will be used as the output texture for the render pass. - let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - // Buffer pool let buffer_pool = DataBufferPool::new(); let ctx = Ctx::new(device, queue, common_utils); - let output = Output::new(&ctx.device); - Self { pipelines: None, - _texture: texture, - _texture_size: texture_size, - texture_view, buffer_pool, - output, ctx, } } + pub fn buffer_pool(&mut self) -> &mut DataBufferPool { + &mut self.buffer_pool + } + // Create a new context struct. This struct contains references to the device, queue, bind group layout, and bind group. pub fn ctx(&self) -> &Ctx { &self.ctx @@ -117,7 +90,7 @@ impl App { } // Draw the elements in the draw list. - pub async fn draw(&self, draw_list: DrawList<'_, '_>) { + pub async fn draw(&self, window: &RenderWindow, draw_list: DrawList) { let mut encoder = self .ctx .device @@ -129,7 +102,7 @@ impl App { let render_pass_desc = wgpu::RenderPassDescriptor { label: Some("Some Render Pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &self.texture_view, + view: &window.texture_view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), @@ -161,24 +134,105 @@ impl App { attach.bind(&mut render_pass); // Draw the element. - element.draw(attach, &mut render_pass); + element.draw(&attach, &mut render_pass); } } - // output - self.output - .output(&mut encoder, &self._texture, self._texture_size, &self.ctx); + // Output + window.finish(&mut encoder); self.ctx.queue.submit(Some(encoder.finish())); - - self.output.get_data(&self.ctx).await; } - pub fn load_data<'a, T>(&mut self, element: &T, data: &T::Data) -> ElementAttach + pub fn drop_all_buffers(&mut self) { + self.buffer_pool.buffers.clear(); + } + + pub fn load_data<'a, T>(&self, element: &T, data: &T::Data, config: &T::Config) -> ElementAttach where T: Element, { - let buffer_pool = &mut self.buffer_pool; + let buffer_pool = &self.buffer_pool; let ctx = &self.ctx; - element.load_data(&ctx, data, buffer_pool) + element.load_data(&ctx, data, buffer_pool, config) + } + + pub fn create_window(&self, window: Window) -> RenderWindow { + RenderWindow::new(window, &self.ctx.device) + } +} + +pub struct Window { + pub width: u32, + pub height: u32, +} + +pub struct RenderWindow { + _texture: wgpu::Texture, + _texture_size: wgpu::Extent3d, + texture_view: wgpu::TextureView, + output: Output, + window: Window, +} + +impl RenderWindow { + pub fn new(window: Window, device: &wgpu::Device) -> Self { + // Create a new texture. This texture will be used as the output texture for the render pass. + let texture_size = wgpu::Extent3d { + width: window.width, + height: window.height, + depth_or_array_layers: 1, + }; + + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("output texture"), + size: texture_size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + view_formats: &[], + }); + + // Create a texture view from the texture. This texture view will be used as the output texture for the render pass. + let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + + let output = Output::new(device); + + Self { + _texture: texture, + _texture_size: texture_size, + texture_view, + output, + window, + } + } + + pub fn output(&self) -> &Output { + &self.output + } + + pub fn finish(&self, encoder: &mut wgpu::CommandEncoder) { + encoder.copy_texture_to_buffer( + wgpu::ImageCopyTexture { + texture: &self._texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::ImageCopyBuffer { + buffer: &self.output.output_buffer, + layout: wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(256 * 4), + rows_per_image: Some(256), + }, + }, + wgpu::Extent3d { + width: self.window.width, + height: self.window.height, + depth_or_array_layers: 1, + }, + ); } } @@ -202,16 +256,21 @@ impl Ctx { } } -pub struct DrawList<'b, 'a: 'b> { - pub elements: Vec<(&'b ElementAttach, ElementsRef<'a>)>, // 修复字段访问权限 +#[derive(Clone)] +pub struct DrawList { + pub elements: Vec<(std::sync::Arc, Elements)>, // 修复字段访问权限 } -impl<'b, 'a: 'b> DrawList<'b, 'a> { +impl DrawList { pub fn new() -> Self { Self { elements: vec![] } } - pub fn push>>(&mut self, element: Ele, attach: &'a ElementAttach) { + pub fn push>( + &mut self, + element: Ele, + attach: std::sync::Arc, + ) { self.elements.push((attach, element.into())); } } @@ -298,7 +357,7 @@ impl DataBufferPool { } } - pub fn get_or_create_buffer(&mut self, key: BufferKey, f: F) -> DB + pub fn get_or_create_buffer(&self, key: BufferKey, f: F) -> DB where F: FnOnce() -> wgpu::Buffer, { @@ -325,7 +384,7 @@ impl BufferKey { } pub struct Output { - output_buffer: wgpu::Buffer, + pub output_buffer: wgpu::Buffer, } impl Output { @@ -346,34 +405,6 @@ impl Output { Self { output_buffer } } - pub fn output( - &self, - encoder: &mut wgpu::CommandEncoder, - texture: &wgpu::Texture, - texture_size: wgpu::Extent3d, - ctx: &Ctx, - ) { - let u32_size = std::mem::size_of::() as u32; - - encoder.copy_texture_to_buffer( - wgpu::ImageCopyTexture { - aspect: wgpu::TextureAspect::All, - texture: &texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - }, - wgpu::ImageCopyBuffer { - buffer: &self.output_buffer, - layout: wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: Some(u32_size * 256), - rows_per_image: Some(256), - }, - }, - texture_size, - ); - } - pub async fn get_data(&self, ctx: &Ctx) { let device = &ctx.device; // 需要对映射变量设置范围,以便我们能够解除缓冲区的映射 @@ -404,29 +435,27 @@ impl Output { mod test { use mp_core::{PluginManager, RadarGridData}; + use wgpu::core::pipeline; - use crate::elements::{Element, PPI}; + use crate::elements::{ppi::PPIConfig, Element, PPI}; use super::*; #[test] fn test_app() { let plugin_manager = PluginManager::new( - // r#"/Users/tsuki/projects/mp/loaders"# - r#"C:\Users\qwin7\projects\radarmp\loaders"#, + r#"/Users/tsuki/projects/mp/loaders"#, // r#"C:\Users\qwin7\projects\radarmp\loaders"#, ) .unwrap(); let data = plugin_manager.try_load_data( - // "/Users/tsuki/Desktop/Z_RADR_I_X5775_20230726180000_O_DOR-XPD-CAP-FMT.BIN.zip", - r#"C:\Users\qwin7\Downloads\ZJSXAA_20230113070200_R.dat.gz"#, + "/Users/tsuki/Desktop/Z_RADR_I_X5775_20230726180000_O_DOR-XPD-CAP-FMT.BIN.zip", + // r#"C:\Users\qwin7\Downloads\ZJSXAA_20230113070200_R.dat.gz"#, ); pollster::block_on(async { let mut app = App::instant().await; app.init().await; - let pipelines = app.pipelines.as_ref().unwrap(); - let ppi = pipelines.ppi(); let ctx = &app.ctx; let buffer_pool = &mut app.buffer_pool; @@ -434,17 +463,33 @@ mod test { let first_block = data.first().unwrap(); // Convert the first block into a PPI struct. - if let Ok(data) = first_block.try_into() { - let attachment = ppi.load_data(&ctx, data, buffer_pool); + // if let Ok(data) = first_block.try_into() { + // // let attachment = { + // // let pipelines = app.pipelines.as_mut().unwrap(); + // // let ppi = pipelines.ppi(); - // Create a new draw list and push the attachment into it. + // // // let attachment = ppi.load_data( + // // // &ctx, + // // // data, + // // // buffer_pool, + // // // &PPIConfig { + // // // colormap: vec![[1.0, 1.0, 1.0, 1.0], [1.0, 1.0, 1.0, 1.0]], + // // // color_range: [0.0, 1.0], + // // // }, + // // // ); + // // attachment + // // }; - let mut draw_list = DrawList::new(); - draw_list.push(ppi, &attachment); + // let pipeline = app.pipelines(); + // let ppi = pipeline.ppi(); - // Draw the elements in the draw list. - app.draw(draw_list).await; - } + // // Create a new draw list and push the attachment into it. + // let mut draw_list = DrawList::new(); + // // draw_list.push(ppi, &attachment); + + // // Draw the elements in the draw list. + // // app.draw(draw_list).await; + // } } else { panic!("Failed to load data"); } diff --git a/mp_elements/src/elements/mod.rs b/mp_elements/src/elements/mod.rs index 6a8d304..d18f662 100644 --- a/mp_elements/src/elements/mod.rs +++ b/mp_elements/src/elements/mod.rs @@ -1,22 +1,33 @@ +use std::sync::Arc; pub mod ppi; -use crate::app::{BufferKey, Ctx, DataBufferPool}; +use crate::{ + app::{BufferKey, Ctx, DataBufferPool}, + App, +}; +use mp_core::config; pub use ppi::PPI; use std::any::Any; use wgpu::util::DeviceExt; macro_rules! elements { ($(($element_name:ident,$element: ty),)+) => { + #[derive(Clone)] pub enum Elements { - $($element_name($element),)+ + $($element_name(Arc<$element>),)+ } + #[derive(Clone, Copy)] pub enum ElementsRef<'a> { $($element_name(&'a $element),)+ } + pub enum ElementsMut<'a> { + $($element_name(&'a mut $element),)+ + } + $( - impl From<$element> for Elements { - fn from(element: $element) -> Self { + impl From> for Elements { + fn from(element: Arc<$element>) -> Self { Elements::$element_name(element) } } @@ -26,6 +37,21 @@ macro_rules! elements { ElementsRef::$element_name(element) } } + + impl<'a> From<&'a mut $element> for ElementsMut<'a> { + fn from(element: &'a mut $element) -> Self { + ElementsMut::$element_name(element) + } + } + + impl<'a> From<&'a mut $element> for ElementsRef<'a> { + fn from(element: &'a mut $element) -> Self { + ElementsRef::$element_name(element) + } + } + + + )+ impl Elements { @@ -57,6 +83,20 @@ macro_rules! elements { } + impl<'a> ElementsMut<'a> { + pub fn pipeline(&'a self) -> &wgpu::RenderPipeline { + match self { + $(ElementsMut::$element_name(element) => element.pipeline(),)+ + } + } + + pub fn draw(&'a self, attach: &ElementAttach, render_pass: &mut wgpu::RenderPass) { + match self { + $(ElementsMut::$element_name(element) => element.draw(attach, render_pass),)+ + } + } + } + }; } @@ -65,6 +105,8 @@ pub trait Element { type Uniform; type Data; + type Config; + fn new(ctx: &Ctx) -> Self; // fn new_attachment<'a>(&self, ctx: &Ctx) -> ElementAttach; @@ -84,7 +126,8 @@ pub trait Element { &self, ctx: &Ctx, data: &Self::Data, - buffer_pool: &mut DataBufferPool, + buffer_pool: &DataBufferPool, + config: &Self::Config, ) -> ElementAttach; } diff --git a/mp_elements/src/elements/ppi.rs b/mp_elements/src/elements/ppi.rs index e204df8..68e666e 100644 --- a/mp_elements/src/elements/ppi.rs +++ b/mp_elements/src/elements/ppi.rs @@ -1,6 +1,6 @@ -use crate::Shaders; -use rust_embed::RustEmbed; -use std::{ops::Sub, result, vec}; +use crate::tools::colormap::{ColorMap, ColormapParams}; +use log::*; +use std::{ops::Sub, vec}; use crate::{ app::{BufferKey, Ctx, DataBufferPool}, @@ -20,6 +20,12 @@ const RMAXNUM: u64 = 50; pub struct PPI { bind_group_layout: wgpu::BindGroupLayout, pipeline: wgpu::RenderPipeline, + colormap: Option, +} + +pub struct PPIConfig { + pub colormap: Vec<[f32; 4]>, + pub color_range: [f32; 2], } #[repr(C)] @@ -67,8 +73,14 @@ impl Element for PPI { type Uniform = PPIUniform; type Data = RadarGridData; + type Config = PPIConfig; + fn new(ctx: &Ctx) -> Self { let device = &ctx.device; + + // Tools + let colormap_layout = ColorMap::layout(device); + // Group Layout let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("PPI Bind Group Layout"), @@ -106,6 +118,8 @@ impl Element for PPI { bind_group_layouts: &[ // common_tools &ctx.common_tools.bind_group_layout, + // colormap + &colormap_layout, // ppi &bind_group_layout, ], @@ -155,6 +169,7 @@ impl Element for PPI { PPI { bind_group_layout: bind_group_layout, pipeline: render_pipeline, + colormap: None, } } @@ -188,12 +203,13 @@ impl Element for PPI { &self, ctx: &Ctx, data: &Self::Data, - buffer_pool: &mut DataBufferPool, + buffer_pool: &DataBufferPool, + config: &Self::Config, ) -> ElementAttach { + info!("Loading PPI data"); let (vertex, index) = self.bake(data); - println!("index: {:?}", &(index.as_ref()).unwrap()[0..24]); - println!("vertex: {:?}", &vertex[0..4]); let device = &ctx.device; + let queue = &ctx.queue; let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("PPI Uniform Buffer"), @@ -247,12 +263,22 @@ impl Element for PPI { label: Some("PPI Bind Group"), }); + // ColorMap Bind Group + let color_map = ColorMap::new( + device, + queue, + ColormapParams::new(config.colormap.clone(), config.color_range), + ); + let color_map_group = color_map.bind_group(device); + // self.colormap = Some(color_map); + ElementAttach { vertex_buffer: vertex_buffer, index_buffer: Some(index_buffer), num_indices, uniform_buffer: Some(uniform_buffer), - bind_group: vec![(1, bind_group)], + // bind group 0 is always common tools + bind_group: vec![(1, color_map_group), (2, bind_group)], data_buffer_key: Some(key), } } @@ -262,7 +288,7 @@ impl PPI { fn init_shader(device: &wgpu::Device) -> wgpu::ShaderModule { // let shader_str = merge_shader(r#"/Users/tsuki/projects/mp/mp_elements/shaders/ppi.wgsl"#); - let shader_str = get_shader("ppi_merged.wgsl").as_ref(); + let shader_str = get_shader("elements/ppi_merged.wgsl"); let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("PPI Shader Module"), diff --git a/mp_elements/src/elementvec.rs b/mp_elements/src/elementvec.rs index be75368..e13d68b 100644 --- a/mp_elements/src/elementvec.rs +++ b/mp_elements/src/elementvec.rs @@ -1,27 +1,29 @@ use crate::app::Ctx; use crate::elements::*; +use std::sync::Arc; macro_rules! elementvec { - ($({$element: ident, $element_ty: ty})+) => { + ($({$element: ident, $element_ty: ty, $element_mut: ident})+) => { pub struct ElementVec { - $($element: $element_ty,)+ + $($element: Arc<$element_ty> ,)+ } impl ElementVec { pub fn init(ctx: &Ctx) -> Self { // Compile the shaders, create the pipelines, etc. Self { - $($element: <$element_ty>::new(ctx),)+ + $($element: Arc::new((<$element_ty>::new(ctx))) ,)+ } } - $(pub fn $element(&self) -> &$element_ty { + $(pub fn $element(&self) -> &Arc< $element_ty> { &self.$element })+ + } }; () => {}; } elementvec!({ - ppi, PPI + ppi, PPI, ppi_mut }); diff --git a/mp_elements/src/lib.rs b/mp_elements/src/lib.rs index 8092bf7..c7a2c38 100644 --- a/mp_elements/src/lib.rs +++ b/mp_elements/src/lib.rs @@ -2,6 +2,7 @@ pub mod app; pub mod elements; pub mod elementvec; pub mod renderer; +pub mod tools; mod utils; pub use app::App; diff --git a/mp_elements/src/renderer/camera.rs b/mp_elements/src/renderer/camera.rs index 5f5a980..ef345f4 100644 --- a/mp_elements/src/renderer/camera.rs +++ b/mp_elements/src/renderer/camera.rs @@ -1,4 +1,5 @@ use glam::*; +#[derive(Debug, Clone)] pub struct Camera { pub position: Vec3, pub center: Vec3, diff --git a/mp_elements/src/renderer/projection.rs b/mp_elements/src/renderer/projection.rs index d09ece8..5dd0db7 100644 --- a/mp_elements/src/renderer/projection.rs +++ b/mp_elements/src/renderer/projection.rs @@ -1,4 +1,5 @@ use glam::*; +#[derive(Clone, Debug)] pub struct Projection { aspect: f32, fovy: f32, @@ -24,3 +25,14 @@ impl Projection { Mat4::perspective_rh(self.fovy, self.aspect, self.znear, self.zfar) } } + +impl Default for Projection { + fn default() -> Self { + Self { + aspect: 1.0, + fovy: 45.0, + znear: 0.1, + zfar: 100.0, + } + } +} diff --git a/mp_elements/src/tools/colormap.rs b/mp_elements/src/tools/colormap.rs new file mode 100644 index 0000000..7a1478c --- /dev/null +++ b/mp_elements/src/tools/colormap.rs @@ -0,0 +1,212 @@ +use wgpu::util::DeviceExt; + +pub struct ColorMap { + uniform: ColorMapUniform, + uniform_buffer: wgpu::Buffer, + unifrom_bind_group_layout: wgpu::BindGroupLayout, + + texture: wgpu::Texture, + texture_view: wgpu::TextureView, + sampler: wgpu::Sampler, +} + +pub struct ColormapParams { + pub colors: Vec<[f32; 4]>, + pub color_range: [f32; 2], +} + +impl ColormapParams { + pub fn from_u8(colors: Vec<[u8; 4]>, color_range: [f32; 2]) -> Self { + let colors = colors + .iter() + .map(|c| { + let c = [c[0] as f32, c[1] as f32, c[2] as f32, c[3] as f32]; + let c = [c[0] / 255.0, c[1] / 255.0, c[2] / 255.0, c[3] / 255.0]; + c + }) + .collect::>(); + ColormapParams { + colors, + color_range, + } + } + pub fn new(colors: Vec<[f32; 4]>, color_range: [f32; 2]) -> Self { + ColormapParams { + colors, + color_range, + } + } + fn as_texture_data(&self) -> Vec { + self.colors + .iter() + .flat_map(|color| bytemuck::bytes_of(color)) + .copied() + .collect() + } +} + +#[derive(Debug, Clone, Copy, encase::ShaderType)] +#[repr(C)] +pub struct ColorMapUniform { + color_count: u32, + value_min: f32, + value_max: f32, + invalid_value: f32, +} + +impl ColorMapUniform { + fn as_slice(&self) -> encase::internal::Result> { + let mut buffer = encase::UniformBuffer::new(Vec::new()); + buffer.write(self)?; + Ok(buffer.into_inner()) + } +} + +impl ColorMap { + pub fn new(device: &wgpu::Device, queue: &wgpu::Queue, params: ColormapParams) -> Self { + let uniform = ColorMapUniform { + color_count: params.colors.len() as u32, + value_min: params.color_range[0], + value_max: params.color_range[1], + invalid_value: 0.0, + }; + + let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("ColorMap Uniform Buffer"), + contents: uniform.as_slice().unwrap().as_slice(), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + + let texture = Self::create_color_texture(device, queue, ¶ms); + let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + let sampler = Self::create_sampler(device); + + let bind_group_layout = Self::layout(device); + + ColorMap { + uniform, + uniform_buffer: buffer, + unifrom_bind_group_layout: bind_group_layout, + sampler, + texture, + texture_view, + // uniform_bind_group: bind_group, + } + } + + pub fn layout(device: &wgpu::Device) -> wgpu::BindGroupLayout { + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("ColorMap Bind Group Layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::all(), + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D1, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::all(), + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + ], + }) + } + + pub fn bind_group(&self, device: &wgpu::Device) -> wgpu::BindGroup { + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("ColorMap Bind Group"), + layout: &self.unifrom_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&self.texture_view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&self.sampler), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: self.uniform_buffer.as_entire_binding(), + }, + ], + }); + + bind_group + } + + pub fn bind_group_layout(&self) -> &wgpu::BindGroupLayout { + &self.unifrom_bind_group_layout + } + + pub fn set_uniform(&mut self, f: F) + where + F: FnOnce(&mut ColorMapUniform), + { + f(&mut self.uniform); + } + + pub fn update(&mut self, queue: &wgpu::Queue) { + queue.write_buffer( + &self.uniform_buffer, + 0, + self.uniform.as_slice().unwrap().as_slice(), + ); + } + + fn create_color_texture( + device: &wgpu::Device, + queue: &wgpu::Queue, + params: &ColormapParams, + ) -> wgpu::Texture { + device.create_texture_with_data( + queue, + &wgpu::TextureDescriptor { + label: Some("ColorMap Texture"), + size: wgpu::Extent3d { + width: params.colors.len() as u32, + height: 1, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D1, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }, + wgpu::util::TextureDataOrder::LayerMajor, + ¶ms.as_texture_data(), + ) + } + + fn create_sampler(device: &wgpu::Device) -> wgpu::Sampler { + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + label: Some("ColorMap Sampler"), + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Nearest, + min_filter: wgpu::FilterMode::Nearest, + ..Default::default() + }); + + sampler + } +} diff --git a/mp/src/shaders/mod.rs b/mp_elements/src/tools/mod.rs similarity index 58% rename from mp/src/shaders/mod.rs rename to mp_elements/src/tools/mod.rs index 0092133..83c08c5 100644 --- a/mp/src/shaders/mod.rs +++ b/mp_elements/src/tools/mod.rs @@ -1,2 +1 @@ pub mod colormap; -pub mod ppi; diff --git a/mp_elements/src/utils.rs b/mp_elements/src/utils.rs index 9591969..c772381 100644 --- a/mp_elements/src/utils.rs +++ b/mp_elements/src/utils.rs @@ -2,8 +2,8 @@ use crate::Shaders; use std::borrow::Cow; use std::str; -pub fn get_shader(name: &str) -> Cow<'static, str> { +pub fn get_shader(name: &str) -> String { let file = Shaders::get(name).unwrap(); - let string = String::from_utf8_lossy(&file.data); + let string = String::from_utf8(file.data.to_vec()).unwrap(); string }