mod constructor;
mod execute;
mod prepare;
use crate::ecs::sprite::components::{SpriteBlendMode, SpriteStencilMode};
use crate::ecs::world::World;
use crate::render::wgpu::rendergraph::{PassExecutionContext, PassNode};
use crate::render::wgpu::sprite_texture_atlas::SpriteTextureAtlas;
use nalgebra_glm::{Mat4, Vec2, Vec4};
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct SpriteInstance {
pub position: Vec2,
pub size: Vec2,
pub uv_min: Vec2,
pub uv_max: Vec2,
pub color: Vec4,
pub rotation_scale: Vec4,
pub anchor: Vec2,
pub depth: f32,
pub texture_slot: u32,
pub texture_slot2: u32,
pub blend_factor: f32,
pub gradient_type: u32,
pub gradient_param_a: f32,
pub gradient_param_b: f32,
pub advanced_blend_mode: u32,
pub _padding: [f32; 2],
}
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct SpriteVertex {
pub offset: Vec2,
pub uv: Vec2,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct GlobalUniforms {
pub view_projection: Mat4,
pub screen_size: Vec2,
pub atlas_slots_per_row: f32,
pub atlas_slot_uv_size: f32,
}
struct SpriteDrawBatch {
blend_mode: SpriteBlendMode,
stencil_mode: SpriteStencilMode,
stencil_reference: u32,
clip_rect: Option<[f32; 4]>,
instance_start: u32,
instance_count: u32,
}
pub struct SpritePass {
pipelines_normal: [wgpu::RenderPipeline; 4],
pipelines_stencil_write: [wgpu::RenderPipeline; 4],
pipelines_stencil_test: [wgpu::RenderPipeline; 4],
pipeline_advanced_normal: wgpu::RenderPipeline,
pipeline_advanced_stencil_test: wgpu::RenderPipeline,
global_uniform_buffer: wgpu::Buffer,
instance_buffer: wgpu::Buffer,
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
uniform_bind_group: wgpu::BindGroup,
texture_bind_group_layout: wgpu::BindGroupLayout,
texture_bind_group: Option<wgpu::BindGroup>,
background_bind_group_layout: wgpu::BindGroupLayout,
background_texture: wgpu::Texture,
background_view: wgpu::TextureView,
background_sampler: wgpu::Sampler,
background_bind_group: Option<wgpu::BindGroup>,
background_texture_size: (u32, u32),
max_instances: usize,
instance_data: Vec<SpriteInstance>,
blend_modes: Vec<SpriteBlendMode>,
stencil_modes: Vec<SpriteStencilMode>,
stencil_references: Vec<u8>,
clip_rects: Vec<Option<[f32; 4]>>,
draw_batches: Vec<SpriteDrawBatch>,
atlas: SpriteTextureAtlas,
stencil_texture: wgpu::Texture,
stencil_view: wgpu::TextureView,
stencil_texture_size: (u32, u32),
cached_view_projection: Option<Mat4>,
cached_surface_width: u32,
cached_surface_height: u32,
sort_indices: Vec<usize>,
sorted_instances_scratch: Vec<SpriteInstance>,
sorted_blend_modes_scratch: Vec<SpriteBlendMode>,
sorted_stencil_modes_scratch: Vec<SpriteStencilMode>,
sorted_stencil_references_scratch: Vec<u8>,
sorted_clip_rects_scratch: Vec<Option<[f32; 4]>>,
}
struct SpritePipelineConfig<'a> {
layout: &'a wgpu::PipelineLayout,
shader: &'a wgpu::ShaderModule,
color_format: wgpu::TextureFormat,
blend_state: wgpu::BlendState,
depth_stencil: Option<wgpu::DepthStencilState>,
color_write_mask: wgpu::ColorWrites,
label: &'a str,
fragment_entry: &'a str,
}
fn create_sprite_pipeline(
device: &wgpu::Device,
config: &SpritePipelineConfig,
) -> wgpu::RenderPipeline {
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some(config.label),
layout: Some(config.layout),
vertex: wgpu::VertexState {
module: config.shader,
entry_point: Some("vs_main"),
buffers: &[wgpu::VertexBufferLayout {
array_stride: std::mem::size_of::<SpriteVertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
wgpu::VertexAttribute {
offset: 0,
shader_location: 0,
format: wgpu::VertexFormat::Float32x2,
},
wgpu::VertexAttribute {
offset: std::mem::size_of::<Vec2>() as wgpu::BufferAddress,
shader_location: 1,
format: wgpu::VertexFormat::Float32x2,
},
],
}],
compilation_options: wgpu::PipelineCompilationOptions::default(),
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: config.depth_stencil.clone(),
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: config.shader,
entry_point: Some(config.fragment_entry),
targets: &[Some(wgpu::ColorTargetState {
format: config.color_format,
blend: Some(config.blend_state),
write_mask: config.color_write_mask,
})],
compilation_options: wgpu::PipelineCompilationOptions::default(),
}),
multiview_mask: None,
cache: None,
})
}
fn stencil_depth_state_normal() -> wgpu::DepthStencilState {
wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth24PlusStencil8,
depth_write_enabled: Some(false),
depth_compare: Some(wgpu::CompareFunction::Always),
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState {
compare: wgpu::CompareFunction::Always,
fail_op: wgpu::StencilOperation::Keep,
depth_fail_op: wgpu::StencilOperation::Keep,
pass_op: wgpu::StencilOperation::Keep,
},
back: wgpu::StencilFaceState {
compare: wgpu::CompareFunction::Always,
fail_op: wgpu::StencilOperation::Keep,
depth_fail_op: wgpu::StencilOperation::Keep,
pass_op: wgpu::StencilOperation::Keep,
},
read_mask: 0,
write_mask: 0,
},
bias: wgpu::DepthBiasState::default(),
}
}
fn stencil_depth_state_write() -> wgpu::DepthStencilState {
wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth24PlusStencil8,
depth_write_enabled: Some(false),
depth_compare: Some(wgpu::CompareFunction::Always),
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState {
compare: wgpu::CompareFunction::Always,
fail_op: wgpu::StencilOperation::Keep,
depth_fail_op: wgpu::StencilOperation::Keep,
pass_op: wgpu::StencilOperation::Replace,
},
back: wgpu::StencilFaceState {
compare: wgpu::CompareFunction::Always,
fail_op: wgpu::StencilOperation::Keep,
depth_fail_op: wgpu::StencilOperation::Keep,
pass_op: wgpu::StencilOperation::Replace,
},
read_mask: 0xFF,
write_mask: 0xFF,
},
bias: wgpu::DepthBiasState::default(),
}
}
fn stencil_depth_state_test() -> wgpu::DepthStencilState {
wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth24PlusStencil8,
depth_write_enabled: Some(false),
depth_compare: Some(wgpu::CompareFunction::Always),
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState {
compare: wgpu::CompareFunction::Equal,
fail_op: wgpu::StencilOperation::Keep,
depth_fail_op: wgpu::StencilOperation::Keep,
pass_op: wgpu::StencilOperation::Keep,
},
back: wgpu::StencilFaceState {
compare: wgpu::CompareFunction::Equal,
fail_op: wgpu::StencilOperation::Keep,
depth_fail_op: wgpu::StencilOperation::Keep,
pass_op: wgpu::StencilOperation::Keep,
},
read_mask: 0xFF,
write_mask: 0,
},
bias: wgpu::DepthBiasState::default(),
}
}
impl SpritePass {
pub fn atlas_view(&self) -> &wgpu::TextureView {
&self.atlas.view
}
pub fn atlas_sampler(&self) -> &wgpu::Sampler {
&self.atlas.sampler
}
pub fn atlas_slots_per_row(&self) -> u32 {
self.atlas.slots_per_row
}
pub fn upload_texture_to_atlas(
&mut self,
queue: &wgpu::Queue,
slot: u32,
rgba_data: &[u8],
width: u32,
height: u32,
) {
self.atlas
.upload_texture(queue, slot, rgba_data, width, height);
}
}
impl PassNode<World> for SpritePass {
fn name(&self) -> &str {
"sprite_pass"
}
fn reads(&self) -> Vec<&str> {
vec![]
}
fn writes(&self) -> Vec<&str> {
vec![]
}
fn reads_writes(&self) -> Vec<&str> {
vec!["color"]
}
fn prepare(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, configs: &World) {
if let Some((width, height)) = configs.resources.window.cached_viewport_size {
self.ensure_stencil_texture(device, width, height);
self.prepare_sprites(configs, width, height);
self.write_buffers(queue, width, height);
}
}
fn execute<'r, 'e>(
&mut self,
context: PassExecutionContext<'r, 'e, World>,
) -> Result<
Vec<crate::render::wgpu::rendergraph::SubGraphRunCommand<'r>>,
crate::render::wgpu::rendergraph::RenderGraphError,
> {
let color_view = context.get_texture_view("color")?;
let color_texture = context.get_texture("color").ok();
self.render_sprites(
context.encoder,
color_view,
color_texture,
context.device,
self.cached_surface_width,
self.cached_surface_height,
);
Ok(vec![])
}
}