use crate::errors::AleaticoResult;
use crate::renderer::ctx::Gpu;
use crate::renderer::render_item::RenderItem;
use crate::renderer::render_pass::MainPass;
use crate::renderer::resources::mesh::{Mesh, MeshId};
use crate::renderer::resources::pipelines::{Pipeline, PipelineDescriptor, PipelineId};
use crate::renderer::resources::texture::TextureId;
use crate::renderer::resources::{ResourceStore, texture};
use crate::renderer::surface::Surface;
use std::sync::Arc;
use winit::window::Window;
use crate::renderer::camera::Camera;
use crate::renderer::resources::material::{Material, MaterialDescriptor, MaterialId};
use crate::renderer::vertex::DescribableVertex;
pub mod ctx;
pub mod render_item;
pub mod render_pass;
pub mod resources;
pub mod surface;
pub mod vertex;
pub mod camera;
#[derive(Clone, Copy, Debug, Default)]
pub struct RenderStats {
pub triangles: usize,
}
pub struct Renderer {
gpu: Gpu,
surface: Surface,
settings: RenderSettings,
resources: ResourceStore,
stats: RenderStats,
camera: Camera,
}
#[derive(Debug, Clone, Copy)]
pub struct RenderSettings {
pub clear_color: wgpu::Color,
}
impl Default for RenderSettings {
fn default() -> Self {
Self {
clear_color: wgpu::Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 1.0,
},
}
}
}
impl Renderer {
pub async fn new(window: Arc<Window>) -> AleaticoResult<Self> {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
#[cfg(not(target_arch = "wasm32"))]
backends: wgpu::Backends::PRIMARY,
#[cfg(target_arch = "wasm32")]
backends: wgpu::Backends::GL,
flags: Default::default(),
memory_budget_thresholds: Default::default(),
backend_options: Default::default(),
display: None,
});
let raw_surface = instance.create_surface(window.clone())?;
let mut adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
compatible_surface: Some(&raw_surface),
force_fallback_adapter: false,
})
.await?;
let surface = Surface::new(window, raw_surface, &mut adapter)?;
let (width, height) = (surface.config.width as f32, surface.config.height as f32);
let mut gpu = Gpu::new(instance, adapter).await?;
let camera = Camera::new(&mut gpu, width, height);
let resources = ResourceStore::new(&mut gpu, surface.config.format, camera.bind_group_layout())?;
Ok(Self {
gpu,
surface,
settings: Default::default(),
resources,
stats: Default::default(),
camera,
})
}
pub fn render(&mut self) -> AleaticoResult<()> {
let Some(frame) = self.surface.acquire_frame(self.gpu.device())? else {
return Ok(());
};
let mut encoder = self.gpu.create_encoder(Some("Render Pass Encoder"));
let color_attachments = wgpu::RenderPassColorAttachment {
view: &frame.view(),
resolve_target: None,
depth_slice: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(self.settings.clear_color),
store: wgpu::StoreOp::Store,
},
};
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(color_attachments)],
depth_stencil_attachment: None,
occlusion_query_set: None,
timestamp_writes: None,
multiview_mask: None,
});
MainPass::render(&mut render_pass, &self.camera, &self.resources, &mut self.stats)?;
}
self.gpu.submit(std::iter::once(encoder.finish()));
frame.present();
self.resources.render_item_store.clear();
Ok(())
}
pub fn resize(&mut self, width: u32, height: u32) {
self.surface.resize(self.gpu.device(), width, height);
}
pub fn stats(&self) -> RenderStats {
self.stats
}
pub fn submit(&mut self, render_item: RenderItem) {
self.resources.render_item_store.push(render_item);
}
pub fn create_pipeline<'a, V: DescribableVertex<'a>>(&mut self, shader: &'a str) -> AleaticoResult<PipelineId> {
let pipeline = Pipeline::new(
self.gpu.device(),
PipelineDescriptor::from_wgsl::<V>(self.surface.config.format, shader),
self.camera.bind_group_layout(),
)?;
Ok(self.resources.pipeline_store.insert(pipeline))
}
pub fn create_non_indexed_mesh<T: bytemuck::Pod + bytemuck::Zeroable>(
&mut self,
vertices: &[T],
) -> AleaticoResult<MeshId> {
let mesh = Mesh::non_indexed(&self.gpu.device(), vertices);
Ok(self.resources.mesh_store.insert(mesh))
}
pub fn create_indexed_mesh<T: bytemuck::Pod + bytemuck::Zeroable>(
&mut self,
vertices: &[T],
indices: &[u16],
) -> AleaticoResult<MeshId> {
let mesh = Mesh::indexed(&self.gpu.device(), vertices, indices);
Ok(self.resources.mesh_store.insert(mesh))
}
pub fn create_texture(&mut self, bytes: &[u8]) -> AleaticoResult<TextureId> {
let texture = texture::Texture::from_bytes(&mut self.gpu, bytes)?;
Ok(self.resources.texture_store.insert(texture))
}
pub fn create_material(&mut self, descriptor: MaterialDescriptor) -> AleaticoResult<MaterialId> {
let material = Material::new(self.resources.builtin_pipelines().textured, descriptor)?;
Ok(self.resources.material_store.insert(material))
}
}