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