mod constructor;
mod execute;
mod prepare;
use crate::ecs::world::World;
use crate::render::wgpu::rendergraph::{PassExecutionContext, PassNode};
use nalgebra_glm::Mat4;
const INITIAL_RECT_CAPACITY: usize = 4096;
const INITIAL_TEXT_VERTEX_CAPACITY: usize = 64000;
const INITIAL_TEXT_INDEX_CAPACITY: usize = 96000;
const INITIAL_TEXT_INSTANCE_CAPACITY: usize = 1024;
const INITIAL_CHARACTER_COLOR_CAPACITY: usize = 10000;
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
struct UiRectInstance {
position_size: [f32; 4],
color: [f32; 4],
border_color: [f32; 4],
clip_rect: [f32; 4],
params: [f32; 4],
shadow_color: [f32; 4],
shadow_params: [f32; 4],
effect_params: [f32; 4],
quad_corner_01: [f32; 4],
quad_corner_23: [f32; 4],
effect_kind: u32,
is_quad: u32,
_padding: [u32; 2],
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
struct TextInstanceData {
color: [f32; 4],
outline_color: [f32; 4],
clip_rect: [f32; 4],
params: [f32; 4],
position: [f32; 4],
}
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
struct UiPassVertex {
position: [f32; 2],
}
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
struct UiTextVertex {
position: [f32; 3],
tex_coords: [f32; 2],
character_index: u32,
text_instance_index: u32,
_padding: u32,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
struct GlobalUniforms {
projection: Mat4,
}
struct TextDrawCall {
index_start: u32,
index_count: u32,
layer: super::UiLayer,
}
struct LayerDrawGroup {
rect_start: u32,
rect_count: u32,
text_draw_start: usize,
text_draw_count: usize,
}
#[derive(Clone, Copy, Debug)]
pub(super) struct SlabAlloc {
pub start: u32,
pub capacity: u32,
}
#[derive(Clone, Debug)]
pub(super) struct TextSlotEntry {
pub signature: u64,
pub vertex_alloc: SlabAlloc,
pub index_alloc: SlabAlloc,
pub index_count: u32,
pub char_color_alloc: SlabAlloc,
}
#[derive(Default)]
pub(super) struct SlabAllocator {
pub free: Vec<SlabAlloc>,
pub high_water: u32,
}
impl SlabAllocator {
pub fn alloc(&mut self, needed: u32) -> SlabAlloc {
if needed == 0 {
return SlabAlloc {
start: 0,
capacity: 0,
};
}
if let Some(index) = self.free.iter().position(|s| s.capacity >= needed) {
return self.free.swap_remove(index);
}
let start = self.high_water;
self.high_water += needed;
SlabAlloc {
start,
capacity: needed,
}
}
pub fn free(&mut self, alloc: SlabAlloc) {
if alloc.capacity > 0 {
self.free.push(alloc);
}
}
}
pub struct UiPass {
rect_pipeline: wgpu::RenderPipeline,
text_pipeline: wgpu::RenderPipeline,
global_uniform_buffer: wgpu::Buffer,
global_uniform_bind_group: wgpu::BindGroup,
rect_quad_vertex_buffer: wgpu::Buffer,
rect_quad_index_buffer: wgpu::Buffer,
rect_instance_buffer: wgpu::Buffer,
rect_draw_order_buffer: wgpu::Buffer,
rect_instance_bind_group_layout: wgpu::BindGroupLayout,
rect_instance_bind_group: wgpu::BindGroup,
rect_instance_capacity: usize,
rect_draw_order_capacity: usize,
rect_count: u32,
prev_rect_instances: Vec<UiRectInstance>,
text_vertex_buffer: wgpu::Buffer,
text_index_buffer: wgpu::Buffer,
text_instance_buffer: wgpu::Buffer,
character_color_buffer: wgpu::Buffer,
character_bg_color_buffer: wgpu::Buffer,
text_data_bind_group_layout: wgpu::BindGroupLayout,
text_data_bind_group: wgpu::BindGroup,
text_font_bind_group_layout: wgpu::BindGroupLayout,
text_font_bind_groups: Vec<wgpu::BindGroup>,
_text_global_bind_group_layout: wgpu::BindGroupLayout,
text_global_bind_group: wgpu::BindGroup,
text_vertex_capacity: usize,
text_index_capacity: usize,
text_instance_capacity: usize,
character_color_capacity: usize,
character_bg_color_capacity: usize,
prev_text_instance_data: Vec<TextInstanceData>,
text_slots: std::collections::HashMap<u32, TextSlotEntry>,
text_vertex_slab: SlabAllocator,
text_index_slab: SlabAllocator,
char_color_slab: SlabAllocator,
prev_character_colors: Vec<[f32; 4]>,
prev_character_bg_colors: Vec<[f32; 4]>,
text_draw_calls: Vec<TextDrawCall>,
layer_draw_groups: Vec<LayerDrawGroup>,
cached_atlas_view: Option<wgpu::TextureView>,
screen_width: f32,
screen_height: f32,
}
impl PassNode<World> for UiPass {
fn name(&self) -> &'static str {
"ui_pass"
}
fn reads(&self) -> Vec<&str> {
vec![]
}
fn writes(&self) -> Vec<&str> {
vec![]
}
fn reads_writes(&self) -> Vec<&str> {
vec!["color", "depth"]
}
fn prepare(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, world: &World) {
self.prepare_pass(device, queue, world);
}
fn runs_in_compose_only_phase(&self) -> bool {
true
}
fn execute<'r, 'e>(
&mut self,
context: PassExecutionContext<'r, 'e, World>,
) -> Result<
Vec<crate::render::wgpu::rendergraph::SubGraphRunCommand<'r>>,
crate::render::wgpu::rendergraph::RenderGraphError,
> {
self.execute_pass(context)
}
}