use crate::ecs::sprite::components::{SpriteBlendMode, SpriteStencilMode};
use super::SpritePass;
impl SpritePass {
pub(super) fn pipeline_for_batch(
&self,
blend_mode: SpriteBlendMode,
stencil_mode: SpriteStencilMode,
) -> &wgpu::RenderPipeline {
if blend_mode.is_advanced() {
return match stencil_mode {
SpriteStencilMode::Test => &self.pipeline_advanced_stencil_test,
_ => &self.pipeline_advanced_normal,
};
}
let blend_index = match blend_mode {
SpriteBlendMode::Alpha => 0,
SpriteBlendMode::Additive => 1,
SpriteBlendMode::Multiply => 2,
SpriteBlendMode::Screen => 3,
_ => 0,
};
match stencil_mode {
SpriteStencilMode::None => &self.pipelines_normal[blend_index],
SpriteStencilMode::Write => &self.pipelines_stencil_write[blend_index],
SpriteStencilMode::Test => &self.pipelines_stencil_test[blend_index],
}
}
pub(super) fn has_advanced_blend_batches(&self) -> bool {
self.draw_batches
.iter()
.any(|batch| batch.blend_mode.is_advanced())
}
pub(super) fn render_sprites(
&mut self,
encoder: &mut wgpu::CommandEncoder,
color_view: &wgpu::TextureView,
color_texture: Option<&wgpu::Texture>,
device: &wgpu::Device,
surface_width: u32,
surface_height: u32,
) {
if self.instance_data.is_empty() {
return;
}
let has_advanced = self.has_advanced_blend_batches();
if has_advanced {
self.ensure_background_texture(device, surface_width, surface_height);
if self.background_bind_group.is_none() {
self.update_background_bind_group(device);
}
}
let has_stencil_sprites = self
.stencil_modes
.iter()
.any(|mode| *mode != SpriteStencilMode::None);
let depth_stencil_attachment = if has_stencil_sprites {
Some(wgpu::RenderPassDepthStencilAttachment {
view: &self.stencil_view,
depth_ops: None,
stencil_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(0),
store: wgpu::StoreOp::Store,
}),
})
} else {
Some(wgpu::RenderPassDepthStencilAttachment {
view: &self.stencil_view,
depth_ops: None,
stencil_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(0),
store: wgpu::StoreOp::Discard,
}),
})
};
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Sprite Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: color_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: depth_stencil_attachment.clone(),
occlusion_query_set: None,
multiview_mask: None,
timestamp_writes: None,
});
render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
if let Some(ref bind_group) = self.texture_bind_group {
render_pass.set_bind_group(1, bind_group, &[]);
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
for batch_index in 0..self.draw_batches.len() {
let batch = &self.draw_batches[batch_index];
if batch.blend_mode.is_advanced() {
drop(render_pass);
if let Some(source_texture) = color_texture {
encoder.copy_texture_to_texture(
source_texture.as_image_copy(),
self.background_texture.as_image_copy(),
wgpu::Extent3d {
width: surface_width,
height: surface_height,
depth_or_array_layers: 1,
},
);
}
render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Sprite Render Pass (Advanced)"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: color_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: depth_stencil_attachment.clone(),
occlusion_query_set: None,
multiview_mask: None,
timestamp_writes: None,
});
render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
if let Some(ref tex_bind_group) = self.texture_bind_group {
render_pass.set_bind_group(1, tex_bind_group, &[]);
}
if let Some(ref bg_bind_group) = self.background_bind_group {
render_pass.set_bind_group(2, bg_bind_group, &[]);
}
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
render_pass
.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
}
let batch = &self.draw_batches[batch_index];
if let Some(clip) = batch.clip_rect {
let x = (clip[0] as u32).min(surface_width);
let y = (clip[1] as u32).min(surface_height);
let max_x = (clip[2] as u32).min(surface_width);
let max_y = (clip[3] as u32).min(surface_height);
let width = max_x.saturating_sub(x);
let height = max_y.saturating_sub(y);
if width == 0 || height == 0 {
continue;
}
render_pass.set_scissor_rect(x, y, width, height);
} else {
render_pass.set_scissor_rect(0, 0, surface_width, surface_height);
}
let pipeline = self.pipeline_for_batch(batch.blend_mode, batch.stencil_mode);
render_pass.set_pipeline(pipeline);
if batch.stencil_mode == SpriteStencilMode::Write
|| batch.stencil_mode == SpriteStencilMode::Test
{
render_pass.set_stencil_reference(batch.stencil_reference);
}
render_pass.draw_indexed(
0..6,
0,
batch.instance_start..batch.instance_start + batch.instance_count,
);
}
}
}
}