161 lines
7.4 KiB
Rust
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;
|