Downsample textures on import if necessary for technical reasons

Compression formats are limited to various sizes (16383×16383 for
WebP, 16384×16384 for Basis Universal). If the size is exceeded,
the texture fails to import. To avoid this, textures are now downsampled
with a warning printed when necessary.

The warning is not printed if the user has specified a size limit
and the size limit is being honored.

This also allows setting Size Limit up to 16383 pixels (the lowest
common denominator out of all compression modes), instead of being
limited to 4096 pixels.
This commit is contained in:
Hugo Locurcio 2024-06-22 00:14:21 +02:00
parent 8a6c1e8f52
commit cd7a708952
No known key found for this signature in database
GPG Key ID: 39E8F8BE30B0A49C
2 changed files with 39 additions and 3 deletions

View File

@ -90,9 +90,13 @@
<member name="process/size_limit" type="int" setter="" getter="" default="0">
If set to a value greater than [code]0[/code], the size of the texture is limited on import to a value smaller than or equal to the value specified here. For non-square textures, the size limit affects the longer dimension, with the shorter dimension scaled to preserve aspect ratio. Resizing is performed using cubic interpolation.
This can be used to reduce memory usage without affecting the source images, or avoid issues with textures not displaying on mobile/web platforms (as these usually can't display textures larger than 4096×4096).
[b]Note:[/b] Even if this is set to [code]0[/code], import size is limited to the following dimensions for technical reasons. Depending on [member compress/mode], textures will be downsampled on import if necessary:
- [b]Lossy:[/b] 16383 pixels width or height, whichever is larger;
- [b]Basis Universal:[/b] 16384 pixels width or height, whichever is larger;
- [b]All other modes:[/b] 32768 pixels width or height, whichever is larger.
</member>
<member name="roughness/mode" type="int" setter="" getter="" default="0">
The color channel to consider as a roughness map in this texture. Only effective if Roughness &gt; Src Normal is not empty.
The color channel to consider as a roughness map in this texture. Only effective if [member roughness/src_normal] is not empty.
</member>
<member name="roughness/src_normal" type="String" setter="" getter="" default="&quot;&quot;">
The path to the texture to consider as a normal map for roughness filtering on import. Specifying this can help decrease specular aliasing slightly in 3D.

View File

@ -241,7 +241,10 @@ void ResourceImporterTexture::get_import_options(const String &p_path, List<Impo
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/normal_map_invert_y"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/hdr_as_srgb"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/hdr_clamp_exposure"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "process/size_limit", PROPERTY_HINT_RANGE, "0,4096,1"), 0));
// Maximum bound is the highest allowed value for lossy compression (the lowest common denominator).
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "process/size_limit", PROPERTY_HINT_RANGE, "0,16383,1"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "detect_3d/compress_to", PROPERTY_HINT_ENUM, "Disabled,VRAM Compressed,Basis Universal"), (p_preset == PRESET_DETECT) ? 1 : 0));
// Do path based customization only if a path was passed.
@ -454,7 +457,28 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
const bool normal_map_invert_y = p_options["process/normal_map_invert_y"];
// Support for texture streaming is not implemented yet.
const bool stream = false;
const int size_limit = p_options["process/size_limit"];
int size_limit = p_options["process/size_limit"];
bool using_fallback_size_limit = false;
if (size_limit == 0) {
using_fallback_size_limit = true;
// If no size limit is defined, use a fallback size limit to prevent textures from looking incorrect or failing to import.
switch (compress_mode) {
case COMPRESS_LOSSY:
// Maximum WebP size on either axis.
size_limit = 16383;
break;
case COMPRESS_BASIS_UNIVERSAL:
// Maximum Basis Universal size on either axis.
size_limit = 16384;
break;
default:
// As of June 2024, no GPU can correctly display a texture larger than 32768 pixels on either axis.
size_limit = 32768;
break;
}
}
const bool hdr_as_srgb = p_options["process/hdr_as_srgb"];
if (hdr_as_srgb) {
loader_flags |= ImageFormatLoader::FLAG_FORCE_LINEAR;
@ -523,11 +547,19 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
int new_width = size_limit;
int new_height = target_image->get_height() * new_width / target_image->get_width();
if (using_fallback_size_limit) {
// Only warn if downsizing occurred when the user did not explicitly request it.
WARN_PRINT(vformat("%s: Texture was downsized on import as its width (%d pixels) exceeded the importable size limit (%d pixels).", p_source_file, target_image->get_width(), size_limit));
}
target_image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC);
} else {
int new_height = size_limit;
int new_width = target_image->get_width() * new_height / target_image->get_height();
if (using_fallback_size_limit) {
// Only warn if downsizing occurred when the user did not explicitly request it.
WARN_PRINT(vformat("%s: Texture was downsized on import as its height (%d pixels) exceeded the importable size limit (%d pixels).", p_source_file, target_image->get_height(), size_limit));
}
target_image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC);
}