Refactor folder structure

This commit is contained in:
2026-02-18 00:50:46 +09:00
parent 426786397c
commit db8ca971a8
413 changed files with 2885 additions and 3634 deletions

View File

@@ -0,0 +1,7 @@
using Ghost.Core.Attributes;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Ghost.Graphics")]
[assembly: InternalsVisibleTo("Ghost.Engine")]
[assembly: EngineAssembly]

View File

@@ -0,0 +1,6 @@
namespace Ghost.Core.Attributes;
[AttributeUsage(AttributeTargets.Assembly)]
public sealed class EngineAssemblyAttribute : Attribute
{
}

View File

@@ -0,0 +1,11 @@
namespace Ghost.Core.Contracts;
public interface ICloneable
{
object Clone();
}
public interface ICloneable<T>
{
T Clone();
}

View File

@@ -0,0 +1,6 @@
namespace Ghost.Core.Contracts;
internal interface IReleasable
{
void InternalRelease();
}

View 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>

View 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);
}
}

View 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,
};
}
}

View 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);
}
}

View 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();
}
}

View 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);
}
}

View 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();
}
}

View 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;
}
}

View 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;
}
}

View 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;

View File

@@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Ghost.Core.Utilities;
internal class EnumUtility
{
}

View 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);
}
}

View 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();
}
}

View 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}");
}
}
}
}