From c1310fd2f3f5d78d13c63de955cf09afcd7ba3dd Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Thu, 24 Mar 2022 16:51:36 -0700 Subject: [PATCH] Misc improvements to Basis Universal. To increase efficiency enable compressed mip maps from Basis Universal. To decrease corruption max the ETC1S quality. To keep compatibility use the first mip of the previous internal Godot format. --- editor/import/resource_importer_texture.cpp | 17 ++--- modules/basis_universal/register_types.cpp | 72 +++++++++++---------- scene/resources/texture.cpp | 32 +++++++-- 3 files changed, 74 insertions(+), 47 deletions(-) diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 8d6db7d1e008..678d1b1dbe93 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -303,15 +303,11 @@ void ResourceImporterTexture::save_to_ctex_format(FileAccess *f, const Refstore_16(p_image->get_height()); f->store_32(p_image->get_mipmap_count()); f->store_32(p_image->get_format()); - - for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { - Vector data = Image::basis_universal_packer(p_image->get_image_from_mipmap(i), p_channels); - int data_len = data.size(); - f->store_32(data_len); - - const uint8_t *r = data.ptr(); - f->store_buffer(r, data_len); - } + Vector data = Image::basis_universal_packer(p_image, p_channels); + int data_len = data.size(); + f->store_32(data_len); + const uint8_t *r = data.ptr(); + f->store_buffer(r, data_len); } break; } } @@ -368,7 +364,8 @@ void ResourceImporterTexture::_save_ctex(const Ref &p_image, const String Ref image = p_image->duplicate(); - if (((p_compress_mode == COMPRESS_BASIS_UNIVERSAL) || (p_compress_mode == COMPRESS_VRAM_COMPRESSED && p_force_po2_for_compressed)) && p_mipmaps) { + if ((((p_compress_mode == COMPRESS_BASIS_UNIVERSAL) && p_force_po2_for_compressed) || (p_compress_mode == COMPRESS_VRAM_COMPRESSED && p_force_po2_for_compressed)) && + p_mipmaps) { image->resize_to_po2(); } diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp index 8e328a519d88..859a47df1848 100644 --- a/modules/basis_universal/register_types.cpp +++ b/modules/basis_universal/register_types.cpp @@ -32,9 +32,11 @@ #include "core/os/os.h" #include "servers/rendering_server.h" +#include #ifdef TOOLS_ENABLED #include +#include #endif #include @@ -52,44 +54,45 @@ enum BasisDecompressFormat { #ifdef TOOLS_ENABLED static Vector basis_universal_packer(const Ref &p_image, Image::UsedChannels p_channels) { Vector budata; - { + basisu::basis_compressor_params params; Ref image = p_image->duplicate(); - - // unfortunately, basis universal does not support compressing supplied mipmaps, - // so for the time being, only compressing individual images will have to do. - - if (image->has_mipmaps()) { - image->clear_mipmaps(); - } if (image->get_format() != Image::FORMAT_RGBA8) { image->convert(Image::FORMAT_RGBA8); } - - basisu::image buimg(image->get_width(), image->get_height()); - + Ref image_single = image->duplicate(); { - Vector vec = image->get_data(); + if (image_single->has_mipmaps()) { + image_single->clear_mipmaps(); + } + basisu::image buimg(image_single->get_width(), image_single->get_height()); + Vector vec = image_single->get_data(); const uint8_t *r = vec.ptr(); - memcpy(buimg.get_ptr(), r, vec.size()); + params.m_source_images.push_back(buimg); } - - basisu::basis_compressor_params params; - params.m_uastc = true; - params.m_max_endpoint_clusters = 512; - params.m_max_selector_clusters = 512; + basisu::vector source_images; + for (int32_t mipmap_i = 1; mipmap_i < image->get_mipmap_count(); mipmap_i++) { + Ref mip = image->get_image_from_mipmap(mipmap_i); + basisu::image buimg(mip->get_width(), mip->get_height()); + Vector vec = mip->get_data(); + const uint8_t *r = vec.ptr(); + memcpy(buimg.get_ptr(), r, vec.size()); + source_images.push_back(buimg); + } + params.m_source_mipmap_images.push_back(source_images); + params.m_quality_level = -1; + params.m_max_endpoint_clusters = basisu::BASISU_MAX_ENDPOINT_CLUSTERS; + params.m_max_selector_clusters = basisu::BASISU_MAX_SELECTOR_CLUSTERS; + params.m_no_endpoint_rdo = true; + params.m_no_selector_rdo = true; + params.m_mip_fast = false; params.m_multithreading = true; - //params.m_quality_level = 0; - //params.m_disable_hierarchical_endpoint_codebooks = true; - //params.m_no_selector_rdo = true; + params.m_uastc = false; basisu::job_pool jpool(OS::get_singleton()->get_processor_count()); params.m_pJob_pool = &jpool; - params.m_mip_gen = false; //sorry, please some day support provided mipmaps. - params.m_source_images.push_back(buimg); - BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_RG; params.m_check_for_alpha = false; @@ -223,12 +226,16 @@ static Ref basis_universal_unpacker(const Vector &p_buffer) { ERR_FAIL_COND_V(!tr.validate_header(ptr, size), image); - basist::basisu_image_info info; - tr.get_image_info(ptr, size, info, 0); + basist::basisu_file_info info; + tr.get_file_info(ptr, size, info); + basist::basisu_image_info image_info; + tr.get_image_info(ptr, size, image_info, 0); int block_size = basist::basis_get_bytes_per_block_or_pixel(format); Vector gpudata; - gpudata.resize(info.m_total_blocks * block_size); + ERR_FAIL_INDEX_V(0, info.m_image_mipmap_levels.size(), Ref()); + uint32_t total_mip_levels = info.m_image_mipmap_levels[0]; + gpudata.resize(Image::get_image_data_size(image_info.m_width, image_info.m_height, imgfmt, total_mip_levels > 1)); { uint8_t *w = gpudata.ptrw(); @@ -239,11 +246,11 @@ static Ref basis_universal_unpacker(const Vector &p_buffer) { int ofs = 0; tr.start_transcoding(ptr, size); - for (uint32_t i = 0; i < info.m_total_levels; i++) { + for (uint32_t i = 0; i < total_mip_levels; i++) { basist::basisu_image_level_info level; tr.get_image_level_info(ptr, size, level, 0, i); - bool ret = tr.transcode_image_level(ptr, size, 0, i, dst + ofs, level.m_total_blocks - i, format); + bool ret = tr.transcode_image_level(ptr, size, 0, i, dst + ofs, level.m_total_blocks, format); if (!ret) { printf("failed! on level %i\n", i); break; @@ -251,10 +258,9 @@ static Ref basis_universal_unpacker(const Vector &p_buffer) { ofs += level.m_total_blocks * block_size; }; - }; - - image.instantiate(); - image->create(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata); + image.instantiate(); + image->create(image_info.m_width, image_info.m_height, total_mip_levels > 1, imgfmt, gpudata); + } return image; } diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 3113987fbcf9..c5ca7df7911a 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -347,7 +347,7 @@ Ref CompressedTexture2D::load_image_from_file(FileAccess *f, int p_size_l uint32_t mipmaps = f->get_32(); Image::Format format = Image::Format(f->get_32()); - if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP || data_format == DATA_FORMAT_BASIS_UNIVERSAL) { + if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP) { //look for a PNG or WEBP file inside int sw = w; @@ -378,9 +378,7 @@ Ref CompressedTexture2D::load_image_from_file(FileAccess *f, int p_size_l } Ref img; - if (data_format == DATA_FORMAT_BASIS_UNIVERSAL && Image::basis_universal_unpacker) { - img = Image::basis_universal_unpacker(pv); - } else if (data_format == DATA_FORMAT_PNG && Image::png_unpacker) { + if (data_format == DATA_FORMAT_PNG && Image::png_unpacker) { img = Image::png_unpacker(pv); } else if (data_format == DATA_FORMAT_WEBP && Image::webp_unpacker) { img = Image::webp_unpacker(pv); @@ -439,6 +437,32 @@ Ref CompressedTexture2D::load_image_from_file(FileAccess *f, int p_size_l return image; } + } else if (data_format == DATA_FORMAT_BASIS_UNIVERSAL) { + int sw = w; + int sh = h; + uint32_t size = f->get_32(); + if (p_size_limit > 0 && (sw > p_size_limit || sh > p_size_limit)) { + //can't load this due to size limit + sw = MAX(sw >> 1, 1); + sh = MAX(sh >> 1, 1); + f->seek(f->get_position() + size); + return Ref(); + } + Vector pv; + pv.resize(size); + { + uint8_t *wr = pv.ptrw(); + f->get_buffer(wr, size); + } + Ref img; + img = Image::basis_universal_unpacker(pv); + if (img.is_null() || img->is_empty()) { + ERR_FAIL_COND_V(img.is_null() || img->is_empty(), Ref()); + } + format = img->get_format(); + sw = MAX(sw >> 1, 1); + sh = MAX(sh >> 1, 1); + return img; } else if (data_format == DATA_FORMAT_IMAGE) { int size = Image::get_image_data_size(w, h, format, mipmaps ? true : false);