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