use crate::wgpu::backend::WGPUBackend;
use crate::wgpu::binding::BindGroupLayout;
use crate::wgpu::buffer::{Buffer, DynamicBuffer};
use crate::wgpu::cubemap::GPUCubemap;
use crate::wgpu::gpu_context::GpuContext;
use crate::wgpu::samplers::Sampler;
use crate::wgpu::texture_array::GPUTextureArray;
use crate::wgpu::textures::GPUTexture;
pub struct BindGroup(wgpu::BindGroup);
impl BindGroup {
pub(crate) fn raw(&self) -> &wgpu::BindGroup {
&self.0
}
}
enum BufferContents<'a> {
Empty(u64),
Data(&'a [u8]),
}
pub struct BufferBuilder<'a> {
label: Option<&'a str>,
usage: wgpu::BufferUsages,
contents: BufferContents<'a>,
}
impl<'a> Default for BufferBuilder<'a> {
fn default() -> Self {
Self { label: None, usage: wgpu::BufferUsages::empty(), contents: BufferContents::Empty(0) }
}
}
impl<'a> BufferBuilder<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn label(mut self, label: impl Into<Option<&'a str>>) -> Self {
self.label = label.into();
self
}
pub fn usage(mut self, usage: wgpu::BufferUsages) -> Self {
self.usage = usage;
self
}
pub fn uniform(self) -> Self {
self.usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
}
pub fn storage(self) -> Self {
self.usage(wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST)
}
pub fn data(mut self, data: &'a [u8]) -> Self {
self.contents = BufferContents::Data(data);
self
}
pub fn size(mut self, size: u64) -> Self {
self.contents = BufferContents::Empty(size);
self
}
pub fn build(self, backend: &WGPUBackend) -> Buffer {
let raw = self.build_raw(&backend.device);
Buffer::new(raw, GpuContext::from_backend(backend))
}
pub(crate) fn build_raw(self, device: &wgpu::Device) -> wgpu::Buffer {
match self.contents {
BufferContents::Data(data) => {
use wgpu::util::DeviceExt;
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: self.label,
contents: data,
usage: self.usage,
})
}
BufferContents::Empty(size) => device.create_buffer(&wgpu::BufferDescriptor {
label: self.label,
size,
usage: self.usage,
mapped_at_creation: false,
}),
}
}
}
enum DynamicKind {
Uniform,
Storage,
}
pub struct DynamicBufferBuilder<'a> {
label: Option<&'a str>,
kind: DynamicKind,
element_size: u64,
count: u64,
}
impl<'a> DynamicBufferBuilder<'a> {
pub fn uniform(element_size: u64, count: u64) -> Self {
Self { label: None, kind: DynamicKind::Uniform, element_size, count }
}
pub fn storage(element_size: u64, count: u64) -> Self {
Self { label: None, kind: DynamicKind::Storage, element_size, count }
}
pub fn label(mut self, label: impl Into<Option<&'a str>>) -> Self {
self.label = label.into();
self
}
pub fn build(self, backend: &WGPUBackend) -> DynamicBuffer {
let (usage, stride) = match self.kind {
DynamicKind::Uniform => (
wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
dynamic_uniform_offset_stride(&backend.device, self.element_size),
),
DynamicKind::Storage => (
wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
dynamic_storage_offset_stride(&backend.device, self.element_size),
),
};
let buffer = BufferBuilder::new()
.label(self.label)
.usage(usage)
.size(stride * self.count)
.build(backend);
DynamicBuffer::new(buffer, stride, self.element_size)
}
}
pub fn dynamic_uniform_offset_stride(device: &wgpu::Device, element_size: u64) -> u64 {
align_to(element_size, device.limits().min_uniform_buffer_offset_alignment as u64)
}
pub fn dynamic_storage_offset_stride(device: &wgpu::Device, element_size: u64) -> u64 {
align_to(element_size, device.limits().min_storage_buffer_offset_alignment as u64)
}
fn align_to(size: u64, alignment: u64) -> u64 {
size.div_ceil(alignment) * alignment
}
fn dynamic_buffer_binding(buffer: &wgpu::Buffer, element_size: u64) -> wgpu::BindingResource<'_> {
wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer,
offset: 0,
size: wgpu::BufferSize::new(element_size),
})
}
pub struct BindGroupBuilder<'a> {
label: Option<&'a str>,
layout: &'a wgpu::BindGroupLayout,
entries: Vec<wgpu::BindGroupEntry<'a>>,
next_binding: u32,
}
impl<'a> BindGroupBuilder<'a> {
pub fn new(layout: &'a BindGroupLayout) -> Self {
Self::new_raw(layout.raw())
}
pub(crate) fn new_raw(layout: &'a wgpu::BindGroupLayout) -> Self {
Self { label: None, layout, entries: Vec::new(), next_binding: 0 }
}
pub fn label(mut self, label: impl Into<Option<&'a str>>) -> Self {
self.label = label.into();
self
}
pub fn buffer(self, buffer: &'a Buffer) -> Self {
let binding = self.next_binding;
self.buffer_at(binding, buffer)
}
pub fn buffer_at(mut self, binding: u32, buffer: &'a Buffer) -> Self {
self.entries.push(wgpu::BindGroupEntry { binding, resource: buffer.raw().as_entire_binding() });
self.next_binding = self.next_binding.max(binding + 1);
self
}
pub fn dynamic_buffer(self, buffer: &'a DynamicBuffer) -> Self {
let binding = self.next_binding;
self.dynamic_buffer_at(binding, buffer)
}
pub fn dynamic_buffer_at(mut self, binding: u32, buffer: &'a DynamicBuffer) -> Self {
let resource = dynamic_buffer_binding(buffer.buffer.raw(), buffer.element_size);
self.entries.push(wgpu::BindGroupEntry { binding, resource });
self.next_binding = self.next_binding.max(binding + 1);
self
}
pub fn texture_2d(self, texture: &'a GPUTexture) -> Self {
let binding = self.next_binding;
self.texture_2d_at(binding, texture)
}
pub fn texture_2d_at(self, binding: u32, texture: &'a GPUTexture) -> Self {
self.texture_view_at(binding, texture.view())
}
pub fn texture_array(self, texture: &'a GPUTextureArray) -> Self {
let binding = self.next_binding;
self.texture_array_at(binding, texture)
}
pub fn texture_array_at(self, binding: u32, texture: &'a GPUTextureArray) -> Self {
self.texture_view_at(binding, texture.view())
}
pub fn texture_cubemap(self, texture: &'a GPUCubemap) -> Self {
let binding = self.next_binding;
self.texture_cubemap_at(binding, texture)
}
pub fn texture_cubemap_at(self, binding: u32, texture: &'a GPUCubemap) -> Self {
self.texture_view_at(binding, texture.view())
}
pub(crate) fn texture_view_at(mut self, binding: u32, view: &'a wgpu::TextureView) -> Self {
self.entries.push(wgpu::BindGroupEntry { binding, resource: wgpu::BindingResource::TextureView(view) });
self.next_binding = self.next_binding.max(binding + 1);
self
}
pub fn sampler(self, sampler: &'a Sampler) -> Self {
let binding = self.next_binding;
self.sampler_at(binding, sampler)
}
pub fn sampler_at(self, binding: u32, sampler: &'a Sampler) -> Self {
self.sampler_raw_at(binding, sampler.raw())
}
pub(crate) fn sampler_raw_at(mut self, binding: u32, sampler: &'a wgpu::Sampler) -> Self {
self.entries.push(wgpu::BindGroupEntry { binding, resource: wgpu::BindingResource::Sampler(sampler) });
self.next_binding = self.next_binding.max(binding + 1);
self
}
pub fn build(self, device: &wgpu::Device) -> BindGroup {
BindGroup(self.build_raw(device))
}
pub(crate) fn build_raw(self, device: &wgpu::Device) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: self.label,
layout: self.layout,
entries: &self.entries,
})
}
}