Files
sbox-public/engine/Sandbox.Engine/Systems/Render/RenderAttributes.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

395 lines
12 KiB
C#

using NativeEngine;
using Sandbox.Rendering;
using System.Runtime.CompilerServices;
namespace Sandbox;
/// <summary>
/// RenderAttributes are a set of values that are passed to the renderer.
/// They can be a variety of primitive types, textures, samplers or buffers.
/// You can access attributes in the shader by binding them to a variable:
/// <code>
/// float4 CornerRadius &lt; Attribute( "BorderRadius" ); &gt;;
/// Texture2D g_tColor &lt; Attribute( "Texture" ); SrgbRead( false ); &gt;;
/// </code>
/// <seealso cref="Renderer.Attributes"/>
/// <seealso cref="Graphics.DrawModel(Model, Transform, RenderAttributes)"/>
/// <seealso cref="ComputeShader.DispatchWithAttributes(RenderAttributes, int, int, int)"/>
/// </summary>
public partial class RenderAttributes
{
private CRenderAttributes attributes;
private bool manuallyAllocated;
internal static RenderAttributePool Pool = new();
internal CRenderAttributes Get()
{
return attributes;
}
internal void Set( CRenderAttributes a )
{
TryFree();
attributes = a;
manuallyAllocated = false;
}
public RenderAttributes()
{
manuallyAllocated = true;
attributes = CRenderAttributes.Create();
}
internal RenderAttributes( CRenderAttributes attr )
{
Set( attr );
}
~RenderAttributes()
{
TryFree();
}
private void TryFree()
{
if ( manuallyAllocated )
{
//
// garry: I'm deleting these on the main thread. I don't think there's actually
// any danger here because generally when you're using these you're passing them
// into something (like a render) and it's using it immediately and not holding on
// to it.
//
var a = attributes;
MainThread.Queue( () => a.DeleteThis() );
}
attributes = default;
ClearUsedTextures();
}
public void Clear()
{
if ( !attributes.IsValid ) return;
attributes.Clear( true, true );
ClearUsedTextures();
}
internal void Clear( bool freeMemory )
{
if ( !attributes.IsValid ) return;
attributes.Clear( freeMemory, true );
ClearUsedTextures();
}
public void SetCombo( in StringToken k, in int value )
{
if ( !attributes.IsValid ) return;
attributes.SetComboValue( k, (byte)value );
}
[Obsolete( "Please use SetComboEnum" )]
public void SetCombo( in string k, in Enum value )
{
if ( !attributes.IsValid ) return;
attributes.SetComboValue( k, (byte)(int)(object)value );
}
public void SetComboEnum<T>( in StringToken k, in T value ) where T : unmanaged, Enum
{
if ( !attributes.IsValid ) return;
attributes.SetComboValue( k, (byte)value.AsInt() );
}
public void SetCombo( in StringToken k, in bool value )
{
SetCombo( k, value ? 1 : 0 );
}
public T GetComboEnum<T>( in StringToken k, in T defaultValue ) where T : Enum
{
if ( !attributes.IsValid )
return defaultValue;
return (T)(object)(int)attributes.GetComboValue( k, (byte)(int)(object)defaultValue );
}
public bool GetComboBool( in StringToken k, in bool defaultValue = false )
{
if ( !attributes.IsValid )
return defaultValue;
return attributes.GetComboValue( k, (byte)(defaultValue ? 1 : 0) ) == 1;
}
public int GetComboInt( in StringToken k, in int defaultValue = 0 )
{
if ( !attributes.IsValid )
return defaultValue;
return attributes.GetComboValue( k, (byte)defaultValue );
}
/// <summary>
/// Internal for a reason - don't expose!
/// </summary>
internal void SetPointer( in StringToken k, in IntPtr value )
{
if ( !attributes.IsValid ) return;
attributes.SetPtrValue( k, value );
}
public void Set( in StringToken k, in int value )
{
if ( !attributes.IsValid ) return;
attributes.SetIntValue( k, value );
}
public void Set( in StringToken k, in Vector2Int value )
{
if ( !attributes.IsValid ) return;
attributes.SetIntVector4DValue( k, value.x, value.y, 0, 0 );
}
public void Set( in StringToken k, in Vector3Int value )
{
if ( !attributes.IsValid ) return;
attributes.SetIntVector4DValue( k, value.x, value.y, value.z, 0 );
}
public void Set( in StringToken k, in Texture value, in int mip = -1 )
{
if ( !attributes.IsValid ) return;
var native = value?.native ?? default;
Set( k, native, mip );
SetUsedTexture( k, value );
}
public void Set( in StringToken k, in SamplerState value )
{
if ( !attributes.IsValid ) return;
attributes.SetSamplerValue( k, new( value ) );
}
internal void Set( in StringToken k, in ITexture value, in int mip = -1 )
{
if ( !attributes.IsValid ) return;
attributes.SetTextureValue( k, value, mip );
}
public void Set( in StringToken k, in float value )
{
if ( !attributes.IsValid ) return;
attributes.SetFloatValue( k, value );
}
public void Set( in StringToken k, in double value )
{
if ( !attributes.IsValid ) return;
attributes.SetFloatValue( k, (float)value );
}
public void Set( in StringToken k, in string value )
{
if ( !attributes.IsValid ) return;
attributes.SetStringValue( k, value );
}
public void Set( in StringToken k, in bool value )
{
if ( !attributes.IsValid ) return;
attributes.SetBoolValue( k, value );
}
public void Set( in StringToken k, in Vector4 value )
{
if ( !attributes.IsValid ) return;
attributes.SetVector4DValue( k, value );
}
public void Set( in StringToken k, in Angles value )
{
Set( k, new Vector3( value.pitch, value.yaw, value.roll ) );
}
public void Set( in StringToken k, in Vector3 value )
{
if ( !attributes.IsValid ) return;
attributes.SetVectorValue( k, value );
}
public void Set( in StringToken k, in Vector2 value )
{
if ( !attributes.IsValid ) return;
attributes.SetVector2DValue( k, value );
}
public void Set( in StringToken k, in GpuBuffer value )
{
if ( !attributes.IsValid ) return;
attributes.SetBufferValue( k, value.native );
}
/// <summary>
/// Set a constant buffer to a specific value
/// </summary>
public unsafe void SetData<T>( in StringToken k, Span<T> value ) where T : unmanaged
{
if ( !SandboxedUnsafe.IsAcceptablePod<T>() )
throw new ArgumentException( $"{nameof( T )} must be a POD type" );
int actualSize = value.Length * Unsafe.SizeOf<T>();
if ( actualSize <= 0 ) return;
fixed ( T* ptr = value )
RenderTools.SetDynamicConstantBufferData( attributes, k, Graphics.Context, (IntPtr)ptr, actualSize );
}
/// <summary>
/// Set a constant buffer to a specific value
/// </summary>
public unsafe void SetData<T>( in StringToken k, T value ) where T : unmanaged
{
if ( !SandboxedUnsafe.IsAcceptablePod<T>() )
throw new ArgumentException( $"{nameof( T )} must be a POD type" );
if ( !attributes.IsValid ) return;
int actualSize = Unsafe.SizeOf<T>();
if ( actualSize <= 0 ) return;
RenderTools.SetDynamicConstantBufferData( attributes, k, Graphics.Context, (IntPtr)(&value), actualSize );
}
/// <summary>
/// Set a constant buffer to a specific value
/// </summary>
public unsafe void SetData<T>( in StringToken k, T[] value ) where T : unmanaged
{
if ( !attributes.IsValid ) return;
SetData( k, value.AsSpan() );
}
/// <summary>
/// Set a constant buffer to a specific value
/// </summary>
public unsafe void SetData<T>( in StringToken k, List<T> value ) where T : unmanaged
{
if ( !attributes.IsValid ) return;
SetData( k, value.ToArray() );
}
/// <summary>
/// Get a bool value - else defaultValue if missing
/// </summary>
public bool GetBool( in StringToken name, in bool defaultValue = default )
{
if ( !attributes.IsValid ) return defaultValue;
return attributes.GetBoolValue( name, defaultValue );
}
/// <summary>
/// Get a vector3 value - else defaultValue if missing
/// </summary>
public Vector3 GetVector( in StringToken name, in Vector3 defaultValue = default )
{
if ( !attributes.IsValid ) return defaultValue;
return attributes.GetVectorValue( name, defaultValue );
}
/// <summary>
/// Get a vector4 value - else defaultValue if missing
/// </summary>
public Vector4 GetVector4( in StringToken name, in Vector4 defaultValue = default )
{
if ( !attributes.IsValid ) return defaultValue;
return attributes.GetVector4DValue( name, defaultValue );
}
/// <summary>
/// Get a vector4 value - else defaultValue if missing
/// </summary>
public Angles GetAngles( in StringToken name, in Angles defaultValue = default )
{
if ( !attributes.IsValid ) return defaultValue;
var d = new Vector3( defaultValue.pitch, defaultValue.yaw, defaultValue.roll );
d = attributes.GetVectorValue( name, d );
return new Angles( d.x, d.y, d.z );
}
/// <summary>
/// Get a float value - else defaultValue if missing
/// </summary>
public float GetFloat( in StringToken name, in float defaultValue = default )
{
if ( !attributes.IsValid ) return defaultValue;
return attributes.GetFloatValue( name, defaultValue );
}
/// <summary>
/// Get a int value - else defaultValue if missing
/// </summary>
public int GetInt( in StringToken name, in int defaultValue = default )
{
if ( !attributes.IsValid ) return defaultValue;
return attributes.GetIntValue( name, defaultValue );
}
/// <summary>
/// Get a matrix value - else defaultValue if missing
/// </summary>
public Matrix GetMatrix( in StringToken name, in Matrix defaultValue = default )
{
if ( !attributes.IsValid ) return defaultValue;
return attributes.GetVMatrixValue( name, defaultValue );
}
/// <summary>
/// Get a texture value - else defaultValue if missing
/// </summary>
public Texture GetTexture( in StringToken name, in Texture defaultValue = default )
{
if ( !attributes.IsValid ) return defaultValue;
var texturePointer = attributes.GetTextureValue( name, default );
if ( texturePointer.IsNull ) return defaultValue;
return Texture.FromNative( texturePointer );
}
public void Set( in StringToken k, in Matrix value )
{
if ( !attributes.IsValid ) return;
attributes.SetVMatrixValue( k, value );
}
internal void MergeTo( RenderAttributes renderAttributes )
{
if ( renderAttributes is null ) throw new ArgumentNullException( nameof( renderAttributes ) );
if ( !attributes.IsValid || !renderAttributes.attributes.IsValid ) return;
attributes.MergeToPtr( renderAttributes.attributes );
}
// These are for backwards compatibility with compiled shit
public void SetCombo( [StringToken.Convert] in string k, in int value ) => SetCombo( (StringToken)k, value );
public void SetCombo( [StringToken.Convert] in string k, in bool value ) => SetCombo( (StringToken)k, value );
public void Set( [StringToken.Convert] in string k, in bool value ) => Set( (StringToken)k, value );
public void Set( [StringToken.Convert] in string k, in int value ) => Set( (StringToken)k, value );
public void Set( [StringToken.Convert] in string k, in Vector2Int value ) => Set( (StringToken)k, value );
public void Set( [StringToken.Convert] in string k, in Vector3Int value ) => Set( (StringToken)k, value );
public void Set( [StringToken.Convert] in string k, in Vector4 value ) => Set( (StringToken)k, value );
public void Set( [StringToken.Convert] in string k, in Vector3 value ) => Set( (StringToken)k, value );
public void Set( [StringToken.Convert] in string k, in Vector2 value ) => Set( (StringToken)k, value );
public void Set( [StringToken.Convert] in string k, in Matrix value ) => Set( (StringToken)k, value );
public void Set( [StringToken.Convert] in string k, in Angles value ) => Set( (StringToken)k, value );
public void Set( [StringToken.Convert] in string k, in string value ) => Set( (StringToken)k, value );
public void Set( [StringToken.Convert] in string k, in Texture value, in int mip = -1 ) => Set( (StringToken)k, value, mip );
public void Set( [StringToken.Convert] in string k, in float value ) => Set( (StringToken)k, value );
public void Set( [StringToken.Convert] in string k, in double value ) => Set( (StringToken)k, (float)value );
public Texture GetTexture( [StringToken.Convert] in string name, in Texture defaultValue = default ) => GetTexture( (StringToken)name, defaultValue );
}