Cores/Nds/Compute/ComputeRenderer3D.Gpu.cs
namespace Emulotl.Nds;
public sealed partial class ComputeRenderer3D
{
private const int MaxVariants = 256;
private const int BinStride = 2048 / 32;
private const int CoarseBinStride = BinStride / 32;
private const int CoarseTileCountX = 8;
private int _screenWidth, _screenHeight;
private int _tileSize;
private int _coarseTileCountY, _coarseTileArea, _coarseTileW, _coarseTileH;
private int _clearCoarseBinMaskLocalSize;
private int _tilesPerLine, _tileLines;
private int _maxWorkTiles, _maxYSpanIndices;
private int _numVariants = 1;
private readonly uint[] _metaData = new uint[148];
private const int BufRing = 3;
private int _bufIdx;
private int _texBufIdx;
private readonly GpuBuffer<uint>[] _metaBufferR = new GpuBuffer<uint>[BufRing];
private readonly GpuBuffer<RenderPolygonGpu>[] _renderPolygonBufferR = new GpuBuffer<RenderPolygonGpu>[BufRing];
private readonly GpuBuffer<SpanSetupY>[] _ySpanSetupBufferR = new GpuBuffer<SpanSetupY>[BufRing];
private readonly GpuBuffer<SetupIndices>[] _ySpanIndicesBufferR = new GpuBuffer<SetupIndices>[BufRing];
private GpuBuffer<uint> _metaBuffer;
private GpuBuffer<RenderPolygonGpu> _renderPolygonBuffer;
private GpuBuffer<SpanSetupY> _ySpanSetupBuffer;
private GpuBuffer<SpanSetupX> _xSpanSetupBuffer;
private GpuBuffer<SetupIndices> _ySpanIndicesBuffer;
private GpuBuffer<uint> _binResultBuffer;
private GpuBuffer<uint> _workDescBuffer;
private readonly GpuBuffer<uint>[] _tileMemory = new GpuBuffer<uint>[3];
private GpuBuffer<uint> _finalTileBuffer;
private GpuBuffer<GpuBuffer.IndirectDispatchArguments> _indirectArgs;
private Texture _outputTex;
private readonly GpuBuffer<uint>[] _texBufSmallR = new GpuBuffer<uint>[BufRing];
private readonly GpuBuffer<uint>[] _texBufLargeR = new GpuBuffer<uint>[BufRing];
private GpuBuffer<uint> _texBufSmall;
private GpuBuffer<uint> _texBufLarge;
private ComputeShader _csClearCoarseBinMask;
private ComputeShader _csClearIndirectWorkCount;
private ComputeShader _csInterpSpans;
private ComputeShader _csBinCombined;
private ComputeShader _csCalcWorkOffset;
private ComputeShader _csSortWork;
private ComputeShader _csRaster;
private ComputeShader _csDepthBlend;
private ComputeShader _csFinalPass;
private bool _gpuReady;
private void InitGpuResources()
{
ComputeDimensions();
for ( int i = 0; i < BufRing; i++ )
{
_metaBufferR[i] = new GpuBuffer<uint>( _metaData.Length, GpuBuffer.UsageFlags.Structured );
_renderPolygonBufferR[i] = new GpuBuffer<RenderPolygonGpu>( 2048, GpuBuffer.UsageFlags.Structured );
_ySpanSetupBufferR[i] = new GpuBuffer<SpanSetupY>( MaxYSpanSetups, GpuBuffer.UsageFlags.Structured );
_ySpanIndicesBufferR[i] = new GpuBuffer<SetupIndices>( _maxYSpanIndices, GpuBuffer.UsageFlags.Structured );
}
_metaBuffer = _metaBufferR[0];
_renderPolygonBuffer = _renderPolygonBufferR[0];
_ySpanSetupBuffer = _ySpanSetupBufferR[0];
_ySpanIndicesBuffer = _ySpanIndicesBufferR[0];
_xSpanSetupBuffer = new GpuBuffer<SpanSetupX>( _maxYSpanIndices, GpuBuffer.UsageFlags.Structured );
int binResultUints = BinResultHeaderUints
+ _tilesPerLine * _tileLines * CoarseBinStride
+ _tilesPerLine * _tileLines * BinStride
+ _tilesPerLine * _tileLines * BinStride;
_binResultBuffer = new GpuBuffer<uint>( binResultUints, GpuBuffer.UsageFlags.Structured );
_workDescBuffer = new GpuBuffer<uint>( _maxWorkTiles * 2 * 2, GpuBuffer.UsageFlags.Structured );
int tileUints = _tileSize * _tileSize * _maxWorkTiles;
for ( int i = 0; i < 3; i++ )
_tileMemory[i] = new GpuBuffer<uint>( tileUints, GpuBuffer.UsageFlags.Structured );
_finalTileBuffer = new GpuBuffer<uint>( 3 * 2 * _screenWidth * _screenHeight, GpuBuffer.UsageFlags.Structured );
_indirectArgs = new GpuBuffer<GpuBuffer.IndirectDispatchArguments>(
MaxVariants + 1, GpuBuffer.UsageFlags.Structured | GpuBuffer.UsageFlags.IndirectDrawArguments );
_outputTex = Texture.CreateRenderTarget()
.WithSize( _screenWidth, _screenHeight )
.WithFormat( ImageFormat.RGBA8888 )
.WithUAVBinding()
.Create();
for ( int i = 0; i < BufRing; i++ )
{
_texBufSmallR[i] = new GpuBuffer<uint>( SmallArea * SmallLayers, GpuBuffer.UsageFlags.Structured );
_texBufLargeR[i] = new GpuBuffer<uint>( LargeArea * LargeLayers, GpuBuffer.UsageFlags.Structured );
}
_texBufSmall = _texBufSmallR[0];
_texBufLarge = _texBufLargeR[0];
_texCache.Clear();
_freeSmall.Clear();
_freeLarge.Clear();
_numLayersSmall = 0;
_numLayersLarge = 0;
_texBufIdx = 0;
_cacheBuiltVersion = ulong.MaxValue;
_texturesChanged = true;
_csClearCoarseBinMask = new ComputeShader( "shaders/nds3d_clearcoarsebinmask.shader" );
_csClearIndirectWorkCount = new ComputeShader( "shaders/nds3d_clearindirectworkcount.shader" );
_csInterpSpans = new ComputeShader( "shaders/nds3d_interpspans.shader" );
_csBinCombined = new ComputeShader( "shaders/nds3d_bincombined.shader" );
_csCalcWorkOffset = new ComputeShader( "shaders/nds3d_calcworkoffset.shader" );
_csSortWork = new ComputeShader( "shaders/nds3d_sortwork.shader" );
_csRaster = new ComputeShader( "shaders/nds3d_raster.shader" );
_csDepthBlend = new ComputeShader( "shaders/nds3d_depthblend.shader" );
_csFinalPass = new ComputeShader( "shaders/nds3d_finalpass.shader" );
_gpuReady = true;
}
private static int BinResultHeaderUints => MaxVariants * 4 + MaxVariants + 4;
private static int GetMSBit( int v )
{
if ( v <= 0 ) return 0;
int b = 1;
while ( (b << 1) <= v ) b <<= 1;
return b;
}
private void ComputeDimensions()
{
int scale = _scaleFactor;
_screenWidth = 256 * scale;
_screenHeight = 192 * scale;
int tileScale = GetMSBit( 2 * scale / 9 );
tileScale <<= 1;
if ( tileScale == 0 ) tileScale = 1;
_tileSize = Math.Min( 8 * tileScale, 32 );
_coarseTileCountY = _tileSize < 32 ? 4 : 6;
_clearCoarseBinMaskLocalSize = _tileSize < 32 ? 64 : 48;
_coarseTileArea = CoarseTileCountX * _coarseTileCountY;
_coarseTileW = CoarseTileCountX * _tileSize;
_coarseTileH = _coarseTileCountY * _tileSize;
_tilesPerLine = _screenWidth / _tileSize;
_tileLines = _screenHeight / _tileSize;
_maxWorkTiles = _tilesPerLine * _tileLines * 16;
_maxYSpanIndices = 64 * 2048 * scale;
}
private void FillMeta()
{
_metaData[0] = _gpu.RenderNumPolygons;
_metaData[1] = (uint)_numVariants;
_metaData[2] = _gpu.RenderAlphaRef;
_metaData[3] = _gpu.RenderDispCnt;
uint cr = (_gpu.RenderClearAttr1 << 1) & 0x3E; if ( cr != 0 ) cr++;
uint cg = (_gpu.RenderClearAttr1 >> 4) & 0x3E; if ( cg != 0 ) cg++;
uint cb = (_gpu.RenderClearAttr1 >> 9) & 0x3E; if ( cb != 0 ) cb++;
uint ca = (_gpu.RenderClearAttr1 >> 16) & 0x1F;
_metaData[140] = cr | (cg << 8) | (cb << 16) | (ca << 24);
_metaData[141] = ((_gpu.RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF;
_metaData[142] = _gpu.RenderClearAttr1 & 0x3F008000;
uint xoff = (_gpu.RenderClearAttr2 >> 16) & 0xFF;
uint yoff = (_gpu.RenderClearAttr2 >> 24) & 0xFF;
_metaData[146] = BitConverter.SingleToUInt32Bits( xoff / 256.0f );
_metaData[147] = BitConverter.SingleToUInt32Bits( yoff / 256.0f );
for ( int i = 0; i < 32; i++ )
{
uint color = _gpu.RenderToonTable[i];
uint r = (color << 1) & 0x3E;
uint g = (color >> 4) & 0x3E;
uint b = (color >> 9) & 0x3E;
if ( r != 0 ) r++;
if ( g != 0 ) g++;
if ( b != 0 ) b++;
_metaData[4 + i * 4 + 0] = r | (g << 8) | (b << 16);
}
for ( int i = 0; i < 34; i++ )
_metaData[4 + i * 4 + 1] = _gpu.RenderFogDensityTable[i];
for ( int i = 0; i < 8; i++ )
{
uint color = _gpu.RenderEdgeTable[i];
uint r = (color << 1) & 0x3E;
uint g = (color >> 4) & 0x3E;
uint b = (color >> 9) & 0x3E;
if ( r != 0 ) r++;
if ( g != 0 ) g++;
if ( b != 0 ) b++;
_metaData[4 + i * 4 + 2] = r | (g << 8) | (b << 16);
}
_metaData[143] = _gpu.RenderFogOffset;
_metaData[144] = _gpu.RenderFogShift;
uint fr = (_gpu.RenderFogColor << 1) & 0x3E; if ( fr != 0 ) fr++;
uint fg = (_gpu.RenderFogColor >> 4) & 0x3E; if ( fg != 0 ) fg++;
uint fb = (_gpu.RenderFogColor >> 9) & 0x3E; if ( fb != 0 ) fb++;
uint fa = (_gpu.RenderFogColor >> 16) & 0x1F;
_metaData[145] = fr | (fg << 8) | (fb << 16) | (fa << 24);
}
}