1use crate::gl;
2use crate::version::Version;
3use crate::version::Api;
4
5use crate::CapabilitiesSource;
6use crate::backend::Facade;
7use crate::context::Context;
8use crate::ContextExt;
9
10use std::{ffi, ptr};
11use std::rc::Rc;
12
13use crate::GlObject;
14use crate::Handle;
15
16use crate::program::{ProgramCreationError, ShaderType, SpirvEntryPoint};
17
18pub struct Shader {
20 context: Rc<Context>,
21 id: Handle,
22}
23
24impl GlObject for Shader {
25 type Id = Handle;
26
27 #[inline]
28 fn get_id(&self) -> Handle {
29 self.id
30 }
31}
32
33impl Drop for Shader {
34 fn drop(&mut self) {
35 let ctxt = self.context.make_current();
36
37 unsafe {
38 match self.id {
39 Handle::Id(id) => {
40 assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
41 ctxt.version >= &Version(Api::GlEs, 2, 0));
42 ctxt.gl.DeleteShader(id);
43 },
44 Handle::Handle(id) => {
45 assert!(ctxt.extensions.gl_arb_shader_objects);
46 ctxt.gl.DeleteObjectARB(id);
47 }
48 }
49 }
50 }
51}
52
53pub fn build_shader<F: ?Sized>(facade: &F, shader_type: gl::types::GLenum, source_code: &str)
55 -> Result<Shader, ProgramCreationError> where F: Facade
56{
57 unsafe {
58 let ctxt = facade.get_context().make_current();
59
60 if ctxt.capabilities.supported_glsl_versions.is_empty() {
61 return Err(ProgramCreationError::CompilationNotSupported);
62 }
63
64 if !check_shader_type_compatibility(&ctxt, shader_type) {
65 return Err(ProgramCreationError::ShaderTypeNotSupported);
66 }
67
68 let source_code = ffi::CString::new(source_code.as_bytes()).unwrap();
69
70 let id = if ctxt.version >= &Version(Api::Gl, 2, 0) ||
71 ctxt.version >= &Version(Api::GlEs, 2, 0)
72 {
73 Handle::Id(ctxt.gl.CreateShader(shader_type))
74 } else if ctxt.extensions.gl_arb_shader_objects {
75 Handle::Handle(ctxt.gl.CreateShaderObjectARB(shader_type))
76 } else {
77 unreachable!()
78 };
79
80 if id == Handle::Id(0) || id == Handle::Handle(0 as gl::types::GLhandleARB) {
81 return Err(ProgramCreationError::ShaderTypeNotSupported);
82 }
83
84 match id {
85 Handle::Id(id) => {
86 assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
87 ctxt.version >= &Version(Api::GlEs, 2, 0));
88 ctxt.gl.ShaderSource(id, 1, [ source_code.as_ptr() ].as_ptr(), ptr::null());
89 },
90 Handle::Handle(id) => {
91 assert!(ctxt.extensions.gl_arb_shader_objects);
92 ctxt.gl.ShaderSourceARB(id, 1, [ source_code.as_ptr() ].as_ptr(), ptr::null());
93 }
94 }
95
96 {
98 ctxt.report_debug_output_errors.set(false);
99
100 match id {
101 Handle::Id(id) => {
102 assert!(ctxt.version >= &Version(Api::Gl, 2, 0)||
103 ctxt.version >= &Version(Api::GlEs, 2, 0));
104 ctxt.gl.CompileShader(id);
105 },
106 Handle::Handle(id) => {
107 assert!(ctxt.extensions.gl_arb_shader_objects);
108 ctxt.gl.CompileShaderARB(id);
109 }
110 }
111
112 ctxt.report_debug_output_errors.set(true);
113 }
114
115 let compilation_success = {
117 let mut compilation_success: gl::types::GLint = 0;
118 match id {
119 Handle::Id(id) => {
120 assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
121 ctxt.version >= &Version(Api::GlEs, 2, 0));
122 ctxt.gl.GetShaderiv(id, gl::COMPILE_STATUS, &mut compilation_success);
123 },
124 Handle::Handle(id) => {
125 assert!(ctxt.extensions.gl_arb_shader_objects);
126 ctxt.gl.GetObjectParameterivARB(id, gl::OBJECT_COMPILE_STATUS_ARB,
127 &mut compilation_success);
128 }
129 }
130 compilation_success
131 };
132
133 if compilation_success == 1 {
134 Ok(Shader {
135 context: facade.get_context().clone(),
136 id
137 })
138
139 } else {
140 let mut error_log_size: gl::types::GLint = 0;
142
143 match id {
144 Handle::Id(id) => {
145 assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
146 ctxt.version >= &Version(Api::GlEs, 2, 0));
147 ctxt.gl.GetShaderiv(id, gl::INFO_LOG_LENGTH, &mut error_log_size);
148 },
149 Handle::Handle(id) => {
150 assert!(ctxt.extensions.gl_arb_shader_objects);
151 ctxt.gl.GetObjectParameterivARB(id, gl::OBJECT_INFO_LOG_LENGTH_ARB,
152 &mut error_log_size);
153 }
154 }
155
156 let mut error_log: Vec<u8> = Vec::with_capacity(error_log_size as usize);
157
158 match id {
159 Handle::Id(id) => {
160 assert!(ctxt.version >= &Version(Api::Gl, 2, 0) ||
161 ctxt.version >= &Version(Api::GlEs, 2, 0));
162 ctxt.gl.GetShaderInfoLog(id, error_log_size, &mut error_log_size,
163 error_log.as_mut_ptr() as *mut gl::types::GLchar);
164 },
165 Handle::Handle(id) => {
166 assert!(ctxt.extensions.gl_arb_shader_objects);
167 ctxt.gl.GetInfoLogARB(id, error_log_size, &mut error_log_size,
168 error_log.as_mut_ptr() as *mut gl::types::GLchar);
169 }
170 }
171
172 error_log.set_len(error_log_size as usize);
173
174 match String::from_utf8(error_log) {
175 Ok(msg) => Err(ProgramCreationError::CompilationError(msg, ShaderType::from_opengl_type(shader_type))),
176 Err(_) => Err(
177 ProgramCreationError::CompilationError("Could not convert the log \
178 message to UTF-8".to_owned(), ShaderType::from_opengl_type(shader_type))
179 ),
180 }
181 }
182 }
183}
184
185pub fn build_spirv_shader<F: ?Sized>(facade: &F, shader_type: gl::types::GLenum, spirv: &SpirvEntryPoint)
187 -> Result<Shader, ProgramCreationError> where F: Facade
188{
189 unsafe {
190 let ctxt = facade.get_context().make_current();
191
192 if ctxt.capabilities.supported_glsl_versions.is_empty() {
193 return Err(ProgramCreationError::CompilationNotSupported);
194 }
195
196 if !check_shader_type_compatibility(&ctxt, shader_type) {
197 return Err(ProgramCreationError::ShaderTypeNotSupported);
198 }
199
200 let id = if ctxt.version >= &Version(Api::Gl, 4, 6) ||
201 (ctxt.version >= &Version(Api::Gl, 4, 1) && ctxt.extensions.gl_arb_gl_spirv)
202 {
203 ctxt.gl.CreateShader(shader_type)
204 } else {
205 return Err(ProgramCreationError::CompilationNotSupported);
206 };
207
208 if id == 0 {
209 return Err(ProgramCreationError::ShaderTypeNotSupported);
210 }
211
212 let binary = &spirv.binary;
213 let entry_point = ffi::CString::new(spirv.entry_point.as_bytes()).unwrap();
214
215 const GL_SHADER_BINARY_FORMAT_SPIR_V: gl::types::GLenum = 0x9551;
216
217 ctxt.gl.ShaderBinary(1, &id, GL_SHADER_BINARY_FORMAT_SPIR_V, binary.as_ptr() as _, binary.len() as _);
218
219 {
221 ctxt.report_debug_output_errors.set(false);
222
223 ctxt.gl.SpecializeShader(id, entry_point.as_ptr() as _, 0, ptr::null(), ptr::null());
224
225 ctxt.report_debug_output_errors.set(true);
226 }
227
228 let compilation_success = {
230 let mut compilation_success: gl::types::GLint = 0;
231 ctxt.gl.GetShaderiv(id, gl::COMPILE_STATUS, &mut compilation_success);
232 compilation_success
233 };
234
235 if compilation_success == 1 {
236 Ok(Shader {
237 context: facade.get_context().clone(),
238 id: Handle::Id(id)
239 })
240 } else {
241 let mut error_log_size: gl::types::GLint = 0;
243
244 ctxt.gl.GetShaderiv(id, gl::INFO_LOG_LENGTH, &mut error_log_size);
245
246 let mut error_log: Vec<u8> = Vec::with_capacity(error_log_size as usize);
247
248 ctxt.gl.GetShaderInfoLog(id, error_log_size, &mut error_log_size,
249 error_log.as_mut_ptr() as *mut gl::types::GLchar);
250
251 error_log.set_len(error_log_size as usize);
252
253 match String::from_utf8(error_log) {
254 Ok(msg) => Err(ProgramCreationError::CompilationError(msg, ShaderType::from_opengl_type(shader_type))),
255 Err(_) => Err(
256 ProgramCreationError::CompilationError("Could not convert the log \
257 message to UTF-8".to_owned(), ShaderType::from_opengl_type(shader_type))
258 ),
259 }
260 }
261 }
262}
263
264pub fn check_shader_type_compatibility<C: ?Sized>(ctxt: &C, shader_type: gl::types::GLenum)
265 -> bool where C: CapabilitiesSource
266{
267 match shader_type {
268 gl::VERTEX_SHADER | gl::FRAGMENT_SHADER => (),
269 gl::GEOMETRY_SHADER => {
270 if !(ctxt.get_version() >= &Version(Api::Gl, 3, 2))
271 && !(ctxt.get_version() >= &Version(Api::GlEs, 3, 2))
272 && !ctxt.get_extensions().gl_arb_geometry_shader4
273 && !ctxt.get_extensions().gl_ext_geometry_shader4
274 && !ctxt.get_extensions().gl_ext_geometry_shader
275 && !ctxt.get_extensions().gl_oes_geometry_shader
276 {
277 return false;
278 }
279 },
280 gl::TESS_CONTROL_SHADER | gl::TESS_EVALUATION_SHADER => {
281 if !(ctxt.get_version() >= &Version(Api::Gl, 4, 0))
282 && !(ctxt.get_version() >= &Version(Api::GlEs, 3, 2))
283 && !ctxt.get_extensions().gl_arb_tessellation_shader
284 && !ctxt.get_extensions().gl_oes_tessellation_shader
285 {
286 return false;
287 }
288 },
289 gl::COMPUTE_SHADER => {
290 if !(ctxt.get_version() >= &Version(Api::Gl, 4, 3))
291 && !(ctxt.get_version() >= &Version(Api::GlEs, 3, 1))
292 && !ctxt.get_extensions().gl_arb_compute_shader
293 {
294 return false;
295 }
296 },
297 _ => unreachable!()
298 };
299
300 true
301}