use super::framebuffer::*;
use super::texture::*;
use super::buffer::*;
use super::raw::*;
use super::uniform::*;
use vertex_array_object::*;
use draw::*;
use compute_program::ComputeProgram;
use std;
use glutin;
use glutin::GlContext;
mod errors {
error_chain! {
errors {
}
}
}
use self::errors::*;
#[derive(Copy, Clone)]
pub struct BufferObjectTargetLocation<'a> {
pub target: BufferBaseTarget,
pub index: BlockIndex,
pub buffer: &'a Buffer,
}
impl<'a> std::fmt::Debug for BufferObjectTargetLocation<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"BufferObjectTargetLocation {{ target: {:?}, index: {:?}, buffer id: {:?} }}",
self.target,
self.index,
self.buffer.get_id()
)
}
}
pub struct Renderer {
state: FramebufferState,
vertex_array_object: VertexArrayObjectId,
default_framebuffer: DefaultFramebuffer,
}
impl Renderer {
pub fn new(context: &glutin::Context) -> Result<Renderer> {
unsafe {
context
.make_current()
.chain_err(|| "Unable to make Renderer context current")?;
}
gl::load_with(|symbol| context.get_proc_address(symbol) as *const _);
let vao;
unsafe {
vao = create_vertex_array();
bind_vertex_array(vao);
}
Ok(Renderer {
state: FramebufferState::default(),
vertex_array_object: vao,
default_framebuffer: DefaultFramebuffer::new(),
})
}
pub fn bind_default_framebuffer(&mut self) {
self.default_framebuffer.bind();
self.state.sync(&self.default_framebuffer.state());
}
pub fn bind_framebuffer(&mut self, framebuffer: &GeneralFramebuffer) {
framebuffer.bind();
self.state.sync(&framebuffer.state());
}
pub fn clear_default_framebuffer(&mut self, buffers: ClearBuffers) -> () {
self.bind_default_framebuffer();
unsafe {
clear(buffers);
}
}
pub fn clear_framebuffer(&mut self, framebuffer: &GeneralFramebuffer, buffers: ClearBuffers) -> () {
self.bind_framebuffer(framebuffer);
unsafe {
clear(buffers);
}
}
pub fn unbind_all(&mut self) -> Result<()> {
self.bind_default_framebuffer();
unsafe {
for i in 0..16 {
bind_image_texture(
i,
TextureId::empty(),
0,
None,
ImageAccess::ReadWrite,
ImageInternalFormat::RGBA8,
).chain_err(|| "Could not unbind images")?;
}
}
unsafe {
for i in 0..16 {
bind_texture_unit(i, TextureId::empty());
}
}
unsafe {
for target in [BufferBaseTarget::Uniform, BufferBaseTarget::ShaderStorage].iter() {
gl::BindBuffersBase(target.into(), 0, 4, std::ptr::null());
}
}
Ok(())
}
pub fn draw(&mut self, draw_command: &DrawCommand) -> Result<()> {
if let Some(ref framebuffer) = draw_command.framebuffer {
if let Some(ref attachments) = draw_command.attachments {
framebuffer
.attach_textures(attachments)
.chain_err(|| "Could not attach textures")?;
} else {
framebuffer
.attach_textures(&DrawTextureTarget::default())
.chain_err(|| "Could not attach textures")?;
}
self.bind_framebuffer(framebuffer);
} else {
self.bind_default_framebuffer();
}
if let Some(pipeline) = draw_command.pipeline {
pipeline.bind().chain_err(|| "Could not bind pipeline")?;
} else {
draw_command
.compute
.unwrap()
.bind()
.chain_err(|| "Could not bind program")?;
}
for image in draw_command.images.iter() {
(image.0)
.bind_image(image.1, image.2, image.3)
.chain_err(|| "Could not bind image")?;
}
for image in draw_command.images_3d.iter() {
(image.0)
.bind_image(image.1, image.2, image.3)
.chain_err(|| "Could not bind image")?;
}
for image in draw_command.images_read.iter() {
(image.0)
.bind_image_read(image.1, image.2, image.3)
.chain_err(|| "Could not bind image for reading")?;
}
for image in draw_command.images_write.iter() {
(image.0)
.bind_image_write(image.1, image.2, image.3)
.chain_err(|| "Could not bind image for writing")?;
}
for texture in draw_command.textures_1d.iter() {
(texture.0).bind_texture(texture.1);
}
for texture in draw_command.textures_2d.iter() {
(texture.0).bind_texture(texture.1);
}
for texture in draw_command.textures_3d.iter() {
(texture.0).bind_texture(texture.1);
}
for texture in draw_command.textures_cubemap.iter() {
(texture.0).bind_texture(texture.1);
}
for texture_1d_array in draw_command.texture_1d_arrays.iter() {
let raw_ids: Vec<TextureId> = texture_1d_array
.0
.iter()
.map(|texture| match *texture {
Some(ref tex) => tex.get_id(),
None => TextureId::empty(),
})
.collect();
unsafe {
bind_textures(texture_1d_array.1, raw_ids.as_slice()).chain_err(|| "Could not bind texture 1d array")?;
}
}
for texture_2d_array in draw_command.texture_2d_arrays.iter() {
let raw_ids: Vec<TextureId> = texture_2d_array
.0
.iter()
.map(|texture| match *texture {
Some(ref tex) => tex.get_id(),
None => TextureId::empty(),
})
.collect();
unsafe {
bind_textures(texture_2d_array.1, raw_ids.as_slice()).chain_err(|| "Could not bind texture 2d array")?;
}
}
for texture_3d_array in draw_command.texture_3d_arrays.iter() {
let raw_ids: Vec<TextureId> = texture_3d_array
.0
.iter()
.map(|texture| match *texture {
Some(ref tex) => tex.get_id(),
None => TextureId::empty(),
})
.collect();
unsafe {
bind_textures(texture_3d_array.1, raw_ids.as_slice()).chain_err(|| "Could not bind texture 3d array")?;
}
}
for texture_cube_array in draw_command.texture_cubemap_arrays.iter() {
let raw_ids: Vec<TextureId> = texture_cube_array
.0
.iter()
.map(|texture| match *texture {
Some(ref tex) => tex.get_id(),
None => TextureId::empty(),
})
.collect();
unsafe {
bind_textures(texture_cube_array.1, raw_ids.as_slice())
.chain_err(|| "Could not bind texture cube array")?;
}
}
for storage in draw_command.storages.iter() {
(storage.0)
.bind_base(BufferBaseTarget::ShaderStorage, storage.1)
.chain_err(|| "Could not bind storage buffer")?;
}
for storage in draw_command.storage_ranges.iter() {
(storage.0)
.bind_range(BufferBaseTarget::ShaderStorage, storage.1, storage.2, storage.3)
.chain_err(|| "Could not bind storage range buffer")?;
}
for storage in draw_command.storages_read.iter() {
(storage.0)
.bind_base(BufferBaseTarget::ShaderStorage, storage.1)
.chain_err(|| "Could not bind storage buffer for reading")?;
}
for storage in draw_command.storage_range_reads.iter() {
(storage.0)
.bind_range(BufferBaseTarget::ShaderStorage, storage.1, storage.2, storage.3)
.chain_err(|| "Could not bind storage buffer range for reading")?;
}
for uniform in draw_command.uniforms.iter() {
if let Some(pipeline) = draw_command.pipeline {
program_uniform(pipeline.get_id(), uniform.1, uniform.0).chain_err(|| "Could not upload uniform")?;
}
if let Some(compute) = draw_command.compute {
program_uniform(compute.get_id(), uniform.1, uniform.0).chain_err(|| "Could not upload uniform")?;
}
}
for uniform in draw_command.uniform_buffers.iter() {
(uniform.0)
.bind_base(BufferBaseTarget::Uniform, uniform.1)
.chain_err(|| "Could not bind uniform buffer")?;
}
for uniform in draw_command.uniform_ranges.iter() {
(uniform.0)
.bind_range(BufferBaseTarget::Uniform, uniform.1, uniform.2, uniform.3)
.chain_err(|| "Could not bind UBO range")?;
}
for atomic in draw_command.atomic_counters.iter() {
(atomic.0)
.bind_base(BufferBaseTarget::AtomicCounter, atomic.1)
.chain_err(|| "Could not bind atomic buffer")?;
}
match draw_command.draw_command_type {
DrawCommandType::DrawArrays { first, count } => unsafe {
draw_arrays(draw_command.mode, first, count).chain_err(|| "Failure in draw command")?;
},
DrawCommandType::DrawArraysIndirect { indirect, offset } => {
indirect
.bind_as_indirect()
.chain_err(|| "Could not bind indirect buffer")?;
unsafe {
draw_arrays_indirect(draw_command.mode, offset).chain_err(|| "Failure in draw command")?;
}
}
DrawCommandType::DrawArraysInstanced {
first,
count,
primitive_count,
} => unsafe {
draw_arrays_instanced(draw_command.mode, first, count, primitive_count)
.chain_err(|| "Failure in draw command")?;
},
DrawCommandType::DrawArraysInstancedBaseInstance {
first,
count,
primitive_count,
base_instance,
} => unsafe {
draw_arrays_instanced_base_instace(draw_command.mode, first, count, primitive_count, base_instance)
.chain_err(|| "Failure in draw command")?;
},
DrawCommandType::MultiDrawArrays {
first,
count,
draw_count,
} => unsafe {
multi_draw_arrays(draw_command.mode, first, count, draw_count).chain_err(|| "Failure in draw command")?;
},
DrawCommandType::MultiDrawArraysIndirect {
indirect,
draw_count,
stride,
} => {
indirect
.bind_as_indirect()
.chain_err(|| "Could not bind indirect buffer")?;
unsafe {
multi_draw_arrays_indirect(draw_command.mode, draw_count, stride)
.chain_err(|| "Failure in draw command")?;
}
},
DrawCommandType::MultiDrawElementsIndirect {
indices,
indirect,
draw_count,
stride,
} => {
unsafe {
bind_buffer(BindBufferTarget::ElementArrayBuffer, indices.get_id());
}
indirect
.bind_as_indirect()
.chain_err(|| "Could not bind indirect buffer")?;
unsafe {
multi_draw_elements_indirect(draw_command.mode, draw_count, stride)
.chain_err(|| "Failure in draw command")?;
}
},
DrawCommandType::Compute {
num_groups_x,
num_groups_y,
num_groups_z,
} => unsafe {
dispatch_compute(num_groups_x as u32, num_groups_y as u32, num_groups_z as u32)
.chain_err(|| "Error during command dispatch")?;
},
}
if let Some(barriers) = draw_command.barriers {
self.memory_barrier(barriers);
}
Ok(())
}
pub fn memory_barrier(&mut self, barriers: MemoryBarriers) {
unsafe {
memory_barrier(barriers);
}
}
pub fn dispatch_compute(
&mut self,
buffers: &[BufferObjectTargetLocation],
program: &ComputeProgram,
command: DispatchCommand,
) -> Result<()> {
program.bind().chain_err(|| "Unable to bind program")?;
for buffer_target in buffers {
unsafe {
bind_buffer_base(buffer_target.target, buffer_target.index, buffer_target.buffer.get_id())
.chain_err(|| "Unable to bind buffer")?;
}
}
unsafe {
dispatch_compute(command.num_groups_x, command.num_groups_y, command.num_groups_z)
.chain_err(|| "Error during command dispatch")?;
}
Ok(())
}
pub fn dispatch_compute_indirect<T: Buffer>(
&mut self,
buffers: &[BufferObjectTargetLocation],
program: &ComputeProgram,
command_buffer: &T,
command_offset: usize,
) -> Result<()> {
program.bind().chain_err(|| "Unable to bind program")?;
for buffer_target in buffers {
unsafe {
bind_buffer_base(buffer_target.target, buffer_target.index, buffer_target.buffer.get_id())
.chain_err(|| "Unable to bind buffer")?;
}
}
unsafe {
bind_buffer(BindBufferTarget::DispatchIndirectBuffer, command_buffer.get_id())
.chain_err(|| "Could not bind dispatch indirect buffer")?;
dispatch_compute_indirect(command_offset).chain_err(|| "Error during command dispatch")?;
}
Ok(())
}
pub fn get_default_framebuffer(&self) -> &DefaultFramebuffer {
&self.default_framebuffer
}
pub fn get_mut_default_framebuffer(&mut self) -> &mut DefaultFramebuffer {
&mut self.default_framebuffer
}
pub fn get_state(&self) -> FramebufferState {
self.state
}
pub fn set_state(&mut self, state: FramebufferState) {
self.state = state;
}
pub fn set_viewport(&mut self, viewport: Viewport) {
self.default_framebuffer.set_viewport(viewport);
}
pub fn set_enable(&mut self, option: EnableOption) {
self.default_framebuffer.set_enable(option);
}
pub fn set_disable(&mut self, option: EnableOption) {
self.default_framebuffer.set_disable(option);
}
pub fn set_stencil_test(&mut self, face: Face, stencil_test: StencilTest) {
self.default_framebuffer.set_stencil_test(face, stencil_test);
}
pub fn set_depth_test(&mut self, depth_test: DepthTest) {
self.default_framebuffer.set_depth_test(depth_test);
}
pub fn set_depth_mask(&mut self, depth_mask: DepthMask) {
self.default_framebuffer.set_depth_mask(depth_mask);
}
pub fn set_depth_range(&mut self, depth_range: DepthRange) {
self.default_framebuffer.set_depth_range(depth_range);
}
pub fn set_logic_operation(&mut self, logic_operation: LogicOperation) {
self.default_framebuffer.set_logic_operation(logic_operation);
}
pub fn set_blending_equation(
&mut self,
blending_equation_rgb: BlendingEquation,
blending_equation_alpha: BlendingEquation,
) {
self.default_framebuffer
.set_blending_equation(blending_equation_rgb, blending_equation_alpha);
}
pub fn set_linear_blending_factors(
&mut self,
source_rgb: LinearBlendingFactor,
destination_rgb: LinearBlendingFactor,
source_alpha: LinearBlendingFactor,
destination_alpha: LinearBlendingFactor,
) {
self.default_framebuffer.set_linear_blending_factors(
source_rgb,
destination_rgb,
source_alpha,
destination_alpha,
);
}
pub fn set_face_orientation(&mut self, face_orientation: FaceOrientation) {
self.default_framebuffer.set_face_orientation(face_orientation);
}
pub fn set_cull_face(&mut self, cull_face: Face) {
self.default_framebuffer.set_cull_face(cull_face);
}
pub fn set_clear_color(&mut self, clear_color: ClearColor) {
self.default_framebuffer.set_clear_color(clear_color);
}
pub fn set_color_mask(&mut self, color_mask: [bool; 4]) {
self.default_framebuffer.set_color_mask(color_mask);
}
}
impl Drop for Renderer {
fn drop(&mut self) {
unsafe {
delete_vertex_array(self.vertex_array_object);
}
}
}