performance(Drawable): Adjusts Turret Positioning, Recoil and Muzzle for Model Draw to Update Only when Necessary#2046
Conversation
Skyaero42
left a comment
There was a problem hiding this comment.
Any indication on the performance gain?
This may need extensive testing for mismatching.
| //------------------------------------------------------------------------------------------------- | ||
| void W3DModelDraw::setNeedUpdateTurretPositioning(Bool set) | ||
| { | ||
| m_needUpdateTurretPosition = set; // A simple function with the dangers of an atomic bomb, misuse and it'll cause desync |
| if( xfer->getXferMode() == XFER_LOAD && m_subObjectVec.empty() == FALSE ) | ||
| updateSubObjects(); | ||
|
|
||
| #if !RETAIL_COMPATIBLE_CRC |
There was a problem hiding this comment.
Does this require a xfer version change?
I am not sure, but the devs does mention it being not a major problem. Line handleClientRecoil function are majority just For Loop Checks, each time checks with for loops based on how many Maximum Weapons are in game, (In this case, 3), and checks for each Weapon Slot whether there are any recoil to handle. If it does, it handles the Model Bone Recoil and handles their Muzzle from showing. Every Barrel Bone present will require checks. handleClientTurretPositioning however, is O(n) based. It calls for all Turrets for each Weapon Slot to update Every Frame regardless if there are any changes. For each turret in the runtime it calls for AIUpdateInterface to get their Turret Pitch and Angle, then Transforms the Matrix up to 2 Times (One for Angle, One for Pitch). This is called every frame. The changes just add a go/stop variable at the beginning of both functions, (m_doHandleRecoil, and m_needUpdateTurretPosition) to inform W3DModelDraw to allow the calling of the respective functions. |
|
I think this can be simplified by removing the changes to xfer and instead forcing a recompute right after you load the savegame, like this (do test it): diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp
@@ void Object::loadPostProcess()
if( m_xferContainedByID != INVALID_ID )
m_containedBy = TheGameLogic->findObjectByID(m_xferContainedByID);
else
m_containedBy = NULL;
+
+ setNeedUpdateTurretPositioning(TRUE);diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp b/GeneralsMD/Code/GameEngineDevice/
Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp
@@ void W3DModelDraw::loadPostProcess( void )
// extend base class
DrawModule::loadPostProcess();
+
+ m_needUpdateTurretPosition = TRUE;
+ m_doHandleRecoil = TRUE;This may change the CRC right after loading a savegame but I don't see how that would be a problem |
This change seems logical, tested on my end from loading a retail compatible save and it seems fine. I'll adjust and update my commit. |
Caball009
left a comment
There was a problem hiding this comment.
I don't think the turret positioning implementation is right.
I built a couple of firebases. I moved the camera somewhere else and then back to the firebases and the turrets snap to a certain angle. It looks like a purely visual thing, though, because it didn't cause a mismatch with a client that didn't have PR.
|
Alright, thanks for reporting. Pushed an update, it should fix it. Edit: Nevermind, the issue still persist, just less noticable. I'm looking into it. |
3ea78f3 to
3e96ab1
Compare
|
| Filename | Overview |
|---|---|
| GeneralsMD/Code/GameEngine/Source/GameLogic/AI/TurretAI.cpp | Adds setNeedUpdateTurretPositioning calls in all turret state transitions. One if-body-on-same-line style violation at the new setTurretEnabled call. |
| Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp | Gates handleClientTurretPositioning and handleClientRecoil behind new flags; resets them in constructor, loadPostProcess, handleWeaponFireFX, and rebuildWeaponRecoilInfo. |
| GeneralsMD/Code/GameEngine/Include/Common/DrawModule.h | Adds setNeedUpdateTurretPositioning as a pure virtual to ObjectDrawInterface, correctly scoped so only ObjectDrawInterface implementors must provide it. |
| GeneralsMD/Code/GameEngine/Source/GameClient/Drawable.cpp | Implements Drawable::setNeedUpdateTurretPositioning, forwarding only to draw modules with an ObjectDrawInterface. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp | Implements Object::setNeedUpdateTurretPositioning with deduplication guard; initialises flag to FALSE in constructor and forces TRUE in loadPostProcess. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant TAI as TurretAI (Logic)
participant OBJ as Object
participant DRW as Drawable
participant MDL as W3DModelDraw
Note over TAI: Turret starts rotating
TAI->>OBJ: setNeedUpdateTurretPositioning(TRUE)
OBJ->>OBJ: cache check
OBJ->>DRW: setNeedUpdateTurretPositioning(TRUE)
DRW->>MDL: setNeedUpdateTurretPositioning(TRUE)
loop Every render frame (rotating)
MDL->>MDL: handleClientTurretPositioning() runs
end
Note over TAI: Turret aligns
TAI->>OBJ: setNeedUpdateTurretPositioning(FALSE)
OBJ->>DRW: setNeedUpdateTurretPositioning(FALSE)
DRW->>MDL: setNeedUpdateTurretPositioning(FALSE)
loop Every render frame (idle)
MDL->>MDL: handleClientTurretPositioning() SKIPPED
end
Note over MDL: Weapon fires
MDL->>MDL: "handleWeaponFireFX sets m_doHandleRecoil=TRUE"
loop Every render frame (RECOIL/SETTLE)
MDL->>MDL: handleClientRecoil() runs
end
Note over MDL: All barrels reach IDLE, m_doHandleRecoil=FALSE
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant TAI as TurretAI (Logic)
participant OBJ as Object
participant DRW as Drawable
participant MDL as W3DModelDraw
Note over TAI: Turret starts rotating
TAI->>OBJ: setNeedUpdateTurretPositioning(TRUE)
OBJ->>OBJ: cache check
OBJ->>DRW: setNeedUpdateTurretPositioning(TRUE)
DRW->>MDL: setNeedUpdateTurretPositioning(TRUE)
loop Every render frame (rotating)
MDL->>MDL: handleClientTurretPositioning() runs
end
Note over TAI: Turret aligns
TAI->>OBJ: setNeedUpdateTurretPositioning(FALSE)
OBJ->>DRW: setNeedUpdateTurretPositioning(FALSE)
DRW->>MDL: setNeedUpdateTurretPositioning(FALSE)
loop Every render frame (idle)
MDL->>MDL: handleClientTurretPositioning() SKIPPED
end
Note over MDL: Weapon fires
MDL->>MDL: "handleWeaponFireFX sets m_doHandleRecoil=TRUE"
loop Every render frame (RECOIL/SETTLE)
MDL->>MDL: handleClientRecoil() runs
end
Note over MDL: All barrels reach IDLE, m_doHandleRecoil=FALSE
Reviews (3): Last reviewed commit: "Redo Fix for Turret Snapping" | Re-trigger Greptile
| if(!m_needUpdateTurretPosition && !m_lastNeedUpdateTurretPosition) | ||
| return; | ||
|
|
||
| m_lastNeedUpdateTurretPosition = m_needUpdateTurretPosition; |
There was a problem hiding this comment.
Single Object flag spans all turret slots
m_needUpdateTurretPosition is one Bool for the entire object, but handleClientTurretPositioning loops over MAX_TURRETS. Any path that clears the flag (e.g. a two-turret unit where RecenterTurretState finishes for slot 1) suppresses updates for every other turret slot. A per-slot flag array, or a reference-counted active-turret counter, would prevent one turret's quiescence from silencing a sibling that is still rotating.
Prompt To Fix With AI
This is a comment left during a code review.
Path: GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp
Line: 2426-2429
Comment:
**Single Object flag spans all turret slots**
`m_needUpdateTurretPosition` is one `Bool` for the entire object, but `handleClientTurretPositioning` loops over `MAX_TURRETS`. Any path that clears the flag (e.g. a two-turret unit where `RecenterTurretState` finishes for slot 1) suppresses updates for every other turret slot. A per-slot flag array, or a reference-counted active-turret counter, would prevent one turret's quiescence from silencing a sibling that is still rotating.
How can I resolve this? If you propose a fix, please make it concise.
Ignore the above, the issue is linked towards my GameClient Drawable Iteration Issue in another PR. That being said, without that Iteration feature even before the fix attempt, I cannot replicate the bug by moving the camera either by scrolling or clicking from the minimap. @Caball009 , able to send a video on replicating it? |
|
Here you go. firebase_turret_angle_snap.mp4 |
|
Thank you, I have reattempted the fix as TurretAI's function expands beyond AIUpdate. I have also tested the fix on the current updated version of the SH Repo. Should be fine now. |
|
Nice, I'll try to make some time to test it later. Can you rebase this branch? |
955c9e1 to
b402a9b
Compare
It's done. Sorry for the wait. |
…Update when Necessary
…a on Client's side
b402a9b to
ab42a7e
Compare
Modifies the GameLogic to tell handleClientTurretPositioning() and handleClientRecoil() function in W3DModelDraw to only update when Necessary.