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 wgpu::{Backends, Instance}; type DB = std::sync::Arc; const BACKENDS_DEFAULT: u32 = Backends::DX12.bits() | Backends::METAL.bits() | Backends::GL.bits() | Backends::BROWSER_WEBGPU.bits(); pub struct App { pub(crate) pipelines: Option, ctx: Ctx, buffer_pool: DataBufferPool, } impl App { pub async fn instant() -> Self { // 修复方法名称拼写错误 // Instance is a handle to the backend. It is used to create adapters. let instance = Instance::new(wgpu::InstanceDescriptor { backends: Backends::from_bits(BACKENDS_DEFAULT).unwrap(), ..Default::default() }); // Request an adapter, which is a handle to a physical device. let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::default(), compatible_surface: None, force_fallback_adapter: false, }) .await .unwrap(); // Request a device and a queue from the adapter. The device is the handle to the GPU, and the queue is used to submit commands to the GPU. let (device, queue) = adapter .request_device( &wgpu::DeviceDescriptor { required_features: wgpu::Features::POLYGON_MODE_LINE, ..Default::default() }, None, ) .await .unwrap(); // 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); // Buffer pool let buffer_pool = DataBufferPool::new(); let ctx = Ctx::new(device, queue, common_utils); Self { pipelines: None, buffer_pool, 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 } // Initialize the app. This method creates the pipelines and initializes the elements. pub async fn init(&mut self) { self.pipelines = Some(ElementVec::init(&self.ctx)); } // Get a reference to the pipelines. pub fn pipelines(&self) -> &ElementVec { self.pipelines.as_ref().unwrap() } pub fn common_utils_mut(&mut self) -> &mut CommonUtils { &mut self.ctx.common_tools } // Draw the elements in the draw list. pub async fn draw(&self, window: &RenderWindow, draw_list: DrawList) { let mut encoder = self .ctx .device .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("draw_command_encoder"), }); { let render_pass_desc = wgpu::RenderPassDescriptor { label: Some("Some Render Pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &window.texture_view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), store: wgpu::StoreOp::Store, }, })], ..Default::default() }; let mut render_pass = encoder.begin_render_pass(&render_pass_desc); // Set the common utils bind group. let common_utils = &self.ctx.common_tools.bind_group; // Draw each element in the draw list. for (attach, element) in draw_list.elements { let pipeline = element.pipeline(); // Set the pipeline for the render pass. render_pass.set_pipeline(pipeline); // Set the common utils bind group for the render pass. render_pass.set_bind_group(0, common_utils, &[]); // Set the bind group for the render pass. for (index, bind_group) in attach.bind_group.iter() { render_pass.set_bind_group(*index, bind_group, &[]); } // Set the bind group for the render pass. attach.bind(&mut render_pass); // Draw the element. element.draw(&attach, &mut render_pass); } } // Output window.finish(&mut encoder); self.ctx.queue.submit(Some(encoder.finish())); } 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 = &self.buffer_pool; let ctx = &self.ctx; 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, }, ); } } pub struct Ctx { pub device: wgpu::Device, pub queue: wgpu::Queue, pub common_tools: CommonUtils, } impl Ctx { pub fn bind_group_layout(&self) -> Vec { vec![] } fn new(device: wgpu::Device, queue: wgpu::Queue, common: CommonUtils) -> Self { Self { device, queue, common_tools: common, } } } #[derive(Clone)] pub struct DrawList { pub elements: Vec<(std::sync::Arc, Elements)>, // 修复字段访问权限 } impl DrawList { pub fn new() -> Self { Self { elements: vec![] } } pub fn push>( &mut self, element: Ele, attach: std::sync::Arc, ) { self.elements.push((attach, element.into())); } } // #[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable, Default)] // #[repr(C)] #[derive(Debug, Clone, Copy, encase::ShaderType, Default)] pub struct CommonUniform { pub model_matrix: glam::Mat4, pub view_matrix: glam::Mat4, pub proj_matrix: glam::Mat4, pub camera_position: glam::Vec3, pub camera_front: glam::Vec3, pub camera_up: glam::Vec3, pub light_position: glam::Vec3, pub light_color: glam::Vec3, pub light_intensity: f32, } impl CommonUniform { fn as_slice(&self) -> encase::internal::Result> { let mut buffer = encase::UniformBuffer::new(Vec::new()); buffer.write(self)?; Ok(buffer.into_inner()) } } pub struct CommonUtils { pub bind_group_layout: wgpu::BindGroupLayout, pub bind_group: wgpu::BindGroup, pub uniform_buffer: wgpu::Buffer, } impl CommonUtils { fn new(device: &wgpu::Device) -> Self { let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("common_utils_bind_group_layout"), entries: &[wgpu::BindGroupLayoutEntry { binding: 0, visibility: wgpu::ShaderStages::all(), ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: None, }, count: None, }], }); let common_uniform = CommonUniform::default(); let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("common_utils_buffer"), contents: &common_uniform.as_slice().unwrap(), usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::UNIFORM, }); let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("common_utils_bind_group"), layout: &bind_group_layout, entries: &[wgpu::BindGroupEntry { binding: 0, resource: buffer.as_entire_binding(), }], }); Self { bind_group_layout, bind_group, uniform_buffer: buffer, } } } pub struct DataBufferPool { // buffers: HashMap, buffers: Cache, } impl DataBufferPool { pub fn new() -> Self { Self { buffers: Cache::new(10), } } pub fn get_or_create_buffer(&self, key: BufferKey, f: F) -> DB where F: FnOnce() -> wgpu::Buffer, { let buffer = self .buffers .get_or_insert_with(&key, || { Ok::, ()>(std::sync::Arc::new(f())) }) .unwrap(); buffer } } #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct BufferKey { pub id: u64, pub from: String, } impl BufferKey { pub fn new(from: String) -> Self { Self { id: 0, from } } } pub struct Output { pub output_buffer: wgpu::Buffer, } impl Output { pub fn new(device: &wgpu::Device) -> Self { let u32_size = std::mem::size_of::() as u32; let output_buffer_size = (u32_size * 256 * 256) as wgpu::BufferAddress; let output_buffer_desc = wgpu::BufferDescriptor { size: output_buffer_size, usage: wgpu::BufferUsages::COPY_DST // MAP_READ 告诉 wpgu 我们要在 cpu 端读取此缓冲区 | wgpu::BufferUsages::MAP_READ, label: None, mapped_at_creation: false, }; let output_buffer = device.create_buffer(&output_buffer_desc); Self { output_buffer } } pub async fn get_data(&self, ctx: &Ctx) { let device = &ctx.device; // 需要对映射变量设置范围,以便我们能够解除缓冲区的映射 let buffer_slice = self.output_buffer.slice(..); // 注意:我们必须在 await future 之前先创建映射,然后再调用 device.poll()。 // 否则,应用程序将停止响应。 let (tx, rx) = flume::bounded(1); buffer_slice.map_async(wgpu::MapMode::Read, move |result| { tx.send(result).unwrap(); }); device.poll(wgpu::Maintain::Wait).panic_on_timeout(); if let Ok(Ok(())) = rx.recv_async().await { let data = buffer_slice.get_mapped_range(); use image::{ImageBuffer, Rgba}; let buffer = ImageBuffer::, _>::from_raw(256, 256, data).unwrap(); buffer.save("image.png").unwrap(); println!("保存图片成功!"); // 解除缓冲区映射 self.output_buffer.unmap(); } else { panic!("从 gpu 读取数据失败!"); } } } mod test { use mp_core::{PluginManager, RadarGridData}; use wgpu::core::pipeline; 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"#, ) .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"#, ); pollster::block_on(async { let mut app = App::instant().await; app.init().await; let ctx = &app.ctx; let buffer_pool = &mut app.buffer_pool; if let Ok(data) = data { let first_block = data.first().unwrap(); // Convert the first block into a PPI struct. // if let Ok(data) = first_block.try_into() { // // let attachment = { // // let pipelines = app.pipelines.as_mut().unwrap(); // // let ppi = pipelines.ppi(); // // // 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 pipeline = app.pipelines(); // let ppi = pipeline.ppi(); // // 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"); } }) } }