Skip to content

Commit 46e1d0d

Browse files
committed
Squashed commit of the following:
commit 2a98d1e40a513c82329640fa5ad1c5eb0c2471c3 Author: DoubleStyx <79298541+DoubleStyx@users.noreply.github.com> Date: Tue Jun 9 14:30:23 2026 -0500 Refactor renderer frame and asset orchestration
1 parent 6e117a1 commit 46e1d0d

28 files changed

Lines changed: 748 additions & 273 deletions

File tree

crates/renderide/src/app/driver/frame.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,7 @@ impl AppDriver {
260260

261261
fn drain_submit_completion_work(&mut self) {
262262
if let Some(target) = self.target.as_mut() {
263-
let gpu = target.gpu_mut();
264-
self.runtime.maintain_nonblocking_gpu_jobs(gpu);
265-
self.runtime.drain_reflection_probe_render_tasks(gpu);
266-
self.runtime.drain_camera_render_tasks(gpu);
263+
self.runtime.drain_submit_completion_work(target.gpu_mut());
267264
}
268265
}
269266

crates/renderide/src/backend/asset_transfers.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,28 @@ impl AssetTransferQueue {
425425
.contains_key(&asset_id)
426426
}
427427

428+
/// Point render-buffer asset ids with retained uploads and no active background build.
429+
pub(in crate::backend::asset_transfers) fn startable_point_render_buffer_upload_ids(
430+
&self,
431+
) -> Vec<i32> {
432+
self.pending_point_render_buffer_uploads
433+
.keys()
434+
.copied()
435+
.filter(|asset_id| !self.active_point_render_buffer_builds.contains(asset_id))
436+
.collect()
437+
}
438+
439+
/// Trail render-buffer asset ids with retained uploads and no active background build.
440+
pub(in crate::backend::asset_transfers) fn startable_trail_render_buffer_upload_ids(
441+
&self,
442+
) -> Vec<i32> {
443+
self.pending_trail_render_buffer_uploads
444+
.keys()
445+
.copied()
446+
.filter(|asset_id| !self.active_trail_render_buffer_builds.contains(asset_id))
447+
.collect()
448+
}
449+
428450
/// Invalidates in-flight point render-buffer work for `asset_id`.
429451
#[inline]
430452
pub(crate) fn cancel_point_render_buffer_generation(&mut self, asset_id: i32) -> bool {

crates/renderide/src/backend/asset_transfers/integrator/drain.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use crate::materials::MaterialSystem;
88
use crate::profiling::{AssetIntegrationProfileSample, plot_asset_integration};
99

1010
use super::super::AssetTransferQueue;
11-
use super::super::particle_task::drain_ready_particle_builds;
11+
use super::super::particle_task::{
12+
drain_ready_particle_builds, enqueue_startable_particle_uploads,
13+
};
1214
use super::gpu_context::{AssetUploadGpuContext, GpuHandles, collect_gpu_handles};
1315
use super::queue::AssetTaskLane;
1416
use super::step::{StepResult, step_asset_task};
@@ -34,6 +36,10 @@ const DEADLINE_POLL_STRIDE: u32 = 4;
3436
struct LaneDrainOutcome {
3537
/// Whether the lane still had queued work when the drain ended.
3638
pending: bool,
39+
/// Whether the lane ended with queued work that cannot make progress until background state changes.
40+
blocked_on_background: bool,
41+
/// Whether at least one task step completed useful work.
42+
made_progress: bool,
3743
/// Queue steps processed by this drain.
3844
processed: u32,
3945
}
@@ -384,6 +390,16 @@ fn finalize_drain(
384390
render: outcomes.integration.render.processed,
385391
particle: outcomes.particle.processed,
386392
};
393+
let made_progress = outcomes.integration.main.made_progress
394+
|| outcomes.integration.high_priority.made_progress
395+
|| outcomes.integration.normal_priority.made_progress
396+
|| outcomes.integration.render.made_progress
397+
|| outcomes.particle.made_progress;
398+
let blocked_on_background = outcomes.integration.main.blocked_on_background
399+
|| outcomes.integration.high_priority.blocked_on_background
400+
|| outcomes.integration.normal_priority.blocked_on_background
401+
|| outcomes.integration.render.blocked_on_background
402+
|| outcomes.particle.blocked_on_background;
387403
summary.finish(
388404
asset,
389405
DrainFinishState {
@@ -395,6 +411,8 @@ fn finalize_drain(
395411
particle: outcomes.particle.pending,
396412
},
397413
processed,
414+
made_progress,
415+
blocked_on_background,
398416
particle_elapsed: outcomes.particle_elapsed,
399417
elapsed: outcomes.integration_elapsed,
400418
},
@@ -495,6 +513,7 @@ fn drain_particle_asset_tasks(
495513
) -> LaneDrainOutcome {
496514
profiling::scope!("asset::particle_drain");
497515
let particle_gpu = super::step::particle_task_gpu(gpu);
516+
let startable_before = enqueue_startable_particle_uploads(asset);
498517
let ready_before = drain_ready_particle_builds(asset, particle_gpu.as_ref(), particle_deadline);
499518
let queued = drain_lane(
500519
asset,
@@ -506,15 +525,28 @@ fn drain_particle_asset_tasks(
506525
AssetTaskLane::Particle,
507526
);
508527
let ready_after = drain_ready_particle_builds(asset, particle_gpu.as_ref(), particle_deadline);
528+
let startable_after = enqueue_startable_particle_uploads(asset);
509529
LaneDrainOutcome {
510530
pending: ready_before.pending
511531
|| queued.pending
512532
|| ready_after.pending
513533
|| asset.has_ready_particle_build_results(),
534+
blocked_on_background: queued.blocked_on_background
535+
|| (ready_before.pending && particle_gpu.is_none())
536+
|| (ready_after.pending && particle_gpu.is_none())
537+
|| startable_before.pending
538+
|| startable_after.pending,
539+
made_progress: queued.made_progress
540+
|| ready_before.processed > 0
541+
|| ready_after.processed > 0
542+
|| startable_before.enqueued > 0
543+
|| startable_after.enqueued > 0,
514544
processed: ready_before
515545
.processed
546+
.saturating_add(startable_before.enqueued)
516547
.saturating_add(queued.processed)
517-
.saturating_add(ready_after.processed),
548+
.saturating_add(ready_after.processed)
549+
.saturating_add(startable_after.enqueued),
518550
}
519551
}
520552

@@ -535,13 +567,16 @@ fn drain_lane(
535567
let mut yielded: usize = 0;
536568
let mut iter_count: u32 = 0;
537569
let mut processed: u32 = 0;
570+
let mut made_progress = false;
538571
loop {
539572
// Coarse deadline check: every `DEADLINE_POLL_STRIDE` iterations rather than every
540573
// iteration, so cheap task steps (e.g. texture mip progression) do not pay the
541574
// `Instant::now` syscall on every pop.
542575
if iter_count.is_multiple_of(DEADLINE_POLL_STRIDE) && Instant::now() >= deadline {
543576
return LaneDrainOutcome {
544577
pending: !asset.integrator.lane_is_empty(lane),
578+
blocked_on_background: false,
579+
made_progress,
545580
processed,
546581
};
547582
}
@@ -550,6 +585,8 @@ fn drain_lane(
550585
let Some(mut task) = task_opt else {
551586
return LaneDrainOutcome {
552587
pending: false,
588+
blocked_on_background: false,
589+
made_progress,
553590
processed,
554591
};
555592
};
@@ -559,6 +596,7 @@ fn drain_lane(
559596
StepResult::Continue => {
560597
asset.integrator.push_front_lane(task, lane);
561598
yielded = 0;
599+
made_progress = true;
562600
}
563601
StepResult::YieldBackground => {
564602
asset.integrator.push_back_lane(task, lane);
@@ -567,12 +605,15 @@ fn drain_lane(
567605
if yielded >= lane_len {
568606
return LaneDrainOutcome {
569607
pending: false,
608+
blocked_on_background: lane_len > 0,
609+
made_progress,
570610
processed,
571611
};
572612
}
573613
}
574614
StepResult::Done => {
575615
yielded = 0;
616+
made_progress = true;
576617
}
577618
}
578619
}

crates/renderide/src/backend/asset_transfers/integrator/summary.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ pub struct AssetIntegrationDrainSummary {
4949
pub render_budget_exhausted: bool,
5050
/// Whether particle-lane work exceeded its separate post-main budget.
5151
pub particle_budget_exhausted: bool,
52+
/// Whether at least one task made useful forward progress during the drain.
53+
pub made_progress: bool,
54+
/// Whether queued work remains but every runnable task is waiting on background/GPU state.
55+
pub blocked_on_background: bool,
5256
/// Wall-clock time spent in non-particle integration lanes.
5357
pub elapsed: Duration,
5458
/// Wall-clock time spent in the particle lane.
@@ -88,6 +92,8 @@ impl AssetIntegrationDrainSummary {
8892
self.processed_normal_priority_tasks = finish.processed.normal_priority;
8993
self.processed_render_tasks = finish.processed.render;
9094
self.processed_particle_tasks = finish.processed.particle;
95+
self.made_progress = finish.made_progress;
96+
self.blocked_on_background = finish.blocked_on_background;
9197
self.elapsed = finish.elapsed;
9298
self.particle_elapsed = finish.particle_elapsed;
9399
self.peak_queued = asset.integrator.peak_queued();
@@ -136,6 +142,8 @@ pub(super) struct DrainFinishState {
136142
pub(super) gpu_ready: bool,
137143
pub(super) budgets: BudgetExhaustion,
138144
pub(super) processed: ProcessedLaneCounts,
145+
pub(super) made_progress: bool,
146+
pub(super) blocked_on_background: bool,
139147
pub(super) particle_elapsed: Duration,
140148
pub(super) elapsed: Duration,
141149
}

crates/renderide/src/backend/asset_transfers/integrator/video_poll.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@ pub(super) fn poll_video_texture_events(
1717
) {
1818
// Idle scenes never instantiate a video player; skip the `mem::take` swap and the inner
1919
// profiling scope entirely so the tracy timeline doesn't carry a zero-work zone every frame.
20-
if asset.video.video_players.is_empty() {
20+
if asset.video.video_players.is_empty() && asset.video.retiring_player_count() == 0 {
2121
return;
2222
}
2323
profiling::scope!("asset::video_texture_poll_events");
24+
asset.video.poll_retiring_players();
25+
if asset.video.video_players.is_empty() {
26+
return;
27+
}
2428
let mut video_textures = std::mem::take(&mut asset.video.video_players);
2529
{
2630
profiling::scope!("video::sample_clock_errors");

0 commit comments

Comments
 (0)