use std::marker::PhantomData;
use winit::window::Window;
use crate::{log, primitives::{mesh::Mesh, textures::{depth_textures::DepthTexture, Texture}, vertices::Vertex}, utils::{files::Files, resources::{Handle, ResourceCache}}};
use super::pipelines::Pipeline;
#[derive(Debug)]
pub struct RenderEngine {
pub surface: wgpu::Surface<'static>,
pub device: wgpu::Device,
pub queue: wgpu::Queue,
pub config: wgpu::SurfaceConfiguration,
pub size: winit::dpi::PhysicalSize<u32>,
mesh_cache: ResourceCache<Mesh>,
texture_cache: ResourceCache<Texture>,
pipeline_cache: ResourceCache<Pipeline>,
pub depth_texture: DepthTexture,
pub(crate) start_time: u128,
pub time_since_start: f32,
pub delta_time: f32,
pub window: Window }
impl RenderEngine {
pub fn window(&self) -> &Window { &self.window }
pub fn texture(&self, handle: &Handle<Texture>) -> &Texture { self.texture_cache.get(handle).unwrap() }
pub fn texture_from_path(&self, path: impl Into<String>) -> &Texture {
self.texture_cache.get(
&Handle {
hash: ResourceCache::<Texture>::hash_path(path.into()),
data: Default::default()
}
).unwrap()
}
pub fn mesh(&self, handle: &Handle<Mesh>) -> &Mesh { self.mesh_cache.get(handle).unwrap() }
pub async fn new(window: Window) -> Self {
let size = window.inner_size();
log!("Creating instance...");
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
..Default::default()
});
log!("Creating surface...");
let surface = unsafe { instance.create_surface_unsafe(wgpu::SurfaceTargetUnsafe::from_window(&window).unwrap()) }.unwrap();
log!("Creating adapter...");
let adapter = instance.request_adapter(
&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
compatible_surface: Some(&surface),
force_fallback_adapter: false
}
).await.unwrap();
log!("Creating device and queue...");
let (device, queue) = adapter.request_device(
&wgpu::DeviceDescriptor {
required_features: adapter.features(),
required_limits: adapter.limits(), label: None
},
None
).await.unwrap();
log!("Performing final capabilities creation...");
let capabilities = surface.get_capabilities(&adapter);
let format = capabilities.formats.iter().copied()
.filter(|f| f.is_srgb()).next()
.unwrap_or(capabilities.formats[0]);
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: format, width: size.width.max(1), height: size.height.max(1),
present_mode: capabilities.present_modes[0],
alpha_mode: capabilities.alpha_modes[0],
view_formats: vec![],
desired_maximum_frame_latency: 2
};
surface.configure(&device, &config);
let depth_texture = DepthTexture::new(&config, &device, "depth_texture");
let now = web_time::SystemTime::now();
let start_time = now.duration_since(web_time::UNIX_EPOCH).unwrap().as_millis();
log!("Created render engine");
Self {
window, surface, device,
queue, config, size, depth_texture,
start_time,
time_since_start: 0.0,
delta_time: 0.0,
mesh_cache: ResourceCache::new(),
texture_cache: ResourceCache::new(),
pipeline_cache: ResourceCache::new()
}
}
pub fn resize(&mut self, new_size: winit::dpi::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);
self.depth_texture = DepthTexture::new(&self.config, &self.device, "depth_texture");
}
}
pub fn next_frame(&self) { self.window().request_redraw(); }
pub fn create_texture(&mut self, path: impl Into<String>, bytes: &[u8]) -> Handle<Texture> {
self.texture_cache.load(path, || {
Texture::from_bytes(
&self.device,
&self.queue,
bytes,
"diffuse"
).unwrap()
})
}
pub fn load_texture(&mut self, path: &str) -> Handle<Texture> {
self.texture_cache.load(path, || {
Texture::from_bytes(
&self.device,
&self.queue,
&Files::load_bytes(path).expect("Failed to load file"),
"diffuse"
).unwrap()
})
}
pub fn create_mesh(&mut self, path: &str, vertices: &[Vertex], indices: &[u16]) -> Handle<Mesh> {
self.mesh_cache.load(path, || {
Mesh::from_raw(&self.device, vertices, indices)
})
}
pub fn draw_textured_mesh<'rpass>(
&'rpass self,
pass: &mut wgpu::RenderPass<'rpass>,
mesh: &'rpass Handle<Mesh>,
texture: &'rpass Handle<Texture>,
instance_buffer: &'rpass wgpu::Buffer,
instance_count: u32
) {
self.texture(texture).bind(pass, 1);
self.mesh(mesh).draw(pass, instance_buffer, instance_count);
}
pub fn draw_textured_list_mesh<'rpass>(
&'rpass self,
pass: &mut wgpu::RenderPass<'rpass>,
mesh: &'rpass Handle<Mesh>,
texture: &'rpass Handle<Texture>,
instance_buffer: &'rpass wgpu::Buffer,
instance_count: u32
) {
self.texture(texture).bind(pass, 1);
self.mesh(mesh).draw_list(pass, instance_buffer, instance_count);
}
pub fn pipeline(&self, handle: &Handle<Pipeline>) -> Option<&Pipeline> { self.pipeline_cache.get(handle) }
pub fn pipeline_mut(&mut self, handle: &Handle<Pipeline>) -> Option<&mut Pipeline> { self.pipeline_cache.get_mut(handle) }
pub fn pipeline_path(&self, path: impl Into<String>) -> Option<&Pipeline> {
self.pipeline_cache.get(&Handle { hash: ResourceCache::<Pipeline>::hash_path(path.into()), data: PhantomData::default() })
}
pub fn pipeline_path_mut(&mut self, path: impl Into<String>) -> Option<&mut Pipeline> {
self.pipeline_cache.get_mut(&Handle { hash: ResourceCache::<Pipeline>::hash_path(path.into()), data: PhantomData::default() })
}
pub fn register_pipeline(&mut self, path: impl Into<String>, pipeline: Pipeline) {
self.pipeline_cache.insert(ResourceCache::<Pipeline>::hash_path(path.into()), pipeline);
}
pub fn verify_pipeline_exists<F>(&mut self, path: impl Into<String>, create: F) where F: Fn(&RenderEngine) -> Pipeline {
let hash = ResourceCache::<Pipeline>::hash_path(path.into());
if self.pipeline_cache.get(&Handle { hash, data: PhantomData::default() }).is_none() {
self.pipeline_cache.insert(hash, create(&self));
}
}
}