Created
April 14, 2022 21:37
-
-
Save cdecompilador/bf9e8b2969d7641ad101b66208c6c0ae to your computer and use it in GitHub Desktop.
wgpu 1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use winit::{ | |
event::*, | |
event_loop::{EventLoop, ControlFlow}, | |
window::{Window, WindowBuilder}, | |
dpi::PhysicalSize | |
}; | |
/// The state if a wgpu rendering context for a certain window | |
struct State { | |
/// A surface is a target where to draw | |
surface: wgpu::Surface, | |
/// The details on how to draw to a `self.surface`, contains things like | |
/// the format, the size, and when the changes are propagated from the | |
/// gpu to the render target (PresentMode) | |
config: wgpu::SurfaceConfiguration, | |
/// A connection to a logical rendering device, can interact with resources | |
/// on the gpu like buffer, textures, setup the pipeline (shader modules, | |
/// samplers, bind groups) and create an encoder that translates each | |
/// draw command to something each associated physical device can | |
/// understand | |
device: wgpu::Device, | |
/// Groups the translated commands to a single one and send them to the | |
/// physical device/s | |
queue: wgpu::Queue, | |
/// The size of the surface, this is the size of the window also (but its | |
/// not mandatory) | |
size: PhysicalSize<u32> | |
} | |
impl State { | |
async fn new(window: &Window) -> Self { | |
// Initialize the backend of wgpu | |
let instance = wgpu::Instance::new(wgpu::Backends::VULKAN); | |
// Create the surface from the window thanks to the RawWindowHandle | |
// trait | |
let surface = unsafe { instance.create_surface(window) }; | |
// Retrieve a high performant physical device | |
let adapter = instance.request_adapter( | |
&wgpu::RequestAdapterOptions { | |
power_preference: wgpu::PowerPreference::default(), | |
compatible_surface: Some(&surface), | |
// We don't want software rendering fallback | |
force_fallback_adapter: false, | |
} | |
).await.unwrap(); | |
// Retrieve the device and queue (logical abstractions) linked to the | |
// physical device reference by `adapter` | |
let (device, queue) = adapter.request_device( | |
&wgpu::DeviceDescriptor { | |
features: wgpu::Features::empty(), | |
limits: wgpu::Limits::default(), | |
label: None | |
}, | |
None | |
).await.unwrap(); | |
// Configure the surface, the device is used because the surface is | |
// stored in some physical device/s | |
let size = window.inner_size(); | |
let config = wgpu::SurfaceConfiguration { | |
usage: wgpu::TextureUsages::RENDER_ATTACHMENT, | |
format: surface.get_preferred_format(&adapter).unwrap(), | |
width: size.width, | |
height: size.height, | |
present_mode: wgpu::PresentMode::Fifo | |
}; | |
surface.configure(&device, &config); | |
Self { | |
surface, | |
config, | |
device, | |
queue, | |
size | |
} | |
} | |
/// Handle resizing | |
fn resize(&mut self, new_size: PhysicalSize<u32>) { | |
if new_size.width > 0 && new_size.height > 0 { | |
self.size = new_size; | |
self.config.width = new_size.width; | |
self.config.height = new_size.height; | |
self.surface.configure(&self.device, &self.config); | |
} | |
} | |
fn render(&self) -> Result<(), wgpu::SurfaceError> { | |
// Get the texture of the current frame (the backbuffer) and its | |
// metadata (size, format, etc...) needed for the `Pipeline` and to | |
// setup a `BindGroup` | |
let output = self.surface.get_current_texture()?; | |
let view = | |
output.texture.create_view(&wgpu::TextureViewDescriptor::default()); | |
// Create a command encoder from the device, the avaible commands are | |
// render passes, gpu buffer operations and compute passes | |
let mut encoder = self.device.create_command_encoder( | |
&wgpu::CommandEncoderDescriptor { | |
label: None | |
} | |
); | |
// Start a render pass, the render pass can set a current pipeline, | |
// interact with shaders, draw meshes, and change the viewport | |
let render_pass = encoder.begin_render_pass( | |
&wgpu::RenderPassDescriptor { | |
label: None, | |
color_attachments: &[ | |
wgpu::RenderPassColorAttachment { | |
view: &view, | |
resolve_target: None, | |
ops: wgpu::Operations { | |
load: wgpu::LoadOp::Clear(wgpu::Color { | |
r: 0.3, | |
g: 0.2, | |
b: 0.1, | |
a: 1.0 | |
}), | |
store: true | |
} | |
} | |
], | |
depth_stencil_attachment: None | |
} | |
); | |
drop(render_pass); | |
// Merge all the commands on the encoder to one and send them to the | |
// gpu | |
self.queue.submit(std::iter::once(encoder.finish())); | |
// Swap the texture with one the render target will present to the user | |
output.present(); | |
Ok(()) | |
} | |
} | |
fn main() -> Result<(), Box<dyn std::error::Error>> { | |
// Create the event loop and link it to a new widow | |
let event_loop = EventLoop::new(); | |
let window = WindowBuilder::new() | |
.build(&event_loop)?; | |
// Create wgpu rendering context | |
let mut state = pollster::block_on(State::new(&window)); | |
// Setup the event loop | |
event_loop.run(move |event, _, control_flow| { | |
*control_flow = ControlFlow::Poll; | |
match event { | |
Event::WindowEvent { | |
ref event, | |
window_id | |
} if window_id == window.id() => match event { | |
// Setup exit | |
WindowEvent::CloseRequested | | |
WindowEvent::KeyboardInput { | |
input: KeyboardInput { | |
state: ElementState::Pressed, | |
virtual_keycode: Some(VirtualKeyCode::Escape), | |
.. | |
}, | |
.. | |
} => | |
*control_flow = ControlFlow::Exit, | |
// Handle resizing | |
WindowEvent::Resized(new_size) => | |
state.resize(*new_size), | |
_ => {} | |
}, | |
// Handle redraw | |
Event::RedrawRequested(window_id) if window_id == window.id() => { | |
match state.render() { | |
Ok(()) => {}, | |
Err(wgpu::SurfaceError::Outdated) => | |
state.resize(state.size), | |
Err(e) => panic!("Render Error {:?}", e) | |
}; | |
} | |
Event::MainEventsCleared => | |
window.request_redraw(), | |
_ => {} | |
} | |
}); | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment