Cores/Nds/Compute/ComputeRenderer2D.Dispatch.cs
using Sandbox.Rendering;

namespace Emulotl.Nds;

public sealed partial class ComputeRenderer2D
{
	private ComputeShader _csLayerPre;
	private ComputeShader _csSpritePre;
	private ComputeShader _csSprite;
	private ComputeShader _csCompositor;
	private ComputeShader _csCapture;

	private int _slot;

	private void InitShaders()
	{
		_csLayerPre = new ComputeShader( "shaders/nds2d_layerpre.shader" );
		_csSpritePre = new ComputeShader( "shaders/nds2d_spritepre.shader" );
		_csSprite = new ComputeShader( "shaders/nds2d_sprite.shader" );
		_csCompositor = new ComputeShader( "shaders/nds2d_compositor.shader" );
		_csCapture = new ComputeShader( "shaders/nds2d_capture.shader" );
	}

	private void DisposeShaders()
	{
		_csLayerPre = null;
		_csSpritePre = null;
		_csSprite = null;
		_csCompositor = null;
		_csCapture = null;
	}

	private void UploadBuffers( int slot )
	{
		_vramBG.SetData<uint>( _snapVramBG[slot] );
		_vramOBJ.SetData<uint>( _snapVramOBJ[slot] );
		_palBG.SetData<uint>( _snapPalBG[slot] );
		_palOBJ.SetData<uint>( _snapPalOBJ[slot] );
		_bgConfigBuf.SetData<sBGConfig>( _snapLayer[slot] );
		_oamConfigBuf.SetData<sOAM>( _snapOAM[slot] );
		_rotscaleBuf.SetData<Vec4i>( _snapRot[slot] );
		_scanlineBuf.SetData<sScanline>( _snapScan[slot] );

		if ( _snapDispMode[slot] == 2 )
			_vramDisplay.SetData<uint>( _snapVramDisplay[slot] );
	}

	public bool UploadAndBuildCommandList()
	{
		if ( !GpuReady || RenderCommandList == null )
			return false;

		if ( !AcquireReadSlot( out int slot ) )
			return false;

		_slot = slot;
		UploadBuffers( slot );

		CommandList cmd = RenderCommandList;
		cmd.Reset();

		if ( _snapComp[slot].Enable3D != 0 && _render3D != null )
		{
			_render3D.BuildCommandList( cmd );
			if ( _output3D != null )
				cmd.UavBarrier( _output3D );
		}

		for ( int l = 0; l < 4; l++ )
			PrerenderLayer( cmd, l );

		PrerenderSprites( cmd );
		DoRenderSprites( cmd );
		RenderScreen( cmd );

		if ( _num == 0 && _snapCapOn[_slot] )
			DoCapture( cmd );

		return true;
	}

	private void DoCapture( CommandList cmd )
	{
		int s = _slot;
		int width = _snapCapWidth[s];
		int height = _snapCapHeight[s];
		int capsize = _snapCapSize[s];
		int dstblock = _snapCapBank[s];
		int dstoffset = _snapCapOffset[s];
		int layer = capsize == 0 ? ((dstblock << 2) | dstoffset) : dstblock;
		int bufferHeight = capsize == 0 ? 128 : 256;

		cmd.UavBarrier( _captureSrcA );

		if ( _snapCapSrcBEn[s] != 0 && _snapCapFeedback[s] == 0 )
			_captureSrcB.SetData<uint>( _snapCapSrcB[s] );

		cmd.Attributes.Set( "SrcA2D", _captureSrcA );
		cmd.Attributes.Set( "SrcA3D", _output3D ?? _captureSrcA );
		cmd.Attributes.Set( "SrcB", _captureSrcB );
		cmd.Attributes.Set( "CaptureOut128", CaptureOutput128Tex );
		cmd.Attributes.Set( "CaptureOut256", CaptureOutput256Tex );
		cmd.Attributes.Set( "uSrcA3D", _snapCapSrcA3D[s] );
		cmd.Attributes.Set( "uSrcBEnable", _snapCapSrcBEn[s] );
		cmd.Attributes.Set( "uMode", _snapCapMode[s] );
		cmd.Attributes.Set( "uEVA", _snapCapEVA[s] );
		cmd.Attributes.Set( "uEVB", _snapCapEVB[s] );
		cmd.Attributes.Set( "uDstWidth", width );
		cmd.Attributes.Set( "uDstHeight", height );
		cmd.Attributes.Set( "uCapSize", capsize );
		cmd.Attributes.Set( "uDstLayer", layer );
		cmd.Attributes.Set( "uDstOffset", capsize == 0 ? 0 : dstoffset );
		cmd.Attributes.Set( "uBufferHeight", bufferHeight );
		cmd.Attributes.Set( "uScale", _scale );
		cmd.Attributes.Set( "uFeedback", _snapCapFeedback[s] );
		cmd.Attributes.Set( "uSrcBLayer", _snapCapSrcBLayer[s] );
		cmd.Attributes.Set( "uSrcBYOff", _snapCapSrcBYOff[s] );
		cmd.DispatchCompute( _csCapture, width * _scale, height * _scale, 1 );
		cmd.UavBarrier( capsize == 0 ? CaptureOutput128Tex : CaptureOutput256Tex );
	}

	private void PrerenderLayer( CommandList cmd, int layer )
	{
		sBGConfig bg = _snapLayer[_slot][layer];
		if ( bg.Type >= 6 || bg.SizeX <= 0 )
			return;

		cmd.Attributes.Set( "VramBG", _vramBG );
		cmd.Attributes.Set( "PalBG", _palBG );
		cmd.Attributes.Set( "BGConfig", _bgConfigBuf );
		cmd.Attributes.Set( "OutputLayer", _bgLayer[layer] );
		cmd.Attributes.Set( "VRAMMask", _bgVramMask );
		cmd.Attributes.Set( "CurBG", layer );
		cmd.Attributes.Set( "uScale", _scale );
		Texture cap128 = Cap128;
		if ( cap128 != null )
		{
			cmd.Attributes.Set( "CaptureOut128", cap128 );
			cmd.Attributes.Set( "CaptureOut256", Cap256 );
		}
		cmd.DispatchCompute( _csLayerPre, bg.SizeX, bg.SizeY, 1 );
		cmd.UavBarrier( _bgLayer[layer] );
	}

	private void PrerenderSprites( CommandList cmd )
	{
		cmd.Attributes.Set( "VramOBJ", _vramOBJ );
		cmd.Attributes.Set( "PalOBJ", _palOBJ );
		cmd.Attributes.Set( "OAMConfig", _oamConfigBuf );
		cmd.Attributes.Set( "OutputAtlas", _spriteAtlas );
		cmd.Attributes.Set( "VRAMMask", _objVramMask );
		cmd.Attributes.Set( "NumSprites", _snapNumSprites[_slot] );
		cmd.Attributes.Set( "uScale", _scale );
		Texture cap128 = Cap128;
		if ( cap128 != null )
		{
			cmd.Attributes.Set( "CaptureOut128", cap128 );
			cmd.Attributes.Set( "CaptureOut256", Cap256 );
		}
		cmd.DispatchCompute( _csSpritePre, AtlasW, AtlasH, 1 );
		cmd.UavBarrier( _spriteAtlas );
	}

	private void DoRenderSprites( CommandList cmd )
	{
		int ow = ScreenW * _scale;
		int oh = ScreenH * _scale;

		cmd.Attributes.Set( "SpriteAtlas", _spriteAtlas );
		cmd.Attributes.Set( "OAMConfig", _oamConfigBuf );
		cmd.Attributes.Set( "Rotscale", _rotscaleBuf );
		cmd.Attributes.Set( "OBJColor", _objColor );
		cmd.Attributes.Set( "OBJFlags", _objFlags );
		cmd.Attributes.Set( "NumSprites", _snapNumSprites[_slot] );
		cmd.Attributes.Set( "ScaleFactor", _scale );
		cmd.Attributes.Set( "WindowEnable", _snapWindowEnable[_slot] );
		cmd.Attributes.Set( "OutputW", ow );
		cmd.Attributes.Set( "OutputH", oh );
		cmd.DispatchCompute( _csSprite, ow, oh, 1 );
		cmd.UavBarrier( _objColor );
		cmd.UavBarrier( _objFlags );
	}

	private void RenderScreen( CommandList cmd )
	{
		int ow = ScreenW * _scale;
		int oh = ScreenH * _scale;
		sCompositorConfig comp = _snapComp[_slot];

		Texture target = (_snapScreenSwap[_slot] && _swapPeer != null) ? _swapPeer.OutputTexture : _output;
		Texture bg0 = (comp.Enable3D != 0 && _output3D != null) ? _output3D : _bgLayer[0];

		cmd.Attributes.Set( "CaptureSrcA", _captureSrcA );
		cmd.Attributes.Set( "BGLayer0", bg0 );
		cmd.Attributes.Set( "BGLayer1", _bgLayer[1] );
		cmd.Attributes.Set( "BGLayer2", _bgLayer[2] );
		cmd.Attributes.Set( "BGLayer3", _bgLayer[3] );
		cmd.Attributes.Set( "OBJColor", _objColor );
		cmd.Attributes.Set( "OBJFlags", _objFlags );
		cmd.Attributes.Set( "Scanlines", _scanlineBuf );
		cmd.Attributes.Set( "BGConfig", _bgConfigBuf );
		cmd.Attributes.Set( "MosaicTable", _mosaicBuf );
		cmd.Attributes.Set( "VramDisplay", _vramDisplay );
		cmd.Attributes.Set( "DispMode", _snapDispMode[_slot] );
		cmd.Attributes.Set( "VramCap", _snapVramCap[_slot] );
		if ( Cap256 != null )
			cmd.Attributes.Set( "CaptureOut256", Cap256 );
		cmd.Attributes.Set( "OutputTex", target );
		cmd.Attributes.Set( "ScaleFactor", _scale );
		cmd.Attributes.Set( "BGPrio0", comp.BGPrio0 );
		cmd.Attributes.Set( "BGPrio1", comp.BGPrio1 );
		cmd.Attributes.Set( "BGPrio2", comp.BGPrio2 );
		cmd.Attributes.Set( "BGPrio3", comp.BGPrio3 );
		cmd.Attributes.Set( "EnableOBJ", comp.EnableOBJ );
		cmd.Attributes.Set( "Enable3D", comp.Enable3D );
		cmd.Attributes.Set( "BlendCnt", comp.BlendCnt );
		cmd.Attributes.Set( "BlendEffect", comp.BlendEffect );
		cmd.Attributes.Set( "BlendCoef0", comp.BlendCoef0 );
		cmd.Attributes.Set( "BlendCoef1", comp.BlendCoef1 );
		cmd.Attributes.Set( "BlendCoef2", comp.BlendCoef2 );
		cmd.Attributes.Set( "OutputW", ow );
		cmd.Attributes.Set( "OutputH", oh );
		cmd.Attributes.Set( "BrightMode", _snapBrightMode[_slot] );
		cmd.Attributes.Set( "BrightFactor", _snapBrightFactor[_slot] );
		cmd.Attributes.Set( "ScreenOff", _snapScreenOff[_slot] );
		cmd.DispatchCompute( _csCompositor, ow, oh, 1 );
		cmd.UavBarrier( target );
	}
}