fyrox_graphics/gl/
read_buffer.rs1use crate::{
22 buffer::{Buffer, BufferKind, BufferUsage},
23 core::{algebra::Vector2, math::Rect},
24 error::FrameworkError,
25 framebuffer::FrameBuffer,
26 gl::{buffer::GlBuffer, framebuffer::GlFrameBuffer, server::GlGraphicsServer, ToGlConstant},
27 gpu_texture::{image_2d_size_bytes, GpuTextureKind},
28 read_buffer::AsyncReadBuffer,
29};
30use glow::{HasContext, PixelPackData};
31use std::rc::Weak;
32
33struct ReadRequest {
34 fence: glow::Fence,
35}
36
37pub struct GlAsyncReadBuffer {
38 server: Weak<GlGraphicsServer>,
39 buffer: GlBuffer,
40 request: Option<ReadRequest>,
41 pixel_count: usize,
42 pixel_size: usize,
43}
44
45impl GlAsyncReadBuffer {
46 pub fn new(
47 server: &GlGraphicsServer,
48 pixel_size: usize,
49 pixel_count: usize,
50 ) -> Result<Self, FrameworkError> {
51 let size_bytes = pixel_count * pixel_size;
52 let buffer = GlBuffer::new(
53 server,
54 size_bytes,
55 BufferKind::PixelRead,
56 BufferUsage::StreamRead,
57 )?;
58 Ok(Self {
59 server: server.weak(),
60 buffer,
61 request: None,
62 pixel_count,
63 pixel_size,
64 })
65 }
66}
67
68impl AsyncReadBuffer for GlAsyncReadBuffer {
69 fn schedule_pixels_transfer(
70 &mut self,
71 framebuffer: &dyn FrameBuffer,
72 color_buffer_index: u32,
73 rect: Option<Rect<i32>>,
74 ) -> Result<(), FrameworkError> {
75 if self.request.is_some() {
76 return Ok(());
77 }
78
79 let server = self.server.upgrade().unwrap();
80
81 let framebuffer = framebuffer
82 .as_any()
83 .downcast_ref::<GlFrameBuffer>()
84 .unwrap();
85
86 let color_attachment = &framebuffer
87 .color_attachments()
88 .get(color_buffer_index as usize)
89 .ok_or_else(|| {
90 FrameworkError::Custom(format!(
91 "Framebuffer {:?} does not have {} color attachment!",
92 framebuffer.id(),
93 color_buffer_index
94 ))
95 })?
96 .texture;
97
98 let color_attachment = color_attachment.borrow();
99 let attachment_pixel_descriptor = color_attachment.pixel_kind().pixel_descriptor();
100
101 let color_attachment_size =
102 if let GpuTextureKind::Rectangle { width, height } = color_attachment.kind() {
103 Vector2::new(width, height)
104 } else {
105 return Err(FrameworkError::Custom(
106 "Only rectangular textures can be read from GPU!".to_string(),
107 ));
108 };
109
110 let actual_size = image_2d_size_bytes(
111 color_attachment.pixel_kind(),
112 color_attachment_size.x,
113 color_attachment_size.y,
114 );
115 let self_bytes_count = self.pixel_count * self.pixel_size;
116 if actual_size != self_bytes_count {
117 return Err(FrameworkError::Custom(format!(
118 "Pixel buffer size {} does not match the size {} of the color \
119 attachment {} of the frame buffer {:?}",
120 self_bytes_count,
121 actual_size,
122 color_buffer_index,
123 framebuffer.id()
124 )));
125 }
126
127 let target_rect = match rect {
128 Some(rect) => rect,
129 None => Rect::new(
130 0,
131 0,
132 color_attachment_size.x as i32,
133 color_attachment_size.y as i32,
134 ),
135 };
136
137 unsafe {
138 let buffer_gl_usage = self.buffer.kind.into_gl();
139
140 server.gl.bind_buffer(buffer_gl_usage, Some(self.buffer.id));
141
142 server
143 .gl
144 .bind_framebuffer(glow::READ_FRAMEBUFFER, framebuffer.id());
145
146 server
147 .gl
148 .read_buffer(glow::COLOR_ATTACHMENT0 + color_buffer_index);
149
150 server.gl.read_pixels(
151 target_rect.position.x,
152 target_rect.position.y,
153 target_rect.size.x,
154 target_rect.size.y,
155 attachment_pixel_descriptor.format,
156 attachment_pixel_descriptor.data_type,
157 PixelPackData::BufferOffset(0),
158 );
159
160 server.gl.bind_buffer(buffer_gl_usage, None);
161
162 self.request = Some(ReadRequest {
163 fence: server
164 .gl
165 .fence_sync(glow::SYNC_GPU_COMMANDS_COMPLETE, 0)
166 .unwrap(),
167 });
168
169 Ok(())
170 }
171 }
172
173 fn is_request_running(&self) -> bool {
174 self.request.is_some()
175 }
176
177 fn try_read(&mut self) -> Option<Vec<u8>> {
178 let server = self.server.upgrade()?;
179
180 let request = self.request.as_ref()?;
181
182 let mut buffer = vec![0; self.pixel_count * self.pixel_size];
183
184 unsafe {
185 let fence_state = server.gl.client_wait_sync(request.fence, 0, 0);
188 if fence_state != glow::TIMEOUT_EXPIRED && fence_state != glow::WAIT_FAILED {
189 self.buffer.read_data(&mut buffer).unwrap();
190
191 server.gl.delete_sync(request.fence);
192 self.request = None;
193
194 Some(buffer)
195 } else {
196 None
197 }
198 }
199 }
200}