summaryrefslogtreecommitdiff
path: root/engine/src/shader.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/shader.rs')
-rw-r--r--engine/src/shader.rs198
1 files changed, 198 insertions, 0 deletions
diff --git a/engine/src/shader.rs b/engine/src/shader.rs
new file mode 100644
index 0000000..0bbca77
--- /dev/null
+++ b/engine/src/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),
+}