use crate::context::Context;
use bytemuck::{Pod, Zeroable};
pub struct GPUVec<T: Pod + Zeroable> {
dirty: bool,
len: usize,
usage: wgpu::BufferUsages,
buffer: Option<wgpu::Buffer>,
data: Option<Vec<T>>,
}
impl<T: Pod + Zeroable> GPUVec<T> {
pub fn new(data: Vec<T>, buf_type: BufferType, _alloc_type: AllocationType) -> GPUVec<T> {
let usage = buf_type.to_wgpu();
GPUVec {
dirty: true,
len: data.len(),
usage,
buffer: None,
data: Some(data),
}
}
pub fn new_empty(buf_type: BufferType, _alloc_type: AllocationType) -> GPUVec<T> {
let usage = buf_type.to_wgpu();
GPUVec {
dirty: false,
len: 0,
usage,
buffer: None,
data: Some(Vec::new()),
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn len(&self) -> usize {
if self.dirty {
match self.data {
Some(ref d) => d.len(),
None => panic!("This should never happen."),
}
} else {
self.len
}
}
#[inline]
pub fn data_mut(&mut self) -> &mut Option<Vec<T>> {
self.dirty = true;
&mut self.data
}
#[inline]
pub fn data(&self) -> &Option<Vec<T>> {
&self.data
}
#[inline]
pub fn is_on_gpu(&self) -> bool {
self.buffer.is_some()
}
#[inline]
pub fn dirty(&self) -> bool {
self.dirty
}
#[inline]
pub fn trash(&self) -> bool {
self.dirty
}
#[inline]
pub fn is_on_ram(&self) -> bool {
self.data.is_some()
}
#[inline]
pub fn buffer(&self) -> Option<&wgpu::Buffer> {
self.buffer.as_ref()
}
#[inline]
pub fn usage(&self) -> wgpu::BufferUsages {
self.usage
}
#[inline]
pub fn load_to_gpu(&mut self) {
let ctxt = Context::get();
if let Some(ref data) = self.data {
if data.is_empty() {
return;
}
let bytes = bytemuck::cast_slice(data);
if !self.is_on_gpu() {
self.len = data.len();
let buffer = ctxt.create_buffer_init(
Some("GPUVec buffer"),
bytes,
self.usage | wgpu::BufferUsages::COPY_DST,
);
self.buffer = Some(buffer);
} else if self.dirty {
self.len = data.len();
if let Some(ref buffer) = self.buffer {
let buffer_size = buffer.size() as usize;
let data_size = bytes.len();
if data_size <= buffer_size {
ctxt.write_buffer(buffer, 0, bytes);
} else {
let new_buffer = ctxt.create_buffer_init(
Some("GPUVec buffer"),
bytes,
self.usage | wgpu::BufferUsages::COPY_DST,
);
self.buffer = Some(new_buffer);
}
}
}
}
self.dirty = false;
}
#[inline]
pub fn ensure_on_gpu(&mut self) -> Option<&wgpu::Buffer> {
self.load_to_gpu();
self.buffer.as_ref()
}
#[inline]
pub fn unload_from_gpu(&mut self) {
self.len = self.len();
self.buffer = None;
self.dirty = false;
}
#[inline]
pub fn unload_from_ram(&mut self) {
if self.dirty && self.is_on_gpu() {
self.load_to_gpu();
}
self.data = None;
}
}
impl<T: Clone + Pod + Zeroable> GPUVec<T> {
#[inline]
pub fn to_owned(&self) -> Option<Vec<T>> {
self.data.as_ref().cloned()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BufferType {
Array,
ElementArray,
}
impl BufferType {
#[inline]
pub fn to_wgpu(self) -> wgpu::BufferUsages {
match self {
BufferType::Array => wgpu::BufferUsages::VERTEX,
BufferType::ElementArray => wgpu::BufferUsages::INDEX,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AllocationType {
StaticDraw,
DynamicDraw,
StreamDraw,
}