Skip to content

bugfix(bgfx): Expand the A1R5G5B5 terrain atlas to BGRA8 off Windows - #4

Open
githubawn wants to merge 523 commits into
bobtista:bobtista/topic/trunkfrom
githubawn:fix/a1r5g5b5-atlas-expand
Open

bugfix(bgfx): Expand the A1R5G5B5 terrain atlas to BGRA8 off Windows#4
githubawn wants to merge 523 commits into
bobtista:bobtista/topic/trunkfrom
githubawn:fix/a1r5g5b5-atlas-expand

Conversation

@githubawn

@githubawn githubawn commented Jul 31, 2026

Copy link
Copy Markdown

WW3D_FORMAT_A1R5G5B5 maps to bgfx::TextureFormat::BGR5A1, which is not a native GLES format. Emitting it leaves bgfx to convert during upload, over-reading the source buffer, and what reaches the screen is the terrain atlas — the game's only large A1R5G5B5 surface — as coloured speckle across the ground.

Fix

Expand A1R5G5B5 to BGRA8 up front, exactly as the A4R4G4B4 path beside it already does. Three linked places all need it, which is why a partial change appears to do nothing:

  1. GetBgfxTextureUploadFormat() expanded A4R4G4B4 but not A1R5G5B5
  2. IsTerrainAtlasTexture() only accepted BGR5A1, so expanding the format alone would have silently cost the terrain its tile-aware mip chain and fallen back to a generic one
  3. UploadTerrainAtlasMips() hardcoded BGR5A1 and uploaded raw 16-bit data

The atlas is filtered as 16-bit throughout — that is what the tile-aware mip builder works on — so every level, authored or generated, is widened just before it is handed to bgfx. CopyTextureLevel already knows how to widen A1R5G5B5, so it is asked for the upload format outright and the mip builder's 16-bit input comes from the source snapshot, avoiding a second allocation.

Platform impact

All of it sits behind !defined(_WIN32); Windows keeps the native 16-bit upload byte for byte.

Testing

Verified on the Emscripten/WebGL build: the shell map's sand and rock rendered as coloured speckle before and correctly after. Not runtime-verified on Android GLES or desktop GL, though they take the same non-Windows path.

bobtista added 30 commits July 30, 2026 22:35
… on Windows

(cherry picked from commit 3704ae283516a47b27d4a5511566d4f8f6169a7c)
(cherry picked from commit c2e4ebd508ef4ebdaa1bdc1ac513632dce0bf5a6)
…n-Windows builds

(cherry picked from commit bc2a0e474438860d80addcbf6e1795624624ecd9)
(cherry picked from commit 79c1e3c42fb96fa060f78720ed80ab3f424162a4)
…destroyed

(cherry picked from commit 757b56e262097d43b3dbba912e1026fef11a6efe)
(cherry picked from commit 5e6c40439b05766d872e32e4a3f5b780bd92f7ab)
…pling can't read out of bounds and crash

(cherry picked from commit bcf96610c984fde5d1bd614cb94569c06ba13db6)
(cherry picked from commit d3f978645a3a89cb747220d6aa9f58631b0e87e6)
…ady on construction complete (non-retail CRC)

(cherry picked from commit 323a3c220da03d8e7b7c636a5fc049d6f9715f9b)
(cherry picked from commit dbee96e4b2aeb170ff4f89a9d64af98881ecae7a)
…erals Challenge so the next general isn't instantly defeated

(cherry picked from commit 255fc6bacfa2db0a346b87f550f0dffff8889237)
(cherry picked from commit 053938d17936b1cba41436c658a0bf38f5467ba5)
…e Continue starts the next battle instead of an instant defeat

(cherry picked from commit ba12ef4621e95d7719c542f17388802532f40ba1)
(cherry picked from commit 09110dd45e6b4dcfcac75b5334de6d7fe9a6866d)
…int so debug builds link

(cherry picked from commit a3d78a591598b5abadd60470de79d4077e42e252)
(cherry picked from commit 60e92e4fa5dc9559f87ecdb9a5b4c6bada19ae42)
…_ZOOM hack, deferring camera height limits to GameData.ini

(cherry picked from commit a2625f8ee57fb8c9955a8f5a52444f7f1d4dd1b2)
(cherry picked from commit 79fe3af9c0bfe5e6bf8d0302678b414251b05333)
(cherry picked from commit 6aca92fca0d16b65a30b76ced3d0cc40e2d528ee)
(cherry picked from commit 8b5dceefe4b42fc9c7c1420565efaa5d1170337c)
bobtista and others added 23 commits July 30, 2026 22:37
BGR5A1 is not a native GLES format. Emitting it leaves bgfx to convert during
upload, over-reading the source buffer, and what reaches the screen is the
terrain atlas - the game's only large A1R5G5B5 surface - as coloured speckle
across the ground.

Expand A1R5G5B5 to BGRA8 up front, exactly as the A4R4G4B4 path beside it
already does, and accept the expanded format where the atlas is recognized so
the terrain keeps its tile-aware mip chain rather than silently falling back to
a generic one.

The atlas is filtered as 16-bit throughout - that is what the tile-aware mip
builder works on - so every level, authored or generated, is widened just before
it is handed to bgfx. CopyTextureLevel already knows how to widen A1R5G5B5, so
it is asked for the upload format outright and the mip builder's 16-bit input is
taken from the source snapshot, which avoids a second allocation.

All of it sits behind !defined(_WIN32); Windows keeps the native 16-bit upload
byte for byte.
@githubawn
githubawn force-pushed the fix/a1r5g5b5-atlas-expand branch from 7b93ced to 0c65212 Compare July 31, 2026 11:12
// matte pixels used by projected decals such as spy satellite grids.
return bgfx::TextureFormat::BGRA8;
}
#if !defined(_WIN32)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This needs to follow backend behavior rather than host OS. In the pinned bgfx, Emscripten maps BGR5A1 to GL_RGBA/GL_UNSIGNED_SHORT_5_5_5_1 and still advertises BGFX_CAPS_FORMAT_TEXTURE_2D, so a caps-only check would miss WebGL. On other renderers, the cap can correctly indicate that native BGR5A1 upload is unavailable.

Please expand on Emscripten or when native BGR5A1 2D support is absent; !defined(_WIN32) unnecessarily expands native Metal and desktop GL uploads.

Please also revise the WebGL explanation: the pinned bgfx source shows an A1R5G5B5-versus-RGBA5551 packed-layout mismatch while the upload remains 16 bpp, and I do not see evidence of a source-buffer over-read on this path.

prev.resize(static_cast<size_t>(mipWidth) * mipHeight);
for (unsigned y = 0; y < mipHeight; ++y)
{
std::memcpy(&prev[static_cast<size_t>(y) * mipWidth],

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This bypasses CopyTextureLevel’s pitch normalization and unconditionally reads 2 * mipWidth bytes from every source row. A short-pitch snapshot passes the existing validation, and both this copy and the new ExpandA1R5G5B5ToBGRA8 can then read past the final row. The adjacent A4R4G4B4 expander has the same inherited weakness, but this change should not add another instance. Please either require Pitch >= 2 * width, or clamp and zero-fill consistently in both places. Clamping alone is insufficient because prev.resize() may preserve pixels from the preceding level. On the native BGR5A1 path, continuing to seed prev from mem->data preserves the previous pitch-normalized behavior.

@bobtista
bobtista force-pushed the bobtista/topic/trunk branch 4 times, most recently from 66cf60b to 0f1e948 Compare August 3, 2026 19:03
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