From 8916a04041c16c4a4e1a6b42cce2caef916aada5 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 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..a9493b70e355 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; @@ -223,12 +224,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 +244,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 +256,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);