From 12f48046b2606fc77a1312a6d5e5fc7ff9feff88 Mon Sep 17 00:00:00 2001 From: HampusM Date: Fri, 13 Oct 2023 23:32:00 +0200 Subject: refactor(engine): move uses of OpenGL to OpenGL module --- Cargo.lock | 1 + engine/Cargo.toml | 1 + engine/src/currently_bound.rs | 22 ---- engine/src/lib.rs | 3 +- engine/src/object.rs | 13 ++- engine/src/opengl/currently_bound.rs | 22 ++++ engine/src/opengl/mod.rs | 38 +++++++ engine/src/opengl/shader.rs | 198 +++++++++++++++++++++++++++++++++++ engine/src/opengl/vertex_array.rs | 131 +++++++++++++++++++++++ engine/src/opengl/vertex_buffer.rs | 93 ++++++++++++++++ engine/src/renderer/mod.rs | 35 +++---- engine/src/renderer/vertex_array.rs | 131 ----------------------- engine/src/renderer/vertex_buffer.rs | 93 ---------------- engine/src/shader.rs | 198 ----------------------------------- 14 files changed, 506 insertions(+), 473 deletions(-) delete mode 100644 engine/src/currently_bound.rs create mode 100644 engine/src/opengl/currently_bound.rs create mode 100644 engine/src/opengl/mod.rs create mode 100644 engine/src/opengl/shader.rs create mode 100644 engine/src/opengl/vertex_array.rs create mode 100644 engine/src/opengl/vertex_buffer.rs delete mode 100644 engine/src/renderer/vertex_array.rs delete mode 100644 engine/src/renderer/vertex_buffer.rs delete mode 100644 engine/src/shader.rs diff --git a/Cargo.lock b/Cargo.lock index df893dd..13b0fcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,6 +85,7 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" name = "engine" version = "0.1.0" dependencies = [ + "bitflags", "gl", "glfw", "thiserror", diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 7b42cab..40b2e9f 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" glfw = { path = "../glfw" } thiserror = "1.0.49" gl = "0.14.0" +bitflags = "2.4.0" diff --git a/engine/src/currently_bound.rs b/engine/src/currently_bound.rs deleted file mode 100644 index eefa239..0000000 --- a/engine/src/currently_bound.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::marker::PhantomData; - -/// A token signifying a OpenGL object is currently bound. -pub struct CurrentlyBound<'token, Object> -{ - _token: PhantomData<&'token Object>, -} - -impl<'token, Object> CurrentlyBound<'token, Object> -{ - /// Returns a new `CurrentlyBound`. - /// - /// # Safety - /// A object must actually be currently bound. Otherwise, UB can occur. - #[must_use] - pub unsafe fn new() -> Self - { - Self { - _token: PhantomData, - } - } -} diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 5b313ec..e036c38 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -6,11 +6,10 @@ pub use glfw::WindowSize; use crate::object::Object; use crate::vector::Vec2; +mod opengl; mod renderer; -mod shader; pub mod color; -pub mod currently_bound; pub mod object; pub mod vector; pub mod vertex; diff --git a/engine/src/object.rs b/engine/src/object.rs index 6b4bf4e..a650abc 100644 --- a/engine/src/object.rs +++ b/engine/src/object.rs @@ -1,5 +1,10 @@ +use crate::opengl::shader::{ + Error as ShaderError, + Kind as ShaderKind, + Program as ShaderProgram, + Shader, +}; use crate::renderer::Renderable; -use crate::shader::{Kind as ShaderKind, Program as ShaderProgram, Shader}; use crate::vertex::Vertex; #[derive(Debug)] @@ -61,11 +66,11 @@ impl Object pub enum Error { #[error("Failed to create vertex shader")] - CreateVertexShaderFailed(#[source] crate::shader::Error), + CreateVertexShaderFailed(#[source] ShaderError), #[error("Failed to create fragment shader")] - CreateFragmentShaderFailed(#[source] crate::shader::Error), + CreateFragmentShaderFailed(#[source] ShaderError), #[error("Failed to link shader program")] - LinkShaderProgramFailed(#[source] crate::shader::Error), + LinkShaderProgramFailed(#[source] ShaderError), } diff --git a/engine/src/opengl/currently_bound.rs b/engine/src/opengl/currently_bound.rs new file mode 100644 index 0000000..eefa239 --- /dev/null +++ b/engine/src/opengl/currently_bound.rs @@ -0,0 +1,22 @@ +use std::marker::PhantomData; + +/// A token signifying a OpenGL object is currently bound. +pub struct CurrentlyBound<'token, Object> +{ + _token: PhantomData<&'token Object>, +} + +impl<'token, Object> CurrentlyBound<'token, Object> +{ + /// Returns a new `CurrentlyBound`. + /// + /// # Safety + /// A object must actually be currently bound. Otherwise, UB can occur. + #[must_use] + pub unsafe fn new() -> Self + { + Self { + _token: PhantomData, + } + } +} diff --git a/engine/src/opengl/mod.rs b/engine/src/opengl/mod.rs new file mode 100644 index 0000000..a58e72e --- /dev/null +++ b/engine/src/opengl/mod.rs @@ -0,0 +1,38 @@ +use bitflags::bitflags; +use glfw::WindowSize; + +use crate::vector::Vec2; + +pub mod currently_bound; +pub mod shader; +pub mod vertex_array; +pub mod vertex_buffer; + +pub fn set_viewport(position: &Vec2, size: &WindowSize) +{ + unsafe { + #[allow(clippy::cast_possible_wrap)] + gl::Viewport( + position.x as i32, + position.y as i32, + size.width as i32, + size.height as i32, + ); + } +} + +pub fn clear_buffers(mask: BufferClearMask) +{ + unsafe { + gl::Clear(mask.bits()); + } +} + +bitflags! { + #[derive(Debug, Clone, Copy)] + pub struct BufferClearMask: u32 { + const COLOR = gl::COLOR_BUFFER_BIT; + const DEPTH = gl::DEPTH_BUFFER_BIT; + const STENCIL = gl::STENCIL_BUFFER_BIT; + } +} diff --git a/engine/src/opengl/shader.rs b/engine/src/opengl/shader.rs new file mode 100644 index 0000000..0bbca77 --- /dev/null +++ b/engine/src/opengl/shader.rs @@ -0,0 +1,198 @@ +use std::ffi::CStr; +use std::ptr::null_mut; + +#[derive(Debug)] +pub struct Shader +{ + shader: gl::types::GLuint, +} + +impl Shader +{ + pub fn new(kind: Kind) -> Self + { + let shader = unsafe { gl::CreateShader(kind.into_gl()) }; + + Self { shader } + } + + pub fn set_source(&self, source: &str) -> Result<(), Error> + { + if !source.is_ascii() { + return Err(Error::SourceNotAscii); + } + + unsafe { + #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] + gl::ShaderSource( + self.shader, + 1, + &source.as_ptr().cast(), + &(source.len() as gl::types::GLint), + ); + } + + Ok(()) + } + + pub fn compile(&self) -> Result<(), Error> + { + unsafe { + gl::CompileShader(self.shader); + } + + let mut compile_success = gl::types::GLint::default(); + + unsafe { + gl::GetShaderiv(self.shader, gl::COMPILE_STATUS, &mut compile_success); + } + + if compile_success == 0 { + let info_log = self.get_info_log(); + + return Err(Error::CompileFailed(info_log)); + } + + Ok(()) + } + + fn get_info_log(&self) -> String + { + let mut buf = vec![gl::types::GLchar::default(); 512]; + + unsafe { + #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] + gl::GetShaderInfoLog( + self.shader, + buf.len() as gl::types::GLsizei, + null_mut(), + buf.as_mut_ptr(), + ); + } + + let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) }; + + unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) } + } +} + +impl Drop for Shader +{ + fn drop(&mut self) + { + unsafe { + gl::DeleteShader(self.shader); + } + } +} + +/// Shader kind. +#[derive(Debug, Clone, Copy)] +pub enum Kind +{ + Vertex, + Fragment, +} + +impl Kind +{ + fn into_gl(self) -> gl::types::GLenum + { + match self { + Self::Vertex => gl::VERTEX_SHADER, + Self::Fragment => gl::FRAGMENT_SHADER, + } + } +} + +/// Shader program +#[derive(Debug)] +pub struct Program +{ + program: gl::types::GLuint, +} + +impl Program +{ + pub fn new() -> Self + { + let program = unsafe { gl::CreateProgram() }; + + Self { program } + } + + pub fn attach(&self, shader: &Shader) + { + unsafe { + gl::AttachShader(self.program, shader.shader); + } + } + + pub fn link(&self) -> Result<(), Error> + { + unsafe { + gl::LinkProgram(self.program); + } + + let mut link_success = gl::types::GLint::default(); + + unsafe { + gl::GetProgramiv(self.program, gl::LINK_STATUS, &mut link_success); + } + + if link_success == 0 { + let info_log = self.get_info_log(); + + return Err(Error::CompileFailed(info_log)); + } + + Ok(()) + } + + pub fn activate(&self) + { + unsafe { + gl::UseProgram(self.program); + } + } + + fn get_info_log(&self) -> String + { + let mut buf = vec![gl::types::GLchar::default(); 512]; + + unsafe { + #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] + gl::GetProgramInfoLog( + self.program, + buf.len() as gl::types::GLsizei, + null_mut(), + buf.as_mut_ptr(), + ); + } + + let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) }; + + unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) } + } +} + +impl Drop for Program +{ + fn drop(&mut self) + { + unsafe { + gl::DeleteProgram(self.program); + } + } +} + +/// Shader error. +#[derive(Debug, thiserror::Error)] +pub enum Error +{ + #[error("All characters in source are not within the ASCII range")] + SourceNotAscii, + + #[error("Failed to compile: {0}")] + CompileFailed(String), +} diff --git a/engine/src/opengl/vertex_array.rs b/engine/src/opengl/vertex_array.rs new file mode 100644 index 0000000..f255b8a --- /dev/null +++ b/engine/src/opengl/vertex_array.rs @@ -0,0 +1,131 @@ +use std::mem::size_of; + +use crate::opengl::currently_bound::CurrentlyBound; +use crate::opengl::vertex_buffer::VertexBuffer; +use crate::vertex::{Attribute, AttributeComponentType, Vertex}; + +const VERTEX_STRIDE: usize = size_of::(); + +#[derive(Debug)] +pub struct VertexArray +{ + array: gl::types::GLuint, +} + +impl VertexArray +{ + pub fn new() -> Self + { + let mut array = 0; + + unsafe { + gl::GenVertexArrays(1, &mut array); + } + + Self { array } + } + + /// Draws the currently bound vertex array. + pub fn draw( + _currently_bound: &CurrentlyBound, + primitive_kind: PrimitiveKind, + start_index: u32, + index_cnt: u32, + ) + { + unsafe { + #[allow(clippy::cast_possible_wrap)] + gl::DrawArrays( + primitive_kind.into_gl(), + start_index as gl::types::GLint, + index_cnt as gl::types::GLsizei, + ); + } + } + + pub fn configure_attrs( + _currently_bound: &CurrentlyBound, + _vert_buf_curr_bound: &CurrentlyBound, + ) + { + let mut offset = 0; + + for attr in Vertex::attrs() { + Self::vertex_attrib_ptr(attr, offset); + + Self::enable_vertex_attrib_array(attr.index); + + offset += attr.component_size * attr.component_cnt as usize; + } + } + + #[allow(clippy::inline_always)] + #[inline(always)] + pub fn bind(&self, cb: impl FnOnce(CurrentlyBound<'_, Self>)) + { + unsafe { gl::BindVertexArray(self.array) } + + // SAFETY: A vertex array object is currently bound + let currently_bound = unsafe { CurrentlyBound::new() }; + + cb(currently_bound); + } + + fn vertex_attrib_ptr(vertex_attr: &Attribute, offset: usize) + { + unsafe { + #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] + gl::VertexAttribPointer( + vertex_attr.index as gl::types::GLuint, + vertex_attr.component_cnt as i32, + Self::vertex_attr_type_to_gl(&vertex_attr.component_type), + gl::FALSE, + VERTEX_STRIDE as gl::types::GLsizei, + offset as *const _, + ); + } + } + + fn enable_vertex_attrib_array(index: usize) + { + unsafe { + #[allow(clippy::cast_possible_truncation)] + gl::EnableVertexAttribArray(index as gl::types::GLuint); + } + } + + fn vertex_attr_type_to_gl( + vertex_attr_type: &AttributeComponentType, + ) -> gl::types::GLenum + { + match vertex_attr_type { + AttributeComponentType::Float => gl::FLOAT, + } + } +} + +impl Drop for VertexArray +{ + fn drop(&mut self) + { + unsafe { + gl::DeleteVertexArrays(1, &self.array); + } + } +} + +#[derive(Debug)] +pub enum PrimitiveKind +{ + Triangles, +} + +impl PrimitiveKind +{ + fn into_gl(self) -> gl::types::GLenum + { + match self { + Self::Triangles => gl::TRIANGLES, + } + } +} diff --git a/engine/src/opengl/vertex_buffer.rs b/engine/src/opengl/vertex_buffer.rs new file mode 100644 index 0000000..0050a9f --- /dev/null +++ b/engine/src/opengl/vertex_buffer.rs @@ -0,0 +1,93 @@ +use std::mem::size_of_val; + +use crate::opengl::currently_bound::CurrentlyBound; +use crate::vertex::Vertex; + +#[derive(Debug)] +pub struct VertexBuffer +{ + buffer: gl::types::GLuint, +} + +impl VertexBuffer +{ + pub fn new() -> Self + { + let mut buffer = gl::types::GLuint::default(); + + unsafe { + gl::GenBuffers(1, &mut buffer); + }; + + Self { buffer } + } + + #[allow(clippy::inline_always)] + #[inline(always)] + pub fn bind(&self, cb: impl FnOnce(CurrentlyBound<'_, Self>)) + { + unsafe { + gl::BindBuffer(gl::ARRAY_BUFFER, self.buffer); + } + + // SAFETY: A vertex array object is currently bound + let currently_bound = unsafe { CurrentlyBound::new() }; + + cb(currently_bound); + } + + /// Stores vertices in the currently bound vertex bound. + pub fn store( + _currently_bound: &CurrentlyBound, + vertices: &[Vertex], + usage: BufferUsage, + ) + { + unsafe { + #[allow(clippy::cast_possible_wrap)] + gl::BufferData( + gl::ARRAY_BUFFER, + size_of_val(vertices) as gl::types::GLsizeiptr, + vertices.as_ptr().cast(), + usage.into_gl(), + ); + } + } +} + +impl Drop for VertexBuffer +{ + fn drop(&mut self) + { + #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] + unsafe { + gl::DeleteBuffers(1, &self.buffer); + } + } +} + +#[derive(Debug)] +#[allow(dead_code)] +pub enum BufferUsage +{ + /// The buffer data is set only once and used by the GPU at most a few times. + Stream, + + /// The buffer data is set only once and used many times. + Static, + + /// The buffer data is changed a lot and used many times. + Dynamic, +} + +impl BufferUsage +{ + fn into_gl(self) -> gl::types::GLenum + { + match self { + Self::Stream => gl::STREAM_DRAW, + Self::Static => gl::STATIC_DRAW, + Self::Dynamic => gl::DYNAMIC_DRAW, + } + } +} diff --git a/engine/src/renderer/mod.rs b/engine/src/renderer/mod.rs index a23d5a0..4ed3c0a 100644 --- a/engine/src/renderer/mod.rs +++ b/engine/src/renderer/mod.rs @@ -3,14 +3,13 @@ use std::process::abort; use glfw::WindowSize; -use crate::renderer::vertex_array::{PrimitiveKind, VertexArray}; -use crate::renderer::vertex_buffer::{BufferUsage, VertexBuffer}; +use crate::opengl::shader::Program as ShaderProgram; +use crate::opengl::vertex_array::{PrimitiveKind, VertexArray}; +use crate::opengl::vertex_buffer::{BufferUsage, VertexBuffer}; +use crate::opengl::{clear_buffers, BufferClearMask}; use crate::vector::Vec2; use crate::vertex::Vertex; -mod vertex_array; -mod vertex_buffer; - pub fn initialize(window: &glfw::Window) -> Result<(), Error> { gl::load_with(|symbol| { @@ -38,9 +37,7 @@ pub fn initialize(window: &glfw::Window) -> Result<(), Error> pub fn render<'renderable>(renderables: impl IntoIterator) { - unsafe { - gl::Clear(gl::COLOR_BUFFER_BIT); - } + clear_buffers(BufferClearMask::COLOR); for renderable in renderables { renderable.shader_program.activate(); @@ -51,10 +48,15 @@ pub fn render<'renderable>(renderables: impl IntoIterator, size: &WindowSize) +{ + crate::opengl::set_viewport(position, size); +} + #[derive(Debug)] pub struct Renderable { - shader_program: crate::shader::Program, + shader_program: ShaderProgram, vertex_arr: VertexArray, /// Vertex buffer has to live as long as the vertex array @@ -63,7 +65,7 @@ pub struct Renderable impl Renderable { - pub fn new(shader_program: crate::shader::Program, vertices: &[Vertex]) -> Self + pub fn new(shader_program: ShaderProgram, vertices: &[Vertex]) -> Self { let vertex_arr = VertexArray::new(); let vertex_buffer = VertexBuffer::new(); @@ -84,19 +86,6 @@ impl Renderable } } -pub fn set_viewport(position: &Vec2, size: &WindowSize) -{ - unsafe { - #[allow(clippy::cast_possible_wrap)] - gl::Viewport( - position.x as i32, - position.y as i32, - size.width as i32, - size.height as i32, - ); - } -} - /// Renderer error. #[derive(Debug, thiserror::Error)] pub enum Error diff --git a/engine/src/renderer/vertex_array.rs b/engine/src/renderer/vertex_array.rs deleted file mode 100644 index 2bd6a5b..0000000 --- a/engine/src/renderer/vertex_array.rs +++ /dev/null @@ -1,131 +0,0 @@ -use std::mem::size_of; - -use crate::currently_bound::CurrentlyBound; -use crate::renderer::vertex_buffer::VertexBuffer; -use crate::vertex::{Attribute, AttributeComponentType, Vertex}; - -const VERTEX_STRIDE: usize = size_of::(); - -#[derive(Debug)] -pub struct VertexArray -{ - array: gl::types::GLuint, -} - -impl VertexArray -{ - pub fn new() -> Self - { - let mut array = 0; - - unsafe { - gl::GenVertexArrays(1, &mut array); - } - - Self { array } - } - - /// Draws the currently bound vertex array. - pub fn draw( - _currently_bound: &CurrentlyBound, - primitive_kind: PrimitiveKind, - start_index: u32, - index_cnt: u32, - ) - { - unsafe { - #[allow(clippy::cast_possible_wrap)] - gl::DrawArrays( - primitive_kind.into_gl(), - start_index as gl::types::GLint, - index_cnt as gl::types::GLsizei, - ); - } - } - - pub fn configure_attrs( - _currently_bound: &CurrentlyBound, - _vert_buf_curr_bound: &CurrentlyBound, - ) - { - let mut offset = 0; - - for attr in Vertex::attrs() { - Self::vertex_attrib_ptr(attr, offset); - - Self::enable_vertex_attrib_array(attr.index); - - offset += attr.component_size * attr.component_cnt as usize; - } - } - - #[allow(clippy::inline_always)] - #[inline(always)] - pub fn bind(&self, cb: impl FnOnce(CurrentlyBound<'_, Self>)) - { - unsafe { gl::BindVertexArray(self.array) } - - // SAFETY: A vertex array object is currently bound - let currently_bound = unsafe { CurrentlyBound::new() }; - - cb(currently_bound); - } - - fn vertex_attrib_ptr(vertex_attr: &Attribute, offset: usize) - { - unsafe { - #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] - gl::VertexAttribPointer( - vertex_attr.index as gl::types::GLuint, - vertex_attr.component_cnt as i32, - Self::vertex_attr_type_to_gl(&vertex_attr.component_type), - gl::FALSE, - VERTEX_STRIDE as gl::types::GLsizei, - offset as *const _, - ); - } - } - - fn enable_vertex_attrib_array(index: usize) - { - unsafe { - #[allow(clippy::cast_possible_truncation)] - gl::EnableVertexAttribArray(index as gl::types::GLuint); - } - } - - fn vertex_attr_type_to_gl( - vertex_attr_type: &AttributeComponentType, - ) -> gl::types::GLenum - { - match vertex_attr_type { - AttributeComponentType::Float => gl::FLOAT, - } - } -} - -impl Drop for VertexArray -{ - fn drop(&mut self) - { - unsafe { - gl::DeleteVertexArrays(1, &self.array); - } - } -} - -#[derive(Debug)] -pub enum PrimitiveKind -{ - Triangles, -} - -impl PrimitiveKind -{ - fn into_gl(self) -> gl::types::GLenum - { - match self { - Self::Triangles => gl::TRIANGLES, - } - } -} diff --git a/engine/src/renderer/vertex_buffer.rs b/engine/src/renderer/vertex_buffer.rs deleted file mode 100644 index 57debc3..0000000 --- a/engine/src/renderer/vertex_buffer.rs +++ /dev/null @@ -1,93 +0,0 @@ -use std::mem::size_of_val; - -use crate::currently_bound::CurrentlyBound; -use crate::vertex::Vertex; - -#[derive(Debug)] -pub struct VertexBuffer -{ - buffer: gl::types::GLuint, -} - -impl VertexBuffer -{ - pub fn new() -> Self - { - let mut buffer = gl::types::GLuint::default(); - - unsafe { - gl::GenBuffers(1, &mut buffer); - }; - - Self { buffer } - } - - #[allow(clippy::inline_always)] - #[inline(always)] - pub fn bind(&self, cb: impl FnOnce(CurrentlyBound<'_, Self>)) - { - unsafe { - gl::BindBuffer(gl::ARRAY_BUFFER, self.buffer); - } - - // SAFETY: A vertex array object is currently bound - let currently_bound = unsafe { CurrentlyBound::new() }; - - cb(currently_bound); - } - - /// Stores vertices in the currently bound vertex bound. - pub fn store( - _currently_bound: &CurrentlyBound, - vertices: &[Vertex], - usage: BufferUsage, - ) - { - unsafe { - #[allow(clippy::cast_possible_wrap)] - gl::BufferData( - gl::ARRAY_BUFFER, - size_of_val(vertices) as gl::types::GLsizeiptr, - vertices.as_ptr().cast(), - usage.into_gl(), - ); - } - } -} - -impl Drop for VertexBuffer -{ - fn drop(&mut self) - { - #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] - unsafe { - gl::DeleteBuffers(1, &self.buffer); - } - } -} - -#[derive(Debug)] -#[allow(dead_code)] -pub enum BufferUsage -{ - /// The buffer data is set only once and used by the GPU at most a few times. - Stream, - - /// The buffer data is set only once and used many times. - Static, - - /// The buffer data is changed a lot and used many times. - Dynamic, -} - -impl BufferUsage -{ - fn into_gl(self) -> gl::types::GLenum - { - match self { - Self::Stream => gl::STREAM_DRAW, - Self::Static => gl::STATIC_DRAW, - Self::Dynamic => gl::DYNAMIC_DRAW, - } - } -} diff --git a/engine/src/shader.rs b/engine/src/shader.rs deleted file mode 100644 index 0bbca77..0000000 --- a/engine/src/shader.rs +++ /dev/null @@ -1,198 +0,0 @@ -use std::ffi::CStr; -use std::ptr::null_mut; - -#[derive(Debug)] -pub struct Shader -{ - shader: gl::types::GLuint, -} - -impl Shader -{ - pub fn new(kind: Kind) -> Self - { - let shader = unsafe { gl::CreateShader(kind.into_gl()) }; - - Self { shader } - } - - pub fn set_source(&self, source: &str) -> Result<(), Error> - { - if !source.is_ascii() { - return Err(Error::SourceNotAscii); - } - - unsafe { - #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] - gl::ShaderSource( - self.shader, - 1, - &source.as_ptr().cast(), - &(source.len() as gl::types::GLint), - ); - } - - Ok(()) - } - - pub fn compile(&self) -> Result<(), Error> - { - unsafe { - gl::CompileShader(self.shader); - } - - let mut compile_success = gl::types::GLint::default(); - - unsafe { - gl::GetShaderiv(self.shader, gl::COMPILE_STATUS, &mut compile_success); - } - - if compile_success == 0 { - let info_log = self.get_info_log(); - - return Err(Error::CompileFailed(info_log)); - } - - Ok(()) - } - - fn get_info_log(&self) -> String - { - let mut buf = vec![gl::types::GLchar::default(); 512]; - - unsafe { - #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] - gl::GetShaderInfoLog( - self.shader, - buf.len() as gl::types::GLsizei, - null_mut(), - buf.as_mut_ptr(), - ); - } - - let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) }; - - unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) } - } -} - -impl Drop for Shader -{ - fn drop(&mut self) - { - unsafe { - gl::DeleteShader(self.shader); - } - } -} - -/// Shader kind. -#[derive(Debug, Clone, Copy)] -pub enum Kind -{ - Vertex, - Fragment, -} - -impl Kind -{ - fn into_gl(self) -> gl::types::GLenum - { - match self { - Self::Vertex => gl::VERTEX_SHADER, - Self::Fragment => gl::FRAGMENT_SHADER, - } - } -} - -/// Shader program -#[derive(Debug)] -pub struct Program -{ - program: gl::types::GLuint, -} - -impl Program -{ - pub fn new() -> Self - { - let program = unsafe { gl::CreateProgram() }; - - Self { program } - } - - pub fn attach(&self, shader: &Shader) - { - unsafe { - gl::AttachShader(self.program, shader.shader); - } - } - - pub fn link(&self) -> Result<(), Error> - { - unsafe { - gl::LinkProgram(self.program); - } - - let mut link_success = gl::types::GLint::default(); - - unsafe { - gl::GetProgramiv(self.program, gl::LINK_STATUS, &mut link_success); - } - - if link_success == 0 { - let info_log = self.get_info_log(); - - return Err(Error::CompileFailed(info_log)); - } - - Ok(()) - } - - pub fn activate(&self) - { - unsafe { - gl::UseProgram(self.program); - } - } - - fn get_info_log(&self) -> String - { - let mut buf = vec![gl::types::GLchar::default(); 512]; - - unsafe { - #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)] - gl::GetProgramInfoLog( - self.program, - buf.len() as gl::types::GLsizei, - null_mut(), - buf.as_mut_ptr(), - ); - } - - let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) }; - - unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) } - } -} - -impl Drop for Program -{ - fn drop(&mut self) - { - unsafe { - gl::DeleteProgram(self.program); - } - } -} - -/// Shader error. -#[derive(Debug, thiserror::Error)] -pub enum Error -{ - #[error("All characters in source are not within the ASCII range")] - SourceNotAscii, - - #[error("Failed to compile: {0}")] - CompileFailed(String), -} -- cgit v1.2.3-18-g5258