From cd7a7089522103edd9ac8cd0463ede795ce5d51d Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Sat, 22 Jun 2024 00:14:21 +0200 Subject: [PATCH] Downsample textures on import if necessary for technical reasons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- doc/classes/ResourceImporterTexture.xml | 6 +++- editor/import/resource_importer_texture.cpp | 36 +++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/doc/classes/ResourceImporterTexture.xml b/doc/classes/ResourceImporterTexture.xml index 678aa2101c6..fd6642bb32c 100644 --- a/doc/classes/ResourceImporterTexture.xml +++ b/doc/classes/ResourceImporterTexture.xml @@ -90,9 +90,13 @@ 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. - The color channel to consider as a roughness map in this texture. Only effective if Roughness > 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. 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. diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 487b8fc175c..47ddc86c2f0 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -241,7 +241,10 @@ void ResourceImporterTexture::get_import_options(const String &p_path, Listpush_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); }