using System.Collections.Concurrent; namespace Sandbox.Rendering; public sealed partial class CommandList { public unsafe class AttributeAccess { private readonly ConcurrentDictionary _renderTargets = new(); internal Func _get; internal CommandList list; RenderAttributes attributes => _get(); internal AttributeAccess( CommandList cmdlist, Func _getter ) { list = cmdlist; _get = _getter; } public void Set( StringToken token, float f ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, entry.Data1.x ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object4 = this, Data1 = new Vector4( f, 0, 0, 0 ) } ); } public void Set( StringToken token, double f ) => Set( token, (float)f ); public void Set( StringToken token, Vector2 vector2 ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, new Vector2( entry.Data1.x, entry.Data1.y ) ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object4 = this, Data1 = new Vector4( vector2.x, vector2.y, 0, 0 ) } ); } public void Set( StringToken token, Vector3 vector3 ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, new Vector3( entry.Data1.x, entry.Data1.y, entry.Data1.z ) ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object4 = this, Data1 = new Vector4( vector3.x, vector3.y, vector3.z, 0 ) } ); } public void Set( StringToken token, Vector4 vector4 ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, entry.Data1 ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object4 = this, Data1 = vector4 } ); } public void Set( StringToken token, int i ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, (int)entry.Data1.x ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object4 = this, Data1 = new Vector4( i, 0, 0, 0 ) } ); } public void Set( StringToken token, bool b ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, (int)entry.Data1.x != 0 ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object4 = this, Data1 = new Vector4( b ? 1 : 0, 0, 0, 0 ) } ); } public void Set( StringToken token, Matrix matrix ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, (Matrix)entry.Object2 ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object2 = matrix, Object4 = this } ); } public void Set( StringToken token, GpuBuffer buffer ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, (GpuBuffer)entry.Object2 ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object2 = buffer, Object4 = this } ); } public void Set( StringToken token, Texture texture, int mip = -1 ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, (Texture)entry.Object2, (int)entry.Data1.x ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object2 = texture, Object4 = this, Data1 = new Vector4( mip, 0, 0, 0 ) } ); } public void Set( StringToken token, SamplerState samplerState ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, (SamplerState)entry.Object2 ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object2 = samplerState, Object4 = this } ); } public void SetCombo( StringToken token, int value ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.SetCombo( (StringToken)entry.Object1, (int)entry.Data1.x ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object4 = this, Data1 = new Vector4( value, 0, 0, 0 ) } ); } public void SetCombo( StringToken token, bool value ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.SetCombo( (StringToken)entry.Object1, (int)entry.Data1.x != 0 ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object4 = this, Data1 = new Vector4( value ? 1 : 0, 0, 0, 0 ) } ); } public void SetCombo( StringToken token, T t ) where T : unmanaged, Enum { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.SetComboEnum( (StringToken)entry.Object1, (T)entry.Object2 ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object2 = t, Object4 = this } ); } public void SetData( StringToken token, T data ) where T : unmanaged { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.SetData( (StringToken)entry.Object1, (T)entry.Object2 ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object2 = data, Object4 = this } ); } /// /// Set a special value /// public void SetValue( StringToken token, RenderValue value ) { switch ( value ) { case RenderValue.ColorTarget: { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, Graphics.SceneLayer.GetColorTarget() ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object4 = this } ); } break; case RenderValue.DepthTarget: { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, Graphics.SceneLayer.GetDepthTarget() ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object4 = this } ); } break; case RenderValue.MsaaCombo: { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.SetCombo( (StringToken)entry.Object1, Graphics.IdealMsaaLevel != MultisampleAmount.MultisampleNone ? 1 : 0 ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object4 = this } ); } break; } } /// /// Set the color texture from this named render target to this attribute /// public void Set( StringToken token, RenderTargetHandle.ColorTextureRef buffer, int mip = -1 ) { static void Execute( ref Entry entry, CommandList commandList ) { if ( commandList.state.GetRenderTarget( (string)entry.Object5 ) is not { } target ) { Log.Warning( $"[{commandList.DebugName ?? "CommandList"}] Unknown rt: {(string)entry.Object5}" ); return; } var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, target.ColorTarget, (int)entry.Data1.x ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object5 = buffer.Name, Object4 = this, Data1 = new Vector4( mip, 0, 0, 0 ) } ); } /// /// Set the color texture from this named render target to this attribute /// public void Set( StringToken token, RenderTargetHandle.ColorIndexRef buffer ) { static void Execute( ref Entry entry, CommandList commandList ) { if ( commandList.state.GetRenderTarget( (string)entry.Object5 ) is not { } target ) { Log.Warning( $"[{commandList.DebugName ?? "CommandList"}] Unknown rt: {(string)entry.Object5}" ); return; } var attrAccess = (AttributeAccess)entry.Object4; attrAccess.attributes.Set( (StringToken)entry.Object1, target.ColorTarget.Index ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object5 = buffer.Name, Object4 = this } ); } /// /// Set the size of this named render target to this float2 attribute /// public void Set( StringToken token, RenderTargetHandle.SizeHandle size, bool inverse = false ) { static void Execute( ref Entry entry, CommandList commandList ) { if ( commandList.state.GetRenderTarget( (string)entry.Object5 ) is not { } target ) { Log.Warning( $"[{commandList.DebugName ?? "CommandList"}] Unknown rt: {(string)entry.Object5}" ); return; } var s = target.ColorTarget.Size; var attrAccess = (AttributeAccess)entry.Object4; if ( (int)entry.Data1.x != 0 ) attrAccess.attributes.Set( (StringToken)entry.Object1, new Vector2( 1.0f / s.x, 1.0f / s.y ) ); else attrAccess.attributes.Set( (StringToken)entry.Object1, s ); } list.AddEntry( &Execute, new Entry { Object1 = token, Object5 = size.Name, Object4 = this, Data1 = new Vector4( inverse ? 1 : 0, 0, 0, 0 ) } ); } /// /// Takes a copy of the current viewport's color texture and stores it in targetName on renderAttributes. /// public RenderTargetHandle GrabFrameTexture( string token = "FrameTexture", bool withMips = false ) { return GrabFrameTexture( token, withMips ? Graphics.DownsampleMethod.GaussianBlur : Graphics.DownsampleMethod.None ); } /// /// Takes a copy of the current viewport's color texture and stores it in targetName on renderAttributes. /// public RenderTargetHandle GrabFrameTexture( string token, Graphics.DownsampleMethod downsampleMethod ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object2; var temp = Graphics.GrabFrameTexture( (string)entry.Object5, attrAccess.attributes, (Graphics.DownsampleMethod)(int)entry.Data1.x ); commandList.state.renderTargets[(string)entry.Object5] = temp; attrAccess._renderTargets[(string)entry.Object5] = temp; } list.AddEntry( &Execute, new Entry { Object5 = token, Object2 = this, Data1 = new Vector4( (int)downsampleMethod, 0, 0, 0 ) } ); return new RenderTargetHandle { Name = token }; } /// /// Takes a copy of the current viewport's depth texture and stores it in targetName on renderAttributes. /// public RenderTargetHandle GrabDepthTexture( string token = "DepthTexture" ) { static void Execute( ref Entry entry, CommandList commandList ) { var attrAccess = (AttributeAccess)entry.Object2; var temp = Graphics.GrabDepthTexture( (string)entry.Object5, attrAccess.attributes ); commandList.state.renderTargets[(string)entry.Object5] = temp; attrAccess._renderTargets[(string)entry.Object5] = temp; } list.AddEntry( &Execute, new Entry { Object5 = token, Object2 = this } ); return new RenderTargetHandle { Name = token }; } /// /// Get the actual render target by name. Useful for externals that need to access the render target directly. /// public RenderTarget GetRenderTarget( string name ) { if ( _renderTargets.TryGetValue( name, out var rt ) ) return rt; return null; } internal void ClearRenderTargets() { _renderTargets.Clear(); } } /// /// These are the attributes for the current view. Setting a variable here will let you pass it down to /// other places in the render pipeline. /// public AttributeAccess GlobalAttributes { get; private set; } /// /// Access to the local attributes. What these are depends on where the command list is being called. /// If we're calling from a renderable, these are the attributes for that renderable. /// public AttributeAccess Attributes { get; private set; } RenderAttributes GetFrameAttributes() => Graphics.FrameAttributes; RenderAttributes GetLocalAttributes() => Graphics.Attributes; } public enum RenderValue { /// /// The color texure we're currently rendering to /// ColorTarget, /// /// The depth texture we're currently rendering to /// DepthTarget, /// /// Will set the named combo to 1 if MSAA is active, otherwise 0. /// MsaaCombo, }