You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

232 lines
7.8 KiB

use winit::{event_loop::{EventLoop, ControlFlow}, window::WindowBuilder, window::Window, event::{Event, WindowEvent, DeviceEvent}, dpi::{Size, PhysicalSize}};
use wgpu::{Instance, Surface, Backends, Features, Adapter, DeviceDescriptor, Device, Queue, Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, Extent3d, CommandEncoder, CommandEncoderDescriptor, CommandBuffer, ImageCopyTexture, Origin3d, TextureAspect, SurfaceConfiguration, SurfaceTexture, PresentMode, ImageDataLayout};
use image::{flat::{FlatSamples, SampleLayout}, ColorType};
const DEFAULT_DIMS: [u32; 2] = [100, 100];
pub struct RenderLoop {
window: Window,
instance: Instance,
surface: Surface,
adapter: Adapter,
device: Device,
queue: Queue,
texture_buffer: Texture,
cpu_image: FlatSamples<Vec<u8>>,
}
impl RenderLoop {
// TODO: add an error type
pub async fn new() -> Option<(EventLoop<()>, RenderLoop)> {
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_visible(true)
.with_inner_size(Size::Physical(PhysicalSize {
width: DEFAULT_DIMS[0],
height: DEFAULT_DIMS[1],
}))
.build(&event_loop).ok()?;
let instance = Instance::new(Backends::PRIMARY);
let surface = unsafe { instance.create_surface(&window) };
let required_features = Features::empty();
let mut adapters: Vec<_> = instance.enumerate_adapters(Backends::all()).filter(|a| a.is_surface_supported(&surface) && a.features().contains(required_features)).collect();
if adapters.is_empty() {
return None;
}
println!("available adapters:");
for (i, adapter) in adapters.iter().enumerate() {
let info = adapter.get_info();
println!("{}: {}", i, info.name);
}
// TODO: intelligently select an adapter
let adapter = adapters.swap_remove(1);
let limits = adapter.limits();
let device_descriptor = DeviceDescriptor {
label: None,
features: required_features,
limits: limits.clone(),
};
let (device, queue) = adapter.request_device(&device_descriptor, None).await.ok()?;
let win_size = window.inner_size();
println!("ws: {:?}", win_size);
let texture = device.create_texture(
&TextureDescriptor {
label: None,
size: Extent3d {
width: win_size.width,
height: win_size.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Bgra8Unorm,
usage: TextureUsages::COPY_SRC | TextureUsages::COPY_DST,
}
);
surface.configure(&device, &SurfaceConfiguration {
usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_DST,
format: TextureFormat::Bgra8Unorm,
width: win_size.width,
height: win_size.height,
present_mode: PresentMode::Mailbox,
});
let cpu_image = FlatSamples {
samples: vec![64u8; win_size.width as usize * win_size.height as usize * 4],
layout: SampleLayout {
channels: 4,
channel_stride: 1,
width: win_size.width,
width_stride: 4,
height: win_size.height,
height_stride: win_size.width as usize * 4,
},
color_hint: Some(ColorType::Rgba8),
};
Some((event_loop, RenderLoop {
window,
instance,
surface,
adapter,
device,
queue,
texture_buffer: texture,
cpu_image,
})
)
}
async fn rebuild(&mut self) {
self.surface = unsafe { self.instance.create_surface(&self.window) };
let win_size = self.window.inner_size();
self.texture_buffer = self.device.create_texture(
&TextureDescriptor {
label: None,
size: Extent3d {
width: win_size.width,
height: win_size.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Bgra8Unorm,
usage: TextureUsages::COPY_SRC | TextureUsages::COPY_DST,
}
);
self.surface.configure(&self.device, &SurfaceConfiguration {
usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_DST,
format: TextureFormat::Bgra8Unorm,
width: win_size.width,
height: win_size.height,
present_mode: PresentMode::Mailbox,
});
self.cpu_image = FlatSamples {
samples: vec![64u8; win_size.width as usize * win_size.height as usize * 4],
layout: SampleLayout {
channels: 4,
channel_stride: 1,
width: win_size.width,
width_stride: 4,
height: win_size.height,
height_stride: win_size.width as usize * 4,
},
color_hint: Some(ColorType::Rgba8),
};
}
async fn render(&mut self) -> Option<()>{
let st = loop {
let sto = self.surface.get_current_texture().ok();
if let Some(st@SurfaceTexture { suboptimal: false, .. }) = sto {
break st;
} else {
self.rebuild().await;
}
};
let win_size = self.window.inner_size();
self.queue.write_texture(
ImageCopyTexture {
texture: &self.texture_buffer,
mip_level: 0,
origin: Origin3d::ZERO,
aspect: TextureAspect::All,
},
&self.cpu_image.samples,
ImageDataLayout {
offset: 0,
bytes_per_row: core::num::NonZeroU32::new(win_size.width * 4),
rows_per_image: None,
},
Extent3d {
width: win_size.width,
height: win_size.height,
depth_or_array_layers: 1,
}
);
let mut encoder = self.device.create_command_encoder(&CommandEncoderDescriptor { label: None });
encoder.copy_texture_to_texture(
ImageCopyTexture {
texture: &self.texture_buffer,
mip_level: 0,
origin: Origin3d::ZERO,
aspect: TextureAspect::All,
},
ImageCopyTexture {
texture: &st.texture,
mip_level: 0,
origin: Origin3d::ZERO,
aspect: TextureAspect::All,
},
Extent3d {
width: win_size.width,
height: win_size.height,
depth_or_array_layers: 1,
}
);
self.queue.submit(Some(encoder.finish()));
// I need this here or my flakey GPU driver crashes
self.device.poll(wgpu::Maintain::Wait);
self.queue.on_submitted_work_done().await;
st.present();
Some(())
}
pub fn run(mut self, event_loop: EventLoop<()>) {
event_loop.run(move |ev, _, cf| {
//println!("running event loop: {:?}", ev);
*cf = ControlFlow::Wait;
match ev {
Event::MainEventsCleared => {
self.window.request_redraw();
}
Event::RedrawRequested(_) => {
pollster::block_on(self.render());
}
Event::WindowEvent {
event: WindowEvent::Resized(new_size),
..
} => {
pollster::block_on(self.rebuild());
}
Event::WindowEvent {
event: WindowEvent::CloseRequested | WindowEvent::Destroyed,
..
} => {
*cf = ControlFlow::Exit;
}
_ => ()
}
})
}
}