From 1895039aa24909eea48d60bd217d7e55d8c17873 Mon Sep 17 00:00:00 2001 From: Dmitry Stepanov Date: Sat, 31 Aug 2024 18:41:50 +0300 Subject: [PATCH] pixel buffer for async framebuffer reads --- .../src/renderer/framework/gpu_texture.rs | 8 +- fyrox-impl/src/renderer/framework/mod.rs | 1 + .../src/renderer/framework/pixel_buffer.rs | 124 ++++++++++++++++++ 3 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 fyrox-impl/src/renderer/framework/pixel_buffer.rs diff --git a/fyrox-impl/src/renderer/framework/gpu_texture.rs b/fyrox-impl/src/renderer/framework/gpu_texture.rs index 3f37b1596..efaa24449 100644 --- a/fyrox-impl/src/renderer/framework/gpu_texture.rs +++ b/fyrox-impl/src/renderer/framework/gpu_texture.rs @@ -168,10 +168,10 @@ pub enum PixelElementKind { } pub struct PixelDescriptor { - data_type: u32, - format: u32, - internal_format: u32, - swizzle_mask: Option<[i32; 4]>, + pub data_type: u32, + pub format: u32, + pub internal_format: u32, + pub swizzle_mask: Option<[i32; 4]>, } impl PixelKind { diff --git a/fyrox-impl/src/renderer/framework/mod.rs b/fyrox-impl/src/renderer/framework/mod.rs index 694c3e473..ee2928eaf 100644 --- a/fyrox-impl/src/renderer/framework/mod.rs +++ b/fyrox-impl/src/renderer/framework/mod.rs @@ -25,5 +25,6 @@ pub mod framebuffer; pub mod geometry_buffer; pub mod gpu_program; pub mod gpu_texture; +pub mod pixel_buffer; pub mod query; pub mod state; diff --git a/fyrox-impl/src/renderer/framework/pixel_buffer.rs b/fyrox-impl/src/renderer/framework/pixel_buffer.rs new file mode 100644 index 000000000..e8d914227 --- /dev/null +++ b/fyrox-impl/src/renderer/framework/pixel_buffer.rs @@ -0,0 +1,124 @@ +// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +use crate::{ + core::{array_as_u8_slice_mut, math::Rect}, + renderer::framework::{ + error::FrameworkError, framebuffer::FrameBuffer, gpu_texture::GpuTextureKind, + state::PipelineState, + }, +}; +use bytemuck::Pod; +use glow::{HasContext, PixelPackData}; +use std::rc::Weak; + +pub struct PixelBuffer { + id: glow::NativeBuffer, + state: Weak, +} + +impl Drop for PixelBuffer { + fn drop(&mut self) { + if let Some(state) = self.state.upgrade() { + unsafe { + state.gl.delete_buffer(self.id); + } + } + } +} + +impl PixelBuffer { + pub fn new(state: &PipelineState) -> Result { + unsafe { + let id = state.gl.create_buffer()?; + Ok(Self { + id, + state: state.weak(), + }) + } + } + + pub fn schedule_pixels_transfer( + &self, + state: &PipelineState, + framebuffer: &FrameBuffer, + color_buffer_index: u32, + rect: Option>, + ) -> Result<(), FrameworkError> { + let color_attachment = &framebuffer + .color_attachments() + .get(color_buffer_index as usize) + .ok_or_else(|| { + FrameworkError::Custom(format!( + "Framebuffer {:?} does not have {} color attachment!", + framebuffer.id(), + color_buffer_index + )) + })? + .texture; + let color_attachment = color_attachment.borrow(); + let attachment_pixel_descriptor = color_attachment.pixel_kind().pixel_descriptor(); + let target_rect = match rect { + Some(rect) => rect, + None => { + if let GpuTextureKind::Rectangle { width, height } = color_attachment.kind() { + Rect::new(0, 0, width as i32, height as i32) + } else { + return Err(FrameworkError::Custom( + "Only rectangular textures can be read from GPU!".to_string(), + )); + } + } + }; + unsafe { + state.gl.bind_buffer(glow::PIXEL_PACK_BUFFER, Some(self.id)); + state + .gl + .bind_framebuffer(glow::READ_FRAMEBUFFER, framebuffer.id()); + state + .gl + .read_buffer(glow::COLOR_ATTACHMENT0 + color_buffer_index); + state.gl.read_pixels( + target_rect.position.x, + target_rect.position.y, + target_rect.size.x, + target_rect.size.y, + attachment_pixel_descriptor.format, + attachment_pixel_descriptor.data_type, + PixelPackData::BufferOffset(0), + ); + state.gl.bind_buffer(glow::PIXEL_PACK_BUFFER, None); + Ok(()) + } + } + + pub fn read(&self, state: &PipelineState, buffer: &mut [T]) + where + T: Pod, + { + unsafe { + state.gl.bind_buffer(glow::PIXEL_PACK_BUFFER, Some(self.id)); + state + .gl + .get_buffer_sub_data(glow::PIXEL_PACK_BUFFER, 0, array_as_u8_slice_mut(buffer)); + state.gl.bind_buffer(glow::PIXEL_PACK_BUFFER, None); + } + } +}