From 7fa76b7db07cc04b1ada04f63404898db3bb643f Mon Sep 17 00:00:00 2001 From: Ehbw Date: Thu, 25 Jun 2026 13:43:17 +0100 Subject: [PATCH 1/2] fix(extra-natives/rdr3): graphic natives flickering --- .../src/GraphicsNatives.cpp | 51 +++++++++++++------ 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/code/components/extra-natives-rdr3/src/GraphicsNatives.cpp b/code/components/extra-natives-rdr3/src/GraphicsNatives.cpp index 704b4cd533..a6ff186169 100644 --- a/code/components/extra-natives-rdr3/src/GraphicsNatives.cpp +++ b/code/components/extra-natives-rdr3/src/GraphicsNatives.cpp @@ -6,6 +6,8 @@ #include "Hooking.Stubs.h" #include "GamePrimitives.h" #include "scrEngine.h" +#include +#include enum ScriptImDrawType : uint32_t { @@ -28,6 +30,9 @@ struct ScriptImRequest static constexpr int kMaxScriptImRequests = 1024; static std::vector g_scriptImRequests{}; +static std::vector g_scriptImRenderBuffer{}; + +static std::atomic g_scriptImReady{false}; static ScriptImRequest** g_scriptImBuffer; static uint16_t* g_scriptImEntriesCount; @@ -43,20 +48,28 @@ static void RenderScriptIm() // The game never uses this buffer itself, but it *may* be changed in the future updates. // This code isn't ready for such a scenario, so might potentially require tweaks. // Like if something allocate script im buffer before us, we would need to resize it - // and not just rewrite everything without custom stuff. - - if (g_scriptImRequests.empty()) + // and not just rewrite everything without custom stuff. + + // Wait until the main thread is done for this frame before pushing new requests + if (g_scriptImReady.load(std::memory_order_acquire)) + { + g_scriptImRenderBuffer.clear(); + std::swap(g_scriptImRequests, g_scriptImRenderBuffer); + g_scriptImReady.store(false, std::memory_order_relaxed); + } + + if (g_scriptImRenderBuffer.empty()) { return; } - *g_scriptImEntriesCount = g_scriptImRequests.size(); + *g_scriptImEntriesCount = g_scriptImRenderBuffer.size(); // If the buffer isn't allocated, let's do it. if (!*g_scriptImBuffer) { initScriptImBuffer(); - } + } // Failed to allocate? Bail out. if (!*g_scriptImBuffer) @@ -66,11 +79,9 @@ static void RenderScriptIm() } // Move stuff from our temp vector to the in-game buffer. - std::move(g_scriptImRequests.begin(), g_scriptImRequests.end(), *g_scriptImBuffer); + std::copy(g_scriptImRenderBuffer.begin(), g_scriptImRenderBuffer.end(), *g_scriptImBuffer); g_origRenderScriptIm(); - - g_scriptImRequests.clear(); *g_scriptImEntriesCount = 0; } @@ -90,8 +101,8 @@ struct DrawOriginStore static bool (*isGamePaused)(); -static int* g_frameDrawIndex1; -static int* g_frameDrawIndex2; +static int* g_renderBufferIndex; +static int* g_updateBufferIndex; static DrawOriginStore* g_drawOriginStore; static uint32_t* g_scriptDrawOriginIndex; @@ -124,8 +135,8 @@ static HookFunction hookFunction([]() { auto location = hook::get_pattern("48 69 D0 10 04 00 00 48 8D 05 ? ? ? ? 48 03"); - g_frameDrawIndex1 = hook::get_address(location - 20); - g_frameDrawIndex2 = hook::get_address(location - 7); + g_renderBufferIndex = hook::get_address(location - 20); + g_updateBufferIndex = hook::get_address(location - 7); g_drawOriginStore = hook::get_address(location + 10); hook::set_call(&isGamePaused, location - 27); @@ -153,13 +164,21 @@ static HookFunction hookFunction([]() g_screenWidth = hook::get_address(location + 2); g_screenHeight = hook::get_address(location + 8); - } + } + + OnMainGameFrame.Connect([]() + { + g_scriptImReady.store(false, std::memory_order_relaxed); + }); + + OnPostFrontendRender.Connect([]() + { + g_scriptImReady.store(true, std::memory_order_release); + }); fx::ScriptEngine::RegisterNativeHandler("SET_DRAW_ORIGIN", [](fx::ScriptContext& context) { - auto drawIndex = *(isGamePaused() ? g_frameDrawIndex2 : g_frameDrawIndex1); - - auto& store = g_drawOriginStore[drawIndex]; + auto& store = g_drawOriginStore[*g_renderBufferIndex]; const auto index = store.m_count; if (index >= 32) From b43f76b3379b8280ae43d0f99d18827fde24bc62 Mon Sep 17 00:00:00 2001 From: Ehbw Date: Fri, 26 Jun 2026 19:40:25 +0100 Subject: [PATCH 2/2] fix(extra-natives/rdr3): SET_DRAW_ORIGIN flickering and positional issue --- .../src/GraphicsNatives.cpp | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/code/components/extra-natives-rdr3/src/GraphicsNatives.cpp b/code/components/extra-natives-rdr3/src/GraphicsNatives.cpp index a6ff186169..e40ead0f12 100644 --- a/code/components/extra-natives-rdr3/src/GraphicsNatives.cpp +++ b/code/components/extra-natives-rdr3/src/GraphicsNatives.cpp @@ -55,7 +55,6 @@ static void RenderScriptIm() { g_scriptImRenderBuffer.clear(); std::swap(g_scriptImRequests, g_scriptImRenderBuffer); - g_scriptImReady.store(false, std::memory_order_relaxed); } if (g_scriptImRenderBuffer.empty()) @@ -83,7 +82,7 @@ static void RenderScriptIm() g_origRenderScriptIm(); *g_scriptImEntriesCount = 0; -} +} struct DrawOriginData { @@ -99,12 +98,13 @@ struct DrawOriginStore char pad_404[12]; }; -static bool (*isGamePaused)(); - static int* g_renderBufferIndex; static int* g_updateBufferIndex; static DrawOriginStore* g_drawOriginStore; -static uint32_t* g_scriptDrawOriginIndex; +static uint32_t* g_scriptDrawOriginIndex; + +static int g_updateDrawOriginCount = 0; +static DrawOriginData g_updateDrawOrigin[32]; struct WorldhorizonManager { @@ -138,8 +138,6 @@ static HookFunction hookFunction([]() g_renderBufferIndex = hook::get_address(location - 20); g_updateBufferIndex = hook::get_address(location - 7); g_drawOriginStore = hook::get_address(location + 10); - - hook::set_call(&isGamePaused, location - 27); } { @@ -174,14 +172,18 @@ static HookFunction hookFunction([]() OnPostFrontendRender.Connect([]() { g_scriptImReady.store(true, std::memory_order_release); + if (g_updateDrawOriginCount) + { + auto drawIndex = *g_updateBufferIndex; + std::move(g_updateDrawOrigin, g_updateDrawOrigin + g_updateDrawOriginCount, g_drawOriginStore[drawIndex].m_items); + g_drawOriginStore[drawIndex].m_count = g_updateDrawOriginCount; + g_updateDrawOriginCount = 0; + } }); fx::ScriptEngine::RegisterNativeHandler("SET_DRAW_ORIGIN", [](fx::ScriptContext& context) { - auto& store = g_drawOriginStore[*g_renderBufferIndex]; - const auto index = store.m_count; - - if (index >= 32) + if (g_updateDrawOriginCount >= 32) { return; } @@ -191,11 +193,10 @@ static HookFunction hookFunction([]() data.m_pos.y = context.GetArgument(1); data.m_pos.z = context.GetArgument(2); data.m_is2d = context.GetArgument(3); - - store.m_items[index] = data; - store.m_count += 1; - - *g_scriptDrawOriginIndex = index; + + g_updateDrawOrigin[g_updateDrawOriginCount] = data; + *g_scriptDrawOriginIndex = g_updateDrawOriginCount; + g_updateDrawOriginCount += 1; }); fx::ScriptEngine::RegisterNativeHandler("CLEAR_DRAW_ORIGIN", [](fx::ScriptContext& context)