/ egui_glow / src / post_process.rs
post_process.rs
  1  #![allow(unsafe_code)]
  2  use crate::check_for_gl_error;
  3  use crate::misc_util::{compile_shader, link_program};
  4  use crate::vao::BufferInfo;
  5  use glow::HasContext as _;
  6  
  7  /// Uses a framebuffer to render everything in linear color space and convert it back to `sRGB`
  8  /// in a separate "post processing" step
  9  pub(crate) struct PostProcess {
 10      gl: std::rc::Rc<glow::Context>,
 11      pos_buffer: glow::Buffer,
 12      index_buffer: glow::Buffer,
 13      vao: crate::vao::VertexArrayObject,
 14      is_webgl_1: bool,
 15      texture: glow::Texture,
 16      texture_size: (i32, i32),
 17      fbo: glow::Framebuffer,
 18      program: glow::Program,
 19  }
 20  
 21  impl PostProcess {
 22      pub(crate) unsafe fn new(
 23          gl: std::rc::Rc<glow::Context>,
 24          shader_prefix: &str,
 25          is_webgl_1: bool,
 26          width: i32,
 27          height: i32,
 28      ) -> Result<PostProcess, String> {
 29          let fbo = gl.create_framebuffer()?;
 30  
 31          gl.bind_framebuffer(glow::FRAMEBUFFER, Some(fbo));
 32  
 33          let texture = gl.create_texture().unwrap();
 34  
 35          gl.bind_texture(glow::TEXTURE_2D, Some(texture));
 36  
 37          gl.tex_parameter_i32(
 38              glow::TEXTURE_2D,
 39              glow::TEXTURE_WRAP_S,
 40              glow::CLAMP_TO_EDGE as i32,
 41          );
 42  
 43          gl.tex_parameter_i32(
 44              glow::TEXTURE_2D,
 45              glow::TEXTURE_WRAP_T,
 46              glow::CLAMP_TO_EDGE as i32,
 47          );
 48  
 49          gl.tex_parameter_i32(
 50              glow::TEXTURE_2D,
 51              glow::TEXTURE_MIN_FILTER,
 52              glow::NEAREST as i32,
 53          );
 54  
 55          gl.tex_parameter_i32(
 56              glow::TEXTURE_2D,
 57              glow::TEXTURE_MAG_FILTER,
 58              glow::NEAREST as i32,
 59          );
 60  
 61          gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
 62  
 63          let (internal_format, format) = if is_webgl_1 {
 64              (glow::SRGB_ALPHA, glow::SRGB_ALPHA)
 65          } else {
 66              (glow::SRGB8_ALPHA8, glow::RGBA)
 67          };
 68  
 69          gl.tex_image_2d(
 70              glow::TEXTURE_2D,
 71              0,
 72              internal_format as i32,
 73              width,
 74              height,
 75              0,
 76              format,
 77              glow::UNSIGNED_BYTE,
 78              None,
 79          );
 80          check_for_gl_error!(&gl, "post process texture initialization");
 81  
 82          gl.framebuffer_texture_2d(
 83              glow::FRAMEBUFFER,
 84              glow::COLOR_ATTACHMENT0,
 85              glow::TEXTURE_2D,
 86              Some(texture),
 87              0,
 88          );
 89          gl.bind_texture(glow::TEXTURE_2D, None);
 90          gl.bind_framebuffer(glow::FRAMEBUFFER, None);
 91  
 92          let vert_shader = compile_shader(
 93              &gl,
 94              glow::VERTEX_SHADER,
 95              &format!(
 96                  "{}\n{}",
 97                  shader_prefix,
 98                  include_str!("shader/post_vertex_100es.glsl")
 99              ),
100          )?;
101          let frag_shader = compile_shader(
102              &gl,
103              glow::FRAGMENT_SHADER,
104              &format!(
105                  "{}\n{}",
106                  shader_prefix,
107                  include_str!("shader/post_fragment_100es.glsl")
108              ),
109          )?;
110          let program = link_program(&gl, [vert_shader, frag_shader].iter())?;
111  
112          let positions = vec![0.0f32, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0];
113  
114          let indices = vec![0u8, 1, 2, 1, 2, 3];
115  
116          let pos_buffer = gl.create_buffer()?;
117          gl.bind_buffer(glow::ARRAY_BUFFER, Some(pos_buffer));
118          gl.buffer_data_u8_slice(
119              glow::ARRAY_BUFFER,
120              bytemuck::cast_slice(&positions),
121              glow::STATIC_DRAW,
122          );
123  
124          let a_pos_loc = gl
125              .get_attrib_location(program, "a_pos")
126              .ok_or_else(|| "failed to get location of a_pos".to_string())?;
127          let vao = crate::vao::VertexArrayObject::new(
128              &gl,
129              pos_buffer,
130              vec![BufferInfo {
131                  location: a_pos_loc,
132                  vector_size: 2,
133                  data_type: glow::FLOAT,
134                  normalized: false,
135                  stride: 0,
136                  offset: 0,
137              }],
138          );
139  
140          let index_buffer = gl.create_buffer()?;
141          gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buffer));
142          gl.buffer_data_u8_slice(glow::ELEMENT_ARRAY_BUFFER, &indices, glow::STATIC_DRAW);
143  
144          gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
145          check_for_gl_error!(&gl, "post process initialization");
146  
147          Ok(PostProcess {
148              gl,
149              pos_buffer,
150              index_buffer,
151              vao,
152              is_webgl_1,
153              texture,
154              texture_size: (width, height),
155              fbo,
156              program,
157          })
158      }
159  
160      pub(crate) unsafe fn begin(&mut self, width: i32, height: i32) {
161          if (width, height) != self.texture_size {
162              self.gl.bind_texture(glow::TEXTURE_2D, Some(self.texture));
163              self.gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
164  
165              let (internal_format, format) = if self.is_webgl_1 {
166                  (glow::SRGB_ALPHA, glow::SRGB_ALPHA)
167              } else {
168                  (glow::SRGB8_ALPHA8, glow::RGBA)
169              };
170              self.gl.tex_image_2d(
171                  glow::TEXTURE_2D,
172                  0,
173                  internal_format as i32,
174                  width,
175                  height,
176                  0,
177                  format,
178                  glow::UNSIGNED_BYTE,
179                  None,
180              );
181  
182              self.gl.bind_texture(glow::TEXTURE_2D, None);
183              self.texture_size = (width, height);
184          }
185  
186          self.gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.fbo));
187          self.gl.clear_color(0.0, 0.0, 0.0, 0.0);
188          self.gl.clear(glow::COLOR_BUFFER_BIT);
189  
190          check_for_gl_error!(&self.gl, "PostProcess::begin");
191      }
192  
193      pub(crate) unsafe fn bind(&self) {
194          self.gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.fbo));
195      }
196  
197      pub(crate) unsafe fn end(&self) {
198          self.gl.bind_framebuffer(glow::FRAMEBUFFER, None);
199          self.gl.disable(glow::SCISSOR_TEST);
200  
201          self.gl.use_program(Some(self.program));
202  
203          self.gl.active_texture(glow::TEXTURE0);
204          self.gl.bind_texture(glow::TEXTURE_2D, Some(self.texture));
205          let u_sampler_loc = self
206              .gl
207              .get_uniform_location(self.program, "u_sampler")
208              .unwrap();
209          self.gl.uniform_1_i32(Some(&u_sampler_loc), 0);
210          self.vao.bind(&self.gl);
211  
212          self.gl
213              .bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(self.index_buffer));
214          self.gl
215              .draw_elements(glow::TRIANGLES, 6, glow::UNSIGNED_BYTE, 0);
216          self.vao.unbind(&self.gl);
217          self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None);
218          self.gl.bind_texture(glow::TEXTURE_2D, None);
219          self.gl.use_program(None);
220  
221          check_for_gl_error!(&self.gl, "PostProcess::end");
222      }
223  
224      pub(crate) unsafe fn destroy(&self) {
225          self.gl.delete_buffer(self.pos_buffer);
226          self.gl.delete_buffer(self.index_buffer);
227          self.gl.delete_program(self.program);
228          self.gl.delete_framebuffer(self.fbo);
229          self.gl.delete_texture(self.texture);
230      }
231  }