taisei/src/resource/texture_loader/basisu_cache.c
2024-05-17 14:11:48 +02:00

195 lines
4.3 KiB
C

/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "basisu_cache.h"
#include "basisu.h"
#include "log.h"
#include "pixmap/pixmap.h"
#include "rwops/rwops_zstd.h"
#include <basisu_transcoder_c_api.h>
// FIXME: The cache is not atomic.
// This may be a problem if multiple threads are loading identical .basis files,
// or if multiple Taisei instances are running for some reason.
enum {
ENTRY_PATH_SIZE = 256,
};
static bool texture_loader_basisu_make_cache_path(
const char *basisu_hash,
const basist_transcode_level_params *tc_params,
size_t bufsize,
char buf[bufsize]
) {
int len = snprintf(
buf, bufsize,
"cache/textures/basisu-%s/%u_%u_%s_%x",
basisu_hash,
tc_params->image_index,
tc_params->level_index,
basist_get_format_name(tc_params->format),
tc_params->decode_flags
);
if(len >= bufsize) {
log_error("Cache entry name is too long");
return false;
}
return true;
}
bool texture_loader_basisu_load_cached(
const char *basisu_hash,
const basist_transcode_level_params *tc_params,
const basist_image_level_desc *level_desc,
PixmapFormat expected_px_format,
PixmapOrigin expected_px_origin,
uint32_t expected_size,
Pixmap *out_pixmap
) {
char path[ENTRY_PATH_SIZE];
if(!texture_loader_basisu_make_cache_path(basisu_hash, tc_params, sizeof(path), path)) {
return false;
}
if(!vfs_query(path).exists) {
BASISU_DEBUG("%s not found", path);
return false;
}
SDL_RWops *rw = vfs_open(path, VFS_MODE_READ);
if(!rw) {
log_error("VFS error: %s", vfs_get_error());
return false;
}
rw = SDL_RWWrapZstdReader(rw, true);
bool deserialize_ok = pixmap_load_stream(rw, PIXMAP_FILEFORMAT_INTERNAL, out_pixmap, 0);
SDL_RWclose(rw);
if(!deserialize_ok) {
log_error("%s: Failed to deserialize cached pixmap", path);
return false;
}
if(out_pixmap->format != expected_px_format) {
const char *n = pixmap_format_name(out_pixmap->format);
if(n) {
log_error("%s: Bad cache entry: Expected format %s, got %s",
path,
pixmap_format_name(expected_px_format),
n
);
} else {
log_error("%s: Bad cache entry: Expected format %s, got 0x%04x",
path,
pixmap_format_name(expected_px_format),
out_pixmap->format
);
}
goto bad_entry;
}
if(out_pixmap->origin != expected_px_origin) {
const char *origin_name[] = {
[PIXMAP_ORIGIN_BOTTOMLEFT] = "BOTTOMLEFT",
[PIXMAP_ORIGIN_TOPLEFT] = "TOPLEFT",
};
log_error("%s: Bad cache entry: Expected origin %s, got %s",
path,
origin_name[expected_px_origin],
origin_name[out_pixmap->origin]
);
goto bad_entry;
}
if(
out_pixmap->width != level_desc->orig_width ||
out_pixmap->height != level_desc->orig_height
) {
log_error("%s: Bad cache entry: Expected dimensions %ux%u, got %ux%u",
path,
level_desc->orig_width,
level_desc->orig_height,
out_pixmap->width,
out_pixmap->height
);
goto bad_entry;
}
if(out_pixmap->data_size != expected_size) {
log_error("%s: Bad cache entry: Expected data size %u, got %u",
path,
expected_size,
out_pixmap->data_size
);
goto bad_entry;
}
BASISU_DEBUG("Loaded cache entry from %s", path);
return true;
bad_entry:
mem_free(out_pixmap->data.untyped);
out_pixmap->data.untyped = NULL;
return false;
}
bool texture_loader_basisu_cache(
const char *basisu_hash,
const basist_transcode_level_params *tc_params,
const basist_image_level_desc *level_desc,
const Pixmap *pixmap
) {
char path[ENTRY_PATH_SIZE];
if(!texture_loader_basisu_make_cache_path(basisu_hash, tc_params, sizeof(path), path)) {
return false;
}
if(!vfs_mkparents(path)) {
log_error("VFS error: %s", vfs_get_error());
return false;
}
SDL_RWops *rw = vfs_open(path, VFS_MODE_WRITE);
if(!rw) {
log_error("VFS error: %s", vfs_get_error());
return false;
}
rw = SDL_RWWrapZstdWriter(rw, RW_ZSTD_LEVEL_DEFAULT, true);
PixmapSaveOptions opts = PIXMAP_DEFAULT_SAVE_OPTIONS;
opts.file_format = PIXMAP_FILEFORMAT_INTERNAL;
bool serialize_ok = pixmap_save_stream(rw, pixmap, &opts);
SDL_RWclose(rw);
if(!serialize_ok) {
log_error("%s: Failed to serialize pixmap", path);
return false;
}
BASISU_DEBUG("Cached pixmap at %s", path);
return true;
}