render: skip rebuilding occlusion-culled statics on most frames
This commit is contained in:
parent
8f8642aec2
commit
7b0d690cf3
4 changed files with 55 additions and 3 deletions
|
|
@ -1177,6 +1177,17 @@ REXCVAR_DEFINE_BOOL(skate3_native_render_scene_occlusion_cull, true, "Skate 3",
|
|||
"items.")
|
||||
.lifecycle(rex::cvar::Lifecycle::kHotReload);
|
||||
|
||||
REXCVAR_DEFINE_BOOL(skate3_native_render_scene_occlusion_cull_build, true,
|
||||
"Skate 3",
|
||||
"Also skip the per-frame scene-item rebuild for statics "
|
||||
"the occlusion cull proved hidden, on 3 of every 4 "
|
||||
"frames (staggered per instance). The every-4th-frame "
|
||||
"rebuild re-enters the item in the rendered scene so "
|
||||
"disocclusion is re-tested and the persistent shadow "
|
||||
"caster cache stays refreshed. Requires "
|
||||
"skate3_native_render_scene_occlusion_cull.")
|
||||
.lifecycle(rex::cvar::Lifecycle::kHotReload);
|
||||
|
||||
REXCVAR_DEFINE_BOOL(skate3_native_render_scene_occlusion_cull_guest, true,
|
||||
"Skate 3",
|
||||
"Also skip the GUEST engine's draw-list dispatch for "
|
||||
|
|
@ -8261,6 +8272,21 @@ void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) {
|
|||
if (wloop_prof) {
|
||||
g_bi_records.fetch_add(count, std::memory_order_relaxed);
|
||||
}
|
||||
// Build-side occlusion skip: the render thread's culled-ctx set (the same
|
||||
// sticky snapshot the guest dispatch filter uses, expiring with it when
|
||||
// the native render idles or the cull's motion gate stands down). A ctx
|
||||
// in it skips the whole item rebuild on 3 of every 4 frames; the
|
||||
// staggered rebuild frame re-enters it in scene.items so the render side
|
||||
// re-tests occlusion (disocclusion drops it from the set) and the shadow
|
||||
// caster caches keep refreshing.
|
||||
static std::vector<uint32_t> s_build_culled; // guest render thread only
|
||||
s_build_culled.clear();
|
||||
if (REXCVAR_GET(skate3_native_render_scene_occlusion_cull_build)) {
|
||||
CopyOcclusionCulledCtxs(s_build_culled);
|
||||
if (!s_build_culled.empty()) {
|
||||
scene.occl_build_skipped.reserve(s_build_culled.size());
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
const SubmitRecord& r = records[i];
|
||||
// Primary opaque list of the chosen view only; other lists (shadow
|
||||
|
|
@ -8368,6 +8394,14 @@ void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) {
|
|||
if (!seen.insert(r.a).second) {
|
||||
continue;
|
||||
}
|
||||
if (!s_build_culled.empty() &&
|
||||
((g_guest_frame + ((r.a >> 4) * 2654435761u >> 8)) & 3u) != 0u &&
|
||||
std::binary_search(s_build_culled.begin(), s_build_culled.end(),
|
||||
r.a)) {
|
||||
scene.occl_build_skipped.push_back(r.a);
|
||||
g_occl_build_skipped.fetch_add(1, std::memory_order_relaxed);
|
||||
continue;
|
||||
}
|
||||
if (!REXCVAR_GET(skate3_native_render_scene_world_items)) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -430,6 +430,11 @@ struct FrameScene {
|
|||
};
|
||||
PhotoFx photo_fx;
|
||||
std::vector<DrawItem> items;
|
||||
// MeshContexts the build-side occlusion skip left out of `items` this
|
||||
// frame (see skate3_native_render_scene_occlusion_cull_build): the
|
||||
// render thread re-publishes them as culled so the guest dispatch filter
|
||||
// keeps excluding them between their staggered rebuild frames.
|
||||
std::vector<uint32_t> occl_build_skipped;
|
||||
};
|
||||
|
||||
// One frame submission record from the hook layer (see RenderMeshRecord).
|
||||
|
|
|
|||
|
|
@ -7143,7 +7143,7 @@ void LogFrameStats(const FrameScene& scene, uint64_t frames, uint32_t drawn,
|
|||
"reused={}] dynobj[valid={} drawn={}] water[valid={} drawn={}] "
|
||||
"lw[ctxs={} ents={} stamp={} fade0={} resc={} fill={} pal={} rows={}] "
|
||||
"refl[pair={} flat={} gate={:#x}] flips={} alt[bones={} shadow={}] "
|
||||
"rows_inst={} pal_srv={} occl_culled={} guest_skip={}",
|
||||
"rows_inst={} pal_srv={} occl_culled={} guest_skip={} build_skip={}",
|
||||
frames, scene.items.size(), drawn, g_draws_2d.load(), drawn_2d,
|
||||
drawn_spline, g_draws_spline.load(),
|
||||
g_draws_2d_other.load(), g_draws_2d_dropped.load(),
|
||||
|
|
@ -7178,7 +7178,7 @@ void LogFrameStats(const FrameScene& scene, uint64_t frames, uint32_t drawn,
|
|||
g_hair_route_flips.load(), g_hair_bone_alternations.load(),
|
||||
g_shadow_alternations.load(), g_char_rows_inst_served.load(),
|
||||
g_pal_served_total.load(), g_occl_culled.load(),
|
||||
g_occl_guest_skipped.load());
|
||||
g_occl_guest_skipped.load(), g_occl_build_skipped.load());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -9980,8 +9980,18 @@ bool RenderScene(const NativeGuestOutputRenderContext& context, void* /*user_dat
|
|||
stamp_route(1);
|
||||
}
|
||||
// Publish the culled-ctx set every rendered frame, including empty ones:
|
||||
// the guest filter must clear promptly when the cull stands down.
|
||||
// the guest filter must clear promptly when the cull stands down. Ctxs
|
||||
// the build-side skip left out of this scene stay culled by re-inclusion
|
||||
// (they could not be re-tested; their staggered rebuild frame is when
|
||||
// they re-enter scene.items and get a fresh verdict) - but only while
|
||||
// the cull is ACTIVE: on stand-down (debug views, the camera-motion
|
||||
// gate, teleports) the set must drain so every skipped item resumes
|
||||
// building on the next guest frame.
|
||||
{
|
||||
if (occl_cull_active) {
|
||||
culled_ctxs.insert(culled_ctxs.end(), scene.occl_build_skipped.begin(),
|
||||
scene.occl_build_skipped.end());
|
||||
}
|
||||
const int64_t now_ms =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
|
|
|
|||
|
|
@ -968,6 +968,9 @@ inline std::atomic<uint64_t> g_occl_culled{0};
|
|||
// filter removed before the guest's draw-list renderer ran (see
|
||||
// skate3_native_render_scene_occlusion_cull_guest).
|
||||
inline std::atomic<uint64_t> g_occl_guest_skipped{0};
|
||||
// Session-cumulative count of item rebuilds the build-side occlusion skip
|
||||
// left out (see skate3_native_render_scene_occlusion_cull_build).
|
||||
inline std::atomic<uint64_t> g_occl_build_skipped{0};
|
||||
// Guest render thread: decomposition of the per-item build walk.
|
||||
inline PerfWindow g_pw_bi_core; // BuildItemFromMeshCached total (hit validate + misses)
|
||||
inline PerfWindow g_pw_bi_fp; // ComputeItemFingerprint on the cache-hit path
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue