adoptUnchanged method

void adoptUnchanged(
  1. ResourceRealizer previous
)

Carries realized resources over from previous (the realizer the live scene was built with) for every resource whose spec and payload bytes are unchanged, so a hot reload rebuilds only what actually changed instead of re-uploading the whole document (seconds for a large scene). Call before preload; preload skips carried-over entries.

Unchanged payload bytes are detected by object identity, which holds for sidecar-cached source loads and for documents sharing chunk buffers; a byte-equal but distinct buffer just rebuilds that resource.

Implementation

void adoptUnchanged(ResourceRealizer previous) {
  const equality = DeepCollectionEquality();
  final oldResources = _encodedResources(previous.document);
  final newResources = _encodedResources(document);

  bool payloadUnchanged(LocalId id) {
    final old = previous.document.payloads[id];
    final current = document.payloads[id];
    if (old == null || current == null) return false;
    return old.encoding == current.encoding &&
        old.layout == current.layout &&
        old.format == current.format &&
        old.width == current.width &&
        old.height == current.height &&
        old.length == current.length &&
        identical(old.bytes, current.bytes);
  }

  bool specUnchanged(LocalId id) =>
      equality.equals(oldResources[id], newResources[id]);

  final adoptedTextures = <LocalId>{};
  for (final entry in document.resources.entries) {
    final id = entry.key;
    final resource = entry.value;
    if (!specUnchanged(id)) continue;
    switch (resource) {
      case GeometryResource():
        if (resource.vertices case final vertices?
            when !payloadUnchanged(vertices)) {
          continue;
        }
        if (resource.indices case final indices?
            when !payloadUnchanged(indices)) {
          continue;
        }
        final geometry = previous._geometries[id];
        if (geometry != null) _geometries[id] = geometry;
      case TextureResource():
        if (resource.payload case final payload?
            when !payloadUnchanged(payload)) {
          continue;
        }
        final texture = previous._textures[id];
        if (texture != null) {
          _textures[id] = texture;
          adoptedTextures.add(id);
        }
      case EnvironmentResource():
        if (resource.environment case PayloadEnvironment(
          :final payload,
        ) when !payloadUnchanged(payload)) {
          continue;
        }
        final environment = previous._environments[id];
        if (environment != null) _environments[id] = environment;
      default:
        break;
    }
  }
  // A material realizes against its referenced textures, so it carries over
  // only when they all did.
  for (final entry in document.resources.entries) {
    final id = entry.key;
    final resource = entry.value;
    if (resource is! MaterialResource || !specUnchanged(id)) continue;
    final textureRefs = [
      for (final value in resource.properties.values)
        if (value case ResourceRefValue(:final id)) id,
    ];
    if (!textureRefs.every(adoptedTextures.contains)) continue;
    final material = previous._materials[id];
    if (material != null) _materials[id] = material;
  }
}