From 4490b422944951779e43b0d372ce33e8d19ea2b4 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 increase the 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 | 70 +++++++++++---------- scene/resources/texture.cpp | 32 ++++++++-- 3 files changed, 72 insertions(+), 47 deletions(-) diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index deb30478649b..564b0f410be8 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -315,15 +315,11 @@ void ResourceImporterTexture::save_to_ctex_format(Ref 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; } } @@ -380,7 +376,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 e80d453df7fc..241a1608dcbd 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,43 @@ 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 = basisu::BASISU_QUALITY_MIN; + 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 = true; + params.m_rdo_uastc = true; + params.m_rdo_uastc_multithreading = true; 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; @@ -222,12 +223,16 @@ static Ref basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size 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(); @@ -238,11 +243,11 @@ static Ref basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size 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 %u\n", i); break; @@ -250,10 +255,9 @@ static Ref basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size 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 f31a71eada29..7cfb60aa7de3 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -641,7 +641,7 @@ Ref CompressedTexture2D::load_image_from_file(Ref f, int p_si 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; @@ -672,9 +672,7 @@ Ref CompressedTexture2D::load_image_from_file(Ref f, int p_si } 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); @@ -733,6 +731,32 @@ Ref CompressedTexture2D::load_image_from_file(Ref f, int p_si 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);