Skip to content

Commit db14efc

Browse files
committed
compress the DFG LUT with zstd
- use resgen to package the DFG LUT into filament, instead of using a large include of a C array. - add a zstd compression step, which reduces the size from ~100KB to ~75KB. The overhead of zstd is about 90KB uncompressed, however it will pay for itself tenfold when we use it to compress material packages.
1 parent d1ca059 commit db14efc

File tree

14 files changed

+208
-29
lines changed

14 files changed

+208
-29
lines changed

android/filament-android/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ add_library(abseil STATIC IMPORTED)
6767
set_target_properties(abseil PROPERTIES IMPORTED_LOCATION
6868
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libabseil.a)
6969

70+
add_library(zstd STATIC IMPORTED)
71+
set_target_properties(zstd PROPERTIES IMPORTED_LOCATION
72+
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libzstd.a)
73+
7074
if (FILAMENT_ENABLE_FGVIEWER)
7175
add_library(fgviewer STATIC IMPORTED)
7276
set_target_properties(fgviewer PROPERTIES IMPORTED_LOCATION
@@ -142,6 +146,7 @@ target_link_libraries(filament-jni
142146
PRIVATE utils
143147
PRIVATE perfetto
144148
PRIVATE abseil
149+
PRIVATE zstd
145150

146151
# libgeometry is PUBLIC because gltfio uses it.
147152
PUBLIC geometry

filament/CMakeLists.txt

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ set(SRCS
152152
src/fg/PassNode.cpp
153153
src/fg/ResourceNode.cpp
154154
src/fsr.cpp
155+
src/ZstdHelper.cpp
155156
)
156157

157158
set(PRIVATE_HDRS
@@ -235,6 +236,7 @@ set(PRIVATE_HDRS
235236
src/materials/fsr/ffx_a.h
236237
src/materials/fsr/ffx_fsr1.h
237238
src/materials/fsr/ffx_fsr1_mobile.fs
239+
src/ZstdHelper.h
238240
)
239241

240242
set(MATERIAL_SRCS
@@ -605,6 +607,28 @@ add_custom_command(
605607
APPEND
606608
)
607609

610+
file(MAKE_DIRECTORY "${GENERATION_ROOT}/generated/data/")
611+
612+
set(output_path_dfg_bin "${GENERATION_ROOT}/generated/data/dfg.bin")
613+
add_custom_command(
614+
OUTPUT ${output_path_dfg_bin}
615+
COMMAND cmgen --quiet --size=${DFG_LUT_SIZE} --ibl-dfg-multiscatter --ibl-dfg-cloth --ibl-dfg=${output_path_dfg_bin}
616+
DEPENDS cmgen
617+
COMMENT "Generating DFG LUT to ${output_path_dfg_bin}"
618+
)
619+
620+
set(dfg_package_name "dfg")
621+
get_resgen_vars(${RESOURCE_DIR} ${dfg_package_name})
622+
add_custom_command(
623+
OUTPUT ${RESGEN_OUTPUTS}
624+
COMMAND resgen ${RESGEN_FLAGS} -Z ${output_path_dfg_bin}
625+
DEPENDS resgen
626+
DEPENDS ${output_path_dfg_bin}
627+
COMMENT "Generating DFG resource package for ${dfg_package_name}"
628+
)
629+
list(APPEND GENERATED_RESOURCE_HDRS ${RESGEN_HEADER})
630+
list(APPEND GENERATED_RESOURCE_SRCS ${RESGEN_SOURCE})
631+
608632
# Add the generated resource files to the source and header lists.
609633
list(APPEND PRIVATE_HDRS ${GENERATED_RESOURCE_HDRS})
610634
list(APPEND SRCS ${GENERATED_RESOURCE_SRCS})
@@ -613,16 +637,6 @@ if (DEFINED RESGEN_SOURCE_FLAGS)
613637
set_source_files_properties(${GENERATED_RESOURCE_SRCS} PROPERTIES COMPILE_FLAGS ${RESGEN_SOURCE_FLAGS})
614638
endif()
615639

616-
file(MAKE_DIRECTORY "${GENERATION_ROOT}/generated/data/")
617-
618-
set(output_path "${GENERATION_ROOT}/generated/data/dfg.inc")
619-
add_custom_command(
620-
OUTPUT ${output_path}
621-
COMMAND cmgen --quiet --size=${DFG_LUT_SIZE} --ibl-dfg-multiscatter --ibl-dfg-cloth --ibl-dfg=${output_path}
622-
DEPENDS cmgen
623-
COMMENT "Generating DFG LUT ${output_path}"
624-
)
625-
list(APPEND DATA_BINS ${output_path})
626640

627641
# ==================================================================================================
628642
# Includes & target definition
@@ -633,7 +647,7 @@ include_directories(${GENERATION_ROOT})
633647
include_directories(src)
634648

635649
# we're building a library
636-
add_library(${TARGET} STATIC ${PRIVATE_HDRS} ${PUBLIC_HDRS} ${SRCS} ${DATA_BINS})
650+
add_library(${TARGET} STATIC ${PRIVATE_HDRS} ${PUBLIC_HDRS} ${SRCS})
637651

638652
# specify where the public headers of this library are
639653
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
@@ -650,6 +664,7 @@ target_link_libraries(${TARGET} PUBLIC math)
650664
target_link_libraries(${TARGET} PUBLIC utils)
651665
target_link_libraries(${TARGET} PUBLIC filaflat)
652666
target_link_libraries(${TARGET} PUBLIC filabridge)
667+
target_link_libraries(${TARGET} PRIVATE zstd)
653668

654669
if (FILAMENT_USE_ABSEIL_LOGGING)
655670
target_link_libraries(${TARGET} PUBLIC absl::log)

filament/src/DFG.cpp

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,66 @@
1616

1717
#include "DFG.h"
1818

19+
#include "ZstdHelper.h"
20+
1921
#include "details/Engine.h"
2022
#include "details/Texture.h"
2123

22-
namespace filament {
24+
#include <filament/Texture.h>
25+
26+
#include <backend/DriverEnums.h>
27+
28+
#include <utils/debug.h>
29+
#include <utils/compiler.h>
30+
#include <utils/Panic.h>
2331

24-
const uint16_t DFG::DFG_LUT[] = {
25-
#include "generated/data/dfg.inc"
26-
};
32+
#include <cstdlib>
33+
#include <cstdint>
34+
#include <cstddef>
35+
#include <utility>
36+
37+
#include "generated/resources/dfg.h"
38+
39+
namespace filament {
2740

28-
void DFG::init(FEngine& engine) noexcept {
41+
void DFG::init(FEngine& engine) {
2942
constexpr size_t fp16Count = DFG_LUT_SIZE * DFG_LUT_SIZE * 3;
3043
constexpr size_t byteCount = fp16Count * sizeof(uint16_t);
3144

32-
static_assert(sizeof(DFG_LUT) == byteCount, "DFG_LUT_SIZE doesn't match size of the DFG LUT");
33-
34-
Texture* lut = Texture::Builder()
45+
Texture::Builder builder = Texture::Builder()
3546
.width(DFG_LUT_SIZE)
3647
.height(DFG_LUT_SIZE)
37-
.format(backend::TextureFormat::RGB16F)
38-
.build(engine);
48+
.format(backend::TextureFormat::RGB16F);
49+
50+
if (ZstdHelper::isCompressed(DFG_PACKAGE, DFG_DFG_SIZE)) {
51+
const size_t decodedSize = ZstdHelper::getDecodedSize(DFG_PACKAGE, byteCount);
52+
assert_invariant(decodedSize == byteCount);
53+
54+
// If the LUT is Zstd compressed, decompress it.
55+
void* decodedData = malloc(decodedSize);
3956

40-
Texture::PixelBufferDescriptor buffer(DFG_LUT, byteCount,
41-
Texture::Format::RGB, Texture::Type::HALF);
57+
FILAMENT_CHECK_POSTCONDITION(decodedData)
58+
<< "Couldn't allocate " << decodedSize << " bytes for DFG LUT decompression.";
4259

43-
lut->setImage(engine, 0, std::move(buffer));
60+
if (UTILS_LIKELY(decodedData)) {
61+
bool const success = ZstdHelper::decompress(
62+
decodedData, decodedSize, DFG_PACKAGE, byteCount);
63+
64+
FILAMENT_CHECK_POSTCONDITION(success) << "Couldn't decompress DFG LUT.";
65+
66+
// Use decodedData for the texture
67+
Texture* lut = builder.build(engine);
68+
lut->setImage(engine, 0, { decodedData, decodedSize,
69+
Texture::Format::RGB, Texture::Type::HALF,
70+
+[](void* buffer, size_t, void*) { free(buffer); } });
71+
mLUT = downcast(lut);
72+
return;
73+
}
74+
}
4475

76+
assert_invariant(DFG_DFG_SIZE == byteCount);
77+
Texture* lut = builder.build(engine);
78+
lut->setImage(engine, 0, { DFG_PACKAGE, byteCount, Texture::Format::RGB, Texture::Type::HALF });
4579
mLUT = downcast(lut);
4680
}
4781

filament/src/DFG.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323

2424
#include <utils/compiler.h>
2525

26+
#include <cstdint>
27+
#include <cstddef>
28+
2629
namespace filament {
2730

2831
class FEngine;
@@ -40,7 +43,7 @@ class DFG {
4043
DFG& operator=(DFG const& rhs) = delete;
4144
DFG& operator=(DFG&& rhs) = delete;
4245

43-
void init(FEngine& engine) noexcept;
46+
void init(FEngine& engine);
4447

4548
size_t getLutSize() const noexcept {
4649
return DFG_LUT_SIZE;
@@ -61,9 +64,6 @@ class DFG {
6164

6265
// make sure to use the right size here
6366
static constexpr size_t DFG_LUT_SIZE = FILAMENT_DFG_LUT_SIZE;
64-
65-
// this lookup table is generated with cmgen
66-
static const uint16_t DFG_LUT[];
6767
};
6868

6969
#undef FILAMENT_DFG_LUT_SIZE

filament/src/ZstdHelper.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (C) 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "ZstdHelper.h"
18+
19+
#include <zstd.h>
20+
21+
#include <cstddef>
22+
#include <cstdint>
23+
24+
namespace filament {
25+
26+
bool ZstdHelper::isCompressed(const void* src, size_t src_size) noexcept {
27+
if (src_size < 4) {
28+
return false;
29+
}
30+
const uint32_t magic = *static_cast<const uint32_t*>(src);
31+
return magic == ZSTD_MAGICNUMBER;
32+
}
33+
34+
size_t ZstdHelper::getDecodedSize(const void* src, size_t src_size) noexcept {
35+
return ZSTD_getFrameContentSize(src, src_size);
36+
}
37+
38+
size_t ZstdHelper::decompress(void* dst, size_t dst_size, const void* src, size_t src_size) noexcept {
39+
return ZSTD_decompress(dst, dst_size, src, src_size);
40+
}
41+
42+
} // namespace filament

filament/src/ZstdHelper.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright (C) 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef TNT_FILAMENT_ZSTD_HELPER_H
18+
#define TNT_FILAMENT_ZSTD_HELPER_H
19+
20+
#include <stddef.h>
21+
22+
namespace filament {
23+
24+
class ZstdHelper {
25+
public:
26+
/**
27+
* Checks if the given binary blob is Zstd compressed.
28+
* @param src Pointer to the source data.
29+
* @param src_size Size of the source data.
30+
* @return True if the data is Zstd compressed, false otherwise.
31+
*/
32+
static bool isCompressed(const void* src, size_t src_size) noexcept;
33+
34+
/**
35+
* Returns the decompressed size of a Zstd compressed blob.
36+
* @param src Pointer to the source data.
37+
* @param src_size Size of the source data.
38+
* @return The decompressed size, or 0 if an error occurs.
39+
*/
40+
static size_t getDecodedSize(const void* src, size_t src_size) noexcept;
41+
42+
/**
43+
* Decompresses a Zstd compressed blob into a pre-allocated buffer.
44+
* @param dst Pointer to the destination buffer.
45+
* @param dst_size Size of the destination buffer.
46+
* @param src Pointer to the source data.
47+
* @param src_size Size of the source data.
48+
* @return The number of bytes decompressed, or 0 if an error occurs.
49+
*/
50+
static size_t decompress(void* dst, size_t dst_size, const void* src, size_t src_size) noexcept;
51+
};
52+
53+
} // namespace filament
54+
55+
#endif // TNT_FILAMENT_ZSTD_HELPER_H

ios/samples/app-template.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ targetTemplates:
2121
base:
2222
OTHER_LDFLAGS: ["-lfilament", "-lbackend", "-lfilaflat", "-lktxreader",
2323
"-lfilabridge", "-lutils", "-lsmol-v", "-lgeometry", "-libl",
24-
"-labseil"]
24+
"-labseil", "-lzstd"]
2525
ENABLE_BITCODE: NO
2626
CLANG_CXX_LANGUAGE_STANDARD: gnu++17
2727
# This allows users to not have to specify a unique bundle ID when building the sample apps.

ios/samples/backend-test/backend-test.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@
255255
"-lgeometry",
256256
"-libl",
257257
"-labseil",
258+
"-lzstd",
258259
"-lfilamat",
259260
"-force_load ../../../out/ios-debug/filament/lib/arm64/libbackend_test.a",
260261
);
@@ -363,6 +364,7 @@
363364
"-lgeometry",
364365
"-libl",
365366
"-labseil",
367+
"-lzstd",
366368
"-lfilamat",
367369
"-force_load ../../../out/ios-debug/filament/lib/arm64/libbackend_test.a",
368370
);
@@ -464,6 +466,7 @@
464466
"-lgeometry",
465467
"-libl",
466468
"-labseil",
469+
"-lzstd",
467470
"-lfilamat",
468471
"-force_load ../../../out/ios-debug/filament/lib/arm64/libbackend_test.a",
469472
);
@@ -510,6 +513,7 @@
510513
"-lgeometry",
511514
"-libl",
512515
"-labseil",
516+
"-lzstd",
513517
"-lfilamat",
514518
"-force_load ../../../out/ios-debug/filament/lib/arm64/libbackend_test.a",
515519
);

ios/samples/gltf-viewer/gltf-viewer.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@
426426
"-lgeometry",
427427
"-libl",
428428
"-labseil",
429+
"-lzstd",
429430
"-lgltfio_core",
430431
"-luberarchive",
431432
"-limage",
@@ -482,6 +483,7 @@
482483
"-lgeometry",
483484
"-libl",
484485
"-labseil",
486+
"-lzstd",
485487
"-lgltfio_core",
486488
"-luberarchive",
487489
"-limage",
@@ -600,6 +602,7 @@
600602
"-lgeometry",
601603
"-libl",
602604
"-labseil",
605+
"-lzstd",
603606
"-lgltfio_core",
604607
"-luberarchive",
605608
"-limage",
@@ -656,6 +659,7 @@
656659
"-lgeometry",
657660
"-libl",
658661
"-labseil",
662+
"-lzstd",
659663
"-lgltfio_core",
660664
"-luberarchive",
661665
"-limage",

0 commit comments

Comments
 (0)