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