Tensor/ConvolutionShape.cs
using System;
namespace HumanoidMocapX.TensorInference;
/// <summary>Dimensions for one square, ungrouped 2D convolution. Images use
/// [height,width,channels] storage and weights use [outputs,channels,kernel,kernel].</summary>
public readonly struct ConvolutionShape
{
public int Height { get; }
public int Width { get; }
public int Channels { get; }
public int Outputs { get; }
public int Kernel { get; }
public int Stride { get; }
public int Padding { get; }
public int OutputHeight { get; }
public int OutputWidth { get; }
public int InputElements { get; }
public int WeightElements { get; }
public int OutputElements { get; }
public int KernelElements { get; }
public int OutputPixels => OutputHeight * OutputWidth;
const int MaximumTileDimension = 65535 * 16;
/// <summary>Validates positive image/filter dimensions, nonnegative zero padding
/// and the GPU buffer/dispatch limits. Rejects kernels leaving no output pixels.</summary>
public ConvolutionShape(int height, int width, int channels, int outputs, int kernel, int stride = 1, int padding = 0)
{
if (height < 1 || width < 1 || channels < 1 || outputs < 1 || kernel < 1 || stride < 1 || padding < 0)
throw new ArgumentException("Convolution dimensions and stride must be positive; padding must be nonnegative.");
var paddedHeight = (long)height + 2L * padding;
var paddedWidth = (long)width + 2L * padding;
if (paddedHeight > uint.MaxValue || paddedWidth > uint.MaxValue)
throw new ArgumentException("Padded convolution dimensions exceed GPU coordinate limits.");
if (paddedHeight < kernel || paddedWidth < kernel)
throw new ArgumentException("Convolution kernel is larger than the padded image.");
var outputHeight = (paddedHeight - kernel) / stride + 1;
var outputWidth = (paddedWidth - kernel) / stride + 1;
if (outputHeight > MaximumTileDimension || outputWidth > MaximumTileDimension
|| outputHeight * outputWidth > MaximumTileDimension || outputs > MaximumTileDimension)
throw new ArgumentException("Convolution output exceeds GPU dispatch limits.");
InputElements = Elements(height, width, channels);
KernelElements = Elements(kernel, kernel, channels);
WeightElements = Elements(KernelElements, outputs, 1);
OutputElements = Elements((int)outputHeight, (int)outputWidth, outputs);
Height = height; Width = width; Channels = channels; Outputs = outputs;
Kernel = kernel; Stride = stride; Padding = padding;
OutputHeight = (int)outputHeight; OutputWidth = (int)outputWidth;
}
static int Elements(int height, int width, int channels)
{
if ((long)height * width > int.MaxValue / channels)
throw new ArgumentException("Convolution tensor exceeds the supported buffer index range.");
return height * width * channels;
}
}