From 7259ec2418a5ee6e9edc899ff2edad9395f1d442 Mon Sep 17 00:00:00 2001 From: Dmitry Stepanov Date: Sun, 6 Oct 2024 20:13:37 +0300 Subject: [PATCH] renamed PixelBuffer to AsyncReadBuffer + isolated gl-specific code --- fyrox-graphics/src/gl/mod.rs | 1 + fyrox-graphics/src/gl/program.rs | 16 ++++++ .../{pixel_buffer.rs => gl/read_buffer.rs} | 57 +++++++++---------- fyrox-graphics/src/gl/server.rs | 14 +++++ fyrox-graphics/src/gpu_program.rs | 15 ----- fyrox-graphics/src/lib.rs | 2 +- fyrox-graphics/src/read_buffer.rs | 51 +++++++++++++++++ fyrox-graphics/src/server.rs | 8 ++- fyrox-impl/src/renderer/gbuffer/mod.rs | 3 +- fyrox-impl/src/renderer/occlusion/mod.rs | 6 +- .../src/renderer/occlusion/optimizer.rs | 23 ++++---- 11 files changed, 132 insertions(+), 64 deletions(-) rename fyrox-graphics/src/{pixel_buffer.rs => gl/read_buffer.rs} (84%) create mode 100644 fyrox-graphics/src/read_buffer.rs diff --git a/fyrox-graphics/src/gl/mod.rs b/fyrox-graphics/src/gl/mod.rs index e0be1176a..3590b692a 100644 --- a/fyrox-graphics/src/gl/mod.rs +++ b/fyrox-graphics/src/gl/mod.rs @@ -22,6 +22,7 @@ pub mod buffer; pub mod framebuffer; pub mod program; pub mod query; +pub mod read_buffer; pub mod server; pub mod texture; diff --git a/fyrox-graphics/src/gl/program.rs b/fyrox-graphics/src/gl/program.rs index b1e01bf30..8ee877b04 100644 --- a/fyrox-graphics/src/gl/program.rs +++ b/fyrox-graphics/src/gl/program.rs @@ -18,6 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +use crate::gpu_program::SamplerKind; use crate::{ core::{ algebra::{Matrix2, Matrix3, Matrix4, Vector2, Vector3, Vector4}, @@ -42,6 +43,21 @@ use std::{ rc::{Rc, Weak}, }; +impl SamplerKind { + pub fn glsl_name(&self) -> &str { + match self { + SamplerKind::Sampler1D => "sampler1D", + SamplerKind::Sampler2D => "sampler2D", + SamplerKind::Sampler3D => "sampler3D", + SamplerKind::SamplerCube => "samplerCube", + SamplerKind::USampler1D => "usampler1D", + SamplerKind::USampler2D => "usampler2D", + SamplerKind::USampler3D => "usampler3D", + SamplerKind::USamplerCube => "usamplerCube", + } + } +} + unsafe fn create_shader( server: &GlGraphicsServer, name: String, diff --git a/fyrox-graphics/src/pixel_buffer.rs b/fyrox-graphics/src/gl/read_buffer.rs similarity index 84% rename from fyrox-graphics/src/pixel_buffer.rs rename to fyrox-graphics/src/gl/read_buffer.rs index 09a3b4411..1ea35c55c 100644 --- a/fyrox-graphics/src/pixel_buffer.rs +++ b/fyrox-graphics/src/gl/read_buffer.rs @@ -18,35 +18,37 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::gl::framebuffer::GlFrameBuffer; -use crate::gl::server::GlGraphicsServer; -use crate::gl::ToGlConstant; use crate::{ buffer::{Buffer, BufferKind, BufferUsage}, core::{algebra::Vector2, math::Rect}, error::FrameworkError, framebuffer::FrameBuffer, - gl::buffer::GlBuffer, + gl::{buffer::GlBuffer, framebuffer::GlFrameBuffer, server::GlGraphicsServer, ToGlConstant}, gpu_texture::{image_2d_size_bytes, GpuTextureKind}, + read_buffer::AsyncReadBuffer, }; -use bytemuck::Pod; use glow::{HasContext, PixelPackData}; -use std::marker::PhantomData; +use std::rc::Weak; struct ReadRequest { fence: glow::Fence, } -pub struct PixelBuffer { +pub struct GlAsyncReadBuffer { + server: Weak, buffer: GlBuffer, request: Option, pixel_count: usize, - phantom_data: PhantomData, + pixel_size: usize, } -impl PixelBuffer { - pub fn new(server: &GlGraphicsServer, pixel_count: usize) -> Result { - let size_bytes = pixel_count * size_of::(); +impl GlAsyncReadBuffer { + pub fn new( + server: &GlGraphicsServer, + pixel_size: usize, + pixel_count: usize, + ) -> Result { + let size_bytes = pixel_count * pixel_size; let buffer = GlBuffer::new( server, size_bytes, @@ -54,16 +56,18 @@ impl PixelBuffer { BufferUsage::StreamRead, )?; Ok(Self { + server: server.weak(), buffer, request: None, pixel_count, - phantom_data: Default::default(), + pixel_size, }) } +} - pub fn schedule_pixels_transfer( +impl AsyncReadBuffer for GlAsyncReadBuffer { + fn schedule_pixels_transfer( &mut self, - server: &GlGraphicsServer, framebuffer: &dyn FrameBuffer, color_buffer_index: u32, rect: Option>, @@ -72,6 +76,8 @@ impl PixelBuffer { return Ok(()); } + let server = self.server.upgrade().unwrap(); + let framebuffer = framebuffer .as_any() .downcast_ref::() @@ -106,7 +112,7 @@ impl PixelBuffer { color_attachment_size.x, color_attachment_size.y, ); - let self_bytes_count = self.pixel_count * size_of::(); + let self_bytes_count = self.pixel_count * self.pixel_size; if actual_size != self_bytes_count { return Err(FrameworkError::Custom(format!( "Pixel buffer size {} does not match the size {} of the color \ @@ -164,24 +170,23 @@ impl PixelBuffer { } } - pub fn is_request_running(&self) -> bool { + fn is_request_running(&self) -> bool { self.request.is_some() } - pub fn try_read(&mut self, server: &GlGraphicsServer) -> Option> - where - T: Pod + Default + Copy, - { + fn try_read(&mut self) -> Option> { + let server = self.server.upgrade()?; + let request = self.request.as_ref()?; - let mut buffer = vec![T::default(); self.pixel_count]; + let mut buffer = vec![0; self.pixel_count * self.pixel_size]; unsafe { // For some reason, glGetSynciv still blocks execution and produces GPU stall, ruining // the performance. glClientWaitSync with timeout=0 does not have this issue. let fence_state = server.gl.client_wait_sync(request.fence, 0, 0); if fence_state != glow::TIMEOUT_EXPIRED && fence_state != glow::WAIT_FAILED { - self.read_internal(&mut buffer); + self.buffer.read_data(&mut buffer).unwrap(); server.gl.delete_sync(request.fence); self.request = None; @@ -192,12 +197,4 @@ impl PixelBuffer { } } } - - fn read_internal(&self, buffer: &mut [T]) - where - T: Pod, - { - let gl_buffer = &self.buffer as &dyn Buffer; - gl_buffer.read_data_of_type(buffer).unwrap() - } } diff --git a/fyrox-graphics/src/gl/server.rs b/fyrox-graphics/src/gl/server.rs index 5e8ef87c6..239ade3e0 100644 --- a/fyrox-graphics/src/gl/server.rs +++ b/fyrox-graphics/src/gl/server.rs @@ -19,8 +19,10 @@ // SOFTWARE. use crate::gl::program::GlProgram; +use crate::gl::read_buffer::GlAsyncReadBuffer; use crate::gl::ToGlConstant; use crate::gpu_program::{GpuProgram, PropertyDefinition}; +use crate::read_buffer::AsyncReadBuffer; use crate::server::{GraphicsServer, ServerCapabilities}; use crate::{ buffer::{Buffer, BufferKind, BufferUsage}, @@ -1131,6 +1133,18 @@ impl GraphicsServer for GlGraphicsServer { )?)) } + fn create_async_read_buffer( + &self, + pixel_size: usize, + pixel_count: usize, + ) -> Result, FrameworkError> { + Ok(Box::new(GlAsyncReadBuffer::new( + self, + pixel_size, + pixel_count, + )?)) + } + fn as_any(&self) -> &dyn Any { self } diff --git a/fyrox-graphics/src/gpu_program.rs b/fyrox-graphics/src/gpu_program.rs index 0029180ed..8dc9d5c1a 100644 --- a/fyrox-graphics/src/gpu_program.rs +++ b/fyrox-graphics/src/gpu_program.rs @@ -133,21 +133,6 @@ pub enum SamplerKind { USamplerCube, } -impl SamplerKind { - pub fn glsl_name(&self) -> &str { - match self { - SamplerKind::Sampler1D => "sampler1D", - SamplerKind::Sampler2D => "sampler2D", - SamplerKind::Sampler3D => "sampler3D", - SamplerKind::SamplerCube => "samplerCube", - SamplerKind::USampler1D => "usampler1D", - SamplerKind::USampler2D => "usampler2D", - SamplerKind::USampler3D => "usampler3D", - SamplerKind::USamplerCube => "usamplerCube", - } - } -} - /// Shader property with default value. #[derive(Serialize, Deserialize, Debug, PartialEq, Reflect, Visit)] pub enum PropertyKind { diff --git a/fyrox-graphics/src/lib.rs b/fyrox-graphics/src/lib.rs index 7cf4593bf..98fcfc14c 100644 --- a/fyrox-graphics/src/lib.rs +++ b/fyrox-graphics/src/lib.rs @@ -34,8 +34,8 @@ pub mod geometry_buffer; pub mod gl; pub mod gpu_program; pub mod gpu_texture; -pub mod pixel_buffer; pub mod query; +pub mod read_buffer; pub mod server; pub mod stats; pub mod uniform; diff --git a/fyrox-graphics/src/read_buffer.rs b/fyrox-graphics/src/read_buffer.rs new file mode 100644 index 000000000..216db225b --- /dev/null +++ b/fyrox-graphics/src/read_buffer.rs @@ -0,0 +1,51 @@ +// 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::math::Rect, error::FrameworkError, framebuffer::FrameBuffer}; +use bytemuck::Pod; + +pub trait AsyncReadBuffer { + fn schedule_pixels_transfer( + &mut self, + framebuffer: &dyn FrameBuffer, + color_buffer_index: u32, + rect: Option>, + ) -> Result<(), FrameworkError>; + fn is_request_running(&self) -> bool; + fn try_read(&mut self) -> Option>; +} + +impl dyn AsyncReadBuffer { + pub fn try_read_of_type(&mut self) -> Option> + where + T: Pod, + { + let mut bytes = self.try_read()?; + let typed = unsafe { + Some(Vec::from_raw_parts( + bytes.as_mut_ptr() as *mut T, + bytes.len() / size_of::(), + bytes.capacity() / size_of::(), + )) + }; + std::mem::forget(bytes); + typed + } +} diff --git a/fyrox-graphics/src/server.rs b/fyrox-graphics/src/server.rs index 60251cf67..75779fd4a 100644 --- a/fyrox-graphics/src/server.rs +++ b/fyrox-graphics/src/server.rs @@ -18,13 +18,14 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::gpu_program::{GpuProgram, PropertyDefinition}; use crate::{ buffer::{Buffer, BufferKind, BufferUsage}, error::FrameworkError, framebuffer::{Attachment, FrameBuffer}, + gpu_program::{GpuProgram, PropertyDefinition}, gpu_texture::{GpuTexture, GpuTextureKind, MagnificationFilter, MinificationFilter, PixelKind}, query::Query, + read_buffer::AsyncReadBuffer, stats::PipelineStatistics, }; use std::{ @@ -91,6 +92,11 @@ pub trait GraphicsServer: Any { fragment_source: &str, properties: &[PropertyDefinition], ) -> Result, FrameworkError>; + fn create_async_read_buffer( + &self, + pixel_size: usize, + pixel_count: usize, + ) -> Result, FrameworkError>; fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; fn weak(self: Rc) -> Weak; diff --git a/fyrox-impl/src/renderer/gbuffer/mod.rs b/fyrox-impl/src/renderer/gbuffer/mod.rs index 07d05da94..180e7ff68 100644 --- a/fyrox-impl/src/renderer/gbuffer/mod.rs +++ b/fyrox-impl/src/renderer/gbuffer/mod.rs @@ -317,8 +317,7 @@ impl GBuffer { let view_projection = camera.view_projection_matrix(); if quality_settings.use_occlusion_culling { - self.occlusion_tester - .try_query_visibility_results(server, graph); + self.occlusion_tester.try_query_visibility_results(graph); }; let viewport = Rect::new(0, 0, self.width, self.height); diff --git a/fyrox-impl/src/renderer/occlusion/mod.rs b/fyrox-impl/src/renderer/occlusion/mod.rs index 7287dc702..c69d1ce48 100644 --- a/fyrox-impl/src/renderer/occlusion/mod.rs +++ b/fyrox-impl/src/renderer/occlusion/mod.rs @@ -253,10 +253,8 @@ impl OcclusionTester { }) } - pub fn try_query_visibility_results(&mut self, server: &GlGraphicsServer, graph: &Graph) { - let Some(visibility_buffer) = self - .visibility_buffer_optimizer - .read_visibility_mask(server) + pub fn try_query_visibility_results(&mut self, graph: &Graph) { + let Some(visibility_buffer) = self.visibility_buffer_optimizer.read_visibility_mask() else { return; }; diff --git a/fyrox-impl/src/renderer/occlusion/optimizer.rs b/fyrox-impl/src/renderer/occlusion/optimizer.rs index 549cf25ac..560581ac1 100644 --- a/fyrox-impl/src/renderer/occlusion/optimizer.rs +++ b/fyrox-impl/src/renderer/occlusion/optimizer.rs @@ -18,28 +18,29 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -use crate::renderer::cache::uniform::UniformBufferCache; use crate::{ core::{color::Color, math::Rect, ImmutableString}, renderer::{ + cache::uniform::UniformBufferCache, framework::{ error::FrameworkError, - framebuffer::{Attachment, AttachmentKind, FrameBuffer}, + framebuffer::{ + Attachment, AttachmentKind, FrameBuffer, ResourceBindGroup, ResourceBinding, + }, geometry_buffer::GeometryBuffer, gl::server::GlGraphicsServer, gpu_program::{GpuProgram, UniformLocation}, gpu_texture::{ GpuTexture, GpuTextureKind, MagnificationFilter, MinificationFilter, PixelKind, }, - pixel_buffer::PixelBuffer, + read_buffer::AsyncReadBuffer, + server::GraphicsServer, + uniform::StaticUniformBuffer, ColorMask, DrawParameters, ElementRange, }, make_viewport_matrix, }, }; -use fyrox_graphics::framebuffer::{ResourceBindGroup, ResourceBinding}; -use fyrox_graphics::server::GraphicsServer; -use fyrox_graphics::uniform::StaticUniformBuffer; use std::{cell::RefCell, rc::Rc}; struct VisibilityOptimizerShader { @@ -66,7 +67,7 @@ impl VisibilityOptimizerShader { pub struct VisibilityBufferOptimizer { framebuffer: Box, - pixel_buffer: PixelBuffer, + pixel_buffer: Box, shader: VisibilityOptimizerShader, w_tiles: usize, h_tiles: usize, @@ -98,7 +99,7 @@ impl VisibilityBufferOptimizer { texture: optimized_visibility_buffer, }], )?, - pixel_buffer: PixelBuffer::new(server, w_tiles * h_tiles)?, + pixel_buffer: server.create_async_read_buffer(size_of::(), w_tiles * h_tiles)?, shader: VisibilityOptimizerShader::new(server)?, w_tiles, h_tiles, @@ -109,8 +110,8 @@ impl VisibilityBufferOptimizer { self.pixel_buffer.is_request_running() } - pub fn read_visibility_mask(&mut self, server: &GlGraphicsServer) -> Option> { - self.pixel_buffer.try_read(server) + pub fn read_visibility_mask(&mut self) -> Option> { + self.pixel_buffer.try_read_of_type() } pub fn optimize( @@ -164,7 +165,7 @@ impl VisibilityBufferOptimizer { )?; self.pixel_buffer - .schedule_pixels_transfer(server, &*self.framebuffer, 0, None)?; + .schedule_pixels_transfer(&*self.framebuffer, 0, None)?; Ok(()) }