Refactor folder structure
This commit is contained in:
7
src/Runtime/Ghost.Core/AssemblyInfo.cs
Normal file
7
src/Runtime/Ghost.Core/AssemblyInfo.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Ghost.Core.Attributes;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Ghost.Graphics")]
|
||||
[assembly: InternalsVisibleTo("Ghost.Engine")]
|
||||
|
||||
[assembly: EngineAssembly]
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Ghost.Core.Attributes;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly)]
|
||||
public sealed class EngineAssemblyAttribute : Attribute
|
||||
{
|
||||
}
|
||||
11
src/Runtime/Ghost.Core/Contracts/ICloneable.cs
Normal file
11
src/Runtime/Ghost.Core/Contracts/ICloneable.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Ghost.Core.Contracts;
|
||||
|
||||
public interface ICloneable
|
||||
{
|
||||
object Clone();
|
||||
}
|
||||
|
||||
public interface ICloneable<T>
|
||||
{
|
||||
T Clone();
|
||||
}
|
||||
6
src/Runtime/Ghost.Core/Contracts/IReleasable.cs
Normal file
6
src/Runtime/Ghost.Core/Contracts/IReleasable.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Ghost.Core.Contracts;
|
||||
|
||||
internal interface IReleasable
|
||||
{
|
||||
void InternalRelease();
|
||||
}
|
||||
32
src/Runtime/Ghost.Core/Ghost.Core.csproj
Normal file
32
src/Runtime/Ghost.Core/Ghost.Core.csproj
Normal file
@@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
<DefineConstants>$(DefineConstants);PLATEFORME_WIN64</DefineConstants>
|
||||
<IsTrimmable>True</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<IsAotCompatible>True</IsAotCompatible>
|
||||
<DefineConstants>$(DefineConstants);PLATEFORME_WIN64</DefineConstants>
|
||||
<IsTrimmable>True</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Misaki.HighPerformance" Version="1.0.4" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Jobs" Version="1.2.2" />
|
||||
<PackageReference Include="Misaki.HighPerformance.LowLevel" Version="1.3.3" />
|
||||
<PackageReference Include="Misaki.HighPerformance.Mathematics" Version="1.3.1" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="10.0.1" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.26100.6" />
|
||||
<PackageReference Include="ZLinq" Version="1.5.4" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
110
src/Runtime/Ghost.Core/Graphics/PipelineState.cs
Normal file
110
src/Runtime/Ghost.Core/Graphics/PipelineState.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
namespace Ghost.Core.Graphics;
|
||||
|
||||
public enum ZTest : byte
|
||||
{
|
||||
Disabled,
|
||||
Less,
|
||||
LessEqual,
|
||||
Equal,
|
||||
GreaterEqual,
|
||||
Greater,
|
||||
NotEqual,
|
||||
Always
|
||||
}
|
||||
|
||||
public enum ZWrite : byte
|
||||
{
|
||||
Off,
|
||||
On
|
||||
}
|
||||
|
||||
public enum Cull : byte
|
||||
{
|
||||
Off,
|
||||
Front,
|
||||
Back
|
||||
}
|
||||
|
||||
public enum Blend : byte
|
||||
{
|
||||
Opaque,
|
||||
Alpha,
|
||||
Additive,
|
||||
Multiply,
|
||||
PremultipliedAlpha
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ColorWriteMask : byte
|
||||
{
|
||||
None = 0,
|
||||
Red = 1 << 0,
|
||||
Green = 1 << 1,
|
||||
Blue = 1 << 2,
|
||||
Alpha = 1 << 3,
|
||||
All = Red | Green | Blue | Alpha
|
||||
}
|
||||
|
||||
public struct PipelineState
|
||||
{
|
||||
public ZTest ZTest
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public ZWrite ZWrite
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public Cull Cull
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public Blend Blend
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public ColorWriteMask ColorMask
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
public static PipelineState Default => new PipelineState
|
||||
{
|
||||
ZTest = ZTest.LessEqual,
|
||||
ZWrite = ZWrite.On,
|
||||
Cull = Cull.Back,
|
||||
Blend = Blend.Opaque,
|
||||
ColorMask = ColorWriteMask.All
|
||||
};
|
||||
|
||||
public readonly ulong GetHashCode64()
|
||||
{
|
||||
// 32-bit packed key for states controlled by material / overrides.
|
||||
// layout:
|
||||
// 0..3 Blend (4 bits)
|
||||
// 4..6 Cull (3 bits)
|
||||
// 7..10 DeafaultState (4 bits)
|
||||
// 11 ZWrite (1 bit)
|
||||
// 12..15 ColorMask (4 bits)
|
||||
|
||||
var key = 0u;
|
||||
key |= ((uint)Blend & 0xFu) << 0;
|
||||
key |= ((uint)Cull & 0x7u) << 4;
|
||||
key |= ((uint)ZTest & 0xFu) << 7;
|
||||
key |= ((uint)ZWrite & 0x1u) << 11;
|
||||
key |= ((uint)ColorMask & 0xFu) << 12;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
var code64 = GetHashCode64();
|
||||
return ((int)code64) ^ (int)(code64 >> 32);
|
||||
}
|
||||
}
|
||||
108
src/Runtime/Ghost.Core/Graphics/ShaderDescriptor.cs
Normal file
108
src/Runtime/Ghost.Core/Graphics/ShaderDescriptor.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
namespace Ghost.Core.Graphics;
|
||||
|
||||
public enum KeywordSpace
|
||||
{
|
||||
Local,
|
||||
Global,
|
||||
}
|
||||
|
||||
public enum ShaderPropertyType
|
||||
{
|
||||
None,
|
||||
Float, Float2, Float3, Float4,
|
||||
Float4x4,
|
||||
Int, Int2, Int3, Int4,
|
||||
UInt, UInt2, UInt3, UInt4,
|
||||
Bool, Bool2, Bool3, Bool4,
|
||||
Texture2D, Texture3D, TextureCube,
|
||||
Texture2DArray, TextureCubeArray,
|
||||
Sampler
|
||||
}
|
||||
|
||||
public struct ShaderEntryPoint
|
||||
{
|
||||
public string entry;
|
||||
public string shader;
|
||||
|
||||
public readonly bool IsCreated => !string.IsNullOrEmpty(entry) && !string.IsNullOrEmpty(shader);
|
||||
}
|
||||
|
||||
public struct KeywordsGroup
|
||||
{
|
||||
public KeywordSpace space;
|
||||
public List<string> keywords;
|
||||
}
|
||||
|
||||
public struct PropertyDescriptor
|
||||
{
|
||||
public string name;
|
||||
public int offset;
|
||||
public int size;
|
||||
public ShaderPropertyType type;
|
||||
|
||||
public object? defaultValue;
|
||||
}
|
||||
|
||||
public struct PassDescriptor
|
||||
{
|
||||
public string identifier;
|
||||
public string name;
|
||||
|
||||
public ShaderEntryPoint taskShader;
|
||||
public ShaderEntryPoint meshShader;
|
||||
public ShaderEntryPoint pixelShader;
|
||||
public string[] defines;
|
||||
public string[] includes;
|
||||
public KeywordsGroup[] keywords;
|
||||
public PipelineState localPipeline;
|
||||
public string? hlsl;
|
||||
}
|
||||
|
||||
public class ShaderDescriptor
|
||||
{
|
||||
public string name = string.Empty;
|
||||
public int cbufferSize;
|
||||
public PropertyDescriptor[] globalProperties = null!;
|
||||
public PropertyDescriptor[] properties = null!;
|
||||
public PassDescriptor[] passes = null!;
|
||||
public string? hlsl;
|
||||
}
|
||||
|
||||
public static class ShaderDescriptorExtensions
|
||||
{
|
||||
public static int GetSize(this ShaderPropertyType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
ShaderPropertyType.Float
|
||||
or ShaderPropertyType.Int
|
||||
or ShaderPropertyType.UInt
|
||||
or ShaderPropertyType.Bool => 4,
|
||||
|
||||
ShaderPropertyType.Float2
|
||||
or ShaderPropertyType.Int2
|
||||
or ShaderPropertyType.UInt2
|
||||
or ShaderPropertyType.Bool2 => 8,
|
||||
|
||||
ShaderPropertyType.Float3
|
||||
or ShaderPropertyType.Int3
|
||||
or ShaderPropertyType.UInt3
|
||||
or ShaderPropertyType.Bool3 => 12,
|
||||
|
||||
ShaderPropertyType.Float4
|
||||
or ShaderPropertyType.Int4
|
||||
or ShaderPropertyType.UInt4
|
||||
or ShaderPropertyType.Bool4 => 16,
|
||||
|
||||
ShaderPropertyType.Float4x4 => 64,
|
||||
|
||||
ShaderPropertyType.Texture2D
|
||||
or ShaderPropertyType.Texture3D
|
||||
or ShaderPropertyType.TextureCube
|
||||
or ShaderPropertyType.Texture2DArray
|
||||
or ShaderPropertyType.TextureCubeArray
|
||||
or ShaderPropertyType.Sampler => 4, // Bindless resource use uint32
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
242
src/Runtime/Ghost.Core/Handle.cs
Normal file
242
src/Runtime/Ghost.Core/Handle.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
namespace Ghost.Core;
|
||||
|
||||
public readonly struct Handle<T> : IEquatable<Handle<T>>
|
||||
{
|
||||
public int ID
|
||||
{
|
||||
get => field - 1;
|
||||
}
|
||||
|
||||
public int Generation
|
||||
{
|
||||
get => field - 1;
|
||||
}
|
||||
|
||||
public Handle(int id, int generation)
|
||||
{
|
||||
ID = id + 1;
|
||||
Generation = generation + 1;
|
||||
}
|
||||
|
||||
public static Handle<T> Invalid => default;
|
||||
|
||||
public readonly bool IsValid => this != Invalid;
|
||||
public readonly bool IsInvalid => this == Invalid;
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return ID + (Generation << 16);
|
||||
}
|
||||
|
||||
public readonly override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Handle<T> id && Equals(id);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Handle<{typeof(T).Name}>({ID}, {Generation})";
|
||||
}
|
||||
|
||||
public readonly bool Equals(Handle<T> other)
|
||||
{
|
||||
return ID == other.ID && Generation == other.Generation;
|
||||
}
|
||||
|
||||
public readonly int CompareTo(Handle<T> other)
|
||||
{
|
||||
return ID.CompareTo(other.ID);
|
||||
}
|
||||
|
||||
public static bool operator ==(Handle<T> a, Handle<T> b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Handle<T> a, Handle<T> b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct Identifier<T> : IEquatable<Identifier<T>>
|
||||
{
|
||||
public int Value
|
||||
{
|
||||
get => field - 1;
|
||||
}
|
||||
|
||||
public Identifier(int value)
|
||||
{
|
||||
Value = value + 1;
|
||||
}
|
||||
|
||||
public static Identifier<T> Invalid => default;
|
||||
|
||||
public readonly bool IsValid => this != Invalid;
|
||||
public readonly bool IsInvalid => this == Invalid;
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
public readonly override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Identifier<T> id && Equals(id);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Identifier<{typeof(T).Name}>({Value})";
|
||||
}
|
||||
|
||||
public readonly bool Equals(Identifier<T> other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
public readonly int CompareTo(Identifier<T> other)
|
||||
{
|
||||
return Value.CompareTo(other.Value);
|
||||
}
|
||||
|
||||
public static bool operator ==(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator <(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return a.Value < b.Value;
|
||||
}
|
||||
|
||||
public static bool operator >(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return a.Value > b.Value;
|
||||
}
|
||||
|
||||
public static bool operator <=(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return a.Value <= b.Value;
|
||||
}
|
||||
|
||||
public static bool operator >=(Identifier<T> a, Identifier<T> b)
|
||||
{
|
||||
return a.Value >= b.Value;
|
||||
}
|
||||
|
||||
public static implicit operator int(Identifier<T> id) => id.Value;
|
||||
public static implicit operator Identifier<T>(int value) => new Identifier<T>(value);
|
||||
}
|
||||
|
||||
public readonly struct Key64<T> : IEquatable<Key64<T>>
|
||||
{
|
||||
public ulong Value
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public Key64(ulong value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static Key64<T> Invalid => new(0);
|
||||
|
||||
public bool IsValid => this != Invalid;
|
||||
public bool IsInvalid => this == Invalid;
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
|
||||
public readonly bool Equals(Key64<T> other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
public readonly int CompareTo(Key64<T> other)
|
||||
{
|
||||
return Value.CompareTo(other.Value);
|
||||
}
|
||||
|
||||
public readonly override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Key64<T> id && Equals(id);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString("X16");
|
||||
}
|
||||
|
||||
public static bool operator ==(Key64<T> a, Key64<T> b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Key64<T> a, Key64<T> b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct Key128<T> : IEquatable<Key128<T>>
|
||||
{
|
||||
public UInt128 Value
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public Key128(UInt128 value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static Key128<T> Invalid => new(0);
|
||||
|
||||
public bool IsValid => this != Invalid;
|
||||
public bool IsInvalid => this == Invalid;
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
|
||||
public readonly bool Equals(Key128<T> other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
public readonly int CompareTo(Key128<T> other)
|
||||
{
|
||||
return Value.CompareTo(other.Value);
|
||||
}
|
||||
|
||||
public readonly override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Key128<T> id && Equals(id);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString("X16");
|
||||
}
|
||||
|
||||
public static bool operator ==(Key128<T> a, Key128<T> b)
|
||||
{
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
public static bool operator !=(Key128<T> a, Key128<T> b)
|
||||
{
|
||||
return !a.Equals(b);
|
||||
}
|
||||
}
|
||||
214
src/Runtime/Ghost.Core/Logging.cs
Normal file
214
src/Runtime/Ghost.Core/Logging.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ghost.Core;
|
||||
|
||||
public enum LogLevel
|
||||
{
|
||||
Info,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
public readonly struct LogMessage
|
||||
{
|
||||
public LogLevel Level
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public string Message
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public string? StackTrace
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public DateTime Timestamp
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public LogMessage(LogLevel level, string message, string? stackTrace = null)
|
||||
{
|
||||
Level = level;
|
||||
Message = message;
|
||||
StackTrace = stackTrace;
|
||||
Timestamp = DateTime.Now;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (StackTrace != null)
|
||||
{
|
||||
return $"{Timestamp:HH:mm:ss} [{Level}] {Message}\n{StackTrace}";
|
||||
}
|
||||
|
||||
return $"{Timestamp:HH:mm:ss} [{Level}] {Message}";
|
||||
}
|
||||
}
|
||||
|
||||
public interface ILogger
|
||||
{
|
||||
ReadOnlyObservableCollection<LogMessage> Logs
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
void Log(string message, LogLevel level);
|
||||
void Log(Exception exception);
|
||||
void Assert(bool condition, string message);
|
||||
void Clear();
|
||||
}
|
||||
|
||||
public static class Logger
|
||||
{
|
||||
// TODO: Add file logging.
|
||||
private class LoggerImpl : ILogger
|
||||
{
|
||||
private readonly ObservableCollection<LogMessage> _logs = new();
|
||||
private readonly ReadOnlyObservableCollection<LogMessage> _readOnly;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public ReadOnlyObservableCollection<LogMessage> Logs => _readOnly;
|
||||
|
||||
public LoggerImpl()
|
||||
{
|
||||
_readOnly = new ReadOnlyObservableCollection<LogMessage>(_logs);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public void Log(string message, LogLevel level)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_logs.Add(new LogMessage(level, message));
|
||||
}
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public void Log(Exception exception)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_logs.Add(new LogMessage(LogLevel.Error, exception.Message, exception.StackTrace));
|
||||
}
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public void Assert(bool condition, string message)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
Log(message, LogLevel.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_logs.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ILogger s_logger = new LoggerImpl();
|
||||
|
||||
public static ReadOnlyObservableCollection<LogMessage> Logs => s_logger.Logs;
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void Log(LogLevel level, object? message)
|
||||
{
|
||||
s_logger.Log(message?.ToString() ?? "null", level);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void Log(LogLevel level, string message)
|
||||
{
|
||||
s_logger.Log(message, level);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void Log(LogLevel level, string format, params object?[] args)
|
||||
{
|
||||
s_logger.Log(string.Format(format, args), level);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void LogInfo(object? message)
|
||||
{
|
||||
s_logger.Log(message?.ToString() ?? "null", LogLevel.Info);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void LogInfo(string message)
|
||||
{
|
||||
s_logger.Log(message, LogLevel.Info);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void LogInfo(string format, params object?[] args)
|
||||
{
|
||||
s_logger.Log(string.Format(format, args), LogLevel.Info);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void LogWarning(object? message)
|
||||
{
|
||||
s_logger.Log(message?.ToString() ?? "null", LogLevel.Warning);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void LogWarning(string message)
|
||||
{
|
||||
s_logger.Log(message, LogLevel.Warning);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void LogWarning(string format, params object?[] args)
|
||||
{
|
||||
s_logger.Log(string.Format(format, args), LogLevel.Warning);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void LogError(object? message)
|
||||
{
|
||||
s_logger.Log(message?.ToString() ?? "null", LogLevel.Error);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void LogError(string message)
|
||||
{
|
||||
s_logger.Log(message, LogLevel.Error);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void LogError(string format, params object?[] args)
|
||||
{
|
||||
s_logger.Log(string.Format(format, args), LogLevel.Error);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void LogError(Exception ex)
|
||||
{
|
||||
s_logger.Log(ex);
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
public static void Assert(bool condition, string message)
|
||||
{
|
||||
s_logger.Assert(condition, message);
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
s_logger.Clear();
|
||||
}
|
||||
}
|
||||
407
src/Runtime/Ghost.Core/Result.cs
Normal file
407
src/Runtime/Ghost.Core/Result.cs
Normal file
@@ -0,0 +1,407 @@
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Core;
|
||||
|
||||
public readonly struct Result
|
||||
{
|
||||
private readonly string? _message;
|
||||
private readonly bool _isSuccess;
|
||||
|
||||
public readonly string? Message => _message;
|
||||
public readonly bool IsSuccess => _isSuccess;
|
||||
public readonly bool IsFailure => !IsSuccess;
|
||||
|
||||
public Result(bool success, string? message = null)
|
||||
{
|
||||
_isSuccess = success;
|
||||
_message = message;
|
||||
}
|
||||
|
||||
public static Result Success()
|
||||
{
|
||||
return new Result(true);
|
||||
}
|
||||
|
||||
public static Result Failure(string? message = null)
|
||||
{
|
||||
return new Result(false, message);
|
||||
}
|
||||
|
||||
public static Result Failure(Error status)
|
||||
{
|
||||
return new Result(false, status.ToString());
|
||||
}
|
||||
|
||||
public static Result<T> Success<T>(T value)
|
||||
{
|
||||
return Result<T>.Success(value);
|
||||
}
|
||||
|
||||
public static Result<T> Failure<T>(string? message = null)
|
||||
{
|
||||
return Result<T>.Failure(message);
|
||||
}
|
||||
|
||||
public static Result<T> Failure<T>(Error status)
|
||||
{
|
||||
return Result<T>.Failure(status.ToString());
|
||||
}
|
||||
|
||||
public void Deconstruct(out bool success, out string? message)
|
||||
{
|
||||
success = IsSuccess;
|
||||
message = Message;
|
||||
}
|
||||
|
||||
public override string ToString() => IsSuccess ? "OK" : $"Error: {Message}";
|
||||
|
||||
public static implicit operator bool(Result result) => result.IsSuccess;
|
||||
}
|
||||
|
||||
public readonly struct Result<T>
|
||||
{
|
||||
private readonly T _value;
|
||||
private readonly string? _message;
|
||||
private readonly bool _isSuccess;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value. Undefined if the result is a failure.
|
||||
/// </summary>
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
if (IsFailure)
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot access Value when Result is a failure. {_message}");
|
||||
}
|
||||
#endif
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly string? Message => _message;
|
||||
public readonly bool IsSuccess => _isSuccess;
|
||||
public readonly bool IsFailure => !IsSuccess;
|
||||
|
||||
public Result(bool success, T value, string? message = null)
|
||||
{
|
||||
_isSuccess = success;
|
||||
_value = value;
|
||||
_message = message;
|
||||
}
|
||||
|
||||
public static Result<T> Success(T value)
|
||||
{
|
||||
return new Result<T>(true, value);
|
||||
}
|
||||
|
||||
public static Result<T> Failure(string? message = null)
|
||||
{
|
||||
return new Result<T>(false, default!, message);
|
||||
}
|
||||
|
||||
public void Deconstruct(out bool success, out T value, out string? message)
|
||||
{
|
||||
success = IsSuccess;
|
||||
value = Value;
|
||||
message = Message;
|
||||
}
|
||||
|
||||
public override string ToString() => IsSuccess ? $"OK: {Value}" : $"Error: {Message}";
|
||||
|
||||
public static implicit operator Result<T>(T? data) => data is not null ? Success(data) : Failure(null);
|
||||
public static implicit operator Result<T>(Result result) => result.IsSuccess ? Success(default!) : Failure(result.Message);
|
||||
public static implicit operator bool(Result<T> result) => result.IsSuccess;
|
||||
}
|
||||
|
||||
public enum Error : byte
|
||||
{
|
||||
None,
|
||||
NotFound,
|
||||
InvalidArgument,
|
||||
InvalidState,
|
||||
InternalError,
|
||||
PermissionDenied,
|
||||
NotSupported,
|
||||
OutOfMemory,
|
||||
Timeout,
|
||||
Cancelled,
|
||||
UnknownError,
|
||||
|
||||
Success = None,
|
||||
}
|
||||
|
||||
public readonly struct Result<T, E>
|
||||
where E : struct, Enum
|
||||
{
|
||||
private readonly T _value;
|
||||
private readonly E _error;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value. Undefined if the result is a failure.
|
||||
/// </summary>
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
if (IsFailure)
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot access Value when Result is a failure. Error: {_error}");
|
||||
}
|
||||
#endif
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
public E Error => _error;
|
||||
public bool IsSuccess => EqualityComparer<E>.Default.Equals(_error, default);
|
||||
public bool IsFailure => !IsSuccess;
|
||||
|
||||
public Result(T value, E status)
|
||||
{
|
||||
_value = value;
|
||||
_error = status;
|
||||
}
|
||||
|
||||
public static Result<T, E> Success(T value)
|
||||
{
|
||||
return new Result<T, E>(value, default);
|
||||
}
|
||||
|
||||
public static Result<T, E> Failure(E status)
|
||||
{
|
||||
return new Result<T, E>(default!, status);
|
||||
}
|
||||
|
||||
public void Deconstruct(out T value, out E status)
|
||||
{
|
||||
value = Value;
|
||||
status = Error;
|
||||
}
|
||||
|
||||
public override string ToString() => $"Value: {_value}, Status: {_error}";
|
||||
|
||||
public static implicit operator Result<T, E>(T data) => new(data, default);
|
||||
public static implicit operator Result<T, E>(E status) => new(default!, status);
|
||||
public static implicit operator bool(Result<T, E> result) => result.IsSuccess;
|
||||
}
|
||||
|
||||
public readonly ref struct RefResult<T, E>
|
||||
where E : struct, Enum
|
||||
{
|
||||
private readonly ref T _value;
|
||||
private readonly E _error;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the value. Undefined if the result is a failure.
|
||||
/// </summary>
|
||||
public ref T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
#if DEBUG || GHOST_EDITOR
|
||||
if (IsFailure)
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot access Value when Result is a failure. Error: {_error}");
|
||||
}
|
||||
#endif
|
||||
return ref _value;
|
||||
}
|
||||
}
|
||||
|
||||
public E Error => _error;
|
||||
public bool IsSuccess => EqualityComparer<E>.Default.Equals(_error, default);
|
||||
public bool IsFailure => !IsSuccess;
|
||||
|
||||
public RefResult(ref T value, E error)
|
||||
{
|
||||
_value = ref value;
|
||||
_error = error;
|
||||
}
|
||||
|
||||
public static RefResult<T, E> Success(ref T value)
|
||||
{
|
||||
return new RefResult<T, E>(ref value, default);
|
||||
}
|
||||
|
||||
public static RefResult<T, E> Failure(E error)
|
||||
{
|
||||
return new RefResult<T, E>(ref Unsafe.NullRef<T>(), error);
|
||||
}
|
||||
|
||||
public void Deconstruct(out bool success, out Ref<T> value, out E status)
|
||||
{
|
||||
success = IsSuccess;
|
||||
value = new Ref<T>(ref Value);
|
||||
status = Error;
|
||||
}
|
||||
|
||||
public override string ToString() => $"Value: {_value}, Status: {_error}";
|
||||
|
||||
public static implicit operator RefResult<T, E>(Ref<T> data) => new(ref data.Get(), default);
|
||||
public static implicit operator RefResult<T, E>(E error) => new(ref Unsafe.NullRef<T>(), error);
|
||||
public static implicit operator bool(RefResult<T, E> result) => result.IsSuccess;
|
||||
}
|
||||
|
||||
public static class ResultExtensions
|
||||
{
|
||||
public static void ThrowIfFailed(this Error result, [CallerArgumentExpression(nameof(result))] string? op = null)
|
||||
{
|
||||
if (result != Error.None)
|
||||
{
|
||||
throw new InvalidOperationException($"{op} failed: {result}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void ThrowIfFailed(this Result result, [CallerArgumentExpression(nameof(result))] string? op = null)
|
||||
{
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
throw new InvalidOperationException($"{op} failed: {result.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public static T GetValueOrThrow<T>(this Result<T> result, [CallerArgumentExpression(nameof(result))] string? op = null)
|
||||
{
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
throw new InvalidOperationException($"{op} failed: {result.Message}");
|
||||
}
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
public static T GetValueOrThrow<T, S>(this Result<T, S> result, [CallerArgumentExpression(nameof(result))] string? op = null)
|
||||
where S : struct, Enum
|
||||
{
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
throw new InvalidOperationException($"{op} failed: status {result.Error}");
|
||||
}
|
||||
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
public static T? GetValueOrDefault<T>(this Result<T> result, T? defaultValue = default)
|
||||
{
|
||||
return result.IsSuccess ? result.Value : defaultValue;
|
||||
}
|
||||
|
||||
public static T? GetValueOrDefault<T, S>(this Result<T, S> result, T? defaultValue = default)
|
||||
where S : struct, Enum
|
||||
{
|
||||
return result.IsSuccess ? result.Value : defaultValue;
|
||||
}
|
||||
|
||||
public static bool TryGetValue<T>(this Result<T> result, out T value)
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
value = result.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryGetValue<T, S>(this Result<T, S> result, out T value)
|
||||
where S : struct, Enum
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
value = result.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Result OnSuccess(this Result result, Action action)
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
action();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result<T> OnSuccess<T>(this Result<T> result, Action<T> action)
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
action(result.Value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result<T, E> OnSuccess<T, E>(this Result<T, E> result, Action<T> action)
|
||||
where E : struct, Enum
|
||||
{
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
action(result.Value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result OnFailed(this Result result, Action<string?> action)
|
||||
{
|
||||
if (result.IsFailure)
|
||||
{
|
||||
action(result.Message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result<T> OnFailed<T>(this Result<T> result, Action<string?> action)
|
||||
{
|
||||
if (result.IsFailure)
|
||||
{
|
||||
action(result.Message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result<T, E> OnFailed<T, E>(this Result<T, E> result, Action<E> action)
|
||||
where E : struct, Enum
|
||||
{
|
||||
if (result.IsFailure)
|
||||
{
|
||||
action(result.Error);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Result<U> Then<T, U>(this Result<T> result, Func<T, Result<U>> func)
|
||||
{
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return Result<U>.Failure(result.Message);
|
||||
}
|
||||
|
||||
return func(result.Value);
|
||||
}
|
||||
|
||||
public static Result<U, E> Then<T, U, E>(this Result<T, E> result, Func<T, Result<U, E>> func)
|
||||
where E : struct, Enum
|
||||
{
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return Result<U, E>.Failure(result.Error);
|
||||
}
|
||||
|
||||
return func(result.Value);
|
||||
}
|
||||
}
|
||||
67
src/Runtime/Ghost.Core/TypeHandle.cs
Normal file
67
src/Runtime/Ghost.Core/TypeHandle.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Core;
|
||||
|
||||
public readonly struct TypeHandle
|
||||
{
|
||||
public readonly IntPtr Value
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
private TypeHandle(IntPtr value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the space handle for the specified space.
|
||||
/// </summary>
|
||||
/// <param name="type">The space to get the handle for.</param>
|
||||
/// <returns>The space handle as a nint.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TypeHandle Get(Type type) => new TypeHandle(type.TypeHandle.Value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the space handle for the specified space.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The space to get the handle for.</typeparam>
|
||||
/// <returns>The space handle as a nint.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TypeHandle Get<T>() => Get(typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Converts a TypeHandle to a Type.
|
||||
/// </summary>
|
||||
/// <param name="handle">The TypeHandle to convert.</param>
|
||||
/// <returns>The corresponding Type.</returns>
|
||||
public Type? ToType()
|
||||
{
|
||||
return Type.GetTypeFromHandle(RuntimeTypeHandle.FromIntPtr(Value));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
|
||||
public static implicit operator TypeHandle(IntPtr value)
|
||||
{
|
||||
return new TypeHandle(value);
|
||||
}
|
||||
|
||||
public static implicit operator IntPtr(TypeHandle handle)
|
||||
{
|
||||
return handle.Value;
|
||||
}
|
||||
|
||||
public static implicit operator TypeHandle(Type type)
|
||||
{
|
||||
return Get(type);
|
||||
}
|
||||
|
||||
public static implicit operator Type?(TypeHandle handle)
|
||||
{
|
||||
return handle.ToType();
|
||||
}
|
||||
}
|
||||
36
src/Runtime/Ghost.Core/Utilities/BinaryReader.cs
Normal file
36
src/Runtime/Ghost.Core/Utilities/BinaryReader.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Core.Utilities;
|
||||
|
||||
public ref struct BinaryReader
|
||||
{
|
||||
private readonly Span<byte> _buffer;
|
||||
private int _position;
|
||||
|
||||
public int Position
|
||||
{
|
||||
readonly get => _position;
|
||||
set => _position = value;
|
||||
}
|
||||
|
||||
public BinaryReader(Span<byte> buffer)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
public T Read<T>()
|
||||
where T : unmanaged
|
||||
{
|
||||
var value = Unsafe.ReadUnaligned<T>(ref _buffer[_position]);
|
||||
_position += Unsafe.SizeOf<T>();
|
||||
return value;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> ReadBytes(int length)
|
||||
{
|
||||
var span = _buffer.Slice(_position, length);
|
||||
_position += length;
|
||||
return span;
|
||||
}
|
||||
}
|
||||
41
src/Runtime/Ghost.Core/Utilities/BinaryWriter.cs
Normal file
41
src/Runtime/Ghost.Core/Utilities/BinaryWriter.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Core.Utilities;
|
||||
|
||||
public ref struct BinaryWriter
|
||||
{
|
||||
private readonly Span<byte> _buffer;
|
||||
private int _position;
|
||||
|
||||
public int Position
|
||||
{
|
||||
readonly get => _position;
|
||||
set => _position = value;
|
||||
}
|
||||
|
||||
public BinaryWriter(Span<byte> buffer)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
public unsafe void Write<T>(scoped ref readonly T value)
|
||||
where T : unmanaged
|
||||
{
|
||||
Unsafe.WriteUnaligned(ref _buffer[_position], value);
|
||||
_position += sizeof(T);
|
||||
}
|
||||
|
||||
public void WriteBytes(ReadOnlySpan<byte> data)
|
||||
{
|
||||
data.CopyTo(_buffer.Slice(_position, data.Length));
|
||||
_position += data.Length;
|
||||
}
|
||||
|
||||
public Span<byte> GetSpan(int length)
|
||||
{
|
||||
var span = _buffer.Slice(_position, length);
|
||||
_position += length;
|
||||
return span;
|
||||
}
|
||||
}
|
||||
25
src/Runtime/Ghost.Core/Utilities/CollectionPool.cs
Normal file
25
src/Runtime/Ghost.Core/Utilities/CollectionPool.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Misaki.HighPerformance.Buffer;
|
||||
|
||||
namespace Ghost.Core.Utilities;
|
||||
|
||||
public class CollectionPool<TCollection, TItem>
|
||||
where TCollection : class, ICollection<TItem>, new()
|
||||
{
|
||||
internal static readonly ObjectPool<TCollection> s_pool = new ObjectPool<TCollection>(() => new TCollection(), null, 1);
|
||||
|
||||
public static TCollection Rent()
|
||||
{
|
||||
return s_pool.Rent();
|
||||
}
|
||||
|
||||
public static void Return(TCollection collection)
|
||||
{
|
||||
collection.Clear();
|
||||
s_pool.Return(collection);
|
||||
}
|
||||
}
|
||||
|
||||
public class ListPool<T> : CollectionPool<List<T>, T>;
|
||||
public class HashSetPool<T> : CollectionPool<HashSet<T>, T>;
|
||||
public class DictionaryPool<TKey, TValue> : CollectionPool<Dictionary<TKey, TValue>, KeyValuePair<TKey, TValue>>
|
||||
where TKey : notnull;
|
||||
9
src/Runtime/Ghost.Core/Utilities/EnumUtility.cs
Normal file
9
src/Runtime/Ghost.Core/Utilities/EnumUtility.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ghost.Core.Utilities;
|
||||
|
||||
internal class EnumUtility
|
||||
{
|
||||
}
|
||||
44
src/Runtime/Ghost.Core/Utilities/Hash.cs
Normal file
44
src/Runtime/Ghost.Core/Utilities/Hash.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Core.Utilities;
|
||||
|
||||
public static class Hash
|
||||
{
|
||||
private const ulong _PRIME1 = 0xfa517d6985796b7bul;
|
||||
private const ulong _PRIME2 = 0x589578278297b985ul;
|
||||
private const ulong _PRIME3 = 0x221147a447814b73ul;
|
||||
private const ulong _PRIME4 = 0x9e3779b97f4a7c15ul; // Golden Ratio
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Hash64(ulong a, ulong b)
|
||||
{
|
||||
return a ^ (b * _PRIME4 + (a << 6) + (a >> 2));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Hash64(ulong a, ulong b, ulong c)
|
||||
{
|
||||
ulong h1 = a * _PRIME1;
|
||||
ulong h2 = b * _PRIME2;
|
||||
ulong h3 = c * _PRIME3;
|
||||
|
||||
ulong h = h1 ^ h2 ^ h3;
|
||||
|
||||
h = (h ^ (h >> 33)) * _PRIME4;
|
||||
return h ^ (h >> 29);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Hash64(ulong a, ulong b, ulong c, ulong d)
|
||||
{
|
||||
ulong h1 = a * _PRIME1;
|
||||
ulong h2 = b * _PRIME2;
|
||||
ulong h3 = c * _PRIME3;
|
||||
ulong h4 = d * _PRIME4;
|
||||
|
||||
ulong h = h1 ^ h2 ^ h3 ^ h4;
|
||||
|
||||
h = (h ^ (h >> 33)) * _PRIME1;
|
||||
return h ^ (h >> 29);
|
||||
}
|
||||
}
|
||||
12
src/Runtime/Ghost.Core/Utilities/InternalResource.cs
Normal file
12
src/Runtime/Ghost.Core/Utilities/InternalResource.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Ghost.Core.Contracts;
|
||||
|
||||
namespace Ghost.Core.Utilities;
|
||||
|
||||
internal static class InternalResource
|
||||
{
|
||||
public static void Release<T>(ref T? resource)
|
||||
where T : IReleasable
|
||||
{
|
||||
resource?.InternalRelease();
|
||||
}
|
||||
}
|
||||
115
src/Runtime/Ghost.Core/Utilities/Win32Utility.cs
Normal file
115
src/Runtime/Ghost.Core/Utilities/Win32Utility.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using Misaki.HighPerformance.LowLevel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Versioning;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Ghost.Core.Utilities;
|
||||
|
||||
[SupportedOSPlatform("windows10.0.19041.0")]
|
||||
internal static unsafe partial class Win32Utility
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public readonly ref struct IID_PPV
|
||||
{
|
||||
public readonly Guid* iid;
|
||||
public readonly void** ppv;
|
||||
|
||||
public IID_PPV(Guid* iid, void** ppv)
|
||||
{
|
||||
this.iid = iid;
|
||||
this.ppv = ppv;
|
||||
}
|
||||
|
||||
public void Deconstruct(out Guid* iid, out void** ppv)
|
||||
{
|
||||
iid = this.iid;
|
||||
ppv = this.ppv;
|
||||
}
|
||||
}
|
||||
|
||||
public static Guid* IID_NULL
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IID.IID_NULL));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IID_PPV IID_PPV_ARGS<T>(ComPtr<T>* comPtr)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
return new IID_PPV(Windows.__uuidof<T>(), (void**)comPtr);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Attach<T>(ref this UniquePtr<T> uPtr, T* other)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
var ptr = uPtr.Get();
|
||||
if (ptr != null)
|
||||
{
|
||||
var refCount = ptr->Release();
|
||||
Debug.Assert((refCount != 0) || (ptr != other));
|
||||
}
|
||||
|
||||
uPtr = new UniquePtr<T>(other);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Dispose<T>(ref this UniquePtr<T> uPtr)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
var ptr = uPtr.Detach();
|
||||
if (ptr != null)
|
||||
{
|
||||
ptr->Release();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Result ToResult(this HRESULT hr, [CallerArgumentExpression(nameof(hr))] string? op = null)
|
||||
{
|
||||
if (hr.SUCCEEDED)
|
||||
{
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
return Result.Failure($"{op} failed with code {hr}");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void** ReleaseAndGetVoidAddressOf<T>(ref this ComPtr<T> comPtr)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
return (void**)comPtr.ReleaseAndGetAddressOf();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ComPtr<T> Move<T>(ref this ComPtr<T> comPtr)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
var copy = default(ComPtr<T>);
|
||||
comPtr.Swap(ref copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool HasFlag<T>(this uint flags, T flag)
|
||||
where T : Enum
|
||||
{
|
||||
return (flags & Unsafe.As<T, uint>(ref flag)) != 0;
|
||||
}
|
||||
|
||||
extension(MemoryLeakException)
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ThrowIfRefCountNonZero(uint count)
|
||||
{
|
||||
if (count != 0)
|
||||
{
|
||||
throw new MemoryLeakException($"Reference count is not zero: {count}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user