Skip to content

Commit 56ed76a

Browse files
committed
Merge pull request #97582 from BlueCube3310/basisu-hdr
BasisU: Update to 1.50.0 and add HDR support
2 parents a1e768c + 200ed09 commit 56ed76a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+17246
-924
lines changed

editor/import/resource_importer_layered_texture.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -339,11 +339,6 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
339339
return err;
340340
}
341341

342-
if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) {
343-
//basis universal does not support float formats, fall back
344-
compress_mode = COMPRESS_VRAM_COMPRESSED;
345-
}
346-
347342
if (compress_mode == COMPRESS_VRAM_COMPRESSED) {
348343
//if using video ram, optimize
349344
if (channel_pack == 0) {

editor/import/resource_importer_texture.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -593,11 +593,6 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
593593
}
594594
}
595595

596-
if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) {
597-
// Basis universal does not support float formats, fallback.
598-
compress_mode = COMPRESS_VRAM_COMPRESSED;
599-
}
600-
601596
bool detect_3d = int(p_options["detect_3d/compress_to"]) > 0;
602597
bool detect_roughness = roughness == 0;
603598
bool detect_normal = normal == 0;

modules/basis_universal/SCsub

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ thirdparty_obj = []
1414
thirdparty_dir = "#thirdparty/basis_universal/"
1515
# Sync list with upstream CMakeLists.txt
1616
encoder_sources = [
17+
"3rdparty/android_astc_decomp.cpp",
18+
"basisu_astc_hdr_enc.cpp",
1719
"basisu_backend.cpp",
1820
"basisu_basis_file.cpp",
1921
"basisu_bc7enc.cpp",
@@ -45,6 +47,8 @@ else:
4547
if env["builtin_zstd"]:
4648
env_basisu.Prepend(CPPPATH=["#thirdparty/zstd"])
4749

50+
env_basisu.Prepend(CPPPATH=["#thirdparty/tinyexr"])
51+
4852
if env.dev_build:
4953
env_basisu.Append(CPPDEFINES=[("BASISU_DEVEL_MESSAGES", 1), ("BASISD_ENABLE_DEBUG_FLAGS", 1)])
5054

modules/basis_universal/image_compress_basisu.cpp

Lines changed: 137 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030

3131
#include "image_compress_basisu.h"
3232

33+
#include "core/os/os.h"
34+
#include "core/string/print_string.h"
3335
#include "servers/rendering_server.h"
3436

3537
#include <transcoder/basisu_transcoder.h>
@@ -46,9 +48,48 @@ void basis_universal_init() {
4648
}
4749

4850
#ifdef TOOLS_ENABLED
51+
template <typename T>
52+
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) {
53+
// Source mip's data interpreted as 32-bit RGBA blocks to help with copying pixel data.
54+
const T *mip_src_data = reinterpret_cast<const T *>(p_image_mip_data);
55+
56+
// Reserve space in the padded buffer.
57+
r_mip_data_padded.resize(p_next_width * p_next_height * sizeof(T));
58+
T *data_padded_ptr = reinterpret_cast<T *>(r_mip_data_padded.ptrw());
59+
60+
// Pad mipmap to the nearest block by smearing.
61+
int x = 0, y = 0;
62+
for (y = 0; y < p_height; y++) {
63+
for (x = 0; x < p_width; x++) {
64+
data_padded_ptr[p_next_width * y + x] = mip_src_data[p_width * y + x];
65+
}
66+
67+
// First, smear in x.
68+
for (; x < p_next_width; x++) {
69+
data_padded_ptr[p_next_width * y + x] = data_padded_ptr[p_next_width * y + x - 1];
70+
}
71+
}
72+
73+
// Then, smear in y.
74+
for (; y < p_next_height; y++) {
75+
for (x = 0; x < p_next_width; x++) {
76+
data_padded_ptr[p_next_width * y + x] = data_padded_ptr[p_next_width * y + x - p_next_width];
77+
}
78+
}
79+
}
80+
4981
Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels) {
82+
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
83+
5084
Ref<Image> image = p_image->duplicate();
51-
image->convert(Image::FORMAT_RGBA8);
85+
bool is_hdr = false;
86+
87+
if (image->get_format() <= Image::FORMAT_RGB565) {
88+
image->convert(Image::FORMAT_RGBA8);
89+
} else if (image->get_format() <= Image::FORMAT_RGBE9995) {
90+
image->convert(Image::FORMAT_RGBAF);
91+
is_hdr = true;
92+
}
5293

5394
basisu::basis_compressor_params params;
5495

@@ -74,32 +115,42 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
74115
basisu::job_pool job_pool(OS::get_singleton()->get_processor_count());
75116
params.m_pJob_pool = &job_pool;
76117

77-
BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_RG;
78-
switch (p_channels) {
79-
case Image::USED_CHANNELS_L: {
80-
decompress_format = BASIS_DECOMPRESS_RGB;
81-
} break;
82-
case Image::USED_CHANNELS_LA: {
83-
params.m_force_alpha = true;
84-
decompress_format = BASIS_DECOMPRESS_RGBA;
85-
} break;
86-
case Image::USED_CHANNELS_R: {
87-
decompress_format = BASIS_DECOMPRESS_R;
88-
} break;
89-
case Image::USED_CHANNELS_RG: {
90-
params.m_force_alpha = true;
91-
image->convert_rg_to_ra_rgba8();
92-
decompress_format = BASIS_DECOMPRESS_RG;
93-
} break;
94-
case Image::USED_CHANNELS_RGB: {
95-
decompress_format = BASIS_DECOMPRESS_RGB;
96-
} break;
97-
case Image::USED_CHANNELS_RGBA: {
98-
params.m_force_alpha = true;
99-
decompress_format = BASIS_DECOMPRESS_RGBA;
100-
} break;
118+
BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_MAX;
119+
120+
if (is_hdr) {
121+
decompress_format = BASIS_DECOMPRESS_HDR_RGB;
122+
params.m_hdr = true;
123+
params.m_uastc_hdr_options.set_quality_level(0);
124+
125+
} else {
126+
switch (p_channels) {
127+
case Image::USED_CHANNELS_L: {
128+
decompress_format = BASIS_DECOMPRESS_RGB;
129+
} break;
130+
case Image::USED_CHANNELS_LA: {
131+
params.m_force_alpha = true;
132+
decompress_format = BASIS_DECOMPRESS_RGBA;
133+
} break;
134+
case Image::USED_CHANNELS_R: {
135+
decompress_format = BASIS_DECOMPRESS_R;
136+
} break;
137+
case Image::USED_CHANNELS_RG: {
138+
params.m_force_alpha = true;
139+
image->convert_rg_to_ra_rgba8();
140+
decompress_format = BASIS_DECOMPRESS_RG;
141+
} break;
142+
case Image::USED_CHANNELS_RGB: {
143+
decompress_format = BASIS_DECOMPRESS_RGB;
144+
} break;
145+
case Image::USED_CHANNELS_RGBA: {
146+
params.m_force_alpha = true;
147+
decompress_format = BASIS_DECOMPRESS_RGBA;
148+
} break;
149+
}
101150
}
102151

152+
ERR_FAIL_COND_V(decompress_format == BASIS_DECOMPRESS_MAX, Vector<uint8_t>());
153+
103154
// Copy the source image data with mipmaps into BasisU.
104155
{
105156
const int orig_width = image->get_width();
@@ -113,9 +164,10 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
113164

114165
Vector<uint8_t> image_data = image->get_data();
115166
basisu::vector<basisu::image> basisu_mipmaps;
167+
basisu::vector<basisu::imagef> basisu_mipmaps_hdr;
116168

117169
// Buffer for storing padded mipmap data.
118-
Vector<uint32_t> mip_data_padded;
170+
Vector<uint8_t> mip_data_padded;
119171

120172
for (int32_t i = 0; i <= image->get_mipmap_count(); i++) {
121173
int64_t ofs, size;
@@ -126,31 +178,10 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
126178

127179
// Pad the mipmap's data if its resolution isn't divisible by 4.
128180
if (image->has_mipmaps() && !is_res_div_4 && (width > 2 && height > 2) && (width != next_width || height != next_height)) {
129-
// Source mip's data interpreted as 32-bit RGBA blocks to help with copying pixel data.
130-
const uint32_t *mip_src_data = reinterpret_cast<const uint32_t *>(image_mip_data);
131-
132-
// Reserve space in the padded buffer.
133-
mip_data_padded.resize(next_width * next_height);
134-
uint32_t *data_padded_ptr = mip_data_padded.ptrw();
135-
136-
// Pad mipmap to the nearest block by smearing.
137-
int x = 0, y = 0;
138-
for (y = 0; y < height; y++) {
139-
for (x = 0; x < width; x++) {
140-
data_padded_ptr[next_width * y + x] = mip_src_data[width * y + x];
141-
}
142-
143-
// First, smear in x.
144-
for (; x < next_width; x++) {
145-
data_padded_ptr[next_width * y + x] = data_padded_ptr[next_width * y + x - 1];
146-
}
147-
}
148-
149-
// Then, smear in y.
150-
for (; y < next_height; y++) {
151-
for (x = 0; x < next_width; x++) {
152-
data_padded_ptr[next_width * y + x] = data_padded_ptr[next_width * y + x - next_width];
153-
}
181+
if (is_hdr) {
182+
_basisu_pad_mipmap<BasisRGBAF>(image_mip_data, mip_data_padded, next_width, next_height, width, height, size);
183+
} else {
184+
_basisu_pad_mipmap<uint32_t>(image_mip_data, mip_data_padded, next_width, next_height, width, height, size);
154185
}
155186

156187
// Override the image_mip_data pointer with our temporary Vector.
@@ -159,52 +190,69 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
159190
// Override the mipmap's properties.
160191
width = next_width;
161192
height = next_height;
162-
size = mip_data_padded.size() * 4;
193+
size = mip_data_padded.size();
163194
}
164195

165196
// Get the next mipmap's resolution.
166197
next_width /= 2;
167198
next_height /= 2;
168199

169200
// Copy the source mipmap's data to a BasisU image.
170-
basisu::image basisu_image(width, height);
171-
memcpy(basisu_image.get_ptr(), image_mip_data, size);
201+
if (is_hdr) {
202+
basisu::imagef basisu_image(width, height);
203+
memcpy(reinterpret_cast<uint8_t *>(basisu_image.get_ptr()), image_mip_data, size);
204+
205+
if (i == 0) {
206+
params.m_source_images_hdr.push_back(basisu_image);
207+
} else {
208+
basisu_mipmaps_hdr.push_back(basisu_image);
209+
}
172210

173-
if (i == 0) {
174-
params.m_source_images.push_back(basisu_image);
175211
} else {
176-
basisu_mipmaps.push_back(basisu_image);
212+
basisu::image basisu_image(width, height);
213+
memcpy(basisu_image.get_ptr(), image_mip_data, size);
214+
215+
if (i == 0) {
216+
params.m_source_images.push_back(basisu_image);
217+
} else {
218+
basisu_mipmaps.push_back(basisu_image);
219+
}
177220
}
178221
}
179222

180-
params.m_source_mipmap_images.push_back(basisu_mipmaps);
223+
if (is_hdr) {
224+
params.m_source_mipmap_images_hdr.push_back(basisu_mipmaps_hdr);
225+
} else {
226+
params.m_source_mipmap_images.push_back(basisu_mipmaps);
227+
}
181228
}
182229

183230
// Encode the image data.
184-
Vector<uint8_t> basisu_data;
185-
186231
basisu::basis_compressor compressor;
187232
compressor.init(params);
188233

189234
int basisu_err = compressor.process();
190-
ERR_FAIL_COND_V(basisu_err != basisu::basis_compressor::cECSuccess, basisu_data);
235+
ERR_FAIL_COND_V(basisu_err != basisu::basis_compressor::cECSuccess, Vector<uint8_t>());
191236

192-
const basisu::uint8_vec &basisu_out = compressor.get_output_basis_file();
193-
basisu_data.resize(basisu_out.size() + 4);
237+
const basisu::uint8_vec &basisu_encoded = compressor.get_output_basis_file();
194238

195-
// Copy the encoded data to the buffer.
196-
{
197-
uint8_t *wb = basisu_data.ptrw();
198-
*(uint32_t *)wb = decompress_format;
239+
Vector<uint8_t> basisu_data;
240+
basisu_data.resize(basisu_encoded.size() + 4);
241+
uint8_t *basisu_data_ptr = basisu_data.ptrw();
199242

200-
memcpy(wb + 4, basisu_out.get_ptr(), basisu_out.size());
201-
}
243+
// Copy the encoded BasisU data into the output buffer.
244+
*(uint32_t *)basisu_data_ptr = decompress_format;
245+
memcpy(basisu_data_ptr + 4, basisu_encoded.get_ptr(), basisu_encoded.size());
246+
247+
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));
202248

203249
return basisu_data;
204250
}
205251
#endif // TOOLS_ENABLED
206252

207253
Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
254+
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
255+
208256
Ref<Image> image;
209257
ERR_FAIL_NULL_V_MSG(p_data, image, "Cannot unpack invalid BasisUniversal data.");
210258

@@ -320,6 +368,23 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
320368
}
321369

322370
} break;
371+
case BASIS_DECOMPRESS_HDR_RGB: {
372+
if (bptc_supported) {
373+
basisu_format = basist::transcoder_texture_format::cTFBC6H;
374+
image_format = Image::FORMAT_BPTC_RGBFU;
375+
} else if (astc_supported) {
376+
basisu_format = basist::transcoder_texture_format::cTFASTC_HDR_4x4_RGBA;
377+
image_format = Image::FORMAT_ASTC_4x4_HDR;
378+
} else {
379+
// No supported VRAM compression formats, decompress.
380+
basisu_format = basist::transcoder_texture_format::cTFRGB_9E5;
381+
image_format = Image::FORMAT_RGBE9995;
382+
}
383+
384+
} break;
385+
default: {
386+
ERR_FAIL_V(image);
387+
} break;
323388
}
324389

325390
src_ptr += 4;
@@ -371,6 +436,9 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
371436
}
372437
}
373438

439+
print_verbose(vformat("BasisU: Transcoding a %dx%d image with %d mipmaps into %s took %d ms.",
440+
image->get_width(), image->get_height(), image->get_mipmap_count(), Image::get_format_name(image_format), OS::get_singleton()->get_ticks_msec() - start_time));
441+
374442
return image;
375443
}
376444

modules/basis_universal/image_compress_basisu.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,20 @@ enum BasisDecompressFormat {
3939
BASIS_DECOMPRESS_RGBA,
4040
BASIS_DECOMPRESS_RG_AS_RA,
4141
BASIS_DECOMPRESS_R,
42+
BASIS_DECOMPRESS_HDR_RGB,
43+
BASIS_DECOMPRESS_MAX
4244
};
4345

4446
void basis_universal_init();
4547

4648
#ifdef TOOLS_ENABLED
49+
struct BasisRGBAF {
50+
uint32_t r;
51+
uint32_t g;
52+
uint32_t b;
53+
uint32_t a;
54+
};
55+
4756
Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedChannels p_channels);
4857
#endif
4958

thirdparty/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,13 @@ Files extracted from upstream source:
5959
## basis_universal
6060

6161
- Upstream: https://github.com/BinomialLLC/basis_universal
62-
- Version: 1.16.4 (900e40fb5d2502927360fe2f31762bdbb624455f, 2023)
62+
- Version: 1.50.0 (051ad6d8a64bb95a79e8601c317055fd1782ad3e, 2024)
6363
- License: Apache 2.0
6464

6565
Files extracted from upstream source:
6666

67-
- `encoder/` and `transcoder/` folders, minus `jpgd.{cpp,h}`
67+
- `encoder/` and `transcoder/` folders, with the following files removed from `encoder`:
68+
`jpgd.{cpp,h}`, `3rdparty/{qoi.h,tinydds.h,tinyexr.cpp,tinyexr.h}`
6869
- `LICENSE`
6970

7071
Applied upstream PR https://github.com/BinomialLLC/basis_universal/pull/344 to

0 commit comments

Comments
 (0)