#![deny(missing_docs)]
extern crate "gfx_device_gl" as device;
use std::mem;
use device::attrib;
use device::attrib::IntSize;
use device::draw::CommandBuffer;
use device::shade::{ProgramInfo, UniformValue, ShaderSource, Stage, CreateShaderError};
use device::target::{Rect, ClearData, Mask, Access, Target};
use render::batch::Batch;
use render::mesh::SliceKind;
use render::target::Plane;
pub mod mesh;
pub mod shade;
pub mod state;
pub mod target;
pub mod batch;
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum ProgramError {
Vertex(CreateShaderError),
Fragment(CreateShaderError),
Link(()),
}
const TRACKED_ATTRIBUTES: usize = 8;
type CachedAttribute = (device::RawBufferHandle, attrib::Format);
struct RenderState {
is_frame_buffer_set: bool,
frame: target::Frame,
is_array_buffer_set: bool,
program_name: device::back::Program,
index: Option<device::RawBufferHandle>,
attributes: [Option<CachedAttribute>; TRACKED_ATTRIBUTES],
draw: state::DrawState,
}
impl RenderState {
fn new() -> RenderState {
RenderState {
is_frame_buffer_set: false,
frame: target::Frame::new(0,0),
is_array_buffer_set: false,
program_name: 0,
index: None,
attributes: [None; TRACKED_ATTRIBUTES],
draw: state::DrawState::new(),
}
}
}
struct ParamStorage {
uniforms: Vec<UniformValue>,
blocks : Vec<device::RawBufferHandle>,
textures: Vec<shade::TextureParam>,
}
impl ParamStorage{
fn new() -> ParamStorage {
ParamStorage {
uniforms: Vec::new(),
blocks: Vec::new(),
textures: Vec::new(),
}
}
fn get_mut(&mut self) -> shade::ParamValues {
self.uniforms.truncate(0);
self.blocks.truncate(0);
self.textures.truncate(0);
shade::ParamValues {
uniforms: &mut self.uniforms,
blocks: &mut self.blocks,
textures: &mut self.textures,
}
}
}
trait CommandBufferHelper {
fn bind_target(&mut self, Access, Target, Option<&Plane>);
}
impl<C: CommandBuffer> CommandBufferHelper for C {
fn bind_target(&mut self, access: Access, to: Target,
plane: Option<&Plane>) {
match plane {
None => self.unbind_target(access, to),
Some(&Plane::Surface(ref suf)) =>
self.bind_target_surface(access, to, suf.get_name()),
Some(&Plane::Texture(ref tex, level, layer)) =>
self.bind_target_texture(access, to, tex.get_name(), level, layer),
}
}
}
pub struct Renderer<C: CommandBuffer> {
command_buffer: C,
data_buffer: device::draw::DataBuffer,
common_array_buffer: Result<device::ArrayBufferHandle, ()>,
draw_frame_buffer: device::FrameBufferHandle,
read_frame_buffer: device::FrameBufferHandle,
default_frame_buffer: device::FrameBufferHandle,
render_state: RenderState,
parameters: ParamStorage,
}
impl<C: CommandBuffer> Renderer<C> {
pub fn reset(&mut self) {
self.command_buffer.clear();
self.data_buffer.clear();
self.render_state = RenderState::new();
}
pub fn as_buffer(&self) -> (&C, &device::draw::DataBuffer) {
(&self.command_buffer, &self.data_buffer)
}
pub fn clone_empty(&self) -> Renderer<C> {
Renderer {
command_buffer: CommandBuffer::new(),
data_buffer: device::draw::DataBuffer::new(),
common_array_buffer: self.common_array_buffer,
draw_frame_buffer: self.draw_frame_buffer,
read_frame_buffer: self.read_frame_buffer,
default_frame_buffer: self.default_frame_buffer,
render_state: RenderState::new(),
parameters: ParamStorage::new(),
}
}
pub fn clear(&mut self, data: ClearData, mask: Mask, frame: &target::Frame) {
self.bind_frame(frame);
self.command_buffer.call_clear(data, mask);
}
pub fn draw<B: Batch>(&mut self, batch: &B, frame: &target::Frame) {
self.bind_frame(frame);
let (mesh, link, slice, program, state) = batch.get_data();
self.bind_program(batch, program);
self.bind_state(state);
self.bind_mesh(mesh, link, program.get_info());
self.draw_slice(slice, None);
}
pub fn draw_instanced<B: Batch>(&mut self, batch: B,
count: device::InstanceCount,
base: device::VertexCount,
frame: &target::Frame) {
self.bind_frame(frame);
let (mesh, link, slice, program, state) = batch.get_data();
self.bind_program(&batch, program);
self.bind_state(state);
self.bind_mesh(mesh, link, program.get_info());
self.draw_slice(slice, Some((count, base)));
}
pub fn blit(&mut self, source: &target::Frame, source_rect: Rect,
destination: &target::Frame, dest_rect: Rect, mask: Mask) {
if mask.intersects(device::target::COLOR) {
debug_assert!(source.is_default() || !source.colors.is_empty());
debug_assert!(destination.is_default() || !destination.colors.is_empty());
}
if mask.intersects(device::target::DEPTH) {
debug_assert!(source.is_default() || source.depth.is_some());
debug_assert!(destination.is_default() || destination.depth.is_some());
}
if mask.intersects(device::target::STENCIL) {
debug_assert!(source.is_default() || source.stencil.is_some());
debug_assert!(destination.is_default() || destination.stencil.is_some());
}
self.bind_frame(destination);
self.bind_read_frame(source);
self.command_buffer.call_blit(source_rect, dest_rect, mask);
}
pub fn update_buffer_vec<T: Copy>(&mut self, buf: device::BufferHandle<T>,
data: &[T], offset_elements: usize) {
let esize = mem::size_of::<T>();
let offset_bytes = esize * offset_elements;
debug_assert!(data.len() * esize + offset_bytes <= buf.get_info().size);
let pointer = self.data_buffer.add_vec(data);
self.command_buffer.update_buffer(buf.get_name(), pointer, offset_bytes);
}
pub fn update_buffer_struct<U, T: Copy>(&mut self,
buf: device::BufferHandle<U>, data: &T) {
debug_assert!(mem::size_of::<T>() <= buf.get_info().size);
let pointer = self.data_buffer.add_struct(data);
self.command_buffer.update_buffer(buf.get_name(), pointer, 0);
}
pub fn update_texture<T: Copy>(&mut self, tex: device::TextureHandle,
img: device::tex::ImageInfo, data: &[T]) {
debug_assert!(tex.get_info().contains(&img));
let pointer = self.data_buffer.add_vec(data);
self.command_buffer.update_texture(tex.get_info().kind, tex.get_name(), img, pointer);
}
fn bind_frame(&mut self, frame: &target::Frame) {
if self.render_state.frame.width != frame.width ||
self.render_state.frame.height != frame.height {
self.command_buffer.set_viewport(Rect {
x: 0,
y: 0,
w: frame.width,
h: frame.height,
});
self.render_state.frame.width = frame.width;
self.render_state.frame.height = frame.height;
}
if frame.is_default() {
if self.render_state.is_frame_buffer_set {
self.command_buffer.bind_frame_buffer(Access::Draw, self.default_frame_buffer.get_name());
self.render_state.is_frame_buffer_set = false;
}
} else {
if !self.render_state.is_frame_buffer_set {
self.command_buffer.bind_frame_buffer(Access::Draw, self.draw_frame_buffer.get_name());
self.render_state.is_frame_buffer_set = true;
}
for (i, _) in self.render_state.frame.colors.iter().enumerate()
.skip(frame.colors.len()) {
self.command_buffer.unbind_target(Access::Draw, Target::Color(i as u8));
}
self.render_state.frame.colors.truncate(frame.colors.len());
for (i, (cur, new)) in self.render_state.frame.colors.iter_mut()
.zip(frame.colors.iter()).enumerate() {
if *cur != *new {
self.command_buffer.bind_target(Access::Draw, Target::Color(i as u8), Some(new));
*cur = *new;
}
}
self.command_buffer.set_draw_color_buffers(frame.colors.len());
for (i, new) in frame.colors.iter().enumerate()
.skip(self.render_state.frame.colors.len()) {
self.command_buffer.bind_target(Access::Draw, Target::Color(i as u8), Some(new));
self.render_state.frame.colors.push(*new);
}
if self.render_state.frame.depth != frame.depth {
self.command_buffer.bind_target(Access::Draw, Target::Depth, frame.depth.as_ref());
self.render_state.frame.depth = frame.depth;
}
if self.render_state.frame.stencil != frame.stencil {
self.command_buffer.bind_target(Access::Draw, Target::Stencil, frame.stencil.as_ref());
self.render_state.frame.stencil = frame.stencil;
}
}
}
fn bind_read_frame(&mut self, frame: &target::Frame) {
self.command_buffer.bind_frame_buffer(Access::Read, self.read_frame_buffer.get_name());
if frame.colors.is_empty() {
self.command_buffer.unbind_target(Access::Read, Target::Color(0));
}else {
self.command_buffer.bind_target(Access::Read, Target::Color(0), Some(&frame.colors[0]));
}
self.command_buffer.bind_target(Access::Read, Target::Depth, frame.depth.as_ref());
self.command_buffer.bind_target(Access::Read, Target::Stencil, frame.stencil.as_ref());
}
fn bind_state(&mut self, state: &state::DrawState) {
if self.render_state.draw.primitive != state.primitive {
self.command_buffer.set_primitive(state.primitive);
}
if self.render_state.draw.multi_sample != state.multi_sample {
self.command_buffer.set_multi_sample(state.multi_sample);
}
if self.render_state.draw.scissor != state.scissor {
self.command_buffer.set_scissor(state.scissor);
}
if self.render_state.draw.depth != state.depth || self.render_state.draw.stencil != state.stencil ||
self.render_state.draw.primitive.get_cull_mode() != state.primitive.get_cull_mode() {
self.command_buffer.set_depth_stencil(state.depth, state.stencil,
state.primitive.get_cull_mode());
}
if self.render_state.draw.blend != state.blend {
self.command_buffer.set_blend(state.blend);
}
if self.render_state.draw.color_mask != state.color_mask {
self.command_buffer.set_color_mask(state.color_mask);
}
self.render_state.draw = *state;
}
fn bind_program<B: Batch>(&mut self, batch: &B, program: &device::ProgramHandle) {
if self.render_state.program_name != program.get_name() {
self.command_buffer.bind_program(program.get_name());
self.render_state.program_name = program.get_name();
}
batch.fill_params(self.parameters.get_mut());
self.upload_parameters(program);
}
fn upload_parameters(&mut self, program: &device::ProgramHandle) {
let info = program.get_info();
if self.parameters.uniforms.len() != info.uniforms.len() ||
self.parameters.blocks.len() != info.blocks.len() ||
self.parameters.textures.len() != info.textures.len() {
error!("Mismatching number of uniforms ({:?}), blocks ({:?}), or \
textures ({:?}) in `upload_parameters` for program: {:?}",
self.parameters.uniforms.len(),
self.parameters.blocks.len(),
self.parameters.textures.len(),
info);
}
for (var, value) in info.uniforms.iter()
.zip(self.parameters.uniforms.iter()) {
self.command_buffer.bind_uniform(var.location, *value);
}
for (i, (_, buf)) in info.blocks.iter()
.zip(self.parameters.blocks.iter()).enumerate() {
self.command_buffer.bind_uniform_block(
program.get_name(),
i as device::UniformBufferSlot,
i as device::UniformBlockIndex,
buf.get_name()
);
}
for (i, (var, &(tex, sampler))) in info.textures.iter()
.zip(self.parameters.textures.iter()).enumerate() {
if sampler.is_some() && tex.get_info().kind.get_aa_mode().is_some() {
error!("A sampler provided for an AA texture: {}", var.name.clone());
}
self.command_buffer.bind_uniform(var.location, UniformValue::I32(i as i32));
self.command_buffer.bind_texture(i as device::TextureSlot,
tex.get_info().kind, tex.get_name(), sampler);
}
}
fn bind_mesh(&mut self, mesh: &mesh::Mesh, link: &mesh::Link, info: &ProgramInfo) {
if !self.render_state.is_array_buffer_set {
self.common_array_buffer.map(|ab|
self.command_buffer.bind_array_buffer(ab.get_name())
).is_ok();
self.render_state.is_array_buffer_set = true;
}
for (attr_index, sat) in link.attribute_indices().zip(info.attributes.iter()) {
let vat = &mesh.attributes[attr_index];
let loc = sat.location as usize;
let need_update = loc >= self.render_state.attributes.len() ||
match self.render_state.attributes[loc] {
Some((buf, fmt)) => buf != vat.buffer || fmt != vat.format,
None => true,
};
if need_update {
self.command_buffer.bind_attribute(loc as device::AttributeSlot,
vat.buffer.get_name(), vat.format);
if loc < self.render_state.attributes.len() {
self.render_state.attributes[loc] = Some((vat.buffer, vat.format));
}
}
}
}
fn bind_index<T>(&mut self, buf: device::BufferHandle<T>) {
if self.render_state.index != Some(buf.raw()) {
self.command_buffer.bind_index(buf.get_name());
self.render_state.index = Some(buf.raw());
}
}
fn draw_slice(&mut self, slice: &mesh::Slice,
instances: Option<(device::InstanceCount, device::VertexCount)>) {
let mesh::Slice { start, end, prim_type, kind } = *slice;
match kind {
SliceKind::Vertex => {
self.command_buffer.call_draw(prim_type, start, end - start, instances);
},
SliceKind::Index8(buf, base) => {
self.bind_index(buf);
self.command_buffer.call_draw_indexed(prim_type, IntSize::U8, start, end - start, base, instances);
},
SliceKind::Index16(buf, base) => {
self.bind_index(buf);
self.command_buffer.call_draw_indexed(prim_type, IntSize::U16, start, end - start, base, instances);
},
SliceKind::Index32(buf, base) => {
self.bind_index(buf);
self.command_buffer.call_draw_indexed(prim_type, IntSize::U32, start, end - start, base, instances);
},
}
}
}
pub trait DeviceHelper: device::Device {
fn create_renderer(&mut self) -> Renderer<<Self as device::Device>::CommandBuffer>;
fn create_mesh<T: mesh::VertexFormat + Copy>(&mut self, data: &[T]) -> mesh::Mesh;
fn link_program(&mut self, vs_src: ShaderSource, fs_src: ShaderSource)
-> Result<device::ProgramHandle, ProgramError>;
}
impl<D: device::Device> DeviceHelper for D {
fn create_renderer(&mut self) -> Renderer<<D as device::Device>::CommandBuffer> {
Renderer {
command_buffer: CommandBuffer::new(),
data_buffer: device::draw::DataBuffer::new(),
common_array_buffer: self.create_array_buffer(),
draw_frame_buffer: self.create_frame_buffer(),
read_frame_buffer: self.create_frame_buffer(),
default_frame_buffer: device::get_main_frame_buffer(),
render_state: RenderState::new(),
parameters: ParamStorage::new(),
}
}
fn create_mesh<T: mesh::VertexFormat + Copy>(&mut self, data: &[T]) -> mesh::Mesh {
let nv = data.len();
debug_assert!(nv < {
use std::num::Int;
let val: device::VertexCount = Int::max_value();
val as usize
});
let buf = self.create_buffer_static(data);
mesh::Mesh::from_format(buf, nv as device::VertexCount)
}
fn link_program(&mut self, vs_src: ShaderSource, fs_src: ShaderSource)
-> Result<device::ProgramHandle, ProgramError> {
let vs = match self.create_shader(Stage::Vertex, vs_src) {
Ok(s) => s,
Err(e) => return Err(ProgramError::Vertex(e)),
};
let fs = match self.create_shader(Stage::Fragment, fs_src) {
Ok(s) => s,
Err(e) => return Err(ProgramError::Fragment(e)),
};
match self.shader_targets(&fs_src) {
Some(targets) => self.create_program(&[vs, fs], Some(&targets[])),
None => self.create_program(&[vs, fs], None),
}.map_err(|e| ProgramError::Link(e))
}
}