PointCache: improve performance and compression, always compress
Point Caches (used by particle system, cloth, boids etc.) are now always compressed, uzing zstd coupled with lossless data filtering. - This is both smaller cache files _and_ faster than the old "Heavy" compression mode, - And smaller data files and same or slightly faster speed than using no compression at all, - There was not much difference between compression levels once data filtering got added, so option to pick them was removed. - So there's no downside to just always using the compression, which makes for a simpler UI. - RNA change: removes PointCache.compression property. More details and cache size / performance numbers in the PR. Pull Request: https://projects.blender.org/blender/blender/pulls/144356
This commit is contained in:
committed by
Aras Pranckevicius
parent
fbdc501c50
commit
3d9155eb0a
@@ -2649,7 +2649,6 @@ url_manual_mapping = (
|
||||
("bpy.types.object.visible_diffuse*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-diffuse"),
|
||||
("bpy.types.objectsolverconstraint*", "animation/constraints/motion_tracking/object_solver.html#bpy-types-objectsolverconstraint"),
|
||||
("bpy.types.particlesystemmodifier*", "physics/particles/index.html#bpy-types-particlesystemmodifier"),
|
||||
("bpy.types.pointcache.compression*", "physics/baking.html#bpy-types-pointcache-compression"),
|
||||
("bpy.types.rendersettings.threads*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-threads"),
|
||||
("bpy.types.scenedisplay.render_aa*", "render/workbench/sampling.html#bpy-types-scenedisplay-render-aa"),
|
||||
("bpy.types.sceneeevee.motion_blur*", "render/eevee/render_settings/motion_blur.html#bpy-types-sceneeevee-motion-blur"),
|
||||
|
||||
@@ -188,10 +188,6 @@ def point_cache_ui(self, cache, enabled, cachetype):
|
||||
subcol.active = cache.use_disk_cache
|
||||
subcol.prop(cache, "use_library_path", text="Use Library Path")
|
||||
|
||||
col = flow.column()
|
||||
col.active = cache.use_disk_cache
|
||||
col.prop(cache, "compression", text="Compression")
|
||||
|
||||
if cache.id_data.library and not cache.use_disk_cache:
|
||||
can_bake = False
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
@@ -35,6 +36,7 @@
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "BLI_compression.hh"
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_rotation.h"
|
||||
@@ -83,8 +85,6 @@
|
||||
lzo_align_t __LZO_MMODEL var[((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t)]
|
||||
#endif
|
||||
|
||||
#define LZO_OUT_LEN(size) ((size) + (size) / 16 + 64 + 3)
|
||||
|
||||
#if 0 // #ifdef WITH_LZMA
|
||||
# include "LzmaLib.h"
|
||||
#endif
|
||||
@@ -128,12 +128,14 @@ static int ptcache_extra_datasize[] = {
|
||||
};
|
||||
|
||||
/* forward declarations */
|
||||
static int ptcache_file_compressed_read(PTCacheFile *pf, uchar *result, uint len);
|
||||
static int ptcache_file_compressed_read(PTCacheFile *pf,
|
||||
uchar *result,
|
||||
uint items_num,
|
||||
uint item_size);
|
||||
static void ptcache_file_compressed_write(PTCacheFile *pf,
|
||||
const void *data,
|
||||
uint items_num,
|
||||
uint item_size,
|
||||
PointCacheCompression compression);
|
||||
uint item_size);
|
||||
static int ptcache_file_write(PTCacheFile *pf, const void *data, uint items_num, uint item_size);
|
||||
static bool ptcache_file_read(PTCacheFile *pf, void *f, uint items_num, uint item_size);
|
||||
|
||||
@@ -709,8 +711,7 @@ static int ptcache_dynamicpaint_write(PTCacheFile *pf, void *dp_v)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ptcache_file_compressed_write(
|
||||
pf, surface->data->type_data, total_points, in_stride, PTCACHE_COMPRESS_ZSTD_FAST);
|
||||
ptcache_file_compressed_write(pf, surface->data->type_data, total_points, in_stride);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -751,9 +752,8 @@ static int ptcache_dynamicpaint_read(PTCacheFile *pf, void *dp_v)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ret = ptcache_file_compressed_read(pf,
|
||||
static_cast<uchar *>(surface->data->type_data),
|
||||
data_len * surface->data->total_points);
|
||||
int ret = ptcache_file_compressed_read(
|
||||
pf, static_cast<uchar *>(surface->data->type_data), surface->data->total_points, data_len);
|
||||
if (ret) {
|
||||
CLOG_ERROR(&LOG, "Dynamic Paint: Unable to read the compressed cache data");
|
||||
return 0;
|
||||
@@ -1508,11 +1508,13 @@ static void ptcache_file_close(PTCacheFile *pf)
|
||||
}
|
||||
}
|
||||
|
||||
static int ptcache_file_compressed_read(PTCacheFile *pf, uchar *result, uint len)
|
||||
static int ptcache_file_compressed_read(PTCacheFile *pf,
|
||||
uchar *result,
|
||||
uint items_num,
|
||||
uint item_size)
|
||||
{
|
||||
int r = 0;
|
||||
size_t in_len;
|
||||
uchar *in;
|
||||
|
||||
uchar compressed_val = 0;
|
||||
ptcache_file_read(pf, &compressed_val, 1, sizeof(uchar));
|
||||
@@ -1525,26 +1527,36 @@ static int ptcache_file_compressed_read(PTCacheFile *pf, uchar *result, uint len
|
||||
/* do nothing */
|
||||
}
|
||||
else {
|
||||
in = MEM_calloc_arrayN<uchar>(in_len, "pointcache_compressed_buffer");
|
||||
uchar *in = MEM_calloc_arrayN<uchar>(in_len, "pointcache_compressed_buffer");
|
||||
ptcache_file_read(pf, in, in_len, sizeof(uchar));
|
||||
|
||||
uchar *decomp_result = result;
|
||||
if (compressed == PTCACHE_COMPRESS_ZSTD_FILTERED) {
|
||||
decomp_result = MEM_malloc_arrayN<uchar>(items_num * item_size,
|
||||
"pointcache_unfilter_buffer");
|
||||
}
|
||||
#if 0 // #ifdef WITH_LZO
|
||||
if (compressed == PTCACHE_COMPRESS_LZO_DEPRECATED) {
|
||||
size_t out_len = len;
|
||||
r = lzo1x_decompress_safe(in, (lzo_uint)in_len, result, (lzo_uint *)&out_len, nullptr);
|
||||
size_t out_len = items_num * item_size;
|
||||
r = lzo1x_decompress_safe(in, (lzo_uint)in_len, decomp_result, (lzo_uint *)&out_len, nullptr);
|
||||
}
|
||||
#endif
|
||||
#if 0 // #ifdef WITH_LZMA
|
||||
if (compressed == PTCACHE_COMPRESS_LZMA_DEPRECATED) {
|
||||
size_t leni = in_len, leno = len;
|
||||
size_t leni = in_len, leno = items_num * item_size;
|
||||
uchar lzma_props[16] = {};
|
||||
uint lzma_props_size = 0;
|
||||
ptcache_file_read(pf, &lzma_props_size, 1, sizeof(uint));
|
||||
ptcache_file_read(pf, lzma_props, lzma_props_size, sizeof(uchar));
|
||||
r = LzmaUncompress(result, &leno, in, &leni, lzma_props, lzma_props_size);
|
||||
r = LzmaUncompress(decomp_result, &leno, in, &leni, lzma_props, lzma_props_size);
|
||||
}
|
||||
#endif
|
||||
if (ELEM(compressed, PTCACHE_COMPRESS_ZSTD_FAST, PTCACHE_COMPRESS_ZSTD_SLOW)) {
|
||||
const size_t err = ZSTD_decompress(result, len, in, in_len);
|
||||
if (ELEM(compressed,
|
||||
PTCACHE_COMPRESS_ZSTD_FILTERED,
|
||||
PTCACHE_COMPRESS_ZSTD_FAST_DEPRECATED,
|
||||
PTCACHE_COMPRESS_ZSTD_SLOW_DEPRECATED))
|
||||
{
|
||||
const size_t err = ZSTD_decompress(decomp_result, items_num * item_size, in, in_len);
|
||||
r = ZSTD_isError(err);
|
||||
}
|
||||
else {
|
||||
@@ -1552,102 +1564,47 @@ static int ptcache_file_compressed_read(PTCacheFile *pf, uchar *result, uint len
|
||||
r = 1;
|
||||
}
|
||||
MEM_freeN(in);
|
||||
|
||||
/* Un-filter the decompressed data, if needed. */
|
||||
if (compressed == PTCACHE_COMPRESS_ZSTD_FILTERED) {
|
||||
blender::unfilter_transpose_delta(decomp_result, result, items_num, item_size);
|
||||
MEM_freeN(decomp_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ptcache_file_read(pf, result, len, sizeof(uchar));
|
||||
ptcache_file_read(pf, result, items_num * item_size, sizeof(uchar));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void ptcache_file_compressed_write(PTCacheFile *pf,
|
||||
const void *data,
|
||||
uint items_num,
|
||||
uint item_size,
|
||||
PointCacheCompression compression)
|
||||
uint item_size)
|
||||
{
|
||||
/* Allocate space for compressed data. */
|
||||
const PointCacheCompression compression = PTCACHE_COMPRESS_ZSTD_FILTERED;
|
||||
const uint data_size = items_num * item_size;
|
||||
size_t out_len = 0;
|
||||
if (ELEM(compression, PTCACHE_COMPRESS_ZSTD_FAST, PTCACHE_COMPRESS_ZSTD_SLOW)) {
|
||||
out_len = ZSTD_compressBound(data_size);
|
||||
if (ZSTD_isError(out_len)) {
|
||||
compression = PTCACHE_COMPRESS_NO;
|
||||
}
|
||||
}
|
||||
else if (compression != PTCACHE_COMPRESS_NO) {
|
||||
out_len = LZO_OUT_LEN(data_size);
|
||||
}
|
||||
|
||||
size_t out_len = ZSTD_compressBound(data_size);
|
||||
blender::Array<uchar> out(out_len);
|
||||
|
||||
/* Do actual compression. */
|
||||
int r = 0;
|
||||
uchar lzma_props[16] = {};
|
||||
size_t lzma_props_size = 5;
|
||||
/* Filter the data: transpose by bytes; delta-encode. */
|
||||
blender::Array<uchar> filtered(data_size);
|
||||
blender::filter_transpose_delta((const uint8_t *)data, filtered.data(), items_num, item_size);
|
||||
|
||||
#if 0 // #ifdef WITH_LZO
|
||||
if (compression == PTCACHE_COMPRESS_LZO_DEPRECATED) {
|
||||
LZO_HEAP_ALLOC(wrkmem, LZO1X_MEM_COMPRESS);
|
||||
|
||||
r = lzo1x_1_compress(
|
||||
(const uchar *)data, (lzo_uint)data_size, out.data(), (lzo_uint *)&out_len, wrkmem);
|
||||
if (!(r == LZO_E_OK) || (out_len >= data_size)) {
|
||||
compression = PTCACHE_COMPRESS_NO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if 0 // #ifdef WITH_LZMA
|
||||
if (compression == PTCACHE_COMPRESS_LZMA_DEPRECATED) {
|
||||
r = LzmaCompress(out.data(),
|
||||
&out_len,
|
||||
(const uchar *)data,
|
||||
data_size, /* Assume `sizeof(char) == 1`. */
|
||||
lzma_props,
|
||||
&lzma_props_size,
|
||||
5,
|
||||
1 << 24,
|
||||
3,
|
||||
0,
|
||||
2,
|
||||
32,
|
||||
2);
|
||||
|
||||
if (!(r == SZ_OK) || (out_len >= data_size)) {
|
||||
compression = PTCACHE_COMPRESS_NO;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (ELEM(compression, PTCACHE_COMPRESS_ZSTD_FAST, PTCACHE_COMPRESS_ZSTD_SLOW)) {
|
||||
/* Zstd level zero is "default" (currently 3). */
|
||||
const int zstd_level = compression == PTCACHE_COMPRESS_ZSTD_SLOW ? 22 : 0;
|
||||
r = ZSTD_compress(out.data(), out_len, data, data_size, zstd_level);
|
||||
if (ZSTD_isError(r)) {
|
||||
compression = PTCACHE_COMPRESS_NO;
|
||||
}
|
||||
else {
|
||||
out_len = r;
|
||||
r = 0;
|
||||
}
|
||||
}
|
||||
/* Do compression: always zstd level 3. */
|
||||
const int zstd_level = 3;
|
||||
size_t res = ZSTD_compress(out.data(), out_len, filtered.data(), data_size, zstd_level);
|
||||
out_len = res;
|
||||
|
||||
/* Write to file. */
|
||||
const uchar compression_val = compression;
|
||||
ptcache_file_write(pf, &compression_val, 1, sizeof(uchar));
|
||||
if (compression != PTCACHE_COMPRESS_NO) {
|
||||
uint size = out_len;
|
||||
ptcache_file_write(pf, &size, 1, sizeof(uint));
|
||||
ptcache_file_write(pf, out.data(), out_len, sizeof(uchar));
|
||||
}
|
||||
else {
|
||||
ptcache_file_write(pf, data, data_size, sizeof(uchar));
|
||||
}
|
||||
|
||||
if (compression == PTCACHE_COMPRESS_LZMA_DEPRECATED) {
|
||||
uint size = lzma_props_size;
|
||||
ptcache_file_write(pf, &lzma_props_size, 1, sizeof(uint));
|
||||
ptcache_file_write(pf, lzma_props, size, sizeof(uchar));
|
||||
}
|
||||
uint size = out_len;
|
||||
ptcache_file_write(pf, &size, 1, sizeof(uint));
|
||||
ptcache_file_write(pf, out.data(), out_len, sizeof(uchar));
|
||||
}
|
||||
|
||||
static bool ptcache_file_read(PTCacheFile *pf, void *f, uint items_num, uint item_size)
|
||||
@@ -1671,20 +1628,6 @@ static bool ptcache_file_data_read(PTCacheFile *pf)
|
||||
|
||||
return true;
|
||||
}
|
||||
static int ptcache_file_data_write(PTCacheFile *pf)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BPHYS_TOT_DATA; i++) {
|
||||
if ((pf->data_types & (1 << i)) &&
|
||||
!ptcache_file_write(pf, pf->cur[i], 1, ptcache_data_size[i]))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
static int ptcache_file_header_begin_read(PTCacheFile *pf)
|
||||
{
|
||||
uint typeflag = 0;
|
||||
@@ -1990,9 +1933,9 @@ static PTCacheMem *ptcache_disk_frame_to_mem(PTCacheID *pid, int cfra)
|
||||
|
||||
if (pf->flag & PTCACHE_TYPEFLAG_COMPRESS) {
|
||||
for (i = 0; !error && i < BPHYS_TOT_DATA; i++) {
|
||||
uint out_len = pm->totpoint * ptcache_data_size[i];
|
||||
if (pf->data_types & (1 << i)) {
|
||||
error = ptcache_file_compressed_read(pf, static_cast<uchar *>(pm->data[i]), out_len);
|
||||
error = ptcache_file_compressed_read(
|
||||
pf, static_cast<uchar *>(pm->data[i]), pm->totpoint, ptcache_data_size[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2028,7 +1971,8 @@ static PTCacheMem *ptcache_disk_frame_to_mem(PTCacheID *pid, int cfra)
|
||||
if (pf->flag & PTCACHE_TYPEFLAG_COMPRESS) {
|
||||
error = ptcache_file_compressed_read(pf,
|
||||
static_cast<uchar *>(extra->data),
|
||||
extra->totdata * ptcache_extra_datasize[extra->type]);
|
||||
extra->totdata,
|
||||
ptcache_extra_datasize[extra->type]);
|
||||
}
|
||||
else {
|
||||
ptcache_file_read(pf, extra->data, extra->totdata, ptcache_extra_datasize[extra->type]);
|
||||
@@ -2077,36 +2021,16 @@ static int ptcache_mem_frame_to_disk(PTCacheID *pid, PTCacheMem *pm)
|
||||
pf->flag |= PTCACHE_TYPEFLAG_EXTRADATA;
|
||||
}
|
||||
|
||||
const PointCacheCompression compression = PointCacheCompression(pid->cache->compression);
|
||||
if (compression != PTCACHE_COMPRESS_NO) {
|
||||
pf->flag |= PTCACHE_TYPEFLAG_COMPRESS;
|
||||
}
|
||||
pf->flag |= PTCACHE_TYPEFLAG_COMPRESS;
|
||||
|
||||
if (!ptcache_file_header_begin_write(pf) || !pid->write_header(pf)) {
|
||||
error = 1;
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
if (compression != PTCACHE_COMPRESS_NO) {
|
||||
for (i = 0; i < BPHYS_TOT_DATA; i++) {
|
||||
if (pm->data[i]) {
|
||||
ptcache_file_compressed_write(
|
||||
pf, pm->data[i], pm->totpoint, ptcache_data_size[i], compression);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
void *cur[BPHYS_TOT_DATA];
|
||||
BKE_ptcache_mem_pointers_init(pm, cur);
|
||||
ptcache_file_pointers_init(pf);
|
||||
|
||||
for (i = 0; i < pm->totpoint; i++) {
|
||||
ptcache_data_copy(cur, pf->cur);
|
||||
if (!ptcache_file_data_write(pf)) {
|
||||
error = 1;
|
||||
break;
|
||||
}
|
||||
BKE_ptcache_mem_pointers_incr(cur);
|
||||
for (i = 0; i < BPHYS_TOT_DATA; i++) {
|
||||
if (pm->data[i]) {
|
||||
ptcache_file_compressed_write(pf, pm->data[i], pm->totpoint, ptcache_data_size[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2122,13 +2046,8 @@ static int ptcache_mem_frame_to_disk(PTCacheID *pid, PTCacheMem *pm)
|
||||
ptcache_file_write(pf, &extra->type, 1, sizeof(uint));
|
||||
ptcache_file_write(pf, &extra->totdata, 1, sizeof(uint));
|
||||
|
||||
if (compression != PTCACHE_COMPRESS_NO) {
|
||||
ptcache_file_compressed_write(
|
||||
pf, extra->data, extra->totdata, ptcache_extra_datasize[extra->type], compression);
|
||||
}
|
||||
else {
|
||||
ptcache_file_write(pf, extra->data, extra->totdata, ptcache_extra_datasize[extra->type]);
|
||||
}
|
||||
ptcache_file_compressed_write(
|
||||
pf, extra->data, extra->totdata, ptcache_extra_datasize[extra->type]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
43
source/blender/blenlib/BLI_compression.hh
Normal file
43
source/blender/blenlib/BLI_compression.hh
Normal file
@@ -0,0 +1,43 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* Utilities for lossless data compression.
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Transforms array of data, making it more compressible,
|
||||
* especially if data is smoothly varying. Typically you do
|
||||
* this before compression with a general purpose compressor.
|
||||
*
|
||||
* Transposes input array so that output data is first byte of
|
||||
* all items, then 2nd byte of all items, etc. And successive
|
||||
* items within each "byte stream" are stored as difference
|
||||
* from previous byte.
|
||||
*
|
||||
* See https://aras-p.info/blog/2023/03/01/Float-Compression-7-More-Filtering-Optimization/
|
||||
* for details.
|
||||
*/
|
||||
void filter_transpose_delta(const uint8_t *src, uint8_t *dst, size_t items_num, size_t item_size);
|
||||
|
||||
/**
|
||||
* Reverses the data transform done by #unfilter_transpose_delta.
|
||||
* Typically you do this after decompression with a general purpose
|
||||
* compressor.
|
||||
*/
|
||||
void unfilter_transpose_delta(const uint8_t *src,
|
||||
uint8_t *dst,
|
||||
size_t items_num,
|
||||
size_t item_size);
|
||||
|
||||
} // namespace blender
|
||||
@@ -53,6 +53,7 @@ set(SRC
|
||||
intern/bitmap_draw_2d.cc
|
||||
intern/boxpack_2d.cc
|
||||
intern/cache_mutex.cc
|
||||
intern/compression.cc
|
||||
intern/compute_context.cc
|
||||
intern/convexhull_2d.cc
|
||||
intern/cpp_type.cc
|
||||
@@ -206,6 +207,7 @@ set(SRC
|
||||
BLI_compiler_attrs.h
|
||||
BLI_compiler_compat.h
|
||||
BLI_compiler_typecheck.h
|
||||
BLI_compression.hh
|
||||
BLI_compute_context.hh
|
||||
BLI_concurrent_map.hh
|
||||
BLI_console.h
|
||||
@@ -528,6 +530,7 @@ if(WITH_GTESTS)
|
||||
tests/BLI_bounds_test.cc
|
||||
tests/BLI_build_config_test.cc
|
||||
tests/BLI_color_test.cc
|
||||
tests/BLI_compression_test.cc
|
||||
tests/BLI_convexhull_2d_test.cc
|
||||
tests/BLI_cpp_type_test.cc
|
||||
tests/BLI_csv_parse_test.cc
|
||||
|
||||
44
source/blender/blenlib/intern/compression.cc
Normal file
44
source/blender/blenlib/intern/compression.cc
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_compression.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void filter_transpose_delta(const uint8_t *src, uint8_t *dst, size_t items_num, size_t item_size)
|
||||
{
|
||||
for (size_t ib = 0; ib < item_size; ib++) {
|
||||
uint8_t prev = 0;
|
||||
const uint8_t *src_ptr = src + ib;
|
||||
size_t it = 0;
|
||||
for (; it < items_num; it++) {
|
||||
uint8_t v = *src_ptr;
|
||||
*dst = v - prev;
|
||||
prev = v;
|
||||
src_ptr += item_size;
|
||||
dst += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unfilter_transpose_delta(const uint8_t *src, uint8_t *dst, size_t items_num, size_t item_size)
|
||||
{
|
||||
for (size_t ib = 0; ib < item_size; ib++) {
|
||||
uint8_t prev = 0;
|
||||
uint8_t *dst_ptr = dst + ib;
|
||||
for (size_t it = 0; it < items_num; it++) {
|
||||
uint8_t v = *src + prev;
|
||||
prev = v;
|
||||
*dst_ptr = v;
|
||||
src += 1;
|
||||
dst_ptr += item_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
55
source/blender/blenlib/tests/BLI_compression_test.cc
Normal file
55
source/blender/blenlib/tests/BLI_compression_test.cc
Normal file
@@ -0,0 +1,55 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_compression.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
TEST(compression, filter_transpose_delta)
|
||||
{
|
||||
constexpr int num = 5;
|
||||
constexpr int size = 3;
|
||||
uint8_t input[num * size] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 5, 4, 3};
|
||||
uint8_t filtered_exp[num * size] = {0, 2, 6, 26, 227, 1, 2, 10, 42, 205, 1, 4, 16, 68, 170};
|
||||
uint8_t filtered[num * size] = {};
|
||||
uint8_t unfiltered[num * size] = {};
|
||||
filter_transpose_delta(input, filtered, num, size);
|
||||
EXPECT_EQ_ARRAY(filtered_exp, filtered, num * size);
|
||||
unfilter_transpose_delta(filtered, unfiltered, num, size);
|
||||
EXPECT_EQ_ARRAY(input, unfiltered, num * size);
|
||||
}
|
||||
|
||||
static uint32_t pcg_rand(uint32_t &rng_state)
|
||||
{
|
||||
uint32_t state = rng_state;
|
||||
rng_state = rng_state * 747796405u + 2891336453u;
|
||||
uint32_t word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
|
||||
return (word >> 22u) ^ word;
|
||||
}
|
||||
|
||||
TEST(compression, filter_transpose_delta_stress)
|
||||
{
|
||||
uint32_t rng = 1;
|
||||
|
||||
constexpr int size = 64 * 1024;
|
||||
Array<uint8_t> input(size);
|
||||
Array<uint8_t> filtered(size);
|
||||
Array<uint8_t> unfiltered(size);
|
||||
for (uint8_t &val : input) {
|
||||
val = pcg_rand(rng);
|
||||
}
|
||||
|
||||
const int strides[] = {1, 2, 3, 4, 5, 8, 13, 16, 25, 48, 64, 65, 101, 300, 512, 513, size};
|
||||
for (int stride : strides) {
|
||||
const int num = size / stride;
|
||||
filter_transpose_delta(input.data(), filtered.data(), num, stride);
|
||||
unfilter_transpose_delta(filtered.data(), unfiltered.data(), num, stride);
|
||||
EXPECT_EQ_ARRAY(input.data(), unfiltered.data(), num * stride);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -1250,15 +1250,13 @@ static void do_version_remove_lzo_and_lzma_compression(FileData *fd, Object *obj
|
||||
|
||||
LISTBASE_FOREACH (PTCacheID *, pid, &pidlist) {
|
||||
bool found_incompatible_cache = false;
|
||||
if (pid->cache->compression == PTCACHE_COMPRESS_LZO_DEPRECATED) {
|
||||
pid->cache->compression = PTCACHE_COMPRESS_ZSTD_FAST;
|
||||
if (ELEM(pid->cache->compression,
|
||||
PTCACHE_COMPRESS_LZO_DEPRECATED,
|
||||
PTCACHE_COMPRESS_LZMA_DEPRECATED))
|
||||
{
|
||||
pid->cache->compression = PTCACHE_COMPRESS_ZSTD_FILTERED;
|
||||
found_incompatible_cache = true;
|
||||
}
|
||||
else if (pid->cache->compression == PTCACHE_COMPRESS_LZMA_DEPRECATED) {
|
||||
pid->cache->compression = PTCACHE_COMPRESS_ZSTD_SLOW;
|
||||
found_incompatible_cache = true;
|
||||
}
|
||||
|
||||
if (pid->type == PTCACHE_TYPE_DYNAMICPAINT) {
|
||||
/* Dynamicpaint was hardcoded to use LZO. */
|
||||
found_incompatible_cache = true;
|
||||
|
||||
@@ -95,7 +95,7 @@ typedef struct PointCache {
|
||||
int totpoint;
|
||||
/** Modifier stack index. */
|
||||
int index;
|
||||
/** #PointCacheCompression. */
|
||||
/** #PointCacheCompression. Used for versioning only; now cache is always compressed. */
|
||||
short compression;
|
||||
char _pad0[2];
|
||||
|
||||
@@ -149,12 +149,16 @@ enum {
|
||||
PTCACHE_FLAGS_COPY = PTCACHE_DISK_CACHE | PTCACHE_EXTERNAL | PTCACHE_IGNORE_LIBPATH,
|
||||
};
|
||||
|
||||
/* Note: the enum values look like bit flags, but they are not really;
|
||||
* it is just an enum with strange values. */
|
||||
/**
|
||||
* Cache files baked before 5.0 could have used LZO or LZMA.
|
||||
* During 5.0 alpha ZSTD compression had two settings.
|
||||
* Now only the ZSTD+filtering option is used.
|
||||
*/
|
||||
typedef enum PointCacheCompression {
|
||||
PTCACHE_COMPRESS_NO = 0,
|
||||
PTCACHE_COMPRESS_LZO_DEPRECATED = 1, /* Removed in 5.0. */
|
||||
PTCACHE_COMPRESS_LZMA_DEPRECATED = 2, /* Removed in 5.0. */
|
||||
PTCACHE_COMPRESS_ZSTD_FAST = 4,
|
||||
PTCACHE_COMPRESS_ZSTD_SLOW = 8,
|
||||
PTCACHE_COMPRESS_ZSTD_FILTERED = 3,
|
||||
PTCACHE_COMPRESS_ZSTD_FAST_DEPRECATED = 4, /* Used only during 5.0 alpha. */
|
||||
PTCACHE_COMPRESS_ZSTD_SLOW_DEPRECATED = 8, /* Used only during 5.0 alpha. */
|
||||
} PointCacheCompression;
|
||||
|
||||
@@ -957,18 +957,6 @@ static void rna_def_pointcache_common(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
static const EnumPropertyItem point_cache_compress_items[] = {
|
||||
{PTCACHE_COMPRESS_NO, "NO", 0, "None", "No compression"},
|
||||
/* TODO: Deprecated. Remove after short testing period in 5.0. */
|
||||
# if 0 // WITH_LZO, WITH_LZMA
|
||||
{PTCACHE_COMPRESS_LZO, "LIGHT", 0, "Lite", "Fast but not so effective compression"},
|
||||
{PTCACHE_COMPRESS_LZMA, "HEAVY", 0, "Heavy", "Effective but slow compression"},
|
||||
# endif
|
||||
{PTCACHE_COMPRESS_ZSTD_FAST, "FAST", 0, "Fast", "Fast but not so effective compression"},
|
||||
{PTCACHE_COMPRESS_ZSTD_SLOW, "SLOW", 0, "Slow", "Effective but slow compression"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
RNA_def_struct_path_func(srna, "rna_PointCache_path");
|
||||
|
||||
RNA_define_lib_overridable(true);
|
||||
@@ -997,10 +985,6 @@ static void rna_def_pointcache_common(StructRNA *srna)
|
||||
RNA_def_property_ui_text(prop, "Cache Index", "Index number of cache files");
|
||||
RNA_def_property_update(prop, NC_OBJECT, "rna_Cache_idname_change");
|
||||
|
||||
prop = RNA_def_property(srna, "compression", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, point_cache_compress_items);
|
||||
RNA_def_property_ui_text(prop, "Cache Compression", "Compression method to be used");
|
||||
|
||||
/* flags */
|
||||
prop = RNA_def_property(srna, "is_baked", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", PTCACHE_BAKED);
|
||||
|
||||
Reference in New Issue
Block a user