You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

412 lines
10 KiB

  1. use std::ffi::CString;
  2. use std::fs::File;
  3. use std::io::Read;
  4. use std::path::Path;
  5. use std::ptr::{null, null_mut};
  6. use std::str::from_utf8;
  7. use crate::{
  8. error::Error,
  9. matrix::Matrix4f,
  10. misc::{AsEnum, Bindable},
  11. vector::{Vector2f, Vector4f},
  12. };
  13. /* Programm */
  14. #[derive(Default)]
  15. pub struct Program {
  16. id: gl::GLuint,
  17. }
  18. impl Program {
  19. pub fn builder() -> Builder {
  20. Builder::default()
  21. }
  22. pub fn from_shaders<I>(iter: I) -> Result<Self, Error>
  23. where
  24. I: IntoIterator<Item = Shader>,
  25. {
  26. Self::from_shaders_result(iter.into_iter().map(Ok))
  27. }
  28. pub fn from_shaders_result<I, E>(iter: I) -> Result<Self, E>
  29. where
  30. I: IntoIterator<Item = Result<Shader, E>>,
  31. E: From<Error>,
  32. {
  33. let mut builder = Self::builder();
  34. for shader in iter.into_iter() {
  35. builder = builder.add_shader(shader?);
  36. }
  37. let program = builder.build()?;
  38. Ok(program)
  39. }
  40. pub fn id(&self) -> gl::GLuint {
  41. self.id
  42. }
  43. pub fn uniform<T>(&self, location: T, uniform: Uniform<'_>) -> Result<(), Error>
  44. where
  45. T: IntoUniformLocation,
  46. {
  47. let location = IntoUniformLocation::into_location(location, &self)?;
  48. match uniform {
  49. Uniform::Matrix4f(v) => gl::uniform_matrix_4fv(location, 1, gl::FALSE, v.as_ptr()),
  50. Uniform::Vector4f(v) => gl::uniform_4fv(location, 1, v.as_ptr()),
  51. Uniform::Vector2f(v) => gl::uniform_2fv(location, 1, v.as_ptr()),
  52. Uniform::Float(v) => gl::uniform_1f(location, v),
  53. Uniform::Texture(i) => gl::uniform_1i(location, i),
  54. Uniform::TextureVec(i) => gl::uniform_1iv(location, i.len() as _, i.as_ptr()),
  55. }
  56. Ok(())
  57. }
  58. pub fn uniform_location<T>(&self, name: T) -> Result<gl::GLint, Error>
  59. where
  60. T: Into<String>,
  61. {
  62. let name = Into::<String>::into(name);
  63. let name = CString::new(name)?;
  64. let location = Error::checked(|| gl::get_uniform_location(self.id, name.as_ptr()))?;
  65. if location >= 0 {
  66. Ok(location)
  67. } else {
  68. Err(Error::ShaderUnknownUniform(name.into_string().unwrap()))
  69. }
  70. }
  71. pub fn uniform_block_index<T>(&self, name: T) -> Result<gl::GLuint, Error>
  72. where
  73. T: Into<String>,
  74. {
  75. let name = Into::<String>::into(name);
  76. let name = CString::new(name)?;
  77. let location = Error::checked(|| gl::get_uniform_block_index(self.id, name.as_ptr()))?;
  78. if location != gl::INVALID_INDEX {
  79. Ok(location)
  80. } else {
  81. Err(Error::ShaderUnknownUniform(name.into_string().unwrap()))
  82. }
  83. }
  84. pub fn uniform_block_binding<T>(&self, index: T, binding: gl::GLuint) -> Result<(), Error>
  85. where
  86. T: IntoUniformBlockIndex,
  87. {
  88. let index = IntoUniformBlockIndex::into_index(index, &self)?;
  89. gl::uniform_block_binding(self.id, index, binding);
  90. Ok(())
  91. }
  92. }
  93. impl Drop for Program {
  94. fn drop(&mut self) {
  95. gl::delete_program(self.id);
  96. }
  97. }
  98. impl Bindable for Program {
  99. fn bind(&self) {
  100. gl::use_program(self.id);
  101. }
  102. fn unbind(&self) {
  103. gl::use_program(0);
  104. }
  105. }
  106. /* Shader */
  107. pub struct Shader {
  108. id: gl::GLuint,
  109. }
  110. impl Shader {
  111. pub fn from_string<F>(type_: Type, mut source: String, f: F) -> Result<Self, Error>
  112. where
  113. F: Fn(&str) -> Result<String, Error>,
  114. {
  115. while let Some(p) = source.find("#pragma include ") {
  116. let s = &source[p..];
  117. let e = s.find('\n').unwrap_or_else(|| s.len());
  118. let s = &s[..e];
  119. let c = f(&s[16..])?;
  120. source = source.replace(s, &c);
  121. }
  122. let id = gl::create_shader(type_.as_enum());
  123. let id = Error::err_if(&0, id)?;
  124. let source = CString::new(source)?;
  125. let ptr = source.as_ptr();
  126. let ptr: *const *const _ = &ptr;
  127. gl::shader_source(id, 1, ptr as *const *const _, null());
  128. gl::compile_shader(id);
  129. let mut success = 1;
  130. gl::get_shader_iv(id, gl::COMPILE_STATUS, &mut success);
  131. if success != 1 {
  132. let mut len = 0;
  133. gl::get_shader_iv(id, gl::INFO_LOG_LENGTH, &mut len);
  134. let mut buffer = Vec::<u8>::with_capacity(len as usize + 1);
  135. buffer.resize(len as usize, 0);
  136. gl::get_shader_info_log(
  137. id,
  138. len,
  139. null_mut(),
  140. buffer.as_mut_ptr() as *mut gl::types::GLchar,
  141. );
  142. let msg = from_utf8(&buffer)?;
  143. let code = source.into_string().unwrap();
  144. let code = code
  145. .lines()
  146. .enumerate()
  147. .map(|(i, s)| format!("{:03}: {}\n", i + 1, s))
  148. .collect();
  149. return Err(Error::ShaderCompile {
  150. code,
  151. error: msg.into(),
  152. });
  153. }
  154. Ok(Self { id })
  155. }
  156. pub fn from_reader<R, F>(type_: Type, reader: &mut R, f: F) -> Result<Self, Error>
  157. where
  158. R: Read,
  159. F: Fn(&str) -> Result<String, Error>,
  160. {
  161. let mut source = String::new();
  162. reader.read_to_string(&mut source)?;
  163. Self::from_string(type_, source, f)
  164. }
  165. pub fn from_file<P, F>(type_: Type, path: P, f: F) -> Result<Self, Error>
  166. where
  167. P: AsRef<Path>,
  168. F: Fn(&str) -> Result<String, Error>,
  169. {
  170. let mut file = File::open(&path)?;
  171. match Self::from_reader(type_, &mut file, f) {
  172. Ok(v) => Ok(v),
  173. Err(Error::ShaderCompile { code, error }) => Err(Error::ShaderCompile {
  174. code: format!("{}\n{}", path.as_ref().display(), code),
  175. error,
  176. }),
  177. Err(err) => Err(err),
  178. }
  179. }
  180. pub fn id(&self) -> gl::GLuint {
  181. self.id
  182. }
  183. }
  184. impl Drop for Shader {
  185. fn drop(&mut self) {
  186. gl::delete_shader(self.id);
  187. }
  188. }
  189. /* Builder */
  190. #[derive(Default)]
  191. pub struct Builder {
  192. shaders: Vec<Shader>,
  193. transform_feedback_varyings: Option<(TransformFeedbackVaryingsMode, &'static [&'static str])>,
  194. }
  195. impl Builder {
  196. pub fn add_shader(mut self, shader: Shader) -> Self {
  197. self.shaders.push(shader);
  198. self
  199. }
  200. pub fn set_transform_feedback_varyings(
  201. mut self,
  202. mode: TransformFeedbackVaryingsMode,
  203. value: &'static [&'static str],
  204. ) -> Self {
  205. self.transform_feedback_varyings = Some((mode, value));
  206. self
  207. }
  208. pub fn build(self) -> Result<Program, Error> {
  209. let id = gl::create_program();
  210. let id = Error::err_if(&0, id)?;
  211. for shader in &self.shaders {
  212. gl::attach_shader(id, shader.id());
  213. }
  214. if let Some((mode, tfv)) = self.transform_feedback_varyings {
  215. let tfv = tfv
  216. .iter()
  217. .map(|v: &&str| CString::new(*v))
  218. .collect::<Result<Vec<_>, _>>()?;
  219. let tfv = tfv
  220. .iter()
  221. .map(|v: &CString| v.as_ptr() as *const gl::GLchar)
  222. .collect::<Vec<_>>();
  223. let ptr: *const *const gl::GLchar = tfv.as_slice().as_ptr();
  224. Error::checked(|| {
  225. gl::transform_feedback_varyings(id, tfv.len() as _, ptr, mode.as_enum())
  226. })?;
  227. }
  228. gl::link_program(id);
  229. for shader in &self.shaders {
  230. gl::detach_shader(id, shader.id());
  231. }
  232. let mut success = 1;
  233. gl::get_program_iv(id, gl::LINK_STATUS, &mut success);
  234. if success != 1 {
  235. let mut len = 0;
  236. gl::get_program_iv(id, gl::INFO_LOG_LENGTH, &mut len);
  237. let mut buffer = Vec::<u8>::with_capacity(len as usize + 1);
  238. buffer.resize(len as usize, 0);
  239. gl::get_program_info_log(
  240. id,
  241. len,
  242. null_mut(),
  243. buffer.as_mut_ptr() as *mut gl::types::GLchar,
  244. );
  245. let msg = from_utf8(&buffer).map_err(Error::Utf8Error)?;
  246. return Err(Error::ShaderLink(msg.into()));
  247. }
  248. Ok(Program { id })
  249. }
  250. }
  251. /* Type */
  252. #[derive(Debug, Copy, Clone, Eq, PartialEq)]
  253. pub enum Type {
  254. Vertex,
  255. Compute,
  256. Fragment,
  257. Geometry,
  258. TesslationControl,
  259. TesslationEvaluation,
  260. }
  261. impl AsEnum for Type {
  262. fn as_enum(&self) -> gl::GLenum {
  263. match self {
  264. Self::Vertex => gl::VERTEX_SHADER,
  265. Self::Compute => gl::COMPUTE_SHADER,
  266. Self::Fragment => gl::FRAGMENT_SHADER,
  267. Self::Geometry => gl::GEOMETRY_SHADER,
  268. Self::TesslationControl => gl::TESS_CONTROL_SHADER,
  269. Self::TesslationEvaluation => gl::TESS_EVALUATION_SHADER,
  270. }
  271. }
  272. }
  273. /* TransformFeedbackVaryingsMode */
  274. pub enum TransformFeedbackVaryingsMode {
  275. Interleaved,
  276. Separate,
  277. }
  278. impl AsEnum for TransformFeedbackVaryingsMode {
  279. fn as_enum(&self) -> gl::GLenum {
  280. match self {
  281. Self::Interleaved => gl::INTERLEAVED_ATTRIBS,
  282. Self::Separate => gl::SEPARATE_ATTRIBS,
  283. }
  284. }
  285. }
  286. /* Uniform */
  287. pub enum Uniform<'a> {
  288. Matrix4f(&'a Matrix4f),
  289. Vector4f(&'a Vector4f),
  290. Vector2f(&'a Vector2f),
  291. Float(f32),
  292. Texture(gl::GLint),
  293. TextureVec(&'a [gl::GLint]),
  294. }
  295. /* IntoUniformLocation */
  296. pub trait IntoUniformLocation {
  297. fn into_location(self, program: &Program) -> Result<gl::GLint, Error>;
  298. }
  299. impl IntoUniformLocation for gl::GLint {
  300. fn into_location(self, _program: &Program) -> Result<gl::GLint, Error> {
  301. Ok(self)
  302. }
  303. }
  304. impl IntoUniformLocation for &str {
  305. fn into_location(self, program: &Program) -> Result<gl::GLint, Error> {
  306. program.uniform_location(self)
  307. }
  308. }
  309. impl IntoUniformLocation for String {
  310. fn into_location(self, program: &Program) -> Result<gl::GLint, Error> {
  311. program.uniform_location(self)
  312. }
  313. }
  314. /* IntoUniformBlockIndex */
  315. pub trait IntoUniformBlockIndex {
  316. fn into_index(self, program: &Program) -> Result<gl::GLuint, Error>;
  317. }
  318. impl IntoUniformBlockIndex for gl::GLuint {
  319. fn into_index(self, _program: &Program) -> Result<gl::GLuint, Error> {
  320. Ok(self)
  321. }
  322. }
  323. impl IntoUniformBlockIndex for &str {
  324. fn into_index(self, program: &Program) -> Result<gl::GLuint, Error> {
  325. program.uniform_block_index(self)
  326. }
  327. }
  328. impl IntoUniformBlockIndex for String {
  329. fn into_index(self, program: &Program) -> Result<gl::GLuint, Error> {
  330. program.uniform_block_index(self)
  331. }
  332. }