mallumo-gls 0.43.0

Small low level library for modern (4.5 Core) OpenGL
Documentation
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 _);

        // Create empty VAO to satisfy OpenGL Core requirement
        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();

        // Images
        unsafe {
            for i in 0..16 {
                bind_image_texture(
                    i,
                    TextureId::empty(),
                    0,
                    None,
                    ImageAccess::ReadWrite,
                    ImageInternalFormat::RGBA8,
                ).chain_err(|| "Could not unbind images")?;
            }
        }

        // Textures
        unsafe {
            for i in 0..16 {
                bind_texture_unit(i, TextureId::empty());
            }
        }

        // Uniforms, Storages
        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<()> {
        // Bind framebuffer along with requested state
        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();
        }

        // Bind pipeline
        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")?;
        }

        // Bind images
        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")?;
        }

        // Bind textures
        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")?;
            }
        }

        // Bind storages
        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")?;
        }

        // Bind uniforms
        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")?;
        }

        // Bind atomics
        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);
        }
    }
}