feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")
Import("env_modules")
@ -11,47 +12,74 @@ thirdparty_obj = []
# Not unbundled so far since not widespread as shared library
thirdparty_dir = "#thirdparty/basis_universal/"
# Only build the encoder for editor builds
basisu_encoder = env.editor_build
# Sync list with upstream CMakeLists.txt
encoder_sources = [
"basisu_backend.cpp",
"basisu_basis_file.cpp",
"basisu_bc7enc.cpp",
"basisu_opencl.cpp",
"basisu_comp.cpp",
"basisu_enc.cpp",
"basisu_etc.cpp",
"basisu_frontend.cpp",
"basisu_gpu_texture.cpp",
"basisu_kernels_sse.cpp",
"basisu_pvrtc1_4.cpp",
"basisu_resampler.cpp",
"basisu_resample_filters.cpp",
"basisu_ssim.cpp",
"basisu_uastc_enc.cpp",
"pvpngreader.cpp",
]
encoder_sources = [thirdparty_dir + "encoder/" + file for file in encoder_sources]
if basisu_encoder:
encoder_sources = [
"3rdparty/android_astc_decomp.cpp",
"basisu_astc_hdr_enc.cpp",
"basisu_backend.cpp",
"basisu_basis_file.cpp",
"basisu_bc7enc.cpp",
"basisu_opencl.cpp",
"basisu_comp.cpp",
"basisu_enc.cpp",
"basisu_etc.cpp",
"basisu_frontend.cpp",
"basisu_gpu_texture.cpp",
"basisu_kernels_sse.cpp",
"basisu_pvrtc1_4.cpp",
"basisu_resampler.cpp",
"basisu_resample_filters.cpp",
"basisu_ssim.cpp",
"basisu_uastc_enc.cpp",
"pvpngreader.cpp",
]
encoder_sources = [thirdparty_dir + "encoder/" + file for file in encoder_sources]
transcoder_sources = [thirdparty_dir + "transcoder/basisu_transcoder.cpp"]
# Treat Basis headers as system headers to avoid raising warnings. Not supported on MSVC.
if not env.msvc:
env_basisu.Append(
CPPFLAGS=["-isystem", Dir(thirdparty_dir).path, "-isystem", Dir("#thirdparty/jpeg-compressor").path]
)
env_basisu.Append(CPPFLAGS=["-isystem", Dir(thirdparty_dir).path])
else:
env_basisu.Prepend(CPPPATH=[thirdparty_dir, "#thirdparty/jpeg-compressor"])
env_basisu.Prepend(CPPPATH=[thirdparty_dir])
if basisu_encoder:
env_basisu.Prepend(CPPPATH=["#thirdparty/jpeg-compressor"])
env_basisu.Prepend(CPPPATH=["#thirdparty/tinyexr"])
if env["builtin_zstd"]:
env_basisu.Prepend(CPPPATH=["#thirdparty/zstd"])
if env.dev_build:
env_basisu.Append(CPPDEFINES=[("BASISU_DEVEL_MESSAGES", 1), ("BASISD_ENABLE_DEBUG_FLAGS", 1)])
env_thirdparty = env_basisu.Clone()
env_thirdparty.disable_warnings()
if env.editor_build:
env_thirdparty.Append(CPPDEFINES=["BASISU_NO_IMG_LOADERS"])
# Disable unneeded features to reduce binary size.
# <https://github.com/BinomialLLC/basis_universal/wiki/How-to-Use-and-Configure-the-Transcoder>
env_thirdparty.Append(
CPPDEFINES=[
# Storage formats.
# Godot only implements `.basis` support through basis_universal.
# Support for `.ktx` files are implemented with a direct libktx implementation.
# Building the encoder requires `BASISD_SUPPORT_KTX2` to be enabled,
# so we can only disable Zstandard compression for `.ktx` files
# (this is not used in `.basis` files).
("BASISD_SUPPORT_KTX2_ZSTD", 0),
# GPU compression formats.
("BASISD_SUPPORT_ATC", 0), # Proprietary Adreno format not supported by Godot.
("BASISD_SUPPORT_FXT1", 0), # Legacy format not supported by Godot.
("BASISD_SUPPORT_PVRTC1", 0), # Legacy format not supported by Godot.
("BASISD_SUPPORT_PVRTC2", 0), # Legacy format not supported by Godot.
]
)
if basisu_encoder:
env_thirdparty.add_source_files(thirdparty_obj, encoder_sources)
env_thirdparty.add_source_files(thirdparty_obj, transcoder_sources)
env.modules_sources += thirdparty_obj

View file

@ -1,5 +1,6 @@
def can_build(env, platform):
env.module_add_dependencies("basis_universal", ["jpg"])
if env.editor_build: # Encoder dependencies
env.module_add_dependencies("basis_universal", ["jpg", "tinyexr"])
return True

View file

@ -30,25 +30,73 @@
#include "image_compress_basisu.h"
#include "core/io/image.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
#include "servers/rendering_server.h"
#include <transcoder/basisu_transcoder.h>
#ifdef TOOLS_ENABLED
#include <encoder/basisu_comp.h>
static Mutex init_mutex;
static bool initialized = false;
#endif
void basis_universal_init() {
#ifdef TOOLS_ENABLED
basisu::basisu_encoder_init();
#endif
basist::basisu_transcoder_init();
}
#ifdef TOOLS_ENABLED
template <typename T>
inline void _basisu_pad_mipmap(const uint8_t *p_image_mip_data, Vector<uint8_t> &r_mip_data_padded, int p_next_width, int p_next_height, int p_width, int p_height, int64_t p_size) {
// Source mip's data interpreted as 32-bit RGBA blocks to help with copying pixel data.
const T *mip_src_data = reinterpret_cast<const T *>(p_image_mip_data);
// Reserve space in the padded buffer.
r_mip_data_padded.resize(p_next_width * p_next_height * sizeof(T));
T *data_padded_ptr = reinterpret_cast<T *>(r_mip_data_padded.ptrw());
// Pad mipmap to the nearest block by smearing.
int x = 0, y = 0;
for (y = 0; y < p_height; y++) {
for (x = 0; x < p_width; x++) {
data_padded_ptr[p_next_width * y + x] = mip_src_data[p_width * y + x];
}
// First, smear in x.
for (; x < p_next_width; x++) {
data_padded_ptr[p_next_width * y + x] = data_padded_ptr[p_next_width * y + x - 1];
}
}
// Then, smear in y.
for (; y < p_next_height; y++) {
for (x = 0; x < p_next_width; x++) {
data_padded_ptr[p_next_width * y + x] = data_padded_ptr[p_next_width * y + x - p_next_width];
}
}
}
Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels) {
init_mutex.lock();
if (!initialized) {
basisu::basisu_encoder_init();
initialized = true;
}
init_mutex.unlock();
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
Ref<Image> image = p_image->duplicate();
image->convert(Image::FORMAT_RGBA8);
bool is_hdr = false;
if (image->get_format() <= Image::FORMAT_RGB565) {
image->convert(Image::FORMAT_RGBA8);
} else if (image->get_format() <= Image::FORMAT_RGBE9995) {
image->convert(Image::FORMAT_RGBAF);
is_hdr = true;
}
basisu::basis_compressor_params params;
@ -74,34 +122,42 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
basisu::job_pool job_pool(OS::get_singleton()->get_processor_count());
params.m_pJob_pool = &job_pool;
BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_RG;
switch (p_channels) {
case Image::USED_CHANNELS_L: {
decompress_format = BASIS_DECOMPRESS_RGB;
} break;
case Image::USED_CHANNELS_LA: {
params.m_force_alpha = true;
decompress_format = BASIS_DECOMPRESS_RGBA;
} break;
case Image::USED_CHANNELS_R: {
decompress_format = BASIS_DECOMPRESS_RGB;
} break;
case Image::USED_CHANNELS_RG: {
// Currently RG textures are compressed as DXT5/ETC2_RGBA8 with a RA -> RG swizzle,
// as BasisUniversal didn't use to support ETC2_RG11 transcoding.
params.m_force_alpha = true;
image->convert_rg_to_ra_rgba8();
decompress_format = BASIS_DECOMPRESS_RG_AS_RA;
} break;
case Image::USED_CHANNELS_RGB: {
decompress_format = BASIS_DECOMPRESS_RGB;
} break;
case Image::USED_CHANNELS_RGBA: {
params.m_force_alpha = true;
decompress_format = BASIS_DECOMPRESS_RGBA;
} break;
BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_MAX;
if (is_hdr) {
decompress_format = BASIS_DECOMPRESS_HDR_RGB;
params.m_hdr = true;
params.m_uastc_hdr_options.set_quality_level(0);
} else {
switch (p_channels) {
case Image::USED_CHANNELS_L: {
decompress_format = BASIS_DECOMPRESS_RGB;
} break;
case Image::USED_CHANNELS_LA: {
params.m_force_alpha = true;
decompress_format = BASIS_DECOMPRESS_RGBA;
} break;
case Image::USED_CHANNELS_R: {
decompress_format = BASIS_DECOMPRESS_R;
} break;
case Image::USED_CHANNELS_RG: {
params.m_force_alpha = true;
image->convert_rg_to_ra_rgba8();
decompress_format = BASIS_DECOMPRESS_RG;
} break;
case Image::USED_CHANNELS_RGB: {
decompress_format = BASIS_DECOMPRESS_RGB;
} break;
case Image::USED_CHANNELS_RGBA: {
params.m_force_alpha = true;
decompress_format = BASIS_DECOMPRESS_RGBA;
} break;
}
}
ERR_FAIL_COND_V(decompress_format == BASIS_DECOMPRESS_MAX, Vector<uint8_t>());
// Copy the source image data with mipmaps into BasisU.
{
const int orig_width = image->get_width();
@ -115,9 +171,10 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
Vector<uint8_t> image_data = image->get_data();
basisu::vector<basisu::image> basisu_mipmaps;
basisu::vector<basisu::imagef> basisu_mipmaps_hdr;
// Buffer for storing padded mipmap data.
Vector<uint32_t> mip_data_padded;
Vector<uint8_t> mip_data_padded;
for (int32_t i = 0; i <= image->get_mipmap_count(); i++) {
int64_t ofs, size;
@ -128,31 +185,10 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
// Pad the mipmap's data if its resolution isn't divisible by 4.
if (image->has_mipmaps() && !is_res_div_4 && (width > 2 && height > 2) && (width != next_width || height != next_height)) {
// Source mip's data interpreted as 32-bit RGBA blocks to help with copying pixel data.
const uint32_t *mip_src_data = reinterpret_cast<const uint32_t *>(image_mip_data);
// Reserve space in the padded buffer.
mip_data_padded.resize(next_width * next_height);
uint32_t *data_padded_ptr = mip_data_padded.ptrw();
// Pad mipmap to the nearest block by smearing.
int x = 0, y = 0;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
data_padded_ptr[next_width * y + x] = mip_src_data[width * y + x];
}
// First, smear in x.
for (; x < next_width; x++) {
data_padded_ptr[next_width * y + x] = data_padded_ptr[next_width * y + x - 1];
}
}
// Then, smear in y.
for (; y < next_height; y++) {
for (x = 0; x < next_width; x++) {
data_padded_ptr[next_width * y + x] = data_padded_ptr[next_width * y + x - next_width];
}
if (is_hdr) {
_basisu_pad_mipmap<BasisRGBAF>(image_mip_data, mip_data_padded, next_width, next_height, width, height, size);
} else {
_basisu_pad_mipmap<uint32_t>(image_mip_data, mip_data_padded, next_width, next_height, width, height, size);
}
// Override the image_mip_data pointer with our temporary Vector.
@ -161,7 +197,7 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
// Override the mipmap's properties.
width = next_width;
height = next_height;
size = mip_data_padded.size() * 4;
size = mip_data_padded.size();
}
// Get the next mipmap's resolution.
@ -169,44 +205,61 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
next_height /= 2;
// Copy the source mipmap's data to a BasisU image.
basisu::image basisu_image(width, height);
memcpy(basisu_image.get_ptr(), image_mip_data, size);
if (is_hdr) {
basisu::imagef basisu_image(width, height);
memcpy(reinterpret_cast<uint8_t *>(basisu_image.get_ptr()), image_mip_data, size);
if (i == 0) {
params.m_source_images_hdr.push_back(basisu_image);
} else {
basisu_mipmaps_hdr.push_back(basisu_image);
}
if (i == 0) {
params.m_source_images.push_back(basisu_image);
} else {
basisu_mipmaps.push_back(basisu_image);
basisu::image basisu_image(width, height);
memcpy(basisu_image.get_ptr(), image_mip_data, size);
if (i == 0) {
params.m_source_images.push_back(basisu_image);
} else {
basisu_mipmaps.push_back(basisu_image);
}
}
}
params.m_source_mipmap_images.push_back(basisu_mipmaps);
if (is_hdr) {
params.m_source_mipmap_images_hdr.push_back(basisu_mipmaps_hdr);
} else {
params.m_source_mipmap_images.push_back(basisu_mipmaps);
}
}
// Encode the image data.
Vector<uint8_t> basisu_data;
basisu::basis_compressor compressor;
compressor.init(params);
int basisu_err = compressor.process();
ERR_FAIL_COND_V(basisu_err != basisu::basis_compressor::cECSuccess, basisu_data);
ERR_FAIL_COND_V(basisu_err != basisu::basis_compressor::cECSuccess, Vector<uint8_t>());
const basisu::uint8_vec &basisu_out = compressor.get_output_basis_file();
basisu_data.resize(basisu_out.size() + 4);
const basisu::uint8_vec &basisu_encoded = compressor.get_output_basis_file();
// Copy the encoded data to the buffer.
{
uint8_t *wb = basisu_data.ptrw();
*(uint32_t *)wb = decompress_format;
Vector<uint8_t> basisu_data;
basisu_data.resize(basisu_encoded.size() + 4);
uint8_t *basisu_data_ptr = basisu_data.ptrw();
memcpy(wb + 4, basisu_out.get_ptr(), basisu_out.size());
}
// Copy the encoded BasisU data into the output buffer.
*(uint32_t *)basisu_data_ptr = decompress_format;
memcpy(basisu_data_ptr + 4, basisu_encoded.get_ptr(), basisu_encoded.size());
print_verbose(vformat("BasisU: Encoding a %dx%d image with %d mipmaps took %d ms.", p_image->get_width(), p_image->get_height(), p_image->get_mipmap_count(), OS::get_singleton()->get_ticks_msec() - start_time));
return basisu_data;
}
#endif // TOOLS_ENABLED
Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
Ref<Image> image;
ERR_FAIL_NULL_V_MSG(p_data, image, "Cannot unpack invalid BasisUniversal data.");
@ -219,15 +272,68 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
// Get supported compression formats.
bool bptc_supported = RS::get_singleton()->has_os_feature("bptc");
bool astc_supported = RS::get_singleton()->has_os_feature("astc");
bool rgtc_supported = RS::get_singleton()->has_os_feature("rgtc");
bool s3tc_supported = RS::get_singleton()->has_os_feature("s3tc");
bool etc2_supported = RS::get_singleton()->has_os_feature("etc2");
bool needs_ra_rg_swap = false;
bool needs_rg_trim = false;
switch (*(uint32_t *)(src_ptr)) {
BasisDecompressFormat decompress_format = (BasisDecompressFormat)(*(uint32_t *)(src_ptr));
switch (decompress_format) {
case BASIS_DECOMPRESS_R: {
if (rgtc_supported) {
basisu_format = basist::transcoder_texture_format::cTFBC4_R;
image_format = Image::FORMAT_RGTC_R;
} else if (s3tc_supported) {
basisu_format = basist::transcoder_texture_format::cTFBC1;
image_format = Image::FORMAT_DXT1;
} else if (etc2_supported) {
basisu_format = basist::transcoder_texture_format::cTFETC2_EAC_R11;
image_format = Image::FORMAT_ETC2_R11;
} else {
// No supported VRAM compression formats, decompress.
basisu_format = basist::transcoder_texture_format::cTFRGBA32;
image_format = Image::FORMAT_RGBA8;
needs_rg_trim = true;
}
} break;
case BASIS_DECOMPRESS_RG: {
// RGTC transcoding is currently performed with RG_AS_RA, fail.
ERR_FAIL_V(image);
if (rgtc_supported) {
basisu_format = basist::transcoder_texture_format::cTFBC5_RG;
image_format = Image::FORMAT_RGTC_RG;
} else if (s3tc_supported) {
basisu_format = basist::transcoder_texture_format::cTFBC3;
image_format = Image::FORMAT_DXT5_RA_AS_RG;
} else if (etc2_supported) {
basisu_format = basist::transcoder_texture_format::cTFETC2_EAC_RG11;
image_format = Image::FORMAT_ETC2_RG11;
} else {
// No supported VRAM compression formats, decompress.
basisu_format = basist::transcoder_texture_format::cTFRGBA32;
image_format = Image::FORMAT_RGBA8;
needs_ra_rg_swap = true;
needs_rg_trim = true;
}
} break;
case BASIS_DECOMPRESS_RG_AS_RA: {
if (s3tc_supported) {
basisu_format = basist::transcoder_texture_format::cTFBC3;
image_format = Image::FORMAT_DXT5_RA_AS_RG;
} else if (etc2_supported) {
basisu_format = basist::transcoder_texture_format::cTFETC2;
image_format = Image::FORMAT_ETC2_RA_AS_RG;
} else {
// No supported VRAM compression formats, decompress.
basisu_format = basist::transcoder_texture_format::cTFRGBA32;
image_format = Image::FORMAT_RGBA8;
needs_ra_rg_swap = true;
needs_rg_trim = true;
}
} break;
case BASIS_DECOMPRESS_RGB: {
if (bptc_supported) {
@ -267,20 +373,24 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
basisu_format = basist::transcoder_texture_format::cTFRGBA32;
image_format = Image::FORMAT_RGBA8;
}
} break;
case BASIS_DECOMPRESS_RG_AS_RA: {
if (s3tc_supported) {
basisu_format = basist::transcoder_texture_format::cTFBC3;
image_format = Image::FORMAT_DXT5_RA_AS_RG;
} else if (etc2_supported) {
basisu_format = basist::transcoder_texture_format::cTFETC2;
image_format = Image::FORMAT_ETC2_RA_AS_RG;
case BASIS_DECOMPRESS_HDR_RGB: {
if (bptc_supported) {
basisu_format = basist::transcoder_texture_format::cTFBC6H;
image_format = Image::FORMAT_BPTC_RGBFU;
} else if (astc_supported) {
basisu_format = basist::transcoder_texture_format::cTFASTC_HDR_4x4_RGBA;
image_format = Image::FORMAT_ASTC_4x4_HDR;
} else {
// No supported VRAM compression formats, decompress.
basisu_format = basist::transcoder_texture_format::cTFRGBA32;
image_format = Image::FORMAT_RGBA8;
needs_ra_rg_swap = true;
basisu_format = basist::transcoder_texture_format::cTFRGB_9E5;
image_format = Image::FORMAT_RGBE9995;
}
} break;
default: {
ERR_FAIL_V(image);
} break;
}
@ -324,6 +434,18 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
image->convert_ra_rgba8_to_rg();
}
if (needs_rg_trim) {
// Remove unnecessary color channels from uncompressed textures.
if (decompress_format == BASIS_DECOMPRESS_R) {
image->convert(Image::FORMAT_R8);
} else if (decompress_format == BASIS_DECOMPRESS_RG || decompress_format == BASIS_DECOMPRESS_RG_AS_RA) {
image->convert(Image::FORMAT_RG8);
}
}
print_verbose(vformat("BasisU: Transcoding a %dx%d image with %d mipmaps into %s took %d ms.",
image->get_width(), image->get_height(), image->get_mipmap_count(), Image::get_format_name(image_format), OS::get_singleton()->get_ticks_msec() - start_time));
return image;
}

View file

@ -38,11 +38,21 @@ enum BasisDecompressFormat {
BASIS_DECOMPRESS_RGB,
BASIS_DECOMPRESS_RGBA,
BASIS_DECOMPRESS_RG_AS_RA,
BASIS_DECOMPRESS_R,
BASIS_DECOMPRESS_HDR_RGB,
BASIS_DECOMPRESS_MAX
};
void basis_universal_init();
#ifdef TOOLS_ENABLED
struct BasisRGBAF {
uint32_t r;
uint32_t g;
uint32_t b;
uint32_t a;
};
Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels);
#endif

View file

@ -1,13 +0,0 @@
diff --git a/thirdparty/basis_universal/encoder/basisu_enc.cpp b/thirdparty/basis_universal/encoder/basisu_enc.cpp
index c431ceaf12..e87dd636a2 100644
--- a/thirdparty/basis_universal/encoder/basisu_enc.cpp
+++ b/thirdparty/basis_universal/encoder/basisu_enc.cpp
@@ -409,7 +409,7 @@ namespace basisu
bool load_jpg(const char *pFilename, image& img)
{
int width = 0, height = 0, actual_comps = 0;
- uint8_t *pImage_data = jpgd::decompress_jpeg_image_from_file(pFilename, &width, &height, &actual_comps, 4, jpgd::jpeg_decoder::cFlagLinearChromaFiltering);
+ uint8_t *pImage_data = jpgd::decompress_jpeg_image_from_file(pFilename, &width, &height, &actual_comps, 4, jpgd::jpeg_decoder::cFlagBoxChromaFiltering);
if (!pImage_data)
return false;