use std::collections::HashMap;
use std::any;
use super::*;
pub struct SharedState {
pub scissor: Option<Bounds2<i32>>,
pub blend_mode: BlendMode,
pub depth_test: Option<Compare>,
pub cull_mode: Option<CullMode>,
}
struct DrawData {
vertices: VertexBuffer,
indices: IndexBuffer,
}
trait IDrawBuffer: any::Any {
fn clear(&mut self);
fn data(&self, g: &mut Graphics) -> DrawData;
fn draw(&self, g: &mut Graphics);
fn draw_range(&self, g: &mut Graphics, range: ops::Range<usize>, data: &DrawData);
fn commands_len(&self) -> usize;
fn shared_state(&self) -> SharedState;
}
impl dyn IDrawBuffer {
#[inline]
fn downcast_mut<V: TVertex, U: TUniform + 'static>(&mut self) -> Option<&mut DrawBuilder<V, U>> {
(self as &mut dyn any::Any).downcast_mut::<DrawBuilder<V, U>>()
}
}
impl<T: TVertex, U: TUniform + 'static> IDrawBuffer for DrawBuilder<T, U> {
fn clear(&mut self) {
self.clear();
}
fn data(&self, g: &mut Graphics) -> DrawData {
let vertices = g.vertex_buffer(&self.vertices, BufferUsage::Static).unwrap();
let indices = g.index_buffer(&self.indices, self.vertices.len() as u16, BufferUsage::Static).unwrap();
DrawData { vertices, indices }
}
fn draw(&self, g: &mut Graphics) {
draw(self, g)
}
fn draw_range(&self, g: &mut Graphics, range: ops::Range<usize>, data: &DrawData) {
let batch = DrawBatch {
commands: &self.commands[range],
vertices: data.vertices,
indices: data.indices,
};
draw_range(self, g, &batch)
}
fn commands_len(&self) -> usize {
self.commands.len()
}
fn shared_state(&self) -> SharedState {
SharedState {
scissor: self.scissor,
blend_mode: self.blend_mode,
depth_test: self.depth_test,
cull_mode: self.cull_mode,
}
}
}
pub struct DrawBatch<'a> {
pub commands: &'a [drawbuf::DrawCommand],
pub vertices: VertexBuffer,
pub indices: IndexBuffer,
}
fn draw<V: TVertex, U: TUniform>(this: &DrawBuilder<V, U>, g: &mut Graphics) {
let vertices = g.vertex_buffer(&this.vertices, BufferUsage::Static).unwrap();
let indices = g.index_buffer(&this.indices, this.vertices.len() as u16, BufferUsage::Static).unwrap();
let range = DrawBatch {
commands: &this.commands,
vertices,
indices,
};
draw_range(this, g, &range);
g.release(indices);
g.release(vertices);
}
fn draw_range<V: TVertex, U: TUniform>(this: &DrawBuilder<V, U>, g: &mut Graphics, batch: &DrawBatch) {
for cmd in batch.commands {
let uniforms = &this.uniforms[cmd.pipeline_state.uniform_index as usize];
g.draw_indexed(&DrawIndexedArgs {
scissor: cmd.pipeline_state.scissor,
blend_mode: cmd.pipeline_state.blend_mode,
depth_test: cmd.pipeline_state.depth_test,
cull_mode: cmd.pipeline_state.cull_mode,
mask: cmd.pipeline_state.mask,
prim_type: cmd.pipeline_state.prim_type,
shader: cmd.pipeline_state.shader,
uniforms: &[uniforms],
vertices: &[DrawVertexBuffer {
buffer: batch.vertices,
divisor: VertexDivisor::PerVertex,
}],
indices: batch.indices,
index_start: cmd.index_start,
index_end: cmd.index_end,
instances: -1,
});
}
}
struct DrawSub {
id: any::TypeId,
range: ops::Range<usize>,
}
#[derive(Default)]
pub struct DrawPool {
pool: HashMap<any::TypeId, Box<dyn IDrawBuffer>>,
subs: Vec<DrawSub>,
}
impl DrawPool {
#[inline]
pub fn new() -> DrawPool {
DrawPool {
pool: HashMap::new(),
subs: Vec::new(),
}
}
pub fn clear(&mut self) {
for buf in self.pool.values_mut() {
buf.clear();
}
self.subs.clear();
}
pub fn get<V: TVertex, U: TUniform + 'static>(&mut self) -> &mut DrawBuilder<V, U> {
let mut shared_state = None;
if let Some(last) = self.subs.last_mut() {
let buf = self.pool.get_mut(&last.id).unwrap();
if let Some(buf) = buf.downcast_mut::<V, U>() {
return unsafe { &mut *(buf as *mut DrawBuilder<V, U>) };
}
last.range.end = buf.commands_len();
shared_state = Some(buf.shared_state());
}
let id = any::TypeId::of::<DrawBuilder<V, U>>();
let buf = self.pool.entry(id)
.or_insert_with(|| {
let new_buf = DrawBuilder::<V, U>::new();
Box::new(new_buf) as Box<dyn IDrawBuffer>
});
let buf = buf.downcast_mut::<V, U>().unwrap();
buf.auto_state_tracking = false;
if let Some(shared_state) = shared_state {
buf.scissor = shared_state.scissor;
buf.blend_mode = shared_state.blend_mode;
buf.depth_test = shared_state.depth_test;
buf.cull_mode = shared_state.cull_mode;
}
let cmd_len = buf.commands_len();
let range = cmd_len..cmd_len;
self.subs.push(DrawSub { id, range });
return buf;
}
pub fn draw(&mut self, g: &mut Graphics) {
if let Some(last) = self.subs.last_mut() {
if let Some(buf) = self.pool.get_mut(&last.id) {
last.range.end = buf.commands_len();
}
}
let data: HashMap<any::TypeId, DrawData> = self.pool.iter().map(|(&id, buf)| {
(id, buf.data(g))
}).collect();
for sub in &self.subs {
let buf = self.pool.get(&sub.id).unwrap();
let data = data.get(&sub.id).unwrap();
buf.draw_range(g, sub.range.clone(), data);
}
for data in data.values() {
g.release(data.indices);
g.release(data.vertices);
}
}
pub fn draw_unordered(&mut self, g: &mut Graphics) {
for buf in self.pool.values() {
buf.draw(g);
}
}
}