Refactor high quality texture import

* Only two texture import modes for low/high quality now:
  * S3TC/BPTC
  * ETC2/ASTC
* Makes sense given this is the general preferred and most compatible combination in most platforms.
* Removed lossy_quality from VRAM texture compression options. It was unused everywhere.
* Added a new "high_quality" option to texture import. When enabled, it uses BPTC/ASTC (BC7/ASTC4x4) instead of S3TC/ETC2 (DXT1-5/ETC2,ETCA).
* Changed MacOS export settings so required texture formats depend on the architecture selected.

This solves the following problems:

* Makes it simpler to import textures as high quality, without having to worry about the specific format used.
* As the editor can now run on platforms such as web, Mac OS with Apple Silicion and Android, it should no longer be assumed that S3TC/BPTC is available by default for it.
This commit is contained in:
Juan Linietsky 2023-01-25 12:17:11 +01:00
parent 17a8597355
commit 28f51ba547
30 changed files with 457 additions and 265 deletions

View File

@ -78,6 +78,10 @@ const char *Image::format_names[Image::FORMAT_MAX] = {
"ETC2_RGB8A1", "ETC2_RGB8A1",
"ETC2_RA_AS_RG", "ETC2_RA_AS_RG",
"FORMAT_DXT5_RA_AS_RG", "FORMAT_DXT5_RA_AS_RG",
"ASTC_4x4",
"ASTC_4x4_HDR",
"ASTC_8x8",
"ASTC_8x8_HDR",
}; };
SavePNGFunc Image::save_png_func = nullptr; SavePNGFunc Image::save_png_func = nullptr;
@ -2187,16 +2191,16 @@ void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Forma
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
if (unlikely(p_data.size() != size)) { if (unlikely(p_data.size() != size)) {
String description_mipmaps; String description_mipmaps = get_format_name(p_format) + " ";
if (p_use_mipmaps) { if (p_use_mipmaps) {
const int num_mipmaps = get_image_required_mipmaps(p_width, p_height, p_format); const int num_mipmaps = get_image_required_mipmaps(p_width, p_height, p_format);
if (num_mipmaps != 1) { if (num_mipmaps != 1) {
description_mipmaps = vformat("with %d mipmaps", num_mipmaps); description_mipmaps += vformat("with %d mipmaps", num_mipmaps);
} else { } else {
description_mipmaps = "with 1 mipmap"; description_mipmaps += "with 1 mipmap";
} }
} else { } else {
description_mipmaps = "without mipmaps"; description_mipmaps += "without mipmaps";
} }
const String description = vformat("%dx%dx%d (%s)", p_width, p_height, get_format_pixel_size(p_format), description_mipmaps); const String description = vformat("%dx%dx%d (%s)", p_width, p_height, get_format_pixel_size(p_format), description_mipmaps);
ERR_FAIL_MSG(vformat("Expected Image data size of %s = %d bytes, got %d bytes instead.", description, size, p_data.size())); ERR_FAIL_MSG(vformat("Expected Image data size of %s = %d bytes, got %d bytes instead.", description, size, p_data.size()));
@ -2618,35 +2622,35 @@ Error Image::decompress() {
return OK; return OK;
} }
Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality, ASTCFormat p_astc_format) { Error Image::compress(CompressMode p_mode, CompressSource p_source, ASTCFormat p_astc_format) {
ERR_FAIL_INDEX_V_MSG(p_mode, COMPRESS_MAX, ERR_INVALID_PARAMETER, "Invalid compress mode."); ERR_FAIL_INDEX_V_MSG(p_mode, COMPRESS_MAX, ERR_INVALID_PARAMETER, "Invalid compress mode.");
ERR_FAIL_INDEX_V_MSG(p_source, COMPRESS_SOURCE_MAX, ERR_INVALID_PARAMETER, "Invalid compress source."); ERR_FAIL_INDEX_V_MSG(p_source, COMPRESS_SOURCE_MAX, ERR_INVALID_PARAMETER, "Invalid compress source.");
return compress_from_channels(p_mode, detect_used_channels(p_source), p_lossy_quality, p_astc_format); return compress_from_channels(p_mode, detect_used_channels(p_source), p_astc_format);
} }
Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality, ASTCFormat p_astc_format) { Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, ASTCFormat p_astc_format) {
ERR_FAIL_COND_V(data.is_empty(), ERR_INVALID_DATA); ERR_FAIL_COND_V(data.is_empty(), ERR_INVALID_DATA);
switch (p_mode) { switch (p_mode) {
case COMPRESS_S3TC: { case COMPRESS_S3TC: {
ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE); ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE);
_image_compress_bc_func(this, p_lossy_quality, p_channels); _image_compress_bc_func(this, p_channels);
} break; } break;
case COMPRESS_ETC: { case COMPRESS_ETC: {
ERR_FAIL_COND_V(!_image_compress_etc1_func, ERR_UNAVAILABLE); ERR_FAIL_COND_V(!_image_compress_etc1_func, ERR_UNAVAILABLE);
_image_compress_etc1_func(this, p_lossy_quality); _image_compress_etc1_func(this);
} break; } break;
case COMPRESS_ETC2: { case COMPRESS_ETC2: {
ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE); ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE);
_image_compress_etc2_func(this, p_lossy_quality, p_channels); _image_compress_etc2_func(this, p_channels);
} break; } break;
case COMPRESS_BPTC: { case COMPRESS_BPTC: {
ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE); ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE);
_image_compress_bptc_func(this, p_lossy_quality, p_channels); _image_compress_bptc_func(this, p_channels);
} break; } break;
case COMPRESS_ASTC: { case COMPRESS_ASTC: {
ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE); ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE);
_image_compress_astc_func(this, p_lossy_quality, p_astc_format); _image_compress_astc_func(this, p_astc_format);
} break; } break;
case COMPRESS_MAX: { case COMPRESS_MAX: {
ERR_FAIL_V(ERR_INVALID_PARAMETER); ERR_FAIL_V(ERR_INVALID_PARAMETER);
@ -3000,11 +3004,11 @@ ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr; ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr;
void (*Image::_image_compress_bc_func)(Image *, float, Image::UsedChannels) = nullptr; void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr;
void (*Image::_image_compress_bptc_func)(Image *, float, Image::UsedChannels) = nullptr; void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr;
void (*Image::_image_compress_etc1_func)(Image *, float) = nullptr; void (*Image::_image_compress_etc1_func)(Image *) = nullptr;
void (*Image::_image_compress_etc2_func)(Image *, float, Image::UsedChannels) = nullptr; void (*Image::_image_compress_etc2_func)(Image *, Image::UsedChannels) = nullptr;
void (*Image::_image_compress_astc_func)(Image *, float, Image::ASTCFormat) = nullptr; void (*Image::_image_compress_astc_func)(Image *, Image::ASTCFormat) = nullptr;
void (*Image::_image_decompress_bc)(Image *) = nullptr; void (*Image::_image_decompress_bc)(Image *) = nullptr;
void (*Image::_image_decompress_bptc)(Image *) = nullptr; void (*Image::_image_decompress_bptc)(Image *) = nullptr;
void (*Image::_image_decompress_etc1)(Image *) = nullptr; void (*Image::_image_decompress_etc1)(Image *) = nullptr;
@ -3426,8 +3430,8 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible); ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible);
ClassDB::bind_method(D_METHOD("detect_used_channels", "source"), &Image::detect_used_channels, DEFVAL(COMPRESS_SOURCE_GENERIC)); ClassDB::bind_method(D_METHOD("detect_used_channels", "source"), &Image::detect_used_channels, DEFVAL(COMPRESS_SOURCE_GENERIC));
ClassDB::bind_method(D_METHOD("compress", "mode", "source", "lossy_quality", "astc_format"), &Image::compress, DEFVAL(COMPRESS_SOURCE_GENERIC), DEFVAL(0.7), DEFVAL(ASTC_FORMAT_4x4)); ClassDB::bind_method(D_METHOD("compress", "mode", "source", "astc_format"), &Image::compress, DEFVAL(COMPRESS_SOURCE_GENERIC), DEFVAL(ASTC_FORMAT_4x4));
ClassDB::bind_method(D_METHOD("compress_from_channels", "mode", "channels", "lossy_quality", "astc_format"), &Image::compress_from_channels, DEFVAL(0.7), DEFVAL(ASTC_FORMAT_4x4)); ClassDB::bind_method(D_METHOD("compress_from_channels", "mode", "channels", "astc_format"), &Image::compress_from_channels, DEFVAL(ASTC_FORMAT_4x4));
ClassDB::bind_method(D_METHOD("decompress"), &Image::decompress); ClassDB::bind_method(D_METHOD("decompress"), &Image::decompress);
ClassDB::bind_method(D_METHOD("is_compressed"), &Image::is_compressed); ClassDB::bind_method(D_METHOD("is_compressed"), &Image::is_compressed);
@ -3547,11 +3551,11 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(ASTC_FORMAT_8x8); BIND_ENUM_CONSTANT(ASTC_FORMAT_8x8);
} }
void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels)) { void Image::set_compress_bc_func(void (*p_compress_func)(Image *, UsedChannels)) {
_image_compress_bc_func = p_compress_func; _image_compress_bc_func = p_compress_func;
} }
void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels)) { void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, UsedChannels)) {
_image_compress_bptc_func = p_compress_func; _image_compress_bptc_func = p_compress_func;
} }

View File

@ -149,11 +149,11 @@ public:
static ImageMemLoadFunc _tga_mem_loader_func; static ImageMemLoadFunc _tga_mem_loader_func;
static ImageMemLoadFunc _bmp_mem_loader_func; static ImageMemLoadFunc _bmp_mem_loader_func;
static void (*_image_compress_bc_func)(Image *, float, UsedChannels p_channels); static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels);
static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, UsedChannels p_channels); static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels);
static void (*_image_compress_etc1_func)(Image *, float); static void (*_image_compress_etc1_func)(Image *);
static void (*_image_compress_etc2_func)(Image *, float, UsedChannels p_channels); static void (*_image_compress_etc2_func)(Image *, UsedChannels p_channels);
static void (*_image_compress_astc_func)(Image *, float, ASTCFormat p_format); static void (*_image_compress_astc_func)(Image *, ASTCFormat p_format);
static void (*_image_decompress_bc)(Image *); static void (*_image_decompress_bc)(Image *);
static void (*_image_decompress_bptc)(Image *); static void (*_image_decompress_bptc)(Image *);
@ -368,8 +368,8 @@ public:
COMPRESS_SOURCE_MAX, COMPRESS_SOURCE_MAX,
}; };
Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7, ASTCFormat p_astc_format = ASTC_FORMAT_4x4); Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, ASTCFormat p_astc_format = ASTC_FORMAT_4x4);
Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality = 0.7, ASTCFormat p_astc_format = ASTC_FORMAT_4x4); Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, ASTCFormat p_astc_format = ASTC_FORMAT_4x4);
Error decompress(); Error decompress();
bool is_compressed() const; bool is_compressed() const;
@ -391,8 +391,8 @@ public:
Rect2i get_used_rect() const; Rect2i get_used_rect() const;
Ref<Image> get_region(const Rect2i &p_area) const; Ref<Image> get_region(const Rect2i &p_area) const;
static void set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels)); static void set_compress_bc_func(void (*p_compress_func)(Image *, UsedChannels));
static void set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels)); static void set_compress_bptc_func(void (*p_compress_func)(Image *, UsedChannels));
static String get_format_name(Format p_format); static String get_format_name(Format p_format);
Error load_png_from_buffer(const Vector<uint8_t> &p_array); Error load_png_from_buffer(const Vector<uint8_t> &p_array);

View File

@ -553,6 +553,16 @@ void OS::add_frame_delay(bool p_can_draw) {
} }
} }
OS::PreferredTextureFormat OS::get_preferred_texture_format() const {
#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
return PREFERRED_TEXTURE_FORMAT_ETC2_ASTC; // By rule, ARM hardware uses ETC texture compression.
#elif defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)
return PREFERRED_TEXTURE_FORMAT_S3TC_BPTC; // By rule, X86 hardware prefers S3TC and derivatives.
#else
return PREFERRED_TEXTURE_FORMAT_S3TC_BPTC; // Override in platform if needed.
#endif
}
OS::OS() { OS::OS() {
singleton = this; singleton = this;

View File

@ -290,6 +290,14 @@ public:
virtual Vector<String> get_granted_permissions() const { return Vector<String>(); } virtual Vector<String> get_granted_permissions() const { return Vector<String>(); }
virtual void process_and_drop_events() {} virtual void process_and_drop_events() {}
enum PreferredTextureFormat {
PREFERRED_TEXTURE_FORMAT_S3TC_BPTC,
PREFERRED_TEXTURE_FORMAT_ETC2_ASTC
};
virtual PreferredTextureFormat get_preferred_texture_format() const;
OS(); OS();
virtual ~OS(); virtual ~OS();
}; };

View File

@ -75,12 +75,10 @@
<return type="int" enum="Error" /> <return type="int" enum="Error" />
<param index="0" name="mode" type="int" enum="Image.CompressMode" /> <param index="0" name="mode" type="int" enum="Image.CompressMode" />
<param index="1" name="source" type="int" enum="Image.CompressSource" default="0" /> <param index="1" name="source" type="int" enum="Image.CompressSource" default="0" />
<param index="2" name="lossy_quality" type="float" default="0.7" /> <param index="2" name="astc_format" type="int" enum="Image.ASTCFormat" default="0" />
<param index="3" name="astc_format" type="int" enum="Image.ASTCFormat" default="0" />
<description> <description>
Compresses the image to use less memory. Can not directly access pixel data while the image is compressed. Returns error if the chosen compression mode is not available. Compresses the image to use less memory. Can not directly access pixel data while the image is compressed. Returns error if the chosen compression mode is not available.
The [param mode] parameter helps to pick the best compression method for DXT and ETC2 formats. It is ignored for ASTC compression. The [param mode] parameter helps to pick the best compression method for DXT and ETC2 formats. It is ignored for ASTC compression.
The [param lossy_quality] parameter is optional for compressors that support it.
For ASTC compression, the [param astc_format] parameter must be supplied. For ASTC compression, the [param astc_format] parameter must be supplied.
</description> </description>
</method> </method>
@ -88,12 +86,10 @@
<return type="int" enum="Error" /> <return type="int" enum="Error" />
<param index="0" name="mode" type="int" enum="Image.CompressMode" /> <param index="0" name="mode" type="int" enum="Image.CompressMode" />
<param index="1" name="channels" type="int" enum="Image.UsedChannels" /> <param index="1" name="channels" type="int" enum="Image.UsedChannels" />
<param index="2" name="lossy_quality" type="float" default="0.7" /> <param index="2" name="astc_format" type="int" enum="Image.ASTCFormat" default="0" />
<param index="3" name="astc_format" type="int" enum="Image.ASTCFormat" default="0" />
<description> <description>
Compresses the image to use less memory. Can not directly access pixel data while the image is compressed. Returns error if the chosen compression mode is not available. Compresses the image to use less memory. Can not directly access pixel data while the image is compressed. Returns error if the chosen compression mode is not available.
This is an alternative to [method compress] that lets the user supply the channels used in order for the compressor to pick the best DXT and ETC2 formats. For other formats (non DXT or ETC2), this argument is ignored. This is an alternative to [method compress] that lets the user supply the channels used in order for the compressor to pick the best DXT and ETC2 formats. For other formats (non DXT or ETC2), this argument is ignored.
The [param lossy_quality] parameter is optional for compressors that support it.
For ASTC compression, the [param astc_format] parameter must be supplied. For ASTC compression, the [param astc_format] parameter must be supplied.
</description> </description>
</method> </method>

View File

@ -2285,20 +2285,12 @@
<member name="rendering/textures/lossless_compression/force_png" type="bool" setter="" getter="" default="false"> <member name="rendering/textures/lossless_compression/force_png" type="bool" setter="" getter="" default="false">
If [code]true[/code], the texture importer will import lossless textures using the PNG format. Otherwise, it will default to using WebP. If [code]true[/code], the texture importer will import lossless textures using the PNG format. Otherwise, it will default to using WebP.
</member> </member>
<member name="rendering/textures/vram_compression/import_bptc" type="bool" setter="" getter="" default="false"> <member name="rendering/textures/vram_compression/import_etc2_astc" type="bool" setter="" getter="" default="false">
If [code]true[/code], the texture importer will import VRAM-compressed textures using the BPTC algorithm. This texture compression algorithm is only supported on desktop platforms, and only when using the Vulkan renderer. If [code]true[/code], the texture importer will import VRAM-compressed textures using the Ericsson Texture Compression 2 algorithm for lower quality textures and normalmaps and Adaptable Scalable Texture Compression algorithm for high quality textures (in 4x4 block size).
[b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]). [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]).
</member> </member>
<member name="rendering/textures/vram_compression/import_etc" type="bool" setter="" getter="" default="false"> <member name="rendering/textures/vram_compression/import_s3tc_bptc" type="bool" setter="" getter="" default="true">
If [code]true[/code], the texture importer will import VRAM-compressed textures using the Ericsson Texture Compression algorithm. This algorithm doesn't support alpha channels in textures. If [code]true[/code], the texture importer will import VRAM-compressed textures using the S3 Texture Compression algorithm (DXT1-5) for lower quality textures and the the BPTC algorithm (BC6H and BC7) for high quality textures. This algorithm is only supported on PC desktop platforms and consoles.
[b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]).
</member>
<member name="rendering/textures/vram_compression/import_etc2" type="bool" setter="" getter="" default="true">
If [code]true[/code], the texture importer will import VRAM-compressed textures using the Ericsson Texture Compression 2 algorithm. This texture compression algorithm is only supported when using the Vulkan renderer.
[b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]).
</member>
<member name="rendering/textures/vram_compression/import_s3tc" type="bool" setter="" getter="" default="true">
If [code]true[/code], the texture importer will import VRAM-compressed textures using the S3 Texture Compression algorithm. This algorithm is only supported on desktop platforms and consoles.
[b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]). [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]).
</member> </member>
<member name="rendering/textures/webp_compression/compression_method" type="int" setter="" getter="" default="2"> <member name="rendering/textures/webp_compression/compression_method" type="int" setter="" getter="" default="2">

View File

@ -65,6 +65,10 @@ Config::Config() {
} }
bptc_supported = extensions.has("GL_ARB_texture_compression_bptc") || extensions.has("EXT_texture_compression_bptc"); bptc_supported = extensions.has("GL_ARB_texture_compression_bptc") || extensions.has("EXT_texture_compression_bptc");
astc_supported = extensions.has("GL_KHR_texture_compression_astc") || extensions.has("GL_OES_texture_compression_astc") || extensions.has("GL_KHR_texture_compression_astc_ldr") || extensions.has("GL_KHR_texture_compression_astc_hdr");
astc_hdr_supported = extensions.has("GL_KHR_texture_compression_astc_ldr");
astc_layered_supported = extensions.has("GL_KHR_texture_compression_astc_sliced_3d");
#ifdef GLES_OVER_GL #ifdef GLES_OVER_GL
float_texture_supported = true; float_texture_supported = true;
etc2_supported = false; etc2_supported = false;

View File

@ -77,6 +77,9 @@ public:
bool rgtc_supported = false; bool rgtc_supported = false;
bool bptc_supported = false; bool bptc_supported = false;
bool etc2_supported = false; bool etc2_supported = false;
bool astc_supported = false;
bool astc_hdr_supported = false;
bool astc_layered_supported = false;
bool force_vertex_shading = false; bool force_vertex_shading = false;

View File

@ -594,6 +594,50 @@ Ref<Image> TextureStorage::_get_gl_image_and_format(const Ref<Image> &p_image, I
} }
decompress_ra_to_rg = true; decompress_ra_to_rg = true;
} break; } break;
case Image::FORMAT_ASTC_4x4: {
if (config->astc_supported) {
r_gl_internal_format = _EXT_COMPRESSED_RGBA_ASTC_4x4_KHR;
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
} break;
case Image::FORMAT_ASTC_4x4_HDR: {
if (config->astc_hdr_supported) {
r_gl_internal_format = _EXT_COMPRESSED_RGBA_ASTC_4x4_KHR;
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
} break;
case Image::FORMAT_ASTC_8x8: {
if (config->astc_supported) {
r_gl_internal_format = _EXT_COMPRESSED_RGBA_ASTC_8x8_KHR;
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
} break;
case Image::FORMAT_ASTC_8x8_HDR: {
if (config->astc_hdr_supported) {
r_gl_internal_format = _EXT_COMPRESSED_RGBA_ASTC_8x8_KHR;
r_gl_format = GL_RGBA;
r_gl_type = GL_UNSIGNED_BYTE;
r_compressed = true;
} else {
need_decompress = true;
}
} break;
default: { default: {
ERR_FAIL_V_MSG(Ref<Image>(), "Image Format: " + itos(p_format) + " is not supported by the OpenGL3 Renderer"); ERR_FAIL_V_MSG(Ref<Image>(), "Image Format: " + itos(p_format) + " is not supported by the OpenGL3 Renderer");
} }

View File

@ -84,6 +84,36 @@ namespace GLES3 {
#define _EXT_COMPRESSED_RGBA8_ETC2_EAC 0x9278 #define _EXT_COMPRESSED_RGBA8_ETC2_EAC 0x9278
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 #define _EXT_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279
#define _EXT_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0
#define _EXT_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1
#define _EXT_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2
#define _EXT_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3
#define _EXT_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4
#define _EXT_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5
#define _EXT_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6
#define _EXT_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7
#define _EXT_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8
#define _EXT_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9
#define _EXT_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA
#define _EXT_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB
#define _EXT_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC
#define _EXT_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC
#define _EXT_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD
#define _GL_TEXTURE_EXTERNAL_OES 0x8D65 #define _GL_TEXTURE_EXTERNAL_OES 0x8D65
#define _EXT_TEXTURE_CUBE_MAP_SEAMLESS 0x884F #define _EXT_TEXTURE_CUBE_MAP_SEAMLESS 0x884F

View File

@ -309,6 +309,9 @@ bool Utilities::has_os_feature(const String &p_feature) const {
if (p_feature == "bptc") { if (p_feature == "bptc") {
return config->bptc_supported; return config->bptc_supported;
} }
if (p_feature == "astc") {
return config->astc_supported;
}
if (p_feature == "etc" || p_feature == "etc2") { if (p_feature == "etc" || p_feature == "etc2") {
return config->etc2_supported; return config->etc2_supported;

View File

@ -115,6 +115,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
capitalize_string_remaps["arm64-v8a"] = "arm64-v8a"; capitalize_string_remaps["arm64-v8a"] = "arm64-v8a";
capitalize_string_remaps["armeabi-v7a"] = "armeabi-v7a"; capitalize_string_remaps["armeabi-v7a"] = "armeabi-v7a";
capitalize_string_remaps["arvr"] = "ARVR"; capitalize_string_remaps["arvr"] = "ARVR";
capitalize_string_remaps["astc"] = "ASTC";
capitalize_string_remaps["bg"] = "BG"; capitalize_string_remaps["bg"] = "BG";
capitalize_string_remaps["bidi"] = "BiDi"; capitalize_string_remaps["bidi"] = "BiDi";
capitalize_string_remaps["bp"] = "BP"; capitalize_string_remaps["bp"] = "BP";

View File

@ -129,10 +129,20 @@ void EditorExport::add_export_preset(const Ref<EditorExportPreset> &p_preset, in
} }
String EditorExportPlatform::test_etc2() const { String EditorExportPlatform::test_etc2() const {
const bool etc2_supported = GLOBAL_GET("rendering/textures/vram_compression/import_etc2"); const bool etc2_supported = GLOBAL_GET("rendering/textures/vram_compression/import_etc2_astc");
if (!etc2_supported) { if (!etc2_supported) {
return TTR("Target platform requires 'ETC2' texture compression. Enable 'Import Etc 2' in Project Settings."); return TTR("Target platform requires 'ETC2/ASTC' texture compression. Enable 'Import ETC2 ASTC' in Project Settings.");
}
return String();
}
String EditorExportPlatform::test_bc() const {
const bool bc_supported = GLOBAL_GET("rendering/textures/vram_compression/import_s3tc_bptc");
if (!bc_supported) {
return TTR("Target platform requires 'S3TC/BPTC' texture compression. Enable 'Import S3TC BPTC' in Project Settings.");
} }
return String(); return String();

View File

@ -229,6 +229,7 @@ public:
virtual Ref<Texture2D> get_run_icon() const { return get_logo(); } virtual Ref<Texture2D> get_run_icon() const { return get_logo(); }
String test_etc2() const; String test_etc2() const;
String test_bc() const;
bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const = 0; virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const = 0;
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const = 0; virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const = 0;

View File

@ -123,6 +123,9 @@ bool ResourceImporterLayeredTexture::get_option_visibility(const String &p_path,
if (p_option == "compress/lossy_quality" && p_options.has("compress/mode")) { if (p_option == "compress/lossy_quality" && p_options.has("compress/mode")) {
return int(p_options["compress/mode"]) == COMPRESS_LOSSY; return int(p_options["compress/mode"]) == COMPRESS_LOSSY;
} }
if ((p_option == "compress/high_quality" || p_option == "compress/hdr_compression") && p_options.has("compress/mode")) {
return int(p_options["compress/mode"]) == COMPRESS_VRAM_COMPRESSED;
}
return true; return true;
} }
@ -136,9 +139,9 @@ String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const {
void ResourceImporterLayeredTexture::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const { void ResourceImporterLayeredTexture::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/high_quality"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Disabled,Enabled,RGBA Only"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized,Normal Map (RG Channels)"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized,Normal Map (RG Channels)"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mipmaps/limit", PROPERTY_HINT_RANGE, "-1,256"), -1)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mipmaps/limit", PROPERTY_HINT_RANGE, "-1,256"), -1));
@ -283,8 +286,8 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons
Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
int compress_mode = p_options["compress/mode"]; int compress_mode = p_options["compress/mode"];
float lossy = p_options["compress/lossy_quality"]; float lossy = p_options["compress/lossy_quality"];
float high_quality = p_options["compress/high_quality"];
int hdr_compression = p_options["compress/hdr_compression"]; int hdr_compression = p_options["compress/hdr_compression"];
int bptc_ldr = p_options["compress/bptc_ldr"];
bool mipmaps = p_options["mipmaps/generate"]; bool mipmaps = p_options["mipmaps/generate"];
int channel_pack = p_options["compress/channel_pack"]; int channel_pack = p_options["compress/channel_pack"];
@ -389,9 +392,10 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
texture_import->compress_mode = compress_mode; texture_import->compress_mode = compress_mode;
texture_import->lossy = lossy; texture_import->lossy = lossy;
texture_import->hdr_compression = hdr_compression; texture_import->hdr_compression = hdr_compression;
texture_import->bptc_ldr = bptc_ldr;
texture_import->mipmaps = mipmaps; texture_import->mipmaps = mipmaps;
texture_import->used_channels = used_channels; texture_import->used_channels = used_channels;
texture_import->high_quality = high_quality;
_check_compress_ctex(p_source_file, texture_import); _check_compress_ctex(p_source_file, texture_import);
if (r_metadata) { if (r_metadata) {
Dictionary meta; Dictionary meta;
@ -406,12 +410,11 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
} }
const char *ResourceImporterLayeredTexture::compression_formats[] = { const char *ResourceImporterLayeredTexture::compression_formats[] = {
"bptc", "s3tc_bptc",
"s3tc", "etc2_astc",
"etc",
"etc2",
nullptr nullptr
}; };
String ResourceImporterLayeredTexture::get_import_settings_string() const { String ResourceImporterLayeredTexture::get_import_settings_string() const {
String s; String s;
@ -450,12 +453,16 @@ bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_p
bool valid = true; bool valid = true;
while (compression_formats[index]) { while (compression_formats[index]) {
String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]); String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
bool test = GLOBAL_GET(setting_path); if (ProjectSettings::get_singleton()->has_setting(setting_path)) {
if (test) { bool test = GLOBAL_GET(setting_path);
if (!formats_imported.has(compression_formats[index])) { if (test) {
valid = false; if (!formats_imported.has(compression_formats[index])) {
break; valid = false;
break;
}
} }
} else {
WARN_PRINT("Setting for imported format not found: " + setting_path);
} }
index++; index++;
} }
@ -484,64 +491,83 @@ void ResourceImporterLayeredTexture::_check_compress_ctex(const String &p_source
// Must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc). // Must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc).
// Android, GLES 2.x // Android, GLES 2.x
bool can_bptc = GLOBAL_GET("rendering/textures/vram_compression/import_bptc"); const bool can_s3tc_bptc = GLOBAL_GET("rendering/textures/vram_compression/import_s3tc_bptc") || OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_S3TC_BPTC;
if (can_bptc) { const bool can_etc2_astc = GLOBAL_GET("rendering/textures/vram_compression/import_etc2_astc") || OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_ETC2_ASTC;
r_texture_import->formats_imported.push_back("bptc"); // BPTC needs to be added anyway.
// Add list of formats imported
if (can_s3tc_bptc) {
r_texture_import->formats_imported.push_back("s3tc_bptc");
} }
if (can_etc2_astc) {
r_texture_import->formats_imported.push_back("etc2_astc");
}
bool can_compress_hdr = r_texture_import->hdr_compression > 0; bool can_compress_hdr = r_texture_import->hdr_compression > 0;
ERR_FAIL_NULL(r_texture_import->image); ERR_FAIL_NULL(r_texture_import->image);
bool is_hdr = (r_texture_import->image->get_format() >= Image::FORMAT_RF && r_texture_import->image->get_format() <= Image::FORMAT_RGBE9995); bool is_hdr = (r_texture_import->image->get_format() >= Image::FORMAT_RF && r_texture_import->image->get_format() <= Image::FORMAT_RGBE9995);
bool is_ldr = (r_texture_import->image->get_format() >= Image::FORMAT_L8 && r_texture_import->image->get_format() <= Image::FORMAT_RGB565);
bool can_s3tc = GLOBAL_GET("rendering/textures/vram_compression/import_s3tc");
ERR_FAIL_NULL(r_texture_import->slices); ERR_FAIL_NULL(r_texture_import->slices);
// Can compress hdr, but hdr with alpha is not compressible. // Can compress hdr, but hdr with alpha is not compressible.
if (r_texture_import->hdr_compression == 2) { bool use_uncompressed = false;
// The user selected to compress hdr anyway, so force an alpha-less format.
if (r_texture_import->image->get_format() == Image::FORMAT_RGBAF) {
for (int i = 0; i < r_texture_import->slices->size(); i++) {
r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBF);
}
} else if (r_texture_import->image->get_format() == Image::FORMAT_RGBAH) { if (is_hdr) {
for (int i = 0; i < r_texture_import->slices->size(); i++) { if (r_texture_import->used_channels == Image::USED_CHANNELS_LA || r_texture_import->used_channels == Image::USED_CHANNELS_RGBA) {
r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBH); if (r_texture_import->hdr_compression == 2) {
// The user selected to compress hdr anyway, so force an alpha-less format.
if (r_texture_import->image->get_format() == Image::FORMAT_RGBAF) {
for (int i = 0; i < r_texture_import->slices->size(); i++) {
r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBF);
}
} else if (r_texture_import->image->get_format() == Image::FORMAT_RGBAH) {
for (int i = 0; i < r_texture_import->slices->size(); i++) {
r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBH);
}
}
} else {
can_compress_hdr = false;
} }
} }
} else {
can_compress_hdr = false;
}
if (is_hdr && can_compress_hdr) { if (!can_compress_hdr) {
if (!can_bptc) {
//default to rgbe //default to rgbe
if (r_texture_import->image->get_format() != Image::FORMAT_RGBE9995) { if (r_texture_import->image->get_format() != Image::FORMAT_RGBE9995) {
for (int i = 0; i < r_texture_import->slices->size(); i++) { for (int i = 0; i < r_texture_import->slices->size(); i++) {
r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBE9995); r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBE9995);
} }
} }
use_uncompressed = true;
} }
}
if (use_uncompressed) {
_save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + extension, COMPRESS_VRAM_UNCOMPRESSED, r_texture_import->lossy, Image::COMPRESS_S3TC /* IGNORED */, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, false);
} else { } else {
can_bptc = false; if (can_s3tc_bptc) {
} Image::CompressMode image_compress_mode;
String image_compress_format;
if (is_ldr && can_bptc) { if (r_texture_import->high_quality || is_hdr) {
if (r_texture_import->bptc_ldr == 0 || (r_texture_import->bptc_ldr == 1 && !(r_texture_import->used_channels == Image::USED_CHANNELS_LA || r_texture_import->used_channels == Image::USED_CHANNELS_RGBA))) { image_compress_mode = Image::COMPRESS_BPTC;
can_bptc = false; image_compress_format = "bptc";
} } else {
} image_compress_mode = Image::COMPRESS_S3TC;
if (!(r_texture_import->used_channels == Image::USED_CHANNELS_LA || r_texture_import->used_channels == Image::USED_CHANNELS_RGBA)) { image_compress_format = "s3tc";
if (GLOBAL_GET("rendering/textures/vram_compression/import_etc2")) { }
_save_tex(*r_texture_import->slices, r_texture_import->save_path + ".etc2." + extension, r_texture_import->compress_mode, r_texture_import->lossy, Image::COMPRESS_ETC2, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, true); _save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + image_compress_format + "." + extension, r_texture_import->compress_mode, r_texture_import->lossy, image_compress_mode, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, true);
r_texture_import->platform_variants->push_back("etc2"); r_texture_import->platform_variants->push_back(image_compress_format);
r_texture_import->formats_imported.push_back("etc2");
} }
if (can_bptc || can_s3tc) { if (can_etc2_astc) {
_save_tex(*r_texture_import->slices, r_texture_import->save_path + ".s3tc." + extension, r_texture_import->compress_mode, r_texture_import->lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, false); Image::CompressMode image_compress_mode;
r_texture_import->platform_variants->push_back("s3tc"); String image_compress_format;
r_texture_import->formats_imported.push_back("s3tc"); if (r_texture_import->high_quality || is_hdr) {
image_compress_mode = Image::COMPRESS_ASTC;
image_compress_format = "astc";
} else {
image_compress_mode = Image::COMPRESS_ETC2;
image_compress_format = "etc2";
}
_save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + image_compress_format + "." + extension, r_texture_import->compress_mode, r_texture_import->lossy, image_compress_mode, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, true);
r_texture_import->platform_variants->push_back(image_compress_format);
} }
return;
} }
EditorNode::add_io_error(vformat(TTR("%s: No suitable PC VRAM compression algorithm enabled in Project Settings (S3TC or BPTC). This texture may not display correctly on desktop platforms."), p_source_file));
} }

View File

@ -51,8 +51,8 @@ public:
int compress_mode = 0; int compress_mode = 0;
float lossy = 1.0; float lossy = 1.0;
int hdr_compression = 0; int hdr_compression = 0;
int bptc_ldr = 0;
bool mipmaps = true; bool mipmaps = true;
bool high_quality = false;
Image::UsedChannels used_channels = Image::USED_CHANNELS_RGBA; Image::UsedChannels used_channels = Image::USED_CHANNELS_RGBA;
virtual ~LayeredTextureImport() {} virtual ~LayeredTextureImport() {}
}; };

View File

@ -169,9 +169,14 @@ String ResourceImporterTexture::get_resource_type() const {
} }
bool ResourceImporterTexture::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const { bool ResourceImporterTexture::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
if (p_option == "compress/lossy_quality") { if (p_option == "compress/high_quality" || p_option == "compress/hdr_compression") {
int compress_mode = int(p_options["compress/mode"]); int compress_mode = int(p_options["compress/mode"]);
if (compress_mode != COMPRESS_LOSSY && compress_mode != COMPRESS_VRAM_COMPRESSED) { if (compress_mode != COMPRESS_VRAM_COMPRESSED) {
return false;
}
} else if (p_option == "compress/lossy_quality") {
int compress_mode = int(p_options["compress/mode"]);
if (compress_mode != COMPRESS_LOSSY) {
return false; return false;
} }
} else if (p_option == "compress/hdr_mode") { } else if (p_option == "compress/hdr_mode") {
@ -186,15 +191,6 @@ bool ResourceImporterTexture::get_option_visibility(const String &p_path, const
} }
} else if (p_option == "mipmaps/limit") { } else if (p_option == "mipmaps/limit") {
return p_options["mipmaps/generate"]; return p_options["mipmaps/generate"];
} else if (p_option == "compress/bptc_ldr") {
int compress_mode = int(p_options["compress/mode"]);
if (compress_mode < COMPRESS_VRAM_COMPRESSED) {
return false;
}
if (!GLOBAL_GET("rendering/textures/vram_compression/import_bptc")) {
return false;
}
} }
return true; return true;
@ -216,9 +212,9 @@ String ResourceImporterTexture::get_preset_name(int p_idx) const {
void ResourceImporterTexture::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const { void ResourceImporterTexture::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 2 : 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), p_preset == PRESET_3D ? 2 : 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/high_quality"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Disabled,Enabled,RGBA Only"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/normal_map", PROPERTY_HINT_ENUM, "Detect,Enable,Disabled"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/normal_map", PROPERTY_HINT_ENUM, "Detect,Enable,Disabled"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), (p_preset == PRESET_3D ? true : false))); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), (p_preset == PRESET_3D ? true : false)));
@ -289,7 +285,7 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I
case COMPRESS_VRAM_COMPRESSED: { case COMPRESS_VRAM_COMPRESSED: {
Ref<Image> image = p_image->duplicate(); Ref<Image> image = p_image->duplicate();
image->compress_from_channels(p_compress_format, p_channels, p_lossy_quality); image->compress_from_channels(p_compress_format, p_channels);
f->store_32(CompressedTexture2D::DATA_FORMAT_IMAGE); f->store_32(CompressedTexture2D::DATA_FORMAT_IMAGE);
f->store_16(image->get_width()); f->store_16(image->get_width());
@ -421,7 +417,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
const int pack_channels = p_options["compress/channel_pack"]; const int pack_channels = p_options["compress/channel_pack"];
const int normal = p_options["compress/normal_map"]; const int normal = p_options["compress/normal_map"];
const int hdr_compression = p_options["compress/hdr_compression"]; const int hdr_compression = p_options["compress/hdr_compression"];
const int bptc_ldr = p_options["compress/bptc_ldr"]; const int high_quality = p_options["compress/high_quality"];
// Mipmaps. // Mipmaps.
const bool mipmaps = p_options["mipmaps/generate"]; const bool mipmaps = p_options["mipmaps/generate"];
@ -594,19 +590,22 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
// Android, GLES 2.x // Android, GLES 2.x
const bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995); const bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995);
bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGB565); const bool can_s3tc_bptc = GLOBAL_GET("rendering/textures/vram_compression/import_s3tc_bptc") || OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_S3TC_BPTC;
const bool can_bptc = GLOBAL_GET("rendering/textures/vram_compression/import_bptc"); const bool can_etc2_astc = GLOBAL_GET("rendering/textures/vram_compression/import_etc2_astc") || OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_ETC2_ASTC;
const bool can_s3tc = GLOBAL_GET("rendering/textures/vram_compression/import_s3tc");
if (can_bptc) { // Add list of formats imported
// Add to the list anyway. if (can_s3tc_bptc) {
formats_imported.push_back("bptc"); formats_imported.push_back("s3tc_bptc");
}
if (can_etc2_astc) {
formats_imported.push_back("etc2_astc");
} }
bool can_compress_hdr = hdr_compression > 0; bool can_compress_hdr = hdr_compression > 0;
bool has_alpha = image->detect_alpha() != Image::ALPHA_NONE; bool has_alpha = image->detect_alpha() != Image::ALPHA_NONE;
bool use_uncompressed = false;
if (is_hdr && can_compress_hdr) { if (is_hdr) {
if (has_alpha) { if (has_alpha) {
// Can compress HDR, but HDR with alpha is not compressible. // Can compress HDR, but HDR with alpha is not compressible.
if (hdr_compression == 2) { if (hdr_compression == 2) {
@ -625,36 +624,41 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
// Fallback to RGBE99995. // Fallback to RGBE99995.
if (image->get_format() != Image::FORMAT_RGBE9995) { if (image->get_format() != Image::FORMAT_RGBE9995) {
image->convert(Image::FORMAT_RGBE9995); image->convert(Image::FORMAT_RGBE9995);
use_uncompressed = true;
} }
} }
} }
bool ok_on_pc = false; if (use_uncompressed) {
if (can_bptc || can_s3tc) { _save_ctex(image, p_save_path + ".ctex", COMPRESS_VRAM_UNCOMPRESSED, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
ok_on_pc = true; } else {
Image::CompressMode image_compress_mode = Image::COMPRESS_BPTC; if (can_s3tc_bptc) {
if (!bptc_ldr && can_s3tc && is_ldr) { Image::CompressMode image_compress_mode;
image_compress_mode = Image::COMPRESS_S3TC; String image_compress_format;
if (high_quality || is_hdr) {
image_compress_mode = Image::COMPRESS_BPTC;
image_compress_format = "bptc";
} else {
image_compress_mode = Image::COMPRESS_S3TC;
image_compress_format = "s3tc";
}
_save_ctex(image, p_save_path + "." + image_compress_format + ".ctex", compress_mode, lossy, image_compress_mode, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
r_platform_variants->push_back(image_compress_format);
} }
_save_ctex(image, p_save_path + ".s3tc.ctex", compress_mode, lossy, image_compress_mode, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
r_platform_variants->push_back("s3tc");
formats_imported.push_back("s3tc");
}
if (GLOBAL_GET("rendering/textures/vram_compression/import_etc2")) { if (can_etc2_astc) {
_save_ctex(image, p_save_path + ".etc2.ctex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel); Image::CompressMode image_compress_mode;
r_platform_variants->push_back("etc2"); String image_compress_format;
formats_imported.push_back("etc2"); if (high_quality || is_hdr) {
} image_compress_mode = Image::COMPRESS_ASTC;
image_compress_format = "astc";
if (GLOBAL_GET("rendering/textures/vram_compression/import_etc")) { } else {
_save_ctex(image, p_save_path + ".etc.ctex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel); image_compress_mode = Image::COMPRESS_ETC2;
r_platform_variants->push_back("etc"); image_compress_format = "etc2";
formats_imported.push_back("etc"); }
} _save_ctex(image, p_save_path + "." + image_compress_format + ".ctex", compress_mode, lossy, image_compress_mode, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
r_platform_variants->push_back(image_compress_format);
if (!ok_on_pc) { }
EditorNode::add_io_error(vformat(TTR("%s: No suitable desktop VRAM compression algorithm enabled in Project Settings (S3TC or BPTC). This texture may not display correctly on desktop platforms."), p_source_file));
} }
} else { } else {
// Import normally. // Import normally.
@ -688,10 +692,8 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
} }
const char *ResourceImporterTexture::compression_formats[] = { const char *ResourceImporterTexture::compression_formats[] = {
"bptc", "s3tc_bptc",
"s3tc", "etc2_astc",
"etc",
"etc2",
nullptr nullptr
}; };
String ResourceImporterTexture::get_import_settings_string() const { String ResourceImporterTexture::get_import_settings_string() const {
@ -741,12 +743,16 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co
bool valid = true; bool valid = true;
while (compression_formats[index]) { while (compression_formats[index]) {
String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]); String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
bool test = GLOBAL_GET(setting_path); if (ProjectSettings::get_singleton()->has_setting(setting_path)) {
if (test) { bool test = GLOBAL_GET(setting_path);
if (!formats_imported.has(compression_formats[index])) { if (test) {
valid = false; if (!formats_imported.has(compression_formats[index])) {
break; valid = false;
break;
}
} }
} else {
WARN_PRINT("Setting for imported format not found: " + setting_path);
} }
index++; index++;
} }

View File

@ -1362,11 +1362,8 @@ static const char *project_settings_renames[][2] = {
{ "rendering/quality/shadow_atlas/quadrant_3_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_3_subdiv" }, { "rendering/quality/shadow_atlas/quadrant_3_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_3_subdiv" },
{ "rendering/quality/shadow_atlas/size", "rendering/lights_and_shadows/shadow_atlas/size" }, { "rendering/quality/shadow_atlas/size", "rendering/lights_and_shadows/shadow_atlas/size" },
{ "rendering/quality/shadow_atlas/size.mobile", "rendering/lights_and_shadows/shadow_atlas/size.mobile" }, { "rendering/quality/shadow_atlas/size.mobile", "rendering/lights_and_shadows/shadow_atlas/size.mobile" },
{ "rendering/vram_compression/import_bptc", "rendering/textures/vram_compression/import_bptc" }, { "rendering/vram_compression/import_etc2", "rendering/textures/vram_compression/import_etc2_astc" },
{ "rendering/vram_compression/import_etc", "rendering/textures/vram_compression/import_etc" }, { "rendering/vram_compression/import_s3tc", "rendering/textures/vram_compression/import_s3tc_bptc" },
{ "rendering/vram_compression/import_etc2", "rendering/textures/vram_compression/import_etc2" },
{ "rendering/vram_compression/import_pvrtc", "rendering/textures/vram_compression/import_pvrtc" },
{ "rendering/vram_compression/import_s3tc", "rendering/textures/vram_compression/import_s3tc" },
{ "window/size/width", "window/size/viewport_width" }, { "window/size/width", "window/size/viewport_width" },
{ "window/size/height", "window/size/viewport_height" }, { "window/size/height", "window/size/viewport_height" },
{ "window/size/test_width", "window/size/window_width_override" }, { "window/size/test_width", "window/size/window_width_override" },

View File

@ -35,7 +35,7 @@
#include <astcenc.h> #include <astcenc.h>
void _compress_astc(Image *r_img, float p_lossy_quality, Image::ASTCFormat p_format) { void _compress_astc(Image *r_img, Image::ASTCFormat p_format) {
uint64_t start_time = OS::get_singleton()->get_ticks_msec(); uint64_t start_time = OS::get_singleton()->get_ticks_msec();
// TODO: See how to handle lossy quality. // TODO: See how to handle lossy quality.
@ -83,65 +83,91 @@ void _compress_astc(Image *r_img, float p_lossy_quality, Image::ASTCFormat p_for
const bool mipmaps = r_img->has_mipmaps(); const bool mipmaps = r_img->has_mipmaps();
int width = r_img->get_width(); int width = r_img->get_width();
int height = r_img->get_height(); int height = r_img->get_height();
int required_width = (width % block_x) != 0 ? width + (block_x - (width % block_x)) : width;
int required_height = (height % block_y) != 0 ? height + (block_y - (height % block_y)) : height;
if (width != required_width || height != required_height) {
// Resize texture to fit block size.
r_img->resize(required_width, required_height);
width = required_width;
height = required_height;
}
print_verbose(vformat("astcenc: Encoding image size %dx%d to format %s%s.", width, height, Image::get_format_name(target_format), mipmaps ? ", with mipmaps" : "")); print_verbose(vformat("astcenc: Encoding image size %dx%d to format %s%s.", width, height, Image::get_format_name(target_format), mipmaps ? ", with mipmaps" : ""));
// Initialize astcenc. // Initialize astcenc.
int dest_size = Image::get_image_data_size(width, height, target_format, mipmaps);
Vector<uint8_t> dest_data;
dest_data.resize(dest_size);
uint8_t *dest_write = dest_data.ptrw();
astcenc_config config; astcenc_config config;
config.block_x = block_x; config.block_x = block_x;
config.block_y = block_y; config.block_y = block_y;
config.profile = profile; config.profile = profile;
const float quality = ASTCENC_PRE_MEDIUM;
astcenc_error status = astcenc_config_init(profile, block_x, block_y, block_x, quality, 0, &config); const float quality = ASTCENC_PRE_MEDIUM;
astcenc_error status = astcenc_config_init(profile, block_x, block_y, 1, quality, 0, &config);
ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS,
vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status))); vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status)));
// Context allocation. // Context allocation.
astcenc_context *context; astcenc_context *context;
const unsigned int thread_count = OS::get_singleton()->get_processor_count(); const unsigned int thread_count = 1; // Godot compresses multiple images each on a thread, which is more efficient for large amount of images imported.
status = astcenc_context_alloc(&config, thread_count, &context); status = astcenc_context_alloc(&config, thread_count, &context);
ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS,
vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status))); vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status)));
// Compress image.
Vector<uint8_t> image_data = r_img->get_data(); Vector<uint8_t> image_data = r_img->get_data();
uint8_t *slices = image_data.ptrw();
astcenc_image image; int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
image.dim_x = width; for (int i = 0; i < mip_count + 1; i++) {
image.dim_y = height; int src_mip_w, src_mip_h;
image.dim_z = 1; int src_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, r_img->get_format(), i, src_mip_w, src_mip_h);
image.data_type = ASTCENC_TYPE_U8;
if (is_hdr) { const uint8_t *slices = &image_data.ptr()[src_ofs];
image.data_type = ASTCENC_TYPE_F32;
int dst_mip_w, dst_mip_h;
int dst_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, dst_mip_w, dst_mip_h);
// Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer).
ERR_FAIL_COND(dst_ofs % 8 != 0);
uint8_t *dest_mip_write = (uint8_t *)&dest_write[dst_ofs];
// Compress image.
astcenc_image image;
image.dim_x = src_mip_w;
image.dim_y = src_mip_h;
image.dim_z = 1;
image.data_type = ASTCENC_TYPE_U8;
if (is_hdr) {
image.data_type = ASTCENC_TYPE_F32;
}
image.data = (void **)(&slices);
// Compute the number of ASTC blocks in each dimension.
unsigned int block_count_x = (src_mip_w + block_x - 1) / block_x;
unsigned int block_count_y = (src_mip_h + block_y - 1) / block_y;
size_t comp_len = block_count_x * block_count_y * 16;
const astcenc_swizzle swizzle = {
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
};
status = astcenc_compress_image(context, &image, &swizzle, dest_mip_write, comp_len, 0);
ERR_BREAK_MSG(status != ASTCENC_SUCCESS,
vformat("astcenc: ASTC image compression failed: %s.", astcenc_get_error_string(status)));
astcenc_compress_reset(context);
} }
image.data = reinterpret_cast<void **>(&slices);
// Compute the number of ASTC blocks in each dimension. astcenc_context_free(context);
unsigned int block_count_x = (width + block_x - 1) / block_x;
unsigned int block_count_y = (height + block_y - 1) / block_y;
size_t comp_len = block_count_x * block_count_y * 16;
Vector<uint8_t> compressed_data;
compressed_data.resize(comp_len);
compressed_data.fill(0);
const astcenc_swizzle swizzle = {
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
};
status = astcenc_compress_image(context, &image, &swizzle, compressed_data.ptrw(), comp_len, 0);
ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS,
vformat("astcenc: ASTC image compression failed: %s.", astcenc_get_error_string(status)));
// Replace original image with compressed one. // Replace original image with compressed one.
r_img->set_data(width, height, mipmaps, target_format, compressed_data); r_img->set_data(width, height, mipmaps, target_format, dest_data);
print_verbose(vformat("astcenc: Encoding took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time))); print_verbose(vformat("astcenc: Encoding took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time)));
} }
@ -184,68 +210,81 @@ void _decompress_astc(Image *r_img) {
astcenc_config config; astcenc_config config;
const float quality = ASTCENC_PRE_MEDIUM; const float quality = ASTCENC_PRE_MEDIUM;
astcenc_error status = astcenc_config_init(profile, block_x, block_y, block_x, quality, 0, &config); astcenc_error status = astcenc_config_init(profile, block_x, block_y, 1, quality, 0, &config);
ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS,
vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status))); vformat("astcenc: Configuration initialization failed: %s.", astcenc_get_error_string(status)));
// Context allocation. // Context allocation.
astcenc_context *context = nullptr; astcenc_context *context = nullptr;
const unsigned int thread_count = OS::get_singleton()->get_processor_count(); const unsigned int thread_count = 1;
status = astcenc_context_alloc(&config, thread_count, &context); status = astcenc_context_alloc(&config, thread_count, &context);
ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS,
vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status))); vformat("astcenc: Context allocation failed: %s.", astcenc_get_error_string(status)));
// Decompress image. Image::Format target_format = is_hdr ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8;
const bool mipmaps = r_img->has_mipmaps(); const bool mipmaps = r_img->has_mipmaps();
int width = r_img->get_width(); int width = r_img->get_width();
int height = r_img->get_height(); int height = r_img->get_height();
int dest_size = Image::get_image_data_size(width, height, target_format, mipmaps);
Vector<uint8_t> dest_data;
dest_data.resize(dest_size);
uint8_t *dest_write = dest_data.ptrw();
astcenc_image image; // Decompress image.
image.dim_x = width;
image.dim_y = height;
image.dim_z = 1;
image.data_type = ASTCENC_TYPE_U8;
Image::Format target_format = Image::FORMAT_RGBA8;
if (is_hdr) {
target_format = Image::FORMAT_RGBAF;
image.data_type = ASTCENC_TYPE_F32;
}
Vector<uint8_t> image_data = r_img->get_data(); Vector<uint8_t> image_data = r_img->get_data();
int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
Vector<uint8_t> new_image_data; for (int i = 0; i < mip_count + 1; i++) {
new_image_data.resize(Image::get_image_data_size(width, height, target_format, false)); int src_mip_w, src_mip_h;
new_image_data.fill(0);
uint8_t *slices = new_image_data.ptrw();
image.data = reinterpret_cast<void **>(&slices);
const astcenc_swizzle swizzle = { int src_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, r_img->get_format(), i, src_mip_w, src_mip_h);
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A const uint8_t *src_data = &image_data.ptr()[src_ofs];
}; int src_size;
if (i == mip_count) {
src_size = image_data.size() - src_ofs;
} else {
int auxw, auxh;
src_size = Image::get_image_mipmap_offset_and_dimensions(width, height, r_img->get_format(), i + 1, auxw, auxh) - src_ofs;
}
status = astcenc_decompress_image(context, image_data.ptr(), image_data.size(), &image, &swizzle, 0); int dst_mip_w, dst_mip_h;
ERR_FAIL_COND_MSG(status != ASTCENC_SUCCESS, int dst_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, dst_mip_w, dst_mip_h);
vformat("astcenc: ASTC decompression failed: %s.", astcenc_get_error_string(status))); // Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer).
ERR_FAIL_COND_MSG(image.dim_z > 1, ERR_FAIL_COND(dst_ofs % 8 != 0);
"astcenc: ASTC decompression failed because this is a 3D texture, which is not supported."); uint8_t *dest_mip_write = (uint8_t *)&dest_write[dst_ofs];
astcenc_image image;
image.dim_x = dst_mip_w;
image.dim_y = dst_mip_h;
image.dim_z = 1;
image.data_type = ASTCENC_TYPE_U8;
if (is_hdr) {
target_format = Image::FORMAT_RGBAF;
image.data_type = ASTCENC_TYPE_F32;
}
image.data = (void **)(&dest_mip_write);
const astcenc_swizzle swizzle = {
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
};
status = astcenc_decompress_image(context, src_data, src_size, &image, &swizzle, 0);
ERR_BREAK_MSG(status != ASTCENC_SUCCESS,
vformat("astcenc: ASTC decompression failed: %s.", astcenc_get_error_string(status)));
ERR_BREAK_MSG(image.dim_z > 1,
"astcenc: ASTC decompression failed because this is a 3D texture, which is not supported.");
astcenc_compress_reset(context);
}
astcenc_context_free(context);
// Replace original image with compressed one. // Replace original image with compressed one.
Image::Format image_format = Image::FORMAT_RGBA8; r_img->set_data(width, height, mipmaps, target_format, dest_data);
if (image.data_type == ASTCENC_TYPE_F32) {
image_format = Image::FORMAT_RGBAF;
} else if (image.data_type == ASTCENC_TYPE_U8) {
image_format = Image::FORMAT_RGBA8;
} else if (image.data_type == ASTCENC_TYPE_F16) {
image_format = Image::FORMAT_RGBAH;
} else {
ERR_FAIL_MSG("astcenc: ASTC decompression failed with an unknown format.");
}
r_img->set_data(image.dim_x, image.dim_y, mipmaps, image_format, new_image_data);
print_verbose(vformat("astcenc: Decompression took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time))); print_verbose(vformat("astcenc: Decompression took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time)));
} }

View File

@ -33,7 +33,7 @@
#include "core/io/image.h" #include "core/io/image.h"
void _compress_astc(Image *r_img, float p_lossy_quality, Image::ASTCFormat p_format); void _compress_astc(Image *r_img, Image::ASTCFormat p_format);
void _decompress_astc(Image *r_img); void _decompress_astc(Image *r_img);
#endif // IMAGE_COMPRESS_ASTCENC_H #endif // IMAGE_COMPRESS_ASTCENC_H

View File

@ -129,7 +129,7 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const
} }
} }
void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChannels p_channels) { void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels) {
if (p_image->get_format() >= Image::FORMAT_BPTC_RGBA) { if (p_image->get_format() >= Image::FORMAT_BPTC_RGBA) {
return; //do not compress, already compressed return; //do not compress, already compressed
} }

View File

@ -33,7 +33,7 @@
#include "core/io/image.h" #include "core/io/image.h"
void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChannels p_channels); void image_compress_cvtt(Image *p_image, Image::UsedChannels p_channels);
void image_decompress_cvtt(Image *p_image); void image_decompress_cvtt(Image *p_image);
#endif // IMAGE_COMPRESS_CVTT_H #endif // IMAGE_COMPRESS_CVTT_H

View File

@ -74,25 +74,23 @@ EtcpakType _determine_dxt_type(Image::UsedChannels p_channels) {
} }
} }
void _compress_etc1(Image *r_img, float p_lossy_quality) { void _compress_etc1(Image *r_img) {
_compress_etcpak(EtcpakType::ETCPAK_TYPE_ETC1, r_img, p_lossy_quality); _compress_etcpak(EtcpakType::ETCPAK_TYPE_ETC1, r_img);
} }
void _compress_etc2(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels) { void _compress_etc2(Image *r_img, Image::UsedChannels p_channels) {
EtcpakType type = _determine_etc_type(p_channels); EtcpakType type = _determine_etc_type(p_channels);
_compress_etcpak(type, r_img, p_lossy_quality); _compress_etcpak(type, r_img);
} }
void _compress_bc(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels) { void _compress_bc(Image *r_img, Image::UsedChannels p_channels) {
EtcpakType type = _determine_dxt_type(p_channels); EtcpakType type = _determine_dxt_type(p_channels);
_compress_etcpak(type, r_img, p_lossy_quality); _compress_etcpak(type, r_img);
} }
void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_quality) { void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) {
uint64_t start_time = OS::get_singleton()->get_ticks_msec(); uint64_t start_time = OS::get_singleton()->get_ticks_msec();
// TODO: See how to handle lossy quality.
Image::Format img_format = r_img->get_format(); Image::Format img_format = r_img->get_format();
if (img_format >= Image::FORMAT_DXT1) { if (img_format >= Image::FORMAT_DXT1) {
return; // Do not compress, already compressed. return; // Do not compress, already compressed.

View File

@ -43,10 +43,10 @@ enum class EtcpakType {
ETCPAK_TYPE_DXT5_RA_AS_RG, ETCPAK_TYPE_DXT5_RA_AS_RG,
}; };
void _compress_etc1(Image *r_img, float p_lossy_quality); void _compress_etc1(Image *r_img);
void _compress_etc2(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels); void _compress_etc2(Image *r_img, Image::UsedChannels p_channels);
void _compress_bc(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels); void _compress_bc(Image *r_img, Image::UsedChannels p_channels);
void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_quality); void _compress_etcpak(EtcpakType p_compresstype, Image *r_img);
#endif // IMAGE_COMPRESS_ETCPAK_H #endif // IMAGE_COMPRESS_ETCPAK_H

View File

@ -1711,6 +1711,7 @@ Vector<EditorExportPlatformAndroid::ABI> EditorExportPlatformAndroid::get_enable
void EditorExportPlatformAndroid::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const { void EditorExportPlatformAndroid::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
r_features->push_back("etc2"); r_features->push_back("etc2");
r_features->push_back("astc");
Vector<ABI> abis = get_enabled_abis(p_preset); Vector<ABI> abis = get_enabled_abis(p_preset);
for (int i = 0; i < abis.size(); ++i) { for (int i = 0; i < abis.size(); ++i) {

View File

@ -43,6 +43,7 @@
void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const { void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
// Vulkan and OpenGL ES 3.0 both mandate ETC2 support. // Vulkan and OpenGL ES 3.0 both mandate ETC2 support.
r_features->push_back("etc2"); r_features->push_back("etc2");
r_features->push_back("astc");
Vector<String> architectures = _get_preset_architectures(p_preset); Vector<String> architectures = _get_preset_architectures(p_preset);
for (int i = 0; i < architectures.size(); ++i) { for (int i = 0; i < architectures.size(); ++i) {

View File

@ -48,16 +48,18 @@
#endif #endif
void EditorExportPlatformMacOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const { void EditorExportPlatformMacOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
if (p_preset->get("texture_format/s3tc")) {
r_features->push_back("s3tc");
}
if (p_preset->get("texture_format/etc")) {
r_features->push_back("etc");
}
if (p_preset->get("texture_format/etc2")) {
r_features->push_back("etc2");
}
r_features->push_back(p_preset->get("binary_format/architecture")); r_features->push_back(p_preset->get("binary_format/architecture"));
String architecture = p_preset->get("binary_format/architecture");
if (architecture == "universal" || architecture == "x86_64") {
r_features->push_back("s3tc");
r_features->push_back("bptc");
} else if (architecture == "arm64") {
r_features->push_back("etc2");
r_features->push_back("astc");
} else {
ERR_PRINT("Invalid architecture");
}
} }
bool EditorExportPlatformMacOS::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const HashMap<StringName, Variant> &p_options) const { bool EditorExportPlatformMacOS::get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
@ -210,10 +212,6 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/removable_volumes_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use removable volumes"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/removable_volumes_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use removable volumes"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/removable_volumes_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary())); r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/removable_volumes_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false));
String run_script = "#!/usr/bin/env bash\n" String run_script = "#!/usr/bin/env bash\n"
"unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"\n" "unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"\n"
"open \"{temp_dir}/{exe_name}.app\" --args {cmd_args}"; "open \"{temp_dir}/{exe_name}.app\" --args {cmd_args}";
@ -1766,6 +1764,24 @@ bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorE
} }
} }
String architecture = p_preset->get("binary_format/architecture");
if (architecture == "universal" || architecture == "x86_64") {
const String bc_error = test_bc();
if (!bc_error.is_empty()) {
valid = false;
err += bc_error;
}
} else if (architecture == "arm64") {
const String etc_error = test_etc2();
if (!etc_error.is_empty()) {
valid = false;
err += etc_error;
}
} else {
ERR_PRINT("Invalid architecture");
}
// Look for export templates (official templates, check only is custom templates are not set). // Look for export templates (official templates, check only is custom templates are not set).
if (!dvalid || !rvalid) { if (!dvalid || !rvalid) {
dvalid = exists_export_template("macos.zip", &err); dvalid = exists_export_template("macos.zip", &err);

View File

@ -1626,7 +1626,7 @@ Vector2i DisplayServerWindows::ime_get_selection() const {
ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, string, length); ImmGetCompositionStringW(wd.im_himc, GCS_COMPSTR, string, length);
int32_t utf32_cursor = 0; int32_t utf32_cursor = 0;
for (int32_t i = 0; i < length / sizeof(wchar_t); i++) { for (int32_t i = 0; i < length / int32_t(sizeof(wchar_t)); i++) {
if ((string[i] & 0xfffffc00) == 0xd800) { if ((string[i] & 0xfffffc00) == 0xd800) {
i++; i++;
} }

View File

@ -286,6 +286,10 @@ bool Utilities::has_os_feature(const String &p_feature) const {
return true; return true;
} }
if (p_feature == "astc" && RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ASTC_4x4_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT)) {
return true;
}
return false; return false;
} }

View File

@ -2849,10 +2849,8 @@ RenderingServer::RenderingServer() {
} }
void RenderingServer::init() { void RenderingServer::init() {
GLOBAL_DEF_RST("rendering/textures/vram_compression/import_bptc", false); GLOBAL_DEF_RST("rendering/textures/vram_compression/import_s3tc_bptc", OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_S3TC_BPTC);
GLOBAL_DEF_RST("rendering/textures/vram_compression/import_s3tc", true); GLOBAL_DEF_RST("rendering/textures/vram_compression/import_etc2_astc", OS::get_singleton()->get_preferred_texture_format() == OS::PREFERRED_TEXTURE_FORMAT_ETC2_ASTC);
GLOBAL_DEF_RST("rendering/textures/vram_compression/import_etc", false);
GLOBAL_DEF_RST("rendering/textures/vram_compression/import_etc2", true);
GLOBAL_DEF("rendering/textures/lossless_compression/force_png", false); GLOBAL_DEF("rendering/textures/lossless_compression/force_png", false);