跳到主要内容
engine-pipeline · 代码
Graphics Learning Lab Engine Pipeline / Source

看见反常 · 动一处 · 抓证据

一条资源管线,在代码里有六个检查点

只读 · 与实验共用计算模型

这页解决什么

从导入、版本和依赖读到缓存、合批与时间线。每段只回答一个问题。

资源导入

入口规则决定错误何时出现

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);
}

DCC → 引擎

一张 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

批次也是资产组织问题

图集、实例化、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);
}

GPU 时间线

最后回到目标帧耗时

用 pass 时间验证,不能凭感觉宣布“优化完成”。

// SECTION:profiler
Timeline frame = profiler.capture(profilerFrames);
float gpuMs = frame.pass('shadow') + frame.pass('material') + frame.pass('post');
if (gpuMs > targetMs) markRisk('budget');