synvael/crates/renderer/src/stats.rs

161 lines
7.4 KiB
Rust

// SPDX-License-Identifier: AGPL-3.0-only
//! Snapshot types describing what the renderer submitted and what device it submitted to.
use crate::renderer::RenderMode;
/// The projection parameters used to build this frame's perspective matrix.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ProjectionInfo {
/// Vertical field of view, in radians.
pub fov_y_radians: f32,
/// Width-to-height ratio of the render target, derived from the swapchain extent.
pub aspect: f32,
/// Distance to the near clip plane, in blocks.
pub near: f32,
/// Distance to the far clip plane, in blocks.
pub far: f32,
}
/// Description of the swapchain currently backing presentation.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct SwapchainInfo {
/// Number of images the swapchain was created with.
pub image_count: u32,
/// Width of the swapchain images, in pixels.
pub width: u32,
/// Height of the swapchain images, in pixels.
pub height: u32,
/// Presentation mode the swapchain was created with, rendered as its Vulkan enum name.
pub present_mode: &'static str,
}
/// What the renderer submitted for one frame, plus the cumulative frame counters.
///
/// Populated at the end of every successful [`Renderer::draw_frame`](crate::Renderer::draw_frame) and retained until the next frame replaces it, so a reader running on a slower cadence than the render loop always observes a complete, self-consistent frame rather than a partially-updated one.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct RenderStats {
/// Total chunk meshes currently uploaded to the GPU, visible or not.
pub uploaded_meshes: usize,
/// Meshes that survived frustum culling and were submitted this frame.
pub visible_meshes: usize,
/// Meshes rejected by frustum culling this frame.
pub culled_meshes: usize,
/// Indexed draw calls recorded this frame: one per visible mesh per raster pass.
pub draw_calls: usize,
/// Triangles submitted this frame, counted across every pass.
pub triangles: u64,
/// Vertices referenced by the submitted meshes, counted across every pass.
pub vertices: u64,
/// Bytes of vertex buffer held by every uploaded mesh, visible or not.
pub vertex_bytes: u64,
/// Bytes of index buffer held by every uploaded mesh, visible or not.
pub index_bytes: u64,
/// The render mode in force this frame, which determines the pass list and therefore the draw-call multiplier.
pub render_mode: RenderMode,
/// Projection parameters used to build this frame's matrix.
pub projection: ProjectionInfo,
/// The swapchain backing presentation at the end of this frame.
pub swapchain: SwapchainInfo,
/// Frames presented since renderer initialisation.
pub frames_presented: u64,
/// Frames abandoned before submission because the swapchain reported itself out of date, typically during a window resize.
pub frames_skipped: u64,
}
impl RenderStats {
/// Returns the fraction of uploaded meshes rejected by frustum culling this frame, in percent.
///
/// Returns zero when nothing was uploaded, since no meshes means no meshes were culled rather than an undefined ratio.
#[must_use]
pub fn cull_ratio_percent(&self) -> f32 {
let considered = self.visible_meshes + self.culled_meshes;
if considered == 0 {
return 0.0;
}
// Mesh counts are bounded by the resident chunk set (thousands), far inside f32's exact-integer range.
#[expect(
clippy::cast_precision_loss,
reason = "mesh counts stay well within f32's exact-integer range"
)]
{
self.culled_meshes as f32 / considered as f32 * 100.0
}
}
}
/// Immutable description of the physical device the renderer selected.
///
/// Queried once at initialisation: every field is a property of the device or driver and cannot change for the lifetime of the renderer. Live memory figures are not part of this and are read separately through [`MemoryUsage`].
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GpuInfo {
/// Marketing name the driver reports for the device.
pub device_name: String,
/// Device class: discrete, integrated, virtual, CPU, or other.
pub device_type: &'static str,
/// PCI vendor identifier, as reported by the driver.
pub vendor_id: u32,
/// Vendor-assigned device identifier.
pub device_id: u32,
/// Driver version, decoded with the vendor's own packing scheme where it differs from the Vulkan convention.
pub driver_version: String,
/// Vulkan API version the device supports, as `major.minor.patch`.
pub api_version: String,
/// Total capacity of every heap flagged `DEVICE_LOCAL`, in bytes. This is dedicated video memory on a discrete device and a share of system memory on an integrated one.
pub vram_total_bytes: u64,
/// Whether `VK_EXT_memory_budget` was available and enabled, and therefore whether [`MemoryUsage`] can report driver-side figures.
pub memory_budget_supported: bool,
}
/// Live memory figures, read on demand rather than cached.
///
/// Two independent views: the driver's own accounting of the device-local heaps (available only where `VK_EXT_memory_budget` is supported) and the renderer's allocator, which sees only what this process suballocates.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct MemoryUsage {
/// Bytes the driver reports as currently in use across the device-local heaps, by every process. [`None`] where `VK_EXT_memory_budget` is unsupported.
pub heap_usage_bytes: Option<u64>,
/// Bytes the driver reports this process may use across the device-local heaps before it risks eviction. [`None`] where `VK_EXT_memory_budget` is unsupported.
pub heap_budget_bytes: Option<u64>,
/// Bytes currently handed out by the renderer's allocator as live suballocations.
pub allocator_allocated_bytes: u64,
/// Bytes the renderer's allocator holds in device memory blocks, including regions not yet suballocated. Always at least `allocator_allocated_bytes`; the difference is allocator slack.
pub allocator_capacity_bytes: u64,
}
/// PCI vendor identifier for NVIDIA, whose driver packs `driver_version` differently from the Vulkan convention.
const VENDOR_NVIDIA: u32 = 0x10DE;
/// PCI vendor identifier for Intel, whose Windows driver packs `driver_version` differently from the Vulkan convention.
const VENDOR_INTEL: u32 = 0x8086;
/// Decodes a `VkPhysicalDeviceProperties::driverVersion` into a human-readable string.
///
/// The field is documented as vendor-specific, and two vendors deviate from the `VK_MAKE_VERSION` packing the rest follow. NVIDIA uses a four-component 10/8/8/6-bit layout. Intel's Windows driver uses a 14/18-bit split; its Linux (Mesa) driver follows the Vulkan convention, so the deviation is applied only on Windows. Every other vendor is decoded as major/minor/patch.
#[must_use]
pub fn decode_driver_version(vendor_id: u32, version: u32) -> String {
if vendor_id == VENDOR_NVIDIA {
return format!(
"{}.{}.{}.{}",
(version >> 22) & 0x3ff,
(version >> 14) & 0x0ff,
(version >> 6) & 0x0ff,
version & 0x3f
);
}
if vendor_id == VENDOR_INTEL && cfg!(windows) {
return format!("{}.{}", version >> 14, version & 0x3fff);
}
format!(
"{}.{}.{}",
version >> 22,
(version >> 12) & 0x3ff,
version & 0xfff
)
}
#[cfg(test)]
#[path = "tests/stats.rs"]
mod tests;