#![deny(missing_docs)]
use draw_state::target::{Depth, Stencil};
use std::error::Error;
use std::any::Any;
use std::{fmt, mem};
use core::{Device, SubmissionResult, IndexType, Resources, VertexCount};
use core::{command, format, handle, texture};
use core::memory::{cast_slice, Typed, Pod, Usage, Bind};
use slice;
use pso;
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq)]
pub enum CopyError<S, D> {
OutOfSrcBounds {
size: S,
copy_end: S,
},
OutOfDstBounds {
size: D,
copy_end: D,
},
Overlap {
src_offset: usize,
dst_offset: usize,
size: usize,
},
NoSrcBindFlag,
NoDstBindFlag,
}
pub type CopyBufferResult = Result<(), CopyError<usize, usize>>;
pub type CopyBufferTextureResult = Result<(), CopyError<usize, [texture::Size; 3]>>;
pub type CopyTextureBufferResult = Result<(), CopyError<[texture::Size; 3], usize>>;
pub type CopyTextureResult = Result<(), CopyError<[texture::Size; 3], [texture::Size; 3]>>;
impl<S, D> fmt::Display for CopyError<S, D>
where S: fmt::Debug + fmt::Display, D: fmt::Debug + fmt::Display
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::CopyError::*;
match *self {
OutOfSrcBounds { ref size, ref copy_end } =>
write!(f, "{}: {} / {}", self.description(), copy_end, size),
OutOfDstBounds { ref size, ref copy_end } =>
write!(f, "{}: {} / {}", self.description(), copy_end, size),
Overlap { ref src_offset, ref dst_offset, ref size } =>
write!(f, "{}: [{} - {}] to [{} - {}]",
self.description(),
src_offset, src_offset + size,
dst_offset, dst_offset + size),
_ => write!(f, "{}", self.description())
}
}
}
impl<S, D> Error for CopyError<S, D>
where S: fmt::Debug + fmt::Display, D: fmt::Debug + fmt::Display
{
fn description(&self) -> &str {
use self::CopyError::*;
match *self {
OutOfSrcBounds {..} => "Copy source is out of bounds",
OutOfDstBounds {..} => "Copy destination is out of bounds",
Overlap {..} => "Copy source and destination are overlapping",
NoSrcBindFlag => "Copy source is missing `TRANSFER_SRC`",
NoDstBindFlag => "Copy destination is missing `TRANSFER_DST`",
}
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq)]
pub enum UpdateError<T> {
OutOfBounds {
target: T,
source: T,
},
UnitCountMismatch {
target: usize,
slice: usize,
},
InvalidUsage(Usage),
}
fn check_update_usage<T>(usage: Usage) -> Result<(), UpdateError<T>> {
if usage == Usage::Dynamic {
Ok(())
} else {
Err(UpdateError::InvalidUsage(usage))
}
}
impl<T: Any + fmt::Debug + fmt::Display> fmt::Display for UpdateError<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
UpdateError::OutOfBounds {ref target, ref source} =>
write!(f, "Write to {} from {} is out of bounds", target, source),
UpdateError::UnitCountMismatch {ref target, ref slice} =>
write!(f, "{}: expected {}, found {}", self.description(), target, slice),
UpdateError::InvalidUsage(usage) =>
write!(f, "{}: {:?}", self.description(), usage),
}
}
}
impl<T: Any + fmt::Debug + fmt::Display> Error for UpdateError<T> {
fn description(&self) -> &str {
match *self {
UpdateError::OutOfBounds {..} => "Write to data is out of bounds",
UpdateError::UnitCountMismatch {..} => "Unit count mismatch",
UpdateError::InvalidUsage(_) => "This memory usage does not allow updates",
}
}
}
#[derive(Debug)]
pub struct Encoder<R: Resources, C> {
command_buffer: C,
raw_pso_data: pso::RawDataSet<R>,
access_info: command::AccessInfo<R>,
handles: handle::Manager<R>,
}
impl<R: Resources, C> From<C> for Encoder<R, C> {
fn from(combuf: C) -> Encoder<R, C> {
Encoder {
command_buffer: combuf,
raw_pso_data: pso::RawDataSet::new(),
access_info: command::AccessInfo::new(),
handles: handle::Manager::new(),
}
}
}
impl<R: Resources, C: command::Buffer<R>> Encoder<R, C> {
pub fn flush<D>(&mut self, device: &mut D)
where D: Device<Resources=R, CommandBuffer=C>
{
self.flush_no_reset(device).unwrap();
self.reset();
}
pub fn flush_no_reset<D>(&mut self, device: &mut D) -> SubmissionResult<()>
where D: Device<Resources=R, CommandBuffer=C>
{
device.pin_submitted_resources(&self.handles);
device.submit(&mut self.command_buffer, &self.access_info)
}
pub fn fenced_flush_no_reset<D>(&mut self,
device: &mut D,
after: Option<handle::Fence<R>>)
-> SubmissionResult<handle::Fence<R>>
where D: Device<Resources=R, CommandBuffer=C>
{
device.pin_submitted_resources(&self.handles);
device.fenced_submit(&mut self.command_buffer, &self.access_info, after)
}
pub fn reset(&mut self) {
self.command_buffer.reset();
self.access_info.clear();
self.handles.clear();
}
pub fn copy_buffer<T: Pod>(&mut self, src: &handle::Buffer<R, T>, dst: &handle::Buffer<R, T>,
src_offset: usize, dst_offset: usize, size: usize) -> CopyBufferResult {
if !src.get_info().bind.contains(Bind::TRANSFER_SRC) {
return Err(CopyError::NoSrcBindFlag);
}
if !dst.get_info().bind.contains(Bind::TRANSFER_DST) {
return Err(CopyError::NoDstBindFlag);
}
let size_bytes = mem::size_of::<T>() * size;
let src_offset_bytes = mem::size_of::<T>() * src_offset;
let src_copy_end = src_offset_bytes + size_bytes;
if src_copy_end > src.get_info().size {
return Err(CopyError::OutOfSrcBounds {
size: src.get_info().size,
copy_end: src_copy_end,
});
}
let dst_offset_bytes = mem::size_of::<T>() * dst_offset;
let dst_copy_end = dst_offset_bytes + size_bytes;
if dst_copy_end > dst.get_info().size {
return Err(CopyError::OutOfDstBounds {
size: dst.get_info().size,
copy_end: dst_copy_end,
});
}
if src == dst &&
src_offset_bytes < dst_copy_end &&
dst_offset_bytes < src_copy_end
{
return Err(CopyError::Overlap {
src_offset: src_offset_bytes,
dst_offset: dst_offset_bytes,
size: size_bytes,
});
}
self.access_info.buffer_read(src.raw());
self.access_info.buffer_write(dst.raw());
self.command_buffer.copy_buffer(
self.handles.ref_buffer(src.raw()).clone(),
self.handles.ref_buffer(dst.raw()).clone(),
src_offset_bytes, dst_offset_bytes, size_bytes);
Ok(())
}
pub fn copy_buffer_to_texture_raw(
&mut self, src: &handle::RawBuffer<R>, src_offset_bytes: usize,
dst: &handle::RawTexture<R>, cube_face: Option<texture::CubeFace>, info: texture::RawImageInfo)
-> CopyBufferTextureResult
{
if !src.get_info().bind.contains(Bind::TRANSFER_SRC) {
return Err(CopyError::NoSrcBindFlag);
}
if !dst.get_info().bind.contains(Bind::TRANSFER_DST) {
return Err(CopyError::NoDstBindFlag);
}
let size_bytes = info.get_byte_count();
let src_copy_end = src_offset_bytes + size_bytes;
if src_copy_end > src.get_info().size {
return Err(CopyError::OutOfSrcBounds {
size: src.get_info().size,
copy_end: src_copy_end,
});
}
let dim = dst.get_info().kind.get_dimensions();
if !info.is_inside(dim) {
let (w, h, d, _) = dim;
return Err(CopyError::OutOfDstBounds {
size: [w, h, d],
copy_end: [info.xoffset + info.width,
info.yoffset + info.height,
info.zoffset + info.depth]
});
}
let dst = texture::TextureCopyRegion {
texture: self.handles.ref_texture(dst).clone(),
kind: dst.get_info().kind,
cube_face,
info,
};
self.access_info.buffer_read(src);
self.command_buffer.copy_buffer_to_texture(
self.handles.ref_buffer(src).clone(), src_offset_bytes,
dst);
Ok(())
}
pub fn copy_texture_to_buffer_raw(
&mut self, src: &handle::RawTexture<R>,
cube_face: Option<texture::CubeFace>, info: texture::RawImageInfo,
dst: &handle::RawBuffer<R>, dst_offset_bytes: usize)
-> CopyTextureBufferResult
{
if !src.get_info().bind.contains(Bind::TRANSFER_SRC) {
return Err(CopyError::NoSrcBindFlag);
}
if !dst.get_info().bind.contains(Bind::TRANSFER_DST) {
return Err(CopyError::NoDstBindFlag);
}
let size_bytes = info.get_byte_count();
let dst_copy_end = dst_offset_bytes + size_bytes;
if dst_copy_end > dst.get_info().size {
return Err(CopyError::OutOfDstBounds {
size: dst.get_info().size,
copy_end: dst_copy_end,
});
}
let dim = src.get_info().kind.get_dimensions();
if !info.is_inside(dim) {
let (w, h, d, _) = dim;
return Err(CopyError::OutOfSrcBounds {
size: [w, h, d],
copy_end: [info.xoffset + info.width,
info.yoffset + info.height,
info.zoffset + info.depth]
});
}
let src = texture::TextureCopyRegion {
texture: self.handles.ref_texture(src).clone(),
kind: src.get_info().kind,
cube_face,
info,
};
self.access_info.buffer_write(dst);
self.command_buffer.copy_texture_to_buffer(src,
self.handles.ref_buffer(dst).clone(), dst_offset_bytes);
Ok(())
}
pub fn copy_texture_to_texture_raw(&mut self,
src: &handle::RawTexture<R>, src_face: Option<texture::CubeFace>, src_info: texture::RawImageInfo,
dst: &handle::RawTexture<R>, dst_face: Option<texture::CubeFace>, dst_info: texture::RawImageInfo)
-> CopyTextureResult
{
if !src.get_info().bind.contains(Bind::TRANSFER_SRC) {
return Err(CopyError::NoSrcBindFlag);
}
let src_dim = src.get_info().kind.get_dimensions();
if !src_info.is_inside(src_dim) {
let (w, h, d, _) = src_dim;
return Err(CopyError::OutOfSrcBounds {
size: [w, h, d],
copy_end: [src_info.xoffset + src_info.width,
src_info.yoffset + src_info.height,
src_info.zoffset + src_info.depth]
});
}
let src = texture::TextureCopyRegion {
texture: self.handles.ref_texture(src).clone(),
kind: src.get_info().kind,
cube_face: src_face,
info: src_info,
};
if !dst.get_info().bind.contains(Bind::TRANSFER_DST) {
return Err(CopyError::NoDstBindFlag);
}
let dst_dim = dst.get_info().kind.get_dimensions();
if !dst_info.is_inside(dst_dim) {
let (w, h, d, _) = dst_dim;
return Err(CopyError::OutOfDstBounds {
size: [w, h, d],
copy_end: [dst_info.xoffset + dst_info.width,
dst_info.yoffset + dst_info.height,
dst_info.zoffset + dst_info.depth]
});
}
let dst = texture::TextureCopyRegion {
texture: self.handles.ref_texture(dst).clone(),
kind: dst.get_info().kind,
cube_face: dst_face,
info: dst_info,
};
self.command_buffer.copy_texture_to_texture(src, dst);
Ok(())
}
pub fn update_buffer<T: Pod>(&mut self, buf: &handle::Buffer<R, T>,
data: &[T], offset_elements: usize)
-> Result<(), UpdateError<usize>>
{
if data.is_empty() { return Ok(()); }
try!(check_update_usage(buf.raw().get_info().usage));
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 {
self.command_buffer.update_buffer(
self.handles.ref_buffer(buf.raw()).clone(),
cast_slice(data), 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) {
use std::slice;
check_update_usage::<usize>(buf.raw().get_info().usage).unwrap();
let slice = unsafe {
slice::from_raw_parts(data as *const T as *const u8, mem::size_of::<T>())
};
self.command_buffer.update_buffer(
self.handles.ref_buffer(buf.raw()).clone(), slice, 0);
}
pub fn update_texture<S, T>(&mut self, tex: &handle::Texture<R, T::Surface>,
cube_face: Option<texture::CubeFace>,
img: texture::NewImageInfo, data: &[S::DataType])
-> Result<(), UpdateError<[texture::Size; 3]>>
where
S: format::SurfaceTyped,
S::DataType: Copy,
T: format::Formatted<Surface = S>,
{
if data.is_empty() { return Ok(()); }
try!(check_update_usage(tex.raw().get_info().usage));
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 dst = texture::TextureCopyRegion {
texture: self.handles.ref_texture(tex.raw()).clone(),
kind: tex.get_info().kind,
cube_face,
info: img.convert(T::get_format()),
};
self.command_buffer.update_texture(dst, cast_slice(data));
Ok(())
}
fn draw_indexed<T>(&mut self, buf: &handle::Buffer<R, T>, ty: IndexType,
slice: &slice::Slice<R>, base: VertexCount,
instances: Option<command::InstanceParams>) {
self.access_info.buffer_read(buf.raw());
self.command_buffer.bind_index(self.handles.ref_buffer(buf.raw()).clone(), ty);
self.command_buffer.call_draw_indexed(slice.start, slice.end - slice.start, base, instances);
}
fn draw_slice(&mut self, slice: &slice::Slice<R>, instances: Option<command::InstanceParams>) {
match slice.buffer {
slice::IndexBuffer::Auto => self.command_buffer.call_draw(
slice.start + slice.base_vertex, slice.end - slice.start, instances),
slice::IndexBuffer::Index16(ref buf) =>
self.draw_indexed(buf, IndexType::U16, slice, slice.base_vertex, instances),
slice::IndexBuffer::Index32(ref buf) =>
self.draw_indexed(buf, IndexType::U32, slice, slice.base_vertex, instances),
}
}
pub fn clear<T: format::RenderFormat>(&mut self,
view: &handle::RenderTargetView<R, T>, value: T::View)
where T::View: Into<command::ClearColor> {
self.clear_raw(view.raw(), value.into());
}
pub fn clear_depth<T: format::DepthFormat>(&mut self,
view: &handle::DepthStencilView<R, T>, depth: Depth) {
self.clear_depth_raw(view.raw(), depth);
}
pub fn clear_stencil<T: format::StencilFormat>(&mut self,
view: &handle::DepthStencilView<R, T>, stencil: Stencil) {
self.clear_stencil_raw(view.raw(), stencil);
}
pub fn clear_raw(&mut self,
view: &handle::RawRenderTargetView<R>,
value: command::ClearColor) {
let target = self.handles.ref_rtv(view).clone();
self.command_buffer.clear_color(target, value)
}
pub fn clear_depth_raw(&mut self,
view: &handle::RawDepthStencilView<R>,
depth: Depth) {
let target = self.handles.ref_dsv(view).clone();
self.command_buffer.clear_depth_stencil(target, Some(depth), None)
}
pub fn clear_stencil_raw(&mut self,
view: &handle::RawDepthStencilView<R>,
stencil: Stencil) {
let target = self.handles.ref_dsv(view).clone();
self.command_buffer.clear_depth_stencil(target, None, Some(stencil))
}
pub fn draw<D: pso::PipelineData<R>>(&mut self, slice: &slice::Slice<R>,
pipeline: &pso::PipelineState<R, D::Meta>, user_data: &D)
{
let (pso, _) = self.handles.ref_pso(pipeline.get_handle());
self.raw_pso_data.clear();
user_data.bake_to(&mut self.raw_pso_data, pipeline.get_meta(), &mut self.handles, &mut self.access_info);
self.command_buffer.bind_pixel_targets(self.raw_pso_data.pixel_targets.clone());
self.command_buffer.bind_pipeline_state(pso.clone());
self.command_buffer.bind_vertex_buffers(self.raw_pso_data.vertex_buffers.clone());
self.command_buffer.set_ref_values(self.raw_pso_data.ref_values);
self.command_buffer.set_scissor(self.raw_pso_data.scissor);
self.command_buffer.bind_constant_buffers(&self.raw_pso_data.constant_buffers);
for &(location, value) in &self.raw_pso_data.global_constants {
self.command_buffer.bind_global_constant(location, value);
}
self.command_buffer.bind_unordered_views(&self.raw_pso_data.unordered_views);
self.command_buffer.bind_resource_views(&self.raw_pso_data.resource_views);
self.command_buffer.bind_samplers(&self.raw_pso_data.samplers);
self.draw_slice(slice, slice.instances);
}
pub fn generate_mipmap<T: format::BlendFormat>(&mut self, view: &handle::ShaderResourceView<R, T>) {
self.generate_mipmap_raw(view.raw())
}
pub fn generate_mipmap_raw(&mut self, view: &handle::RawShaderResourceView<R>) {
let srv = self.handles.ref_srv(view).clone();
self.command_buffer.generate_mipmap(srv);
}
}