use crate::components::Camera3D;
use crate::ecs::asset_id::AssetId;
use crate::ecs::{PipelineContext, RenderOpFailures, StepResult, System};
use crate::gfx::backend::ChunkMesh;
use crate::gfx::ops::{OpFailure, RenderOps};
use crate::gfx::overlay::OverlayFrame;
use crate::gfx::render_slots::RenderSlots;
use crate::gfx::scene_residency::{CHANNEL_MESH, CHANNEL_SHADER, CHANNEL_TEXTURE, SceneResidency};
pub(crate) mod accounting;
pub(crate) mod pressure;
pub(crate) mod stats_log;
const IDENTITY4: [[f32; 4]; 4] = crate::gfx::draw_list::IDENTITY4;
const PRESSURE_SAMPLE_INTERVAL: u64 = 30;
#[derive(Debug, Clone, Copy)]
pub(crate) struct CameraRelativeView {
pub view: [[f32; 4]; 4],
pub cam_pos: [f32; 3],
}
pub(crate) struct ChunkStreamState {
pub(crate) streamer: crate::gfx::streaming::chunk::ChunkStreamer,
pub(crate) draws: std::collections::BTreeMap<crate::gfx::chunk_coord::ChunkCoord, usize>,
pub(crate) chunk_w: f32,
pub(crate) chunk_d: f32,
pub(crate) origin_chunk: crate::gfx::chunk_coord::ChunkCoord,
pub(crate) texture_slot: usize,
pub(crate) normal_map_slot: usize,
pub(crate) material: crate::gfx::render_types::MaterialUniforms,
}
#[derive(Debug, Clone, Default)]
pub struct StreamingStats {
pub texture: Option<(usize, usize, usize)>,
pub mesh: Option<(usize, usize, usize)>,
pub chunk: Option<(usize, usize)>,
pub texture_bytes: Option<(u64, u64)>,
pub mesh_bytes: Option<(u64, u64)>,
pub chunk_bytes: Option<(u64, u64)>,
}
#[derive(Debug, Clone, Copy)]
pub struct StreamingPressure {
pub rss_bytes: u64,
pub budget_bytes: u64,
pub under_pressure: bool,
}
pub(crate) struct StreamingState {
pub(crate) texture_streamer: Option<crate::gfx::streaming::texture::TextureStreamer>,
pub(crate) mesh_streamer: Option<crate::gfx::streaming::mesh::MeshStreamer>,
pub(crate) mesh_stream_draw_indices: Vec<usize>,
pub(crate) chunk_stream: Option<ChunkStreamState>,
pub(crate) shader_warmup: Option<crate::gfx::streaming::shader::ShaderWarmup>,
pub(crate) scene_residency: Option<SceneResidency>,
pub(crate) frame_count: u64,
pub(crate) frames_in_flight: usize,
pub(crate) texture_baseline_budget: Option<u64>,
pub(crate) mesh_baseline_budget: Option<u64>,
pub(crate) chunk_baseline_budget: Option<u64>,
pub(crate) pressure_stage: pressure::StreamPressureStage,
pub(crate) pressure_factor: f64,
pub(crate) last_sampled_rss: Option<u64>,
pub(crate) drift: crate::app::mem_drift::DriftTracker,
pub(crate) last_drift_verdict: Option<crate::app::mem_drift::DriftVerdict>,
pub(crate) heartbeats: stats_log::PoolHeartbeats,
}
impl std::fmt::Debug for StreamingState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StreamingState")
.field("frame_count", &self.frame_count)
.field("texture", &self.texture_streamer.is_some())
.field("mesh", &self.mesh_streamer.is_some())
.field("chunk", &self.chunk_stream.is_some())
.field("pressure", &self.pressure_stage)
.finish()
}
}
#[derive(Debug, Default)]
pub struct StreamingSystem {
scene_status_scratch: Vec<(AssetId, crate::gfx::scene_residency::SceneLoadState, f32)>,
}
impl StreamingSystem {
pub fn new() -> Self {
Self::default()
}
}
impl System for StreamingSystem {
fn step(&mut self, ctx: &mut PipelineContext) -> StepResult {
if !ctx.resources.contains::<StreamingState>() {
return StepResult::Continue;
}
let ram_budget = ctx
.resource::<crate::app::budget::MemoryBudget>()
.map(|b| b.budget_bytes);
let (view_matrix, cam_pos) = ctx
.query::<Camera3D>()
.next()
.map(|c| (c.view_matrix, c.position))
.unwrap_or((IDENTITY4, [0.0; 3]));
let world_hidden = ctx
.resource::<OverlayFrame>()
.map(|o| o.world_hidden)
.unwrap_or(false);
let pin_pair: Option<([AssetId; 2], usize)> = ctx
.resource::<crate::ecs::ActiveSceneFlow>()
.and_then(|slot| slot.flow.as_ref())
.map(|flow| {
let mut pins = [flow.current; 2];
let mut len = 1;
if let crate::gfx::scene_flow::FadePhase::ToBlack { next, .. } = flow.fade
&& next != flow.current
{
pins[1] = next;
len = 2;
}
(pins, len)
});
let scene_pins: Option<&[AssetId]> = pin_pair.as_ref().map(|(pins, len)| &pins[..*len]);
let transient_pool_bytes = ctx.profile.render.transient_pool_bytes;
{
let mut pressure_sample = None;
let mut drift_sample = None;
let state = ctx
.resources
.get_mut::<StreamingState>()
.expect("presence checked above");
if state.frame_count.is_multiple_of(PRESSURE_SAMPLE_INTERVAL)
&& let Some(budget) = ram_budget
{
let rss = crate::app::sysmem::process_resident_bytes();
pressure_sample = state.sample_pressure(rss, budget);
drift_sample = state.sample_drift(rss, budget);
}
if let Some(pressure) = pressure_sample {
ctx.insert_resource(pressure);
}
if let Some(drift) = drift_sample {
ctx.insert_resource(drift);
}
}
let Some(mut queues) = crate::ecs::ActiveRenderQueues::take(ctx.resources) else {
ctx.insert_resource(CameraRelativeView {
view: view_matrix,
cam_pos,
});
return StepResult::Continue;
};
let failures = ctx.resources.remove::<RenderOpFailures>();
let state = ctx
.resources
.get_mut::<StreamingState>()
.expect("presence checked above");
if let Some(failures) = failures {
state.apply_op_failures(&failures.0, &mut queues.slots);
}
let (view, cam_pos) = state.drive(
&mut queues.ops,
&mut queues.slots,
cam_pos,
view_matrix,
world_hidden,
scene_pins,
);
accounting::publish(
concinnity_core::memory::ledger(),
state.pool_reports(transient_pool_bytes),
);
let have_residency = match state.scene_residency.as_ref() {
Some(residency) => {
residency.status_into(&mut self.scene_status_scratch);
true
}
None => false,
};
crate::ecs::ActiveRenderQueues::put(ctx.resources, queues);
ctx.insert_resource(CameraRelativeView { view, cam_pos });
if have_residency {
match ctx.resource_mut::<crate::ecs::SceneResidencyStatus>() {
Some(published) => {
if published.scenes != self.scene_status_scratch {
published.scenes.clone_from(&self.scene_status_scratch);
}
}
None => {
ctx.insert_resource(crate::ecs::SceneResidencyStatus {
scenes: self.scene_status_scratch.clone(),
});
}
}
}
StepResult::Continue
}
}
impl StreamingState {
fn drive_shader_warmup(&mut self, ops: &mut RenderOps) {
let Some((bucket, want_resident)) =
self.shader_warmup.as_ref().and_then(|w| w.next_pending())
else {
return;
};
let resident = if want_resident {
match self.shader_warmup.as_ref().map(|w| w.load(bucket)) {
Some(Ok(stages)) => {
ops.record(move |backend| {
let shader = crate::gfx::backend_init::ShaderBytes {
vert: &stages.vert,
frag: &stages.frag,
shadow: &[],
vert_instanced: &stages.vert_instanced,
deferred: false,
};
let started = std::time::Instant::now();
match backend.install_world_shader(bucket, shader) {
Ok(()) => tracing::info!(
"StreamingSystem: shader bucket {} pipeline ready ({:.1} ms)",
bucket,
started.elapsed().as_secs_f32() * 1000.0
),
Err(e) => tracing::error!(
"StreamingSystem: shader bucket {} pipeline build failed: {}",
bucket,
e
),
}
});
}
Some(Err(e)) => tracing::error!(
"StreamingSystem: shader bucket {} payload unreadable: {}",
bucket,
e
),
None => {}
}
true
} else {
ops.record(move |backend| {
backend.evict_world_shader(bucket);
tracing::info!(
"StreamingSystem: shader bucket {} pipeline released",
bucket
);
});
false
};
if let Some(w) = self.shader_warmup.as_mut() {
w.note_resident(bucket, resident);
}
if let Some(residency) = self.scene_residency.as_mut() {
residency.note_resident((CHANNEL_SHADER, bucket), resident);
}
}
pub(crate) fn apply_op_failures(&mut self, failures: &[OpFailure], slots: &mut RenderSlots) {
for &failure in failures {
match failure {
OpFailure::MeshUpload { stream_id } => {
if let Some(streamer) = &mut self.mesh_streamer {
streamer.note_upload_failed(stream_id);
}
if let Some(residency) = &mut self.scene_residency {
residency.note_resident((CHANNEL_MESH, stream_id as u32), false);
}
}
OpFailure::ChunkAdd { coord } => {
if let Some(cs) = &mut self.chunk_stream
&& let Some(draw_idx) = cs.draws.remove(&coord)
{
slots.free_draw(draw_idx);
tracing::warn!(
"StreamingSystem: chunk add ({},{}) rolled back",
coord.x,
coord.z
);
}
}
}
}
}
fn drive(
&mut self,
ops: &mut RenderOps,
slots: &mut RenderSlots,
cam_pos: [f32; 3],
view_matrix: [[f32; 4]; 4],
world_hidden: bool,
scene_pins: Option<&[AssetId]>,
) -> ([[f32; 4]; 4], [f32; 3]) {
let loads_frozen = self.pressure_stage.freezes_loads();
if let (Some(residency), Some(pins)) = (self.scene_residency.as_mut(), scene_pins) {
let changes = residency.sync_pins(pins);
for (members, blocked) in [(&changes.blocked, true), (&changes.unblocked, false)] {
for &(channel, id) in members {
match channel {
CHANNEL_TEXTURE => {
if let Some(s) = &mut self.texture_streamer {
s.set_blocked(id as usize, blocked);
}
}
CHANNEL_MESH => {
if let Some(s) = &mut self.mesh_streamer {
s.set_blocked(id as usize, blocked);
}
}
CHANNEL_SHADER => {
if let Some(w) = &mut self.shader_warmup {
w.set_blocked(id, blocked);
}
}
_ => {}
}
}
}
}
self.drive_shader_warmup(ops);
let world_hidden = world_hidden
&& !self
.scene_residency
.as_ref()
.is_some_and(|r| r.any_loading());
if !world_hidden && let Some(streamer) = &mut self.texture_streamer {
streamer.update_scores(cam_pos, self.frame_count);
if !loads_frozen {
for slot in streamer.plan_and_dispatch() {
ops.record(move |backend| {
if let Err(e) = backend.evict_texture_slot(slot) {
tracing::warn!("StreamingSystem: texture evict slot {}: {}", slot, e);
}
});
if let Some(residency) = self.scene_residency.as_mut() {
residency.note_resident((CHANNEL_TEXTURE, slot as u32), false);
}
}
}
let residency = &mut self.scene_residency;
streamer.drain_completed(self.frame_count, |slot, image| {
ops.record(move |backend| {
if let Err(e) = backend.update_texture_slot(slot, &image) {
tracing::warn!("StreamingSystem: texture upload slot {}: {}", slot, e);
}
});
if let Some(residency) = residency.as_mut() {
residency.note_resident((CHANNEL_TEXTURE, slot as u32), true);
}
});
if let Some((resident, pending, unloaded)) = self
.heartbeats
.texture
.sample(self.frame_count, || streamer.stats())
{
tracing::info!(
"StreamingSystem: texture streaming -- {} resident, {} pending, {} unloaded",
resident,
pending,
unloaded
);
}
}
if !world_hidden && let Some(streamer) = &mut self.mesh_streamer {
streamer.update_scores(cam_pos, self.frame_count);
if !loads_frozen {
let retire_frame = self.frame_count + self.frames_in_flight as u64;
for stream_id in streamer.plan_and_dispatch() {
if let Some(&draw_idx) = self.mesh_stream_draw_indices.get(stream_id) {
ops.record(move |backend| {
if let Err(e) = backend.evict_mesh(draw_idx, retire_frame) {
tracing::warn!(
"StreamingSystem: mesh evict draw {}: {}",
draw_idx,
e
);
}
});
}
if let Some(residency) = self.scene_residency.as_mut() {
residency.note_resident((CHANNEL_MESH, stream_id as u32), false);
}
}
}
let draw_indices = &self.mesh_stream_draw_indices;
let frame = self.frame_count;
let residency = &mut self.scene_residency;
streamer.drain_completed(self.frame_count, |stream_id, verts, idxs| {
if let Some(&draw_idx) = draw_indices.get(stream_id) {
ops.record_with(move |backend, out| {
if let Err(e) = backend.upload_mesh(draw_idx, &verts, &idxs, frame) {
tracing::debug!(
"StreamingSystem: mesh upload draw {} deferred: {}",
draw_idx,
e
);
out.memory_pressure |=
matches!(e, crate::gfx::error::RenderError::OutOfDeviceMemory(_));
out.failures.push(OpFailure::MeshUpload { stream_id });
}
});
}
if let Some(residency) = residency.as_mut() {
residency.note_resident((CHANNEL_MESH, stream_id as u32), true);
}
});
if let Some((resident, pending, unloaded)) = self
.heartbeats
.mesh
.sample(self.frame_count, || streamer.stats())
{
tracing::info!(
"StreamingSystem: mesh streaming -- {} resident, {} pending, {} unloaded",
resident,
pending,
unloaded
);
}
}
let mut final_view = view_matrix;
let mut final_cam_pos = cam_pos;
if let Some(cs) = &mut self.chunk_stream {
let camera_chunk = cs.streamer.camera_chunk(cam_pos);
let retire_frame = self.frame_count + self.frames_in_flight as u64;
if !loads_frozen {
for coord in cs.streamer.plan_and_dispatch(camera_chunk) {
if let Some(draw_idx) = cs.draws.remove(&coord) {
slots.free_draw(draw_idx);
ops.record(move |backend| {
if let Err(e) = backend.remove_chunk_mesh(draw_idx, retire_frame) {
tracing::warn!(
"StreamingSystem: chunk remove ({},{}): {}",
coord.x,
coord.z,
e
);
}
});
}
}
}
if camera_chunk != cs.origin_chunk {
for (&coord, &draw_idx) in &cs.draws {
let model = chunk_model_matrix(coord, camera_chunk, cs.chunk_w, cs.chunk_d);
ops.record(move |backend| {
if let Err(e) = backend.set_chunk_model(draw_idx, model) {
tracing::warn!(
"StreamingSystem: chunk rebase ({},{}): {}",
coord.x,
coord.z,
e
);
}
});
}
cs.origin_chunk = camera_chunk;
}
let frame = self.frame_count;
let (chunk_w, chunk_d) = (cs.chunk_w, cs.chunk_d);
let (tex, nm, mat) = (cs.texture_slot, cs.normal_map_slot, cs.material);
let mut added: Vec<(crate::gfx::chunk_coord::ChunkCoord, usize)> = Vec::new();
cs.streamer.drain_completed(|coord, verts, idxs| {
if verts.is_empty() || idxs.is_empty() {
tracing::warn!(
"StreamingSystem: chunk add ({},{}): empty chunk geometry",
coord.x,
coord.z
);
return;
}
let model = chunk_model_matrix(coord, camera_chunk, chunk_w, chunk_d);
let dst = slots.allocate_draw();
let draw_idx = match dst {
crate::gfx::draw_slot::SlotAlloc::Reuse(i)
| crate::gfx::draw_slot::SlotAlloc::Append(i) => i,
};
added.push((coord, draw_idx));
ops.record_with(move |backend, out| {
let mesh = ChunkMesh {
verts: &verts,
idxs: &idxs,
model,
texture_slot: tex,
normal_map_slot: nm,
material: mat,
frame,
};
if let Err(e) = backend.add_chunk_mesh(mesh, dst) {
tracing::warn!(
"StreamingSystem: chunk add ({},{}): {}",
coord.x,
coord.z,
e
);
out.memory_pressure |=
matches!(e, crate::gfx::error::RenderError::OutOfDeviceMemory(_));
out.failures.push(OpFailure::ChunkAdd { coord });
}
});
});
for (coord, draw_idx) in added {
cs.draws.insert(coord, draw_idx);
}
let (ox, oz) = camera_chunk.origin_world(cs.chunk_w, cs.chunk_d);
let origin = [ox, 0.0, oz];
final_view =
crate::gfx::chunk_coord::camera_relative_view(view_matrix, cam_pos, origin);
final_cam_pos = [cam_pos[0] - ox, cam_pos[1], cam_pos[2] - oz];
if let Some((resident, pending, near, far)) =
self.heartbeats.chunk.sample(self.frame_count, || {
let (resident, pending) = cs.streamer.stats();
let (near, far) = cs.streamer.detail_counts();
(resident, pending, near, far)
})
{
tracing::info!(
"StreamingSystem: chunk streaming -- {} resident ({} full, {} impostor), {} pending",
resident,
near,
far,
pending
);
}
}
self.frame_count += 1;
(final_view, final_cam_pos)
}
fn sample_pressure(&mut self, rss: Option<u64>, budget: u64) -> Option<StreamingPressure> {
let rss = rss?;
let rising = self.last_sampled_rss.is_some_and(|prev| rss > prev);
let prev_stage = self.pressure_stage;
let decision =
pressure::step_pressure(rss, budget, rising, prev_stage, self.pressure_factor);
use pressure::StreamPressureStage::Evict;
match (prev_stage, decision.stage) {
(_, Evict) => self.apply_byte_factor(decision.budget_factor),
(Evict, _) => self.apply_byte_factor(1.0),
_ => {}
}
self.pressure_stage = decision.stage;
self.pressure_factor = decision.budget_factor;
self.last_sampled_rss = Some(rss);
Some(StreamingPressure {
rss_bytes: rss,
budget_bytes: budget,
under_pressure: decision.stage != pressure::StreamPressureStage::None,
})
}
fn sample_drift(
&mut self,
rss: Option<u64>,
budget: u64,
) -> Option<crate::app::mem_drift::MemoryDrift> {
use crate::app::mem_drift::DriftVerdict;
let rss = rss?;
let heap_live = concinnity_core::memory::stats()?.live_bytes;
let drift = self.drift.sample(rss, heap_live, budget)?;
if self.last_drift_verdict != Some(drift.verdict) {
self.last_drift_verdict = Some(drift.verdict);
let heap_mib = drift.heap_growth_bytes / (1024 * 1024);
let outside_mib = drift.outside_heap_growth_bytes / (1024 * 1024);
let minutes = drift.window_secs / 60;
let reading = drift.verdict.label();
if drift.verdict == DriftVerdict::Settled {
tracing::info!(
"memory drift: {reading} -- heap {heap_mib:+} MiB, outside heap {outside_mib:+} MiB over {minutes} min"
);
} else {
tracing::warn!(
"memory drift: {reading} -- heap {heap_mib:+} MiB, outside heap {outside_mib:+} MiB over {minutes} min"
);
}
}
Some(drift)
}
fn apply_byte_factor(&mut self, factor: f64) {
if let (Some(streamer), Some(baseline)) =
(self.texture_streamer.as_mut(), self.texture_baseline_budget)
{
streamer.set_byte_budget(Some(pressure::scale_budget(baseline, factor)));
}
if let (Some(streamer), Some(baseline)) =
(self.mesh_streamer.as_mut(), self.mesh_baseline_budget)
{
streamer.set_byte_budget(Some(pressure::scale_budget(baseline, factor)));
}
if let (Some(cs), Some(baseline)) = (self.chunk_stream.as_mut(), self.chunk_baseline_budget)
{
cs.streamer
.set_byte_budget(Some(pressure::scale_budget(baseline, factor)));
}
}
fn pool_reports(
&self,
transient_pool_bytes: u64,
) -> impl Iterator<Item = accounting::PoolReport> {
let textures = accounting::textures_report(
self.texture_streamer
.as_ref()
.map(|s| (s.resident_bytes(), s.byte_budget())),
transient_pool_bytes,
);
[
self.mesh_streamer.as_ref().map(|s| {
(
concinnity_core::memory::MemTag::Meshes,
s.resident_bytes(),
s.byte_budget(),
)
}),
self.chunk_stream.as_ref().map(|cs| {
(
concinnity_core::memory::MemTag::Chunks,
cs.streamer.resident_bytes(),
cs.streamer.byte_budget(),
)
}),
]
.into_iter()
.flatten()
.map(
|(tag, resident_bytes, byte_budget)| accounting::PoolReport {
tag,
resident_bytes,
byte_budget,
},
)
.chain(textures)
}
pub(crate) fn streaming_stats(&self) -> StreamingStats {
StreamingStats {
texture: self.texture_streamer.as_ref().map(|s| s.stats()),
mesh: self.mesh_streamer.as_ref().map(|s| s.stats()),
chunk: self.chunk_stream.as_ref().map(|cs| cs.streamer.stats()),
texture_bytes: self
.texture_streamer
.as_ref()
.map(|s| (s.resident_bytes(), s.byte_budget().unwrap_or(0))),
mesh_bytes: self
.mesh_streamer
.as_ref()
.map(|s| (s.resident_bytes(), s.byte_budget().unwrap_or(0))),
chunk_bytes: self.chunk_stream.as_ref().map(|cs| {
(
cs.streamer.resident_bytes(),
cs.streamer.byte_budget().unwrap_or(0),
)
}),
}
}
}
pub(crate) fn chunk_model_matrix(
coord: crate::gfx::chunk_coord::ChunkCoord,
origin: crate::gfx::chunk_coord::ChunkCoord,
chunk_w: f32,
chunk_d: f32,
) -> [[f32; 4]; 4] {
let dx = (coord.x - origin.x) as f32 * chunk_w;
let dz = (coord.z - origin.z) as f32 * chunk_d;
[
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[dx, 0.0, dz, 1.0],
]
}
#[cfg(test)]
mod tests {
use super::*;
use crate::blob::BlobData;
use crate::ecs::{ComponentStorage, Resources};
use crate::gfx::chunk_coord::ChunkCoord;
use crate::gfx::chunk_window::ChunkDetail;
use crate::gfx::mesh_payload::Vertex;
use crate::gfx::mock_backend::{Call, MockBackend, recording_backend};
use crate::gfx::profile::FrameProfile;
use crate::gfx::streaming::chunk::{ChunkSource, ChunkStreamer};
use crate::gfx::streaming::mesh::{DecodedMesh, MeshPayloadSource, MeshStreamer};
use crate::gfx::streaming::texture::{DecodedTexture, PayloadSource, TextureStreamer};
use pressure::StreamPressureStage;
use std::sync::Arc;
const MAX_DRIVE_SPINS: usize = 100_000;
fn vtx() -> Vertex {
Vertex {
pos: [0.0; 3],
normal: [0.0, 1.0, 0.0],
tangent: [1.0, 0.0, 0.0],
color: [1.0; 3],
uv: [0.0; 2],
}
}
fn tri() -> DecodedMesh {
DecodedMesh {
vertices: vec![vtx(), vtx(), vtx()],
indices: vec![0, 1, 2],
}
}
struct ConstTexture;
impl PayloadSource for ConstTexture {
fn fetch(&self, _id: usize) -> Result<DecodedTexture, String> {
Ok(DecodedTexture {
image: crate::bake::texture::TextureImage::rgba8(1, 1, vec![1, 2, 3, 4]),
})
}
}
struct ConstMesh;
impl MeshPayloadSource for ConstMesh {
fn fetch(&self, _id: usize) -> Result<DecodedMesh, String> {
Ok(tri())
}
}
struct ConstChunk;
impl ChunkSource for ConstChunk {
fn generate(
&self,
_coord: ChunkCoord,
_detail: ChunkDetail,
) -> Result<DecodedMesh, String> {
Ok(tri())
}
}
struct FailingChunk;
impl ChunkSource for FailingChunk {
fn generate(
&self,
_coord: ChunkCoord,
_detail: ChunkDetail,
) -> Result<DecodedMesh, String> {
Err("test source".to_string())
}
}
fn translation_view(x: f32, y: f32, z: f32) -> [[f32; 4]; 4] {
[
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[x, y, z, 1.0],
]
}
fn empty_state() -> StreamingState {
StreamingState {
texture_streamer: None,
mesh_streamer: None,
mesh_stream_draw_indices: Vec::new(),
chunk_stream: None,
shader_warmup: None,
scene_residency: None,
frame_count: 0,
frames_in_flight: 2,
texture_baseline_budget: None,
mesh_baseline_budget: None,
chunk_baseline_budget: None,
pressure_stage: StreamPressureStage::None,
pressure_factor: 1.0,
last_sampled_rss: None,
drift: Default::default(),
last_drift_verdict: None,
heartbeats: Default::default(),
}
}
fn pooled_state(resident_cap: usize) -> StreamingState {
let centers = vec![vec![[0.0, 0.0, 0.0]], vec![[100.0, 0.0, 0.0]]];
let mut state = empty_state();
state.texture_streamer = Some(TextureStreamer::new(
Arc::new(ConstTexture),
centers.clone(),
4,
resident_cap,
));
state.mesh_streamer = Some(MeshStreamer::new(
Arc::new(ConstMesh),
centers,
4,
resident_cap,
));
state.mesh_stream_draw_indices = vec![10, 11];
state
}
fn chunk_state(source: Arc<dyn ChunkSource>, near: i32, far: i32) -> ChunkStreamState {
ChunkStreamState {
streamer: ChunkStreamer::new(source, near, far, 64, 16.0, 16.0),
draws: std::collections::BTreeMap::new(),
chunk_w: 16.0,
chunk_d: 16.0,
origin_chunk: ChunkCoord::new(0, 0),
texture_slot: 0,
normal_map_slot: crate::gfx::render_types::NO_NORMAL_MAP_SLOT,
material: crate::gfx::render_types::MaterialUniforms::DEFAULT,
}
}
fn drive_once(
state: &mut StreamingState,
backend: &mut MockBackend,
cam: [f32; 3],
view: [[f32; 4]; 4],
world_hidden: bool,
pins: Option<&[AssetId]>,
) -> ([[f32; 4]; 4], [f32; 3]) {
let mut slots = RenderSlots::new(0, true, &[]);
let mut ops = RenderOps::default();
let out = state.drive(&mut ops, &mut slots, cam, view, world_hidden, pins);
let outcome = ops.replay(backend);
state.apply_op_failures(&outcome.failures, &mut slots);
out
}
fn drive_until(
state: &mut StreamingState,
backend: &mut MockBackend,
cam: [f32; 3],
done: impl Fn(&StreamingState) -> bool,
) {
let mut slots = RenderSlots::new(0, true, &[]);
for _ in 0..MAX_DRIVE_SPINS {
let mut ops = RenderOps::default();
state.drive(&mut ops, &mut slots, cam, IDENTITY4, false, None);
let outcome = ops.replay(backend);
state.apply_op_failures(&outcome.failures, &mut slots);
if done(state) {
return;
}
std::thread::yield_now();
}
panic!("streaming never reached the expected state");
}
struct StepWorld {
components: ComponentStorage,
blob: BlobData,
profile: FrameProfile,
resources: Resources,
scratch: crate::ecs::Arena,
}
impl StepWorld {
fn new() -> Self {
Self {
components: ComponentStorage::default(),
blob: BlobData::empty(),
profile: FrameProfile::default(),
resources: Resources::new(),
scratch: crate::ecs::Arena::with_capacity(64 * 1024),
}
}
fn with_camera(mut self, position: [f32; 3], view_matrix: [[f32; 4]; 4]) -> Self {
self.components.push_typed(Camera3D {
position,
view_matrix,
..Camera3D::bake(Default::default())
});
self
}
fn park_render_queues(&mut self) {
self.resources.insert(crate::ecs::ActiveRenderQueues(Some(
crate::ecs::RenderQueues {
ops: Default::default(),
slots: RenderSlots::new(0, true, &[]),
},
)));
}
fn step(&mut self) -> StepResult {
let mut ctx = PipelineContext {
components: &mut self.components,
blob: &mut self.blob,
profile: &mut self.profile,
resources: &mut self.resources,
frame: crate::ecs::FrameContext::new(&self.scratch),
};
StreamingSystem::new().step(&mut ctx)
}
fn view(&self) -> CameraRelativeView {
*self
.resources
.get::<CameraRelativeView>()
.expect("camera-relative view published")
}
fn parked_state(&self) -> &StreamingState {
self.resources
.get::<StreamingState>()
.expect("state parked again")
}
}
#[test]
fn sample_pressure_engages_and_publishes() {
let mut s = empty_state();
let p = s.sample_pressure(Some(920), 1000).expect("published");
assert_eq!(s.pressure_stage, StreamPressureStage::Gate);
assert!(p.under_pressure);
assert_eq!(p.rss_bytes, 920);
assert_eq!(p.budget_bytes, 1000);
}
#[test]
fn sample_drift_reports_once_settled_and_splits_the_whole_rss_movement() {
const RSS: u64 = 2 * 1024 * 1024 * 1024;
const BUDGET: u64 = 4 * RSS;
let mut s = empty_state();
assert_eq!(s.sample_drift(None, BUDGET), None, "no RSS, no drift");
let d = (0..16)
.find_map(|_| s.sample_drift(Some(RSS), BUDGET))
.expect("a steady run settles the baseline");
assert_eq!(d.verdict, crate::app::mem_drift::DriftVerdict::Settled);
assert_eq!(d.heap_growth_bytes + d.outside_heap_growth_bytes, 0);
}
#[test]
fn sample_drift_never_moves_the_valve() {
const RSS: u64 = 2 * 1024 * 1024 * 1024;
let mut s = empty_state();
for _ in 0..4 {
s.sample_drift(Some(RSS), 4 * RSS);
}
assert_eq!(s.pressure_stage, StreamPressureStage::None);
assert_eq!(s.pressure_factor, 1.0);
assert_eq!(s.last_sampled_rss, None);
}
#[test]
fn sample_pressure_escalates_when_rss_keeps_rising() {
let mut s = empty_state();
s.sample_pressure(Some(910), 1000);
assert_eq!(s.pressure_stage, StreamPressureStage::Gate);
s.sample_pressure(Some(925), 1000);
assert_eq!(s.pressure_stage, StreamPressureStage::Evict);
assert!(s.pressure_factor < 1.0);
}
#[test]
fn sample_pressure_releases_with_hysteresis() {
let mut s = empty_state();
s.sample_pressure(Some(970), 1000); assert_eq!(s.pressure_stage, StreamPressureStage::Evict);
s.sample_pressure(Some(850), 1000);
assert_eq!(s.pressure_stage, StreamPressureStage::Evict);
let p = s.sample_pressure(Some(700), 1000).expect("published");
assert_eq!(s.pressure_stage, StreamPressureStage::None);
assert_eq!(s.pressure_factor, 1.0);
assert!(!p.under_pressure);
}
#[test]
fn sample_pressure_is_inert_without_rss() {
let mut s = empty_state();
s.sample_pressure(Some(970), 1000);
let stage_before = s.pressure_stage;
assert!(s.sample_pressure(None, 1000).is_none());
assert_eq!(s.pressure_stage, stage_before);
}
#[test]
fn chunk_model_matrix_offsets_by_chunk_delta() {
let m = chunk_model_matrix(ChunkCoord::new(2, -3), ChunkCoord::new(0, 0), 16.0, 10.0);
assert_eq!(m[3], [32.0, 0.0, -30.0, 1.0]);
assert_eq!(m[0], [1.0, 0.0, 0.0, 0.0]);
assert_eq!(m[1], [0.0, 1.0, 0.0, 0.0]);
assert_eq!(m[2], [0.0, 0.0, 1.0, 0.0]);
}
#[test]
fn chunk_model_matrix_origin_chunk_is_untranslated() {
let c = ChunkCoord::new(5, 7);
let m = chunk_model_matrix(c, c, 16.0, 16.0);
assert_eq!(m[3], [0.0, 0.0, 0.0, 1.0]);
}
#[test]
fn deep_pressure_scales_each_pool_budget_and_release_restores_the_baseline() {
let mut state = pooled_state(8);
state.chunk_stream = Some(chunk_state(Arc::new(ConstChunk), 0, 0));
state.texture_baseline_budget = Some(4000);
state.mesh_baseline_budget = Some(2000);
state.chunk_baseline_budget = Some(1000);
state.apply_byte_factor(1.0);
state.sample_pressure(Some(970), 1000);
assert_eq!(state.pressure_stage, StreamPressureStage::Evict);
let factor = state.pressure_factor;
assert!(factor < 1.0);
let tex = state.texture_streamer.as_ref().unwrap().byte_budget();
let mesh = state.mesh_streamer.as_ref().unwrap().byte_budget();
let chunk = state.chunk_stream.as_ref().unwrap().streamer.byte_budget();
assert_eq!(tex, Some(pressure::scale_budget(4000, factor)));
assert_eq!(mesh, Some(pressure::scale_budget(2000, factor)));
assert_eq!(chunk, Some(pressure::scale_budget(1000, factor)));
state.sample_pressure(Some(100), 1000);
assert_eq!(state.pressure_stage, StreamPressureStage::None);
assert_eq!(
state.texture_streamer.as_ref().unwrap().byte_budget(),
Some(4000)
);
assert_eq!(
state.mesh_streamer.as_ref().unwrap().byte_budget(),
Some(2000)
);
assert_eq!(
state.chunk_stream.as_ref().unwrap().streamer.byte_budget(),
Some(1000)
);
}
#[test]
fn count_only_pools_gain_no_byte_budget_under_pressure() {
let mut state = pooled_state(8);
state.chunk_stream = Some(chunk_state(Arc::new(ConstChunk), 0, 0));
state.sample_pressure(Some(970), 1000);
assert_eq!(state.pressure_stage, StreamPressureStage::Evict);
assert_eq!(state.texture_streamer.as_ref().unwrap().byte_budget(), None);
assert_eq!(state.mesh_streamer.as_ref().unwrap().byte_budget(), None);
assert_eq!(
state.chunk_stream.as_ref().unwrap().streamer.byte_budget(),
None
);
}
#[test]
fn streaming_stats_reports_every_active_pool() {
let mut state = pooled_state(8);
state.chunk_stream = Some(chunk_state(Arc::new(ConstChunk), 0, 0));
state
.texture_streamer
.as_mut()
.unwrap()
.set_byte_budget(Some(4000));
let stats = state.streaming_stats();
assert_eq!(stats.texture, Some((0, 0, 2)));
assert_eq!(stats.mesh, Some((0, 0, 2)));
assert_eq!(stats.chunk, Some((0, 0)));
assert_eq!(stats.texture_bytes, Some((0, 4000)));
assert_eq!(stats.mesh_bytes, Some((0, 0)));
assert_eq!(stats.chunk_bytes, Some((0, 0)));
}
#[test]
fn streaming_stats_reports_nothing_without_pools() {
let stats = empty_state().streaming_stats();
assert!(stats.texture.is_none());
assert!(stats.mesh.is_none());
assert!(stats.chunk.is_none());
assert!(stats.texture_bytes.is_none());
assert!(stats.mesh_bytes.is_none());
assert!(stats.chunk_bytes.is_none());
}
#[test]
fn debug_reports_the_active_pools_and_the_frame_clock() {
let mut state = pooled_state(8);
state.frame_count = 7;
state.pressure_stage = StreamPressureStage::Gate;
let s = format!("{state:?}");
assert!(s.contains("frame_count: 7"), "{s}");
assert!(s.contains("texture: true"), "{s}");
assert!(s.contains("mesh: true"), "{s}");
assert!(s.contains("chunk: false"), "{s}");
assert!(s.contains("pressure: Gate"), "{s}");
}
#[test]
fn drive_advances_the_frame_clock() {
let (_recorded, mut backend) = recording_backend();
let mut state = empty_state();
drive_once(&mut state, &mut backend, [0.0; 3], IDENTITY4, false, None);
drive_once(&mut state, &mut backend, [0.0; 3], IDENTITY4, false, None);
assert_eq!(state.frame_count, 2);
}
#[test]
fn a_hidden_world_dispatches_no_loads() {
let (_recorded, mut backend) = recording_backend();
let mut state = pooled_state(8);
drive_once(&mut state, &mut backend, [0.0; 3], IDENTITY4, true, None);
assert_eq!(state.texture_streamer.as_ref().unwrap().stats(), (0, 0, 2));
assert_eq!(state.mesh_streamer.as_ref().unwrap().stats(), (0, 0, 2));
}
#[test]
fn a_visible_world_dispatches_texture_and_mesh_loads() {
let (_recorded, mut backend) = recording_backend();
let mut state = pooled_state(8);
drive_once(&mut state, &mut backend, [0.0; 3], IDENTITY4, false, None);
assert!(state.texture_streamer.as_ref().unwrap().stats().2 < 2);
assert!(state.mesh_streamer.as_ref().unwrap().stats().2 < 2);
}
#[test]
fn scene_residency_streams_only_the_pinned_scene_and_swaps_on_switch() {
use crate::gfx::scene_residency::SceneLoadState;
let (_recorded, mut backend) = recording_backend();
let mut state = pooled_state(8);
let scene_a = AssetId(70);
let scene_b = AssetId(71);
let residency = SceneResidency::new(vec![
(scene_a, vec![(CHANNEL_TEXTURE, 0), (CHANNEL_MESH, 0)]),
(scene_b, vec![(CHANNEL_TEXTURE, 1), (CHANNEL_MESH, 1)]),
]);
for (channel, id) in residency.all_members().collect::<Vec<_>>() {
match channel {
CHANNEL_TEXTURE => state
.texture_streamer
.as_mut()
.unwrap()
.set_blocked(id as usize, true),
_ => state
.mesh_streamer
.as_mut()
.unwrap()
.set_blocked(id as usize, true),
}
}
state.scene_residency = Some(residency);
let pins_a = [scene_a];
for _ in 0..MAX_DRIVE_SPINS {
drive_once(
&mut state,
&mut backend,
[0.0; 3],
IDENTITY4,
false,
Some(&pins_a),
);
let r = state.scene_residency.as_ref().unwrap();
if r.state(scene_a) == Some(SceneLoadState::Resident) {
break;
}
std::thread::yield_now();
}
let r = state.scene_residency.as_ref().unwrap();
assert_eq!(r.state(scene_a), Some(SceneLoadState::Resident));
assert_eq!(r.state(scene_b), Some(SceneLoadState::Unloaded));
assert_eq!(
state.texture_streamer.as_ref().unwrap().stats().0,
1,
"only A's texture is resident"
);
let pins_b = [scene_b];
for _ in 0..MAX_DRIVE_SPINS {
drive_once(
&mut state,
&mut backend,
[0.0; 3],
IDENTITY4,
false,
Some(&pins_b),
);
let r = state.scene_residency.as_ref().unwrap();
if r.state(scene_b) == Some(SceneLoadState::Resident)
&& r.state(scene_a) == Some(SceneLoadState::Unloaded)
{
break;
}
std::thread::yield_now();
}
let r = state.scene_residency.as_ref().unwrap();
assert_eq!(r.state(scene_b), Some(SceneLoadState::Resident));
assert_eq!(r.progress(scene_b), Some(1.0));
assert_eq!(r.state(scene_a), Some(SceneLoadState::Unloaded));
assert_eq!(state.texture_streamer.as_ref().unwrap().stats().0, 1);
assert_eq!(state.mesh_streamer.as_ref().unwrap().stats().0, 1);
}
#[test]
fn the_gate_stage_freezes_new_loads() {
let (_recorded, mut backend) = recording_backend();
let mut state = pooled_state(8);
state.chunk_stream = Some(chunk_state(Arc::new(ConstChunk), 0, 0));
state.pressure_stage = StreamPressureStage::Gate;
drive_once(&mut state, &mut backend, [0.0; 3], IDENTITY4, false, None);
assert_eq!(state.texture_streamer.as_ref().unwrap().stats(), (0, 0, 2));
assert_eq!(state.mesh_streamer.as_ref().unwrap().stats(), (0, 0, 2));
assert_eq!(
state.chunk_stream.as_ref().unwrap().streamer.stats(),
(0, 0)
);
}
#[test]
fn the_evict_stage_keeps_planning() {
let (_recorded, mut backend) = recording_backend();
let mut state = pooled_state(8);
state.pressure_stage = StreamPressureStage::Evict;
drive_once(&mut state, &mut backend, [0.0; 3], IDENTITY4, false, None);
assert!(state.texture_streamer.as_ref().unwrap().stats().2 < 2);
assert!(state.mesh_streamer.as_ref().unwrap().stats().2 < 2);
}
#[test]
fn completed_loads_are_uploaded_to_the_backend() {
let (recorded, mut backend) = recording_backend();
let mut state = pooled_state(8);
drive_until(&mut state, &mut backend, [0.0; 3], |s| {
s.texture_streamer.as_ref().unwrap().stats().0 == 2
&& s.mesh_streamer.as_ref().unwrap().stats().0 == 2
});
let s = recorded.lock().unwrap();
assert!(s.saw(&Call::UpdateTextureSlot {
slot: 0,
w: 1,
h: 1
}));
assert!(s.saw(&Call::UpdateTextureSlot {
slot: 1,
w: 1,
h: 1
}));
assert!(s.saw(&Call::UploadMesh {
draw_idx: 10,
vertices: 3,
indices: 3,
}));
assert!(s.saw(&Call::UploadMesh {
draw_idx: 11,
vertices: 3,
indices: 3,
}));
}
#[test]
fn a_streamed_mesh_without_a_draw_slot_uploads_nothing() {
let (recorded, mut backend) = recording_backend();
let mut state = pooled_state(8);
state.mesh_stream_draw_indices.clear();
drive_until(&mut state, &mut backend, [0.0; 3], |s| {
s.mesh_streamer.as_ref().unwrap().stats().0 == 2
});
assert!(
!recorded
.lock()
.unwrap()
.calls
.iter()
.any(|c| matches!(c, Call::UploadMesh { .. }))
);
}
#[test]
fn moving_the_camera_evicts_the_now_distant_item_over_the_cap() {
let (recorded, mut backend) = recording_backend();
let mut state = pooled_state(1);
drive_until(&mut state, &mut backend, [0.0; 3], |s| {
s.texture_streamer.as_ref().unwrap().stats().0 == 1
&& s.mesh_streamer.as_ref().unwrap().stats().0 == 1
});
recorded.lock().unwrap().calls.clear();
drive_once(
&mut state,
&mut backend,
[100.0, 0.0, 0.0],
IDENTITY4,
false,
None,
);
let s = recorded.lock().unwrap();
assert!(s.saw(&Call::EvictTextureSlot(0)), "{:?}", s.calls);
assert!(s.saw(&Call::EvictMesh(10)), "{:?}", s.calls);
}
#[test]
fn without_chunk_streaming_the_view_and_camera_stay_absolute() {
let (_recorded, mut backend) = recording_backend();
let mut state = empty_state();
let view = translation_view(-40.0, -5.0, 40.0);
let cam = [40.0, 5.0, -40.0];
assert_eq!(
drive_once(&mut state, &mut backend, cam, view, false, None),
(view, cam)
);
}
#[test]
fn chunk_streaming_rebases_the_view_onto_the_camera_chunk() {
let (_recorded, mut backend) = recording_backend();
let mut state = empty_state();
state.chunk_stream = Some(chunk_state(Arc::new(FailingChunk), 0, 0));
let cam = [40.0, 5.0, -40.0];
let view = translation_view(-40.0, -5.0, 40.0);
let (out_view, out_cam) = drive_once(&mut state, &mut backend, cam, view, false, None);
let origin = [32.0, 0.0, -48.0];
assert_eq!(out_cam, [8.0, 5.0, 8.0]);
assert_eq!(
out_view,
crate::gfx::chunk_coord::camera_relative_view(view, cam, origin)
);
assert_ne!(out_view, view, "the rebase actually rewrote the view");
assert_eq!(
state.chunk_stream.as_ref().unwrap().origin_chunk,
ChunkCoord::new(2, -3)
);
}
#[test]
fn crossing_into_a_new_chunk_rebases_resident_chunk_models() {
let (recorded, mut backend) = recording_backend();
let mut state = empty_state();
let mut cs = chunk_state(Arc::new(FailingChunk), 1, 1);
cs.draws.insert(ChunkCoord::new(0, 0), 3);
cs.draws.insert(ChunkCoord::new(1, 0), 4);
state.chunk_stream = Some(cs);
drive_once(
&mut state,
&mut backend,
[20.0, 0.0, 0.0],
IDENTITY4,
false,
None,
);
{
let s = recorded.lock().unwrap();
assert!(s.saw(&Call::SetChunkModel(3)));
assert!(s.saw(&Call::SetChunkModel(4)));
}
assert_eq!(
state.chunk_stream.as_ref().unwrap().origin_chunk,
ChunkCoord::new(1, 0)
);
recorded.lock().unwrap().calls.clear();
drive_once(
&mut state,
&mut backend,
[21.0, 0.0, 0.0],
IDENTITY4,
false,
None,
);
assert!(
!recorded
.lock()
.unwrap()
.calls
.iter()
.any(|c| matches!(c, Call::SetChunkModel(_)))
);
}
#[test]
fn chunks_leaving_the_view_window_are_removed_from_the_backend() {
let (recorded, mut backend) = recording_backend();
let mut state = empty_state();
let mut cs = chunk_state(Arc::new(FailingChunk), 0, 0);
cs.draws.insert(ChunkCoord::new(0, 0), 9);
state.chunk_stream = Some(cs);
drive_once(&mut state, &mut backend, [0.0; 3], IDENTITY4, false, None);
drive_once(
&mut state,
&mut backend,
[800.0, 0.0, 0.0],
IDENTITY4,
false,
None,
);
assert!(recorded.lock().unwrap().saw(&Call::RemoveChunkMesh(9)));
assert!(
!state
.chunk_stream
.as_ref()
.unwrap()
.draws
.contains_key(&ChunkCoord::new(0, 0))
);
}
#[test]
fn a_generated_chunk_is_added_and_its_draw_slot_tracked() {
let (recorded, mut backend) = recording_backend();
let mut state = empty_state();
state.chunk_stream = Some(chunk_state(Arc::new(ConstChunk), 0, 0));
drive_until(&mut state, &mut backend, [0.0; 3], |s| {
s.chunk_stream.as_ref().unwrap().streamer.stats().0 == 1
});
let cs = state.chunk_stream.as_ref().unwrap();
assert_eq!(cs.draws.get(&ChunkCoord::new(0, 0)), Some(&0));
assert!(recorded.lock().unwrap().saw(&Call::AddChunkMesh));
}
#[test]
fn a_step_without_parked_state_publishes_no_view() {
let mut w = StepWorld::new();
assert_eq!(w.step(), StepResult::Continue);
assert!(w.resources.get::<CameraRelativeView>().is_none());
}
#[test]
fn a_step_without_a_backend_still_publishes_the_absolute_view() {
let view = translation_view(-1.0, -2.0, -3.0);
let mut w = StepWorld::new().with_camera([1.0, 2.0, 3.0], view);
w.resources.insert(empty_state());
assert_eq!(w.step(), StepResult::Continue);
assert_eq!(w.view().cam_pos, [1.0, 2.0, 3.0]);
assert_eq!(w.view().view, view);
assert_eq!(w.parked_state().frame_count, 0, "no frame was driven");
}
#[test]
fn a_step_without_a_camera_publishes_the_identity_view() {
let mut w = StepWorld::new();
w.resources.insert(empty_state());
w.park_render_queues();
w.step();
assert_eq!(w.view().view, IDENTITY4);
assert_eq!(w.view().cam_pos, [0.0; 3]);
}
#[test]
fn a_step_drives_the_pools_and_reparks_the_queues() {
let mut w = StepWorld::new().with_camera([0.0; 3], IDENTITY4);
w.resources.insert(pooled_state(8));
w.park_render_queues();
w.step();
assert!(
w.resources
.get::<crate::ecs::ActiveRenderQueues>()
.is_some_and(|slot| slot.0.is_some())
);
assert_eq!(w.parked_state().frame_count, 1);
assert!(
w.parked_state()
.texture_streamer
.as_ref()
.unwrap()
.stats()
.2
< 2,
"the pools were driven"
);
}
#[test]
fn an_opaque_overlay_suspends_streaming_without_consuming_the_frame() {
let mut w = StepWorld::new().with_camera([0.0; 3], IDENTITY4);
w.resources.insert(pooled_state(8));
w.park_render_queues();
w.resources.insert(OverlayFrame {
world_hidden: true,
..Default::default()
});
w.step();
assert_eq!(
w.parked_state().texture_streamer.as_ref().unwrap().stats(),
(0, 0, 2)
);
assert!(w.resources.get::<OverlayFrame>().is_some());
}
#[test]
fn ram_pressure_is_sampled_only_on_the_throttled_cadence() {
let mut w = StepWorld::new();
let mut state = empty_state();
state.frame_count = 1;
w.resources.insert(state);
w.resources
.insert(crate::app::budget::MemoryBudget::compute(None, 1));
w.step();
assert!(w.resources.get::<StreamingPressure>().is_none());
}
#[test]
fn ram_pressure_is_not_sampled_without_a_memory_budget() {
let mut w = StepWorld::new();
w.resources.insert(empty_state());
w.step();
assert!(w.resources.get::<StreamingPressure>().is_none());
}
#[test]
fn an_rss_sample_over_the_memory_budget_publishes_engaged_pressure() {
let mut w = StepWorld::new();
w.resources.insert(empty_state());
w.resources
.insert(crate::app::budget::MemoryBudget::compute(None, 1));
w.step();
match crate::app::sysmem::process_resident_bytes() {
Some(_) => {
let p = w.resources.get::<StreamingPressure>().expect("published");
assert!(p.under_pressure);
assert_eq!(p.budget_bytes, 1024 * 1024);
assert_ne!(w.parked_state().pressure_stage, StreamPressureStage::None);
}
None => assert!(w.resources.get::<StreamingPressure>().is_none()),
}
}
}