radarmp/mp_elements/src/app.rs
2024-11-19 02:24:47 +08:00

499 lines
15 KiB
Rust

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<wgpu::Buffer>;
const BACKENDS_DEFAULT: u32 = Backends::DX12.bits()
| Backends::METAL.bits()
| Backends::GL.bits()
| Backends::BROWSER_WEBGPU.bits();
pub struct App {
pub(crate) pipelines: Option<ElementVec>,
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<wgpu::BindGroupLayout> {
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<ElementAttach>, Elements)>, // 修复字段访问权限
}
impl DrawList {
pub fn new() -> Self {
Self { elements: vec![] }
}
pub fn push<Ele: Into<Elements>>(
&mut self,
element: Ele,
attach: std::sync::Arc<ElementAttach>,
) {
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<Vec<u8>> {
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<BufferKey, wgpu::Buffer>,
buffers: Cache<BufferKey, DB>,
}
impl DataBufferPool {
pub fn new() -> Self {
Self {
buffers: Cache::new(10),
}
}
pub fn get_or_create_buffer<F>(&self, key: BufferKey, f: F) -> DB
where
F: FnOnce() -> wgpu::Buffer,
{
let buffer = self
.buffers
.get_or_insert_with(&key, || {
Ok::<std::sync::Arc<wgpu::Buffer>, ()>(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::<u32>() 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::<Rgba<u8>, _>::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");
}
})
}
}