use std::borrow::Cow;
use glam::Affine2;
use wgpu::util::DeviceExt as _;
use super::{
Instances, PREFERRED_TEXTURE_FORMAT, ScreenInfo, TextureRef, UniformState, atlas::Atlas,
data::TexturedVertex,
};
pub(crate) struct Pipeline {
instances: Instances,
render: wgpu::RenderPipeline,
instance_buffer: wgpu::Buffer,
}
impl Pipeline {
pub(crate) fn new(
shader_source: &str,
fragment_shader_entry_point: Option<&str>,
device: &wgpu::Device,
screen_info: &UniformState<ScreenInfo>,
atlas: &Atlas,
) -> Self {
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Component Render Pipeline Layout"),
bind_group_layouts: &[
Some(&atlas.bind_group_layout),
Some(&atlas.rects.bind_group_layout),
Some(&screen_info.bind_group_layout),
],
immediate_size: 0,
});
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Diffuse Texture Shader"),
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(shader_source)),
});
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
buffers: &[TexturedVertex::descriptor(), Instances::descriptor()],
module: &shader,
entry_point: None,
compilation_options: wgpu::PipelineCompilationOptions::default(),
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: fragment_shader_entry_point,
targets: &[Some(wgpu::ColorTargetState {
format: PREFERRED_TEXTURE_FORMAT,
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: wgpu::PipelineCompilationOptions::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Cw,
cull_mode: None,
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_mask: None,
cache: None,
});
let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"),
contents: &[],
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
});
let instances = Instances::default();
Self {
instances,
render: render_pipeline,
instance_buffer,
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn render_instances(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
vertex_buffer: &wgpu::Buffer,
index_buffer: &wgpu::Buffer,
render_pass: &mut wgpu::RenderPass<'_>,
screen_info: &UniformState<ScreenInfo>,
atlas: &Atlas,
) {
if self.instances.is_empty() {
return;
}
let (instances_bytes, instances_len) = {
(self.instances.bytes(), self.instances.len())
};
let instance_buffer_already_pushed = if instances_bytes.len() as u64
> self.instance_buffer.size()
{
self.instance_buffer.destroy();
self.instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Instance Buffer"),
contents: instances_bytes,
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
});
true
} else {
false
};
render_pass.set_pipeline(&self.render);
render_pass.set_bind_group(0, &atlas.bind_group, &[]);
render_pass.set_bind_group(1, &atlas.rects.bind_group, &[]);
render_pass.set_bind_group(2, &screen_info.bind_group, &[]);
render_pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint16);
render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..));
render_pass.draw_indexed(0..6, 0, 0..instances_len as u32);
if !instance_buffer_already_pushed {
queue.write_buffer(&self.instance_buffer, 0, instances_bytes);
}
self.instances.clear();
}
pub(crate) fn push_instance(
&mut self,
transformation: Affine2,
sub_rectangle: (f32, f32, f32, f32),
texture_ref: TextureRef,
) {
self.instances
.push(transformation, sub_rectangle, texture_ref);
}
pub(crate) fn extend_instances(
&mut self,
items: impl Iterator<Item = (Affine2, (f32, f32, f32, f32), TextureRef)>,
) {
self.instances.extend(items);
}
}