Editor/Gpu/OmegaGpuCameraHead.cs
// C# adaptation of VGGT-Omega's camera head. See LICENSE-VGGT and THIRD_PARTY_NOTICES.md.
// Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
using System;
using System.Threading;
using System.Threading.Tasks;
using Sandbox;
using HumanoidMocapX.TensorInference;
namespace HumanoidMocapX.Inference;
internal sealed class OmegaGpuCameraHead : IDisposable
{
readonly FlowGpuStorage storage = new();
readonly OmegaGpuWorkspace workspace;
readonly OmegaGpuBlock[] blocks = new OmegaGpuBlock[OmegaCameraHead.Blocks];
readonly FlowGpuLinear first, second;
readonly GpuBuffer<float> tokenWeight, tokenBias, outputWeight, outputBias, input, features, pooled, normalized, hidden, activated, poses;
readonly GpuBuffer<uint> ranges;
readonly TensorBatch batch;
readonly Action record;
readonly int frames, width, tokens;
GpuBuffer<float> source;
CancellationToken cancellation;
bool running, disposed;
internal OmegaGpuCameraHead(FlowWeights weights, int frames, int width = 2048, int heads = 16)
{
ThreadSafe.AssertIsMainThread();
if (frames < 1 || frames > FlowMotion.MaximumFrames / OmegaCameraHead.PrefixTokens || heads < 1
|| width % heads != 0 || width / heads is not (64 or 128)) throw new ArgumentException("Invalid GPU Omega camera dimensions.");
this.frames = frames; this.width = width; tokens = frames * OmegaCameraHead.PrefixTokens;
try
{
workspace = new(storage, tokens, width);
input = storage.Create<float>(checked(tokens * width)); features = storage.Create<float>(tokens * width);
pooled = storage.Create<float>(frames * width); normalized = storage.Create<float>(frames * width);
hidden = storage.Create<float>(frames * width / 2); activated = storage.Create<float>(frames * width / 2);
poses = storage.Create<float>(frames * 9);
tokenWeight = storage.Weight(weights, "camera_head.token_norm.weight", width); tokenBias = storage.Weight(weights, "camera_head.token_norm.bias", width);
outputWeight = storage.Weight(weights, "camera_head.trunk_norm.weight", width); outputBias = storage.Weight(weights, "camera_head.trunk_norm.bias", width);
for (var index = 0; index < blocks.Length; index++) blocks[index] = new(storage, weights, "camera_head.trunk." + index, width, heads, false);
first = new(storage, weights, "camera_head.camera_branch.0", width, width / 2);
second = new(storage, weights, "camera_head.camera_branch.2", width / 2, 9);
var keyRanges = new uint[tokens * 4];
for (var token = 0; token < tokens; token++) keyRanges[token * 4 + 1] = (uint)tokens;
ranges = storage.Upload<uint>(keyRanges);
source = input; record = Record; batch = new();
}
catch { storage.Dispose(); throw; }
}
/// <summary>Consumes GPU-resident final tokens. The caller retains the source
/// until readback completes, including after cancellation.</summary>
internal async Task RunGpuAsync(GpuBuffer<float> tokensInput, Memory<float> output, CancellationToken token = default)
{
ThreadSafe.AssertIsMainThread(); ObjectDisposedException.ThrowIf(disposed, this);
if (running) throw new InvalidOperationException("Await the current Omega camera prediction before starting another.");
token.ThrowIfCancellationRequested();
if (tokensInput is null || !tokensInput.IsValid || tokensInput.ElementCount != tokens * width || output.Length != frames * 9)
throw new ArgumentException("GPU Omega camera tensors have incorrect dimensions.");
running = true; cancellation = token; source = tokensInput;
try
{
await batch.RunAsync(record, poses, output, token);
OmegaCameraHead.Activate(output.Span);
}
finally { await GameTask.MainThread(); source = input; running = false; cancellation = default; }
}
/// <summary>Writes [frames,9] poses from [frames,17,width] final aggregator tokens.
/// One call at a time, on the main thread. Discard partial output after failure.</summary>
internal async Task RunAsync(ReadOnlyMemory<float> tokensInput, Memory<float> output, CancellationToken token = default)
{
ThreadSafe.AssertIsMainThread(); ObjectDisposedException.ThrowIf(disposed, this);
if (running) throw new InvalidOperationException("Await the current Omega camera prediction before starting another.");
token.ThrowIfCancellationRequested();
if (tokensInput.Length != tokens * width || output.Length != frames * 9)
throw new ArgumentException("GPU Omega camera tensors have incorrect dimensions.");
foreach (var value in tokensInput.Span)
if (!float.IsFinite(value)) throw new ArgumentException("Non-finite Omega camera feature.", nameof(tokensInput));
running = true; cancellation = token;
try
{
input.SetData<float>(tokensInput.Span);
await batch.RunAsync(record, poses, output, token);
OmegaCameraHead.Activate(output.Span);
}
finally { await GameTask.MainThread(); running = false; cancellation = default; }
}
void Record()
{
var kernels = workspace.Kernels;
kernels.Normalize(source, tokenWeight, tokenBias, features, tokens, width);
foreach (var block in blocks) { cancellation.ThrowIfCancellationRequested(); block.Run(workspace, features, ranges, tokens); }
for (var frame = 0; frame < frames; frame++) features.CopyTo(pooled, frame * OmegaCameraHead.PrefixTokens * width, frame * width, width);
kernels.Normalize(pooled, outputWeight, outputBias, normalized, frames, width);
first.Run(kernels, normalized, hidden, frames);
kernels.Activate(hidden, activated, frames * width / 2, TensorActivation.GeluErf);
second.Run(kernels, activated, poses, frames);
}
public void Dispose()
{
ThreadSafe.AssertIsMainThread();
if (disposed) return;
if (running) throw new InvalidOperationException("Await Omega camera readback before disposal.");
disposed = true; batch.Dispose(); storage.Dispose();
}
}