mallumo-gls 0.21.0

Small low level library for modern (4.5 Core) OpenGL
Documentation
use super::framebuffer::*;
use super::texture::*;
use super::pipeline::*;
use super::buffer::*;
use super::raw::*;
use vertex_array_object::*;
use compute_program::ComputeProgram;

use std;
use glutin;

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<'a, C: glutin::GlContext + 'a> {
    context: &'a C,
    state: FramebufferState,
    vertex_array_object: VertexArrayObjectId,
    default_framebuffer: DefaultFramebuffer,
}

impl<'a, C: glutin::GlContext> Renderer<'a, C> {
    pub fn new(context: &'a C) -> Result<Renderer<'a, C>> {
        unsafe {
            context
                .make_current()
                .chain_err(|| "Unable to make Renderer context current")?;
        }

        gl::load_with(|symbol| context.get_proc_address(symbol) as *const _);

        // Create empty VAO to satisfy OpenGL Core requirement
        let vao;
        unsafe {
            vao = create_vertex_array();
            bind_vertex_array(vao);
        }

        Ok(Renderer {
            context: context,
            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 multi_draw_arrays_indirect(
        &mut self,
        framebuffer: &FramebufferRenderTarget,
        buffers: &[BufferObjectTargetLocation],
        textures: &[&Texture],
        pipeline: &Pipeline,
        mode: DrawMode,
        draw_commands_buffer: (&Buffer, usize),
        clear_buffers: ClearBuffers,
    ) -> Result<()> {
        match *framebuffer {
            FramebufferRenderTarget::Default => self.bind_default_framebuffer(),
            FramebufferRenderTarget::General(framebuffer, attachments) => {
                self.bind_framebuffer(framebuffer);
                framebuffer
                    .attach_textures(attachments)
                    .chain_err(|| "Could not attach textures")?;
            }
        }

        if let &FramebufferRenderTarget::General(fbo, attachments) = framebuffer {
            fbo.attach_textures(attachments)
                .chain_err(|| "Could not attach textures")?;
        };


        let res_textures: Vec<ResidentTexture> = textures
            .iter()
            .map(|t| t.make_resident_texture())
            .collect::<_>();

        unsafe {
            if clear_buffers != ClearBuffers::None {
                clear(clear_buffers);
            }
        }

        pipeline.bind().chain_err(|| "Could not bind pipeline")?;

        // TODO refactor into 4 bind buffers
        for buffer_target in buffers {
            unsafe {
                bind_buffer_base(
                    buffer_target.target,
                    buffer_target.index,
                    buffer_target.buffer.get_id(),
                ).chain_err(
                    || format!("Unable to bind buffer {:?}", buffer_target),
                )?;
            }
        }

        unsafe {
            bind_buffer(
                BindBufferTarget::DrawIndirectBuffer,
                draw_commands_buffer.0.get_id(),
            ).chain_err(|| "Could not bind draw indirect buffer")?;
            multi_draw_arrays_indirect(mode.into(), draw_commands_buffer.1)
                .chain_err(|| "Could not draw")?;
        }

        Ok(())
    }

    ///
    /// # Arguments
    ///
    /// * `framebuffer`
    /// * `buffers`
    /// * `textures`
    /// * `pipeline`
    /// * `draw commands buffer`
    /// * `clear buffers`
    ///
    /// # Example
    ///
    pub fn multi_draw_elements_indirect(
        &mut self,
        framebuffer: &FramebufferRenderTarget,
        buffers: &[BufferObjectTargetLocation],
        textures: &[&Texture],
        pipeline: &Pipeline,
        mode: DrawMode,
        draw_commands_buffer: (&Buffer, usize),
        clear_buffers: ClearBuffers,
    ) -> Result<()> {
        // Bind framebuffer (which also syncs its state) and attach textures
        // in case of GeneralFramebuffer
        match *framebuffer {
            FramebufferRenderTarget::Default => self.bind_default_framebuffer(),
            FramebufferRenderTarget::General(framebuffer, attachments) => {
                self.bind_framebuffer(framebuffer);
                framebuffer
                    .attach_textures(attachments)
                    .chain_err(|| "Could not attach textures")?;
            }
        }

        // Make needed textures resident
        let res_textures: Vec<ResidentTexture> = textures
            .iter()
            .map(|t| t.make_resident_texture())
            .collect::<_>();

        // Clear the requested buffers
        unsafe {
            if clear_buffers != ClearBuffers::None {
                clear(clear_buffers);
            }
        }

        // Bind the pipeline
        pipeline.bind().chain_err(|| "Could not bind pipeline")?;

        // Bind buffers
        // TODO refactor into 4 bind buffers
        for buffer_target in buffers {
            unsafe {
                bind_buffer_base(
                    buffer_target.target,
                    buffer_target.index,
                    buffer_target.buffer.get_id(),
                ).chain_err(
                    || format!("Unable to bind buffer {:?}", buffer_target),
                )?;
            }
        }

        // Bind draw command buffer
        // Draw
        unsafe {
            bind_buffer(
                BindBufferTarget::DrawIndirectBuffer,
                draw_commands_buffer.0.get_id(),
            ).chain_err(|| "Could not bind draw indirect buffer")?;

            multi_draw_elements_indirect(mode.into(), draw_commands_buffer.1)
                .chain_err(|| "Could not draw")?;
        }

        Ok(())
    }

    pub fn draw_arrays(
        &mut self,
        framebuffer: &FramebufferRenderTarget,
        buffers: &[BufferObjectTargetLocation],
        textures: &[&Texture],
        pipeline: &Pipeline,
        mode: DrawMode,
        first: usize,
        count: usize,
        clear_buffers: ClearBuffers,
    ) -> Result<()> {
        // Bind framebuffer (which also syncs its state) and attach textures
        // in case of GeneralFramebuffer
        match *framebuffer {
            FramebufferRenderTarget::Default => self.bind_default_framebuffer(),
            FramebufferRenderTarget::General(framebuffer, attachments) => {
                self.bind_framebuffer(framebuffer);
                framebuffer
                    .attach_textures(attachments)
                    .chain_err(|| "Could not attach textures")?;
            }
        }

        // Make needed textures resident
        let res_textures: Vec<ResidentTexture> = textures
            .iter()
            .map(|t| t.make_resident_texture())
            .collect::<_>();

        // Clear the requested buffers
        unsafe {
            if clear_buffers != ClearBuffers::None {
                clear(clear_buffers);
            }
        }

        // Bind the pipeline
        pipeline.bind().chain_err(|| "Could not bind pipeline")?;

        // Bind buffers
        // TODO refactor into 4 bind buffers
        for buffer_target in buffers {
            unsafe {
                bind_buffer_base(
                    buffer_target.target,
                    buffer_target.index,
                    buffer_target.buffer.get_id(),
                ).chain_err(
                    || format!("Unable to bind buffer {:?}", buffer_target),
                )?;
            }
        }

        // Bind draw command buffer
        // Draw
        unsafe {
            draw_arrays(mode, first, count).chain_err(|| "Could not draw")?;
        }

        Ok(())
    }

    pub fn dispatch_compute(
        &mut self,
        buffers: &[BufferObjectTargetLocation],
        textures: &[&Texture],
        program: &ComputeProgram,
        command: DispatchCommand,
    ) -> Result<()> {
        let res_textures: Vec<ResidentTexture> = textures
            .iter()
            .map(|t| t.make_resident_texture())
            .collect::<_>();

        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],
        textures: &[&Texture],
        program: &ComputeProgram,
        command_buffer: &T,
        command_offset: usize,
    ) -> Result<()> {
        let res_textures: Vec<ResidentTexture> = textures
            .iter()
            .map(|t| t.make_resident_texture())
            .collect::<_>();

        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 default_framebuffer(&self) -> &DefaultFramebuffer {
        &self.default_framebuffer
    }

    pub fn mut_default_framebuffer(&mut self) -> &mut DefaultFramebuffer {
        &mut self.default_framebuffer
    }

    pub fn make_current(&mut self) -> Result<()> {
        unsafe {
            self.context
                .make_current()
                .chain_err(|| "Unable to make Renderer context current")?;
        }

        Ok(())
    }

    pub fn swap_buffers(&mut self) -> Result<()> {
        self.context
            .swap_buffers()
            .chain_err(|| "Unable to swap buffers")?;

        Ok(())
    }

    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<'a, C: glutin::GlContext> Drop for Renderer<'a, C> {
    fn drop(&mut self) {
        unsafe {
            delete_vertex_array(self.vertex_array_object);
        }
    }
}