uploadSplats method

void uploadSplats(
  1. Uint8List buffer
)

Uploads raw 128-byte-per-splat data into the flutter_gpu atlas.

Implementation

void uploadSplats(Uint8List buffer) {
  assert(
    buffer.length % GsConst.bytesPerSplat == 0,
    'Splat buffer must be multiple of ${GsConst.bytesPerSplat} bytes',
  );

  final count = buffer.length ~/ GsConst.bytesPerSplat;
  final height = ((_floatTexelsPerSplat * count) / _floatAtlasWidth).ceil();
  final sidecarHeight =
      (count + GsConst.splatsPerRow - 1) ~/ GsConst.splatsPerRow;
  // The SH texture is 12 texels wide per splat (512 * 12 = 6144 columns) so
  // every texture shares the same one-row-per-512-splats height. Packing 12
  // texels per splat linearly into a 512-wide texture instead would grow 12x
  // taller and blow Metal's 16384 dimension cap at ~700K splats.
  final shHeight = sidecarHeight;
  if (sidecarHeight > _maxTextureDimension) {
    throw ArgumentError(
      'Splat count $count exceeds the maximum supported '
      '${_maxTextureDimension * GsConst.splatsPerRow} splats '
      '(sidecar texture height $sidecarHeight > $_maxTextureDimension).',
    );
  }
  final neededBytes = _floatAtlasWidth * height * GsConst.bytesPerTexel;
  final scratch = _ensureAtlasScratch(neededBytes);
  final sidecarNeededBytes = sidecarHeight * GsConst.splatsPerRow * 4;
  final quatBytes = _ensureQuatScratch(sidecarNeededBytes);
  final colorBytes = _ensureColorScratch(sidecarNeededBytes);
  final shNeededBytes =
      shHeight * GsConst.splatsPerRow * _shTexelsPerSplat * 4;
  final shBytes = _ensureShScratch(shNeededBytes);

  final srcF32 = Float32List.view(buffer.buffer);
  final srcU8 = Uint8List.view(buffer.buffer);
  final dstF32 = Float32List.view(scratch.buffer);
  final dstU32 = Uint32List.view(scratch.buffer);
  dstU32.fillRange(0, dstU32.length, 0);
  quatBytes.fillRange(0, sidecarNeededBytes, 0);
  colorBytes.fillRange(0, sidecarNeededBytes, 0);
  shBytes.fillRange(0, shNeededBytes, 0);

  const floatsPerSplat = GsConst.bytesPerSplat ~/ 4;
  for (var idx = 0; idx < count; idx++) {
    final x = (idx & GsConst.splatIdxColMask) * _floatTexelsPerSplat;
    final y = idx >> 9;

    final p0 = (y * _floatAtlasWidth + x) * 4;
    final p1 = p0 + 4;

    final fBase = floatsPerSplat * idx;
    final bBase = GsConst.bytesPerSplat * idx;

    dstF32[p0] = srcF32[fBase];
    dstF32[p0 + 1] = srcF32[fBase + 1];
    dstF32[p0 + 2] = srcF32[fBase + 2];
    dstF32[p0 + 3] = 0;

    dstF32[p1] = srcF32[fBase + 3];
    dstF32[p1 + 1] = srcF32[fBase + 4];
    dstF32[p1 + 2] = srcF32[fBase + 5];
    dstF32[p1 + 3] = 0;

    final sidecarBase = ((idx >> 9) * GsConst.splatsPerRow +
            (idx & GsConst.splatIdxColMask)) *
        4;
    quatBytes[sidecarBase] = srcU8[bBase + GsConst.quatOffset];
    quatBytes[sidecarBase + 1] = srcU8[bBase + GsConst.quatOffset + 1];
    quatBytes[sidecarBase + 2] = srcU8[bBase + GsConst.quatOffset + 2];
    quatBytes[sidecarBase + 3] = srcU8[bBase + GsConst.quatOffset + 3];

    final colorBase = sidecarBase;
    colorBytes[colorBase] = srcU8[bBase + GsConst.colorOffset];
    colorBytes[colorBase + 1] = srcU8[bBase + GsConst.colorOffset + 1];
    colorBytes[colorBase + 2] = srcU8[bBase + GsConst.colorOffset + 2];
    colorBytes[colorBase + 3] = srcU8[bBase + GsConst.colorOffset + 3];

    final shSource = bBase + GsConst.shOffset;
    final shTarget = idx * _shTexelsPerSplat * 4;
    shBytes.setRange(
      shTarget,
      shTarget + GsConst.shPackedWords * 4,
      srcU8,
      shSource,
    );
  }

  if (atlas == null || _atlasHeight != height) {
    atlas = gpu.gpuContext.createTexture(
      gpu.StorageMode.hostVisible,
      _floatAtlasWidth,
      height,
      format: gpu.PixelFormat.r32g32b32a32Float,
      enableRenderTargetUsage: false,
    );
    _atlasHeight = height;
  }

  atlas!.overwrite(scratch.buffer.asByteData(0, neededBytes));

  if (quatTexture == null || _sidecarHeight != sidecarHeight) {
    quatTexture = gpu.gpuContext.createTexture(
      gpu.StorageMode.hostVisible,
      GsConst.splatsPerRow,
      sidecarHeight,
      enableRenderTargetUsage: false,
    );
  }
  quatTexture!.overwrite(quatBytes.buffer.asByteData(0, sidecarNeededBytes));

  if (colorTexture == null || _sidecarHeight != sidecarHeight) {
    colorTexture = gpu.gpuContext.createTexture(
      gpu.StorageMode.hostVisible,
      GsConst.splatsPerRow,
      sidecarHeight,
      enableRenderTargetUsage: false,
    );
  }
  colorTexture!
      .overwrite(colorBytes.buffer.asByteData(0, sidecarNeededBytes));

  _sidecarHeight = sidecarHeight;

  if (shTexture == null || _shHeight != shHeight) {
    shTexture = gpu.gpuContext.createTexture(
      gpu.StorageMode.hostVisible,
      GsConst.splatsPerRow * _shTexelsPerSplat,
      shHeight,
      enableRenderTargetUsage: false,
    );
    _shHeight = shHeight;
  }
  shTexture!.overwrite(shBytes.buffer.asByteData(0, shNeededBytes));

  splatCount = count;
}