[PERF] Async Background Work Lanes for Assets and FS
This commit is contained in:
parent
0adec90f23
commit
b84946a3a0
@ -359,6 +359,16 @@ struct AssetLoadJob {
|
|||||||
entry: AssetEntry,
|
entry: AssetEntry,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct AssetLoadJobResources<'a> {
|
||||||
|
handles: &'a Arc<RwLock<HandleTable>>,
|
||||||
|
target_generations: &'a Arc<RwLock<TargetGenerationTable>>,
|
||||||
|
assets_data: &'a Arc<RwLock<AssetsPayloadSource>>,
|
||||||
|
pipeline_telemetry: &'a Arc<Mutex<AssetPipelineTelemetryState>>,
|
||||||
|
gfx_policy: &'a BankPolicy<GlyphBank>,
|
||||||
|
sound_policy: &'a BankPolicy<SoundBank>,
|
||||||
|
scene_policy: &'a BankPolicy<SceneBank>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct AssetLoadQueueState {
|
struct AssetLoadQueueState {
|
||||||
pending: VecDeque<AssetLoadJob>,
|
pending: VecDeque<AssetLoadJob>,
|
||||||
@ -475,17 +485,17 @@ impl AssetLoadWorker {
|
|||||||
let queue = Arc::new(AssetLoadQueue::default());
|
let queue = Arc::new(AssetLoadQueue::default());
|
||||||
let worker_queue = Arc::clone(&queue);
|
let worker_queue = Arc::clone(&queue);
|
||||||
let join_handle = thread::spawn(move || {
|
let join_handle = thread::spawn(move || {
|
||||||
|
let resources = AssetLoadJobResources {
|
||||||
|
handles: &handles,
|
||||||
|
target_generations: &target_generations,
|
||||||
|
assets_data: &assets_data,
|
||||||
|
pipeline_telemetry: &pipeline_telemetry,
|
||||||
|
gfx_policy: &gfx_policy,
|
||||||
|
sound_policy: &sound_policy,
|
||||||
|
scene_policy: &scene_policy,
|
||||||
|
};
|
||||||
while let Some(job) = worker_queue.next_job() {
|
while let Some(job) = worker_queue.next_job() {
|
||||||
AssetManager::process_load_job(
|
AssetManager::process_load_job(job.clone(), &resources);
|
||||||
job.clone(),
|
|
||||||
&handles,
|
|
||||||
&target_generations,
|
|
||||||
&assets_data,
|
|
||||||
&pipeline_telemetry,
|
|
||||||
&gfx_policy,
|
|
||||||
&sound_policy,
|
|
||||||
&scene_policy,
|
|
||||||
);
|
|
||||||
worker_queue.complete_active(job.handle_id, job.request_generation);
|
worker_queue.complete_active(job.handle_id, job.request_generation);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -942,24 +952,18 @@ impl AssetManager {
|
|||||||
Ok(handle_id)
|
Ok(handle_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_load_job(
|
fn process_load_job(job: AssetLoadJob, resources: &AssetLoadJobResources<'_>) {
|
||||||
job: AssetLoadJob,
|
if !Self::is_current_target_generation(
|
||||||
handles: &Arc<RwLock<HandleTable>>,
|
resources.target_generations,
|
||||||
target_generations: &Arc<RwLock<TargetGenerationTable>>,
|
job.slot,
|
||||||
assets_data: &Arc<RwLock<AssetsPayloadSource>>,
|
job.request_generation,
|
||||||
pipeline_telemetry: &Arc<Mutex<AssetPipelineTelemetryState>>,
|
|
||||||
gfx_policy: &BankPolicy<GlyphBank>,
|
|
||||||
sound_policy: &BankPolicy<SoundBank>,
|
|
||||||
scene_policy: &BankPolicy<SceneBank>,
|
|
||||||
) {
|
) {
|
||||||
if !Self::is_current_target_generation(target_generations, job.slot, job.request_generation)
|
resources.pipeline_telemetry.lock().unwrap().record_stale_discard();
|
||||||
{
|
|
||||||
pipeline_telemetry.lock().unwrap().record_stale_discard();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut handles_map = handles.write().unwrap();
|
let mut handles_map = resources.handles.write().unwrap();
|
||||||
let Some(handle) = handles_map.get_mut(&job.handle_id) else {
|
let Some(handle) = handles_map.get_mut(&job.handle_id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -972,52 +976,53 @@ impl AssetManager {
|
|||||||
handle.progress = ASSET_PROGRESS_LOADING;
|
handle.progress = ASSET_PROGRESS_LOADING;
|
||||||
}
|
}
|
||||||
let started_at = Instant::now();
|
let started_at = Instant::now();
|
||||||
pipeline_telemetry.lock().unwrap().record_started(0);
|
resources.pipeline_telemetry.lock().unwrap().record_started(0);
|
||||||
|
|
||||||
match job.entry.bank_type {
|
match job.entry.bank_type {
|
||||||
BankType::GLYPH => {
|
BankType::GLYPH => {
|
||||||
let result = Self::perform_load_glyph_bank(&job.entry, Arc::clone(assets_data));
|
let result =
|
||||||
|
Self::perform_load_glyph_bank(&job.entry, Arc::clone(resources.assets_data));
|
||||||
match result {
|
match result {
|
||||||
Ok(tilebank) => {
|
Ok(tilebank) => {
|
||||||
if !Self::is_current_target_generation(
|
if !Self::is_current_target_generation(
|
||||||
target_generations,
|
resources.target_generations,
|
||||||
job.slot,
|
job.slot,
|
||||||
job.request_generation,
|
job.request_generation,
|
||||||
) {
|
) {
|
||||||
pipeline_telemetry.lock().unwrap().record_stale_discard();
|
resources.pipeline_telemetry.lock().unwrap().record_stale_discard();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let bank_arc = Arc::new(tilebank);
|
let bank_arc = Arc::new(tilebank);
|
||||||
let resident_arc = gfx_policy.put_resident(
|
let resident_arc = resources.gfx_policy.put_resident(
|
||||||
job.asset_id,
|
job.asset_id,
|
||||||
bank_arc,
|
bank_arc,
|
||||||
job.entry.decoded_size as usize,
|
job.entry.decoded_size as usize,
|
||||||
);
|
);
|
||||||
if !Self::is_current_target_generation(
|
if !Self::is_current_target_generation(
|
||||||
target_generations,
|
resources.target_generations,
|
||||||
job.slot,
|
job.slot,
|
||||||
job.request_generation,
|
job.request_generation,
|
||||||
) {
|
) {
|
||||||
pipeline_telemetry.lock().unwrap().record_stale_discard();
|
resources.pipeline_telemetry.lock().unwrap().record_stale_discard();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gfx_policy.stage(
|
resources.gfx_policy.stage(
|
||||||
job.handle_id,
|
job.handle_id,
|
||||||
resident_arc,
|
resident_arc,
|
||||||
job.entry.decoded_size as usize,
|
job.entry.decoded_size as usize,
|
||||||
);
|
);
|
||||||
Self::complete_load_job(handles, &job, LoadStatus::READY);
|
Self::complete_load_job(resources.handles, &job, LoadStatus::READY);
|
||||||
Self::record_load_job_finished(
|
Self::record_load_job_finished(
|
||||||
pipeline_telemetry,
|
resources.pipeline_telemetry,
|
||||||
&job,
|
&job,
|
||||||
started_at,
|
started_at,
|
||||||
LoadStatus::READY,
|
LoadStatus::READY,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
Self::complete_load_job(handles, &job, LoadStatus::ERROR);
|
Self::complete_load_job(resources.handles, &job, LoadStatus::ERROR);
|
||||||
Self::record_load_job_finished(
|
Self::record_load_job_finished(
|
||||||
pipeline_telemetry,
|
resources.pipeline_telemetry,
|
||||||
&job,
|
&job,
|
||||||
started_at,
|
started_at,
|
||||||
LoadStatus::ERROR,
|
LoadStatus::ERROR,
|
||||||
@ -1026,48 +1031,49 @@ impl AssetManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
BankType::SOUNDS => {
|
BankType::SOUNDS => {
|
||||||
let result = Self::perform_load_sound_bank(&job.entry, Arc::clone(assets_data));
|
let result =
|
||||||
|
Self::perform_load_sound_bank(&job.entry, Arc::clone(resources.assets_data));
|
||||||
match result {
|
match result {
|
||||||
Ok(soundbank) => {
|
Ok(soundbank) => {
|
||||||
if !Self::is_current_target_generation(
|
if !Self::is_current_target_generation(
|
||||||
target_generations,
|
resources.target_generations,
|
||||||
job.slot,
|
job.slot,
|
||||||
job.request_generation,
|
job.request_generation,
|
||||||
) {
|
) {
|
||||||
pipeline_telemetry.lock().unwrap().record_stale_discard();
|
resources.pipeline_telemetry.lock().unwrap().record_stale_discard();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let bank_arc = Arc::new(soundbank);
|
let bank_arc = Arc::new(soundbank);
|
||||||
let resident_arc = sound_policy.put_resident(
|
let resident_arc = resources.sound_policy.put_resident(
|
||||||
job.asset_id,
|
job.asset_id,
|
||||||
bank_arc,
|
bank_arc,
|
||||||
job.entry.decoded_size as usize,
|
job.entry.decoded_size as usize,
|
||||||
);
|
);
|
||||||
if !Self::is_current_target_generation(
|
if !Self::is_current_target_generation(
|
||||||
target_generations,
|
resources.target_generations,
|
||||||
job.slot,
|
job.slot,
|
||||||
job.request_generation,
|
job.request_generation,
|
||||||
) {
|
) {
|
||||||
pipeline_telemetry.lock().unwrap().record_stale_discard();
|
resources.pipeline_telemetry.lock().unwrap().record_stale_discard();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sound_policy.stage(
|
resources.sound_policy.stage(
|
||||||
job.handle_id,
|
job.handle_id,
|
||||||
resident_arc,
|
resident_arc,
|
||||||
job.entry.decoded_size as usize,
|
job.entry.decoded_size as usize,
|
||||||
);
|
);
|
||||||
Self::complete_load_job(handles, &job, LoadStatus::READY);
|
Self::complete_load_job(resources.handles, &job, LoadStatus::READY);
|
||||||
Self::record_load_job_finished(
|
Self::record_load_job_finished(
|
||||||
pipeline_telemetry,
|
resources.pipeline_telemetry,
|
||||||
&job,
|
&job,
|
||||||
started_at,
|
started_at,
|
||||||
LoadStatus::READY,
|
LoadStatus::READY,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
Self::complete_load_job(handles, &job, LoadStatus::ERROR);
|
Self::complete_load_job(resources.handles, &job, LoadStatus::ERROR);
|
||||||
Self::record_load_job_finished(
|
Self::record_load_job_finished(
|
||||||
pipeline_telemetry,
|
resources.pipeline_telemetry,
|
||||||
&job,
|
&job,
|
||||||
started_at,
|
started_at,
|
||||||
LoadStatus::ERROR,
|
LoadStatus::ERROR,
|
||||||
@ -1076,48 +1082,49 @@ impl AssetManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
BankType::SCENE => {
|
BankType::SCENE => {
|
||||||
let result = Self::perform_load_scene_bank(&job.entry, Arc::clone(assets_data));
|
let result =
|
||||||
|
Self::perform_load_scene_bank(&job.entry, Arc::clone(resources.assets_data));
|
||||||
match result {
|
match result {
|
||||||
Ok(scenebank) => {
|
Ok(scenebank) => {
|
||||||
if !Self::is_current_target_generation(
|
if !Self::is_current_target_generation(
|
||||||
target_generations,
|
resources.target_generations,
|
||||||
job.slot,
|
job.slot,
|
||||||
job.request_generation,
|
job.request_generation,
|
||||||
) {
|
) {
|
||||||
pipeline_telemetry.lock().unwrap().record_stale_discard();
|
resources.pipeline_telemetry.lock().unwrap().record_stale_discard();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let bank_arc = Arc::new(scenebank);
|
let bank_arc = Arc::new(scenebank);
|
||||||
let resident_arc = scene_policy.put_resident(
|
let resident_arc = resources.scene_policy.put_resident(
|
||||||
job.asset_id,
|
job.asset_id,
|
||||||
bank_arc,
|
bank_arc,
|
||||||
job.entry.decoded_size as usize,
|
job.entry.decoded_size as usize,
|
||||||
);
|
);
|
||||||
if !Self::is_current_target_generation(
|
if !Self::is_current_target_generation(
|
||||||
target_generations,
|
resources.target_generations,
|
||||||
job.slot,
|
job.slot,
|
||||||
job.request_generation,
|
job.request_generation,
|
||||||
) {
|
) {
|
||||||
pipeline_telemetry.lock().unwrap().record_stale_discard();
|
resources.pipeline_telemetry.lock().unwrap().record_stale_discard();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
scene_policy.stage(
|
resources.scene_policy.stage(
|
||||||
job.handle_id,
|
job.handle_id,
|
||||||
resident_arc,
|
resident_arc,
|
||||||
job.entry.decoded_size as usize,
|
job.entry.decoded_size as usize,
|
||||||
);
|
);
|
||||||
Self::complete_load_job(handles, &job, LoadStatus::READY);
|
Self::complete_load_job(resources.handles, &job, LoadStatus::READY);
|
||||||
Self::record_load_job_finished(
|
Self::record_load_job_finished(
|
||||||
pipeline_telemetry,
|
resources.pipeline_telemetry,
|
||||||
&job,
|
&job,
|
||||||
started_at,
|
started_at,
|
||||||
LoadStatus::READY,
|
LoadStatus::READY,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
Self::complete_load_job(handles, &job, LoadStatus::ERROR);
|
Self::complete_load_job(resources.handles, &job, LoadStatus::ERROR);
|
||||||
Self::record_load_job_finished(
|
Self::record_load_job_finished(
|
||||||
pipeline_telemetry,
|
resources.pipeline_telemetry,
|
||||||
&job,
|
&job,
|
||||||
started_at,
|
started_at,
|
||||||
LoadStatus::ERROR,
|
LoadStatus::ERROR,
|
||||||
|
|||||||
@ -150,11 +150,11 @@ impl AsyncWorkCancelToken {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn cancel(&self) {
|
pub fn cancel(&self) {
|
||||||
self.canceled.store(true, Ordering::Release);
|
self.canceled.store(true, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_canceled(&self) -> bool {
|
pub fn is_canceled(&self) -> bool {
|
||||||
self.canceled.load(Ordering::Acquire)
|
self.canceled.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,12 +277,14 @@ impl AsyncWorkLane {
|
|||||||
|
|
||||||
pub fn request_cancel(&self, job_id: AsyncWorkJobId) -> bool {
|
pub fn request_cancel(&self, job_id: AsyncWorkJobId) -> bool {
|
||||||
let state = self.state.lock().unwrap();
|
let state = self.state.lock().unwrap();
|
||||||
if state.active.is_some_and(|active| active.id == job_id) {
|
if let Some(token) = state
|
||||||
if let Some(token) = &state.active_cancel_token {
|
.active
|
||||||
|
.filter(|active| active.id == job_id)
|
||||||
|
.and(state.active_cancel_token.as_ref())
|
||||||
|
{
|
||||||
token.cancel();
|
token.cancel();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for pending in &state.pending {
|
for pending in &state.pending {
|
||||||
if pending.id == job_id {
|
if pending.id == job_id {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user