Editor/Gpu/FlowGpuDoubleBlock.cs
// C# adaptation of FlowHMR's MMDoubleStreamBlock. See THIRD_PARTY_NOTICES.md.
using Sandbox;
using HumanoidMocapX.TensorInference;

namespace HumanoidMocapX.Inference;

internal sealed class FlowGpuDoubleBlock
{
    readonly Branch motion, video;

    internal FlowGpuDoubleBlock(FlowGpuStorage storage, FlowWeights weights, int index, int width)
    {
        var name = "double_blocks." + index;
        motion = new(storage, weights, name + ".motion", width);
        video = new(storage, weights, name + ".text", width);
    }

    internal void Run(FlowGpuWorkspace workspace, GpuBuffer<float> features, GpuBuffer<float> adapter)
    {
        motion.Prepare(workspace, features, adapter, workspace.MotionGates, 0);
        video.Prepare(workspace, features, adapter, workspace.VideoGates, workspace.StreamElements);
        workspace.Attend(true);
        motion.Finish(workspace, features, workspace.MotionGates, 0);
        video.Finish(workspace, features, workspace.VideoGates, workspace.StreamElements);
    }

    sealed class Branch
    {
        readonly FlowGpuLinear modulation, qkv, projection, first, second;
        readonly GpuBuffer<float> queryScale, keyScale;

        internal Branch(FlowGpuStorage storage, FlowWeights weights, string name, int width)
        {
            modulation = new(storage, weights, name + "_mod.linear", width, 6 * width);
            qkv = new(storage, weights, name + "_qkv", width, 3 * width);
            projection = new(storage, weights, name + "_out_proj", width, width);
            first = new(storage, weights, name + "_mlp.fc1", width, 4 * width);
            second = new(storage, weights, name + "_mlp.fc2", 4 * width, width);
            queryScale = storage.Weight(weights, name + "_q_norm.weight", FlowGpuWorkspace.HeadWidth);
            keyScale = storage.Weight(weights, name + "_k_norm.weight", FlowGpuWorkspace.HeadWidth);
        }

        internal void Prepare(FlowGpuWorkspace w, GpuBuffer<float> features, GpuBuffer<float> adapter,
            GpuBuffer<float> gates, int offset)
        {
            var k = w.Kernels; var rows = w.Frames; var width = w.Width;
            features.CopyTo(w.Slice, offset, 0, w.StreamElements);
            modulation.Run(k, adapter, gates);
            k.Normalize(w.Slice, null, null, w.Normalized, rows, width, 1e-6f);
            k.Modulate(w.Normalized, gates, w.Modified, rows, width, 0, width);
            qkv.Run(k, w.Modified, w.Packed, rows);
            w.PrepareQkv(rows, 3 * width, offset, queryScale, keyScale);
        }

        internal void Finish(FlowGpuWorkspace w, GpuBuffer<float> features, GpuBuffer<float> gates, int offset)
        {
            var k = w.Kernels; var rows = w.Frames; var width = w.Width;
            features.CopyTo(w.Slice, offset, 0, w.StreamElements);
            w.Attended.CopyTo(w.Component, offset, 0, w.StreamElements);
            projection.Run(k, w.Component, w.Projected, rows);
            k.GatedAdd(w.Slice, w.Projected, gates, w.Next, rows, width, 2 * width);
            w.Next.CopyTo(w.Slice, w.StreamElements);
            k.Normalize(w.Slice, null, null, w.Normalized, rows, width, 1e-6f);
            k.Modulate(w.Normalized, gates, w.Modified, rows, width, 3 * width, 4 * width);
            first.Run(k, w.Modified, w.Hidden, rows);
            k.Activate(w.Hidden, w.Activated, 4 * w.StreamElements, TensorActivation.Gelu);
            second.Run(k, w.Activated, w.Projected, rows);
            k.GatedAdd(w.Slice, w.Projected, gates, w.Next, rows, width, 5 * width);
            w.Next.CopyTo(features, 0, offset, w.StreamElements);
        }
    }
}