看见反常 · 动一处 · 抓证据
一条资源管线,在代码里有六个检查点
只读 · 与实验共用计算模型
这页解决什么
从导入、版本和依赖读到缓存、合批与时间线。每段只回答一个问题。
入口规则决定错误何时出现
UV 缝、法线重算、命名和 QA 都会留下证据。
// SECTION:import
ImportReport importAsset(Mesh mesh) {
Mesh split = uvSplit ? splitByUvSeams(mesh) : mesh;
Mesh normals = normalRecalc ? recalculateNormals(split) : split;
QAResult qa = strictNaming ? validateNaming(normals) : warnOnly(normals);
return report(normals, qa);
}
一张 manifest 只允许沿着单向发布链前进
DCC exporter 负责写 manifest,import worker 负责暂存,Unity 只接收校验后的原子发布;失败时保留已发布版本。
// SECTION:sync
PublishResult publishFromDcc(DataAsset asset, Contract expected) {
Manifest manifest = exporter.write(asset, { assetGuid, sourceRevision, payloadFingerprint });
if (!validateManifest(manifest, expected)) {
publisher.keepPublished(publishedArtifact); // no partial Unity state
return rejected(manifest);
}
Artifact staged = staging.write(manifest);
return publisher.publishAtomically(staged);
}
加载一个资源,也要带上它的依赖
AssetBundle 与 Addressables 都绕不开依赖、缓存和并发。
// SECTION:streaming
async Task<Scene> loadScene(Key key) {
Catalog catalog = await catalogHandle;
DependencyGraph graph = catalog.closeDependencies(key);
await cache.ensure(graph, cacheBudget);
return await addressables.loadAsync(key, concurrency);
}
缓存 miss 决定卡顿长什么样
热更新、缓存预算和依赖深度进入同一等待模型。
// SECTION:cache
float stallCost(Cache cache, Graph graph) {
float miss = graph.bytes * (1.0 - cache.hitRate);
return miss / bandwidth + graph.depth * dependencyLatency;
}
批次也是资产组织问题
图集、实例化、LOD 和碰撞体会改变 draw 与内存。
// SECTION:batch
BatchReport batch(Renderer[] renderers) {
Atlas atlas = buildMaterialAtlas(renderers);
int merged = enableInstancing ? instanceCompatible(renderers) : 0;
return report(renderers.length - merged - atlas.mergedDraws, atlas.coverage);
}
最后回到目标帧耗时
用 pass 时间验证,不能凭感觉宣布“优化完成”。
// SECTION:profiler
Timeline frame = profiler.capture(profilerFrames);
float gpuMs = frame.pass('shadow') + frame.pass('material') + frame.pass('post');
if (gpuMs > targetMs) markRisk('budget');