BPTC modes for image_usePrecompressedTextures + image_useCompression

Both can now be set to 2 instead of just 0/1.
For image_usePrecompressedTextures 1 now means "use .dds files no matter
how they're compressed" and 2 means "only use .dds files if they are
compressed with BPTC/BC7 or are uncompressed".

For image_useCompression 1 means "compress textures with S3TC on upload"
(just like before) and 2 means "compress with BPTC/BC7 if available"

I wasn't sure whether this option makes sense for image_useCompression
(over always using BPTC if available), but I can imagine that loading
takes longer with BPTC (the driver has to compress the raw image data
and compressing to S3TC might be faster than for BPTC)
This commit is contained in:
Daniel Gibson 2025-04-08 05:57:09 +02:00
parent f3cd3f31d0
commit 24cd2f1b4e
3 changed files with 59 additions and 17 deletions

View file

@ -386,7 +386,7 @@ public:
static idCVar image_roundDown; // round bad sizes down to nearest power of two
static idCVar image_colorMipLevels; // development aid to see texture mip usage
static idCVar image_downSize; // controls texture downsampling
static idCVar image_useCompression; // 0 = force everything to high quality
static idCVar image_useCompression; // 0 = force everything to high quality 1 = compress with S3TC (DXT) 2 = compress with BPTC if possible
static idCVar image_filter; // changes texture filtering on mipmapped images
static idCVar image_anisotropy; // set the maximum texture anisotropy if available
static idCVar image_lodbias; // change lod bias on mipmapped images

View file

@ -53,10 +53,13 @@ idCVar idImageManager::image_forceDownSize( "image_forceDownSize", "0", CVAR_REN
idCVar idImageManager::image_roundDown( "image_roundDown", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "round bad sizes down to nearest power of two" );
idCVar idImageManager::image_colorMipLevels( "image_colorMipLevels", "0", CVAR_RENDERER | CVAR_BOOL, "development aid to see texture mip usage" );
idCVar idImageManager::image_preload( "image_preload", "1", CVAR_RENDERER | CVAR_BOOL | CVAR_ARCHIVE, "if 0, dynamically load all images" );
idCVar idImageManager::image_useCompression( "image_useCompression", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "0 = force everything to high quality" );
idCVar idImageManager::image_useCompression( "image_useCompression", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER,
"Compress textures on load so they use less VRAM. 1 = compress with S3TC/DXT when uploading 2 = compress with BPTC when uploading (if available) "
"0 = upload uncompressed (unless image_usePrecompressedTextures is 1 and it's loaded from a precompressed .dds file)" );
idCVar idImageManager::image_useAllFormats( "image_useAllFormats", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "allow alpha/intensity/luminance/luminance+alpha" );
idCVar idImageManager::image_useNormalCompression( "image_useNormalCompression", "2", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER, "2 = use rxgb compression for normal maps, 1 = use 256 color compression for normal maps if available" );
idCVar idImageManager::image_usePrecompressedTextures( "image_usePrecompressedTextures", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "use .dds files if present" );
idCVar idImageManager::image_usePrecompressedTextures( "image_usePrecompressedTextures", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_INTEGER,
"1 = use .dds files if present 2 = only use .dds files if they contain BPTC (BC7) textures (those have higher quality than S3TC/DXT) 0 = use uncompressed textures" );
idCVar idImageManager::image_writePrecompressedTextures( "image_writePrecompressedTextures", "0", CVAR_RENDERER | CVAR_BOOL, "write .dds files if necessary" );
idCVar idImageManager::image_writeNormalTGA( "image_writeNormalTGA", "0", CVAR_RENDERER | CVAR_BOOL, "write .tgas of the final normal maps for debugging" );
idCVar idImageManager::image_writeNormalTGAPalletized( "image_writeNormalTGAPalletized", "0", CVAR_RENDERER | CVAR_BOOL, "write .tgas of the final palletized normal maps for debugging" );

View file

@ -215,6 +215,11 @@ GLenum idImage::SelectInternalFormat( const byte **dataPtrs, int numDataPtrs, in
int rgbOr, rgbAnd, aOr, aAnd;
int rgbDiffer, rgbaDiffer;
// TODO: or always use BC7 if available? do textures take longer to load then?
// would look better at least...
const bool useBC7compression = glConfig.bptcTextureCompressionAvailable
&& globalImages->image_useCompression.GetInteger() == 2;
// determine if the rgb channels are all the same
// and if either all rgb or all alpha are 255
c = width*height;
@ -262,12 +267,17 @@ GLenum idImage::SelectInternalFormat( const byte **dataPtrs, int numDataPtrs, in
// catch normal maps first
if ( minimumDepth == TD_BUMP ) {
if ( globalImages->image_useCompression.GetBool() && globalImages->image_useNormalCompression.GetInteger() == 1 && glConfig.sharedTexturePaletteAvailable ) {
// DG: put the glConfig.sharedTexturePaletteAvailable check first because nowadays it's usually false
if ( glConfig.sharedTexturePaletteAvailable && globalImages->image_useCompression.GetBool() && globalImages->image_useNormalCompression.GetInteger() == 1 ) {
// image_useNormalCompression should only be set to 1 on nv_10 and nv_20 paths
return GL_COLOR_INDEX8_EXT;
} else if ( globalImages->image_useCompression.GetBool() && globalImages->image_useNormalCompression.GetInteger() && glConfig.textureCompressionAvailable ) {
// image_useNormalCompression == 2 uses rxgb format which produces really good quality for medium settings
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
if ( useBC7compression ) {
return GL_COMPRESSED_RGBA_BPTC_UNORM;
} else {
// image_useNormalCompression == 2 uses rxgb format which produces really good quality for medium settings
return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
}
} else {
// we always need the alpha channel for bump maps for swizzling
return GL_RGBA8;
@ -282,7 +292,7 @@ GLenum idImage::SelectInternalFormat( const byte **dataPtrs, int numDataPtrs, in
if ( minimumDepth == TD_SPECULAR ) {
// we are assuming that any alpha channel is unintentional
if ( glConfig.textureCompressionAvailable ) {
return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
return useBC7compression ? GL_COMPRESSED_RGBA_BPTC_UNORM : GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
} else {
return GL_RGB5;
}
@ -290,6 +300,9 @@ GLenum idImage::SelectInternalFormat( const byte **dataPtrs, int numDataPtrs, in
if ( minimumDepth == TD_DIFFUSE ) {
// we might intentionally have an alpha channel for alpha tested textures
if ( glConfig.textureCompressionAvailable ) {
if ( useBC7compression ) {
return GL_COMPRESSED_RGBA_BPTC_UNORM;
}
if ( !needAlpha ) {
return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
} else {
@ -319,7 +332,8 @@ GLenum idImage::SelectInternalFormat( const byte **dataPtrs, int numDataPtrs, in
return GL_RGB8; // four bytes
}
if ( glConfig.textureCompressionAvailable ) {
return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; // half byte
return useBC7compression ? GL_COMPRESSED_RGBA_BPTC_UNORM // 1byte/pixel
: GL_COMPRESSED_RGB_S3TC_DXT1_EXT; // half byte
}
return GL_RGB5; // two bytes
}
@ -327,7 +341,7 @@ GLenum idImage::SelectInternalFormat( const byte **dataPtrs, int numDataPtrs, in
// cases with alpha
if ( !rgbaDiffer ) {
if ( minimumDepth != TD_HIGH_QUALITY && glConfig.textureCompressionAvailable ) {
return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; // one byte
return useBC7compression ? GL_COMPRESSED_RGBA_BPTC_UNORM : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; // one byte
}
return GL_INTENSITY8; // single byte for all channels
}
@ -346,7 +360,7 @@ GLenum idImage::SelectInternalFormat( const byte **dataPtrs, int numDataPtrs, in
return GL_RGBA8; // four bytes
}
if ( glConfig.textureCompressionAvailable ) {
return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; // one byte
return useBC7compression ? GL_COMPRESSED_RGBA_BPTC_UNORM : GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; // one byte
}
if ( !rgbDiffer ) {
return GL_LUMINANCE8_ALPHA8; // two bytes, max quality
@ -1166,6 +1180,9 @@ void idImage::WritePrecompressedImage() {
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
header.ddspf.dwFourCC = DDS_MAKEFOURCC('D','X','T','5');
break;
case GL_COMPRESSED_RGBA_BPTC_UNORM:
header.ddspf.dwFourCC = DDS_MAKEFOURCC('B','C','7','0');
break;
}
} else {
header.ddspf.dwFlags = ( internalFormat == GL_COLOR_INDEX8_EXT ) ? DDSF_RGB | DDSF_ID_INDEXCOLOR : DDSF_RGB;
@ -1412,15 +1429,33 @@ bool idImage::CheckPrecompressedImage( bool fullLoad ) {
// DG: same if this is a BC7 (BPTC) texture but the GPU doesn't support that
// or if it uses the additional DX10 header and is *not* a BC7 texture
bool isBC7 = false;
if ( ddspf_dwFourCC == DDS_MAKEFOURCC( 'D', 'X', '1', '0' ) ) {
ddsDXT10addHeader_t *dx10Header = (ddsDXT10addHeader_t *)( data + 4 + sizeof(ddsFileHeader_t) );
unsigned int dxgiFormat = LittleInt( dx10Header->dxgiFormat );
if ( dxgiFormat != 98 // DXGI_FORMAT_BC7_UNORM
|| !glConfig.bptcTextureCompressionAvailable ) {
if (dxgiFormat != 98) {
common->Warning( "Image file '%s' has unsupported dxgiFormat %d - dhewm3 only supports DXGI_FORMAT_BC7_UNORM (98)!",
filename, dxgiFormat);
}
if ( dxgiFormat == 98 ) {
isBC7 = true;
} else {
common->Warning( "Image file '%s' has unsupported dxgiFormat %d - dhewm3 only supports DXGI_FORMAT_BC7_UNORM (98)!",
filename, dxgiFormat);
R_StaticFree( data );
return false;
}
} else if ( ddspf_dwFourCC == DDS_MAKEFOURCC( 'B', 'C', '7', '0' )
|| ddspf_dwFourCC == DDS_MAKEFOURCC( 'B', 'C', '7', 'L' ) )
{
isBC7 = true;
}
if ( isBC7 && !glConfig.bptcTextureCompressionAvailable ) {
R_StaticFree( data );
return false;
}
if ( glConfig.bptcTextureCompressionAvailable
&& globalImages->image_usePrecompressedTextures.GetInteger() == 2 )
{
// only high quality compressed textures, i.e. BC7 (BPTC), are welcome
// or uncompressed ones (that have no FOURCC flag set)
if ( !isBC7 && (ddspf_dwFlags & DDSF_FOURCC) != 0 ) {
R_StaticFree( data );
return false;
}
@ -1494,7 +1529,11 @@ void idImage::UploadPrecompressedImage( byte *data, int len ) {
case DDS_MAKEFOURCC( 'R', 'X', 'G', 'B' ):
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
case DDS_MAKEFOURCC( 'D', 'X', '1', '0' ): // BC7 aka BPTC
case DDS_MAKEFOURCC( 'B', 'C', '7', '0' ): // BC7 aka BPTC - inofficial FourCCs
case DDS_MAKEFOURCC( 'B', 'C', '7', 'L' ):
internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM;
break;
case DDS_MAKEFOURCC( 'D', 'X', '1', '0' ): // BC7 aka BPTC - the official dxgi way
additionalHeaderOffset = 20;
// Note: this is a bit hacky, but in CheckPrecompressedImage() we made sure
// that only BC7 UNORM is accepted if the FourCC is 'DX10'