#![deny(missing_docs)]
use std::mem;
use draw_state::target::{Depth, Stencil};
use gfx_core::{Device, Factory, IndexType, Resources, SubmitInfo, VertexCount};
use gfx_core::{draw, format, handle, tex};
use gfx_core::factory::Phantom;
use mesh;
use pso;
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq)]
pub enum UpdateError<T> {
OutOfBounds {
target: T,
source: T,
},
UnitCountMismatch {
target: usize,
slice: usize,
},
}
pub struct Encoder<R: Resources, C: draw::CommandBuffer<R>> {
command_buffer: C,
data_buffer: draw::DataBuffer,
handles: handle::Manager<R>,
}
impl<R: Resources, C: draw::CommandBuffer<R>> Encoder<R, C> {
pub fn create<F>(factory: &mut F) -> Encoder<R, C> where
F: Factory<R, CommandBuffer = C>
{
Encoder {
command_buffer: factory.create_command_buffer(),
data_buffer: draw::DataBuffer::new(),
handles: handle::Manager::new(),
}
}
pub fn reset(&mut self) {
self.command_buffer.reset();
self.data_buffer.clear();
self.handles.clear();
}
pub fn as_buffer<D>(&self) -> SubmitInfo<D> where
D: Device<Resources=R, CommandBuffer=C> {
SubmitInfo(&self.command_buffer, &self.data_buffer, &self.handles)
}
pub fn clone_empty(&self) -> Encoder<R, C> {
Encoder {
command_buffer: self.command_buffer.clone_empty(),
data_buffer: draw::DataBuffer::new(),
handles: handle::Manager::new(),
}
}
pub fn update_buffer<T: Copy>(&mut self, buf: &handle::Buffer<R, T>,
data: &[T], offset_elements: usize)
-> Result<(), UpdateError<usize>>
{
if data.is_empty() {
return Ok(())
}
let elem_size = mem::size_of::<T>();
let offset_bytes = elem_size * offset_elements;
let bound = data.len().wrapping_mul(elem_size) + offset_bytes;
if bound <= buf.get_info().size {
let pointer = self.data_buffer.add_vec(data);
self.command_buffer.update_buffer(
self.handles.ref_buffer(buf.raw()).clone(),
pointer, offset_bytes);
Ok(())
} else {
Err(UpdateError::OutOfBounds {
target: bound,
source: buf.get_info().size,
})
}
}
pub fn update_constant_buffer<T: Copy>(&mut self, buf: &handle::Buffer<R, T>, data: &T) {
let pointer = self.data_buffer.add_struct(data);
self.command_buffer.update_buffer(
self.handles.ref_buffer(buf.raw()).clone(),
pointer, 0);
}
pub fn update_texture<S, T>(&mut self, tex: &handle::Texture<R, T>,
face: Option<tex::CubeFace>,
img: tex::NewImageInfo, data: &[S::DataType])
-> Result<(), UpdateError<[tex::Size; 3]>>
where
S: format::SurfaceTyped,
S::DataType: Copy,
T: format::Formatted<Surface = S>,
{
if data.is_empty() {
return Ok(())
}
let target_count = img.get_texel_count();
if target_count != data.len() {
return Err(UpdateError::UnitCountMismatch {
target: target_count,
slice: data.len(),
})
}
let dim = tex.get_info().kind.get_dimensions();
if !img.is_inside(dim) {
let (w, h, d, _) = dim;
return Err(UpdateError::OutOfBounds {
target: [
img.xoffset + img.width,
img.yoffset + img.height,
img.zoffset + img.depth,
],
source: [w, h, d],
})
}
let pointer = self.data_buffer.add_vec(data);
self.command_buffer.update_texture(
self.handles.ref_texture(tex.raw()).clone(),
tex.get_info().kind, face, pointer,
img.convert(T::get_format()));
Ok(())
}
fn draw_indexed<T>(&mut self, buf: &handle::Buffer<R, T>, ty: IndexType,
slice: &mesh::Slice<R>, base: VertexCount,
instances: draw::InstanceOption) {
self.command_buffer.bind_index(self.handles.ref_buffer(buf.raw()).clone());
self.command_buffer.call_draw_indexed(ty,
slice.start, slice.end - slice.start, base, instances);
}
fn draw_slice(&mut self, slice: &mesh::Slice<R>, instances: draw::InstanceOption) {
match slice.kind {
mesh::SliceKind::Vertex => self.command_buffer.call_draw(
slice.start, slice.end - slice.start, instances),
mesh::SliceKind::Index8(ref buf, base) =>
self.draw_indexed(buf, IndexType::U8, slice, base, instances),
mesh::SliceKind::Index16(ref buf, base) =>
self.draw_indexed(buf, IndexType::U16, slice, base, instances),
mesh::SliceKind::Index32(ref buf, base) =>
self.draw_indexed(buf, IndexType::U32, slice, base, instances),
}
}
fn clear_all<T>(&mut self,
color: Option<(&handle::RenderTargetView<R, T>, draw::ClearColor)>,
depth: Option<(&handle::DepthStencilView<R, T>, Depth)>,
stencil: Option<(&handle::DepthStencilView<R, T>, Stencil)>)
{
use gfx_core::pso::PixelTargetSet;
let mut pts = PixelTargetSet::new();
pts.colors[0] = color.map(|(ref view, _)|
self.handles.ref_rtv(view.raw()).clone());
pts.depth = depth.map(|(ref view, _)|
self.handles.ref_dsv(view.raw()).clone());
pts.stencil = stencil.map(|(ref view, _)|
self.handles.ref_dsv(view.raw()).clone());
self.command_buffer.bind_pixel_targets(pts);
self.command_buffer.clear(draw::ClearSet(
[color.map(|(_, c)| c), None, None, None],
depth.map(|(_, d)| d), stencil.map(|(_, s)| s)
));
}
pub fn clear<T: format::RenderFormat>(&mut self,
view: &handle::RenderTargetView<R, T>, value: T::View)
where T::View: Into<draw::ClearColor> {
self.clear_all(Some((view, value.into())), None, None)
}
pub fn clear_depth<T: format::DepthFormat>(&mut self,
view: &handle::DepthStencilView<R, T>, depth: Depth) {
self.clear_all(None, Some((view, depth)), None)
}
pub fn clear_stencil<T: format::StencilFormat>(&mut self,
view: &handle::DepthStencilView<R, T>, stencil: Stencil) {
self.clear_all(None, None, Some((view, stencil)))
}
pub fn draw<D: pso::PipelineData<R>>(&mut self, slice: &mesh::Slice<R>,
pipeline: &pso::PipelineState<R, D::Meta>, user_data: &D)
{
let (pso, _) = self.handles.ref_pso(pipeline.get_handle());
self.command_buffer.bind_pipeline_state(pso.clone());
let raw_data = user_data.bake(pipeline.get_meta(), &mut self.handles);
self.command_buffer.bind_vertex_buffers(raw_data.vertex_buffers);
self.command_buffer.bind_constant_buffers(raw_data.constant_buffers);
for &(location, value) in &raw_data.global_constants {
self.command_buffer.bind_global_constant(location, value);
}
self.command_buffer.bind_resource_views(raw_data.resource_views);
self.command_buffer.bind_unordered_views(raw_data.unordered_views);
self.command_buffer.bind_samplers(raw_data.samplers);
self.command_buffer.bind_pixel_targets(raw_data.pixel_targets);
self.command_buffer.set_ref_values(raw_data.ref_values);
self.command_buffer.set_scissor(raw_data.scissor);
self.draw_slice(slice, slice.instances);
}
}