Skip to main content

luminance_webgl/webgl2/
framebuffer.rs

1//! Framebuffer support for WebGL2.
2
3use crate::webgl2::{state::WebGL2State, WebGL2};
4use js_sys::Uint32Array;
5use luminance::{
6  backend::{
7    color_slot::ColorSlot,
8    depth_stencil_slot::DepthStencilSlot,
9    framebuffer::{Framebuffer as FramebufferBackend, FramebufferBackBuffer},
10  },
11  framebuffer::{FramebufferError, IncompleteReason},
12  texture::{Dim2, Dimensionable, Sampler},
13};
14use std::{cell::RefCell, rc::Rc};
15use web_sys::{WebGl2RenderingContext, WebGlFramebuffer, WebGlRenderbuffer};
16
17pub struct Framebuffer<D>
18where
19  D: Dimensionable,
20{
21  // None is the default framebuffer…
22  pub(crate) handle: Option<WebGlFramebuffer>,
23  renderbuffer: Option<WebGlRenderbuffer>,
24  pub(crate) size: D::Size,
25  state: Rc<RefCell<WebGL2State>>,
26}
27
28impl<D> Drop for Framebuffer<D>
29where
30  D: Dimensionable,
31{
32  fn drop(&mut self) {
33    let state = self.state.borrow();
34
35    state.ctx.delete_renderbuffer(self.renderbuffer.as_ref());
36    state.ctx.delete_framebuffer(self.handle.as_ref());
37  }
38}
39
40unsafe impl<D> FramebufferBackend<D> for WebGL2
41where
42  D: Dimensionable,
43{
44  type FramebufferRepr = Framebuffer<D>;
45
46  unsafe fn new_framebuffer<CS, DS>(
47    &mut self,
48    size: D::Size,
49    _: usize,
50    _: &Sampler,
51  ) -> Result<Self::FramebufferRepr, FramebufferError>
52  where
53    CS: ColorSlot<Self, D>,
54    DS: DepthStencilSlot<Self, D>,
55  {
56    let color_formats = CS::color_formats();
57    let depth_format = DS::depth_format();
58    let mut depth_renderbuffer = None;
59
60    let mut state = self.state.borrow_mut();
61
62    let handle = state
63      .create_framebuffer()
64      .ok_or_else(|| FramebufferError::cannot_create())?;
65    state.bind_draw_framebuffer(Some(&handle));
66
67    // reserve textures to speed up slots creation
68    let textures_needed = color_formats.len() + depth_format.map_or(0, |_| 1);
69    state.reserve_textures(textures_needed);
70
71    // color textures
72    if color_formats.is_empty() {
73      state.ctx.draw_buffers(&WebGl2RenderingContext::NONE.into());
74    } else {
75      // Specify the list of color buffers to draw to; to do so, we need to generate a temporary
76      // list (Vec) of 32-bit integers and turn it into a Uint32Array to pass it across WASM
77      // boundary.
78      let color_buf_nb = color_formats.len() as u32;
79      let color_buffers: Vec<_> = (WebGl2RenderingContext::COLOR_ATTACHMENT0
80        ..WebGl2RenderingContext::COLOR_ATTACHMENT0 + color_buf_nb)
81        .collect();
82
83      let buffers = Uint32Array::view(&color_buffers);
84
85      state.ctx.draw_buffers(buffers.as_ref());
86    }
87
88    // depth texture
89    if depth_format.is_none() {
90      let renderbuffer = state
91        .ctx
92        .create_renderbuffer()
93        .ok_or_else(|| FramebufferError::cannot_create())?;
94
95      state
96        .ctx
97        .bind_renderbuffer(WebGl2RenderingContext::RENDERBUFFER, Some(&renderbuffer));
98
99      state.ctx.renderbuffer_storage(
100        WebGl2RenderingContext::RENDERBUFFER,
101        WebGl2RenderingContext::DEPTH_COMPONENT32F,
102        D::width(size) as i32,
103        D::height(size) as i32,
104      );
105      state.ctx.framebuffer_renderbuffer(
106        WebGl2RenderingContext::FRAMEBUFFER,
107        WebGl2RenderingContext::DEPTH_ATTACHMENT,
108        WebGl2RenderingContext::RENDERBUFFER,
109        Some(&renderbuffer),
110      );
111
112      depth_renderbuffer = Some(renderbuffer);
113    }
114
115    let framebuffer = Framebuffer {
116      handle: Some(handle),
117      renderbuffer: depth_renderbuffer,
118      size,
119      state: self.state.clone(),
120    };
121
122    Ok(framebuffer)
123  }
124
125  unsafe fn attach_color_texture(
126    framebuffer: &mut Self::FramebufferRepr,
127    texture: &Self::TextureRepr,
128    attachment_index: usize,
129  ) -> Result<(), FramebufferError> {
130    match texture.target {
131      WebGl2RenderingContext::TEXTURE_2D => {
132        let state = framebuffer.state.borrow();
133        state.ctx.framebuffer_texture_2d(
134          WebGl2RenderingContext::FRAMEBUFFER,
135          WebGl2RenderingContext::COLOR_ATTACHMENT0 + attachment_index as u32,
136          texture.target,
137          Some(&texture.handle),
138          0,
139        );
140
141        Ok(())
142      }
143
144      _ => Err(FramebufferError::unsupported_attachment()),
145    }
146  }
147
148  unsafe fn attach_depth_texture(
149    framebuffer: &mut Self::FramebufferRepr,
150    texture: &Self::TextureRepr,
151  ) -> Result<(), FramebufferError> {
152    match texture.target {
153      WebGl2RenderingContext::TEXTURE_2D => {
154        let state = framebuffer.state.borrow();
155        state.ctx.framebuffer_texture_2d(
156          WebGl2RenderingContext::FRAMEBUFFER,
157          WebGl2RenderingContext::DEPTH_ATTACHMENT,
158          texture.target,
159          Some(&texture.handle),
160          0,
161        );
162
163        Ok(())
164      }
165
166      _ => Err(FramebufferError::unsupported_attachment()),
167    }
168  }
169
170  unsafe fn validate_framebuffer(
171    framebuffer: Self::FramebufferRepr,
172  ) -> Result<Self::FramebufferRepr, FramebufferError> {
173    get_framebuffer_status(&mut framebuffer.state.borrow_mut())?;
174    Ok(framebuffer)
175  }
176
177  unsafe fn framebuffer_size(framebuffer: &Self::FramebufferRepr) -> D::Size {
178    framebuffer.size
179  }
180}
181
182fn get_framebuffer_status(state: &mut WebGL2State) -> Result<(), IncompleteReason> {
183  let status = state
184    .ctx
185    .check_framebuffer_status(WebGl2RenderingContext::FRAMEBUFFER);
186
187  match status {
188    WebGl2RenderingContext::FRAMEBUFFER_COMPLETE => Ok(()),
189    WebGl2RenderingContext::FRAMEBUFFER_INCOMPLETE_ATTACHMENT => {
190      Err(IncompleteReason::IncompleteAttachment)
191    }
192    WebGl2RenderingContext::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => {
193      Err(IncompleteReason::MissingAttachment)
194    }
195    WebGl2RenderingContext::FRAMEBUFFER_UNSUPPORTED => Err(IncompleteReason::Unsupported),
196    WebGl2RenderingContext::FRAMEBUFFER_INCOMPLETE_MULTISAMPLE => {
197      Err(IncompleteReason::IncompleteMultisample)
198    }
199    _ => panic!(
200      "unknown WebGL2 framebuffer incomplete status! status={}",
201      status
202    ),
203  }
204}
205
206unsafe impl FramebufferBackBuffer for WebGL2 {
207  unsafe fn back_buffer(
208    &mut self,
209    size: <Dim2 as Dimensionable>::Size,
210  ) -> Result<Self::FramebufferRepr, FramebufferError> {
211    Ok(Framebuffer {
212      handle: None, // None is the default framebuffer in WebGL
213      renderbuffer: None,
214      size,
215      state: self.state.clone(),
216    })
217  }
218}