1extern crate gl;
2
3use gl::types::{GLenum, GLint};
4use std::ffi::{CStr, CString};
5use crate::{GlEnum, GlUse, GlUniform, GlslType, GlObject};
6
7#[derive(PartialEq, PartialOrd, Eq, Clone, Copy)]
8pub enum ShaderType {
9 VertexShader,
10 FragmentShader,
11 GeometryShader,
12 ComputeShader,
13}
14
15impl GlEnum for ShaderType {
16 fn to_enum(&self) -> GLenum {
17 match *self {
18 ShaderType::VertexShader => { gl::VERTEX_SHADER },
19 ShaderType::FragmentShader => { gl::FRAGMENT_SHADER },
20 _ => { 0 }
21 }
22 }
23}
24
25pub enum GlslVersion {
26 V1_00,
27 V1_20,
28 V1_30,
29 V1_40,
30 V3_00,
31 V3_30,
32}
33
34#[derive(Default, Debug, PartialEq, PartialOrd, Eq, Clone, Copy)]
35pub enum GlslQualifier {
36 Default,
37 #[default]
38 Const,
39 Input,
40 Output,
41 Uniform
42}
43
44impl ToString for GlslQualifier {
45 fn to_string(&self) -> String {
46 match *self {
47 Self::Default => { return "".to_string() },
48 Self::Const => { return "const".to_string() },
49 Self::Input => { return "in".to_string() },
50 Self::Output => { return "out".to_string() },
51 Self::Uniform => { return "uniform".to_string() }
52 }
53 }
54}
55
56#[derive(Default, Debug, PartialEq, Eq, Clone)]
57pub struct GlslVariable {
58 qualifier: GlslQualifier,
59 type_: String,
60 size: u32,
61 name: String
62}
63
64impl GlslVariable {
65 pub fn new<T: GlslType>(qualifier: GlslQualifier, name: &str) -> GlslVariable {
66 GlslVariable { qualifier, type_: T::to_glsl(), size: 1, name: name.to_string() }
67 }
68
69 pub fn new_const<T: GlslType>(name: &str) -> GlslVariable {
70 GlslVariable::new::<T>(GlslQualifier::Const, name)
71 }
72
73 pub fn new_input<T: GlslType>(name: &str) -> GlslVariable {
74 GlslVariable::new::<T>(GlslQualifier::Input, name)
75 }
76
77 pub fn new_output<T: GlslType>(name: &str) -> GlslVariable {
78 GlslVariable::new::<T>(GlslQualifier::Output, name)
79 }
80
81 pub fn new_uniform<T: GlslType>(name: &str) -> GlslVariable {
82 GlslVariable::new::<T>(GlslQualifier::Uniform, name)
83 }
84
85 pub fn set_size(&mut self, size: u32) {
86 debug_assert_ne!(size, 0);
87 self.size = size;
88 }
89}
90
91impl ToString for GlslVariable {
92 fn to_string(&self) -> String {
93 format!("{} {} {}", self.qualifier.to_string(), self.type_, self.name.to_string())
94 }
95}
96
97#[derive(Default, Debug, Eq, PartialEq, PartialOrd, Ord)]
98pub struct Shader(u32);
99
100#[derive(Default, Debug, PartialEq, Eq)]
101pub struct ShaderBuilder {
102 version: String,
103 precision: String,
104 variables: Vec<GlslVariable>,
105 pre_main: Vec<String>,
106 main: String
107}
108
109#[derive(Default, Debug, Eq, PartialEq, PartialOrd, Ord)]
110pub struct Program(u32);
111
112impl Shader {
116 pub fn new(kind: ShaderType) -> Result<Self, String> {
117 let handle = unsafe { gl::CreateShader(kind.to_enum()) };
118 Ok(Shader(handle))
119 }
120
121 pub fn source(&self, source: Vec<String>) {
122 let v: Vec<CString> = source.iter()
123 .map(|s| CString::new(s.as_str()).unwrap())
124 .collect();
125 let src: Vec<_> = v.iter()
126 .map(|s| s.as_ptr())
127 .collect();
128
129 #[cfg(target_arch="aarch64")]
131 unsafe { gl::ShaderSource(self.0, source.len() as i32, src.as_ptr() as *const *const u8, std::ptr::null()) };
132 #[cfg(not(target_arch="aarch64"))]
133 unsafe { gl::ShaderSource(self.0, source.len() as i32, src.as_ptr() as *const *const i8, std::ptr::null()) };
134 }
135
136 pub fn compile(&self) -> Result<(), String> {
137 unsafe { gl::CompileShader(self.0) };
138 let mut success = 1;
139 unsafe {
140 gl::GetShaderiv(self.0, gl::COMPILE_STATUS, &mut success);
141 }
142 if success == 0 {
143 let mut len = 0;
144 unsafe { gl::GetShaderiv(self.0, gl::INFO_LOG_LENGTH, &mut len); }
145 let error = create_whitespace_cstring_with_len(len as usize);
146 unsafe {
147 gl::GetShaderInfoLog(
148 self.0,
149 len,
150 std::ptr::null_mut(),
151 error.as_ptr() as *mut gl::types::GLchar
152 );
153 }
154 return Err(error.to_string_lossy().into_owned());
155 }
156 Ok(())
157 }
158
159 pub fn from_source(kind: ShaderType, source: &CStr) -> Result<Self, String> {
160 let shader = Self::new(kind).unwrap();
161 unsafe { gl::ShaderSource(shader.get_id(), 1 as i32, &source.as_ptr(), std::ptr::null()) };
162 shader.compile().expect("Failed to compile shader");
163 Ok(shader)
164 }
165
166 }
176
177impl GlObject for Shader {
178 fn get_id(&self) -> u32 {
179 self.0
180 }
181}
182
183impl Drop for Shader {
184 fn drop(&mut self) {
185 unsafe { gl::DeleteShader(self.0) };
186 }
187}
188
189impl Program {
194
195 pub fn new() -> Result<Self, String> {
196 let handle = unsafe { gl::CreateProgram() };
197 Ok(Program(handle))
198 }
199
200 pub fn attach_shader(&self, shader: &Shader) {
201 unsafe { gl::AttachShader(self.0, shader.get_id()) };
202 }
203
204 pub fn link(&self) -> Result<(), String> {
205 let mut success = 0;
206 unsafe {
207 gl::LinkProgram(self.0);
208 gl::GetProgramiv(self.0, gl::LINK_STATUS, &mut success);
209 }
210 if success == 0 {
211 let mut len: i32 = 0;
212 unsafe { gl::GetProgramiv(self.0, gl::INFO_LOG_LENGTH, &mut len) };
213 let error = create_whitespace_cstring_with_len(len as usize);
214 unsafe {
215 gl::GetProgramInfoLog(
216 self.0,
217 len,
218 std::ptr::null_mut(),
219 error.as_ptr() as *mut gl::types::GLchar
220 );
221 }
222 return Err(error.to_string_lossy().into_owned());
223 }
224 Ok(())
225 }
226
227 pub fn from_shaders(shaders: &[Shader]) -> Result<Self, String> {
228 let program = Self::new().unwrap();
229 let handle = unsafe { gl::CreateProgram() };
230 for shader in shaders {
231 program.attach_shader(shader);
232 unsafe { gl::AttachShader(handle, shader.get_id()); }
233 }
234 program.link().expect("Failed to link program");
235 Ok(program)
236 }
237
238 pub fn from_source(vert_src: &CStr, frag_src: &CStr) -> Result<Self, String> {
239 let vert_shd = Shader::from_source( ShaderType::VertexShader, vert_src).expect("Failed to create vertex shader");
240 let frag_shd = Shader::from_source( ShaderType::FragmentShader, frag_src).expect("Failed to create fragment shader");
241 let program = Self::new().unwrap();
242 program.attach_shader(&vert_shd);
243 program.attach_shader(&frag_shd);
244 program.link().expect("Failed to link program");
245 Ok(program)
246 }
247
248 pub fn get_attrib_location(&self, name: &str) -> GLint {
249 #[cfg(target_arch="aarch64")]
250 let loc = unsafe { gl::GetAttribLocation(self.0, name.as_ptr() as *const u8) };
251 #[cfg(not(target_arch="aarch64"))]
252 let loc = unsafe { gl::GetAttribLocation(self.0, name.as_ptr() as *const i8) };
253 return loc
254 }
255
256 pub fn get_uniform_location(&self, name: &str) -> GLint {
257 #[cfg(target_arch="aarch64")]
258 unsafe { gl::GetUniformLocation(self.0, name.as_ptr() as *const u8) }
259 #[cfg(not(target_arch="aarch64"))]
260 unsafe { gl::GetUniformLocation(self.0, name.as_ptr() as *const i8) }
261 }
262
263 pub fn send_uniform<T: GlUniform>(&self, location: i32, data: T) {
264 data.send_uniform(location);
265 }
266
267}
268
269impl GlObject for Program {
270 fn get_id(&self) -> u32 { self.0 }
271}
272
273impl GlUse for Program {
274 fn set_used(&self) {
275 unsafe { gl::UseProgram(self.0) };
276 }
277
278 fn set_unused(&self) {
279 unsafe { gl::UseProgram(0) };
280 }
281}
282
283impl Drop for Program {
284 fn drop(&mut self) {
285 unsafe { gl::DeleteProgram(self.0) };
286 }
287}
288
289impl ShaderBuilder {
294 pub fn new() -> Self {
295 ShaderBuilder {
296 version: "#version 140".to_string(),
297 precision: "".to_string(),
298 variables: vec![],
299 pre_main: vec![],
300 main: "".to_string()
301 }
302 }
303
304 pub fn glsl_version(&mut self, version: &str) -> &mut Self {
305 self.version = version.to_string();
306 self
307 }
308
309 pub fn position<T: GlslType>(&mut self) -> &mut Self {
310 self.variables.insert(0, GlslVariable::new_input::<T>( "a_Position"));
311 self
312 }
313
314 pub fn color<T: GlslType>(&mut self) -> &mut Self {
315 self.variables.insert(1, GlslVariable::new_input::<T>("a_Color"));
316 self
317 }
318
319 pub fn texcoord<T: GlslType>(&mut self) -> &mut Self {
320 self.variables.insert(2, GlslVariable::new_input::<T>("a_Texcoord"));
321 self
322 }
323
324 pub fn normal<T: GlslType>(&mut self) -> &mut Self {
325 self.variables.insert(3, GlslVariable::new_input::<T>("a_Normal"));
326 self
327 }
328
329 pub fn add_input<T: GlslType>(&mut self, name: &str) -> &mut Self {
330 self.variables.push(GlslVariable::new_input::<T>(name));
331 self
332 }
333
334 pub fn add_output<T: GlslType>(&mut self, name: &str) -> &mut Self {
335 self.variables.push(GlslVariable::new_output::<T>(name));
336 self
337 }
338
339 pub fn add_uniform<T: GlslType>(&mut self, name: &str) -> &mut Self {
340 self.variables.push(GlslVariable::new_uniform::<T>(name));
341 self
342 }
343
344 pub fn push_pre_main(&mut self, src: &str) -> &mut Self {
345 self.pre_main.push(src.to_string());
346 self
347 }
348
349 pub fn set_main(&mut self, main: &str) -> &mut Self {
350 self.main = main.to_string();
351 self
352 }
353
354 pub fn build(&self, kind: ShaderType) -> Result<Shader, String> {
355 let mut source: Vec<String> = vec![];
356
357 source.push(format!("{}\n", self.version));
358 source.push(format!("{}\n", self.precision));
359 match kind {
365 ShaderType::VertexShader {} => {},
366 _ => {}
367 }
368
369 for variable in &self.variables {
370 source.push(format!("{};", variable.to_string()));
371 }
373
374 let src = self.pre_main.clone();
375 source.extend(src);
376
377 source.push(self.main.clone());
378
379 let shader = Shader::new(kind).unwrap();
385 shader.source(source);
386 shader.compile().expect("Failed to compile shader");
387 Ok(shader)
388 }
389}
390
391fn create_whitespace_cstring_with_len(len: usize) -> CString {
392 let mut buffer: Vec<u8> = Vec::with_capacity(len + 1);
393 buffer.extend([b' '].iter().cycle().take(len));
394 unsafe { CString::from_vec_unchecked(buffer) }
395}