diff --git a/fyrox-graphics-gl/src/buffer.rs b/fyrox-graphics-gl/src/buffer.rs index 1bf90e69f..a16926bcd 100644 --- a/fyrox-graphics-gl/src/buffer.rs +++ b/fyrox-graphics-gl/src/buffer.rs @@ -75,6 +75,7 @@ impl GlBuffer { kind, usage, } = desc; + server.memory_usage.borrow_mut().buffers += size; unsafe { let gl_kind = kind.into_gl(); let gl_usage = usage.into_gl(); @@ -102,6 +103,7 @@ impl Drop for GlBuffer { fn drop(&mut self) { unsafe { if let Some(state) = self.state.upgrade() { + state.memory_usage.borrow_mut().buffers -= self.size.get(); state.gl.delete_buffer(self.id); } } diff --git a/fyrox-graphics-gl/src/server.rs b/fyrox-graphics-gl/src/server.rs index 3cef64e4b..da95a8b6a 100644 --- a/fyrox-graphics-gl/src/server.rs +++ b/fyrox-graphics-gl/src/server.rs @@ -29,6 +29,7 @@ use crate::{ texture::GlTexture, ToGlConstant, }; +use fyrox_graphics::server::ServerMemoryUsage; use fyrox_graphics::{ buffer::{GpuBuffer, GpuBufferDescriptor}, core::{color::Color, log::Log, math::Rect}, @@ -267,6 +268,7 @@ impl InnerState { pub struct GlGraphicsServer { pub gl: glow::Context, pub(crate) state: RefCell, + pub(crate) memory_usage: RefCell, this: RefCell>>, } @@ -578,6 +580,7 @@ impl GlGraphicsServer { #[cfg(not(target_arch = "wasm32"))] gl_surface, )), + memory_usage: Default::default(), this: Default::default(), }; @@ -1196,4 +1199,8 @@ impl GraphicsServer for GlGraphicsServer { } } } + + fn memory_usage(&self) -> ServerMemoryUsage { + self.memory_usage.borrow().clone() + } } diff --git a/fyrox-graphics-gl/src/texture.rs b/fyrox-graphics-gl/src/texture.rs index 7b8b96082..6c29a7d18 100644 --- a/fyrox-graphics-gl/src/texture.rs +++ b/fyrox-graphics-gl/src/texture.rs @@ -62,6 +62,7 @@ pub struct GlTexture { texture: glow::Texture, kind: Cell, pixel_kind: Cell, + size_bytes: Cell, // Force compiler to not implement Send and Sync, because OpenGL is not thread-safe. thread_mark: PhantomData<*const u8>, } @@ -281,10 +282,14 @@ impl GlTexture { texture, kind: desc.kind.into(), pixel_kind: desc.pixel_kind.into(), + size_bytes: Cell::new(0), thread_mark: PhantomData, }; - result.set_data(desc.kind, desc.pixel_kind, desc.mip_count, desc.data)?; + let byte_count = + result.set_data(desc.kind, desc.pixel_kind, desc.mip_count, desc.data)?; + + server.memory_usage.borrow_mut().textures += byte_count; let mut binding = result.make_temp_binding(); if server.gl.supports_debug() { @@ -332,12 +337,63 @@ impl Drop for GlTexture { fn drop(&mut self) { if let Some(state) = self.state.upgrade() { unsafe { + state.memory_usage.borrow_mut().textures -= self.size_bytes.get(); state.gl.delete_texture(self.texture); } } } } +fn desired_byte_count(kind: GpuTextureKind, mip_count: usize, pixel_kind: PixelKind) -> usize { + let mut desired_byte_count = 0; + + 'mip_loop: for mip in 0..mip_count { + match kind { + GpuTextureKind::Line { length } => { + if let Some(length) = length.checked_shr(mip as u32) { + desired_byte_count += image_1d_size_bytes(pixel_kind, length); + } else { + break 'mip_loop; + } + } + GpuTextureKind::Rectangle { width, height } => { + if let (Some(width), Some(height)) = ( + width.checked_shr(mip as u32), + height.checked_shr(mip as u32), + ) { + desired_byte_count += image_2d_size_bytes(pixel_kind, width, height); + } else { + break 'mip_loop; + } + } + GpuTextureKind::Cube { size } => { + if let Some(size) = size.checked_shr(mip as u32) { + desired_byte_count += 6 * image_2d_size_bytes(pixel_kind, size, size); + } else { + break 'mip_loop; + } + } + GpuTextureKind::Volume { + width, + height, + depth, + } => { + if let (Some(width), Some(height), Some(depth)) = ( + width.checked_shr(mip as u32), + height.checked_shr(mip as u32), + depth.checked_shr(mip as u32), + ) { + desired_byte_count += image_3d_size_bytes(pixel_kind, width, height, depth); + } else { + break 'mip_loop; + } + } + }; + } + + desired_byte_count +} + impl GpuTextureTrait for GlTexture { fn set_data( &self, @@ -345,54 +401,10 @@ impl GpuTextureTrait for GlTexture { pixel_kind: PixelKind, mip_count: usize, data: Option<&[u8]>, - ) -> Result<(), FrameworkError> { + ) -> Result { let mip_count = mip_count.max(1); - let mut desired_byte_count = 0; - - 'mip_loop: for mip in 0..mip_count { - match kind { - GpuTextureKind::Line { length } => { - if let Some(length) = length.checked_shr(mip as u32) { - desired_byte_count += image_1d_size_bytes(pixel_kind, length); - } else { - break 'mip_loop; - } - } - GpuTextureKind::Rectangle { width, height } => { - if let (Some(width), Some(height)) = ( - width.checked_shr(mip as u32), - height.checked_shr(mip as u32), - ) { - desired_byte_count += image_2d_size_bytes(pixel_kind, width, height); - } else { - break 'mip_loop; - } - } - GpuTextureKind::Cube { size } => { - if let Some(size) = size.checked_shr(mip as u32) { - desired_byte_count += 6 * image_2d_size_bytes(pixel_kind, size, size); - } else { - break 'mip_loop; - } - } - GpuTextureKind::Volume { - width, - height, - depth, - } => { - if let (Some(width), Some(height), Some(depth)) = ( - width.checked_shr(mip as u32), - height.checked_shr(mip as u32), - depth.checked_shr(mip as u32), - ) { - desired_byte_count += image_3d_size_bytes(pixel_kind, width, height, depth); - } else { - break 'mip_loop; - } - } - }; - } + let desired_byte_count = desired_byte_count(kind, mip_count, pixel_kind); if let Some(data) = data { let actual_data_size = data.len(); @@ -404,6 +416,8 @@ impl GpuTextureTrait for GlTexture { } } + self.size_bytes.set(desired_byte_count); + self.kind.set(kind); self.pixel_kind.set(pixel_kind); @@ -616,7 +630,7 @@ impl GpuTextureTrait for GlTexture { } } - Ok(()) + Ok(desired_byte_count) } fn kind(&self) -> GpuTextureKind { diff --git a/fyrox-graphics/src/gpu_texture.rs b/fyrox-graphics/src/gpu_texture.rs index 262a106ef..836341c67 100644 --- a/fyrox-graphics/src/gpu_texture.rs +++ b/fyrox-graphics/src/gpu_texture.rs @@ -497,14 +497,15 @@ define_as_any_trait!(GpuTextureAsAny => GpuTextureTrait); /// ``` pub trait GpuTextureTrait: GpuTextureAsAny { /// Sets the new data of the texture. This method is also able to change the kind of the texture - /// and its pixel kind. + /// and its pixel kind. Returns the number of bytes occupied by the texture in the video memory. + /// If there's no data specified, the texture will still take the space defined by its metrics. fn set_data( &self, kind: GpuTextureKind, pixel_kind: PixelKind, mip_count: usize, data: Option<&[u8]>, - ) -> Result<(), FrameworkError>; + ) -> Result; /// Returns kind of the texture. fn kind(&self) -> GpuTextureKind; diff --git a/fyrox-graphics/src/server.rs b/fyrox-graphics/src/server.rs index 91a568e65..33e429cf2 100644 --- a/fyrox-graphics/src/server.rs +++ b/fyrox-graphics/src/server.rs @@ -51,6 +51,18 @@ pub struct ServerCapabilities { pub max_lod_bias: f32, } +/// Contains information about used memory per each category of GPU resource. This is not precise +/// data; it only calculates total requested memory by the user of a graphics server and does not +/// include additional memory overhead in the video driver. Yet this information could still be +/// useful. +#[derive(Default, Debug, Clone)] +pub struct ServerMemoryUsage { + /// Total number of bytes used by all textures (including render targets). + pub textures: usize, + /// Total number of bytes used by all buffers (vertex, index, uniform, etc.) + pub buffers: usize, +} + /// A shared reference to a graphics server. pub type SharedGraphicsServer = Rc; @@ -168,6 +180,9 @@ pub trait GraphicsServer: GraphicsServerAsAny { /// way of mipmaps generation, depending on the underlying GAPI capabilities. fn generate_mipmap(&self, texture: &GpuTexture); + /// Fetches the total amount of memory used by the graphics server. + fn memory_usage(&self) -> ServerMemoryUsage; + /// A shortcut for [`Self::create_texture`], that creates a rectangular texture with the given /// size and pixel kind. fn create_2d_render_target(