// // ThumbnailCache.h:编辑器缩略图缓存(Content Browser / 材质纹理槽共用)。 // // 逐帧限量 CPU 解码 + box 降采样 → ImGui 用户纹理(ImTextureData + // RegisterUserTexture)→ 后端建/传 VkImage。零引擎改动。 // // 生命周期(详见 .cpp 注释):进程级静态单例,析构晚于 ImGuiLayer, // CPU 侧由静态析构回收、GPU 侧由 ImGui_ImplVulkan_Shutdown 回收。 // #ifndef HELLOVULKAN_THUMBNAILCACHE_H #define HELLOVULKAN_THUMBNAILCACHE_H #include // ImTextureData / ImTextureRef #include #include #include #include // 一次请求的结果:State==Ready 时 tex 可用于 ImGui::Image(tex, size) struct ThumbView { enum class State { Pending, Ready, Failed }; State state = State::Pending; ImTextureRef tex{}; int w = 0; // 缩略图原始像素尺寸(按比例显示用) int h = 0; }; class EditorThumbnailCache { public: // 进程级单例(编辑器首帧 BeginFrame 时惰性构造) static EditorThumbnailCache& Get(); // 每帧一次:Editor::Draw 开头(ImGui::NewFrame 之后、面板绘制之前) void BeginFrame(); // 幂等请求:命中且就绪即返回;未命中则入队,本帧返回 Pending ThumbView Request(const std::string& assetPath); // 只读查状态(**不**触发解码入队):缺省 → Failed。材质球烘焙用—— // 材质路径不走 stbi 解码,注入前必须能区分"未注入"与"上传中" //(Pending 表示已注入、等后端建纹理) ThumbView Peek(const std::string& assetPath); // 作废某条目的缓存纹理(材质重载/参数编辑后缩略图过期 → 重烘)。 // 丢弃 CPU 记录、纹理交回回收流程;随后 Peek 缺省 → Failed void Invalidate(const std::string& assetPath); // ---- 材质球烘焙(GPU 回读结果注入)---- // path ↔ key 幂等映射:Renderer 只接受/回传 u64 令牌,App 侧登记 unsigned long long BakeKeyForPath(const std::string& path); // 回读结果注入:RGBA8 像素(w×h)→ 缩略图纹理(内部等比降到最长边 // ≤ kThumbMaxDim)。key 未登记则忽略 void CompleteBake(unsigned long long key, const unsigned char* rgba, int w, int h); EditorThumbnailCache(const EditorThumbnailCache&) = delete; EditorThumbnailCache& operator=(const EditorThumbnailCache&) = delete; private: EditorThumbnailCache() = default; ~EditorThumbnailCache(); struct Entry { ImTextureData* tex = nullptr; // 非拥有(删由回收流程) int w = 0; int h = 0; ThumbView::State state = ThumbView::State::Pending; unsigned long long lastUseFrame = 0; int pendingFrames = 0; // 注册后等待后端上传的帧数 }; void DecodeOne(const std::string& path, Entry& e); // 解码→降采样→注册 // 源像素(RGBA8,sw×sh)→ 等比降采样 → ImTextureData 注册(解码与 // 烘焙回读注入共用同一条上传路径) static void UploadRGBA8(const unsigned char* src, int sw, int sh, Entry& e); void InjectRGBA8(const std::string& path, const unsigned char* rgba, int w, int h); bool RetireOne(); // 淘汰最久未用(返回是否淘汰) void DrainRetired(); // 后端已释放→回收 std::unordered_map m_Entries; std::deque m_Queue; // 待解码(重复请求命 entries,天然去重) std::vector m_Retire; // 已标 WantDestroy,等后端释放 unsigned long long m_Frame = 0; // 材质球烘焙 path ↔ key 映射(幂等分配;回读完成即注销) unsigned long long m_BakeKeyCounter = 0; std::unordered_map m_PathToBakeKey; std::unordered_map m_BakeKeyToPath; }; #endif //HELLOVULKAN_THUMBNAILCACHE_H