wgpu basics part 2

This commit is contained in:
core 2023-11-07 14:15:47 -05:00
parent 6d003d2c98
commit cbcaab8a10
Signed by: core
GPG Key ID: FDBF740DADDCEECF
5 changed files with 164 additions and 15 deletions

View File

@ -22,7 +22,10 @@ wasm-logger = "0.2"
serde-wasm-bindgen = "0.6"
chrono = "0.4"
itertools = "0.11"
winit = "0.29"
winit = { version = "0.29", features = ["rwh_05"], default-features = false }
wgpu = { version = "0.18", features = ["webgl"] }
raw-window-handle = "0.5"
wasm-bindgen-futures = "0.4"
[dependencies.js-sys]
version = "0.3"

View File

@ -10,9 +10,10 @@ pub mod colors;
use std::io::Cursor;
use std::ops::{Deref, DerefMut};
use js_sys::Uint8Array;
use log::{debug, info};
use log::{debug, error, info};
use wasm_bindgen::prelude::*;
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
use wgpu::SurfaceError;
use winit::dpi::PhysicalSize;
use winit::event::{ElementState, Event, WindowEvent};
use winit::event::WindowEvent::KeyboardInput;
@ -23,7 +24,8 @@ use winit::platform::web::{WindowExtWebSys, EventLoopExtWebSys};
use crate::command::{exec, should_newline};
use crate::mode::Mode;
use crate::mode::Mode::Reflectivity;
use crate::scope::{Preferences, RenderState, ScopeState};
use crate::scope::{Preferences, ScopeState, WgpuState};
#[wasm_bindgen]
extern "C" {
@ -35,7 +37,7 @@ extern "C" {
pub struct AbiScopeState(ScopeState);
#[wasm_bindgen]
pub fn __nxrd_browser_init() -> AbiScopeState {
pub async fn __nxrd_browser_init(w: u32, h: u32) -> AbiScopeState {
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
utils::set_panic_hook();
@ -44,7 +46,8 @@ pub fn __nxrd_browser_init() -> AbiScopeState {
let event_loop = EventLoop::new().expect("event loop creation failed");
let window = WindowBuilder::new().build(&event_loop).unwrap();
window.set_min_inner_size(Some(PhysicalSize::new(450, 400)));
window.set_min_inner_size(Some(PhysicalSize::new(w, h)));
web_sys::window()
.and_then(|win| win.document())
@ -57,6 +60,9 @@ pub fn __nxrd_browser_init() -> AbiScopeState {
})
.expect("Couldn't append canvas to document body.");
let mut render_state = WgpuState::new(window, PhysicalSize::new(w, h)).await;
// If you see an error here, your IDE is not compiling for webassembly
event_loop.spawn(move |event: Event<_>, window: &EventLoopWindowTarget<_>| {
match event {
@ -64,14 +70,29 @@ pub fn __nxrd_browser_init() -> AbiScopeState {
match event {
KeyboardInput { event, .. } => {
debug!("{:?}", event.physical_key);
if event.physical_key == KeyCode::Escape {
window.exit();
}
},
WindowEvent::CloseRequested => { window.exit(); },
WindowEvent::Resized(physical_size) => {
render_state.reconfigure(*physical_size);
},
WindowEvent::RedrawRequested => {
render_state.update();
match render_state.render() {
Ok(_) => {},
Err(SurfaceError::Lost) => render_state.reconfigure(render_state.size),
Err(SurfaceError::OutOfMemory) => {
error!("out of memory!");
window.exit();
},
Err(e) => error!("transient rendering error: {}", e),
}
},
_ => {}
}
},
Event::AboutToWait => {
render_state.window().request_redraw();
},
_ => {}
}
});

View File

@ -1,6 +1,13 @@
use std::iter;
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, HtmlInputElement};
use wgpu::{Backends, Color, CommandEncoderDescriptor, Device, Features, Instance, InstanceDescriptor, Limits, LoadOp, Operations, Queue, RenderPassColorAttachment, RenderPassDescriptor, StoreOp, Surface, SurfaceConfiguration, SurfaceError, TextureUsages, TextureViewDescriptor};
use winit::dpi::PhysicalSize;
use winit::event::WindowEvent;
use raw_window_handle::HasRawDisplayHandle;
use winit::window::Window;
use nexrad2::Nexrad2Chunk;
use crate::mode::Mode;
use wgpu::DeviceDescriptor;
pub struct ScopeState {
pub ar2: Option<Nexrad2Chunk>,
@ -16,11 +23,132 @@ pub struct ScopeState {
pub show_ui: bool
}
pub struct RenderState {
pub canvas: HtmlCanvasElement,
pub context: CanvasRenderingContext2d
pub struct WgpuState {
pub surface: Surface,
pub device: Device,
pub queue: Queue,
pub surface_config: SurfaceConfiguration,
pub size: PhysicalSize<u32>,
pub window: Window
}
impl WgpuState {
pub async fn new(window: Window, size: PhysicalSize<u32>) -> Self {
let instance = Instance::new(InstanceDescriptor {
backends: Backends::all(),
flags: Default::default(),
dx12_shader_compiler: Default::default(),
gles_minor_version: Default::default(),
});
let surface = unsafe {
instance.create_surface(&window).unwrap()
};
let adapter = instance.request_adapter(
&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
compatible_surface: Some(&surface),
force_fallback_adapter: false,
},
).await.unwrap();
let (device, queue) = adapter.request_device(
&DeviceDescriptor {
features: Features::empty(),
limits: Limits::downlevel_webgl2_defaults(),
label: None,
},
None, // Trace path
).await.unwrap();
let surface_capabilities = surface.get_capabilities(&adapter);
let surface_format = surface_capabilities.formats.iter()
.copied()
.find(|f| f.is_srgb())
.unwrap_or(surface_capabilities.formats[0]);
let surface_config = SurfaceConfiguration {
usage: TextureUsages::RENDER_ATTACHMENT,
format: surface_format,
width: size.width,
height: size.height,
present_mode: surface_capabilities.present_modes[0],
alpha_mode: surface_capabilities.alpha_modes[0],
view_formats: vec![],
};
surface.configure(&device, &surface_config);
Self {
window,
surface,
device,
queue,
surface_config,
size
}
}
pub fn window(&self) -> &Window {
&self.window
}
pub fn reconfigure(&mut self, new_size: PhysicalSize<u32>) {
if new_size.width > 0 && new_size.height > 0 {
self.size = new_size;
self.surface_config.width = new_size.width;
self.surface_config.height = new_size.height;
self.surface.configure(&self.device, &self.surface_config);
}
}
pub fn input(&mut self, event: &WindowEvent) -> bool {
false
}
pub fn update(&mut self) {
}
pub fn render(&mut self) -> Result<(), SurfaceError> {
let output = self.surface.get_current_texture()?;
let view = output.texture.create_view(&TextureViewDescriptor::default());
let mut encoder = self.device.create_command_encoder(&CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
// LOCK BLOCK
{
let render_pass = encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
}),
store: StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
}
self.queue.submit(iter::once(encoder.finish()));
output.present();
Ok(())
}
}
pub struct Preferences {
pub fcs: usize
}

View File

@ -14,9 +14,6 @@
}
canvas {
width: 100%;
height: 100%;
image-rendering: pixelated;
z-index: 10000;
}

View File

@ -1,6 +1,6 @@
import * as wasm from "./wasm/nexrad_browser.js";
await wasm.default();
let global_context = wasm.__nxrd_browser_init();
let global_context = wasm.__nxrd_browser_init(window.innerWidth, window.innerHeight);
/*
function rescaleCanvas(canvas, ctx) {
var dpr = window.devicePixelRatio || 1;