ssbo 视锥剔除优化提升效果一般
This commit is contained in:
parent
f3f8da7b90
commit
86aaa21ddd
@ -1,4 +1,5 @@
|
||||
|
||||
import math
|
||||
from panda3d.core import GeomNode, GeomVertexFormat, GeomVertexWriter
|
||||
from panda3d.core import InternalName, LMatrix4f, NodePath, Vec3
|
||||
import time
|
||||
@ -12,8 +13,9 @@ class ObjectController:
|
||||
- 提交: 离开 chunk 时仅重建该 chunk 的静态表示
|
||||
"""
|
||||
|
||||
def __init__(self, chunk_size=64):
|
||||
def __init__(self, chunk_size=64, chunk_world_size=40.0):
|
||||
self.chunk_size = max(8, int(chunk_size))
|
||||
self.chunk_world_size = max(8.0, float(chunk_world_size))
|
||||
self._reset_state()
|
||||
|
||||
def _reset_state(self):
|
||||
@ -39,6 +41,9 @@ class ObjectController:
|
||||
# }
|
||||
self.chunks = {}
|
||||
self.active_chunks = set()
|
||||
self._next_chunk_id = 0
|
||||
# spatial cell key -> [chunk_id, ...]
|
||||
self._cell_to_chunks = {}
|
||||
|
||||
# UI hierarchy metadata (matches source model parent/child structure)
|
||||
self.tree_root_key = None
|
||||
@ -138,6 +143,33 @@ class ObjectController:
|
||||
self.chunks[chunk_id] = chunk_data
|
||||
return chunk_data
|
||||
|
||||
def _get_cell_key_from_pos(self, pos):
|
||||
inv = 1.0 / self.chunk_world_size
|
||||
return (
|
||||
int(math.floor(pos.x * inv)),
|
||||
int(math.floor(pos.y * inv)),
|
||||
int(math.floor(pos.z * inv)),
|
||||
)
|
||||
|
||||
def _allocate_spatial_chunk(self, root_np, world_pos):
|
||||
"""
|
||||
Allocate object into a spatially-local chunk for better frustum culling.
|
||||
Objects in the same world cell are grouped together, and overflow creates
|
||||
another chunk for that same cell.
|
||||
"""
|
||||
cell_key = self._get_cell_key_from_pos(world_pos)
|
||||
chunk_ids = self._cell_to_chunks.setdefault(cell_key, [])
|
||||
|
||||
for chunk_id in chunk_ids:
|
||||
chunk = self.chunks.get(chunk_id)
|
||||
if chunk and len(chunk["members"]) < self.chunk_size:
|
||||
return chunk_id, chunk
|
||||
|
||||
chunk_id = self._next_chunk_id
|
||||
self._next_chunk_id += 1
|
||||
chunk_ids.append(chunk_id)
|
||||
return chunk_id, self._ensure_chunk(root_np, chunk_id)
|
||||
|
||||
def _rebuild_static_chunk(self, chunk_id):
|
||||
chunk = self.chunks.get(chunk_id)
|
||||
if not chunk:
|
||||
@ -241,8 +273,8 @@ class ObjectController:
|
||||
pick_gnode = GeomNode(f"pick_{global_id}")
|
||||
pick_gnode.add_geom(pick_geom, gnode.get_geom_state(gi))
|
||||
|
||||
chunk_id = global_id // self.chunk_size
|
||||
chunk = self._ensure_chunk(scene_root, chunk_id)
|
||||
world_pos = world_mat.get_row3(3)
|
||||
chunk_id, chunk = self._allocate_spatial_chunk(scene_root, world_pos)
|
||||
|
||||
obj_np = chunk["dynamic_np"].attach_new_node(render_gnode)
|
||||
obj_np.set_mat(world_mat)
|
||||
@ -269,6 +301,7 @@ class ObjectController:
|
||||
t2 = time.time()
|
||||
print(f"[控制器] Static chunk flatten took {(t2 - t1) * 1000:.0f}ms")
|
||||
print(f"[控制器] Built {len(self.chunks)} chunks, {global_id} objects")
|
||||
print(f"[控制器] Spatial chunking: cell={self.chunk_world_size:.1f}, max_members={self.chunk_size}")
|
||||
|
||||
# Fill per-node aggregate IDs and build deterministic preorder list for UI.
|
||||
self._aggregate_tree_ids(self.tree_root_key)
|
||||
|
||||
@ -83,6 +83,11 @@ class SSBOEditor:
|
||||
self._last_group_sync_mat = None
|
||||
self._last_single_sync_gid = None
|
||||
self._last_single_sync_mat = None
|
||||
# Performance toggle: forcing shadow tasks every frame is expensive.
|
||||
# Keep it off by default so frustum/content reduction has clearer FPS impact.
|
||||
self.realtime_shadow_updates = False
|
||||
self._scheduler_tasks_original = None
|
||||
self._realtime_shadow_tasks_enabled = False
|
||||
|
||||
# Initialize ImGui Backend if not already present
|
||||
if not hasattr(self.base, 'imgui_backend'):
|
||||
@ -109,6 +114,13 @@ class SSBOEditor:
|
||||
if model_path:
|
||||
self.load_model(model_path)
|
||||
|
||||
def _capture_scheduler_tasks_snapshot(self):
|
||||
scheduler = getattr(self.rp, "task_scheduler", None)
|
||||
if not scheduler or not hasattr(scheduler, "_tasks"):
|
||||
return
|
||||
if self._scheduler_tasks_original is None:
|
||||
self._scheduler_tasks_original = [list(frame_tasks) for frame_tasks in scheduler._tasks]
|
||||
|
||||
def _enable_realtime_shadow_tasks(self):
|
||||
"""
|
||||
Force PSSM-related scheduled tasks to run every frame to avoid visible
|
||||
@ -118,6 +130,8 @@ class SSBOEditor:
|
||||
if not scheduler or not hasattr(scheduler, "_tasks"):
|
||||
return
|
||||
|
||||
self._capture_scheduler_tasks_snapshot()
|
||||
|
||||
required = {
|
||||
"pssm_scene_shadows",
|
||||
"pssm_distant_shadows",
|
||||
@ -134,6 +148,26 @@ class SSBOEditor:
|
||||
|
||||
if changed:
|
||||
print("[SSBOEditor] Realtime shadow tasks enabled (PSSM updates every frame).")
|
||||
self._realtime_shadow_tasks_enabled = True
|
||||
|
||||
def _disable_realtime_shadow_tasks(self):
|
||||
"""Restore scheduler layout captured before realtime shadow override."""
|
||||
scheduler = getattr(self.rp, "task_scheduler", None)
|
||||
if not scheduler or not hasattr(scheduler, "_tasks"):
|
||||
return
|
||||
if self._scheduler_tasks_original is None:
|
||||
self._realtime_shadow_tasks_enabled = False
|
||||
return
|
||||
scheduler._tasks[:] = [list(frame_tasks) for frame_tasks in self._scheduler_tasks_original]
|
||||
self._realtime_shadow_tasks_enabled = False
|
||||
|
||||
def set_realtime_shadow_updates(self, enabled):
|
||||
"""Public toggle for aggressive per-frame shadow updates."""
|
||||
self.realtime_shadow_updates = bool(enabled)
|
||||
if self.realtime_shadow_updates:
|
||||
self._enable_realtime_shadow_tasks()
|
||||
else:
|
||||
self._disable_realtime_shadow_tasks()
|
||||
|
||||
def load_font(self):
|
||||
"""Load custom font for ImGui"""
|
||||
@ -186,8 +220,8 @@ class SSBOEditor:
|
||||
|
||||
self.model.reparent_to(self.base.render)
|
||||
|
||||
# Keep shadow feedback responsive during interactive edits.
|
||||
self._enable_realtime_shadow_tasks()
|
||||
# Keep this off by default for better overall FPS/scaling with visibility.
|
||||
self.set_realtime_shadow_updates(self.realtime_shadow_updates)
|
||||
|
||||
# NO rp.set_effect() — use RP default rendering for max FPS
|
||||
# NO SSBO creation — vertex positions are baked
|
||||
|
||||
Loading…
Reference in New Issue
Block a user