use crate::colour::Colour;
use crate::context::WgpuClump;
use crate::engine_handle::Engine;
use crate::resource::{ResourceId, ResourceManager};
use crate::shader::Shader;
use crate::texture::UniformTexture;
use crate::vectors::Vec2;
use crate::{vec2, Game};
pub(crate) fn make_pipeline(
device: &wgpu::Device,
topology: wgpu::PrimitiveTopology,
bind_group_layouts: &[&wgpu::BindGroupLayout],
vertex_buffers: &[wgpu::VertexBufferLayout],
shader: &wgpu::ShaderModule,
texture_format: wgpu::TextureFormat,
label: Option<&str>,
) -> wgpu::RenderPipeline {
let layout_label = label.map(|label| format!("{}_layout", label));
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: layout_label.as_deref(), bind_group_layouts,
push_constant_ranges: &[],
});
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label,
layout: Some(&layout),
vertex: wgpu::VertexState {
module: shader,
entry_point: "vs_main", buffers: vertex_buffers, compilation_options: wgpu::PipelineCompilationOptions::default(),
},
fragment: Some(wgpu::FragmentState {
module: shader,
entry_point: "fs_main",
targets: &[Some(wgpu::ColorTargetState {
format: texture_format,
blend: Some(wgpu::BlendState::ALPHA_BLENDING), write_mask: wgpu::ColorWrites::ALL, })],
compilation_options: wgpu::PipelineCompilationOptions::default(),
}),
primitive: wgpu::PrimitiveState {
topology,
strip_index_format: None,
front_face: wgpu::FrontFace::Cw, cull_mode: Some(wgpu::Face::Back), polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1, mask: !0, alpha_to_coverage_enabled: false, },
multiview: None,
cache: None,
})
}
pub(crate) fn render<T>(game: &mut T, engine: &mut Engine) -> Result<(), wgpu::SurfaceError>
where
T: Game,
{
if engine.is_loading() {
return Ok(());
}
let render_handle = RenderHandle::from(engine);
game.render(render_handle);
Ok(())
}
pub struct RenderHandle<'a> {
encoder: Option<wgpu::CommandEncoder>,
surface: Option<wgpu::SurfaceTexture>,
pub(crate) resources: &'a ResourceManager,
defualt_id: ResourceId<Shader>,
defualt_view: wgpu::TextureView,
defualt_view_size: Vec2<u32>,
camera_bindgroup: &'a wgpu::BindGroup,
pub(crate) wgpu: &'a WgpuClump,
format: wgpu::TextureFormat,
}
impl<'a> RenderHandle<'a> {
pub fn begin_pass<'p>(&mut self, clear_colour: Colour) -> Renderer<'_, 'p> {
let mut pass = match &mut self.encoder {
Some(encoder) => Self::create_pass(encoder, &self.defualt_view, clear_colour.into()),
None => unreachable!(),
};
let pipeline = &self
.resources
.get_pipeline(&self.defualt_id)
.unwrap()
.pipeline;
pass.set_pipeline(pipeline);
pass.set_bind_group(1, self.camera_bindgroup, &[]);
Renderer {
pass,
size: self.defualt_view_size,
defualt_id: self.defualt_id,
resources: self.resources,
camera_bindgroup: self.camera_bindgroup,
wgpu: self.wgpu,
}
}
pub fn begin_texture_pass<'o, 'p>(
&'o mut self,
texture: &'o mut UniformTexture,
clear_colour: Colour,
) -> Renderer<'o, 'p> {
let size = texture.get_size();
let mut pass = match &mut self.encoder {
Some(encoder) => Self::create_pass(
encoder,
texture.make_render_view(self.wgpu, self.format),
clear_colour.into(),
),
None => unreachable!(),
};
let pipeline = &self
.resources
.get_pipeline(&self.defualt_id)
.unwrap()
.pipeline;
pass.set_pipeline(pipeline);
pass.set_bind_group(1, self.camera_bindgroup, &[]);
Renderer {
pass,
size,
defualt_id: self.defualt_id,
resources: self.resources,
camera_bindgroup: self.camera_bindgroup,
wgpu: self.wgpu,
}
}
fn create_pass<'p>(
encoder: &'p mut wgpu::CommandEncoder,
view: &'p wgpu::TextureView,
clear_colour: wgpu::Color,
) -> wgpu::RenderPass<'p> {
let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(clear_colour),
store: wgpu::StoreOp::Store,
},
})],
timestamp_writes: None,
occlusion_query_set: None,
depth_stencil_attachment: None,
});
render_pass
}
}
impl<'a> From<&'a mut Engine> for RenderHandle<'a> {
fn from(value: &'a mut Engine) -> Self {
let context = value.context.as_ref().unwrap();
let encoder = context
.wgpu
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("render encoder"),
});
let texture = context.get_surface_texture().unwrap();
let defualt_view_size = texture.texture.size();
let defualt_view_size = vec2!(defualt_view_size.width, defualt_view_size.height);
let defualt_view = texture
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
Self {
encoder: Some(encoder),
surface: Some(texture),
resources: value.get_resources(),
defualt_id: value.defualt_pipe_id(),
defualt_view,
defualt_view_size,
camera_bindgroup: &context.camera_bind_group,
wgpu: &context.wgpu,
format: context.get_texture_format(),
}
}
}
impl Drop for RenderHandle<'_> {
fn drop(&mut self) {
self.wgpu
.queue
.submit(std::iter::once(self.encoder.take().unwrap().finish()));
self.surface.take().unwrap().present();
}
}
pub struct Renderer<'o, 'p>
where
'o: 'p,
{
pub(crate) pass: wgpu::RenderPass<'p>,
pub(crate) size: Vec2<u32>,
pub(crate) resources: &'o ResourceManager,
pub(crate) defualt_id: ResourceId<Shader>,
pub(crate) camera_bindgroup: &'o wgpu::BindGroup,
pub(crate) wgpu: &'o WgpuClump,
}
impl<'p, 'o> Renderer<'p, 'o> {
pub fn reset_camera(&mut self) {
self.pass.set_bind_group(1, self.camera_bindgroup, &[]);
}
pub fn get_size(&self) -> Vec2<u32> {
self.size
}
}