Editor/Gpu/FlowGpuWorkspace.cs
using System;
using Sandbox;
using HumanoidMocapX.TensorInference;
namespace HumanoidMocapX.Inference;
/// <summary>Reuses one GPU workspace across the refiner and all eighteen denoiser blocks.</summary>
internal sealed class FlowGpuWorkspace
{
internal readonly TensorKernels Kernels = new();
internal readonly int Frames, Width, Heads, StreamElements;
internal const int HeadWidth = 64;
internal readonly GpuBuffer<float> Normalized, Modified, Packed, Hidden, Activated, Joined, Projected, Next;
internal readonly GpuBuffer<float> Slice, Component, Query, Key, Value, Attended, MotionGates, VideoGates;
internal readonly GpuBuffer<float> ConditionHidden, ConditionActivated;
readonly GpuBuffer<float> rotary;
readonly GpuBuffer<uint> fullRanges, jointRanges;
internal FlowGpuWorkspace(FlowGpuStorage storage, int frames, int width, int heads)
{
Frames = frames; Width = width; Heads = heads; StreamElements = checked(frames * width);
var both = checked(2 * StreamElements);
Normalized = storage.Create<float>(both); Modified = storage.Create<float>(both);
Packed = storage.Create<float>(7 * both); Hidden = storage.Create<float>(4 * both);
Activated = storage.Create<float>(4 * both); Joined = storage.Create<float>(5 * both);
Projected = storage.Create<float>(both); Next = storage.Create<float>(both);
Slice = storage.Create<float>(both); Component = storage.Create<float>(both);
Query = storage.Create<float>(both); Key = storage.Create<float>(both);
Value = storage.Create<float>(both); Attended = storage.Create<float>(both);
MotionGates = storage.Create<float>(6 * width); VideoGates = storage.Create<float>(6 * width);
ConditionHidden = storage.Create<float>(width); ConditionActivated = storage.Create<float>(width);
var factors = new float[frames * HeadWidth];
new FlowRotary(frames, width, heads).CopyFactors(factors);
rotary = storage.Upload<float>(factors);
var ranges = new uint[8 * frames];
new FlowAttention(frames, width, heads, true).CopyKeyRanges(ranges);
jointRanges = storage.Upload<uint>(ranges);
new FlowAttention(frames, width, heads, false).CopyKeyRanges(ranges.AsSpan(0, 4 * frames));
fullRanges = storage.Upload<uint>(ranges.AsSpan(0, 4 * frames));
}
internal void PrepareQkv(int rows, int stride, int offset, GpuBuffer<float> queryScale,
GpuBuffer<float> keyScale, GpuBuffer<float> queryBias = null, GpuBuffer<float> keyBias = null, bool rms = true)
{
var epsilon = rms ? 1e-6f : 1e-5f;
Kernels.CopyColumns(Packed, Component, rows, stride, 0, Width);
Kernels.Normalize(Component, queryScale, queryBias, Normalized, rows * Heads, HeadWidth, epsilon, rms);
Normalized.CopyTo(Query, 0, offset, rows * Width);
Kernels.CopyColumns(Packed, Component, rows, stride, Width, Width);
Kernels.Normalize(Component, keyScale, keyBias, Normalized, rows * Heads, HeadWidth, epsilon, rms);
Normalized.CopyTo(Key, 0, offset, rows * Width);
Kernels.CopyColumns(Packed, Component, rows, stride, 2 * Width, Width);
Component.CopyTo(Value, 0, offset, rows * Width);
}
internal void Attend(bool joint)
{
var rows = joint ? 2 * Frames : Frames;
if (joint)
{
Kernels.Rotary(Query, rotary, rows, Width, HeadWidth, Frames);
Kernels.Rotary(Key, rotary, rows, Width, HeadWidth, Frames);
}
Kernels.Attention(Query, Key, Value, joint ? jointRanges : fullRanges, Attended, rows, Heads);
}
}