use std::sync::Arc;
use anyhow::Result;
use bevy_ecs::prelude::Resource;
use wgpu::{Device, Queue, Surface, SurfaceConfiguration};
use winit::window::Window;
#[derive(Resource)]
pub struct Renderer {
surface: Surface<'static>,
device: Device,
queue: Queue,
config: SurfaceConfiguration,
size: (u32, u32),
}
impl Renderer {
fn new(window: Arc<Window>) -> Result<Self> {
let size = window.inner_size();
let width = size.width.max(1);
let height = size.height.max(1);
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
flags: wgpu::InstanceFlags::empty(),
..Default::default()
});
let surface = instance.create_surface(window)?;
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
compatible_surface: Some(&surface),
force_fallback_adapter: false,
}))?;
let (device, queue) = pollster::block_on(adapter.request_device(
&wgpu::DeviceDescriptor {
label: Some("Resonance Device"),
required_features: wgpu::Features::empty(),
required_limits: wgpu::Limits::default(),
memory_hints: Default::default(),
experimental_features: Default::default(),
trace: wgpu::Trace::Off,
},
))?;
let surface_caps = surface.get_capabilities(&adapter);
let surface_format = surface_caps
.formats
.iter()
.find(|f| f.is_srgb())
.copied()
.unwrap_or(surface_caps.formats[0]);
let config = SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: surface_format,
width,
height,
present_mode: wgpu::PresentMode::Fifo,
alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2,
};
surface.configure(&device, &config);
log::info!(
"Renderer initialized: {}x{}, format: {:?}",
width,
height,
surface_format
);
Ok(Self {
surface,
device,
queue,
config,
size: (width, height),
})
}
pub fn resize(&mut self, width: u32, height: u32) {
let width = width.max(1);
let height = height.max(1);
if self.size != (width, height) {
self.size = (width, height);
self.config.width = width;
self.config.height = height;
self.surface.configure(&self.device, &self.config);
log::debug!("Renderer resized to {}x{}", width, height);
}
}
pub fn size(&self) -> (u32, u32) {
self.size
}
pub fn device(&self) -> &Device {
&self.device
}
pub fn queue(&self) -> &Queue {
&self.queue
}
pub fn surface(&self) -> &Surface<'_> {
&self.surface
}
pub fn config(&self) -> &SurfaceConfiguration {
&self.config
}
pub fn render(&mut self) -> Result<()> {
let output = self.surface.get_current_texture()?;
let view = output
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
{
let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
}),
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
timestamp_writes: None,
});
}
self.queue.submit(std::iter::once(encoder.finish()));
output.present();
Ok(())
}
}
pub fn create_renderer_sync(window: Arc<Window>) -> Result<Renderer> {
Renderer::new(window)
}