Skip to content

fix(textureloader): Simplify and fix faulty implementations of texture reduction (2)#2814

Open
xezon wants to merge 2 commits into
TheSuperHackers:mainfrom
xezon:xezon/fix-mipmaps
Open

fix(textureloader): Simplify and fix faulty implementations of texture reduction (2)#2814
xezon wants to merge 2 commits into
TheSuperHackers:mainfrom
xezon:xezon/fix-mipmaps

Conversation

@xezon

@xezon xezon commented Jun 20, 2026

Copy link
Copy Markdown

This change further simplifies and fixes the implementations of texture reduction.

I tested retail vs patched game with different texture reduction levels and it looked identical.

@xezon xezon added Minor Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour Refactor Edits the code with insignificant behavior changes, is never user facing Fix Is fixing something, but is not user facing labels Jun 20, 2026
@greptile-apps

greptile-apps Bot commented Jun 20, 2026

Copy link
Copy Markdown

Greptile Summary

This PR follows up on #2789 by further simplifying and fixing the texture reduction pipeline for all three texture types (2D, cube, and volume). The core fix moves the reduced Width/Height/Depth values into the class members in Begin_Compressed_Load, eliminating the duplicate >>= Reduction shift that was incorrectly re-applied inside Load_Compressed_Mipmap. Apply_Mip_Reduction is also refactored to subtract the reduction amount in a single place after both branches, and a WWASSERT is added to guard the unsigned subtraction.

  • Double-reduction bug fixed: Load_Compressed_Mipmap for all three texture types previously re-shifted width/height by Reduction even though Get_Width()/Get_Height() were intended to return the already-unreduced originals; the new flow stores the already-reduced dimensions in Width/Height from the beginning, so no second shift is needed.
  • MipLevelCount initialization corrected: The task constructor now initialises MipLevelCount to MIP_LEVELS_ALL instead of the raw literal 0, matching the sentinel value the rest of the code checks.
  • Named constants and assertion added: Magic literals 4u/1u are replaced by MinTextureDim/MinTextureDepth, and WWASSERT guards are added before unsigned subtractions and surface copies to catch invariant violations early.

Confidence Score: 4/5

Safe to merge with one issue worth addressing: WWASSERT(reduction < mip_level_count) before an unsigned subtraction is the only runtime guard; if that assert compiles out in retail builds and the invariant is violated by a corrupt DDS, the subtraction wraps around and the resulting huge mip count is passed directly to the D3D texture-creation API.

The logic consolidation in Apply_Mip_Reduction — subtracting reduction unconditionally after both branches — is correct, and Validate_Reduction normally guarantees the invariant holds. However, the old code had an explicit mip_level_count < 1 safety floor that was removed without a release-safe equivalent. If a corrupt file produces an unexpected mip_count, the unsigned wrap-around on mip_level_count -= reduction can silently produce a catastrophically large value that gets forwarded to DirectX.

Core/Libraries/Source/WWVegas/WW3D2/textureloader.cpp — specifically the Apply_Mip_Reduction function and the unsigned subtraction at line 1496.

Important Files Changed

Filename Overview
Core/Libraries/Source/WWVegas/WW3D2/textureloader.cpp Fixes texture reduction logic across 2D, cube, and volume texture load paths: constructor init, removal of double-reduction in Load_Compressed_Mipmap, consolidation of mip subtraction in Apply_Mip_Reduction, and named constants. Logic is correct; one potential unsigned underflow path if the WWASSERT is compiled out in release.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Begin_Compressed_Load] --> B[Get_Texture_Information]
    B --> C[Validate_Reduction\nensures reduction < mip_count]
    C --> D[Width = orig_width\nHeight = orig_height]
    D --> E[Apply_Dim_Reduction\nstored directly in Width/Height]
    E --> F[Apply_Mip_Reduction\nuses already-reduced Width/Height]
    F --> G{mip_level_count == MIP_LEVELS_ALL?}
    G -- Yes --> H[mip_level_count = mip_count]
    G -- No --> I[mip_level_count = min requested, mip_count]
    H --> J[WWASSERT reduction < mip_level_count]
    I --> J
    J --> K[mip_level_count -= reduction]
    K --> L[clamp to max_mip_level_count]
    L --> M[Create DX8 Texture with reduced Width x Height]
    M --> N[Load_Compressed_Mipmap]
    N --> O[width = Get_Width - already reduced]
    O --> P{level < mip_level_count?}
    P -- Yes --> Q[WWASSERT width >= 4 && height >= 4]
    Q --> R[Copy_Level_To_Surface]
    R --> S[width >>= 1 / height >>= 1]
    S --> P
    P -- No --> T[Done]
Loading
%%{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"}}}%%
flowchart TD
    A[Begin_Compressed_Load] --> B[Get_Texture_Information]
    B --> C[Validate_Reduction\nensures reduction < mip_count]
    C --> D[Width = orig_width\nHeight = orig_height]
    D --> E[Apply_Dim_Reduction\nstored directly in Width/Height]
    E --> F[Apply_Mip_Reduction\nuses already-reduced Width/Height]
    F --> G{mip_level_count == MIP_LEVELS_ALL?}
    G -- Yes --> H[mip_level_count = mip_count]
    G -- No --> I[mip_level_count = min requested, mip_count]
    H --> J[WWASSERT reduction < mip_level_count]
    I --> J
    J --> K[mip_level_count -= reduction]
    K --> L[clamp to max_mip_level_count]
    L --> M[Create DX8 Texture with reduced Width x Height]
    M --> N[Load_Compressed_Mipmap]
    N --> O[width = Get_Width - already reduced]
    O --> P{level < mip_level_count?}
    P -- Yes --> Q[WWASSERT width >= 4 && height >= 4]
    Q --> R[Copy_Level_To_Surface]
    R --> S[width >>= 1 / height >>= 1]
    S --> P
    P -- No --> T[Done]
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
Core/Libraries/Source/WWVegas/WW3D2/textureloader.cpp:1495-1496
**Unsigned underflow if assert is disabled in release**

`WWASSERT` is typically stripped in release/retail builds. If, despite `Validate_Reduction`'s guards, `reduction >= mip_level_count` ever occurs (e.g., due to a corrupt DDS file returning a smaller-than-expected `mip_count`), the unsigned subtraction on the next line silently wraps around to `~0u`. That value is then passed as `MipLevelCount` to `DX8Wrapper::_Create_DX8_Texture`, almost certainly producing a crash or D3D error rather than a graceful fallback. The old code had an explicit `if (mip_level_count < 1) mip_level_count = 1;` safety net; the new consolidation removed it without an equivalent release-safe guard.

Reviews (2): Last reviewed commit: "Simplify Apply_Mip_Reduction" | Re-trigger Greptile

Comment thread Core/Libraries/Source/WWVegas/WW3D2/textureloader.cpp Outdated
Comment thread Core/Libraries/Source/WWVegas/WW3D2/textureloader.cpp Outdated
Comment thread Core/Libraries/Source/WWVegas/WW3D2/textureloader.cpp Outdated
@xezon xezon requested a review from Skyaero42 June 24, 2026 18:27
Comment thread Core/Libraries/Source/WWVegas/WW3D2/textureloader.cpp

@Skyaero42 Skyaero42 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looks good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Fix Is fixing something, but is not user facing Gen Relates to Generals Minor Severity: Minor < Major < Critical < Blocker Refactor Edits the code with insignificant behavior changes, is never user facing ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants