From 053174c4eaff5cf18fc5862846be35876e55b7bd Mon Sep 17 00:00:00 2001 From: "K. S. Ernest (iFire) Lee" Date: Mon, 18 Jul 2022 17:58:27 -0700 Subject: [PATCH] Improvements to Basis Universal. Enable compressed mip maps from Basis Universal for faster compressions. Increase the quality to avoid corruption. To keep compatibility use the first mip of the previous internal Godot format. --- editor/import/resource_importer_texture.cpp | 16 ++--- modules/basis_universal/register_types.cpp | 72 ++++++++++++--------- scene/resources/texture.cpp | 32 +++++++-- 3 files changed, 75 insertions(+), 45 deletions(-) diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 0eed6184c01c..9d7083a1fe19 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,7 @@ 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_force_po2_for_compressed && p_mipmaps && ((p_compress_mode == COMPRESS_BASIS_UNIVERSAL) || (p_compress_mode == COMPRESS_VRAM_COMPRESSED))) { image->resize_to_po2(); } diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp index e80d453df7fc..b4f45d2c5f87 100644 --- a/modules/basis_universal/register_types.cpp +++ b/modules/basis_universal/register_types.cpp @@ -52,44 +52,51 @@ 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::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); - basisu::basis_compressor_params params; params.m_uastc = true; - params.m_max_endpoint_clusters = 512; - params.m_max_selector_clusters = 512; + params.m_quality_level = basisu::BASISU_QUALITY_MIN; + + params.m_pack_uastc_flags &= ~basisu::cPackUASTCLevelMask; + + static const uint32_t s_level_flags[basisu::TOTAL_PACK_UASTC_LEVELS] = { basisu::cPackUASTCLevelFastest, basisu::cPackUASTCLevelFaster, basisu::cPackUASTCLevelDefault, basisu::cPackUASTCLevelSlower, basisu::cPackUASTCLevelVerySlow }; + params.m_pack_uastc_flags |= s_level_flags[0]; + params.m_rdo_uastc = 0.0f; + params.m_rdo_uastc_quality_scalar = 0.0f; + params.m_rdo_uastc_dict_size = 1024; + + params.m_mip_fast = true; params.m_multithreading = true; - //params.m_quality_level = 0; - //params.m_disable_hierarchical_endpoint_codebooks = true; - //params.m_no_selector_rdo = 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 +229,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 +249,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 +261,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 05ed9238b887..8f6cdf09157c 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -651,7 +651,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; @@ -682,9 +682,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); @@ -743,6 +741,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);