1use crate::{
22 core::log::{Log, MessageKind},
23 error::FrameworkError,
24 gl::server::{GlGraphicsServer, GlKind},
25 gpu_program::{
26 GpuProgramTrait, SamplerKind, ShaderPropertyKind, ShaderResourceDefinition,
27 ShaderResourceKind,
28 },
29};
30use glow::HasContext;
31use std::{marker::PhantomData, rc::Weak};
32
33impl SamplerKind {
34 pub fn glsl_name(&self) -> &str {
35 match self {
36 SamplerKind::Sampler1D => "sampler1D",
37 SamplerKind::Sampler2D => "sampler2D",
38 SamplerKind::Sampler3D => "sampler3D",
39 SamplerKind::SamplerCube => "samplerCube",
40 SamplerKind::USampler1D => "usampler1D",
41 SamplerKind::USampler2D => "usampler2D",
42 SamplerKind::USampler3D => "usampler3D",
43 SamplerKind::USamplerCube => "usamplerCube",
44 }
45 }
46}
47
48unsafe fn create_shader(
49 server: &GlGraphicsServer,
50 name: String,
51 actual_type: u32,
52 source: &str,
53 gl_kind: GlKind,
54) -> Result<glow::Shader, FrameworkError> {
55 let merged_source = prepare_source_code(source, gl_kind);
56
57 let shader = server.gl.create_shader(actual_type)?;
58 server.gl.shader_source(shader, &merged_source);
59 server.gl.compile_shader(shader);
60
61 let status = server.gl.get_shader_compile_status(shader);
62 let compilation_message = server.gl.get_shader_info_log(shader);
63
64 if !status {
65 Log::writeln(
66 MessageKind::Error,
67 format!("Failed to compile {name} shader: {compilation_message}"),
68 );
69 Err(FrameworkError::ShaderCompilationFailed {
70 shader_name: name,
71 error_message: compilation_message,
72 })
73 } else {
74 let msg = if compilation_message.is_empty()
75 || compilation_message.chars().all(|c| c.is_whitespace())
76 {
77 format!("Shader {name} compiled successfully!")
78 } else {
79 format!("Shader {name} compiled successfully!\nAdditional info: {compilation_message}")
80 };
81
82 Log::writeln(MessageKind::Information, msg);
83
84 Ok(shader)
85 }
86}
87
88fn prepare_source_code(code: &str, gl_kind: GlKind) -> String {
89 let mut full_source_code = "#version 330 core\n// include 'shared.glsl'\n".to_owned();
90
91 if gl_kind == GlKind::OpenGLES {
92 full_source_code += r#"
93 precision highp float;
94 precision highp int;
95 precision highp usampler2D;
96 precision highp usampler3D;
97 precision highp usamplerCube;
98 precision highp sampler2D;
99 precision highp sampler3D;
100 precision highp samplerCube;
101 "#;
102 }
103
104 full_source_code += include_str!("shaders/shared.glsl");
105 full_source_code += "\n// end of include\n";
106 full_source_code += code;
107
108 if gl_kind == GlKind::OpenGLES {
109 full_source_code.replace("#version 330 core", "#version 300 es")
110 } else {
111 full_source_code
112 }
113}
114
115pub struct GlProgram {
116 state: Weak<GlGraphicsServer>,
117 pub id: glow::Program,
118 thread_mark: PhantomData<*const u8>,
120}
121
122impl GlProgram {
123 pub fn from_source_and_resources(
124 server: &GlGraphicsServer,
125 program_name: &str,
126 vertex_source: &str,
127 fragment_source: &str,
128 resources: &[ShaderResourceDefinition],
129 ) -> Result<GlProgram, FrameworkError> {
130 let mut vertex_source = vertex_source.to_string();
131 let mut fragment_source = fragment_source.to_string();
132
133 for resource in resources {
135 for other_resource in resources {
136 if std::ptr::eq(resource, other_resource) {
137 continue;
138 }
139
140 if std::mem::discriminant(&resource.kind)
141 == std::mem::discriminant(&other_resource.kind)
142 {
143 if resource.binding == other_resource.binding {
144 return Err(FrameworkError::Custom(format!(
145 "Resource {} and {} using the same binding point {} \
146 in the {program_name} GPU program.",
147 resource.name, other_resource.name, resource.binding
148 )));
149 }
150
151 if resource.name == other_resource.name {
152 return Err(FrameworkError::Custom(format!(
153 "There are two or more resources with same name {} \
154 in the {program_name} GPU program.",
155 resource.name
156 )));
157 }
158 }
159 }
160 }
161
162 for initial_source in [&mut vertex_source, &mut fragment_source] {
164 let mut texture_bindings = String::new();
165
166 for property in resources {
167 let resource_name = &property.name;
168 match property.kind {
169 ShaderResourceKind::Texture { kind, .. } => {
170 let glsl_name = kind.glsl_name();
171 texture_bindings += &format!("uniform {glsl_name} {resource_name};\n");
172 }
173 ShaderResourceKind::PropertyGroup(ref fields) => {
174 if fields.is_empty() {
175 Log::warn(format!(
176 "Uniform block {resource_name} is empty and will be ignored!"
177 ));
178 continue;
179 }
180 let mut block = format!("struct T{resource_name}{{\n");
181 for field in fields {
182 let field_name = &field.name;
183 match field.kind {
184 ShaderPropertyKind::Float { .. } => {
185 block += &format!("\tfloat {field_name};\n");
186 }
187 ShaderPropertyKind::FloatArray { max_len, .. } => {
188 block += &format!("\tfloat {field_name}[{max_len}];\n");
189 }
190 ShaderPropertyKind::Int { .. } => {
191 block += &format!("\tint {field_name};\n");
192 }
193 ShaderPropertyKind::IntArray { max_len, .. } => {
194 block += &format!("\tint {field_name}[{max_len}];\n");
195 }
196 ShaderPropertyKind::UInt { .. } => {
197 block += &format!("\tuint {field_name};\n");
198 }
199 ShaderPropertyKind::UIntArray { max_len, .. } => {
200 block += &format!("\tuint {field_name}[{max_len}];\n");
201 }
202 ShaderPropertyKind::Bool { .. } => {
203 block += &format!("\tbool {field_name};\n");
204 }
205 ShaderPropertyKind::Vector2 { .. } => {
206 block += &format!("\tvec2 {field_name};\n");
207 }
208 ShaderPropertyKind::Vector2Array { max_len, .. } => {
209 block += &format!("\tvec2 {field_name}[{max_len}];\n");
210 }
211 ShaderPropertyKind::Vector3 { .. } => {
212 block += &format!("\tvec3 {field_name};\n");
213 }
214 ShaderPropertyKind::Vector3Array { max_len, .. } => {
215 block += &format!("\tvec3 {field_name}[{max_len}];\n");
216 }
217 ShaderPropertyKind::Vector4 { .. } => {
218 block += &format!("\tvec4 {field_name};\n");
219 }
220 ShaderPropertyKind::Vector4Array { max_len, .. } => {
221 block += &format!("\tvec4 {field_name}[{max_len}];\n");
222 }
223 ShaderPropertyKind::Matrix2 { .. } => {
224 block += &format!("\tmat2 {field_name};\n");
225 }
226 ShaderPropertyKind::Matrix2Array { max_len, .. } => {
227 block += &format!("\tmat2 {field_name}[{max_len}];\n");
228 }
229 ShaderPropertyKind::Matrix3 { .. } => {
230 block += &format!("\tmat3 {field_name};\n");
231 }
232 ShaderPropertyKind::Matrix3Array { max_len, .. } => {
233 block += &format!("\tmat3 {field_name}[{max_len}];\n");
234 }
235 ShaderPropertyKind::Matrix4 { .. } => {
236 block += &format!("\tmat4 {field_name};\n");
237 }
238 ShaderPropertyKind::Matrix4Array { max_len, .. } => {
239 block += &format!("\tmat4 {field_name}[{max_len}];\n");
240 }
241 ShaderPropertyKind::Color { .. } => {
242 block += &format!("\tvec4 {field_name};\n");
243 }
244 }
245 }
246 block += "};\n";
247 block += &format!("layout(std140) uniform U{resource_name} {{ T{resource_name} {resource_name}; }};\n");
248 initial_source.insert_str(0, &block);
249 }
250 }
251 }
252 initial_source.insert_str(0, &texture_bindings);
253 }
254
255 let program = Self::from_source(server, program_name, &vertex_source, &fragment_source)?;
256
257 unsafe {
258 server.set_program(Some(program.id));
259 for resource_definition in resources {
260 match resource_definition.kind {
261 ShaderResourceKind::Texture { .. } => {
262 if let Some(location) = server
263 .gl
264 .get_uniform_location(program.id, &resource_definition.name)
265 {
266 server
267 .gl
268 .uniform_1_i32(Some(&location), resource_definition.binding as i32);
269 }
270 }
271 ShaderResourceKind::PropertyGroup { .. } => {
272 if let Some(shader_block_index) = server.gl.get_uniform_block_index(
273 program.id,
274 &format!("U{}", resource_definition.name),
275 ) {
276 server.gl.uniform_block_binding(
277 program.id,
278 shader_block_index,
279 resource_definition.binding as u32,
280 )
281 } else {
282 Log::warn(format!(
283 "Couldn't find uniform block U{}",
284 resource_definition.name
285 ));
286 }
287 }
288 }
289 }
290 }
291
292 Ok(program)
293 }
294
295 fn from_source(
296 server: &GlGraphicsServer,
297 name: &str,
298 vertex_source: &str,
299 fragment_source: &str,
300 ) -> Result<GlProgram, FrameworkError> {
301 unsafe {
302 let vertex_shader = create_shader(
303 server,
304 format!("{name}_VertexShader"),
305 glow::VERTEX_SHADER,
306 vertex_source,
307 server.gl_kind(),
308 )?;
309 let fragment_shader = create_shader(
310 server,
311 format!("{name}_FragmentShader"),
312 glow::FRAGMENT_SHADER,
313 fragment_source,
314 server.gl_kind(),
315 )?;
316 let program = server.gl.create_program()?;
317 server.gl.attach_shader(program, vertex_shader);
318 server.gl.delete_shader(vertex_shader);
319 server.gl.attach_shader(program, fragment_shader);
320 server.gl.delete_shader(fragment_shader);
321 server.gl.link_program(program);
322 let status = server.gl.get_program_link_status(program);
323 let link_message = server.gl.get_program_info_log(program);
324
325 if !status {
326 Log::writeln(
327 MessageKind::Error,
328 format!("Failed to link {name} shader: {link_message}"),
329 );
330 Err(FrameworkError::ShaderLinkingFailed {
331 shader_name: name.to_owned(),
332 error_message: link_message,
333 })
334 } else {
335 let msg = if link_message.is_empty()
336 || link_message.chars().all(|c| c.is_whitespace())
337 {
338 format!("Shader {name} linked successfully!")
339 } else {
340 format!("Shader {name} linked successfully!\nAdditional info: {link_message}")
341 };
342
343 Log::writeln(MessageKind::Information, msg);
344
345 Ok(Self {
346 state: server.weak(),
347 id: program,
348 thread_mark: PhantomData,
349 })
350 }
351 }
352 }
353}
354
355impl GpuProgramTrait for GlProgram {}
356
357impl Drop for GlProgram {
358 fn drop(&mut self) {
359 if let Some(state) = self.state.upgrade() {
360 unsafe {
361 state.gl.delete_program(self.id);
362 }
363 }
364 }
365}