use std::marker::PhantomData;
use crate::{
assets::{
storage::{ProcessedAssets, RawAssetHandle},
upload::Asset,
},
ecs::system::Res,
wgpu::{
backend::WGPUBackend,
binding::BindGroupTarget,
buffer::Buffer,
buffers::{BindGroup, BindGroupBuilder, BufferBuilder},
flags::BufferUsages,
samplers::{GlobalSamplers, SamplerKind},
},
};
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum BindingInstanceEntry {
Texture(RawAssetHandle),
TextureArray(RawAssetHandle),
Cubemap(RawAssetHandle),
Sampler(SamplerKind),
Uniform(Vec<u8>),
Storage(Vec<u8>),
}
pub struct BindingInstanceDescriptor<T> {
pub target: RawAssetHandle,
pub params: Vec<(&'static str, BindingInstanceEntry)>,
_marker: PhantomData<fn() -> T>,
}
impl<T> BindingInstanceDescriptor<T> {
pub fn new(target: RawAssetHandle, params: Vec<(&'static str, BindingInstanceEntry)>) -> Self {
Self { target, params, _marker: PhantomData }
}
}
pub fn binding_index(entries: &[super::binding::BindingEntry], name: &str) -> Option<u32> {
entries.iter().find(|e| e.name == name).map(|e| e.binding)
}
pub struct GPUBindingInstance<T> {
pub target: RawAssetHandle,
pub bind_group: BindGroup,
buffers: Vec<(&'static str, Buffer)>,
_marker: PhantomData<fn() -> T>,
}
impl<T> GPUBindingInstance<T> {
pub fn update(&self, name: &str, data: &[u8]) {
match self.buffer(name) {
Some(buf) => buf.write(data),
None => tracing::warn!(
"GPUBindingInstance::update: no bound buffer named '{name}' — check for a typo \
against the entries in this instance's BindingInstanceDescriptor"
),
}
}
pub fn buffer(&self, name: &str) -> Option<&Buffer> {
self.buffers.iter().find(|(n, _)| *n == name).map(|(_, buf)| buf)
}
}
impl<T> Asset<WGPUBackend> for GPUBindingInstance<T>
where
T: BindGroupTarget + 'static + Send + Sync,
{
type Source = BindingInstanceDescriptor<T>;
type Deps<'a> = (
Res<'a, ProcessedAssets<T>>,
Res<'a, ProcessedAssets<super::textures::GPUTexture>>,
Res<'a, ProcessedAssets<super::texture_array::GPUTextureArray>>,
Res<'a, ProcessedAssets<super::cubemap::GPUCubemap>>,
Res<'a, GlobalSamplers>,
);
fn upload<'a>(
source: &Self::Source,
backend: &WGPUBackend,
deps: &Self::Deps<'a>,
) -> Option<Self> {
let (targets, textures, texture_arrays, cubemaps, samplers) = deps;
let target = targets.get(source.target)?;
let owned_buffers: Vec<(&'static str, Buffer)> = source
.params
.iter()
.filter_map(|(name, entry)| match entry {
BindingInstanceEntry::Uniform(bytes) => Some((
*name,
BufferBuilder::new()
.usage(BufferUsages::UNIFORM | BufferUsages::COPY_DST | BufferUsages::COPY_SRC)
.data(bytes)
.build(backend),
)),
BindingInstanceEntry::Storage(bytes) => Some((
*name,
BufferBuilder::new()
.usage(BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC)
.data(bytes)
.build(backend),
)),
_ => None,
})
.collect();
let mut builder = BindGroupBuilder::new(target.bind_group_layout());
for (name, entry) in &source.params {
let binding = binding_index(target.binding_entries(), name)?;
builder = match entry {
BindingInstanceEntry::Texture(id) => builder.texture_2d_at(binding, textures.get(*id)?),
BindingInstanceEntry::TextureArray(id) => {
builder.texture_array_at(binding, texture_arrays.get(*id)?)
}
BindingInstanceEntry::Cubemap(id) => builder.texture_cubemap_at(binding, cubemaps.get(*id)?),
BindingInstanceEntry::Sampler(kind) => builder.sampler_at(binding, samplers.get(*kind)),
BindingInstanceEntry::Uniform(_) | BindingInstanceEntry::Storage(_) => {
let buf = &owned_buffers.iter().find(|(n, _)| n == name)?.1;
builder.buffer_at(binding, buf)
}
};
}
let bind_group = builder.build(backend);
Some(Self {
target: source.target,
bind_group,
buffers: owned_buffers,
_marker: PhantomData,
})
}
}
pub type GPUMaterialInstance = GPUBindingInstance<super::material::GPUMaterial>;
pub type MaterialInstanceDescriptor = BindingInstanceDescriptor<super::material::GPUMaterial>;
pub type GPUComputeInstance = GPUBindingInstance<super::compute::GPUCompute>;
pub type ComputeInstanceDescriptor = BindingInstanceDescriptor<super::compute::GPUCompute>;
crate::wgpu::plugin_macros::asset_plugin! {
MaterialInstancePlugin, GPUMaterialInstance
}
crate::wgpu::plugin_macros::asset_plugin! {
ComputeInstancePlugin, GPUComputeInstance
}