Initial commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "wgpu-template"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.100"
|
||||
bytemuck = "1.24.0"
|
||||
cgmath = "0.18.0"
|
||||
crevice = {version = "0.18.0", features = ["cgmath"]}
|
||||
egui = "0.33.3"
|
||||
egui-wgpu = "0.33.3"
|
||||
egui-winit = "0.33.3"
|
||||
env_logger = "0.11.8"
|
||||
pollster = "0.4.0"
|
||||
wgpu = "27.0.1"
|
||||
winit = "0.30.12"
|
||||
137
src/egui_renderer.rs
Normal file
137
src/egui_renderer.rs
Normal file
@ -0,0 +1,137 @@
|
||||
use egui::{Context, PaintCallbackInfo};
|
||||
use egui_wgpu::{CallbackResources, CallbackTrait, Renderer, RendererOptions, ScreenDescriptor};
|
||||
use egui_winit::State;
|
||||
use wgpu::{CommandEncoder, Device, Queue, RenderPass, TextureFormat, TextureView};
|
||||
use winit::{event::WindowEvent, window::Window};
|
||||
|
||||
pub struct EguiState {
|
||||
pub state: State,
|
||||
pub renderer: Renderer,
|
||||
pub frame_started: bool,
|
||||
//pub msaa_texture: TextureView,
|
||||
pub color_format: TextureFormat,
|
||||
}
|
||||
|
||||
impl EguiState {
|
||||
pub fn context(&self) -> &Context {
|
||||
self.state.egui_ctx()
|
||||
}
|
||||
|
||||
pub fn new(device: &Device, output_color_format: TextureFormat, window: &Window) -> EguiState {
|
||||
let egui_context = Context::default();
|
||||
|
||||
let egui_state = egui_winit::State::new(
|
||||
egui_context,
|
||||
egui::ViewportId::ROOT,
|
||||
&window,
|
||||
Some(window.scale_factor() as f32),
|
||||
None,
|
||||
Some(2 * 1024),
|
||||
);
|
||||
|
||||
let options = RendererOptions {
|
||||
//msaa_samples: SAMPLE_COUNT,
|
||||
//depth_stencil_format: Some(TextureFormat::Depth24PlusStencil8),
|
||||
..Default::default()
|
||||
};
|
||||
let egui_renderer = Renderer::new(device, output_color_format, options);
|
||||
|
||||
EguiState {
|
||||
state: egui_state,
|
||||
renderer: egui_renderer,
|
||||
frame_started: false,
|
||||
color_format: output_color_format,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, _device: &Device, _width: u32, _height: u32) {}
|
||||
|
||||
pub fn handle_event(&mut self, window: &Window, event: &WindowEvent) {
|
||||
let _ = self.state.on_window_event(window, event);
|
||||
}
|
||||
|
||||
pub fn begin_frame(&mut self, window: &Window) {
|
||||
let input = self.state.take_egui_input(window);
|
||||
self.state.egui_ctx().begin_pass(input);
|
||||
self.frame_started = true;
|
||||
}
|
||||
|
||||
pub fn end_frame_and_draw(
|
||||
&mut self,
|
||||
device: &Device,
|
||||
queue: &Queue,
|
||||
encoder: &mut CommandEncoder,
|
||||
window: &Window,
|
||||
window_surface_view: &TextureView,
|
||||
screen_descriptor: ScreenDescriptor,
|
||||
) {
|
||||
if !self.frame_started {
|
||||
panic!("begin_frame must be called before end_frame_and_draw can be called!");
|
||||
}
|
||||
|
||||
//self.ppp(screen_descriptor.pixels_per_point);
|
||||
|
||||
let full_output = self.state.egui_ctx().end_pass();
|
||||
|
||||
self.state
|
||||
.handle_platform_output(window, full_output.platform_output);
|
||||
|
||||
let tris = self
|
||||
.state
|
||||
.egui_ctx()
|
||||
.tessellate(full_output.shapes, self.state.egui_ctx().pixels_per_point());
|
||||
for (id, image_delta) in &full_output.textures_delta.set {
|
||||
self.renderer
|
||||
.update_texture(device, queue, *id, image_delta);
|
||||
}
|
||||
self.renderer
|
||||
.update_buffers(device, queue, encoder, &tris, &screen_descriptor);
|
||||
let rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: window_surface_view,
|
||||
resolve_target: None,
|
||||
ops: egui_wgpu::wgpu::Operations {
|
||||
load: egui_wgpu::wgpu::LoadOp::Load,
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
depth_slice: None,
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
label: Some("egui main render pass"),
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
|
||||
self.renderer
|
||||
.render(&mut rpass.forget_lifetime(), &tris, &screen_descriptor);
|
||||
for x in &full_output.textures_delta.free {
|
||||
self.renderer.free_texture(x)
|
||||
}
|
||||
|
||||
self.frame_started = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CallbackFn<P>
|
||||
where
|
||||
P: Fn(PaintCallbackInfo, &mut RenderPass<'static>, &CallbackResources),
|
||||
{
|
||||
//pub : FnOnce(&Device, &Queue, &ScreenDescriptor, &mut CommandEncoder, &mut CallbackResources) -> Vec<>
|
||||
pub paint_fn: P,
|
||||
}
|
||||
|
||||
impl<P> CallbackTrait for CallbackFn<P>
|
||||
where
|
||||
P: Fn(PaintCallbackInfo, &mut RenderPass<'static>, &CallbackResources)
|
||||
+ std::marker::Sync
|
||||
+ std::marker::Send,
|
||||
{
|
||||
fn paint(
|
||||
&self,
|
||||
info: egui::PaintCallbackInfo,
|
||||
render_pass: &mut wgpu::RenderPass<'static>,
|
||||
callback_resources: &egui_wgpu::CallbackResources,
|
||||
) {
|
||||
(self.paint_fn)(info, render_pass, callback_resources)
|
||||
}
|
||||
}
|
||||
74
src/lib.rs
Normal file
74
src/lib.rs
Normal file
@ -0,0 +1,74 @@
|
||||
pub mod egui_renderer;
|
||||
pub mod state;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use winit::{
|
||||
application::ApplicationHandler,
|
||||
event::WindowEvent,
|
||||
event_loop::{self, EventLoop},
|
||||
window::Window,
|
||||
};
|
||||
|
||||
use crate::state::State;
|
||||
|
||||
pub fn run() -> anyhow::Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let event_loop = EventLoop::with_user_event().build()?;
|
||||
let mut app = App::default();
|
||||
event_loop.run_app(&mut app)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// App struct
|
||||
#[derive(Default)]
|
||||
pub struct App {
|
||||
state: Option<State>,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for App {
|
||||
fn resumed(&mut self, event_loop: &event_loop::ActiveEventLoop) {
|
||||
// Create window
|
||||
let window = Arc::new(
|
||||
event_loop
|
||||
.create_window(
|
||||
Window::default_attributes()
|
||||
.with_title("Wgpu Template")
|
||||
.with_resizable(true),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let state = pollster::block_on(State::new(window.clone()));
|
||||
self.state = Some(state);
|
||||
|
||||
window.request_redraw();
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &event_loop::ActiveEventLoop,
|
||||
_window_id: winit::window::WindowId,
|
||||
event: winit::event::WindowEvent,
|
||||
) {
|
||||
let state = self.state.as_mut().unwrap();
|
||||
state.handle_event(&event);
|
||||
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
event_loop.exit();
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
state.render();
|
||||
state.get_window().request_redraw();
|
||||
}
|
||||
|
||||
WindowEvent::Resized(size) => {
|
||||
state.resize(size);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
src/main.rs
Normal file
3
src/main.rs
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() -> anyhow::Result<()> {
|
||||
wgpu_template::run()
|
||||
}
|
||||
169
src/state.rs
Normal file
169
src/state.rs
Normal file
@ -0,0 +1,169 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use cgmath::{Matrix4, Vector3};
|
||||
use crevice::std430::AsStd430;
|
||||
use egui_wgpu::ScreenDescriptor;
|
||||
use wgpu::{Features, FeaturesWGPU, FeaturesWebGPU};
|
||||
use winit::{event::WindowEvent, window::Window};
|
||||
|
||||
use crate::egui_renderer::EguiState;
|
||||
|
||||
pub struct State {
|
||||
window: Arc<Window>,
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
size: winit::dpi::PhysicalSize<u32>,
|
||||
surface: wgpu::Surface<'static>,
|
||||
surface_format: wgpu::TextureFormat,
|
||||
egui_state: EguiState,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub async fn new(window: Arc<Window>) -> State {
|
||||
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions::default())
|
||||
.await
|
||||
.unwrap();
|
||||
let (device, queue) = adapter
|
||||
.request_device(&wgpu::DeviceDescriptor {
|
||||
required_features: Features {
|
||||
features_wgpu: FeaturesWGPU::PUSH_CONSTANTS,
|
||||
features_webgpu: FeaturesWebGPU::empty(),
|
||||
},
|
||||
required_limits: wgpu::Limits {
|
||||
max_push_constant_size: RayMarchingPushConstants::std430_size_static() as u32,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let size = window.inner_size();
|
||||
|
||||
let surface = instance.create_surface(window.clone()).unwrap();
|
||||
let cap = surface.get_capabilities(&adapter);
|
||||
let surface_format = cap.formats[0];
|
||||
|
||||
let state = State {
|
||||
egui_state: EguiState::new(&device, surface_format, &window),
|
||||
|
||||
window,
|
||||
device,
|
||||
queue,
|
||||
size,
|
||||
surface,
|
||||
surface_format,
|
||||
};
|
||||
|
||||
// Configure surface for the first time
|
||||
state.configure_surface();
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
pub fn get_window(&self) -> &Window {
|
||||
&self.window
|
||||
}
|
||||
|
||||
pub fn configure_surface(&self) {
|
||||
let surface_config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: self.surface_format,
|
||||
// Request compatibility with the sRGB-format texture view we‘re going to create later.
|
||||
view_formats: vec![self.surface_format.add_srgb_suffix()],
|
||||
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||
width: self.size.width,
|
||||
height: self.size.height,
|
||||
desired_maximum_frame_latency: 2,
|
||||
present_mode: wgpu::PresentMode::AutoVsync,
|
||||
};
|
||||
self.surface.configure(&self.device, &surface_config);
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
|
||||
self.size = new_size;
|
||||
|
||||
// reconfigure the surface
|
||||
self.configure_surface();
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, event: &WindowEvent) {
|
||||
self.egui_state.handle_event(&self.window, event);
|
||||
}
|
||||
|
||||
pub fn render(&mut self) {
|
||||
// Create texture view
|
||||
let surface_texture = self
|
||||
.surface
|
||||
.get_current_texture()
|
||||
.expect("failed to acquire next swapchain texture");
|
||||
let texture_view = surface_texture
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor {
|
||||
// Without add_srgb_suffix() the image we will be working with
|
||||
// might not be "gamma correct".
|
||||
format: Some(self.surface_format.add_srgb_suffix()),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// Renders a GREEN screen
|
||||
let mut encoder = self.device.create_command_encoder(&Default::default());
|
||||
// Create the renderpass which will clear the screen.
|
||||
let renderpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: None,
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &texture_view,
|
||||
depth_slice: None,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
|
||||
// End the renderpass.
|
||||
drop(renderpass);
|
||||
// Egui
|
||||
{
|
||||
let screen_descriptor = ScreenDescriptor {
|
||||
size_in_pixels: [self.size.width, self.size.height],
|
||||
pixels_per_point: 1.,
|
||||
};
|
||||
self.egui_state.begin_frame(&self.window);
|
||||
|
||||
egui::Window::new("Hello Window").resizable(true).show(
|
||||
self.egui_state.context(),
|
||||
|ui| {
|
||||
ui.label("Hello, world.");
|
||||
},
|
||||
);
|
||||
|
||||
self.egui_state.end_frame_and_draw(
|
||||
&self.device,
|
||||
&self.queue,
|
||||
&mut encoder,
|
||||
&self.window,
|
||||
&texture_view,
|
||||
screen_descriptor,
|
||||
);
|
||||
}
|
||||
|
||||
// Submit the command in the queue to execute
|
||||
self.queue.submit([encoder.finish()]);
|
||||
self.window.pre_present_notify();
|
||||
surface_texture.present();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(AsStd430)]
|
||||
pub struct RayMarchingPushConstants {
|
||||
inverse_projection_matrix: Matrix4<f32>,
|
||||
view_matrix: Matrix4<f32>,
|
||||
camera_pos: Vector3<f32>,
|
||||
}
|
||||
Reference in New Issue
Block a user