From c2491724f5621b79007819a5c4110260e1011b11 Mon Sep 17 00:00:00 2001 From: Lean Mendoza Date: Wed, 24 Jan 2024 15:06:11 -0300 Subject: [PATCH] feat: implement deeper throttling for `Transform`, `MeshRenderer` and `GltfContainer` (#177) * feat: implement deeper throttling for `Transform`, `MeshRenderer` and `GltfContainer` * set times that make senses --- .../decentraland_components/gltf_container.gd | 36 ++------ .../src/dcl/crdt/mod.rs | 19 +++- .../src/godot_classes/dcl_gltf_container.rs | 4 + .../scene_runner/components/gltf_container.rs | 89 ++++++++++++++----- .../scene_runner/components/mesh_renderer.rs | 37 +++++++- .../components/transform_and_parent.rs | 44 ++++++++- .../src/scene_runner/components/tween.rs | 10 +-- .../scene_runner/components/ui/scene_ui.rs | 6 +- .../src/scene_runner/scene.rs | 72 ++++++++------- .../src/scene_runner/scene_manager.rs | 14 +-- .../src/scene_runner/update_scene.rs | 33 ++++--- 11 files changed, 245 insertions(+), 119 deletions(-) diff --git a/godot/src/decentraland_components/gltf_container.gd b/godot/src/decentraland_components/gltf_container.gd index df50a21f..5faa02e5 100644 --- a/godot/src/decentraland_components/gltf_container.gd +++ b/godot/src/decentraland_components/gltf_container.gd @@ -8,9 +8,6 @@ enum GltfContainerLoadingState { FINISHED = 4, } -var file_hash: String = "" -var gltf_node = null - func _ready(): self.async_load_gltf.call_deferred() @@ -20,9 +17,7 @@ func async_load_gltf(): var content_mapping := Global.scene_runner.get_scene_content_mapping(dcl_scene_id) self.dcl_gltf_src = dcl_gltf_src.to_lower() - self.file_hash = content_mapping.get_hash(dcl_gltf_src) - - if self.file_hash.is_empty(): + if content_mapping.get_hash(dcl_gltf_src).is_empty(): dcl_gltf_loading_state = GltfContainerLoadingState.NOT_FOUND return @@ -50,26 +45,13 @@ func async_load_gltf(): printerr("Error on fetch gltf: ", res_instance.get_error()) return - self.async_deferred_add_child.call_deferred(res_instance) - - -func _async_on_gltf_loaded(): - var node = Global.content_provider.get_gltf_from_hash(file_hash) - if node == null: - dcl_gltf_loading_state = GltfContainerLoadingState.FINISHED_WITH_ERROR - return - - var promise: Promise = Global.content_provider.instance_gltf_colliders( - node, dcl_visible_cmask, dcl_invisible_cmask, dcl_scene_id, dcl_entity_id - ) - - await PromiseUtils.async_awaiter(promise) + dcl_pending_node = res_instance - gltf_node = promise.get_data() - self.async_deferred_add_child.call_deferred(gltf_node) +func async_deferred_add_child(): + var new_gltf_node = dcl_pending_node + dcl_pending_node = null -func async_deferred_add_child(new_gltf_node): # Corner case, when the scene is unloaded before the gltf is loaded if not is_inside_tree(): dcl_gltf_loading_state = GltfContainerLoadingState.FINISHED_WITH_ERROR @@ -127,10 +109,10 @@ func change_gltf(new_gltf, visible_meshes_collision_mask, invisible_meshes_colli dcl_visible_cmask = visible_meshes_collision_mask dcl_invisible_cmask = invisible_meshes_collision_mask - if gltf_node != null: + if get_child_count() > 0: + var gltf_node = get_child(0) remove_child(gltf_node) gltf_node.queue_free() - gltf_node = null self.async_load_gltf.call_deferred() else: @@ -139,8 +121,8 @@ func change_gltf(new_gltf, visible_meshes_collision_mask, invisible_meshes_colli visible_meshes_collision_mask != dcl_visible_cmask or invisible_meshes_collision_mask != dcl_invisible_cmask ) - and gltf_node != null + and get_child_count() > 0 ): dcl_visible_cmask = visible_meshes_collision_mask dcl_invisible_cmask = invisible_meshes_collision_mask - update_mask_colliders(gltf_node) + update_mask_colliders(get_child(0)) diff --git a/rust/decentraland-godot-lib/src/dcl/crdt/mod.rs b/rust/decentraland-godot-lib/src/dcl/crdt/mod.rs index eb8808bc..ccabeb3f 100644 --- a/rust/decentraland-godot-lib/src/dcl/crdt/mod.rs +++ b/rust/decentraland-godot-lib/src/dcl/crdt/mod.rs @@ -25,7 +25,22 @@ pub struct SceneCrdtState { pub entities: SceneEntityContainer, } -pub type DirtyLwwComponents = HashMap>; +pub trait InsertIfNotExists { + fn insert_if_not_exists(&mut self, value: T) -> bool; +} + +impl InsertIfNotExists for Vec { + fn insert_if_not_exists(&mut self, value: T) -> bool { + if !self.contains(&value) { + self.push(value); + true + } else { + false + } + } +} + +pub type DirtyLwwComponents = HashMap>; pub type DirtyGosComponents = HashMap>; // message from scene-thread describing new and deleted entities @@ -221,7 +236,7 @@ impl SceneCrdtState { } if !dirty.is_empty() { - dirty_lww_components.insert(*component_id, dirty); + dirty_lww_components.insert(*component_id, dirty.into_iter().collect()); } } } diff --git a/rust/decentraland-godot-lib/src/godot_classes/dcl_gltf_container.rs b/rust/decentraland-godot-lib/src/godot_classes/dcl_gltf_container.rs index 01eb3449..8b01dbd2 100644 --- a/rust/decentraland-godot-lib/src/godot_classes/dcl_gltf_container.rs +++ b/rust/decentraland-godot-lib/src/godot_classes/dcl_gltf_container.rs @@ -87,6 +87,9 @@ pub struct DclGltfContainer { #[export(enum = (Unknown, Loading, NotFound, FinishedWithError, Finished))] dcl_gltf_loading_state: GltfContainerLoadingState, + #[export] + dcl_pending_node: Option>, + #[base] base: Base, } @@ -151,6 +154,7 @@ impl INode for DclGltfContainer { dcl_invisible_cmask: 3, dcl_entity_id: SceneEntityId::INVALID.as_i32(), dcl_gltf_loading_state: GltfContainerLoadingState::Unknown, + dcl_pending_node: None, base, } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/gltf_container.rs b/rust/decentraland-godot-lib/src/scene_runner/components/gltf_container.rs index de9a001c..73e01c7a 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/gltf_container.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/gltf_container.rs @@ -1,3 +1,5 @@ +use std::time::Instant; + use crate::{ dcl::{ components::SceneComponentId, @@ -11,15 +13,25 @@ use crate::{ }; use godot::prelude::*; -pub fn update_gltf_container(scene: &mut Scene, crdt_state: &mut SceneCrdtState) { +pub fn update_gltf_container( + scene: &mut Scene, + crdt_state: &mut SceneCrdtState, + ref_time: &Instant, + end_time_us: i64, +) -> bool { + let mut updated_count = 0; + let mut current_time_us; + let godot_dcl_scene = &mut scene.godot_dcl_scene; - let dirty_lww_components = &scene.current_dirty.lww_components; + let gltf_container_dirty = scene + .current_dirty + .lww_components + .remove(&SceneComponentId::GLTF_CONTAINER); let scene_id = scene.scene_id.0; let gltf_container_component = SceneCrdtStateProtoComponents::get_gltf_container(crdt_state); - if let Some(gltf_container_dirty) = dirty_lww_components.get(&SceneComponentId::GLTF_CONTAINER) - { - for entity in gltf_container_dirty { + if let Some(mut gltf_container_dirty) = gltf_container_dirty { + for entity in gltf_container_dirty.iter() { let new_value = gltf_container_component.get(entity); if new_value.is_none() { continue; @@ -43,16 +55,17 @@ pub fn update_gltf_container(scene: &mut Scene, crdt_state: &mut SceneCrdtState) new_value.invisible_meshes_collision_mask.unwrap_or(3) as i32; if let Some(mut gltf_node) = existing { - gltf_node.call_deferred( - StringName::from(GString::from("change_gltf")), + gltf_node.call( + "change_gltf".into(), &[ - Variant::from(GString::from(new_value.src)), - Variant::from(visible_meshes_collision_mask), - Variant::from(invisible_meshes_collision_mask), + new_value.src.to_variant(), + visible_meshes_collision_mask.to_variant(), + invisible_meshes_collision_mask.to_variant(), ], ); scene.gltf_loading.insert(*entity); } else { + // TODO: preload this resource let mut new_gltf = godot::engine::load::( "res://src/decentraland_components/gltf_container.tscn", ) @@ -60,17 +73,13 @@ pub fn update_gltf_container(scene: &mut Scene, crdt_state: &mut SceneCrdtState) .unwrap() .cast::(); - new_gltf - .bind_mut() - .set_dcl_gltf_src(GString::from(new_value.src)); - new_gltf.bind_mut().set_dcl_scene_id(scene_id); - new_gltf.bind_mut().set_dcl_entity_id(entity.as_i32()); - new_gltf - .bind_mut() - .set_dcl_visible_cmask(visible_meshes_collision_mask); - new_gltf - .bind_mut() - .set_dcl_invisible_cmask(invisible_meshes_collision_mask); + let mut new_gltf_ref = new_gltf.bind_mut(); + new_gltf_ref.set_dcl_gltf_src(GString::from(new_value.src)); + new_gltf_ref.set_dcl_scene_id(scene_id); + new_gltf_ref.set_dcl_entity_id(entity.as_i32()); + new_gltf_ref.set_dcl_visible_cmask(visible_meshes_collision_mask); + new_gltf_ref.set_dcl_invisible_cmask(invisible_meshes_collision_mask); + drop(new_gltf_ref); new_gltf.set_name(GString::from("GltfContainer")); node_3d.add_child(new_gltf.clone().upcast()); @@ -78,11 +87,34 @@ pub fn update_gltf_container(scene: &mut Scene, crdt_state: &mut SceneCrdtState) scene.gltf_loading.insert(*entity); } } + + updated_count += 1; + current_time_us = (std::time::Instant::now() - *ref_time).as_micros() as i64; + if current_time_us > end_time_us { + break; + } + } + + if updated_count < gltf_container_dirty.len() { + gltf_container_dirty.drain(0..updated_count); + scene + .current_dirty + .lww_components + .insert(SceneComponentId::GLTF_CONTAINER, gltf_container_dirty); + return false; } } + + true } -pub fn sync_gltf_loading_state(scene: &mut Scene, crdt_state: &mut SceneCrdtState) { +pub fn sync_gltf_loading_state( + scene: &mut Scene, + crdt_state: &mut SceneCrdtState, + ref_time: &Instant, + end_time_us: i64, +) -> bool { + let mut current_time_us; let godot_dcl_scene = &mut scene.godot_dcl_scene; let gltf_container_loading_state_component = SceneCrdtStateProtoComponents::get_gltf_container_loading_state_mut(crdt_state); @@ -93,6 +125,12 @@ pub fn sync_gltf_loading_state(scene: &mut Scene, crdt_state: &mut SceneCrdtStat .1 .try_get_node_as::(NodePath::from("GltfContainer")); + if let Some(mut gltf_node) = gltf_node.clone() { + if gltf_node.bind().get_dcl_pending_node().is_some() { + gltf_node.call("async_deferred_add_child".into(), &[]); + } + } + let current_state = match gltf_container_loading_state_component.get(entity) { Some(state) => match state.value.as_ref() { Some(value) => GltfContainerLoadingState::from_proto(value.current_state()), @@ -118,5 +156,12 @@ pub fn sync_gltf_loading_state(scene: &mut Scene, crdt_state: &mut SceneCrdtStat { scene.gltf_loading.remove(entity); } + + current_time_us = (std::time::Instant::now() - *ref_time).as_micros() as i64; + if current_time_us > end_time_us { + return false; + } } + + true } diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/mesh_renderer.rs b/rust/decentraland-godot-lib/src/scene_runner/components/mesh_renderer.rs index b67760eb..50cfafb6 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/mesh_renderer.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/mesh_renderer.rs @@ -1,3 +1,5 @@ +use std::time::Instant; + use crate::{ dcl::{ components::{ @@ -63,13 +65,24 @@ pub fn create_or_update_mesh(mesh_instance: &mut Gd, mesh: &PbMe } } -pub fn update_mesh_renderer(scene: &mut Scene, crdt_state: &mut SceneCrdtState) { +pub fn update_mesh_renderer( + scene: &mut Scene, + crdt_state: &mut SceneCrdtState, + ref_time: &Instant, + end_time_us: i64, +) -> bool { + let mut updated_count = 0; + let mut current_time_us; let godot_dcl_scene = &mut scene.godot_dcl_scene; - let dirty_lww_components = &scene.current_dirty.lww_components; - if let Some(mesh_renderer_dirty) = dirty_lww_components.get(&SceneComponentId::MESH_RENDERER) { + let mesh_renderer_dirty = scene + .current_dirty + .lww_components + .remove(&SceneComponentId::MESH_RENDERER); + + if let Some(mut mesh_renderer_dirty) = mesh_renderer_dirty { let mesh_renderer_component = SceneCrdtStateProtoComponents::get_mesh_renderer(crdt_state); - for entity in mesh_renderer_dirty { + for entity in mesh_renderer_dirty.iter() { let new_value = mesh_renderer_component.get(entity); if new_value.is_none() { continue; @@ -107,6 +120,22 @@ pub fn update_mesh_renderer(scene: &mut Scene, crdt_state: &mut SceneCrdtState) node_3d.add_child(mesh_instance_3d.upcast()); } } + updated_count += 1; + current_time_us = (std::time::Instant::now() - *ref_time).as_micros() as i64; + if current_time_us > end_time_us { + break; + } + } + + if updated_count < mesh_renderer_dirty.len() { + mesh_renderer_dirty.drain(0..updated_count); + scene + .current_dirty + .lww_components + .insert(SceneComponentId::MESH_RENDERER, mesh_renderer_dirty); + return false; } } + + true } diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/transform_and_parent.rs b/rust/decentraland-godot-lib/src/scene_runner/components/transform_and_parent.rs index 1ca46e43..4f475e0b 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/transform_and_parent.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/transform_and_parent.rs @@ -1,3 +1,5 @@ +use std::time::Instant; + use godot::{ builtin::math::FloatExt, prelude::{Node, Transform3D, Vector3}, @@ -37,13 +39,23 @@ impl DclTransformAndParent { } } -pub fn update_transform_and_parent(scene: &mut Scene, crdt_state: &mut SceneCrdtState) { +pub fn update_transform_and_parent( + scene: &mut Scene, + crdt_state: &mut SceneCrdtState, + ref_time: &Instant, + end_time_us: i64, +) -> bool { + let mut current_time_us; let godot_dcl_scene = &mut scene.godot_dcl_scene; - let dirty_lww_components = &scene.current_dirty.lww_components; + let dirty_transform = scene + .current_dirty + .lww_components + .remove(&SceneComponentId::TRANSFORM); let transform_component = crdt_state.get_transform(); - if let Some(dirty_transform) = dirty_lww_components.get(&SceneComponentId::TRANSFORM) { - for entity in dirty_transform { + if let Some(mut dirty_transform) = dirty_transform { + let mut updated_count = 0; + for entity in dirty_transform.iter() { let value = if let Some(entry) = transform_component.get(entity) { entry.value.clone() } else { @@ -78,6 +90,23 @@ pub fn update_transform_and_parent(scene: &mut Scene, crdt_state: &mut SceneCrdt godot_dcl_scene.unparented_entities_3d.insert(*entity); godot_dcl_scene.hierarchy_dirty_3d = true; } + + updated_count += 1; + if updated_count % 10 == 0 { + current_time_us = (std::time::Instant::now() - *ref_time).as_micros() as i64; + if current_time_us > end_time_us { + break; + } + } + } + + if updated_count < dirty_transform.len() { + dirty_transform.drain(0..updated_count); + scene + .current_dirty + .lww_components + .insert(SceneComponentId::TRANSFORM, dirty_transform); + return false; } } @@ -141,7 +170,14 @@ pub fn update_transform_and_parent(scene: &mut Scene, crdt_state: &mut SceneCrdt } } } + + current_time_us = (std::time::Instant::now() - *ref_time).as_micros() as i64; + if current_time_us > end_time_us { + return false; + } } + + true } fn detect_entity_id_in_parent_chain( diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/tween.rs b/rust/decentraland-godot-lib/src/scene_runner/components/tween.rs index 3e6d8130..3bc3c089 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/tween.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/tween.rs @@ -1,4 +1,4 @@ -use std::{collections::HashSet, time::Duration}; +use std::time::Duration; use godot::builtin::Basis; @@ -12,7 +12,7 @@ use crate::{ SceneComponentId, }, crdt::{ - last_write_wins::LastWriteWinsComponentOperation, SceneCrdtState, + last_write_wins::LastWriteWinsComponentOperation, InsertIfNotExists, SceneCrdtState, SceneCrdtStateProtoComponents, }, }, @@ -269,14 +269,12 @@ pub fn update_tween(scene: &mut Scene, crdt_state: &mut SceneCrdtState) { .lww_components .get_mut(&SceneComponentId::TRANSFORM) { - dirty.insert(*entity); + dirty.insert_if_not_exists(*entity); } else { - let mut new_dirty = HashSet::new(); - new_dirty.insert(*entity); scene .current_dirty .lww_components - .insert(SceneComponentId::TRANSFORM, new_dirty.clone()); + .insert(SceneComponentId::TRANSFORM, vec![*entity]); } } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/components/ui/scene_ui.rs b/rust/decentraland-godot-lib/src/scene_runner/components/ui/scene_ui.rs index 8d214e75..cc4726e2 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/components/ui/scene_ui.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/components/ui/scene_ui.rs @@ -13,7 +13,7 @@ use crate::{ SceneComponentId, SceneEntityId, }, crdt::{ - last_write_wins::LastWriteWinsComponentOperation, SceneCrdtState, + last_write_wins::LastWriteWinsComponentOperation, InsertIfNotExists, SceneCrdtState, SceneCrdtStateProtoComponents, }, SceneId, @@ -219,11 +219,11 @@ pub fn update_scene_ui( .current_dirty .lww_components .entry(component_id) - .or_insert(HashSet::with_capacity(hidden_dirty.len())); + .or_insert(Vec::with_capacity(hidden_dirty.len())); hidden_dirty.iter().for_each(|entity_id| { if !crdt_state.entities.is_dead(entity_id) { - dirty.insert(*entity_id); + dirty.insert_if_not_exists(*entity_id); } }); } diff --git a/rust/decentraland-godot-lib/src/scene_runner/scene.rs b/rust/decentraland-godot-lib/src/scene_runner/scene.rs index 868f605b..8b97ff8d 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/scene.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/scene.rs @@ -56,7 +56,13 @@ pub struct MaterialItem { pub alive: bool, } -#[derive(Clone, Copy, Debug)] +#[derive(Debug)] +pub struct PartialIteratorState { + pub current_index: usize, + pub items: Vec, +} + +#[derive(Debug)] pub enum SceneUpdateState { None, PrintLogs, @@ -71,6 +77,7 @@ pub enum SceneUpdateState { Billboard, MeshCollider, GltfContainer, + SyncGltfContainer, NftShape, Animator, AvatarShape, @@ -91,41 +98,42 @@ pub enum SceneUpdateState { } impl SceneUpdateState { - pub fn next(self) -> Self { - match self { - Self::None => Self::PrintLogs, - Self::PrintLogs => Self::DeletedEntities, - Self::DeletedEntities => Self::Tween, - Self::Tween => Self::TransformAndParent, - Self::TransformAndParent => Self::VisibilityComponent, - Self::VisibilityComponent => Self::MeshRenderer, - Self::MeshRenderer => Self::ScenePointerEvents, - Self::ScenePointerEvents => Self::Material, - Self::Material => Self::TextShape, - Self::TextShape => Self::Billboard, - Self::Billboard => Self::MeshCollider, - Self::MeshCollider => Self::GltfContainer, - Self::GltfContainer => Self::NftShape, - Self::NftShape => Self::Animator, - Self::Animator => Self::AvatarShape, - Self::AvatarShape => Self::Raycasts, + pub fn next(&self) -> Self { + match &self { + &Self::None => Self::PrintLogs, + &Self::PrintLogs => Self::DeletedEntities, + &Self::DeletedEntities => Self::Tween, + &Self::Tween => Self::TransformAndParent, + &Self::TransformAndParent => Self::VisibilityComponent, + &Self::VisibilityComponent => Self::MeshRenderer, + &Self::MeshRenderer => Self::ScenePointerEvents, + &Self::ScenePointerEvents => Self::Material, + &Self::Material => Self::TextShape, + &Self::TextShape => Self::Billboard, + &Self::Billboard => Self::MeshCollider, + &Self::MeshCollider => Self::GltfContainer, + &Self::GltfContainer => Self::SyncGltfContainer, + &Self::SyncGltfContainer => Self::NftShape, + &Self::NftShape => Self::Animator, + &Self::Animator => Self::AvatarShape, + &Self::AvatarShape => Self::Raycasts, #[cfg(feature = "use_ffmpeg")] - Self::Raycasts => Self::VideoPlayer, + &Self::Raycasts => Self::VideoPlayer, #[cfg(feature = "use_ffmpeg")] - Self::VideoPlayer => Self::AudioStream, + &Self::VideoPlayer => Self::AudioStream, #[cfg(feature = "use_ffmpeg")] - Self::AudioStream => Self::AvatarModifierArea, + &Self::AudioStream => Self::AvatarModifierArea, #[cfg(not(feature = "use_ffmpeg"))] - Self::Raycasts => Self::AvatarModifierArea, - Self::AvatarModifierArea => Self::CameraModeArea, - Self::CameraModeArea => Self::AudioSource, - Self::AudioSource => Self::AvatarAttach, - Self::AvatarAttach => Self::SceneUi, - Self::SceneUi => Self::ProcessRpcs, - Self::ProcessRpcs => Self::ComputeCrdtState, - Self::ComputeCrdtState => Self::SendToThread, - Self::SendToThread => Self::Processed, - Self::Processed => Self::Processed, + &Self::Raycasts => Self::AvatarModifierArea, + &Self::AvatarModifierArea => Self::CameraModeArea, + &Self::CameraModeArea => Self::AudioSource, + &Self::AudioSource => Self::AvatarAttach, + &Self::AvatarAttach => Self::SceneUi, + &Self::SceneUi => Self::ProcessRpcs, + &Self::ProcessRpcs => Self::ComputeCrdtState, + &Self::ComputeCrdtState => Self::SendToThread, + &Self::SendToThread => Self::Processed, + &Self::Processed => Self::Processed, } } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs b/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs index 3f45ba1a..96b02bfb 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/scene_manager.rs @@ -90,6 +90,9 @@ pub struct SceneManager { // This value is the current global tick number, is used for marking the cronolgy of lamport timestamp pub static GLOBAL_TICK_NUMBER: AtomicU32 = AtomicU32::new(0); +const MAX_TIME_PER_SCENE_TICK_US: i64 = 8333; // 50% of update time at 60fps +const MIN_TIME_TO_PROCESS_SCENE_US: i64 = 2083; // 25% of max_time_per_scene_tick_us (4 scenes per frame) + #[godot_api] impl SceneManager { #[signal] @@ -265,7 +268,7 @@ impl SceneManager { } let start_time_us = (std::time::Instant::now() - self.begin_time).as_micros() as i64; - let end_time_us = start_time_us + 1000; + let end_time_us = start_time_us + MAX_TIME_PER_SCENE_TICK_US; self.total_time_seconds_time += delta as f32; @@ -337,6 +340,9 @@ impl SceneManager { if scene.next_tick_us > current_time_us { break; } + if (end_time_us - current_time_us) < MIN_TIME_TO_PROCESS_SCENE_US { + break; + } if let SceneState::Alive = scene.state { if scene.dcl_scene.thread_join_handle.is_finished() { @@ -357,12 +363,8 @@ impl SceneManager { &self.begin_time, &self.ui_canvas_information, ) { - current_time_us = + scene.last_tick_us = (std::time::Instant::now() - self.begin_time).as_micros() as i64; - scene.last_tick_us = current_time_us; - if current_time_us > end_time_us { - break; - } } } } diff --git a/rust/decentraland-godot-lib/src/scene_runner/update_scene.rs b/rust/decentraland-godot-lib/src/scene_runner/update_scene.rs index dd65f5f7..744c6dd2 100644 --- a/rust/decentraland-godot-lib/src/scene_runner/update_scene.rs +++ b/rust/decentraland-godot-lib/src/scene_runner/update_scene.rs @@ -72,6 +72,8 @@ pub fn _process_scene( let mut current_time_us; loop { + let before_compute_update = std::time::Instant::now(); + let should_break = match scene.current_dirty.update_state { SceneUpdateState::None => { let engine_info_component = @@ -92,7 +94,7 @@ pub fn _process_scene( // fix: if the scene is loading, we need to wait until it finishes before spawn the next tick // tick 0 => onStart() => tick=1 => first onUpdate() => tick=2 => second onUpdate() => tick= 3 if tick_number < 3 && !scene.gltf_loading.is_empty() { - sync_gltf_loading_state(scene, crdt_state); + sync_gltf_loading_state(scene, crdt_state, ref_time, end_time_us); return false; } @@ -158,16 +160,14 @@ pub fn _process_scene( false } SceneUpdateState::TransformAndParent => { - update_transform_and_parent(scene, crdt_state); - false + !update_transform_and_parent(scene, crdt_state, ref_time, end_time_us) } SceneUpdateState::VisibilityComponent => { update_visibility(scene, crdt_state); false } SceneUpdateState::MeshRenderer => { - update_mesh_renderer(scene, crdt_state); - false + !update_mesh_renderer(scene, crdt_state, ref_time, end_time_us) } SceneUpdateState::ScenePointerEvents => { update_scene_pointer_events(scene, crdt_state); @@ -190,9 +190,10 @@ pub fn _process_scene( false } SceneUpdateState::GltfContainer => { - update_gltf_container(scene, crdt_state); - sync_gltf_loading_state(scene, crdt_state); - false + !update_gltf_container(scene, crdt_state, ref_time, end_time_us) + } + SceneUpdateState::SyncGltfContainer => { + !sync_gltf_loading_state(scene, crdt_state, ref_time, end_time_us) } SceneUpdateState::NftShape => { update_nft_shape(scene, crdt_state); @@ -355,19 +356,25 @@ pub fn _process_scene( } }; + let this_update_ms = (std::time::Instant::now() - before_compute_update).as_micros() as i64; + if this_update_ms > 5000 { + println!( + "metric:\"{:?}\",{:?},{:?},{:?}", + scene.definition.title, + scene.tick_number, + scene.current_dirty.update_state, + this_update_ms + ); + } + if should_break { return false; } - // let prev = scene.current_dirty.update_state; scene.current_dirty.update_state = scene.current_dirty.update_state.next(); current_time_us = (std::time::Instant::now() - *ref_time).as_micros() as i64; if current_time_us > end_time_us { - // let diff = current_time_us - end_time_us; - // if diff > 3000 { - // println!("exceed time limit by {:?} in the state {:?}", diff, prev); - // } return false; } }