资产浏览器可视化改造 + 材质缩略图 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 缓存—— 修大材质目录首次打开卡顿
This commit is contained in:
@@ -9,6 +9,9 @@
|
||||
|
||||
#include "MaterialEditorPanel.h"
|
||||
#include "MaterialAssetUtil.h"
|
||||
#include "AssetItemView.h" // 纹理选择器网格(缩略图瓦片)
|
||||
#include "EditorColors.h"
|
||||
#include "ThumbnailCache.h"
|
||||
|
||||
#include <Engine/ECS/Components.h>
|
||||
#include <Engine/Renderer/Model.h>
|
||||
@@ -17,6 +20,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -34,6 +38,125 @@ namespace {
|
||||
}
|
||||
|
||||
constexpr float kPreviewSize = 128.0f; // 编辑器顶部大球(Inspector 槽位为 48²)
|
||||
|
||||
// 纹理槽缩略图(同时是拖拽落点):Content Browser 拖纹理资产落入即
|
||||
// 指派该槽(kTextureDragPayload)。空槽显示 "nil" 占位
|
||||
void SlotThumb(const std::string& texPath, float sz, int slotIdx,
|
||||
int& pendingSlot, std::string& pendingPath)
|
||||
{
|
||||
const ImVec4 accent = EditorColors::kTexture;
|
||||
ImGui::Dummy(ImVec2(sz, sz));
|
||||
const ImVec2 p0 = ImGui::GetItemRectMin();
|
||||
const ImVec2 p1 = ImGui::GetItemRectMax();
|
||||
const bool hovered = ImGui::IsItemHovered();
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
|
||||
bool drawn = false;
|
||||
if (!texPath.empty())
|
||||
{
|
||||
const ThumbView v = EditorThumbnailCache::Get().Request(texPath);
|
||||
if (v.state == ThumbView::State::Ready)
|
||||
{
|
||||
float fw = sz, fh = sz;
|
||||
if (v.w > 0 && v.h > 0)
|
||||
{
|
||||
if (v.w >= v.h) fh = sz * static_cast<float>(v.h) / v.w;
|
||||
else fw = sz * static_cast<float>(v.w) / v.h;
|
||||
}
|
||||
const ImVec2 f0(p0.x + (sz - fw) * 0.5f,
|
||||
p0.y + (sz - fh) * 0.5f);
|
||||
dl->AddImage(v.tex, f0, ImVec2(f0.x + fw, f0.y + fh));
|
||||
drawn = true;
|
||||
}
|
||||
}
|
||||
if (!drawn)
|
||||
{
|
||||
// 背板只在未出图时铺:出图时叠加会污染透明像素
|
||||
dl->AddRectFilled(
|
||||
p0, p1,
|
||||
ImGui::GetColorU32(ImVec4(accent.x * 0.25f, accent.y * 0.25f,
|
||||
accent.z * 0.25f, 1.0f)),
|
||||
3.0f);
|
||||
const char* lbl = texPath.empty() ? "nil" : "TEX";
|
||||
const ImVec2 ts = ImGui::CalcTextSize(lbl);
|
||||
dl->AddText(ImVec2(p0.x + (sz - ts.x) * 0.5f,
|
||||
p0.y + (sz - ts.y) * 0.5f),
|
||||
ImGui::GetColorU32(texPath.empty()
|
||||
? EditorColors::kOther
|
||||
: accent),
|
||||
lbl);
|
||||
}
|
||||
dl->AddRect(p0, p1,
|
||||
ImGui::GetColorU32(hovered ? EditorColors::Lighten(accent,
|
||||
0.3f)
|
||||
: accent),
|
||||
3.0f);
|
||||
|
||||
if (ImGui::BeginDragDropTarget())
|
||||
{
|
||||
if (const ImGuiPayload* pl =
|
||||
ImGui::AcceptDragDropPayload(kTextureDragPayload))
|
||||
{
|
||||
pendingSlot = slotIdx;
|
||||
pendingPath = std::string(static_cast<const char*>(pl->Data));
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
}
|
||||
|
||||
// 纹理选择器内容(弹窗内):<none> 清除入口 + 缩略图网格。
|
||||
// 返回 true = 本次有选择,选中路径写入 out(空串 = <none>)
|
||||
bool TexturePickerContent(const EditorContext& ctx, std::string& out)
|
||||
{
|
||||
bool picked = false;
|
||||
if (ImGui::Selectable("<none>"))
|
||||
{
|
||||
out.clear();
|
||||
picked = true;
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
const auto& files = EditorMaterialUtil::AssetTextureFiles();
|
||||
if (files.empty())
|
||||
{
|
||||
ImGui::TextDisabled("(no textures under assets/)");
|
||||
return picked;
|
||||
}
|
||||
|
||||
constexpr float kTile = 64.0f;
|
||||
const float cellW = kTile + ImGui::GetStyle().CellPadding.x * 2.0f;
|
||||
int cols = static_cast<int>(360.0f / cellW);
|
||||
if (cols < 1)
|
||||
{
|
||||
cols = 1;
|
||||
}
|
||||
ImGui::BeginChild("##texgrid", ImVec2(360.0f, 260.0f),
|
||||
ImGuiChildFlags_Borders);
|
||||
if (ImGui::BeginTable("##grid", cols, ImGuiTableFlags_SizingFixedFit))
|
||||
{
|
||||
for (int i = 0; i < cols; ++i)
|
||||
{
|
||||
ImGui::TableSetupColumn("##c",
|
||||
ImGuiTableColumnFlags_WidthFixed,
|
||||
cellW);
|
||||
}
|
||||
for (const auto& f : files)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
const auto r =
|
||||
EditorAssetView::DrawTile(ctx, f, kTile, false);
|
||||
if (r.clicked)
|
||||
{
|
||||
out = f;
|
||||
picked = true;
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
return picked;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void MaterialEditorPanel::OpenAsset(const std::string& path)
|
||||
@@ -228,10 +351,17 @@ void MaterialEditorPanel::DrawAssetMode(const EditorContext& ctx)
|
||||
"Emissive", "Ao", "Roughness"};
|
||||
int pendingSlot = -1;
|
||||
std::string pendingPath;
|
||||
const float thumbSz = ImGui::GetTextLineHeight() * 1.8f;
|
||||
for (Engine::u32 s = 0; s < Engine::Material::kSlotCount; ++s)
|
||||
{
|
||||
std::string tp = asset->GetData().texPaths[s];
|
||||
ImGui::PushID(static_cast<int>(s));
|
||||
|
||||
// 缩略图块(落点:拖入纹理资产即指派该槽)
|
||||
SlotThumb(tp, thumbSz, static_cast<int>(s), pendingSlot, pendingPath);
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::SetNextItemWidth(200.0f);
|
||||
InputTextString(kSlotNames[s], tp);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit())
|
||||
{
|
||||
@@ -245,20 +375,13 @@ void MaterialEditorPanel::DrawAssetMode(const EditorContext& ctx)
|
||||
}
|
||||
if (ImGui::BeginPopup("tex_pick"))
|
||||
{
|
||||
// 清除入口(Unity/UE 语义):该槽置空 → 回退兜底贴图
|
||||
if (ImGui::Selectable("<none>"))
|
||||
// 弹窗内只做选择;pending 统一在循环外落盘(避免 popup 内
|
||||
// 立即重建导致 asset 指针悬空)
|
||||
std::string chosen;
|
||||
if (TexturePickerContent(ctx, chosen))
|
||||
{
|
||||
pendingSlot = static_cast<int>(s);
|
||||
pendingPath.clear();
|
||||
}
|
||||
ImGui::Separator();
|
||||
for (const auto& f : EditorMaterialUtil::AssetTextureFiles())
|
||||
{
|
||||
if (ImGui::Selectable(f.c_str()))
|
||||
{
|
||||
pendingSlot = static_cast<int>(s);
|
||||
pendingPath = f;
|
||||
}
|
||||
pendingPath = chosen;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user