forked from Misaki/GhostEngine
Enhance graphics engine and code organization
Added `InternalsVisibleTo` attribute for "Ghost.Graphics" and "Ghost.Editor" in `AssemblyInfo.cs`. Added a new `EngineAssemblyAttribute` in `EngineAssemblyAttribute.cs`. Added a reference to `Misaki.HighPerformance.Unsafe` in `Ghost.Core.csproj`. Added a new `Bounds` struct to represent axis-aligned bounding boxes in `Bounds.cs`. Added new `Color32` and `Color128` structs for color representation in `Color.cs`. Changed the namespace from `Ghost.Editor.Controls` to `Ghost.Editor.Core.Controls` in multiple files. Changed the implicit conversion operator in `ConstPtr<T>` to use a more descriptive parameter name in `ConstPtr.cs`. Changed the `Mesh` class to use `Color128` instead of `Color32` for color representation. Enhanced the `TypeCache` class to load types from assemblies marked with `EngineAssemblyAttribute`. Enhanced the `ProjectService` class to improve the `GetAllProjectAsync` method by filtering out bad projects. Enhanced the `GraphicsPipeline` class to support both DX12 and D3D12 graphics APIs. Enhanced the `Shader` class to include methods for compiling HLSL shaders and managing root signatures. Enhanced the `MeshRenderPass` class to utilize the new shader compilation methods. Refactored the `AppStateMachine` class to use private fields instead of static fields for state management. Refactored the `ComponentDataView` class to use the new namespace and improve organization. Refactored project references in `Ghost.Graphics.csproj` to include new dependencies and remove outdated ones. Made various adjustments to ensure consistency and improve code quality across multiple files.
This commit is contained in:
251
Ghost.Graphics/Data/Bounds.cs
Normal file
251
Ghost.Graphics/Data/Bounds.cs
Normal file
@@ -0,0 +1,251 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
public struct Bounds : IEquatable<Bounds>
|
||||
{
|
||||
/// <summary>
|
||||
/// The minimum point contained by the AABB.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If any component of <see cref="Min"/> is greater than <see cref="Max"/> then this AABB is invalid.
|
||||
/// </remarks>
|
||||
/// <seealso cref="IsValid"/>
|
||||
public Vector3 Min
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The maximum point contained by the AABB.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If any component of <see cref="Max"/> is less than <see cref="Min"/> then this AABB is invalid.
|
||||
/// </remarks>
|
||||
/// <seealso cref="IsValid"/>
|
||||
public Vector3 Max
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the AABB with the given minimum and maximum.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If you have a center and extents, you can call <see cref="CreateFromCenterAndExtents"/> or <see cref="CreateFromCenterAndHalfExtents"/>
|
||||
/// to create the AABB.
|
||||
/// </remarks>
|
||||
/// <param name="min">Minimum point inside AABB.</param>
|
||||
/// <param name="max">Maximum point inside AABB.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Bounds(Vector3 min, Vector3 max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the AABB from a center and extents.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This function takes full extents. It is the distance between <see cref="Min"/> and <see cref="Max"/>.
|
||||
/// If you have half extents, you can call <see cref="CreateFromCenterAndHalfExtents"/>.
|
||||
/// </remarks>
|
||||
/// <param name="center">Center of AABB.</param>
|
||||
/// <param name="extents">Full extents of AABB.</param>
|
||||
/// <returns>AABB created from inputs.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Bounds CreateFromCenterAndExtents(Vector3 center, Vector3 extents)
|
||||
{
|
||||
return CreateFromCenterAndHalfExtents(center, extents * 0.5f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the AABB from a center and half extents.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This function takes half extents. It is half the distance between <see cref="Min"/> and <see cref="Max"/>.
|
||||
/// If you have full extents, you can call <see cref="CreateFromCenterAndExtents"/>.
|
||||
/// </remarks>
|
||||
/// <param name="center">Center of AABB.</param>
|
||||
/// <param name="halfExtents">Half extents of AABB.</param>
|
||||
/// <returns>AABB created from inputs.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Bounds CreateFromCenterAndHalfExtents(Vector3 center, Vector3 halfExtents)
|
||||
{
|
||||
return new Bounds(center - halfExtents, center + halfExtents);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new AABB with zero extents, centered at the origin.
|
||||
/// </summary>
|
||||
public static Bounds Zero
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
return new Bounds(Vector3.Zero, Vector3.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the extents of the AABB.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Extents is the componentwise distance between min and max.
|
||||
/// </remarks>
|
||||
public readonly Vector3 Extents => Max - Min;
|
||||
|
||||
/// <summary>
|
||||
/// Computes the half extents of the AABB.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// HalfExtents is half of the componentwise distance between min and max. Subtracting HalfExtents from Center
|
||||
/// gives Min and adding HalfExtents to Center gives Max.
|
||||
/// </remarks>
|
||||
public readonly Vector3 HalfExtents => (Max - Min) * 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Computes the center of the AABB.
|
||||
/// </summary>
|
||||
public readonly Vector3 Center => (Max + Min) * 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Check if the AABB is valid.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An AABB is considered valid if <see cref="Min"/> is componentwise less than or equal to <see cref="Max"/>.
|
||||
/// </remarks>
|
||||
/// <returns>True if <see cref="Min"/> is componentwise less than or equal to <see cref="Max"/>.</returns>
|
||||
public readonly bool IsValid => Vector3.Dot(Min, Min) <= Vector3.Dot(Max, Max);
|
||||
|
||||
/// <summary>
|
||||
/// Computes the surface area for this axis aligned bounding box.
|
||||
/// </summary>
|
||||
public readonly float SurfaceArea
|
||||
{
|
||||
get
|
||||
{
|
||||
var diff = Max - Min;
|
||||
return 2 * Vector3.Dot(diff, new Vector3(diff.Y, diff.Z, diff.X));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if the input point is contained by the AABB.
|
||||
/// </summary>
|
||||
/// <param name="point">Point to test.</param>
|
||||
/// <returns>True if AABB contains the input point.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool Contains(Vector3 point) => Vector3.Dot(point, point) >= Vector3.Dot(Min, Min) && Vector3.Dot(point, point) <= Vector3.Dot(Max, Max);
|
||||
|
||||
/// <summary>
|
||||
/// Tests if the input AABB is contained entirely by this AABB.
|
||||
/// </summary>
|
||||
/// <param name="aabb">AABB to test.</param>
|
||||
/// <returns>True if input AABB is contained entirely by this AABB.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool Contains(Bounds aabb) => Vector3.Dot(Min, Min) <= Vector3.Dot(aabb.Min, aabb.Min) && Vector3.Dot(Max, Max) >= Vector3.Dot(aabb.Max, aabb.Max);
|
||||
|
||||
/// <summary>
|
||||
/// Tests if the input AABB overlaps this AABB.
|
||||
/// </summary>
|
||||
/// <param name="aabb">AABB to test.</param>
|
||||
/// <returns>True if input AABB overlaps with this AABB.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool Overlaps(Bounds aabb)
|
||||
{
|
||||
return Vector3.Dot(Max, Max) >= Vector3.Dot(aabb.Min, aabb.Min) && Vector3.Dot(Min, Min) <= Vector3.Dot(aabb.Max, aabb.Max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expands the AABB by the given signed distance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Positive distance expands the AABB while negative distance shrinks the AABB.
|
||||
/// </remarks>
|
||||
/// <param name="signedDistance">Signed distance to expand the AABB with.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Expand(float signedDistance)
|
||||
{
|
||||
Min -= new Vector3(signedDistance);
|
||||
Max += new Vector3(signedDistance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulates the given AABB.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Modifies this AABB so that it contains the given AABB. If the given AABB is already contained by this AABB,
|
||||
/// then this AABB doesn't change.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Contains(Unity.Mathematics.Geometry.MinMaxAABB)"/>
|
||||
/// <param name="aabb">AABB to encapsulate.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Encapsulate(Bounds aabb)
|
||||
{
|
||||
Min = Vector3.Min(Min, aabb.Min);
|
||||
Max = Vector3.Max(Max, aabb.Max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encapsulate the given point.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Modifies this AABB so that it contains the given point. If the given point is already contained by this AABB,
|
||||
/// then this AABB doesn't change.
|
||||
/// </remarks>
|
||||
/// <seealso cref="Contains(Unity.Mathematics.float3)"/>
|
||||
/// <param name="point">Point to encapsulate.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Encapsulate(Vector3 point)
|
||||
{
|
||||
Min = Vector3.Min(Min, point);
|
||||
Max = Vector3.Max(Max, point);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(Bounds other)
|
||||
{
|
||||
return Min.Equals(other.Min) && Max.Equals(other.Max);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is Bounds bounds)
|
||||
{
|
||||
return Equals(bounds);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public static bool operator ==(Bounds left, Bounds right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Bounds left, Bounds right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hash = 17;
|
||||
hash = hash * 31 + Min.GetHashCode();
|
||||
hash = hash * 31 + Max.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly override string ToString()
|
||||
{
|
||||
return string.Format("Bounds({0}, {1})", Min, Max);
|
||||
}
|
||||
}
|
||||
111
Ghost.Graphics/Data/Color.cs
Normal file
111
Ghost.Graphics/Data/Color.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color with 32-bit components."/>
|
||||
/// </summary>
|
||||
public struct Color32 : IEquatable<Color32>
|
||||
{
|
||||
public byte r;
|
||||
public byte g;
|
||||
public byte b;
|
||||
public byte a;
|
||||
|
||||
public Color32(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public Color32(Color color)
|
||||
: this(color.R, color.G, color.B, color.A)
|
||||
{
|
||||
}
|
||||
|
||||
public Color32(Color128 color128)
|
||||
: this((byte)(color128.r * 255.0f), (byte)(color128.g * 255.0f), (byte)(color128.b * 255.0f), (byte)(color128.a * 255.0f))
|
||||
{
|
||||
}
|
||||
|
||||
public readonly bool Equals(Color32 other)
|
||||
{
|
||||
return r == other.r && g == other.g && b == other.b && a == other.a;
|
||||
}
|
||||
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is Color32 color && Equals(color);
|
||||
}
|
||||
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(r, g, b, a);
|
||||
}
|
||||
|
||||
public static bool operator ==(Color32 left, Color32 right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Color32 left, Color32 right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color with 128-bit components.
|
||||
/// </summary>
|
||||
public struct Color128 : IEquatable<Color128>
|
||||
{
|
||||
public float r;
|
||||
public float g;
|
||||
public float b;
|
||||
public float a;
|
||||
|
||||
public Color128(float r, float g, float b, float a)
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public Color128(Color color)
|
||||
: this(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f)
|
||||
{
|
||||
}
|
||||
|
||||
public Color128(Color32 color32)
|
||||
: this(color32.r / 255.0f, color32.g / 255.0f, color32.b / 255.0f, color32.a / 255.0f)
|
||||
{
|
||||
}
|
||||
|
||||
public readonly bool Equals(Color128 other)
|
||||
{
|
||||
return r.Equals(other.r) && g.Equals(other.g) && b.Equals(other.b) && a.Equals(other.a);
|
||||
}
|
||||
|
||||
public override readonly bool Equals(object? obj)
|
||||
{
|
||||
return obj is Color128 color && Equals(color);
|
||||
}
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(r, g, b, a);
|
||||
}
|
||||
|
||||
public static bool operator ==(Color128 left, Color128 right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(Color128 left, Color128 right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a color with 32-bit components, the unmanaged version of <see cref="Color"/>."/>
|
||||
/// </summary>
|
||||
public struct Color32
|
||||
{
|
||||
public byte r;
|
||||
public byte g;
|
||||
public byte b;
|
||||
public byte a;
|
||||
|
||||
public Color32(byte r, byte g, byte b, byte a)
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
public Color32(Color color)
|
||||
: this(color.R, color.G, color.B, color.A)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,5 @@
|
||||
public enum GraphicsAPI
|
||||
{
|
||||
None,
|
||||
DX12
|
||||
D3D12
|
||||
}
|
||||
21
Ghost.Graphics/Data/Material.cs
Normal file
21
Ghost.Graphics/Data/Material.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
public class Material : IDisposable
|
||||
{
|
||||
// TODO: Pipeline state should be abstracted that can support multiple graphics APIs.
|
||||
private ComPtr<ID3D12PipelineState> _pipelineState;
|
||||
|
||||
public Shader Shader
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = Shader.Empty;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_pipelineState.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.Contracts;
|
||||
using Misaki.HighPerformance.Unsafe.Collections;
|
||||
using Misaki.HighPerformance.Unsafe.Helpers;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Vortice.Direct3D12;
|
||||
using Vortice.DXGI;
|
||||
using Vortice.Mathematics;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapacity = 512) : IDisposable
|
||||
public unsafe sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapacity = 512) : IDisposable
|
||||
{
|
||||
private UnsafeList<Vertex> _vertices = new(initialVertexCapacity, Allocator.Persistent);
|
||||
private UnsafeList<int> _indices = new(initialIndexCapacity, Allocator.Persistent);
|
||||
|
||||
private BoundingBox _bounds;
|
||||
private Bounds _boundingBox;
|
||||
|
||||
private IResource? _vertexBuffer;
|
||||
private IResource? _indexBuffer;
|
||||
@@ -23,9 +23,13 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
||||
|
||||
public Span<Vertex> Vertices => _vertices.AsSpan();
|
||||
public Span<int> Indices => _indices.AsSpan();
|
||||
public BoundingBox Bounds => _bounds;
|
||||
public int VertexCount => _vertices.Count;
|
||||
public int IndexCount => _indices.Count;
|
||||
public Bounds BoundingBox => _boundingBox;
|
||||
|
||||
public uint VertexCount => (uint)_vertices.Count;
|
||||
public uint IndexCount => (uint)_indices.Count;
|
||||
|
||||
internal ConstPtr<VertexBufferView> VertexBufferView => (VertexBufferView*)Unsafe.AsPointer(ref _vertexBufferView);
|
||||
internal ConstPtr<IndexBufferView> IndexBufferView => (IndexBufferView*)Unsafe.AsPointer(ref _indexBufferView);
|
||||
|
||||
~Mesh()
|
||||
{
|
||||
@@ -181,7 +185,7 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
||||
{
|
||||
if (_vertices.Count == 0)
|
||||
{
|
||||
_bounds = BoundingBox.Zero;
|
||||
_boundingBox = Bounds.Zero;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -194,14 +198,13 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
||||
max = Vector3.Max(max, pos);
|
||||
}
|
||||
|
||||
_bounds = new BoundingBox(min, max);
|
||||
_boundingBox = new Bounds(min, max);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uploads the mesh data to GPU resources.
|
||||
/// </summary>
|
||||
/// <param name="device">The Direct3D 12 device.</param>
|
||||
/// <param name="commandList">The Direct3D 12 command list to record the upload commands.</param>
|
||||
/// <param name="cmb">The command buffer to record the upload commands.</param>
|
||||
public unsafe void UploadMeshData(ICommandBuffer cmb)
|
||||
{
|
||||
if (VertexCount == 0 || IndexCount == 0)
|
||||
@@ -213,12 +216,12 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
||||
_indexBuffer?.Dispose();
|
||||
|
||||
var vertexBufferSize = (uint)(VertexCount * sizeof(Vertex));
|
||||
var indexBufferSize = (uint)(IndexCount * sizeof(int));
|
||||
var indexBufferSize = IndexCount * sizeof(int);
|
||||
_vertexBuffer = GraphicsPipeline.ResourceAllocator.CreateCopyDestinationBuffer(vertexBufferSize);
|
||||
_indexBuffer = GraphicsPipeline.ResourceAllocator.CreateCopyDestinationBuffer(indexBufferSize);
|
||||
|
||||
using var vertexUploadBuffer = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(vertexBufferSize);
|
||||
using var indexUploadBuffer = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(indexBufferSize);
|
||||
var vertexUploadBuffer = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(vertexBufferSize, true);
|
||||
var indexUploadBuffer = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(indexBufferSize, true);
|
||||
|
||||
vertexUploadBuffer.SetData(_vertices.AsSpan());
|
||||
indexUploadBuffer.SetData(_indices.AsSpan());
|
||||
@@ -226,6 +229,9 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
||||
cmb.CopyResource(_vertexBuffer, 0, vertexUploadBuffer, 0, vertexBufferSize);
|
||||
cmb.CopyResource(_indexBuffer, 0, indexUploadBuffer, 0, indexBufferSize);
|
||||
|
||||
cmb.BarrierTransition(_vertexBuffer, ResourceStates.CopyDest, ResourceStates.VertexAndConstantBuffer);
|
||||
cmb.BarrierTransition(_indexBuffer, ResourceStates.CopyDest, ResourceStates.IndexBuffer);
|
||||
|
||||
_vertexBufferView = new VertexBufferView
|
||||
{
|
||||
BufferLocation = _vertexBuffer.GPUAddress,
|
||||
@@ -237,7 +243,7 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
||||
{
|
||||
BufferLocation = _indexBuffer.GPUAddress,
|
||||
SizeInBytes = indexBufferSize,
|
||||
Format = Format.R32_SInt
|
||||
Format = Format.R32Uint
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
181
Ghost.Graphics/Data/Shader.cs
Normal file
181
Ghost.Graphics/Data/Shader.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Graphics.D3D12;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Win32;
|
||||
using Win32.Graphics.Direct3D;
|
||||
using Win32.Graphics.Direct3D.Fxc;
|
||||
using Win32.Graphics.Direct3D12;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
public unsafe class Shader
|
||||
{
|
||||
private static readonly Shader s_empty = new("ErrorShader");
|
||||
public static Shader Empty => s_empty;
|
||||
|
||||
private ComPtr<ID3D12RootSignature> _rootSignature;
|
||||
|
||||
public ConstPtr<ID3D12RootSignature> RootSignature => new(_rootSignature.Get());
|
||||
|
||||
public Shader(string shaderPath)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compiles HLSL source code from a string into shader bytecode.
|
||||
/// </summary>
|
||||
/// <param name="sourceCode">The string containing the HLSL code.</param>
|
||||
/// <param name="entryPoint">The name of the shader entry point function (e.g., "VSMain").</param>
|
||||
/// <param name="shaderProfile">The shader model to target (e.g., "vs_5_0", "ps_5_0").</param>
|
||||
/// <returns>A byte array containing the compiled shader bytecode.</returns>
|
||||
/// <exception cref="Exception">Thrown if shader compilation fails.</exception>
|
||||
public static unsafe byte[] CompileShader(string sourceCode, string entryPoint, string shaderProfile)
|
||||
{
|
||||
ComPtr<ID3DBlob> bytecodeBlob = default;
|
||||
ComPtr<ID3DBlob> errorBlob = default;
|
||||
|
||||
// Convert strings to null-terminated ASCII for the native function
|
||||
var sourceCodeBytes = Encoding.UTF8.GetBytes(sourceCode);
|
||||
var entryPointBytes = Encoding.UTF8.GetBytes(entryPoint);
|
||||
var shaderProfileBytes = Encoding.UTF8.GetBytes(shaderProfile);
|
||||
|
||||
// Call the D3DCompile function
|
||||
var hr = D3DCompile(
|
||||
sourceCodeBytes.AsSpan(),
|
||||
entryPointBytes.AsSpan(),
|
||||
shaderProfileBytes.AsSpan(),
|
||||
CompileFlags.EnableStrictness | CompileFlags.Debug,
|
||||
bytecodeBlob.GetAddressOf(),
|
||||
errorBlob.GetAddressOf()
|
||||
);
|
||||
|
||||
if (hr.Failure)
|
||||
{
|
||||
// If compilation fails, get the error message from the error blob
|
||||
var errorMessage = "Shader compilation failed.";
|
||||
if (errorBlob.Get() is not null)
|
||||
{
|
||||
errorMessage += "\n" + Encoding.ASCII.GetString(
|
||||
(byte*)errorBlob.Get()->GetBufferPointer(),
|
||||
(int)errorBlob.Get()->GetBufferSize()
|
||||
);
|
||||
}
|
||||
errorBlob.Dispose();
|
||||
throw new Exception(errorMessage);
|
||||
}
|
||||
|
||||
// Copy the compiled bytecode from the blob into a managed byte array
|
||||
var bytecode = new byte[bytecodeBlob.Get()->GetBufferSize()];
|
||||
Marshal.Copy((IntPtr)bytecodeBlob.Get()->GetBufferPointer(), bytecode, 0, bytecode.Length);
|
||||
|
||||
// Clean up the COM blobs
|
||||
bytecodeBlob.Dispose();
|
||||
errorBlob.Dispose();
|
||||
|
||||
return bytecode;
|
||||
}
|
||||
|
||||
private void LoadShader(Span<byte> byteCode)
|
||||
{
|
||||
using ComPtr<ID3D12ShaderReflection> reflector = default;
|
||||
fixed (void* codePtr = byteCode)
|
||||
{
|
||||
D3DReflect(codePtr, (nuint)byteCode.Length, __uuidof<ID3D12ShaderReflection>(), reflector.GetVoidAddressOf());
|
||||
}
|
||||
|
||||
ShaderDescription shaderDesc;
|
||||
reflector.Get()->GetDesc(&shaderDesc);
|
||||
|
||||
var rootParameters = new List<RootParameter>();
|
||||
var staticSamplers = new List<StaticSamplerDescription>();
|
||||
|
||||
for (uint i = 0; i < shaderDesc.BoundResources; i++)
|
||||
{
|
||||
ShaderInputBindDescription bindDesc;
|
||||
reflector.Get()->GetResourceBindingDesc(i, &bindDesc);
|
||||
|
||||
switch (bindDesc.Type)
|
||||
{
|
||||
case ShaderInputType.ConstantBuffer:
|
||||
var cbufferParam = new RootParameter();
|
||||
cbufferParam.ParameterType = RootParameterType.Cbv;
|
||||
cbufferParam.ShaderVisibility = ShaderVisibility.All;
|
||||
cbufferParam.Descriptor.RegisterSpace = bindDesc.Space;
|
||||
cbufferParam.Descriptor.ShaderRegister = bindDesc.BindPoint;
|
||||
|
||||
rootParameters.Add(cbufferParam);
|
||||
|
||||
var cbuffer = reflector.Get()->GetConstantBufferByName(bindDesc.Name);
|
||||
ShaderBufferDescription cbufferDesc;
|
||||
cbuffer->GetDesc(&cbufferDesc);
|
||||
|
||||
for (var j = 0u; j < cbufferDesc.Variables; j++)
|
||||
{
|
||||
var variable = cbuffer->GetVariableByIndex(j);
|
||||
ShaderVariableDescription varDesc;
|
||||
variable->GetDesc(&varDesc);
|
||||
}
|
||||
|
||||
break;
|
||||
case ShaderInputType.TextureBuffer:
|
||||
break;
|
||||
case ShaderInputType.Texture:
|
||||
break;
|
||||
case ShaderInputType.Sampler:
|
||||
var samplerDesc = new StaticSamplerDescription
|
||||
{
|
||||
Filter = Filter.MinMagMipLinear,
|
||||
AddressU = TextureAddressMode.Wrap,
|
||||
AddressV = TextureAddressMode.Wrap,
|
||||
AddressW = TextureAddressMode.Wrap,
|
||||
ShaderVisibility = ShaderVisibility.All,
|
||||
ShaderRegister = bindDesc.BindPoint,
|
||||
RegisterSpace = bindDesc.Space,
|
||||
};
|
||||
staticSamplers.Add(samplerDesc);
|
||||
break;
|
||||
|
||||
case ShaderInputType.UavRwTyped:
|
||||
break;
|
||||
case ShaderInputType.Structured:
|
||||
break;
|
||||
case ShaderInputType.UavRwStructured:
|
||||
break;
|
||||
case ShaderInputType.ByteAddress:
|
||||
break;
|
||||
case ShaderInputType.UavRwByteAddress:
|
||||
break;
|
||||
case ShaderInputType.UavAppendStructured:
|
||||
break;
|
||||
case ShaderInputType.UavConsumeStructured:
|
||||
break;
|
||||
case ShaderInputType.UavRwStructuredWithCounter:
|
||||
break;
|
||||
case ShaderInputType.RtAccelerationStructure:
|
||||
break;
|
||||
case ShaderInputType.UavFeedbackTexture:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateRootSignature()
|
||||
{
|
||||
var rootSignatureDesc = new RootSignatureDescription();
|
||||
|
||||
using ComPtr<ID3DBlob> signature = default;
|
||||
using ComPtr<ID3DBlob> error = default;
|
||||
|
||||
var hr = D3D12SerializeRootSignature(&rootSignatureDesc, RootSignatureVersion.V1_2, signature.GetAddressOf(), error.GetAddressOf());
|
||||
if (hr.Failure)
|
||||
{
|
||||
var errorMessage = System.Text.Encoding.ASCII.GetString((byte*)error.Get()->GetBufferPointer(), (int)error.Get()->GetBufferSize());
|
||||
throw new Exception($"Failed to serialize root signature: {errorMessage}");
|
||||
}
|
||||
|
||||
GraphicsPipeline.GetGraphicsDevice<D3D12GraphicsDevice>().NativeDevice.Ptr->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(), __uuidof<ID3D12RootSignature>(), _rootSignature.GetVoidAddressOf());
|
||||
}
|
||||
}
|
||||
16
Ghost.Graphics/Data/ShaderProperty.cs
Normal file
16
Ghost.Graphics/Data/ShaderProperty.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Win32.Graphics.Direct3D;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
public class ShaderProperty
|
||||
{
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ShaderInputType Type
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ internal readonly struct SwapChainPresenter
|
||||
get;
|
||||
}
|
||||
|
||||
public readonly IntPtr Hwnd
|
||||
public readonly nint Hwnd
|
||||
{
|
||||
get;
|
||||
}
|
||||
@@ -39,12 +39,12 @@ internal readonly struct SwapChainPresenter
|
||||
{
|
||||
Type = TargetType.Composition;
|
||||
SwapChainPanelNative = swapChainPanelNative;
|
||||
Hwnd = IntPtr.Zero;
|
||||
Hwnd = nint.Zero;
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public SwapChainPresenter(IntPtr hwnd, uint width, uint height)
|
||||
public SwapChainPresenter(nint hwnd, uint width, uint height)
|
||||
{
|
||||
Type = TargetType.Hwnd;
|
||||
Hwnd = hwnd;
|
||||
|
||||
@@ -1,9 +1,29 @@
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Win32.Graphics.Dxgi.Common;
|
||||
|
||||
namespace Ghost.Graphics.Data;
|
||||
|
||||
public struct Vertex(Vector4 position, Vector4 normal, Vector4 tangent, Color32 color, Vector4 uv)
|
||||
public struct Vertex(Vector4 position, Vector4 normal, Vector4 tangent, Color128 color, Vector4 uv)
|
||||
{
|
||||
public unsafe struct Semantic
|
||||
{
|
||||
public const Format ALIGNED_FORMAT = Format.R32G32B32A32Float;
|
||||
|
||||
private static readonly byte[] s_positionBytes = Encoding.UTF8.GetBytes("POSITION");
|
||||
private static readonly byte[] s_normalBytes = Encoding.UTF8.GetBytes("NORMAL");
|
||||
private static readonly byte[] s_tangentBytes = Encoding.UTF8.GetBytes("TANGENT");
|
||||
private static readonly byte[] s_colorBytes = Encoding.UTF8.GetBytes("COLOR");
|
||||
private static readonly byte[] s_uvBytes = Encoding.UTF8.GetBytes("UV");
|
||||
|
||||
public static byte* PositionName => (byte*)Unsafe.AsPointer(ref s_positionBytes[0]);
|
||||
public static byte* NormalName => (byte*)Unsafe.AsPointer(ref s_normalBytes[0]);
|
||||
public static byte* TangentName => (byte*)Unsafe.AsPointer(ref s_tangentBytes[0]);
|
||||
public static byte* ColorName => (byte*)Unsafe.AsPointer(ref s_colorBytes[0]);
|
||||
public static byte* UVName => (byte*)Unsafe.AsPointer(ref s_uvBytes[0]);
|
||||
}
|
||||
|
||||
public Vector4 Position
|
||||
{
|
||||
get;
|
||||
@@ -22,7 +42,7 @@ public struct Vertex(Vector4 position, Vector4 normal, Vector4 tangent, Color32
|
||||
set;
|
||||
} = tangent;
|
||||
|
||||
public Color32 Color
|
||||
public Color128 Color
|
||||
{
|
||||
get;
|
||||
set;
|
||||
|
||||
Reference in New Issue
Block a user