use glin::Result;
use rin_gl::types::*;
use std::mem;
use glin::buffer::Cast;
use glin::BufferRange;
use rin_gl as gl;
use rin_math::{Pnt2, pnt2};
use color::consts::*;
use color::*;
use itertools::Itertools;
type Index = usize;
type Arena<T> = densevec::DenseVec<T>;
#[derive(Debug, Clone, Copy)]
pub struct BufferRef{
idx: Index
}
#[derive(Clone, Debug)]
struct Segment {
range: ::std::ops::Range<usize>,
}
impl Segment{
fn split(self, offset: usize) -> (Segment, Segment){
let s1 = Segment{
range: self.range.start .. self.range.start + offset,
};
let s2 = Segment{
range: self.range.start + offset .. self.range.end,
};
(s1, s2)
}
fn join(self, other: Segment) -> ::std::result::Result<Segment, (Segment, Segment)>{
if self.range.end == other.range.start {
Ok(Segment{
range: self.range.start .. other.range.end,
})
}else if self.range.start == other.range.end {
Ok(Segment{
range: other.range.start .. self.range.end,
})
}else{
Err((self, other))
}
}
fn start(&self) -> usize{
self.range.start
}
fn end(&self) -> usize{
self.range.end
}
fn len(&self) -> usize{
self.range.len()
}
}
pub trait InternalCreation<B>{
fn create<C>(gl: &C, initial_size: usize, flags: GLbitfield, target: GLenum) -> Result<B>
where C: glin::CreationContext;
}
#[cfg(not(any(feature="gles", feature="webgl")))]
impl InternalCreation<glin::SharedBufferStorage<u8>> for Allocator<glin::SharedBufferStorage<u8>>{
fn create<C>(gl: &C, initial_size: usize, flags: GLbitfield, _target: GLenum) -> Result<glin::SharedBufferStorage<u8>>
where C: glin::CreationContext
{
gl.new_shared_buffer().create_immutable(initial_size, flags)
}
}
impl InternalCreation<glin::SharedBuffer<u8>> for Allocator<glin::SharedBuffer<u8>>{
fn create<C>(gl: &C, initial_size: usize, flags: GLbitfield, target: GLenum) -> Result<glin::SharedBuffer<u8>>
where C: glin::CreationContext
{
gl.new_shared_buffer().create_target(initial_size, flags, target)
}
}
pub trait Creation<B>{
fn new<C>(gl: &C, initial_size: usize, flags: GLbitfield, target: GLenum) -> Result<Allocator<B>>
where C: glin::CreationContext;
}
impl<B, I: InternalCreation<B>> Creation<B> for I{
fn new<C>(gl: &C, initial_size: usize, flags: GLbitfield, target: GLenum) -> Result<Allocator<B>>
where C: glin::CreationContext
{
let mut segments = Arena::new();
let free_segment = segments.insert_key_gen(Segment{ range: 0 .. initial_size });
Ok(Allocator{
buffer: Self::create(gl, initial_size, flags, target)?,
segments,
free_segments: vec![free_segment],
creation_flags: flags,
target,
})
}
}
pub trait Updater{
fn update<C, T>(&mut self, gl: &C, buffer: &BufferRef, data: &[T]) -> Result<()>
where T: 'static,
C: glin::CreationContext ;
}
#[cfg(not(any(feature="gles", feature="webgl")))]
impl Updater for Allocator<glin::SharedBufferStorage<u8>>{
fn update<C, T>(&mut self, gl: &C, buffer: &BufferRef, data: &[T]) -> Result<()>
where T: 'static,
C: glin::CreationContext
{
let bytes = data.len() * mem::size_of::<T>();
let segment = &self.segments[buffer.idx];
if segment.len() != bytes {
self.reallocate(buffer, gl, bytes)?;
}
if self.creation_flags & gl::DYNAMIC_STORAGE_BIT != 0{
self.buffer(buffer).cast().update(data);
}else{
let staging = gl.new_shared_buffer()
.immutable_from_data(data, 0)
.unwrap();
let mut buff_t: glin::buffer::Range<T,_,_> = self.buffer(buffer).cast().range(..data.len());
staging.copy_to(&mut buff_t);
}
Ok(())
}
}
impl Updater for Allocator<glin::SharedBuffer<u8>>{
fn update<C, T>(&mut self, gl: &C, buffer: &BufferRef, data: &[T]) -> Result<()>
where T: 'static,
C: glin::CreationContext
{
let bytes = data.len() * mem::size_of::<T>();
let segment = &self.segments[buffer.idx];
if segment.len() != bytes {
self.reallocate(buffer, gl, bytes)?;
}
if self.creation_flags & gl::STATIC_DRAW == 0{
self.buffer(buffer).cast().update(data);
}else{
let staging = gl.new_shared_buffer()
.from_data_target(data, gl::STATIC_DRAW, self.target)
.unwrap();
let mut buff_t: glin::buffer::Range<T,_,_> = self.buffer(buffer).cast().range(..data.len());
staging.copy_to(&mut buff_t);
}
Ok(())
}
}
#[cfg(not(any(feature="gles", feature="webgl")))]
type DefaultStore = glin::SharedBufferStorage<u8>;
#[cfg(any(feature="gles", feature="webgl"))]
type DefaultStore = glin::SharedBuffer<u8>;
pub struct Allocator<B = DefaultStore>{
buffer: B,
segments: Arena<Segment>,
free_segments: Vec<Index>,
creation_flags: GLbitfield,
target: GLenum,
}
#[cfg(not(any(feature="gles", feature="webgl")))]
impl Allocator<glin::SharedBufferStorage<u8>>{
pub fn new_storage<C>(gl: &C, initial_size: usize, flags: GLbitfield, target: GLenum) -> Result<Allocator<glin::SharedBufferStorage<u8>>>
where C: glin::CreationContext
{
let mut segments = Arena::new();
let free_segment = segments.insert_key_gen(Segment{ range: 0 .. initial_size });
Ok(Allocator{
buffer: gl.new_shared_buffer().create_immutable(initial_size, flags)?,
segments,
free_segments: vec![free_segment],
creation_flags: flags,
target,
})
}
}
impl Allocator<glin::SharedBuffer<u8>>{
pub fn new_buffer<C>(gl: &C, initial_size: usize, flags: GLbitfield, target: GLenum) -> Result<Allocator<glin::SharedBuffer<u8>>>
where C: glin::CreationContext
{
let mut segments = Arena::new();
let free_segment = segments.insert_key_gen(Segment{ range: 0 .. initial_size });
Ok(Allocator{
buffer: gl.new_shared_buffer().create_target(initial_size, flags, target)?,
segments,
free_segments: vec![free_segment],
creation_flags: flags,
target,
})
}
}
#[cfg(not(feature="webgl"))]
pub trait MapRange<T>: glin::buffer::WithMapRange<T>{}
#[cfg(not(feature="webgl"))]
impl<T, M: glin::buffer::WithMapRange<T>> MapRange<T> for M{}
#[cfg(feature="webgl")]
pub trait MapRange<T>{}
#[cfg(feature="webgl")]
impl<T: 'static> MapRange<T> for glin::SharedBuffer<T>{}
#[cfg(feature="webgl")]
impl<T: 'static> MapRange<T> for glin::Buffer<T>{}
impl<B> Allocator<B>
where B: glin::BufferRange<u8>
+ Clone
+ glin::buffer::WithBackend
+ glin::buffer::WithBackendMut
+ MapRange<u8>,
Self: InternalCreation<B>
{
fn reallocate<C>(&mut self, buffer: &BufferRef, gl: &C, len: usize) -> Result<()>
where C: glin::CreationContext
{
let segment = &self.segments[buffer.idx];
if segment.len() == len {
return Ok(());
}else if segment.len() > len {
let (s1, s2) = segment.clone().split(len);
self.segments[buffer.idx] = s1;
if let Some(contiguous_idx) = self.free_segments.iter()
.find(|idx| self.segments[**idx].start() == s2.end())
{
let contiguous = &mut self.segments[*contiguous_idx];
*contiguous = s2.join(contiguous.clone()).unwrap();
}else{
let idx = self.segments.insert_key_gen(s2);
self.free_segments.push(idx);
}
Ok(())
}else{
if let Some((free_pos, next_idx)) = self.free_segments.iter().enumerate()
.find(|(_free_pos, idx)| self.segments[**idx].start() == segment.end() ||
self.segments[**idx].end() == segment.start())
{
let diff = segment.len() as isize + self.segments[*next_idx].len() as isize - len as isize;
if diff == 0 {
let next = self.segments.remove(*next_idx).unwrap();
self.segments[buffer.idx] = self.segments[buffer.idx].clone().join(next).unwrap();
self.free_segments.remove(free_pos);
return Ok(());
}else if diff > 0{
if self.segments[*next_idx].start() >= self.segments[buffer.idx].start(){
let diff = self.segments[*next_idx].len() - diff as usize;
let (s1, s2) = self.segments[*next_idx].clone().split(diff);
self.segments[buffer.idx] = self.segments[buffer.idx].clone().join(s1).unwrap();
self.segments[*next_idx] = s2;
return Ok(())
}else{
let (s1, s2) = self.segments[*next_idx].clone().split(diff as usize);
self.segments[buffer.idx] = s2.join(self.segments[buffer.idx].clone()).unwrap();
self.segments[*next_idx] = s1;
return Ok(())
}
}
}
let start = self.segments[buffer.idx].start();
let end = self.segments[buffer.idx].end();
if let Some(contiguous_idx) = self.free_segments.iter()
.find(|idx| self.segments[**idx].start() == end)
{
self.segments[*contiguous_idx].range.start = start;
self.segments[buffer.idx].range.end = start;
}
let new_buffer = self._allocate(gl, len, Some(buffer.idx))?;
assert_eq!(buffer.idx, new_buffer.idx);
Ok(())
}
}
pub fn allocate<C>(&mut self, gl: &C, len: usize) -> Result<BufferRef>
where C: glin::CreationContext
{
self._allocate(gl, len, None)
}
fn _allocate<C>(&mut self, gl: &C, len: usize, replace_segment: Option<usize>) -> Result<BufferRef>
where C: glin::CreationContext
{
let free_segment = self.free_segments.iter().enumerate().filter_map(|(pos, idx)| {
let segment = &self.segments[*idx];
if segment.len() >= len {
Some((pos, *idx, segment))
}else{
None
}
}).min_by_key(|(_pos, _idx, segment)| {
segment.len()
});
if let Some((free_pos, segment_idx, free_segment)) = free_segment{
if free_segment.len() == len {
log::trace!("Found exact len, just using free segment");
self.free_segments.remove(free_pos);
if let Some(replace_segment) = replace_segment {
self.segments.swap(replace_segment, segment_idx);
Ok(BufferRef{
idx: replace_segment
})
}else{
Ok(BufferRef{
idx: segment_idx
})
}
}else{
log::trace!("Found non exact len, splitting free segment");
let (s1, s2) = free_segment.clone().split(len);
if let Some(contiguous_idx) = self.free_segments.iter()
.find(|idx| self.segments[**idx].start() == s2.end())
{
let contiguous = &mut self.segments[*contiguous_idx];
log::trace!("Found segment contiguous to remainder at {}, joining", contiguous.start());
*contiguous = s2.join(contiguous.clone()).unwrap();
log::trace!("Joined remanider and contiguous {} .. {}", contiguous.start(), contiguous.end());
self.free_segments.remove(free_pos);
if let Some(replace_segment) = replace_segment {
self.segments.swap(replace_segment, segment_idx);
Ok(BufferRef{
idx: replace_segment
})
}else{
Ok(BufferRef{
idx: segment_idx
})
}
}else{
log::trace!("Not found any contiguous, just inserting remainder {} .. {} as free", s2.start(), s2.end());
self.segments[segment_idx] = s2;
if let Some(replace_segment) = replace_segment {
self.segments[replace_segment] = s1;
Ok(BufferRef{
idx: replace_segment
})
}else{
log::trace!("Creating new {} .. {}", s1.start(), s1.end());
let idx = self.segments.insert_key_gen(s1);
Ok(BufferRef{
idx
})
}
}
}
}else{
let prev_capacity = self.buffer.capacity();
if let Some(segment_at_end_idx) = self.free_segments.iter()
.find(|idx| self.segments[**idx].end() == prev_capacity)
{
let last_segment = self.segments[*segment_at_end_idx].len();
let needs = prev_capacity - last_segment + len;
#[cfg(feature="gl_allocator_exponential")]
let new_capacity = {
let mut new_capacity = prev_capacity as f32 * 1.5;
while new_capacity < needs as f32 {
new_capacity *= 1.5;
}
new_capacity as usize
};
#[cfg(not(feature="gl_allocator_exponential"))]
let new_capacity = needs;
log::trace!("Reallocating gpu buffers reusing last chunk to {}", human(new_capacity));
let new_buffer = Self::create(gl, new_capacity, self.creation_flags, self.target)
.map_err(|err| glin::Error::with_cause(err.kind(), "Error reallocating buffer - last", err))?;
self.buffer.copy_to(&mut new_buffer.clone().into_range(..self.buffer.len()));
self.buffer = new_buffer;
self.segments[*segment_at_end_idx].range.end = new_capacity;
self._allocate(gl, len, replace_segment)
}else{
let needs = prev_capacity + len;
#[cfg(feature="gl_allocator_exponential")]
let new_capacity = {
let mut new_capacity = prev_capacity as f32 * 1.5;
while new_capacity < needs as f32 {
new_capacity *= 1.5;
}
new_capacity as usize
};
#[cfg(not(feature="gl_allocator_exponential"))]
let new_capacity = needs;
log::trace!("Reallocating gpu buffers to {}", human(new_capacity));
let new_buffer = Self::create(gl, new_capacity, self.creation_flags, self.target)
.map_err(|err| glin::Error::with_cause(err.kind(), "Error reallocating buffer", err))?;
self.buffer.copy_to(&mut new_buffer.clone().into_range(..self.buffer.len()));
self.buffer = new_buffer;
let segment = Segment{
range: prev_capacity .. needs,
};
if let Some(replace_segment) = replace_segment {
self.segments[replace_segment] = segment;
Ok(BufferRef{
idx: replace_segment
})
}else{
let idx = self.segments.insert_key_gen(segment);
Ok(BufferRef{
idx
})
}
}
}
}
pub fn from_data<C, T>(&mut self, gl: &C, data: &[T]) -> Result<BufferRef>
where C: glin::CreationContext,
T: 'static,
Self: Updater,
{
let bytes = data.len() * mem::size_of::<T>();
let mut buffer = self.allocate(gl, bytes)?;
self.update_range(gl, &mut buffer, data)?;
Ok(buffer)
}
pub fn buffer(&self, bufferref: &BufferRef) -> glin::buffer::Range<u8, B, B>{
let segment = &self.segments[bufferref.idx];
self.buffer.clone().into_range(segment.range.clone())
}
pub fn cast_buffer<T>(&self, bufferref: &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>,
<B as glin::buffer::Cast<T>>::CastTo: glin::BufferRange<T>
{
let segment = &self.segments[bufferref.idx];
let buffer = glin::buffer::cast::<T, _>(self.buffer.clone());
let start = segment.range.start / mem::size_of::<T>();
let end = segment.range.end / mem::size_of::<T>();
buffer.into_range(start .. end)
}
pub fn full_buffer(&self) -> &B{
&self.buffer
}
pub fn update_range<C, T>(&mut self, gl: &C, bufferref: &BufferRef, data: &[T]) -> Result<()>
where T: 'static,
C: glin::CreationContext,
Self: Updater,
{
self.update(gl, bufferref, data)
}
pub fn free(&mut self, bufferref: BufferRef){
let end = self.segments[bufferref.idx].end();
if let Some(contiguous_idx) = self.free_segments.iter()
.find(|idx| self.segments[**idx].start() == end)
{
let segment = self.segments.remove(bufferref.idx).unwrap();
let contiguous = &mut self.segments[*contiguous_idx];
*contiguous = segment.join(contiguous.clone()).unwrap();
}else{
self.free_segments.push(bufferref.idx)
}
}
pub fn gl_debug(&self, gl: &gl::Renderer, pos: &Pnt2, w: f32, h: f32){
let gl = gl.with_properties(&[
gl::Property::Blend(true),
gl::Property::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA),
]);
let total_capacity = self.buffer.capacity();
gl.draw_rectangle_fill(pos, w, h, &rgba!(&GRAY, 0.5));
for (idx, segment) in self.segments.iter().sorted_by_key(|(_, segment)| segment.start()) {
let free = self.free_segments.iter().find(|free| **free == idx).is_some();
let segment_color = if free { rgba!(&WHITE, 0.1) } else { rgba!(&WHITE, 0.8) };
let segment_x = pos.x + segment.start() as f32 / total_capacity as f32 * w;
let segment_w = segment.len() as f32 / total_capacity as f32 * w;
let segment_pos = pnt2(segment_x, pos.y);
gl.draw_rectangle_fill(&segment_pos, segment_w, h, &segment_color);
gl.draw_rectangle_lines(&segment_pos, segment_w, h, &BLACK);
let text = format!("{}: {}", idx, human(segment.len()));
if text.len() * 8 < segment_w as usize{
let text_x = segment_x + segment_w / 2. - (text.len() * 8 / 2) as f32;
let pos = pnt2(text_x, pos.y + h / 2. + 4.);
gl.draw_bitmap_string(&text, &pos, &BLACK);
}else{
let text = format!("{}", idx);
if text.len() * 8 < segment_w as usize{
let text_x = segment_x + segment_w / 2. - (text.len() * 8 / 2) as f32;
let pos = pnt2(text_x, pos.y + h / 2. + 4.);
gl.draw_bitmap_string(&text, &pos, &BLACK);
}
}
}
}
}
pub fn human(bytes: usize) -> String{
if bytes < 1024 {
bytes.to_string() + "b"
}else if bytes < 1024 * 1024{
format!("{:#.01}Kb", bytes as f32 / 1024 as f32)
}else if bytes < 1024 * 1024 * 1024{
format!("{:#.03}Mb", bytes as f32 / (1024 * 1024) as f32)
}else{
format!("{:#.03}Gb", bytes as f32 / (1024 * 1024 * 1024) as f32)
}
}