This commit is contained in:
cw 2026-07-26 11:48:32 -04:00 • committed by GitHub
commit f3db1e1c8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 28 additions and 24 deletions

View file

@ -6020,7 +6020,6 @@ void ReleaseRetiredAndFlushCaches(const NativeGuestOutputRenderContext& context)
g_r.tex_routes.clear(); g_r.tex_routes.clear();
g_r.words_sticky.clear(); g_r.words_sticky.clear();
g_r.tex_sticky.clear(); g_r.tex_sticky.clear();
g_r.tex_pending_first.clear();
REXLOG_INFO("native-scene: texture cache flushed (debug dialog)"); REXLOG_INFO("native-scene: texture cache flushed (debug dialog)");
} }
if (g_flush_meshes.exchange(false, std::memory_order_relaxed)) { if (g_flush_meshes.exchange(false, std::memory_order_relaxed)) {
@ -8587,7 +8586,27 @@ bool RenderScene(const NativeGuestOutputRenderContext& context, void* /*user_dat
: (g_in_menus_frame.load(std::memory_order_relaxed) ? 2ull : (g_in_menus_frame.load(std::memory_order_relaxed) ? 2ull
: 16ull)); : 16ull));
const uint64_t fp = SampleProbeFingerprint(base, e); const uint64_t fp = SampleProbeFingerprint(base, e);
const bool fp_new = fp != 0 && fp != e.payload_fp; const bool fp_changed = fp != 0 && fp != e.payload_fp;
bool fp_new = false;
if (fp_changed) {
if (e.pending_payload_fp == fp) {
if (e.pending_payload_confirmations < 2) {
++e.pending_payload_confirmations;
}
} else {
e.pending_payload_fp = fp;
e.pending_payload_confirmations = 1;
}
fp_new = e.pending_payload_confirmations >= 2;
if (!fp_new) {
// Verify the candidate promptly. This keeps a completed stream
// responsive while refusing one-frame/mid-copy fingerprints.
e.recheck_frame = std::min(e.recheck_frame, frame_number + 2);
}
} else {
e.pending_payload_fp = 0;
e.pending_payload_confirmations = 0;
}
if (trm) { if (trm) {
REXLOG_INFO( REXLOG_INFO(
"tex-trace: f{} obj={:08X} poll key={:016X} fp={:016X} " "tex-trace: f{} obj={:08X} poll key={:016X} fp={:016X} "
@ -8733,9 +8752,6 @@ bool RenderScene(const NativeGuestOutputRenderContext& context, void* /*user_dat
site.words_key = cur_key; site.words_key = cur_key;
site.area = cur_area; site.area = cur_area;
site.downgrade_since = 0; site.downgrade_since = 0;
if (!g_r.tex_pending_first.empty()) {
g_r.tex_pending_first.erase(tex_ptr);
}
return t; return t;
} }
if (tex_pending) { if (tex_pending) {
@ -8774,20 +8790,6 @@ bool RenderScene(const NativeGuestOutputRenderContext& context, void* /*user_dat
: nullptr; : nullptr;
if (diffuse == nullptr) { if (diffuse == nullptr) {
diffuse = resolve_texture(item.diffuse_tex, 0); diffuse = resolve_texture(item.diffuse_tex, 0);
if (diffuse == &g_r.white && tex_pending && item.diffuse_tex != 0 &&
!item.retained) {
// Brand-new content, first decode in flight, and no previous mip
// cached to serve: draw NOTHING for a short window instead of a
// white flash: the emulated pop-in semantics (the game shows
// streamed content only once its data is ready). Bounded so a
// permanently failing texture still falls back to visible white.
const auto [pit, fresh] =
g_r.tex_pending_first.try_emplace(item.diffuse_tex, frame_number);
if (frame_number - pit->second < 20) {
g_skip_new.fetch_add(1, std::memory_order_relaxed);
return;
}
}
} }
const GuestTexture* lightmap = const GuestTexture* lightmap =
item.lightmap_tex != 0 && REXCVAR_GET(skate3_native_render_scene_lightmaps) item.lightmap_tex != 0 && REXCVAR_GET(skate3_native_render_scene_lightmaps)
@ -11597,4 +11599,3 @@ void ResetSceneFailure() {}
} // namespace skate3::native_scene } // namespace skate3::native_scene
#endif // REX_HAS_D3D12 || REX_HAS_VULKAN #endif // REX_HAS_D3D12 || REX_HAS_VULKAN

View file

@ -91,6 +91,13 @@ struct GuestTexture {
uint32_t payload_size = 0; uint32_t payload_size = 0;
uint64_t payload_fp = 0; uint64_t payload_fp = 0;
uint64_t recheck_frame = 0; uint64_t recheck_frame = 0;
// Candidate observed by payload revalidation. In-place streaming writes
// are not coherent with the native renderer's sampled reads, so one
// changed fingerprint is not sufficient evidence that a complete new
// image exists. Require the same changed fingerprint on two consecutive
// polls before replacing a valid cached decode.
uint64_t pending_payload_fp = 0;
uint8_t pending_payload_confirmations = 0;
// Consecutive failed decodes of this entry (payload still streaming in): // Consecutive failed decodes of this entry (payload still streaming in):
// drives the escalating retry backoff at commit; the first failure // drives the escalating retry backoff at commit; the first failure
// retries fast (the payload usually lands within a few frames; a fixed // retries fast (the payload usually lands within a few frames; a fixed
@ -1004,10 +1011,6 @@ struct RendererState {
uint64_t downgrade_since = 0; uint64_t downgrade_since = 0;
}; };
std::unordered_map<uint64_t, TexStickySite> tex_sticky; std::unordered_map<uint64_t, TexStickySite> tex_sticky;
// First frame a texture object resolved white-with-heal-in-flight:
// brand-new items (no sticky fallback) skip drawing for a short window
// instead of flashing white.
std::unordered_map<uint32_t, uint64_t> tex_pending_first;
bool failed = false; bool failed = false;
bool announced = false; bool announced = false;
}; };