Skip to content

Keep texture filter and wrap per stage, as the DirectX driver does - #14

Merged
nsgomez merged 1 commit into
nsgomez:mainfrom
aspctt:per-stage-texture-parameters
Oct 25, 2026
Merged

nsgomez merged 1 commit into
nsgomez:mainfrom
aspctt:per-stage-texture-parameters

Conversation

@aspctt

@aspctt aspctt commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

TexParameter keeps one set of filter and wrap values for both stages, and the loop that should flag every stage for a refresh only flags stage 0 (textureUnits[0] instead of textureUnits[i]). The refresh also calls glTexParameteri without making that stage's unit active, so stage 0's values can land on whatever stage 1 has bound.

Fixing the index alone would make every value reach both stages, so stage 1's clamps would land on stage 0's textures. In scvk, my Vulkan driver, that model gave black patches on the terrain.

What the DirectX driver does, from SimCity 4 version 641:

  • TexParameter (0x884990) writes the value into the active stage's record (0x80 bytes per stage, filter and wrap at +0xc to +0x18). SetTexture (0x884810) never touches those fields, so filter and wrap belong to the stage, not to the texture.
  • The game keeps its own cache of wrap (0x7d41e0) and filter (0x7d4280) per stage, and skips TexStage and TexParameter when the stage already holds the value. It relies on each stage keeping its own values.

Changes:

  • Each texture unit keeps its own filter and wrap, and TexParameter sets the selected unit's.
  • A unit applies its values to the texture it has bound, after a bind or a change, with its own unit active.
  • After a draw applies the units, the active unit goes back to the one the game selected with TexStage. A draw could leave another unit active before, and TexEnv, TexStageMatrix and the texture uploads in CreateTexture and LoadTextureLevel all act on the active unit.

In scvk this model fixed building shadows repeating along streets (the shadow mask on stage 1 needs clamp) and black slabs on the cut-away terrain beside sloped lots.

OpenGL still keeps the values on the texture object, so one texture bound to both stages with different values at the same time gets the last stage's.

Tested in game on version 641 with an NVIDIA RTX 3070, against upstream main: building shadows along streets and cloud shadows look right.

- TexParameter sets the value on the selected stage only, instead of one set of values shared by both stages
- Each stage applies its own values to the texture it has bound, with its unit active
- The active unit goes back to the one the game selected once a draw has applied the stages
@nsgomez

nsgomez commented Oct 25, 2026

Copy link
Copy Markdown
Owner

Thanks, cheers

@nsgomez
nsgomez merged commit 44a511d into nsgomez:main Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants