Skip to content

bugfix(smudge): Decouple smudge time step from render update#2484

Merged
xezon merged 7 commits intoTheSuperHackers:mainfrom
xezon:xezon/fix-smudge-logic-step
Mar 30, 2026
Merged

bugfix(smudge): Decouple smudge time step from render update#2484
xezon merged 7 commits intoTheSuperHackers:mainfrom
xezon:xezon/fix-smudge-logic-step

Conversation

@xezon
Copy link
Copy Markdown

@xezon xezon commented Mar 22, 2026

This change decouples the smudge time step from the render update. Smudge refers to the heat haze of the USA Microwave Tank. When the game is paused, the heat effect will now pause as well.

TODO

  • Fix GameClientRandomValueReal(-0.03f,0.03f)

@xezon xezon added this to the Decouple logic and rendering milestone Mar 22, 2026
@xezon xezon added Minor Severity: Minor < Major < Critical < Blocker USA Affects USA faction ZH Relates to Zero Hour Rendering Is Rendering related Bug Something is not working right, typically is user facing labels Mar 22, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 22, 2026

Greptile Summary

This PR correctly decouples the USA Microwave Tank heat-haze (smudge) time step from the render update, fixing the bug where the effect continued animating while the game was paused. The core insight is a clean two-phase split: the logic update (ParticleSys::update) now owns all smudge state — it rebuilds the SmudgeSet every logic tick with freshly randomised offsets and m_draw = false; the render update (W3DParticleSys::doParticles) then calls resetDraw() and re-marks only in-frustum particles as drawable before passing control to W3DSmudge::render.

Key improvements beyond the bug fix:

  • A new Smudge::Identifier/SmudgeIdToPtrMap lookup replaces the O(n) scan, enabling findSmudge(particle*) in O(1).
  • The render pass now uses a local copy of m_offset instead of permanently zeroing smudge->m_offset when UV coordinates fall outside valid texel bounds — preventing a subtle state-corruption bug present in the previous code.
  • W3DSmudgeManager is marked final, and m_smudgeCountLastFrame is now set directly in render() rather than being threaded through multiple call-sites.
  • removeSmudgeSet / removeSmudgeFromSet adopt reference-to-pointer signatures that auto-null the caller's variable on removal.

One minor suggestion: initialising m_draw = false inside addSmudgeToSet rather than at every call-site would make the invariant self-enforcing and guard against future misuse of recycled Smudge objects from the free pool.

Confidence Score: 5/5

PR is safe to merge; the decoupling logic is correct, state transitions are consistent across logic and render frames, and no P0/P1 issues were found.

All logic and render paths were traced end-to-end. The m_draw flag is consistently initialised by every call-site immediately after addSmudgeToSet, so no uninitialised-state risk exists in practice. The two-pass render loop correctly handles batching of only drawable smudges without risk of infinite loops or vertex-buffer overflow. The only finding is a P2 defensive-coding suggestion. The existing TODO ("Fix GameClientRandomValueReal") is explicitly called out in the PR description and does not affect correctness.

No files require special attention; all changes are self-consistent.

Important Files Changed

Filename Overview
Core/GameEngine/Include/GameClient/Smudge.h Adds Identifier typedef (void*), m_draw flag to Smudge, STLPort hash specialization for Identifier, resetDraw()/findSmudge() helpers to SmudgeSet, removes setSmudgeCountLastFrame() setter from SmudgeManager, and updates removeSmudgeSet to take a reference-to-pointer for automatic nulling.
Core/GameEngine/Source/GameClient/System/Smudge.cpp Implements new resetDraw(), findSmudge(), and addSmudgeToSet(identifier) methods; also adds m_usedSmudgeMap management across reset(), addSmudgeToSet, removeSmudgeFromSet, and adds hash-map-based identifier lookup.
Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DSmudge.cpp Render function updated to skip smudges with m_draw == false, uses a local offset copy instead of mutating smudge->m_offset, moves m_smudgeCountLastFrame assignment into render(), and converts while loops to range-for style.
Core/GameEngineDevice/Include/W3DDevice/GameClient/W3DSmudge.h Marks W3DSmudgeManager as final, preventing unintended subclassing. Clean, minimal change.
Core/GameEngine/Source/GameClient/System/ParticleSys.cpp Adds the logic-side smudge update: on each logic frame, resets all smudges and rebuilds the SmudgeSet from current particles (with m_draw = false). This is the new owner of smudge state, decoupled from the render cycle.
GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DParticleSys.cpp Render-side smudge handling simplified: instead of creating smudges here, it now calls resetDraw() then sets m_draw = true on in-frustum particles via findSmudge(p). Removes stale visibleSmudgeCount tracking and setSmudgeCountLastFrame() call.

Sequence Diagram

sequenceDiagram
    participant LU as Logic Update<br/>(ParticleSys::update)
    participant SM as SmudgeManager
    participant RU as Render Update<br/>(W3DParticleSys::doParticles)
    participant RN as Renderer<br/>(W3DSmudge::render)

    note over LU,RN: Each Logic Frame (pauses when game is paused)
    LU->>SM: reset() — clears all SmudgeSets & smudges
    LU->>SM: addSmudgeSet()
    loop For each smudge particle
        LU->>SM: addSmudgeToSet(particle*)<br/>sets pos, offset (random), size, opacity<br/>m_draw = false
    end

    note over LU,RN: Each Render Frame (runs even when paused)
    RU->>SM: resetDraw() — sets all m_draw = false
    loop For each in-frustum smudge particle
        RU->>SM: findSmudge(particle*)
        SM-->>RU: Smudge*
        RU->>SM: smudge->m_draw = true
    end

    RU->>RN: render(rinfo)
    note over RN: Pass 1: count visible (m_draw==true),<br/>compute view-space verts & UVs<br/>using local offset copy
    note over RN: Pass 2: upload vertex data to GPU<br/>in batches of ≤SMUDGE_DRAW_SIZE,<br/>skip m_draw==false smudges
    RN->>SM: m_smudgeCountLastFrame = count
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: Core/GameEngine/Source/GameClient/System/ParticleSys.cpp
Line: 3034-3039

Comment:
**`m_draw` initialization belongs inside `addSmudgeToSet`**

`m_draw = false` is always assigned by every call-site right after `addSmudgeToSet` returns. Smudges recycled from the free pool carry whatever `m_draw` value they had when they were last removed, so if a future call-site forgets to reinitialise the field the render pass could spuriously draw a stale smudge.

Consider initialising `m_draw` inside `addSmudgeToSet` (in `Smudge.cpp`) so correctness is self-contained:

```cpp
// In SmudgeSet::addSmudgeToSet, after smudge->m_identifier = identifier:
smudge->m_draw = false;
```

That would also let you drop the explicit `smudge->m_draw = false;` from every call-site.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (7): Last reviewed commit: "Fix offset Y" | Re-trigger Greptile

@xezon xezon force-pushed the xezon/fix-smudge-logic-step branch from 1c343f0 to eb58201 Compare March 23, 2026 19:26
Copy link
Copy Markdown

@Mauller Mauller left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall

@xezon xezon force-pushed the xezon/fix-smudge-logic-step branch from 12d14aa to 60e3af8 Compare March 30, 2026 16:24
@xezon
Copy link
Copy Markdown
Author

xezon commented Mar 30, 2026

Rebased and Y offset adjusted.

@xezon xezon merged commit 920c4e6 into TheSuperHackers:main Mar 30, 2026
23 checks passed
@xezon xezon deleted the xezon/fix-smudge-logic-step branch March 30, 2026 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something is not working right, typically is user facing Minor Severity: Minor < Major < Critical < Blocker Rendering Is Rendering related USA Affects USA faction ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

USA Microwave Heat Haze is not halting on game pause

2 participants