nightshade 0.13.3

A cross-platform data-oriented game engine.
Documentation
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,
                );
            }
        }
    }
}