use smallvec::SmallVec;
use wgpu;
use crate::texture::Texture;
pub fn align(size: usize, alignment: usize) -> usize {
size.div_ceil(alignment) * alignment
}
#[derive(PartialEq, Clone)]
pub enum BgResource {
TexView(wgpu::TextureView),
Buf(wgpu::Buffer),
Sampler(wgpu::Sampler),
}
pub struct BindGroupWrapper {
bind_group: wgpu::BindGroup,
resources: SmallVec<[BgResource; 16]>,
}
impl BindGroupWrapper {
fn new(bind_group: wgpu::BindGroup, resources: SmallVec<[BgResource; 16]>) -> Self {
Self { bind_group, resources }
}
pub fn bg(&self) -> &wgpu::BindGroup {
&self.bind_group
}
pub fn is_stale(&self, entries: &SmallVec<[wgpu::BindGroupEntry; 16]>) -> bool {
if self.resources.len() != entries.len() {
return true;
}
for it in entries.iter().zip(self.resources.iter()) {
let (entry, res) = it;
let is_different = match &entry.resource {
wgpu::BindingResource::Buffer(buffer_binding) => BgResource::Buf(buffer_binding.buffer.clone()) != *res,
wgpu::BindingResource::Sampler(sampler) => BgResource::Sampler((*sampler).clone()) != *res,
wgpu::BindingResource::TextureView(view) => BgResource::TexView((*view).clone()) != *res,
_ => unimplemented!("other binding types"),
};
if is_different {
return true;
}
}
false
}
}
pub struct BindGroupDesc<'a> {
pub label: Option<String>,
pub bind_group_entries: SmallVec<[wgpu::BindGroupEntry<'a>; 16]>,
last_binding_number: u32,
}
impl Default for BindGroupDesc<'_> {
fn default() -> Self {
Self {
label: None,
bind_group_entries: SmallVec::new(),
last_binding_number: 0,
}
}
}
impl<'a> BindGroupDesc<'a> {
pub fn new(label: &str, entries: SmallVec<[wgpu::BindGroupEntry<'a>; 16]>) -> Self {
Self {
label: Some(String::from(label)),
bind_group_entries: entries,
last_binding_number: 0,
}
}
pub fn into_bind_group_wrapper(self, device: &wgpu::Device, layout: &wgpu::BindGroupLayout) -> BindGroupWrapper {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout,
entries: self.bind_group_entries.as_slice(),
label: self.label.as_deref(),
});
let mut bg_resources = SmallVec::new();
for res in self.bind_group_entries {
match res.resource {
wgpu::BindingResource::Buffer(buffer_binding) => bg_resources.push(BgResource::Buf(buffer_binding.buffer.clone())),
wgpu::BindingResource::Sampler(sampler) => bg_resources.push(BgResource::Sampler(sampler.clone())),
wgpu::BindingResource::TextureView(texture_view) => bg_resources.push(BgResource::TexView(texture_view.clone())),
_ => todo!(),
}
}
BindGroupWrapper::new(bind_group, bg_resources)
}
}
pub struct BindGroupBuilder<'a> {
bind_group_desc: Option<BindGroupDesc<'a>>,
}
impl Default for BindGroupBuilder<'_> {
fn default() -> Self {
Self::new()
}
}
impl<'a> BindGroupBuilder<'a> {
pub fn new() -> Self {
Self {
bind_group_desc: Some(BindGroupDesc::default()),
}
}
#[must_use]
pub fn label(mut self, label: &str) -> Self {
self.bind_group_desc.as_mut().unwrap().label = Some(String::from(label));
self
}
#[must_use]
pub fn add_entry_empty(mut self) -> Self {
self.bind_group_desc.as_mut().unwrap().last_binding_number += 1;
self
}
#[must_use]
pub fn add_entry_tex(mut self, tex: &'a Texture) -> Self {
let binding_number = self.bind_group_desc.as_ref().unwrap().last_binding_number;
let entry = wgpu::BindGroupEntry {
binding: binding_number,
resource: wgpu::BindingResource::TextureView(&tex.view),
};
self.bind_group_desc.as_mut().unwrap().bind_group_entries.push(entry);
self.bind_group_desc.as_mut().unwrap().last_binding_number += 1;
self
}
#[must_use]
pub fn add_entry_buf(mut self, buffer: &'a wgpu::Buffer) -> Self {
let binding_number = self.bind_group_desc.as_ref().unwrap().last_binding_number;
let entry = wgpu::BindGroupEntry {
binding: binding_number,
resource: buffer.as_entire_binding(),
};
self.bind_group_desc.as_mut().unwrap().bind_group_entries.push(entry);
self.bind_group_desc.as_mut().unwrap().last_binding_number += 1;
self
}
#[must_use]
pub fn add_entry_buf_chunk<T>(mut self, buffer: &'a wgpu::Buffer) -> Self {
let binding_number = self.bind_group_desc.as_ref().unwrap().last_binding_number;
let binding = wgpu::BufferBinding {
buffer,
offset: 0,
size: wgpu::BufferSize::new(u64::try_from(align(std::mem::size_of::<T>(), 256)).unwrap()),
};
let entry = wgpu::BindGroupEntry {
binding: binding_number,
resource: wgpu::BindingResource::Buffer(binding),
};
self.bind_group_desc.as_mut().unwrap().bind_group_entries.push(entry);
self.bind_group_desc.as_mut().unwrap().last_binding_number += 1;
self
}
#[must_use]
pub fn add_entry_sampler(mut self, sampler: &'a wgpu::Sampler) -> Self {
let binding_number = self.bind_group_desc.as_ref().unwrap().last_binding_number;
let entry = wgpu::BindGroupEntry {
binding: binding_number,
resource: wgpu::BindingResource::Sampler(sampler),
};
self.bind_group_desc.as_mut().unwrap().bind_group_entries.push(entry);
self.bind_group_desc.as_mut().unwrap().last_binding_number += 1;
self
}
pub fn build(&mut self, device: &wgpu::Device, layout: &wgpu::BindGroupLayout) -> BindGroupWrapper {
let desc = self.bind_group_desc.take().unwrap();
desc.into_bind_group_wrapper(device, layout)
}
pub fn build_bind_group(&mut self, device: &wgpu::Device, layout: &wgpu::BindGroupLayout) -> wgpu::BindGroup {
let desc = self.bind_group_desc.take().unwrap();
desc.into_bind_group_wrapper(device, layout).bind_group
}
pub fn build_entries(&mut self) -> SmallVec<[wgpu::BindGroupEntry<'a>; 16]> {
self.bind_group_desc.take().unwrap().bind_group_entries
}
}