1use crate::gl;
2
3use crate::context::CommandContext;
4use crate::version::Version;
5use crate::version::Api;
6
7use crate::backend::Facade;
8use crate::CapabilitiesSource;
9
10use std::fmt;
11use std::collections::hash_map::{self, HashMap};
12use std::hash::BuildHasherDefault;
13
14use fnv::FnvHasher;
15
16use crate::GlObject;
17use crate::ProgramExt;
18use crate::Handle;
19use crate::RawUniformValue;
20
21use crate::program::{COMPILER_GLOBAL_LOCK, ProgramCreationInput, ProgramCreationError, ShaderType, Binary, SpirvProgram};
22use crate::program::GetBinaryError;
23
24use crate::program::reflection::{Uniform, UniformBlock, OutputPrimitives};
25use crate::program::reflection::{Attribute, TransformFeedbackBuffer};
26use crate::program::reflection::{SubroutineData, ShaderStage, SubroutineUniform};
27use crate::program::shader::{build_shader, build_spirv_shader};
28
29use crate::program::raw::RawProgram;
30
31use crate::vertex::VertexFormat;
32
33pub struct Program {
35 raw: RawProgram,
36 outputs_srgb: bool,
37 uses_point_size: bool,
38}
39
40impl Program {
41 pub fn new<'a, F: ?Sized, I>(facade: &F, input: I) -> Result<Program, ProgramCreationError>
43 where I: Into<ProgramCreationInput<'a>>, F: Facade
44 {
45 let input = input.into();
46
47 let (raw, outputs_srgb, uses_point_size) = match input {
48 ProgramCreationInput::SourceCode { vertex_shader, tessellation_control_shader,
49 tessellation_evaluation_shader, geometry_shader,
50 fragment_shader, transform_feedback_varyings,
51 outputs_srgb, uses_point_size } =>
52 {
53 let mut has_geometry_shader = false;
54 let mut has_tessellation_control_shader = false;
55 let mut has_tessellation_evaluation_shader = false;
56
57 let mut shaders = vec![
58 (vertex_shader, ShaderType::Vertex),
59 (fragment_shader, ShaderType::Fragment)
60 ];
61
62 if let Some(gs) = geometry_shader {
63 shaders.push((gs, ShaderType::Geometry));
64 has_geometry_shader = true;
65 }
66
67 if let Some(ts) = tessellation_control_shader {
68 shaders.push((ts, ShaderType::TesselationControl));
69 has_tessellation_control_shader = true;
70 }
71
72 if let Some(ts) = tessellation_evaluation_shader {
73 shaders.push((ts, ShaderType::TesselationEvaluation));
74 has_tessellation_evaluation_shader = true;
75 }
76
77 if transform_feedback_varyings.is_some() &&
79 !(facade.get_context().get_version() >= &Version(Api::Gl, 3, 0)) &&
80 !facade.get_context().get_extensions().gl_ext_transform_feedback
81 {
82 return Err(ProgramCreationError::TransformFeedbackNotSupported);
83 }
84
85 if uses_point_size && (facade.get_context().get_version().0 == Api::Gl) && !(facade.get_context().get_version() >= &Version(Api::Gl, 2, 0)) {
86 return Err(ProgramCreationError::PointSizeNotSupported);
87 }
88
89 let _lock = COMPILER_GLOBAL_LOCK.lock();
90
91 let shaders_store = {
92 let mut shaders_store = Vec::new();
93 for (src, ty) in shaders.into_iter() {
94 shaders_store.push(build_shader(facade, ty.to_opengl_type(), src)?);
95 }
96 shaders_store
97 };
98
99 (RawProgram::from_shaders(facade, &shaders_store, has_geometry_shader,
100 has_tessellation_control_shader, has_tessellation_evaluation_shader,
101 transform_feedback_varyings)?,
102 outputs_srgb, uses_point_size)
103 },
104
105 ProgramCreationInput::Binary { data, outputs_srgb, uses_point_size } => {
106 if uses_point_size && (facade.get_context().get_version().0 == Api::Gl) && !(facade.get_context().get_version() >= &Version(Api::Gl, 2, 0)) {
107 return Err(ProgramCreationError::PointSizeNotSupported);
108 }
109
110 (RawProgram::from_binary(facade, data)?, outputs_srgb, uses_point_size)
111 },
112
113 ProgramCreationInput::SpirV(SpirvProgram { vertex_shader, tessellation_control_shader,
114 tessellation_evaluation_shader, geometry_shader,
115 fragment_shader, transform_feedback_varyings,
116 outputs_srgb, uses_point_size }) =>
117 {
118 let mut has_geometry_shader = false;
119 let mut has_tessellation_control_shader = false;
120 let mut has_tessellation_evaluation_shader = false;
121
122 let mut shaders = vec![
123 (vertex_shader, ShaderType::Vertex),
124 (fragment_shader, ShaderType::Fragment)
125 ];
126
127 if let Some(gs) = geometry_shader {
128 shaders.push((gs, ShaderType::Geometry));
129 has_geometry_shader = true;
130 }
131
132 if let Some(ts) = tessellation_control_shader {
133 shaders.push((ts, ShaderType::TesselationControl));
134 has_tessellation_control_shader = true;
135 }
136
137 if let Some(ts) = tessellation_evaluation_shader {
138 shaders.push((ts, ShaderType::TesselationEvaluation));
139 has_tessellation_evaluation_shader = true;
140 }
141
142 if transform_feedback_varyings.is_some() &&
144 !(facade.get_context().get_version() >= &Version(Api::Gl, 3, 0)) &&
145 !facade.get_context().get_extensions().gl_ext_transform_feedback
146 {
147 return Err(ProgramCreationError::TransformFeedbackNotSupported);
148 }
149
150 if uses_point_size && (facade.get_context().get_version().0 == Api::Gl) && !(facade.get_context().get_version() >= &Version(Api::Gl, 2, 0)) {
151 return Err(ProgramCreationError::PointSizeNotSupported);
152 }
153
154 let _lock = COMPILER_GLOBAL_LOCK.lock();
155
156 let shaders_store = {
157 let mut shaders_store = Vec::new();
158 for (src, ty) in shaders.into_iter() {
159 shaders_store.push(build_spirv_shader(facade, ty.to_opengl_type(), &src)?);
160 }
161 shaders_store
162 };
163
164 (RawProgram::from_shaders(facade, &shaders_store, has_geometry_shader,
165 has_tessellation_control_shader, has_tessellation_evaluation_shader,
166 transform_feedback_varyings)?,
167 outputs_srgb, uses_point_size)
168 }
169 };
170 Ok(Program {
171 raw,
172 outputs_srgb,
173 uses_point_size,
174 })
175 }
176
177 #[inline]
199 pub fn from_source<'a, F: ?Sized>(facade: &F, vertex_shader: &'a str, fragment_shader: &'a str,
200 geometry_shader: Option<&'a str>)
201 -> Result<Program, ProgramCreationError> where F: Facade
202 {
203 Program::new(facade, ProgramCreationInput::SourceCode {
204 vertex_shader,
205 fragment_shader,
206 geometry_shader,
207 tessellation_control_shader: None,
208 tessellation_evaluation_shader: None,
209 transform_feedback_varyings: None,
210 outputs_srgb: true,
211 uses_point_size: false,
212 })
213 }
214
215 #[inline]
220 pub fn get_binary(&self) -> Result<Binary, GetBinaryError> {
221 self.raw.get_binary()
222 }
223
224 #[inline]
236 pub fn get_frag_data_location(&self, name: &str) -> Option<u32> {
237 self.raw.get_frag_data_location(name)
238 }
239
240 #[inline]
242 pub fn get_uniform(&self, name: &str) -> Option<&Uniform> {
243 self.raw.get_uniform(name)
244 }
245
246 #[inline]
258 pub fn uniforms(&self) -> hash_map::Iter<'_, String, Uniform> {
259 self.raw.uniforms()
260 }
261
262 #[inline]
274 pub fn get_uniform_blocks(&self)
275 -> &HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>> {
276 self.raw.get_uniform_blocks()
277 }
278
279 #[inline]
281 pub fn get_transform_feedback_buffers(&self) -> &[TransformFeedbackBuffer] {
282 self.raw.get_transform_feedback_buffers()
283 }
284
285 #[inline]
290 pub fn transform_feedback_matches(&self, format: &VertexFormat, stride: usize) -> bool {
291 self.raw.transform_feedback_matches(format, stride)
292 }
293
294 #[inline]
300 pub fn get_output_primitives(&self) -> Option<OutputPrimitives> {
301 self.raw.get_output_primitives()
302 }
303
304 #[inline]
306 pub fn has_tessellation_shaders(&self) -> bool {
307 self.raw.has_tessellation_shaders()
308 }
309
310 #[inline]
312 pub fn has_tessellation_control_shader(&self) -> bool {
313 self.raw.has_tessellation_control_shader()
314 }
315
316 #[inline]
318 pub fn has_tessellation_evaluation_shader(&self) -> bool {
319 self.raw.has_tessellation_evaluation_shader()
320 }
321
322 #[inline]
324 pub fn has_geometry_shader(&self) -> bool {
325 self.raw.has_geometry_shader()
326 }
327
328 #[inline]
330 pub fn get_attribute(&self, name: &str) -> Option<&Attribute> {
331 self.raw.get_attribute(name)
332 }
333
334 #[inline]
346 pub fn attributes(&self) -> hash_map::Iter<'_, String, Attribute> {
347 self.raw.attributes()
348 }
349
350 #[inline]
352 pub fn has_srgb_output(&self) -> bool {
353 self.outputs_srgb
354 }
355
356 #[inline]
368 pub fn get_shader_storage_blocks(&self)
369 -> &HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>> {
370 self.raw.get_shader_storage_blocks()
371 }
372
373 #[inline]
385 pub fn get_atomic_counters(&self)
386 -> &HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>> {
387 self.raw.get_atomic_counters()
388 }
389
390 #[inline]
404 pub fn get_subroutine_uniforms(&self)
405 -> &HashMap<(String, ShaderStage), SubroutineUniform, BuildHasherDefault<FnvHasher>> {
406 &self.raw.get_subroutine_data().subroutine_uniforms
407 }
408
409 #[inline]
414 pub fn uses_point_size(&self) -> bool {
415 self.uses_point_size
416 }
417}
418
419impl fmt::Debug for Program {
420 #[inline]
421 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
422 write!(formatter, "{:?}", self.raw)
423 }
424}
425
426impl GlObject for Program {
427 type Id = Handle;
428
429 #[inline]
430 fn get_id(&self) -> Handle {
431 self.raw.get_id()
432 }
433}
434
435impl ProgramExt for Program {
436 fn use_program(&self, ctxt: &mut CommandContext<'_>) {
437 if ctxt.version.0 == Api::Gl {
439 if self.uses_point_size && !ctxt.state.enabled_program_point_size {
440 unsafe { ctxt.gl.Enable(gl::PROGRAM_POINT_SIZE); }
441 } else if !self.uses_point_size && ctxt.state.enabled_program_point_size {
442 unsafe { ctxt.gl.Disable(gl::PROGRAM_POINT_SIZE); }
443 }
444 }
445
446 if (ctxt.version >= &Version(Api::Gl, 3, 0) || ctxt.extensions.gl_arb_framebuffer_srgb ||
447 ctxt.extensions.gl_ext_framebuffer_srgb || ctxt.extensions.gl_ext_srgb_write_control) && ctxt.state.enabled_framebuffer_srgb == self.outputs_srgb {
448 ctxt.state.enabled_framebuffer_srgb = !self.outputs_srgb;
449
450 if self.outputs_srgb {
451 unsafe { ctxt.gl.Disable(gl::FRAMEBUFFER_SRGB) };
452 } else {
453 unsafe { ctxt.gl.Enable(gl::FRAMEBUFFER_SRGB) };
454 }
455 }
456
457 self.raw.use_program(ctxt)
458 }
459
460 #[inline]
461 fn set_uniform(&self, ctxt: &mut CommandContext<'_>, uniform_location: gl::types::GLint,
462 value: &RawUniformValue)
463 {
464 self.raw.set_uniform(ctxt, uniform_location, value)
465 }
466
467 #[inline]
468 fn set_uniform_block_binding(&self, ctxt: &mut CommandContext<'_>, block_location: gl::types::GLuint,
469 value: gl::types::GLuint)
470 {
471 self.raw.set_uniform_block_binding(ctxt, block_location, value)
472 }
473
474 #[inline]
475 fn set_shader_storage_block_binding(&self, ctxt: &mut CommandContext<'_>,
476 block_location: gl::types::GLuint,
477 value: gl::types::GLuint)
478 {
479 self.raw.set_shader_storage_block_binding(ctxt, block_location, value)
480 }
481
482 #[inline]
483 fn set_subroutine_uniforms_for_stage(&self, ctxt: &mut CommandContext<'_>,
484 stage: ShaderStage,
485 indices: &[gl::types::GLuint])
486 {
487 self.raw.set_subroutine_uniforms_for_stage(ctxt, stage, indices);
488 }
489
490 #[inline]
491 fn get_uniform(&self, name: &str) -> Option<&Uniform> {
492 self.raw.get_uniform(name)
493 }
494
495 #[inline]
496 fn get_uniform_blocks(&self) -> &HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>> {
497 self.raw.get_uniform_blocks()
498 }
499
500 #[inline]
501 fn get_shader_storage_blocks(&self)
502 -> &HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>> {
503 self.raw.get_shader_storage_blocks()
504 }
505
506 #[inline]
507 fn get_atomic_counters(&self)
508 -> &HashMap<String, UniformBlock, BuildHasherDefault<FnvHasher>> {
509 self.raw.get_atomic_counters()
510 }
511
512 #[inline]
513 fn get_subroutine_data(&self) -> &SubroutineData {
514 self.raw.get_subroutine_data()
515 }
516}