Tensor/TensorKernels.Convolution.cs
using System;
using Sandbox;
namespace HumanoidMocapX.TensorInference;
public sealed partial class TensorKernels
{
readonly ComputeShader convolution = new("shaders/tensor-inference/convolution/convolution_compute.shader");
/// <summary>Convolves an HWC image with OIHW weights and optional output-channel
/// bias, writing an HWC result. Zero padding, square kernels, one image and no
/// dilation/groups. Rejects default shapes, undersized buffers and output aliasing.</summary>
public void Convolve(GpuBuffer<float> input, GpuBuffer<float> weight, GpuBuffer<float> bias,
GpuBuffer<float> output, ConvolutionShape shape)
{
if (shape.OutputElements == 0) throw new ArgumentException("Construct a valid convolution shape.", nameof(shape));
Require(input, shape.InputElements, nameof(input)); Require(weight, shape.WeightElements, nameof(weight));
Require(output, shape.OutputElements, nameof(output));
if (bias is not null) Require(bias, shape.Outputs, nameof(bias));
if (ReferenceEquals(input, output) || ReferenceEquals(weight, output) || ReferenceEquals(bias, output))
throw new ArgumentException("Convolution output must not overlap its inputs.");
var attributes = convolution.Attributes;
attributes.Set("Input", input); attributes.Set("Weight", weight);
attributes.Set("Bias", bias ?? weight); attributes.Set("Output", output);
attributes.Set("Height", shape.Height); attributes.Set("Width", shape.Width);
attributes.Set("Channels", shape.Channels); attributes.Set("Outputs", shape.Outputs);
attributes.Set("Kernel", shape.Kernel); attributes.Set("Stride", shape.Stride); attributes.Set("Padding", shape.Padding);
attributes.Set("OutputWidth", shape.OutputWidth); attributes.Set("OutputPixels", shape.OutputPixels);
attributes.Set("KernelElements", shape.KernelElements); attributes.Set("HasBias", bias is null ? 0 : 1);
// Same register tiling as the linear kernel: 4x4 outputs per thread.
convolution.Dispatch((shape.Outputs - 1) / LinearValuesPerThread + 1, (shape.OutputPixels - 1) / LinearValuesPerThread + 1, 1);
CompleteWrite(output);
}
}