1use crate::framebuffer::DrawCallStatistics;
22use crate::{
23 buffer::{Buffer, BufferKind},
24 core::{color::Color, math::Rect},
25 error::FrameworkError,
26 framebuffer::{
27 Attachment, AttachmentKind, BufferDataUsage, BufferLocation, FrameBuffer,
28 ResourceBindGroup, ResourceBinding, TextureShaderLocation,
29 },
30 geometry_buffer::GeometryBuffer,
31 gl::{
32 buffer::GlBuffer, geometry_buffer::GlGeometryBuffer, program::GlProgram,
33 server::GlGraphicsServer, texture::GlTexture, ToGlConstant,
34 },
35 gpu_program::GpuProgram,
36 gpu_texture::{CubeMapFace, GpuTexture, GpuTextureKind, PixelElementKind},
37 ColorMask, DrawParameters, ElementRange,
38};
39use glow::HasContext;
40use std::rc::Weak;
41
42pub struct GlFrameBuffer {
43 state: Weak<GlGraphicsServer>,
44 fbo: Option<glow::Framebuffer>,
45 depth_attachment: Option<Attachment>,
46 color_attachments: Vec<Attachment>,
47}
48
49unsafe fn set_attachment(server: &GlGraphicsServer, gl_attachment_kind: u32, texture: &GlTexture) {
50 match texture.kind() {
51 GpuTextureKind::Line { .. } => {
52 server.gl.framebuffer_texture(
53 glow::FRAMEBUFFER,
54 gl_attachment_kind,
55 Some(texture.id()),
56 0,
57 );
58 }
59 GpuTextureKind::Rectangle { .. } => {
60 server.gl.framebuffer_texture_2d(
61 glow::FRAMEBUFFER,
62 gl_attachment_kind,
63 glow::TEXTURE_2D,
64 Some(texture.id()),
65 0,
66 );
67 }
68 GpuTextureKind::Cube { .. } => {
69 server.gl.framebuffer_texture_2d(
70 glow::FRAMEBUFFER,
71 gl_attachment_kind,
72 glow::TEXTURE_CUBE_MAP_POSITIVE_X,
73 Some(texture.id()),
74 0,
75 );
76 }
77 GpuTextureKind::Volume { .. } => {
78 server.gl.framebuffer_texture_3d(
79 glow::FRAMEBUFFER,
80 gl_attachment_kind,
81 glow::TEXTURE_3D,
82 Some(texture.id()),
83 0,
84 0,
85 );
86 }
87 }
88}
89
90impl GlFrameBuffer {
91 pub fn new(
92 server: &GlGraphicsServer,
93 depth_attachment: Option<Attachment>,
94 color_attachments: Vec<Attachment>,
95 ) -> Result<Self, FrameworkError> {
96 unsafe {
97 let fbo = server.gl.create_framebuffer()?;
98
99 server.set_framebuffer(Some(fbo));
100
101 if let Some(depth_attachment) = depth_attachment.as_ref() {
102 let depth_attachment_kind = match depth_attachment.kind {
103 AttachmentKind::Color => {
104 panic!("Attempt to use color attachment as depth/stencil!")
105 }
106 AttachmentKind::DepthStencil => glow::DEPTH_STENCIL_ATTACHMENT,
107 AttachmentKind::Depth => glow::DEPTH_ATTACHMENT,
108 };
109 let guard = depth_attachment.texture.borrow();
110 let texture = guard.as_any().downcast_ref::<GlTexture>().unwrap();
111 set_attachment(server, depth_attachment_kind, texture);
112 }
113
114 let mut color_buffers = Vec::new();
115 for (i, color_attachment) in color_attachments.iter().enumerate() {
116 assert_eq!(color_attachment.kind, AttachmentKind::Color);
117 let color_attachment_kind = glow::COLOR_ATTACHMENT0 + i as u32;
118 let guard = color_attachment.texture.borrow();
119 let texture = guard.as_any().downcast_ref::<GlTexture>().unwrap();
120 set_attachment(server, color_attachment_kind, texture);
121 color_buffers.push(color_attachment_kind);
122 }
123
124 if color_buffers.is_empty() {
125 server.gl.draw_buffers(&[glow::NONE])
126 } else {
127 server.gl.draw_buffers(&color_buffers);
128 }
129
130 if server.gl.check_framebuffer_status(glow::FRAMEBUFFER) != glow::FRAMEBUFFER_COMPLETE {
131 return Err(FrameworkError::FailedToConstructFBO);
132 }
133
134 server.set_framebuffer(None);
135
136 Ok(Self {
137 state: server.weak(),
138 fbo: Some(fbo),
139 depth_attachment,
140 color_attachments,
141 })
142 }
143 }
144
145 pub fn backbuffer(server: &GlGraphicsServer) -> Self {
146 Self {
147 state: server.weak(),
148 fbo: None,
149 depth_attachment: None,
150 color_attachments: Default::default(),
151 }
152 }
153
154 pub fn id(&self) -> Option<glow::Framebuffer> {
156 self.fbo
157 }
158}
159
160impl FrameBuffer for GlFrameBuffer {
161 fn color_attachments(&self) -> &[Attachment] {
162 &self.color_attachments
163 }
164
165 fn depth_attachment(&self) -> Option<&Attachment> {
166 self.depth_attachment.as_ref()
167 }
168
169 fn set_cubemap_face(&mut self, attachment_index: usize, face: CubeMapFace) {
170 let server = self.state.upgrade().unwrap();
171
172 unsafe {
173 server.set_framebuffer(self.fbo);
174
175 let attachment = self.color_attachments.get(attachment_index).unwrap();
176 let guard = attachment.texture.borrow();
177 let texture = guard.as_any().downcast_ref::<GlTexture>().unwrap();
178 server.gl.framebuffer_texture_2d(
179 glow::FRAMEBUFFER,
180 glow::COLOR_ATTACHMENT0 + attachment_index as u32,
181 face.into_gl(),
182 Some(texture.id()),
183 0,
184 );
185 }
186 }
187
188 fn blit_to(
189 &self,
190 dest: &dyn FrameBuffer,
191 src_x0: i32,
192 src_y0: i32,
193 src_x1: i32,
194 src_y1: i32,
195 dst_x0: i32,
196 dst_y0: i32,
197 dst_x1: i32,
198 dst_y1: i32,
199 copy_color: bool,
200 copy_depth: bool,
201 copy_stencil: bool,
202 ) {
203 let server = self.state.upgrade().unwrap();
204
205 let source = self;
206 let dest = dest.as_any().downcast_ref::<GlFrameBuffer>().unwrap();
207
208 let mut mask = 0;
209 if copy_color {
210 mask |= glow::COLOR_BUFFER_BIT;
211 }
212 if copy_depth {
213 mask |= glow::DEPTH_BUFFER_BIT;
214 }
215 if copy_stencil {
216 mask |= glow::STENCIL_BUFFER_BIT;
217 }
218
219 unsafe {
220 server
221 .gl
222 .bind_framebuffer(glow::READ_FRAMEBUFFER, source.id());
223 server
224 .gl
225 .bind_framebuffer(glow::DRAW_FRAMEBUFFER, dest.id());
226 server.gl.blit_framebuffer(
227 src_x0,
228 src_y0,
229 src_x1,
230 src_y1,
231 dst_x0,
232 dst_y0,
233 dst_x1,
234 dst_y1,
235 mask,
236 glow::NEAREST,
237 );
238 }
239 }
240
241 fn clear(
242 &mut self,
243 viewport: Rect<i32>,
244 color: Option<Color>,
245 depth: Option<f32>,
246 stencil: Option<i32>,
247 ) {
248 let server = self.state.upgrade().unwrap();
249
250 server.set_scissor_test(false);
251 server.set_viewport(viewport);
252 server.set_framebuffer(self.id());
253
254 unsafe {
255 if self.fbo == Default::default() {
257 let mut mask = 0;
258
259 if let Some(color) = color {
260 server.set_color_write(ColorMask::default());
261 server.set_clear_color(color);
262 mask |= glow::COLOR_BUFFER_BIT;
263 }
264 if let Some(depth) = depth {
265 server.set_depth_write(true);
266 server.set_clear_depth(depth);
267 mask |= glow::DEPTH_BUFFER_BIT;
268 }
269 if let Some(stencil) = stencil {
270 server.set_stencil_mask(0xFFFF_FFFF);
271 server.set_clear_stencil(stencil);
272 mask |= glow::STENCIL_BUFFER_BIT;
273 }
274
275 server.gl.clear(mask);
276 }
277
278 if let Some(depth_stencil) = self.depth_attachment.as_ref() {
280 server.set_depth_write(true);
281 server.set_stencil_mask(0xFFFF_FFFF);
282
283 match depth_stencil.kind {
284 AttachmentKind::Color => unreachable!("depth cannot be color!"),
285 AttachmentKind::DepthStencil => match (depth, stencil) {
286 (Some(depth), Some(stencil)) => {
287 server.gl.clear_buffer_depth_stencil(
288 glow::DEPTH_STENCIL,
289 0,
290 depth,
291 stencil,
292 );
293 }
294 (Some(depth), None) => {
295 let values = [depth];
296 server.gl.clear_buffer_f32_slice(glow::DEPTH, 0, &values);
297 }
298 (None, Some(stencil)) => {
299 let values = [stencil];
300 server.gl.clear_buffer_i32_slice(glow::STENCIL, 0, &values);
301 }
302 (None, None) => {
303 }
305 },
306 AttachmentKind::Depth => {
307 if let Some(depth) = depth {
308 let values = [depth];
309 server.gl.clear_buffer_f32_slice(glow::DEPTH, 0, &values);
310 }
311 }
312 }
313 }
314
315 if let Some(color) = color {
316 server.set_color_write(ColorMask::default());
317
318 for (i, attachment) in self.color_attachments.iter().enumerate() {
319 match attachment.texture.borrow().pixel_kind().element_kind() {
320 PixelElementKind::Float | PixelElementKind::NormalizedUnsignedInteger => {
321 let fvalues = color.as_frgba();
322 server.gl.clear_buffer_f32_slice(
323 glow::COLOR,
324 i as u32,
325 &fvalues.data.0[0],
326 )
327 }
328 PixelElementKind::Integer => {
329 let values = [
330 color.r as i32,
331 color.g as i32,
332 color.b as i32,
333 color.a as i32,
334 ];
335 server
336 .gl
337 .clear_buffer_i32_slice(glow::COLOR, i as u32, &values);
338 }
339 PixelElementKind::UnsignedInteger => {
340 let values = [
341 color.r as u32,
342 color.g as u32,
343 color.b as u32,
344 color.a as u32,
345 ];
346 server
347 .gl
348 .clear_buffer_u32_slice(glow::COLOR, i as u32, &values);
349 }
350 }
351 }
352 }
353 }
354 }
355
356 fn draw(
357 &mut self,
358 geometry: &dyn GeometryBuffer,
359 viewport: Rect<i32>,
360 program: &dyn GpuProgram,
361 params: &DrawParameters,
362 resources: &[ResourceBindGroup],
363 element_range: ElementRange,
364 ) -> Result<DrawCallStatistics, FrameworkError> {
365 let server = self.state.upgrade().unwrap();
366 let geometry = geometry
367 .as_any()
368 .downcast_ref::<GlGeometryBuffer>()
369 .unwrap();
370
371 pre_draw(self.id(), &server, viewport, program, params, resources);
372
373 let (offset, count) = match element_range {
374 ElementRange::Full => (0, geometry.element_count.get()),
375 ElementRange::Specific { offset, count } => (offset, count),
376 };
377
378 let last_triangle_index = offset + count;
379
380 if last_triangle_index > geometry.element_count.get() {
381 Err(FrameworkError::InvalidElementRange {
382 start: offset,
383 end: last_triangle_index,
384 total: geometry.element_count.get(),
385 })
386 } else {
387 let index_per_element = geometry.element_kind.index_per_element();
388 let start_index = offset * index_per_element;
389 let index_count = count * index_per_element;
390
391 unsafe {
392 if index_count > 0 {
393 server.set_vertex_array_object(Some(geometry.vertex_array_object));
394
395 let indices = (start_index * size_of::<u32>()) as i32;
396 server.gl.draw_elements(
397 geometry.mode(),
398 index_count as i32,
399 glow::UNSIGNED_INT,
400 indices,
401 );
402 }
403 }
404
405 Ok(DrawCallStatistics { triangles: count })
406 }
407 }
408
409 fn draw_instances(
410 &mut self,
411 count: usize,
412 geometry: &dyn GeometryBuffer,
413 viewport: Rect<i32>,
414 program: &dyn GpuProgram,
415 params: &DrawParameters,
416 resources: &[ResourceBindGroup],
417 ) -> DrawCallStatistics {
418 let server = self.state.upgrade().unwrap();
419 let geometry = geometry
420 .as_any()
421 .downcast_ref::<GlGeometryBuffer>()
422 .unwrap();
423
424 pre_draw(self.id(), &server, viewport, program, params, resources);
425
426 let index_per_element = geometry.element_kind.index_per_element();
427 let index_count = geometry.element_count.get() * index_per_element;
428 if index_count > 0 {
429 unsafe {
430 server.set_vertex_array_object(Some(geometry.vertex_array_object));
431
432 server.gl.draw_elements_instanced(
433 geometry.mode(),
434 index_count as i32,
435 glow::UNSIGNED_INT,
436 0,
437 count as i32,
438 )
439 }
440 }
441 DrawCallStatistics {
442 triangles: geometry.element_count.get() * count,
443 }
444 }
445}
446
447fn pre_draw(
448 fbo: Option<glow::Framebuffer>,
449 server: &GlGraphicsServer,
450 viewport: Rect<i32>,
451 program: &dyn GpuProgram,
452 params: &DrawParameters,
453 resources: &[ResourceBindGroup],
454) {
455 server.set_framebuffer(fbo);
456 server.set_viewport(viewport);
457 server.apply_draw_parameters(params);
458 let program = program.as_any().downcast_ref::<GlProgram>().unwrap();
459 server.set_program(Some(program.id));
460
461 let mut texture_unit = 0;
462 let mut automatic_binding_index = 0;
463 for bind_group in resources {
464 for binding in bind_group.bindings {
465 match binding {
466 ResourceBinding::Texture {
467 texture,
468 shader_location,
469 } => {
470 let texture = texture.borrow();
471 let texture = texture.as_any().downcast_ref::<GlTexture>().unwrap();
472 match shader_location {
473 TextureShaderLocation::Uniform(uniform) => {
474 unsafe { server.gl.uniform_1_i32(Some(&uniform.id), texture_unit) };
475 texture.bind(server, texture_unit as u32);
476 texture_unit += 1;
477 }
478 TextureShaderLocation::ExplicitBinding(binding) => {
479 texture.bind(server, *binding as u32);
480 }
481 }
482 }
483 ResourceBinding::Buffer {
484 buffer,
485 binding: shader_location,
486 data_usage: data_location,
487 } => {
488 let gl_buffer = buffer
489 .as_any()
490 .downcast_ref::<GlBuffer>()
491 .expect("Must be OpenGL buffer");
492
493 unsafe {
494 let actual_binding = match shader_location {
495 BufferLocation::Auto { .. } => automatic_binding_index,
496 BufferLocation::Explicit { binding } => *binding,
497 };
498
499 match data_location {
500 BufferDataUsage::UseSegment { offset, size } => {
501 assert_ne!(*size, 0);
502 server.gl.bind_buffer_range(
503 gl_buffer.kind().into_gl(),
504 actual_binding as u32,
505 Some(gl_buffer.id),
506 *offset as i32,
507 *size as i32,
508 );
509 }
510 BufferDataUsage::UseEverything => {
511 server.gl.bind_buffer_base(
512 gl_buffer.kind().into_gl(),
513 actual_binding as u32,
514 Some(gl_buffer.id),
515 );
516 }
517 }
518
519 if let BufferLocation::Auto { shader_location } = shader_location {
520 match gl_buffer.kind() {
521 BufferKind::Uniform => server.gl.uniform_block_binding(
522 program.id,
523 *shader_location as u32,
524 automatic_binding_index as u32,
525 ),
526 BufferKind::Vertex
527 | BufferKind::Index
528 | BufferKind::PixelRead
529 | BufferKind::PixelWrite => {}
530 }
531
532 automatic_binding_index += 1;
533 }
534 }
535 }
536 }
537 }
538 }
539}
540
541impl Drop for GlFrameBuffer {
542 fn drop(&mut self) {
543 if let Some(state) = self.state.upgrade() {
544 unsafe {
545 if let Some(id) = self.fbo {
546 state.gl.delete_framebuffer(id);
547 }
548 }
549 }
550 }
551}