render: fill one-to-two frame dyn publish gaps with the previous capture
This commit is contained in:
parent
d3f230b064
commit
fbcffc1445
1 changed files with 44 additions and 11 deletions
|
|
@ -1132,6 +1132,11 @@ REXCVAR_DEFINE_BOOL(skate3_native_render_scene_ring, false, "Skate 3",
|
||||||
"frames); F7 dumps it to logs/scene_ring_<ts>.csv for "
|
"frames); F7 dumps it to logs/scene_ring_<ts>.csv for "
|
||||||
"diffing 1-2 frame artifacts no capture can catch")
|
"diffing 1-2 frame artifacts no capture can catch")
|
||||||
.lifecycle(rex::cvar::Lifecycle::kHotReload);
|
.lifecycle(rex::cvar::Lifecycle::kHotReload);
|
||||||
|
|
||||||
|
REXCVAR_DEFINE_BOOL(skate3_native_render_scene_dyn_gap_fill, true, "Skate 3",
|
||||||
|
"Re-publish character/cloth/skinned items for up to two "
|
||||||
|
"missed publish frames (high-frame-rate body flicker)")
|
||||||
|
.lifecycle(rex::cvar::Lifecycle::kHotReload);
|
||||||
namespace skate3::native_scene {
|
namespace skate3::native_scene {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
@ -8654,12 +8659,21 @@ void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Publish-gap telemetry ("skater flickered invisible for a moment"): a
|
// Publish-gap telemetry and fill ("skater flickered invisible for a
|
||||||
// skinned/ropa/character mesh that published recently, vanished for 1-3
|
// moment"): a skinned/ropa/character mesh that published recently can
|
||||||
// built frames, and came back. Rate-limited log names the mesh and gap so
|
// vanish from the publish stream for 1-2 built frames at high render
|
||||||
// the next flicker sighting is diagnosable from the log alone.
|
// rates (the body serve races the sim; more frequent the faster the
|
||||||
|
// renderer runs, so Vulkan flickered worst). Re-publish the previous
|
||||||
|
// frame's capture for up to two missed frames - a one-frame-old pose is
|
||||||
|
// imperceptible at those rates, and a real despawn stops the fill at the
|
||||||
|
// cap. The rate-limited log keeps naming mesh and gap so sightings stay
|
||||||
|
// diagnosable.
|
||||||
{
|
{
|
||||||
static std::unordered_map<uint32_t, uint64_t> s_last_pub;
|
struct PubTrack {
|
||||||
|
uint64_t frame = 0;
|
||||||
|
DrawItem item;
|
||||||
|
};
|
||||||
|
static std::unordered_map<uint32_t, PubTrack> s_last_pub;
|
||||||
static uint64_t s_pub_frame = 0;
|
static uint64_t s_pub_frame = 0;
|
||||||
++s_pub_frame;
|
++s_pub_frame;
|
||||||
if (s_last_pub.size() > 4096) {
|
if (s_last_pub.size() > 4096) {
|
||||||
|
|
@ -8670,12 +8684,10 @@ void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) {
|
||||||
(item.skinned && !item.bones.empty()))) {
|
(item.skinned && !item.bones.empty()))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto [it, fresh] = s_last_pub.try_emplace(item.mesh, s_pub_frame);
|
PubTrack& t = s_last_pub[item.mesh];
|
||||||
if (fresh) {
|
const uint64_t gap = t.frame != 0 ? s_pub_frame - t.frame : 0;
|
||||||
continue;
|
t.frame = s_pub_frame;
|
||||||
}
|
t.item = item;
|
||||||
const uint64_t gap = s_pub_frame - it->second;
|
|
||||||
it->second = s_pub_frame;
|
|
||||||
if (gap >= 2 && gap <= 4) {
|
if (gap >= 2 && gap <= 4) {
|
||||||
g_dyn_gap.fetch_add(1, std::memory_order_relaxed);
|
g_dyn_gap.fetch_add(1, std::memory_order_relaxed);
|
||||||
static std::atomic<uint32_t> gap_logged{0};
|
static std::atomic<uint32_t> gap_logged{0};
|
||||||
|
|
@ -8689,6 +8701,27 @@ void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (REXCVAR_GET(skate3_native_render_scene_dyn_gap_fill)) {
|
||||||
|
for (auto& [mesh, t] : s_last_pub) {
|
||||||
|
if (t.frame == 0 || t.frame == s_pub_frame) {
|
||||||
|
continue; // published this frame (or placeholder)
|
||||||
|
}
|
||||||
|
const uint64_t gap = s_pub_frame - t.frame;
|
||||||
|
if (gap <= 2) {
|
||||||
|
// t.frame stays at the last REAL publish, so the fill self-limits
|
||||||
|
// to two frames and the telemetry above still sees the true gap
|
||||||
|
// when the stream resumes.
|
||||||
|
scene.items.push_back(t.item);
|
||||||
|
static std::atomic<uint64_t> s_gap_filled{0};
|
||||||
|
const uint64_t n = s_gap_filled.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
if (n < 8 || (n & 1023u) == 0) {
|
||||||
|
REXLOG_INFO(
|
||||||
|
"native-scene: dyn gap fill mesh={:08X} age={} ropa={} fam={} (n={})",
|
||||||
|
mesh, gap, t.item.ropa ? 1 : 0, t.item.char_family, n + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (scene.items.empty()) {
|
if (scene.items.empty()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue