use arrayvec::ArrayVec;
use core::hash::Hash;
use core::marker::PhantomData;
use std::sync::Arc;
use unlit_wgpu::resources::ResourceId;
use unlit_wgpu::scene::MAX_VERTEX_BUFFERS;
use unlit_wgpu::specialize::{
CachedRenderPipeline, Specializable, Specializer, SpecializerKey, SurfaceKey,
VertexBufferLayoutDesc,
};
use crate::components::GpuMesh;
pub type GlobalGroupRebuild = Arc<dyn Fn(&RenderResources) -> wgpu::BindGroup>;
#[derive(Clone, Debug)]
pub struct RenderResources {
pub camera: wgpu::Buffer,
pub globals: wgpu::Buffer,
pub metadata: wgpu::Buffer,
pub joints: wgpu::Buffer,
pub morph_weights: wgpu::Buffer,
}
pub struct PipelineDesc {
pub pipeline: wgpu::RenderPipeline,
pub global: Option<GlobalBinding>,
pub material_layout: Option<wgpu::BindGroupLayout>,
pub mesh_layout: Option<wgpu::BindGroupLayout>,
}
impl core::fmt::Debug for PipelineDesc {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PipelineDesc")
.field("pipeline", &self.pipeline)
.field("global", &self.global)
.field("material_layout", &self.material_layout)
.field("mesh_layout", &self.mesh_layout)
.finish()
}
}
#[derive(Clone)]
pub struct GlobalBinding {
pub bind_group: wgpu::BindGroup,
pub rebuild: GlobalGroupRebuild,
}
impl core::fmt::Debug for GlobalBinding {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("GlobalBinding")
.field("bind_group", &self.bind_group)
.finish_non_exhaustive()
}
}
#[derive(Clone)]
pub(crate) struct RegisteredGlobal {
pub(crate) id: ResourceId,
pub(crate) rebuild: GlobalGroupRebuild,
}
pub struct FamilyContext<'a> {
pub device: &'a wgpu::Device,
pub resources: &'a RenderResources,
}
pub trait PipelineKey: Clone + Hash + Eq + 'static {
type Pipeline: Specializable;
fn base_descriptor(&self) -> <Self::Pipeline as Specializable>::Descriptor;
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct DrawKey {
pub surface: SurfaceKey,
pub vertex_buffers: ArrayVec<(u32, VertexBufferLayoutDesc), MAX_VERTEX_BUFFERS>,
}
impl DrawKey {
pub fn for_mesh(surface: SurfaceKey, mesh: &GpuMesh) -> Self {
Self {
surface,
vertex_buffers: mesh.vertex_layout.clone(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PipelineId(u32);
impl PipelineId {
pub(crate) fn new(index: u32) -> Self {
Self(index)
}
pub(crate) fn as_usize(self) -> usize {
self.0 as usize
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct FamilyKey<K> {
pub(crate) key: K,
pub(crate) draw: DrawKey,
}
impl<K: Clone + Hash + Eq + 'static> SpecializerKey for FamilyKey<K> {
const IS_CANONICAL: bool = true;
type Canonical = Self;
}
impl<K> From<(K, DrawKey)> for FamilyKey<K> {
fn from((key, draw): (K, DrawKey)) -> Self {
Self { key, draw }
}
}
pub struct TrivialSpecializer<K>(PhantomData<fn() -> K>);
impl<K> Default for TrivialSpecializer<K> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<K> Clone for TrivialSpecializer<K> {
fn clone(&self) -> Self {
*self
}
}
impl<K> Copy for TrivialSpecializer<K> {}
impl<K> core::fmt::Debug for TrivialSpecializer<K> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TrivialSpecializer").finish_non_exhaustive()
}
}
impl<K: Clone + Hash + Eq + 'static, T: Specializable> Specializer<T> for TrivialSpecializer<K> {
type Key = FamilyKey<K>;
fn specialize(&self, key: FamilyKey<K>, _descriptor: &mut T::Descriptor) -> FamilyKey<K> {
key
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct RenderPipelineFactory;
impl PipelineFactory<CachedRenderPipeline> for RenderPipelineFactory {
fn descriptor(
&self,
_context: &FamilyContext<'_>,
value: &CachedRenderPipeline,
) -> PipelineDesc {
PipelineDesc {
pipeline: value.pipeline.clone(),
global: None,
material_layout: None,
mesh_layout: None,
}
}
}
pub trait PipelineFactory<T: Specializable> {
fn descriptor(&self, context: &FamilyContext<'_>, value: &T) -> PipelineDesc;
}