499 lines
15 KiB
Rust
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");
|
|
}
|
|
})
|
|
}
|
|
}
|