luminance_webgl/webgl2/
framebuffer.rs1use 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 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 let textures_needed = color_formats.len() + depth_format.map_or(0, |_| 1);
69 state.reserve_textures(textures_needed);
70
71 if color_formats.is_empty() {
73 state.ctx.draw_buffers(&WebGl2RenderingContext::NONE.into());
74 } else {
75 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 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, renderbuffer: None,
214 size,
215 state: self.state.clone(),
216 })
217 }
218}