Editor/Gpu/DinoGpuEncoder.cs
// C# adaptation of the Omega and SAM DINO encoders. See LICENSE-VGGT, LICENSE-DINOV3 and THIRD_PARTY_NOTICES.md.
// Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.
using System;
using System.Threading;
using Sandbox;
using HumanoidMocapX.TensorInference;
namespace HumanoidMocapX.Inference;
/// <summary>Shared DINO encoder for Omega images and SAM body/hand crops.
/// The caller owns storage and submits Run inside a graphics batch.</summary>
internal sealed class DinoGpuEncoder
{
readonly DinoShape shape;
readonly OmegaGpuBlock[] blocks;
readonly ConvolutionShape projection;
readonly GpuBuffer<float> features, patches, weight, bias, classToken, registers, norm, normBias, positions;
readonly GpuBuffer<uint> ranges;
internal DinoGpuEncoder(FlowGpuStorage storage, FlowWeights weights, DinoShape shape)
{
if (shape.Channels == 0 || shape.Channels / shape.Heads != 64) throw new ArgumentException("GPU DINO requires 64-channel heads.");
this.shape = shape; var width = shape.Channels; var name = shape.Name;
blocks = new OmegaGpuBlock[shape.Depth];
features = storage.Create<float>(shape.Tokens * width); patches = storage.Create<float>(shape.Patches * width);
weight = storage.Weight(weights, name + ".patch_embed.proj.weight", 16, 16, 3, width);
bias = storage.Weight(weights, name + ".patch_embed.proj.bias", width);
classToken = storage.Weight(weights, name + ".cls_token", width, 1, 1); registers = storage.Weight(weights, name + ".storage_tokens", width, 4, 1);
norm = storage.Weight(weights, name + ".norm.weight", width); normBias = storage.Weight(weights, name + ".norm.bias", width);
positions = storage.Upload<float>(OmegaPosition.Factors(shape.Height / 16, shape.Width / 16, DinoShape.PrefixTokens,
weights.Get(name + ".rope_embed.periods", width / shape.Heads / 4).Data, shape.IsSam));
var keys = new uint[shape.Tokens * 4];
for (var row = 0; row < shape.Tokens; row++) keys[row * 4 + 1] = (uint)shape.Tokens;
ranges = storage.Upload<uint>(keys);
for (var i = 0; i < blocks.Length; i++) blocks[i] = new(storage, weights, name + ".blocks." + i, width, shape.Heads, false, shape.IsSam);
projection = new(shape.Height, shape.Width, 3, width, 16, 16);
}
/// <summary>Consumes ImageNet-normalized HWC RGB and writes [patches,channels].
/// Output may be consumed by a later GPU stage without readback. The workspace
/// must fit all tokens and have SwiGLU storage for SAM. Discard output on failure.</summary>
internal void Run(OmegaGpuWorkspace workspace, GpuBuffer<float> image, GpuBuffer<float> output, CancellationToken cancellation = default)
{
cancellation.ThrowIfCancellationRequested();
if (image is null || !image.IsValid || image.ElementCount != shape.ImageElements
|| output is null || !output.IsValid || output.ElementCount != shape.Patches * shape.Channels
|| ReferenceEquals(image, output)) throw new ArgumentException("DINO GPU images/features have invalid dimensions or overlap.");
var width = shape.Channels; var kernels = workspace.Kernels;
classToken.CopyTo(features, 0, 0, width); registers.CopyTo(features, 0, width, 4 * width);
kernels.Convolve(image, weight, bias, patches, projection);
patches.CopyTo(features, 0, DinoShape.PrefixTokens * width, shape.Patches * width);
foreach (var block in blocks)
{ cancellation.ThrowIfCancellationRequested(); block.Run(workspace, features, ranges, shape.Tokens, positions, shape.Tokens, sharedKeyRanges: true); }
kernels.Normalize(features, norm, normBias, workspace.Normalized, shape.Tokens, width);
workspace.Normalized.CopyTo(output, DinoShape.PrefixTokens * width, 0, shape.Patches * width);
}
}