feat(options): Implement game options for texture filter mode, anisotropy and MSAA selection#2482
Conversation
|
| Filename | Overview |
|---|---|
| Core/GameEngine/Include/Common/OptionPreferences.h | Adds AliasingMode enum and three new accessor declarations — clean, no issues. |
| Core/GameEngine/Source/Common/OptionPreferences.cpp | Implements the three new preference accessors; clamp return values are correctly captured here, and highestBit is used to round to valid power-of-2 levels. No issues. |
| Core/Libraries/Include/Lib/BaseType.h | Adds highestBit template that rounds down to the nearest power-of-2. Correct for unsigned / non-negative inputs; signed negative inputs would produce UB on the final cast, but all call sites use non-negative values. |
| Core/Libraries/Source/WWVegas/WW3D2/texturefilter.cpp | Defines TextureFilterModeString array and updates _Init_Filters signature to accept an anisotropy level instead of hard-coding 2X. Clean change. |
| Core/Libraries/Source/WWVegas/WW3D2/texturefilter.h | Exposes TextureFilterModeString and updates _Init_Filters declaration to take an anisotropy level. No issues. |
| GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h | Replaces m_antiAliasBoxValue with three properly-typed fields for alias level, texture filter mode, and anisotropy level. Clean change. |
| GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp | Initialises the three new GlobalData fields with sensible defaults and populates them from preferences during load. No issues. |
| GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp | Anti-aliasing combo-box save/load logic is correct; however all three clamp guard calls (antialiasing, texture filter, anisotropy) discard their return values, leaving the guard inactive. |
| GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp | Applies MSAA mode from GlobalData before device creation and re-reads it plus texture filter/anisotropy settings back after device init to capture GPU capability limits. No issues. |
| GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/dx8wrapper.cpp | Passes both texture filter and anisotropy level to _Init_Filters to match the updated signature. No issues. |
| GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/ww3d.cpp | Adds AnisotropyLevel static and Set_Anisotropy_level with correct sequential <= clamping to valid enum values. No issues. |
| GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/ww3d.h | Assigns explicit integer values to MultiSampleModeEnum entries (0, 2, 4, 8) and exposes the new anisotropy level getter/setter pair. No issues. |
Sequence Diagram
sequenceDiagram
participant INI as Options.ini
participant OP as OptionPreferences
participant GD as GlobalData
participant OPM as OptionsMenu (GUI)
participant W3D as WW3D
participant TF as TextureFilterClass
participant DX as DX8Wrapper
Note over INI,DX: Startup / Game Init
INI->>OP: load("Options.ini")
OP->>GD: getAntiAliasing() → m_antiAliasLevel
OP->>GD: getTextureFilterMode() → m_textureFilteringMode
OP->>GD: getTextureAnisotropyLevel() → m_textureAnisotropyLevel
GD->>W3D: Set_MSAA_Mode(m_antiAliasLevel)
W3D->>DX: Set_Render_Device(...)
DX->>TF: _Init_Filters(TextureFilter, AnisotropyLevel)
W3D->>GD: Get_MSAA_Mode() → m_antiAliasLevel (GPU-clamped)
W3D->>GD: Set_Texture_Filter / Get_Texture_Filter → m_textureFilteringMode
W3D->>GD: Set_Anisotropy_level / Get_Anisotropy_level → m_textureAnisotropyLevel
Note over INI,DX: Options Menu Save
OPM->>GD: GadgetComboBox index → m_antiAliasLevel
OPM->>INI: Write AntiAliasing, TextureFilter, AnisotropyLevel
Prompt To Fix All With AI
This is a comment left during a code review.
Path: GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp
Line: 561
Comment:
**`clamp` return value discarded in all three guard calls**
`clamp` is a pure function — it returns the clamped value and does not modify its argument. All three calls below discard the return value, so the clamping has no effect whatsoever:
- Line 561: `index` is not clamped before the `1 << index` shift
- Line 575: `val` is not clamped before indexing into `TextureFilterModeString[val]`
- Line 588: `val` is not clamped before writing the anisotropy preference
The intended guard for the out-of-bounds array access on line 579 (`TextureFilterModeString[val]`) is completely inactive. In practice the upstream `get*` functions already return validated values, but the defensive intent is broken and this would become an unsafe out-of-bounds access the moment those guarantees change.
```suggestion
index = clamp((int)OptionPreferences::MSAA_OFF, index, (int)OptionPreferences::MSAA_8X);
```
Same pattern needs to be applied on lines 575 and 588:
```cpp
val = clamp((int)TextureFilterClass::TEXTURE_FILTER_NONE, val, (int)TextureFilterClass::TEXTURE_FILTER_ANISOTROPIC);
// ...
val = clamp((int)TextureFilterClass::TEXTURE_FILTER_ANISOTROPIC_2X, val, (int)TextureFilterClass::TEXTURE_FILTER_ANISOTROPIC_16X);
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (6): Last reviewed commit: "feat(options): Implement game options fo..." | Re-trigger Greptile
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp
Show resolved
Hide resolved
120d3db to
cfff7d1
Compare
|
Generals is currently broken till i replicate across due to changes in initaialising the texture filtering |
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp
Outdated
Show resolved
Hide resolved
915e548 to
53085fc
Compare
xezon
left a comment
There was a problem hiding this comment.
This implementation can be polished more.
|
|
||
| enum AliasingMode CPP_11(: Int) | ||
| { | ||
| OFF = 0, |
There was a problem hiding this comment.
Please put more into the name to avoid shadowing or macro conflicts.
|
|
||
| UnsignedInt getAntiAliasing(); | ||
| UnsignedInt getTextureFilterMode(); | ||
| UnsignedInt getTextureAnisotropyLevel(); |
| level = WW3D::MultiSampleModeEnum::MULTISAMPLE_MODE_8X; | ||
|
|
||
| if (level > WW3D::MultiSampleModeEnum::MULTISAMPLE_MODE_8X) | ||
| level = WW3D::MultiSampleModeEnum::MULTISAMPLE_MODE_8X; |
There was a problem hiding this comment.
enums are unscoped, so MultiSampleModeEnum:: is redundant.
| else if (level <= WW3D::MultiSampleModeEnum::MULTISAMPLE_MODE_4X) | ||
| level = WW3D::MultiSampleModeEnum::MULTISAMPLE_MODE_4X; | ||
| else if (level <= WW3D::MultiSampleModeEnum::MULTISAMPLE_MODE_8X) | ||
| level = WW3D::MultiSampleModeEnum::MULTISAMPLE_MODE_8X; |
There was a problem hiding this comment.
I think it is a bit odd that Value 5 would become 8. I would expect 7 becomes 4.
Can be simplified and improved by doing
Int level = atoi(it->second.str());
level = clamp(WW3D::MULTISAMPLE_MODE_FIRST, level, WW3D::MULTISAMPLE_MODE_LAST);
level = highestBit(level);Then add in BaseType.h or so
template <typename T>
T highestBit(T v)
{
static_assert(sizeof(T) <= 8, "T must be smaller equal 8");
UnsignedInt64 i = static_cast<UnsignedInt64>(v);
i |= i >> 1;
i |= i >> 2;
i |= i >> 4;
i |= i >> 8;
i |= i >> 16;
i |= i >> 32;
return static_cast<T>(i - (i >> 1));
}This will work for as long as the values are expected to be power of 2, which I think is always the case.
Same for AnisotropicFilterMode
|
|
||
| UnsignedInt OptionPreferences::getTextureAnisotropyLevel() | ||
| { | ||
| OptionPreferences::const_iterator it = find("AnisotropyLevel"); |
There was a problem hiding this comment.
Is this an intuitive term for players? If I am not mistaken NVIDIA and AMD drivers call this Anisotropic.
There was a problem hiding this comment.
It's often hidden by having the user facing texture filter mode option showing Anisotropic x2, Anisotropic x4 etc
The underlying graphics API's use Anisotropy level to set the sampling mode for Anisotropic filtering.
| m_antiAliasBoxValue = 0; | ||
| m_antiAliasLevel = WW3D::MultiSampleModeEnum::MULTISAMPLE_MODE_NONE; | ||
| m_textureFilteringMode = TextureFilterClass::TextureFilterMode::TEXTURE_FILTER_BILINEAR; | ||
| m_textureAnisotropyLevel = TextureFilterClass::AnisotropicFilterMode::TEXTURE_FILTER_ANISOTROPIC_2X; |
There was a problem hiding this comment.
Do these need to be in GlobalData or is there a better place for them?
There was a problem hiding this comment.
There's not really a nice place to centralise them apart from global data, Texture Class is mostly handled by static functions and MSAA is handled inside dx8wrapper through ww3d.
| return load("Options.ini"); | ||
| } | ||
|
|
||
| UnsignedInt OptionPreferences::getAntiAliasing() |
There was a problem hiding this comment.
Should this return the enum type instead?
There was a problem hiding this comment.
I was avoiding adding ww3d and texturefilter headers to the globaldata header
There was a problem hiding this comment.
If you move the enums out of TextureFilterClass then you can forward declare them.
| mode = WW3D::MultiSampleModeEnum::MULTISAMPLE_MODE_4X; | ||
| break; | ||
| case OptionPreferences::AliasingMode::X8: | ||
| mode = WW3D::MultiSampleModeEnum::MULTISAMPLE_MODE_8X; |
There was a problem hiding this comment.
This can also be simplified by using bit shift
mode = (index > 0) ? 1 << index : 0;| prefString = "Trilinear"; | ||
| break; | ||
| case TextureFilterClass::TextureFilterMode::TEXTURE_FILTER_ANISOTROPIC: | ||
| prefString = "Anisotropic"; |
There was a problem hiding this comment.
I suggest provide an enum to string array or function near the enum declaration instead so that the string mapping could be reused elsewhere too and is closer to the enum declaration, which reduces maintenance mistakes.
e222f4b to
09fc9af
Compare
…sotropy level (#2482)
09fc9af to
a4f900f
Compare
|
Updated based on feedback |
Merge By Rebase
Closes: #2474
Closes: #2375
This PR implements options for allowing the enablement of MSAA and the selection of different texture filtering modes and setting the anisotropic filtering level.
The following options are added for anti aliasing
AntiAliasing = 0, 2, 4, 8
The numbers relate to the MSAA sample level and Zero disables MSAA.
TextureFilter = None, Point, Bilinear, Trilinear, Anisotropic
Self explanatory, just choose one and see how the game looks
AnisotropyLevel = 2, 4, 8, 16
The anisotropy level only comes into play when Anisotropic filtering is set. 2 is also the minimum value.
Generals is broken untill the changes are replicated due to changes in texture filter init.