use std::rc::Rc;
use std::cell::{RefCell, Ref, RefMut};
use std::marker::PhantomData;
use std::ops::Range;
use crate::geometry::{Geometry, Submesh};
use super::allocator::{self, Creation, InternalCreation, Updater};
use rin::gl::{self, types::*};
use glin::MapWriteFlags;
use itertools::Either;
use na::Mat4;
use std::borrow::Cow;
use std::mem;
use glin::buffer::Cast;
use densevec::KeyedDenseVec;
#[derive(Debug, Clone, Copy)]
pub struct BufferRef{
buffer: allocator::BufferRef,
dynamic: bool,
}
pub const fn storage_static_flags() -> GLbitfield{
0
}
#[cfg(feature="gl_persistent_map")]
pub const fn storage_dynamic_flags() -> GLbitfield{
gl::MAP_WRITE_BIT | gl::MAP_PERSISTENT_BIT | gl::MAP_COHERENT_BIT
}
#[cfg(not(feature="gl_persistent_map"))]
pub const fn storage_dynamic_flags() -> GLbitfield{
gl::DYNAMIC_STORAGE_BIT
}
pub const fn buffer_static_usage() -> GLenum{
gl::STATIC_DRAW
}
pub const fn buffer_dynamic_usage() -> GLenum{
gl::DYNAMIC_DRAW
}
fn map_flags() -> MapWriteFlags{
if storage_dynamic_flags() & gl::MAP_COHERENT_BIT != 0 {
MapWriteFlags::COHERENT
}else{
MapWriteFlags::empty()
}
}
pub fn model_normal_formats() -> Vec<glin::attributes::Format>{
vec![
gl::attributes::MatFormat{
name: Cow::Borrowed("model"),
location: 5,
cols: 4,
rows: 4,
offset_in_vertex: 0,
}.into_row_formats(),
gl::attributes::MatFormat{
name: Cow::Borrowed("normal"),
location: 9,
cols: 4,
rows: 4,
offset_in_vertex: 16 * mem::size_of::<f32>(),
}.into_row_formats(),
].into_iter()
.flat_map(|f| f)
.collect::<Vec<_>>()
}
pub fn model_format() -> Vec<glin::attributes::Format>{
gl::attributes::MatFormat{
name: Cow::Borrowed("model"),
location: 5,
cols: 4,
rows: 4,
offset_in_vertex: 0,
}.into_row_formats()
}
pub fn material_offsets_format() -> glin::attributes::Format{
gl::attributes::Format{
name: Cow::Borrowed("material_offset"),
location: 4,
num_coords: 1,
src_type: glin::attributes::Type::U32,
dst_type: glin::attributes::Type::U32,
normalize: false,
offset_in_vertex: 0,
}
}
#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub struct VaoId{
dynamic: bool,
indices: bool,
}
#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)]
struct VaoModelBufferId{
vao_id: VaoId,
buffer_id: u32,
}
impl densevec::Key for VaoId{
fn to_usize(self) -> usize{
(self.dynamic as u8 | ((self.indices as u8) << 1)) as usize
}
fn from_usize(id: usize) -> VaoId{
let dynamic = (id & 0x1) != 0;
let indices = ((id >> 1) & 0x1) != 0;
VaoId{
dynamic,
indices,
}
}
}
impl densevec::Key for VaoModelBufferId{
fn to_usize(self) -> usize{
(self.buffer_id << 2) as usize | self.vao_id.to_usize()
}
fn from_usize(id: usize) -> VaoModelBufferId{
let vao_id = VaoId::from_usize(id);
let buffer_id = (id >> 2) as u32;
VaoModelBufferId{
vao_id,
buffer_id
}
}
}
#[derive(Clone, Debug)]
pub struct VaoRange{
indices_range: Option<allocator::BufferRef>,
vertices_range: allocator::BufferRef,
subrange: Option<Range<usize>>,
vao_id: VaoId,
}
impl VaoRange{
pub fn vao_id(&self) -> VaoId{
self.vao_id
}
pub fn has_indices(&self) -> bool{
self.vao_id.indices
}
}
#[derive(Clone)]
pub struct VaoRangeInfo{
pub range: Range<usize>,
pub base_vertex: Option<i32>,
}
pub type IndexAllocator<B> = allocator::Allocator<B>;
pub struct VaoCache<T>{
vaos: KeyedDenseVec<VaoId, glin::Vao>,
shadow_vaos: KeyedDenseVec<VaoModelBufferId, glin::Vao>,
marker: PhantomData<T>,
}
impl<T> VaoCache<T>{
pub fn new() -> VaoCache<T>{
VaoCache{
vaos: KeyedDenseVec::new(),
shadow_vaos: KeyedDenseVec::new(),
marker: PhantomData,
}
}
pub fn vao<C,B,I>(&mut self,
gl: &C,
vao_id: VaoId,
vertex_buffer: &B,
indices_buffer: Option<&I>,
model_buffer: Either<&glin::SharedBuffer<Mat4>, &glin::SharedBuffer<(Mat4,Mat4)>>,
material_offsets_buffer: Option<&glin::SharedBuffer<u32>>) -> &mut glin::Vao
where C: glin::CreationContext,
T: 'static + glin::VertexFormat,
B: 'static + glin::BufferRange<T> + Clone,
I: 'static + glin::BufferRangeMut<glin::IndexT> + Clone,
{
let insert_new = ||{
let bindings = super::default_attribute_bindings();
let mut vao_builder = gl.new_vao()
.bindings_from(&bindings, vertex_buffer.clone());
vao_builder = match model_buffer {
Either::Left(model_buffer) => vao_builder
.instance_attributes_from(model_format(), model_buffer.clone(), 1),
Either::Right(model_normal_buffer) => vao_builder
.instance_attributes_from(model_normal_formats(), model_normal_buffer.clone(), 1),
};
if let Some(indices_buffer) = indices_buffer{
vao_builder = vao_builder.indices(indices_buffer.clone());
}
if let Some(offsets) = material_offsets_buffer{
let formats = material_offsets_format();
vao_builder = vao_builder.instance_attributes_from(formats, offsets.clone(), 1);
}
vao_builder.create().unwrap()
};
if let Either::Left(model_buffer) = model_buffer {
let buffer_id = model_buffer.id();
self.shadow_vaos.entry(VaoModelBufferId{buffer_id, vao_id}).or_insert_with(insert_new)
}else{
self.vaos.entry(vao_id).or_insert_with(insert_new)
}
}
}
pub trait AllocatorFlags{
fn static_flags() -> u32;
fn dynamic_flags() -> u32;
}
impl<T> AllocatorFlags for Allocator<T,glin::SharedBufferStorage<u8>>{
fn static_flags() -> u32{
storage_static_flags()
}
fn dynamic_flags() -> u32{
storage_dynamic_flags()
}
}
impl<T> AllocatorFlags for Allocator<T,glin::SharedBuffer<u8>>{
fn static_flags() -> u32{
buffer_static_usage()
}
fn dynamic_flags() -> u32{
buffer_dynamic_usage()
}
}
pub struct Allocator<T, B>{
static_allocator: Option<allocator::Allocator<B>>,
dynamic_allocator: Option<allocator::Allocator<B>>,
indices: Rc<RefCell<IndexAllocator<B>>>,
vaos_cache: VaoCache<T>,
bytes: usize,
}
impl<T, B> Allocator<T,B>{
pub fn new(bytes: usize, indices: Rc<RefCell<IndexAllocator<B>>>) -> Allocator<T,B>{
Allocator{
static_allocator: None,
dynamic_allocator: None,
indices,
vaos_cache: VaoCache::new(),
bytes,
}
}
}
pub trait BufferExt<T>: glin::BufferRange<T> +
glin::buffer::WithBackend +
glin::buffer::WithBackendMut +
glin::BufferRangeMut<T> +
glin::buffer::MapRange<T> +
glin::buffer::MapRangeMut<T>
{}
impl<T: 'static> BufferExt<T> for glin::Buffer<T>{}
impl<T: 'static> BufferExt<T> for glin::SharedBuffer<T>{}
impl<T: 'static> BufferExt<T> for glin::BufferStorage<T>{}
impl<T: 'static> BufferExt<T> for glin::SharedBufferStorage<T>{}
impl<T, B> Allocator<T,B>
where T: 'static + Clone + glin::VertexFormat,
allocator::Allocator<B>: InternalCreation<B> + Creation<B> + Updater,
B: 'static + Clone +
BufferExt<u8> +
glin::buffer::Cast<glin::IndexT>,
<B as glin::buffer::Cast<glin::IndexT>>::CastTo: 'static + glin::BufferRangeMut<glin::IndexT> + Clone,
Self: AllocatorFlags,
{
fn indices_allocator_mut(&mut self) -> RefMut<allocator::Allocator<B>>{
self.indices.borrow_mut()
}
fn indices_allocator(&self) -> Ref<allocator::Allocator<B>>{
self.indices.borrow()
}
fn vertices_static_allocator<C: glin::CreationContext>(&mut self, gl: &C) -> glin::Result<&mut allocator::Allocator<B>>{
let bytes = self.bytes;
Ok(self.static_allocator.get_or_insert_with(||{
let target = gl::ARRAY_BUFFER;
allocator::Allocator::new(gl, bytes, Self::static_flags(), target).unwrap()
}))
}
fn vertices_dynamic_allocator<C: glin::CreationContext>(&mut self, gl: &C) -> glin::Result<&mut allocator::Allocator<B>>{
let bytes = self.bytes;
Ok(self.dynamic_allocator.get_or_insert_with(||{
let target = gl::ARRAY_BUFFER;
allocator::Allocator::new(gl, bytes, Self::dynamic_flags(), target).unwrap()
}))
}
pub fn vertices_static<C: glin::CreationContext>(&mut self, gl: &C, data: &[T]) -> glin::Result<BufferRef>{
Ok(BufferRef{
buffer: self.vertices_static_allocator(gl)
.and_then(|allocator| allocator.from_data(gl, data))?,
dynamic: false,
})
}
pub fn vertices_dynamic<C: glin::CreationContext>(&mut self, gl: &C, data: &[T]) -> glin::Result<BufferRef>{
Ok(BufferRef{
buffer: self.vertices_dynamic_allocator(gl)
.and_then(|allocator| allocator.from_data(gl, data))?,
dynamic: true
})
}
pub fn indices_static<C: glin::CreationContext>(&mut self, gl: &C, data: &[glin::IndexT]) -> glin::Result<BufferRef>{
Ok(BufferRef{
buffer: self.indices_allocator_mut().from_data(gl, data)?,
dynamic: false,
})
}
fn get_vao<C>(&mut self,
gl: &C,
vao_id: VaoId,
model_buffer: Either<&glin::SharedBuffer<Mat4>, &glin::SharedBuffer<(Mat4,Mat4)>>,
material_offsets_buffer: Option<&glin::SharedBuffer<u32>>) -> glin::Result<&mut glin::Vao>
where C: glin::CreationContext ,
B: glin::buffer::Cast<T>,
<B as glin::buffer::Cast<T>>::CastTo: glin::BufferRange<T> + Clone,
{
let vertex_buffer = if vao_id.dynamic{
self.vertices_dynamic_allocator(gl).unwrap().full_buffer().clone()
}else{
self.vertices_static_allocator(gl).unwrap().full_buffer().clone()
};
let vertex_buffer = glin::buffer::cast::<T,_>(vertex_buffer);
let indices_buffer = if vao_id.indices{
let indices_buffer = self.indices_allocator().full_buffer().clone();
let indices_buffer = glin::buffer::cast::<glin::IndexT,_>(indices_buffer);
Some(indices_buffer)
}else{
None
};
let vao = self.vaos_cache.vao(gl,
vao_id,
&vertex_buffer,
indices_buffer.as_ref(),
model_buffer,
material_offsets_buffer);
vao.set_attribute_buffer_at(0, vertex_buffer)?;
if vao_id.indices {
vao.set_indices_buffer(indices_buffer.unwrap())
}
Ok(vao)
}
pub fn vao<C>(&mut self,
gl: &C,
vao_id: VaoId,
model_normal_buffer: &glin::SharedBuffer<(Mat4, Mat4)>,
material_offsets_buffer: Option<&glin::SharedBuffer<u32>> ) -> glin::Result<&mut glin::Vao>
where C: glin::CreationContext,
B: glin::buffer::Cast<T>,
<B as glin::buffer::Cast<T>>::CastTo: glin::BufferRange<T> + Clone,
{
self.get_vao(gl, vao_id, Either::Right(model_normal_buffer), material_offsets_buffer)
}
pub fn shadow_vao<C>(&mut self,
gl: &C,
vao_id: VaoId,
model_buffer: &glin::SharedBuffer<Mat4>) -> glin::Result<&mut glin::Vao>
where C: glin::CreationContext,
B: glin::buffer::Cast<T>,
<B as glin::buffer::Cast<T>>::CastTo: glin::BufferRange<T> + Clone,
{
self.get_vao(gl, vao_id, Either::Left(model_buffer), None)
}
pub fn submesh_vaos(&mut self, gl: &gl::Renderer, geometry: &Geometry<T>, submeshes: Option<&[Submesh]>, dynamic: bool) ->
(Vec<VaoRange>, VaoRange, BufferRef, Option<BufferRef>)
{
let has_indices = !geometry.indices().is_empty();
let vao_id = VaoId{
dynamic,
indices: has_indices || submeshes.is_some(),
};
let vertices_range;
let indices_range;
let ranges;
let full_range;
if dynamic{
vertices_range = self.vertices_dynamic(gl, geometry.vertices()).unwrap();
}else{
vertices_range = self.vertices_static(gl, geometry.vertices()).unwrap();
}
if has_indices || submeshes.is_some() {
if let Some(submeshes) = submeshes {
let indices_vec = if geometry.indices().is_empty(){
submeshes.iter().flat_map(|submesh|{
submesh.iter().cloned()
}).collect()
}else{
geometry.indices().clone()
};
let mut base_index = 0;
indices_range = Some(self.indices_static(gl, &indices_vec).unwrap());
ranges = submeshes.iter().map(|submesh| {
let range = VaoRange{
indices_range: indices_range.map(|i| i.buffer),
vertices_range: vertices_range.buffer,
subrange: Some(base_index .. base_index + submesh.len()),
vao_id,
};
base_index += submesh.len();
range
}).collect();
full_range = VaoRange{
indices_range: indices_range.map(|i| i.buffer),
vertices_range: vertices_range.buffer,
subrange: None,
vao_id,
};
}else{
indices_range = Some(self.indices_static(gl, geometry.indices()).unwrap());
full_range = VaoRange{
indices_range: indices_range.map(|i| i.buffer),
vertices_range: vertices_range.buffer,
subrange: None,
vao_id: vao_id,
};
ranges = vec![full_range.clone()];
}
}else{
full_range = VaoRange{
vertices_range: vertices_range.buffer,
indices_range: None,
subrange: None,
vao_id,
};
ranges = vec![full_range.clone()];
indices_range = None;
}
(ranges, full_range, vertices_range, indices_range)
}
pub fn vao_range(&self, range: &VaoRange) -> VaoRangeInfo {
let vertex_buffer = if range.vao_id.dynamic{
self.dynamic_allocator.as_ref().unwrap().buffer(&range.vertices_range)
}else{
self.static_allocator.as_ref().unwrap().buffer(&range.vertices_range)
};
if range.has_indices() {
let base_vertex = (vertex_buffer.start() / mem::size_of::<T>()) as i32;
let indices = self.indices_allocator().buffer(range.indices_range.as_ref().unwrap());
let indices_range = if let Some(subrange) = range.subrange.as_ref(){
indices.start() / mem::size_of::<glin::IndexT>() + subrange.start ..
indices.start() / mem::size_of::<glin::IndexT>() + subrange.end
}else{
indices.start() / mem::size_of::<glin::IndexT>() ..
indices.end() / mem::size_of::<glin::IndexT>()
};
VaoRangeInfo{
range: indices_range,
base_vertex: Some(base_vertex),
}
}else{
let vertex_range = vertex_buffer.start() / mem::size_of::<T>() ..
vertex_buffer.end() / mem::size_of::<T>();
VaoRangeInfo{
range: vertex_range,
base_vertex: None,
}
}
}
pub fn command(&self, range: &VaoRange, base_instance: u32, instance_count: u32) -> glin::DrawElementsIndirectCommand{
let vertex_buffer = if range.vao_id.dynamic{
self.dynamic_allocator.as_ref().unwrap().buffer(&range.vertices_range)
}else{
self.static_allocator.as_ref().unwrap().buffer(&range.vertices_range)
};
if range.has_indices() {
let indices: glin::buffer::Range<glin::IndexT,_,_> = self.indices_allocator()
.buffer(range.indices_range.as_ref().unwrap())
.cast();
let indices_range = if let Some(subrange) = range.subrange.as_ref(){
indices.start() + subrange.start .. indices.start() + subrange.end
}else{
indices.start() .. indices.end()
};
let count = indices_range.len() as u32;
let base_vertex = (vertex_buffer.start() / mem::size_of::<T>()) as u32;
glin::DrawElementsIndirectCommand{
count,
instance_count,
first_index: indices_range.start as u32,
base_vertex,
base_instance,
}
}else{
let vertex_range = vertex_buffer.start() / mem::size_of::<T>() ..
vertex_buffer.end() / mem::size_of::<T>();
glin::DrawElementsIndirectCommand{
count: vertex_range.len() as u32,
instance_count,
first_index: vertex_range.start as u32,
base_vertex: base_instance,
base_instance: 0,
}
}
}
pub fn dynamic_vertex_buffer_range(&self, range: &BufferRef) -> glin::buffer::Range<T, <B as glin::buffer::Cast<T>>::CastTo, <B as glin::buffer::Cast<T>>::CastTo>
where B: glin::buffer::Cast<T>
{
self.dynamic_allocator.as_ref().unwrap().buffer(&range.buffer).cast()
}
pub fn update_vertex_buffer_range<C>(&mut self, gl: &C, range: &BufferRef, data: &[T]) -> glin::Result<()>
where C: glin::CreationContext
{
if range.dynamic {
self.dynamic_allocator.as_mut().unwrap().update_range(gl, &range.buffer, data)
}else{
self.static_allocator.as_mut().unwrap().update_range(gl, &range.buffer, data)
}
}
pub fn update_index_buffer_range<C>(&mut self, gl: &C, range: &BufferRef, data: &[glin::IndexT]) -> glin::Result<()>
where C: glin::CreationContext
{
self.indices_allocator_mut().update_range(gl, &range.buffer, data)
}
pub fn debug_draw(&self, gl: &gl::Renderer, pos: &na::Pnt2, w: f32, h: f32){
let mut pos = pos.clone();
if let Some(dyn_allocator) = self.dynamic_allocator.as_ref(){
dyn_allocator.gl_debug(gl, &pos, w, h);
pos.y += h + 10.;
}
if let Some(stc_allocator) = self.static_allocator.as_ref(){
stc_allocator.gl_debug(gl, &pos, w, h);
pos.y += h + 10.;
}
self.indices_allocator().gl_debug(gl, &pos, w, h);
}
}