use glam::UVec2;
use crate::commands::{Command, RemoveTexture, ResourceCommand};
use crate::engine::Engine;
use crate::platform::types::TextureResource;
use crate::render::BackendState;
use crate::renderer::resource::TextureCache;
use crate::renderer::shader2d::runner::Renderer2DRunner;
use crate::renderer::shader3d::draw3d::Draw3D;
use crate::renderer::shader3d::runner::Shader3DRunner;
pub struct RenderDriver {
device: wgpu::Device,
queue: wgpu::Queue,
surface_view_format: wgpu::TextureFormat,
depth_format: wgpu::TextureFormat,
post_sampler: wgpu::Sampler,
textures: TextureCache,
renderer2d: Renderer2DRunner,
renderer_ui: Renderer2DRunner,
render_target: Option<wgpu::Texture>,
depth_target: Option<wgpu::Texture>,
render_target_size: UVec2,
renderer3d: Option<Shader3DRunner>,
}
impl RenderDriver {
pub fn new(backend: &BackendState) -> Self {
let device = backend.device.clone();
let queue = backend.queue.clone();
let surface_view_format = backend.surface_view_format;
let depth_format = wgpu::TextureFormat::Depth32Float;
let renderer2d = Renderer2DRunner::setup(backend, false, depth_format);
let renderer_ui = Renderer2DRunner::setup(backend, true, depth_format);
let post_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("Post Render Sampler"),
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Nearest,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
Self {
device,
queue,
surface_view_format,
depth_format,
post_sampler,
textures: TextureCache::default(),
renderer2d,
renderer_ui,
render_target: None,
depth_target: None,
render_target_size: UVec2::new(0, 0),
renderer3d: None,
}
}
pub fn resize(&mut self) {
self.render_target = None;
self.depth_target = None;
self.render_target_size = UVec2::new(0, 0);
}
pub fn apply_resource_cmds(&mut self, cmds: Vec<ResourceCommand>) {
for cmd in cmds {
match cmd {
ResourceCommand::CreateTexture(c) => {
let tex = TextureCache::create_texture(&self.device, &self.queue, c.data);
self.textures.insert(c.handle.id(), tex);
}
ResourceCommand::CreateRawTexture(c) => {
let tex = TextureCache::create_raw_texture(&self.device, c.texture);
self.textures.insert(c.handle.id(), tex);
}
ResourceCommand::RemoveTexture(RemoveTexture(id)) => {
self.textures.remove(&id);
}
}
}
}
fn ensure_render_target(
&mut self,
g: &Engine,
frame: &wgpu::SurfaceTexture,
) -> (wgpu::Texture, wgpu::TextureView) {
let desired = g.render.logical_size().as_uvec2();
let need_recreate = self
.render_target
.as_ref()
.map(|_| self.render_target_size != desired)
.unwrap_or(true);
if need_recreate {
let size = wgpu::Extent3d {
width: desired.x,
height: desired.y,
depth_or_array_layers: 1,
};
let dimension = frame.texture.dimension();
let texture = self.device.create_texture(&wgpu::TextureDescriptor {
label: Some("Render Texture"),
size,
mip_level_count: 1,
sample_count: 1,
dimension,
format: self.surface_view_format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
self.render_target = Some(texture);
let depth_texture = self.device.create_texture(&wgpu::TextureDescriptor {
label: Some("depth_texture"),
size,
mip_level_count: 1,
sample_count: 1,
dimension,
format: self.depth_format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[self.depth_format],
});
self.depth_target = Some(depth_texture);
self.render_target_size = desired;
}
(
self.render_target.as_ref().unwrap().clone(),
self.depth_target
.as_ref()
.unwrap()
.create_view(&wgpu::TextureViewDescriptor::default()),
)
}
pub fn record_frame(
&mut self,
g: &Engine,
draw_commands: Vec<Command>,
draws3d: Vec<Draw3D>,
encoder: &mut wgpu::CommandEncoder,
frame: &wgpu::SurfaceTexture,
) {
let (render_target, depth_view) = self.ensure_render_target(g, frame);
let mut world_draws: Vec<(crate::commands::Draw, Option<TextureResource>)> = Vec::new();
let mut ui_draws: Vec<(crate::commands::Draw, Option<TextureResource>)> = Vec::new();
for cmd in draw_commands.into_iter() {
match cmd {
Command::Draw(d) => {
let tex = d
.content
.texture_handle()
.and_then(|h| self.textures.get(&h.id()))
.cloned();
world_draws.push((d, tex));
}
Command::DrawUi(d) => {
let tex = d
.content
.texture_handle()
.and_then(|h| self.textures.get(&h.id()))
.cloned();
ui_draws.push((d, tex));
}
}
}
let has_3d = !draws3d.is_empty();
{
let _clear_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Offscreen Clear Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &render_target.create_view(&wgpu::TextureViewDescriptor::default()),
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.0,
g: 0.0,
b: 0.0,
a: 0.0,
}),
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(0.0),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
occlusion_query_set: None,
timestamp_writes: None,
});
}
if has_3d {
if self.renderer3d.is_none() {
self.renderer3d = Some(Shader3DRunner::new(g));
}
self.renderer3d.as_mut().unwrap().record(
g,
&render_target,
&depth_view,
&draws3d,
encoder,
);
}
if !world_draws.is_empty() {
self.renderer2d
.record(g, &render_target, &depth_view, world_draws, encoder);
}
if !ui_draws.is_empty() {
self.renderer_ui
.record(g, &render_target, &depth_view, ui_draws, encoder);
}
let frame_view = frame.texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("Swapchain sRGB View"),
format: Some(self.surface_view_format),
..Default::default()
});
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Post Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &frame_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
timestamp_writes: None,
});
g.render.post_shader.frame_pass(g, &mut render_pass);
let view = render_target.create_view(&wgpu::TextureViewDescriptor::default());
let tex = TextureResource {
texture: render_target,
view,
sampler: self.post_sampler.clone(),
};
g.render.post_shader.draw(&tex, &mut render_pass);
g.render.post_shader.frame_end(&mut render_pass);
}
}