#![allow(missing_docs)]
use gl;
use gfx_core::{self as c, command, state as s};
use gfx_core::target::{ColorValue, Depth, Mirror, Rect, Stencil};
use gfx_core::texture::TextureCopyRegion;
use {Buffer, BufferElement, Program, FrameBuffer, Texture,
NewTexture, Resources, PipelineState, ResourceView, TargetView};
fn primitive_to_gl(primitive: c::Primitive) -> gl::types::GLenum {
use gfx_core::Primitive::*;
match primitive {
PointList => gl::POINTS,
LineList => gl::LINES,
LineStrip => gl::LINE_STRIP,
TriangleList => gl::TRIANGLES,
TriangleStrip => gl::TRIANGLE_STRIP,
LineListAdjacency => gl::LINES_ADJACENCY,
LineStripAdjacency => gl::LINE_STRIP_ADJACENCY,
TriangleListAdjacency => gl::TRIANGLES_ADJACENCY,
TriangleStripAdjacency => gl::TRIANGLE_STRIP_ADJACENCY,
PatchList(_) => gl::PATCHES
}
}
pub type Access = gl::types::GLenum;
#[derive(Clone, Copy, Debug)]
pub struct RawOffset(pub *const gl::types::GLvoid);
unsafe impl Send for RawOffset {}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct DataPointer {
offset: u32,
size: u32,
}
pub struct DataBuffer(Vec<u8>);
impl DataBuffer {
pub fn new() -> DataBuffer {
DataBuffer(Vec::new())
}
fn add(&mut self, data: &[u8]) -> DataPointer {
self.0.extend_from_slice(data);
DataPointer {
offset: (self.0.len() - data.len()) as u32,
size: data.len() as u32,
}
}
pub fn get(&self, ptr: DataPointer) -> &[u8] {
&self.0[ptr.offset as usize..(ptr.offset + ptr.size) as usize]
}
}
#[derive(Clone, Debug)]
pub enum Command {
BindProgram(Program),
BindConstantBuffer(c::pso::ConstantBufferParam<Resources>),
BindResourceView(c::pso::ResourceViewParam<Resources>),
BindUnorderedView(c::pso::UnorderedViewParam<Resources>),
BindSampler(c::pso::SamplerParam<Resources>, Option<gl::types::GLenum>),
BindPixelTargets(c::pso::PixelTargetSet<Resources>),
BindVao,
BindAttribute(c::AttributeSlot, Buffer, BufferElement),
UnbindAttribute(c::AttributeSlot),
BindIndex(Buffer),
BindFrameBuffer(Access, FrameBuffer),
BindUniform(c::shade::Location, c::shade::UniformValue),
SetDrawColorBuffers(c::ColorSlot),
SetRasterizer(s::Rasterizer),
SetViewport(Rect),
SetScissor(Option<Rect>),
SetDepthState(Option<s::Depth>),
SetStencilState(Option<s::Stencil>, (Stencil, Stencil), s::CullFace),
SetBlendState(c::ColorSlot, s::Color),
SetBlendColor(ColorValue),
SetPatches(c::PatchSize),
SetOutputMasks { color: bool, depth: bool, stencil: bool },
CopyBuffer(Buffer, Buffer,
gl::types::GLintptr, gl::types::GLintptr,
gl::types::GLsizeiptr),
CopyBufferToTexture(Buffer, gl::types::GLintptr,
TextureCopyRegion<Texture>),
CopyTextureToBuffer(TextureCopyRegion<NewTexture>,
Buffer, gl::types::GLintptr,
FrameBuffer),
CopyTextureToTexture(TextureCopyRegion<NewTexture>,
TextureCopyRegion<Texture>,
FrameBuffer),
UpdateBuffer(Buffer, DataPointer, usize),
UpdateTexture(TextureCopyRegion<Texture>, DataPointer),
GenerateMipmap(ResourceView),
Clear(Option<command::ClearColor>, Option<Depth>, Option<Stencil>),
Draw(gl::types::GLenum, c::VertexCount, c::VertexCount, Option<command::InstanceParams>),
DrawIndexed(gl::types::GLenum,
gl::types::GLenum,
RawOffset,
c::VertexCount,
c::VertexCount,
Option<command::InstanceParams>),
_Blit(Rect, Rect, Mirror, usize),
}
pub fn generate_reset() -> Vec<Command> {
let color = s::Color {
mask: s::ColorMask::all(),
blend: None,
};
vec![
Command::BindProgram(0),
Command::BindVao,
Command::BindIndex(0),
Command::BindFrameBuffer(gl::FRAMEBUFFER, 0),
Command::SetRasterizer(s::Rasterizer {
front_face: s::FrontFace::CounterClockwise,
cull_face: s::CullFace::Back,
method: s::RasterMethod::Fill,
offset: None,
samples: None,
}),
Command::SetViewport(Rect {
x: 0,
y: 0,
w: 0,
h: 0,
}),
Command::SetScissor(None),
Command::SetDepthState(None),
Command::SetStencilState(None, (0, 0), s::CullFace::Nothing),
Command::SetBlendState(0, color),
Command::SetBlendState(1, color),
Command::SetBlendState(2, color),
Command::SetBlendState(3, color),
Command::SetBlendColor([0f32; 4]),
]
}
struct Cache {
primitive: gl::types::GLenum,
index_type: c::IndexType,
current_vbs: Option<c::pso::VertexBufferSet<Resources>>,
attributes: [Option<BufferElement>; c::MAX_VERTEX_ATTRIBUTES],
resource_binds: [Option<gl::types::GLenum>; c::MAX_RESOURCE_VIEWS],
scissor: bool,
target_dim: (u16, u16, u16),
stencil: Option<s::Stencil>,
cull_face: s::CullFace,
draw_mask: u32,
program: Program,
constant_buffer: Option<c::pso::ConstantBufferParam<Resources>>,
resource_view: Option<c::pso::ResourceViewParam<Resources>>,
scissor_test: Option<Rect>,
depth_state: Option<s::Depth>,
blend_state: Option<(c::ColorSlot, s::Color)>,
blend_color: Option<ColorValue>,
viewport: Option<Rect>,
rasterizer: Option<s::Rasterizer>,
framebuffer: Option<(Access, FrameBuffer)>,
index: Buffer,
}
impl Cache {
pub fn new() -> Cache {
Cache {
primitive: 0,
index_type: c::IndexType::U16,
current_vbs: None,
attributes: [None; c::MAX_VERTEX_ATTRIBUTES],
resource_binds: [None; c::MAX_RESOURCE_VIEWS],
scissor: false,
target_dim: (0, 0, 0),
stencil: None,
cull_face: s::CullFace::Nothing,
draw_mask: 0,
program: 0,
constant_buffer: None,
resource_view: None,
scissor_test: None,
depth_state: None,
blend_state: None,
blend_color: None,
viewport: None,
rasterizer: None,
framebuffer: None,
index: 0,
}
}
fn bind_program(&mut self, program: Program) -> Option<Command> {
if program == self.program {
return None;
}
self.program = program;
Some(Command::BindProgram(program))
}
fn bind_constant_buffer(&mut self, constant_buffer: c::pso::ConstantBufferParam<Resources>) -> Option<Command> {
if self.constant_buffer == Some(constant_buffer) {
return None;
}
self.constant_buffer = Some(constant_buffer);
Some(Command::BindConstantBuffer(constant_buffer))
}
fn bind_resource_view(&mut self, resource_view: c::pso::ResourceViewParam<Resources>) -> Option<Command> {
if self.resource_view == Some(resource_view) {
return None;
}
self.resource_view = Some(resource_view);
Some(Command::BindResourceView(resource_view))
}
fn bind_index(&mut self, buffer: Buffer, itype: c::IndexType) -> Option<Command> {
if self.index == buffer && itype == self.index_type {
return None;
}
self.index_type = itype;
self.index = buffer;
Some(Command::BindIndex(buffer))
}
fn bind_framebuffer(&mut self, access: Access, fb: FrameBuffer) -> Option<Command> {
if self.framebuffer == Some((access, fb)) {
return None;
}
self.framebuffer = Some((access, fb));
Some(Command::BindFrameBuffer(access, fb))
}
fn set_rasterizer(&mut self, rasterizer: s::Rasterizer) -> Option<Command> {
if self.rasterizer == Some(rasterizer) {
return None;
}
self.rasterizer = Some(rasterizer);
Some(Command::SetRasterizer(rasterizer))
}
fn set_viewport(&mut self, rect: Rect) -> Option<Command> {
if self.viewport == Some(rect) {
return None;
}
self.viewport = Some(rect);
Some(Command::SetViewport(rect))
}
fn set_scissor(&mut self, rect: Option<Rect>) -> Option<Command> {
if self.scissor_test == rect {
return None;
}
self.scissor_test = rect;
Some(Command::SetScissor(rect))
}
fn set_depth_state(&mut self, depth: Option<s::Depth>) -> Option<Command> {
if self.depth_state == depth {
return None;
}
self.depth_state = depth;
Some(Command::SetDepthState(depth))
}
fn set_stencil_state(&mut self, option_stencil: Option<s::Stencil>, stencils: (Stencil, Stencil), cullface: s:: CullFace) -> Option<Command> {
Some(Command::SetStencilState(option_stencil, stencils, cullface))
}
fn set_blend_state(&mut self, color_slot: c::ColorSlot, color: s::Color) -> Option<Command> {
if self.blend_state == Some((color_slot, color)) {
return None;
}
self.blend_state = Some((color_slot, color));
Some(Command::SetBlendState(color_slot, color))
}
fn set_blend_color(&mut self, color_value: ColorValue) -> Option<Command> {
if self.blend_color == Some(color_value) {
return None;
}
self.blend_color = Some(color_value);
Some(Command::SetBlendColor(color_value))
}
}
#[derive(Clone, Debug, Default)]
pub struct Workarounds {
pub main_fbo_mask: bool,
}
pub struct CommandBuffer {
pub buf: Vec<Command>,
pub data: DataBuffer,
fbo: FrameBuffer,
pub display_fb: FrameBuffer,
cache: Cache,
active_attribs: usize,
workarounds: Workarounds,
}
impl CommandBuffer {
pub fn new(fbo: FrameBuffer, workarounds: Workarounds) -> CommandBuffer {
CommandBuffer {
buf: Vec::new(),
data: DataBuffer::new(),
fbo,
display_fb: 0 as FrameBuffer,
cache: Cache::new(),
active_attribs: 0,
workarounds,
}
}
fn is_main_target(&self, tv: Option<TargetView>) -> bool {
match tv {
Some(TargetView::Surface(0)) |
None => true,
Some(_) => false,
}
}
}
impl command::Buffer<Resources> for CommandBuffer {
fn reset(&mut self) {
self.buf.clear();
self.data.0.clear();
self.cache = Cache::new();
self.active_attribs = (1 << c::MAX_VERTEX_ATTRIBUTES) - 1;
}
fn bind_pipeline_state(&mut self, pso: PipelineState) {
let cull = pso.rasterizer.cull_face;
self.cache.primitive = primitive_to_gl(pso.primitive);
self.cache.attributes = pso.input;
self.cache.stencil = pso.output.stencil;
self.cache.cull_face = cull;
self.cache.draw_mask = pso.output.draw_mask;
self.buf.extend(self.cache.bind_program(pso.program));
self.cache.scissor = pso.scissor;
self.buf.extend(self.cache.set_rasterizer(pso.rasterizer));
self.buf.extend(self.cache.set_depth_state(pso.output.depth));
self.buf.extend(self.cache.set_stencil_state(pso.output.stencil, (0, 0), cull));
for i in 0..c::MAX_COLOR_TARGETS {
if pso.output.draw_mask & (1 << i) != 0 {
self.buf.extend(self.cache.set_blend_state(i as c::ColorSlot,
pso.output.colors[i]));
}
}
if let c::Primitive::PatchList(num) = pso.primitive {
self.buf.push(Command::SetPatches(num));
}
}
fn bind_vertex_buffers(&mut self, vbs: c::pso::VertexBufferSet<Resources>) {
if self.cache.current_vbs == Some(vbs) {
return;
}
self.cache.current_vbs = Some(vbs);
for i in 0..c::MAX_VERTEX_ATTRIBUTES {
match (vbs.0[i], self.cache.attributes[i]) {
(None, Some(fm)) => {
error!("No vertex input provided for slot {} of format {:?}", i, fm)
}
(Some((buffer, offset)), Some(mut bel)) => {
bel.elem.offset += offset as gl::types::GLuint;
self.buf.push(Command::BindAttribute(
i as c::AttributeSlot,
buffer,
bel));
self.active_attribs |= 1 << i;
}
(_, None) if self.active_attribs & (1 << i) != 0 => {
self.buf.push(Command::UnbindAttribute(i as c::AttributeSlot));
self.active_attribs ^= 1 << i;
}
(_, None) => (),
}
}
}
fn bind_constant_buffers(&mut self, cbs: &[c::pso::ConstantBufferParam<Resources>]) {
for param in cbs.iter() {
self.buf.extend(self.cache.bind_constant_buffer(param.clone()));
}
}
fn bind_global_constant(&mut self, loc: c::shade::Location, value: c::shade::UniformValue) {
self.buf.push(Command::BindUniform(loc, value));
}
fn bind_resource_views(&mut self, srvs: &[c::pso::ResourceViewParam<Resources>]) {
for i in 0..c::MAX_RESOURCE_VIEWS {
self.cache.resource_binds[i] = None;
}
for param in srvs.iter() {
self.cache.resource_binds[param.2 as usize] = Some(param.0.bind);
self.buf.extend(self.cache.bind_resource_view(param.clone()));
}
}
fn bind_unordered_views(&mut self, uavs: &[c::pso::UnorderedViewParam<Resources>]) {
for param in uavs.iter() {
self.buf.push(Command::BindUnorderedView(param.clone()));
}
}
fn bind_samplers(&mut self, ss: &[c::pso::SamplerParam<Resources>]) {
for param in ss.iter() {
let bind = self.cache.resource_binds[param.2 as usize];
self.buf.push(Command::BindSampler(param.clone(), bind));
}
}
fn bind_pixel_targets(&mut self, pts: c::pso::PixelTargetSet<Resources>) {
let is_main = pts.colors.iter().skip(1).find(|c| c.is_some()).is_none() &&
self.is_main_target(pts.colors[0]) &&
self.is_main_target(pts.depth) &&
self.is_main_target(pts.stencil);
if is_main {
self.buf.extend(self.cache.bind_framebuffer(gl::DRAW_FRAMEBUFFER, self.display_fb));
if self.workarounds.main_fbo_mask {
self.buf.push(Command::SetOutputMasks {
color: pts.colors[0].is_some(),
depth: pts.depth.is_some(),
stencil: pts.stencil.is_some(),
});
if pts.colors[0].is_none() {
self.cache.blend_state = None;
}
if pts.depth.is_none() {
self.cache.depth_state = None;
}
if pts.stencil.is_none() {
}
}
} else {
let num = pts.colors
.iter()
.position(|c| c.is_none())
.unwrap_or(pts.colors.len()) as c::ColorSlot;
self.buf.extend(self.cache.bind_framebuffer(gl::DRAW_FRAMEBUFFER, self.fbo));
self.buf.push(Command::BindPixelTargets(pts));
self.buf.push(Command::SetDrawColorBuffers(num));
}
let view = pts.get_view();
self.cache.target_dim = view;
self.buf.extend(
self.cache.set_viewport(Rect {
x: 0,
y: 0,
w: view.0,
h: view.1,
}));
}
fn bind_index(&mut self, buf: Buffer, itype: c::IndexType) {
self.buf.extend(self.cache.bind_index(buf, itype));
}
fn set_scissor(&mut self, rect: Rect) {
use std::cmp;
let scissor = self.cache.scissor;
let target_dim = self.cache.target_dim;
let scissor_rect = if scissor {
Some(Rect {
y: cmp::max(target_dim.1, rect.y + rect.h) -
rect.y -
rect.h,
..rect
})
} else {
None
};
self.buf.extend(self.cache.set_scissor(scissor_rect));
}
fn set_ref_values(&mut self, rv: s::RefValues) {
let stencil = self.cache.stencil;
let cull_face = self.cache.cull_face;
self.buf.extend(self.cache.set_stencil_state(stencil,
rv.stencil,
cull_face));
self.buf.extend(self.cache.set_blend_color(rv.blend));
}
fn copy_buffer(&mut self,
src: Buffer,
dst: Buffer,
src_offset_bytes: usize,
dst_offset_bytes: usize,
size_bytes: usize) {
self.buf.push(Command::CopyBuffer(src, dst,
src_offset_bytes as gl::types::GLintptr,
dst_offset_bytes as gl::types::GLintptr,
size_bytes as gl::types::GLsizeiptr));
}
fn copy_buffer_to_texture(&mut self,
src: Buffer, src_offset_bytes: usize,
dst: TextureCopyRegion<NewTexture>) {
match dst.texture {
NewTexture::Texture(raw) => {
let dst = dst.with_texture(raw);
self.buf.push(Command::CopyBufferToTexture(
src, src_offset_bytes as _, dst
));
}
NewTexture::Surface(s) => {
error!("GL: Cannot copy to a Surface({})", s)
}
}
}
fn copy_texture_to_buffer(&mut self,
src: TextureCopyRegion<NewTexture>,
dst: Buffer, dst_offset_bytes: usize) {
self.buf.push(Command::CopyTextureToBuffer(
src, dst, dst_offset_bytes as _, self.fbo
));
self.cache.framebuffer = None;
}
fn copy_texture_to_texture(&mut self,
src: TextureCopyRegion<NewTexture>,
dst: TextureCopyRegion<NewTexture>) {
match dst.texture {
NewTexture::Texture(raw) => {
let dst = dst.with_texture(raw);
self.buf.push(Command::CopyTextureToTexture(src, dst, self.fbo));
self.cache.framebuffer = None;
}
NewTexture::Surface(s) => {
error!("GL: Cannot copy to a Surface({})", s)
}
}
}
fn update_buffer(&mut self, buf: Buffer, data: &[u8], offset_bytes: usize) {
let ptr = self.data.add(data);
self.buf.push(Command::UpdateBuffer(buf, ptr, offset_bytes));
}
fn update_texture(&mut self,
dst: TextureCopyRegion<NewTexture>,
data: &[u8]) {
let ptr = self.data.add(data);
match dst.texture {
NewTexture::Texture(raw) => {
let dst = dst.with_texture(raw);
self.buf.push(Command::UpdateTexture(dst, ptr))
}
NewTexture::Surface(s) => {
error!("GL: unable to update the contents of a Surface({})", s)
}
}
}
fn generate_mipmap(&mut self, srv: ResourceView) {
self.buf.push(Command::GenerateMipmap(srv));
}
fn clear_color(&mut self, target: TargetView, value: command::ClearColor) {
let mut pts = c::pso::PixelTargetSet::new();
pts.colors[0] = Some(target);
self.bind_pixel_targets(pts);
self.buf.push(Command::Clear(Some(value), None, None));
}
fn clear_depth_stencil(&mut self,
target: TargetView,
depth: Option<Depth>,
stencil: Option<Stencil>) {
let mut pts = c::pso::PixelTargetSet::new();
if depth.is_some() {
pts.depth = Some(target);
}
if stencil.is_some() {
pts.stencil = Some(target);
}
self.bind_pixel_targets(pts);
self.buf.push(Command::Clear(None, depth, stencil));
}
fn call_draw(&mut self,
start: c::VertexCount,
count: c::VertexCount,
instances: Option<command::InstanceParams>) {
self.buf.push(Command::Draw(self.cache.primitive, start, count, instances));
}
fn call_draw_indexed(&mut self,
start: c::VertexCount,
count: c::VertexCount,
base: c::VertexCount,
instances: Option<command::InstanceParams>) {
let (offset, gl_index) = match self.cache.index_type {
c::IndexType::U16 => (start * 2u32, gl::UNSIGNED_SHORT),
c::IndexType::U32 => (start * 4u32, gl::UNSIGNED_INT),
};
self.buf.push(
Command::DrawIndexed(
self.cache.primitive,
gl_index,
RawOffset(offset as *const gl::types::GLvoid),
count,
base,
instances));
}
}