Skip to content

Add BC7 (BPTC) DDS texture support#2515

Open
juliendms wants to merge 1 commit into
CelestiaProject:masterfrom
juliendms:feat/bc7-dds
Open

Add BC7 (BPTC) DDS texture support#2515
juliendms wants to merge 1 commit into
CelestiaProject:masterfrom
juliendms:feat/bc7-dds

Conversation

@juliendms
Copy link
Copy Markdown

@juliendms juliendms commented May 5, 2026

Description

BC7-encoded DDS files use the modern DX10 FourCC plus a 20-byte
DDS_HEADER_DXT10 extension containing a dxgiFormat field
(98 = BC7_UNORM, 99 = BC7_UNORM_SRGB). The current loader rejects
this header, so BC7 textures cannot be loaded even though the upload
path (glCompressedTexImage2D with GL_COMPRESSED_RGBA_BPTC_UNORM)
already works on any GL 4.2+ driver.

The patch:

  • recognises the DX10 FourCC in dds.cpp, reads the 20-byte extension
    and decodes dxgiFormat 97/98/99 (BC7 typeless / UNORM / UNORM_SRGB);
  • adds PixelFormat::BC7 and PixelFormat::BC7_sRGBA mapped to
    GL_COMPRESSED_RGBA_BPTC_UNORM / GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;
  • wires those formats through image.cpp (block size, components, alpha,
    compressed flag) and texture.cpp (internal/external GL format) --
    block geometry is identical to DXT5, so the size math is verbatim;
  • probes GL_ARB_texture_compression_bptc in glsupport, mirroring the
    existing EXT_texture_compression_s3tc flag, and refuses BC7 files
    when BPTC is unavailable rather than handing them to the s3tc software
    fallback (which only knows DXT1/3/5).

The CPU-side software decompressor (dds_decompress.cpp) is intentionally
left untouched. BC7 software decoding is non-trivial (8 modes, partition
tables, endpoint encodings) and unnecessary on any modern GL driver.

Tested

Win32 build (VS 2026 / MSVC 19.50 / CMake 4.2), Windows 11, RX 6750 XT,
against several BC7-encoded planet textures (diffuse, specular, cloud,
night-side) up to 16384^2. Existing DXT1/3/5 loading paths verified
unchanged.

Out of scope

  • BC7 inside .ctx virtual-texture tilesets -- virtualtex.cpp has its
    own DDS reader path that does not go through LoadDDSImage. I plan on investigating CTX pipeline in the coming weeks.
  • BC6H / other DXGI formats -- only BC7 UNORM and UNORM_SRGB are decoded.

Screenshots

IO with all textures in 16k BC7
io bc7

Earth cloud with 16k BC7
earth-cloud-bc7


Disclaimer: The code in this PR was generated by AI
(Anthropic Claude), then reviewed, built and tested by me before
submission.

@juliendms juliendms marked this pull request as ready for review May 5, 2026 16:42
@375gnu
Copy link
Copy Markdown
Collaborator

375gnu commented May 6, 2026

I suppose we should decide about ai contributions.

@juliendms juliendms marked this pull request as draft May 7, 2026 08:50
@juliendms
Copy link
Copy Markdown
Author

juliendms commented May 7, 2026

I put this PR as draft because although "it works" the current implementation is kind of misleading.

  • To render sRGB content (eg. a JPEG texture) encoded in BC7, right now it needs to be encoded as BC7_UNORM to be correctly rendered by Celestia.
  • Encoding sRGB content as BC7_UNORM_SRGB renders incorrectly (too dark) because the GPU applies an sRGB-to-linear transformation, but nothing re-encodes it to sRGB for display.
  • When Rework sRGB support #2483 is merged, I will be able to wire properly BC7_UNORM and BC7_UNORM_SRGB, and BC7_UNORM_SRGB will properly render sRGB content with the SRGBRendering flag set to true.

TL;DR: PR blocked until #2483 is merged for clean implementation.

@ajtribick
Copy link
Copy Markdown
Collaborator

I suppose we should decide about ai contributions.

Can't speak for anyone else, but reviewing AI output doesn't feel like a good use of my free time.

BC7 DDS files use a DX10 FourCC followed by a 20-byte DXT10 extension
header containing a dxgiFormat field. The previous parser only handled
the legacy DXT1/DXT3/DXT5 FourCCs and rejected DX10 outright, which made
BC7-encoded textures unusable even though the GPU upload path
(glCompressedTexImage2D) needs no decompression on the CPU.

Changes:
- pixelformat.h: add PixelFormat::BC7 / BC7_sRGBA enums mapped to the
  GL_COMPRESSED_RGBA_BPTC_UNORM and GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM
  tokens.
- image.cpp: BC7 has the same 4x4 / 16-byte block geometry as DXT5, so
  the format is added alongside DXT5 in formatComponents,
  calcMipLevelSize, isCompressed and hasAlpha. getLinearFormat maps
  BC7_sRGBA -> BC7.
- texture.cpp: getInternalFormat / getExternalFormat handle BC7 in the
  desktop GL branch so the upload calls do not silently get GL_NONE.
  effectiveFormat() also strips BC7_sRGBA to BC7 when sRGB rendering
  is disabled, matching the treatment of the DXTn_sRGBA formats from
  PR CelestiaProject#2483 so a single sRGB-tagged corpus renders correctly in both
  modes.
- dds.cpp: extend the DXGIFormat enum and GetDXT10Format() introduced
  by PR CelestiaProject#2483 to cover BC7_UNORM (98) and BC7_UNORM_SRGB (99).
  IsCompressedFormat covers BC7. BC7 has no software fallback, so
  LoadDDSImage refuses the file when GL_ARB_texture_compression_bptc
  is unavailable and the existing CPU decompressor is gated to s3tc
  formats only.
- glsupport.{h,cpp}: probe GL_ARB_texture_compression_bptc, mirroring
  the existing s3tc flag.

The CPU-side software decompressor (dds_decompress.cpp) is intentionally
left untouched; BC7 software decoding is non-trivial (8 modes, partition
tables, endpoint encodings) and is not needed when the GPU advertises
BPTC, which any GL 4.2+ driver does.
@juliendms
Copy link
Copy Markdown
Author

  • Extended the sRGB→non-sRGB strip table consulted on the upload path with the BC7_sRGBA → BC7 mapping. Without this, a BC7-sRGB-tagged DDS loaded with sRGB rendering disabled would hit the sampler with a hardware sRGB decode applied and no compensating re-encode downstream, rendering one sRGB-curve too dark (see previous comment)
  • Extended the merged DX10 header: shared DXGIFormat enum and dispatch helper gain BC7_UNORM = 98 and BC7_UNORM_SRGB = 99.

Results:

Tests: Jupiter is BC7_sRGBA and Io is BC7, both carry sRGB content.

When sRGB is deactivated: BC7 and BC7_sRGBA renders exactly the same.
sRGB_false

When sRGB is activated: BC7 expects linear encode, while BC7_sRGBA must be used for sRBG encoded content. Here Jupiter renders as expected, while Io renders incorrectly (as expected, because of incorrect DDS flag).
sRGB_true

@juliendms juliendms marked this pull request as ready for review May 12, 2026 09:18
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.

3 participants