ADD: Texture Cache

This commit is contained in:
2025-11-03 15:59:25 +09:00
parent cb495e3f4b
commit c64db86f82
2 changed files with 357 additions and 0 deletions

114
src/core/texture_cache.h Normal file
View File

@@ -0,0 +1,114 @@
#pragma once
#include <core/vk_types.h>
#include <cstdint>
#include <string>
#include <vector>
#include <unordered_map>
class EngineContext;
class ResourceManager;
struct FrameResources;
// Lightweight texture streaming cache.
// - Requests are deduplicated by a hashable TextureKey.
// - Loads happen via ResourceManager (deferred uploads supported).
// - Descriptors registered via watchBinding() are patched in-place
// when the image becomes Resident, leveraging UPDATE_AFTER_BIND.
// - evictToBudget() rewrites watchers to provided fallbacks.
class TextureCache
{
public:
struct TextureKey
{
enum class SourceKind : uint8_t { FilePath, Bytes };
SourceKind kind{SourceKind::FilePath};
std::string path; // used when kind==FilePath
std::vector<uint8_t> bytes; // used when kind==Bytes
bool srgb{false}; // desired sampling format
bool mipmapped{true}; // generate full mip chain
uint64_t hash{0}; // stable dedup key
};
using TextureHandle = uint32_t;
static constexpr TextureHandle InvalidHandle = 0xFFFFFFFFu;
void init(EngineContext *ctx);
void cleanup();
// Deduplicated request; returns a stable handle.
TextureHandle request(const TextureKey &key, VkSampler sampler);
// Register a descriptor binding to patch when the texture is ready.
void watchBinding(TextureHandle handle, VkDescriptorSet set, uint32_t binding,
VkSampler sampler, VkImageView fallbackView);
// Mark a texture as used this frame (for LRU).
void markUsed(TextureHandle handle, uint32_t frameIndex);
// Convenience: mark all handles watched by a descriptor set.
void markSetUsed(VkDescriptorSet set, uint32_t frameIndex);
// Schedule pending loads and patch descriptors for newly created images.
void pumpLoads(ResourceManager &rm, FrameResources &frame);
// Evict least-recently-used entries to fit within a budget in bytes.
void evictToBudget(size_t budgetBytes);
private:
struct Patch
{
VkDescriptorSet set{VK_NULL_HANDLE};
uint32_t binding{0};
VkSampler sampler{VK_NULL_HANDLE};
VkImageView fallbackView{VK_NULL_HANDLE};
};
enum class EntryState : uint8_t { Unloaded, Loading, Resident, Evicted };
struct Entry
{
TextureKey key{};
VkSampler sampler{VK_NULL_HANDLE};
EntryState state{EntryState::Unloaded};
AllocatedImage image{}; // valid when Resident
size_t sizeBytes{0}; // approximate VRAM cost
uint32_t lastUsedFrame{0};
std::vector<Patch> patches; // descriptor patches to rewrite
// Source payload for deferred load
std::string path; // for FilePath
std::vector<uint8_t> bytes; // for Bytes
};
EngineContext *_context{nullptr};
std::vector<Entry> _entries;
std::unordered_map<uint64_t, TextureHandle> _lookup; // key.hash -> handle
std::unordered_map<VkDescriptorSet, std::vector<TextureHandle>> _setToHandles;
size_t _residentBytes{0};
void start_load(Entry &e, ResourceManager &rm);
void patch_ready_entry(const Entry &e);
void patch_to_fallback(const Entry &e);
};
// Helpers to build/digest keys
namespace texcache
{
// 64-bit FNV-1a
inline uint64_t fnv1a64(std::string_view s)
{
const uint64_t FNV_OFFSET = 1469598103934665603ull;
const uint64_t FNV_PRIME = 1099511628211ull;
uint64_t h = FNV_OFFSET;
for (unsigned char c : s) { h ^= c; h *= FNV_PRIME; }
return h;
}
inline uint64_t fnv1a64(const uint8_t *data, size_t n)
{
const uint64_t FNV_OFFSET = 1469598103934665603ull;
const uint64_t FNV_PRIME = 1099511628211ull;
uint64_t h = FNV_OFFSET;
for (size_t i = 0; i < n; ++i) { h ^= data[i]; h *= FNV_PRIME; }
return h;
}
}