Files
HelloVulkan/App/editor/MaterialAssetUtil.h
T
atdunbg 84cf547704 资产浏览器可视化改造 + 材质缩略图 GPU 烘焙 + 纹理上传批式化
- 资产浏览器可视化与配色:面板重构(网格/列表/缩略图)、AssetKind 分类、
  EditorColors 统一配色、AssetItemView 抽出条目绘制;HierarchyPanel/
  MaterialEditorPanel 同步调整
- 材质缩略图改走 GPU 回读烘焙:MaterialPreviewPass 新增 bake pass
  (SDR atlas → host staging copyImageToBuffer)、Renderer 提供
  Request/PollMaterialPreviewBake(无 waitIdle,依赖 per-slot fence),
  Editor 每帧排空后注入 ThumbnailCache——脱离预览 tile 池,消除"滚出视野
  即失效/池耗尽"两问题
- 缩略图失效:BakedIdentity(serial, editVersion) 与当前材质身份比对,
  换贴图槽(重载新 serial)/调参数(editVersion++)不符即 Invalidate 重烘,
  修"修改材质后资产浏览器缩略图不更新"
- 材质加载闸门:每帧至多 1 个 + 按实测耗时冷却,替代固定 2/帧
- 纹理上传批式化:VulkanAssetUploader 新增路径纹理缓存(跨材质去重,
  同一贴图只解码/上传一次)+ 同批新纹理合并为单 cmd/单 submit/单 fence;
  Texture 抽出 RecordPixelsUpload/CreateDeferred/ReleasePendingStaging,
  析构兜底释放 staging;MaterialAsset 纹理所有权移交 uploader 缓存——
  修大材质目录首次打开卡顿
2026-10-18 20:05:29 +08:00

171 lines
5.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// MaterialAssetUtil.h:材质资产编辑共用工具(Inspector / Material Editor
// 面板共享)。
//
// 单一事实来源(原 InspectorPanel.cpp 匿名 ns 实现上提):
// - .material 文件枚举缓存(inline 变量跨 TU 唯一——面板各自创建
// Override 后缓存失效即时可见)
// - Create Override:内建材质快照 → assets/materials/*.material →
// GUID 登记 → 同步加载 → 挂实体覆盖表
//
#ifndef HELLOVULKAN_MATERIALASSETUTIL_H
#define HELLOVULKAN_MATERIALASSETUTIL_H
#include "AssetKind.h" // EditorAsset::IsTexturePath(纹理扩展名单一事实来源)
#include <Engine/Assets/AssetManager.h>
#include <Engine/Core/Log.h>
#include <Engine/ECS/Components.h>
#include <Engine/Renderer/MaterialAsset.h>
#include <Engine/Renderer/Model.h>
#include <algorithm>
#include <cctype>
#include <filesystem>
#include <string>
#include <vector>
namespace EditorMaterialUtil {
namespace fs = std::filesystem;
// .material 文件枚举(会话级缓存 + 脏标)。inline 变量 = 所有 TU
// 共享同一份(Create Override 新建文件后置脏重扫)
inline std::vector<std::string> s_MaterialFiles;
inline bool s_MaterialFilesDirty = true;
inline void InvalidateMaterialFileList() { s_MaterialFilesDirty = true; }
// 递归收集 assets/ 下纹理文件(材质编辑器纹理槽选择器;纹理文件
// 不被编辑器创建,无需脏标)。会话级缓存
inline const std::vector<std::string>& AssetTextureFiles()
{
static std::vector<std::string> s_Files;
static bool s_Cached = false;
if (!s_Cached)
{
s_Cached = true;
if (fs::exists("assets"))
{
for (const auto& entry :
fs::recursive_directory_iterator("assets"))
{
if (!entry.is_regular_file())
{
continue;
}
if (EditorAsset::IsTexturePath(entry.path()))
{
s_Files.push_back(entry.path().generic_string());
}
}
std::sort(s_Files.begin(), s_Files.end());
}
}
return s_Files;
}
inline const std::vector<std::string>& AssetMaterialFiles()
{
if (s_MaterialFilesDirty)
{
s_MaterialFilesDirty = false;
s_MaterialFiles.clear();
if (fs::exists("assets"))
{
for (const auto& entry :
fs::recursive_directory_iterator("assets"))
{
if (!entry.is_regular_file())
{
continue;
}
std::string ext = entry.path().extension().string();
std::transform(ext.begin(), ext.end(), ext.begin(),
[](unsigned char c)
{ return std::tolower(c); });
if (ext == ".material")
{
s_MaterialFiles.push_back(
entry.path().generic_string());
}
}
std::sort(s_MaterialFiles.begin(), s_MaterialFiles.end());
}
}
return s_MaterialFiles;
}
// Create Override:内建材质快照 → assets/materials/<model>_<mesh>.material
// → GUID 登记 → 同步加载 → 挂实体覆盖表。失败返回空串(不挂槽)。
// 数据段 = 内建材质只读快照(纹理路径 + 因子 + 透明语义),此后
// 该资产独立演化,内建材质不受影响
inline std::string CreateMaterialOverride(
const std::string& modelPath,
const Engine::MeshMaterialInfo& info,
Engine::u32 meshIdx,
Engine::ECS::MeshRendererComponent& mr,
Engine::Assets::AssetManager& assets)
{
// 目标文件名:<modelStem>_<meshName|idx>.material;mesh 名非
// [A-Za-z0-9] 字符折叠 '_',空名回退下标;重名追加 _2/_3/...
std::string stem = fs::path(modelPath).stem().string();
std::string leaf = info.name;
if (leaf.empty())
{
leaf = std::to_string(meshIdx);
}
for (char& c : leaf)
{
if (!std::isalnum(static_cast<unsigned char>(c)))
{
c = '_';
}
}
const fs::path dir = "assets/materials";
std::error_code ec;
fs::create_directories(dir, ec);
if (ec)
{
ENGINE_WARN("MaterialUtil: cannot create {}: {}", dir.string(),
ec.message());
return {};
}
std::string base = stem + "_" + leaf;
std::string rel = (dir / (base + ".material")).generic_string();
for (int n = 2; fs::exists(rel); ++n)
{
rel = (dir / (base + "_" + std::to_string(n) + ".material"))
.generic_string();
}
Engine::MaterialAssetData data;
data.texPaths = info.texPaths;
data.params = info.params;
data.transparent = info.transparent;
if (!Engine::MaterialAsset::Save(rel, data))
{
ENGINE_WARN("MaterialUtil: failed to save material override '{}'",
rel);
return {};
}
assets.GetOrCreateGUID(rel);
if (!assets.LoadMaterialAsset(rel).Valid())
{
ENGINE_WARN("MaterialUtil: override '{}' saved but failed to load",
rel);
return {};
}
mr.materialOverrides[meshIdx] = rel;
InvalidateMaterialFileList(); // 新文件进下拉列表
return rel;
}
} // namespace EditorMaterialUtil
#endif //HELLOVULKAN_MATERIALASSETUTIL_H