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:
6
Ghost.Core/AssemblyInfo.cs
Normal file
6
Ghost.Core/AssemblyInfo.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
using Ghost.Core.Attributes;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Ghost.Graphics")]
|
||||||
|
|
||||||
|
[assembly: EngineAssembly]
|
||||||
6
Ghost.Core/Attributes/EngineAssemblyAttribute.cs
Normal file
6
Ghost.Core/Attributes/EngineAssemblyAttribute.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Ghost.Core.Attributes;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Assembly)]
|
||||||
|
public sealed class EngineAssemblyAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -13,5 +13,5 @@ public unsafe readonly struct ConstPtr<T>
|
|||||||
public readonly T* Ptr => _ptr;
|
public readonly T* Ptr => _ptr;
|
||||||
|
|
||||||
public static implicit operator T*(ConstPtr<T> constPtr) => constPtr._ptr;
|
public static implicit operator T*(ConstPtr<T> constPtr) => constPtr._ptr;
|
||||||
public static implicit operator ConstPtr<T>(T* ptr) => new(ptr);
|
public static implicit operator ConstPtr<T>(T* pointer) => new(pointer);
|
||||||
}
|
}
|
||||||
@@ -7,4 +7,10 @@
|
|||||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Misaki.HighPerformance.Unsafe">
|
||||||
|
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using Ghost.Core.Attributes;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Ghost.Editor")]
|
[assembly: InternalsVisibleTo("Ghost.Editor")]
|
||||||
[assembly: InternalsVisibleTo("Ghost.Editor.Core")]
|
[assembly: InternalsVisibleTo("Ghost.Editor.Core")]
|
||||||
|
|
||||||
|
[assembly: EngineAssembly]
|
||||||
@@ -153,9 +153,24 @@ internal partial class ProjectService
|
|||||||
return await ProjectRepository.GetProjectByMetadataPathAsync(path) != null;
|
return await ProjectRepository.GetProjectByMetadataPathAsync(path) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAsyncEnumerable<ProjectInfo> GetAllProjectAsync()
|
public async IAsyncEnumerable<ProjectInfo> GetAllProjectAsync()
|
||||||
{
|
{
|
||||||
return ProjectRepository.GetAllProjectsAsync();
|
var badProjectList = new List<ProjectInfo>();
|
||||||
|
await foreach (var project in ProjectRepository.GetAllProjectsAsync())
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(project.MetadataPath) || !File.Exists(project.MetadataPath))
|
||||||
|
{
|
||||||
|
badProjectList.Add(project);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return project;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var badProject in badProjectList)
|
||||||
|
{
|
||||||
|
await ProjectRepository.RemoveProjectAsync(badProject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Result<ProjectMetadataInfo>> CreateProjectAsync(string projectName, string projectDirectory, Version engineVersion, string templatePath)
|
public async Task<Result<ProjectMetadataInfo>> CreateProjectAsync(string projectName, string projectDirectory, Version engineVersion, string templatePath)
|
||||||
|
|||||||
@@ -2,18 +2,18 @@
|
|||||||
|
|
||||||
internal partial class AppStateMachine : IDisposable, IAsyncDisposable
|
internal partial class AppStateMachine : IDisposable, IAsyncDisposable
|
||||||
{
|
{
|
||||||
private Dictionary<StateKey, Lazy<IAppState>> s_states = new();
|
private Dictionary<StateKey, Lazy<IAppState>> _states = new();
|
||||||
private IAppState? s_current;
|
private IAppState? _current;
|
||||||
|
|
||||||
public void RegisterState(StateKey key, Func<IAppState> stateFactory)
|
public void RegisterState(StateKey key, Func<IAppState> stateFactory)
|
||||||
{
|
{
|
||||||
s_states[key] = new(stateFactory);
|
_states[key] = new(stateFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task TransitionToAsync(StateKey stateKey, object? parameter = null)
|
public async Task TransitionToAsync(StateKey stateKey, object? parameter = null)
|
||||||
{
|
{
|
||||||
var previous = s_current;
|
var previous = _current;
|
||||||
if (!s_states.TryGetValue(stateKey, out var next))
|
if (!_states.TryGetValue(stateKey, out var next))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"State '{stateKey}' is not registered.");
|
throw new InvalidOperationException($"State '{stateKey}' is not registered.");
|
||||||
}
|
}
|
||||||
@@ -32,24 +32,25 @@ internal partial class AppStateMachine : IDisposable, IAsyncDisposable
|
|||||||
|
|
||||||
await next.Value.OnEnteredAsync(parameter);
|
await next.Value.OnEnteredAsync(parameter);
|
||||||
|
|
||||||
s_current = next.Value;
|
_current = next.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
s_states.Clear();
|
_states.Clear();
|
||||||
|
|
||||||
s_current?.OnExitingAsync().GetAwaiter().GetResult();
|
_current?.OnExitingAsync().GetAwaiter().GetResult();
|
||||||
s_current?.OnExitedAsync().GetAwaiter().GetResult();
|
_current?.OnExitedAsync().GetAwaiter().GetResult();
|
||||||
|
_current = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
s_states.Clear();
|
_states.Clear();
|
||||||
if (s_current != null)
|
if (_current != null)
|
||||||
{
|
{
|
||||||
await s_current.OnExitingAsync();
|
await _current.OnExitingAsync();
|
||||||
await s_current.OnExitedAsync();
|
await _current.OnExitedAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using Ghost.Core.Attributes;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Ghost.UnitTest")]
|
[assembly: InternalsVisibleTo("Ghost.UnitTest")]
|
||||||
[assembly: InternalsVisibleTo("Ghost.Editor")]
|
[assembly: InternalsVisibleTo("Ghost.Editor")]
|
||||||
|
|
||||||
|
[assembly: EngineAssembly]
|
||||||
@@ -5,7 +5,7 @@ using Microsoft.UI.Xaml.Data;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Windows.Globalization.NumberFormatting;
|
using Windows.Globalization.NumberFormatting;
|
||||||
|
|
||||||
namespace Ghost.Editor.Controls;
|
namespace Ghost.Editor.Core.Controls;
|
||||||
|
|
||||||
public sealed partial class PropertyField : ContentControl
|
public sealed partial class PropertyField : ContentControl
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<ResourceDictionary
|
<ResourceDictionary
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="using:Ghost.Editor.Controls">
|
xmlns:local="using:Ghost.Editor.Core.Controls">
|
||||||
|
|
||||||
<Style TargetType="local:PropertyField">
|
<Style TargetType="local:PropertyField">
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
using Ghost.Editor.Core.Controls;
|
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Ghost.Editor.Controls;
|
namespace Ghost.Editor.Core.Controls;
|
||||||
|
|
||||||
[TemplatePart(Name = "XComponent", Type = typeof(NumberBox))]
|
[TemplatePart(Name = "XComponent", Type = typeof(NumberBox))]
|
||||||
[TemplatePart(Name = "YComponent", Type = typeof(NumberBox))]
|
[TemplatePart(Name = "YComponent", Type = typeof(NumberBox))]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<ResourceDictionary
|
<ResourceDictionary
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="using:Ghost.Editor.Controls">
|
xmlns:local="using:Ghost.Editor.Core.Controls">
|
||||||
|
|
||||||
<Style TargetType="local:Vector3Field">
|
<Style TargetType="local:Vector3Field">
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using Microsoft.UI.Xaml;
|
|||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Ghost.Editor.Controls.Internal;
|
namespace Ghost.Editor.Core.Controls.Internal;
|
||||||
|
|
||||||
internal unsafe sealed partial class ComponentDataView : Control
|
internal unsafe sealed partial class ComponentDataView : Control
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<ResourceDictionary
|
<ResourceDictionary
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="using:Ghost.Editor.Controls.Internal">
|
xmlns:local="using:Ghost.Editor.Core.Controls.Internal">
|
||||||
|
|
||||||
<Style TargetType="local:ComponentDataView">
|
<Style TargetType="local:ComponentDataView">
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Ghost.Editor.Controls.Internal;
|
using Ghost.Editor.Core.Controls.Internal;
|
||||||
using Ghost.Editor.Core.Inspector;
|
using Ghost.Editor.Core.Inspector;
|
||||||
using Ghost.Editor.Core.Resources;
|
using Ghost.Editor.Core.Resources;
|
||||||
using Ghost.Engine.Editor;
|
using Ghost.Engine.Editor;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Ghost.Entities;
|
using Ghost.Core.Attributes;
|
||||||
|
using Ghost.Entities;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.Utilities;
|
namespace Ghost.Editor.Core.Utilities;
|
||||||
@@ -9,9 +10,24 @@ public static class TypeCache
|
|||||||
|
|
||||||
static TypeCache()
|
static TypeCache()
|
||||||
{
|
{
|
||||||
_types = AppDomain.CurrentDomain.GetAssemblies()
|
var loadableTypes = new List<Type>();
|
||||||
.SelectMany(assembly => assembly.DefinedTypes)
|
var assembliesToScan = AppDomain.CurrentDomain.GetAssemblies()
|
||||||
.ToArray();
|
.Where(a => a.GetCustomAttribute<EngineAssemblyAttribute>() != null);
|
||||||
|
|
||||||
|
foreach (var assembly in assembliesToScan)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
loadableTypes.AddRange(assembly.GetTypes());
|
||||||
|
}
|
||||||
|
catch (ReflectionTypeLoadException ex)
|
||||||
|
{
|
||||||
|
var types = ex.Types.Where(t => t != null);
|
||||||
|
loadableTypes.AddRange(types!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_types = loadableTypes.Select(t => t.GetTypeInfo()).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Type[] GetTypes()
|
public static Type[] GetTypes()
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using Ghost.Core.Attributes;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Ghost.UnitTest")]
|
[assembly: InternalsVisibleTo("Ghost.UnitTest")]
|
||||||
|
|
||||||
|
[assembly: EngineAssembly]
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Ghost.Editor.Controls;
|
using Ghost.Editor.Core.Controls;
|
||||||
using Ghost.Editor.Core.Inspector;
|
using Ghost.Editor.Core.Inspector;
|
||||||
using Ghost.Engine.Components;
|
using Ghost.Engine.Components;
|
||||||
using Ghost.Engine.Utilities;
|
using Ghost.Engine.Utilities;
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ using Ghost.Data.Services;
|
|||||||
using Ghost.Editor.Core.AssetHandle;
|
using Ghost.Editor.Core.AssetHandle;
|
||||||
using Ghost.Editor.View.Windows;
|
using Ghost.Editor.View.Windows;
|
||||||
using Ghost.Engine;
|
using Ghost.Engine;
|
||||||
|
using Ghost.Engine.Services;
|
||||||
|
using Ghost.Graphics;
|
||||||
|
using Microsoft.UI.Xaml.Media;
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.AppState;
|
namespace Ghost.Editor.Core.AppState;
|
||||||
|
|
||||||
@@ -18,6 +21,9 @@ internal class EditorState : IAppState
|
|||||||
App.Window = null;
|
App.Window = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_engineCore?.ShutDown();
|
||||||
|
CompositionTarget.Rendering -= OnRendering;
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +36,10 @@ internal class EditorState : IAppState
|
|||||||
|
|
||||||
ProjectService.CurrentProject = metadataInfo;
|
ProjectService.CurrentProject = metadataInfo;
|
||||||
|
|
||||||
|
_engineCore = App.GetService<EngineCore>();
|
||||||
|
_engineCore.Start(new Engine.Models.LaunchArgument());
|
||||||
|
CompositionTarget.Rendering += OnRendering;
|
||||||
|
|
||||||
_window = App.GetService<EngineEditorWindow>();
|
_window = App.GetService<EngineEditorWindow>();
|
||||||
_window.Activate();
|
_window.Activate();
|
||||||
|
|
||||||
@@ -38,22 +48,29 @@ internal class EditorState : IAppState
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnExitedAsync()
|
public Task OnExitedAsync()
|
||||||
{
|
{
|
||||||
if (_engineCore != null)
|
|
||||||
{
|
|
||||||
await _engineCore.ShutDownAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
_window?.Close();
|
_window?.Close();
|
||||||
_window = null;
|
_window = null;
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnEnteredAsync(object? parameter)
|
public Task OnEnteredAsync(object? parameter)
|
||||||
{
|
{
|
||||||
AssetDatabase.Initialize();
|
AssetDatabase.Initialize();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
_engineCore = App.GetService<EngineCore>();
|
private void OnRendering(object? sender, object e)
|
||||||
await _engineCore.StartAsync(new Engine.Models.LaunchArgument());
|
{
|
||||||
|
if (GraphicsPipeline.IsGpuReady())
|
||||||
|
{
|
||||||
|
_window?.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.High, () =>
|
||||||
|
{
|
||||||
|
PlayerLoopService.Update();
|
||||||
|
GraphicsPipeline.SignalCPUReady();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
63
Ghost.Editor/Properties/Resources.Designer.cs
generated
Normal file
63
Ghost.Editor/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Ghost.Editor.Properties {
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
|
/// </summary>
|
||||||
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
|
// class via a tool like ResGen or Visual Studio.
|
||||||
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
|
// with the /str option, or rebuild your VS project.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources {
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
|
get {
|
||||||
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Ghost.Editor.Properties.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
|
/// resource lookups using this strongly typed resource class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
|
get {
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ using Ghost.Graphics;
|
|||||||
using Ghost.Graphics.Contracts;
|
using Ghost.Graphics.Contracts;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using WinRT;
|
||||||
|
|
||||||
namespace Ghost.Editor.View.Pages.EngineEditor;
|
namespace Ghost.Editor.View.Pages.EngineEditor;
|
||||||
|
|
||||||
@@ -20,25 +21,17 @@ internal sealed partial class ScenePage : NavigationTabPage
|
|||||||
SwapChainPanel.SizeChanged += SwapChainPanel_SizeChanged;
|
SwapChainPanel.SizeChanged += SwapChainPanel_SizeChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRendering(object? sender, object e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SwapChainPanel_Loaded(object sender, RoutedEventArgs e)
|
private void SwapChainPanel_Loaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
//var guid = typeof(ISwapChainPanelNative2).GUID;
|
var guid = typeof(ISwapChainPanelNative.Interface).GUID;
|
||||||
//((IWinRTObject)SwapChainPanel).NativeObject.TryAs(guid, out var swapChainPanelNativeHandle);
|
((IWinRTObject)SwapChainPanel).NativeObject.TryAs(guid, out var swapChainPanelNativeHandle);
|
||||||
_swapChainPanelNative = ISwapChainPanelNative.FromSwapChainPanel(SwapChainPanel);
|
_swapChainPanelNative = new ISwapChainPanelNative(swapChainPanelNativeHandle);
|
||||||
|
|
||||||
//_swapChainPanelNative = new ISwapChainPanelNative2(swapChainPanelNativeHandle);
|
|
||||||
_renderView = GraphicsPipeline.GraphicsDevice.CreateRenderer(new(_swapChainPanelNative, (uint)SwapChainPanel.ActualWidth, (uint)SwapChainPanel.ActualHeight));
|
_renderView = GraphicsPipeline.GraphicsDevice.CreateRenderer(new(_swapChainPanelNative, (uint)SwapChainPanel.ActualWidth, (uint)SwapChainPanel.ActualHeight));
|
||||||
|
|
||||||
//CompositionTarget.Rendering += OnRendering;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SwapChainPanel_Unloaded(object sender, RoutedEventArgs e)
|
private void SwapChainPanel_Unloaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
//CompositionTarget.Rendering -= OnRendering;
|
|
||||||
_swapChainPanelNative.Dispose();
|
_swapChainPanelNative.Dispose();
|
||||||
_renderView?.Dispose();
|
_renderView?.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:data="using:Ghost.Data.Models"
|
xmlns:data="using:Ghost.Data.Models"
|
||||||
xmlns:editor="using:Ghost.Editor.Controls"
|
xmlns:editor="using:Ghost.Editor.Core.Controls"
|
||||||
xmlns:local="using:Ghost.Editor.View.Pages.Landing"
|
xmlns:local="using:Ghost.Editor.View.Pages.Landing"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
NavigationCacheMode="Enabled"
|
NavigationCacheMode="Enabled"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ using System.Collections.ObjectModel;
|
|||||||
|
|
||||||
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
||||||
|
|
||||||
internal partial class CreateProjectViewModel(NotificationService notificationService, ProjectService projectService, AppStateMachine stateService) : ObservableObject, INavigationAware
|
internal partial class CreateProjectViewModel(INotificationService notificationService, ProjectService projectService, AppStateMachine stateService) : ObservableObject, INavigationAware
|
||||||
{
|
{
|
||||||
public ObservableCollection<TemplateData> templates = new();
|
public ObservableCollection<TemplateData> templates = new();
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using Ghost.Core.Attributes;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Ghost.Editor")]
|
[assembly: InternalsVisibleTo("Ghost.Editor")]
|
||||||
|
|
||||||
|
[assembly: EngineAssembly]
|
||||||
@@ -2,25 +2,33 @@
|
|||||||
using Ghost.Engine.Services;
|
using Ghost.Engine.Services;
|
||||||
using Ghost.Graphics;
|
using Ghost.Graphics;
|
||||||
using Ghost.Graphics.Data;
|
using Ghost.Graphics.Data;
|
||||||
|
using Misaki.HighPerformance.Unsafe.Buffer;
|
||||||
|
|
||||||
namespace Ghost.Engine;
|
namespace Ghost.Engine;
|
||||||
|
|
||||||
internal class EngineCore
|
internal class EngineCore
|
||||||
{
|
{
|
||||||
public async Task StartAsync(LaunchArgument args)
|
public void Start(LaunchArgument args)
|
||||||
{
|
{
|
||||||
ActivationHandler.Handle(args);
|
ActivationHandler.Handle(args);
|
||||||
GraphicsPipeline.Initialize(GraphicsAPI.DX12);
|
|
||||||
|
AllocationManager.Initialize();
|
||||||
|
|
||||||
|
GraphicsPipeline.Initialize(GraphicsAPI.D3D12);
|
||||||
GraphicsPipeline.Start();
|
GraphicsPipeline.Start();
|
||||||
|
|
||||||
Logger.LogInfo("Engine started successfully.");
|
Logger.LogInfo("Engine started successfully.");
|
||||||
|
|
||||||
await Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ShutDownAsync()
|
public void IncrementCPUFenceValue()
|
||||||
{
|
{
|
||||||
|
GraphicsPipeline.SignalCPUReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShutDown()
|
||||||
|
{
|
||||||
|
GraphicsPipeline.SignalCPUReady();
|
||||||
GraphicsPipeline.Shutdown();
|
GraphicsPipeline.Shutdown();
|
||||||
await Task.CompletedTask;
|
AllocationManager.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
namespace Ghost.Engine.Services;
|
using Ghost.Entities;
|
||||||
|
|
||||||
|
namespace Ghost.Engine.Services;
|
||||||
|
|
||||||
internal static class PlayerLoopService
|
internal static class PlayerLoopService
|
||||||
{
|
{
|
||||||
private static Timer? _timer;
|
|
||||||
private static bool _isRunning = false;
|
private static bool _isRunning = false;
|
||||||
|
|
||||||
// TODO: Implement the actual time system
|
// TODO: Implement the actual time system
|
||||||
@@ -10,47 +11,47 @@ internal static class PlayerLoopService
|
|||||||
|
|
||||||
public static void Start()
|
public static void Start()
|
||||||
{
|
{
|
||||||
//if (_isRunning)
|
if (_isRunning)
|
||||||
//{
|
{
|
||||||
// return;
|
return;
|
||||||
//}
|
|
||||||
|
|
||||||
//foreach (var gameObject in SceneManager.QueryRootGameObjects())
|
|
||||||
//{
|
|
||||||
// gameObject.Start();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//_timer ??= new Timer(FixedUpdate, null, 0, (int)(fixedDeltaTime * 1000));
|
|
||||||
|
|
||||||
//while (_isRunning)
|
|
||||||
//{
|
|
||||||
// Update();
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Update()
|
for (var i = 0; i < World.WorldCount; i++)
|
||||||
{
|
{
|
||||||
//foreach (var gameObject in SceneManager.QueryRootGameObjects())
|
var world = World.GetWorld(i);
|
||||||
//{
|
|
||||||
// gameObject.Update();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//foreach (var gameObject in SceneManager.QueryRootGameObjects())
|
foreach (var script in world.QueryScript())
|
||||||
//{
|
{
|
||||||
// gameObject.LateUpdate();
|
script.Start();
|
||||||
//}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void FixedUpdate(object? state)
|
public static void Update()
|
||||||
{
|
{
|
||||||
//foreach (var gameObject in SceneManager.QueryRootGameObjects())
|
for (var i = 0; i < World.WorldCount; i++)
|
||||||
//{
|
{
|
||||||
// gameObject.FixedUpdate();
|
var world = World.GetWorld(i);
|
||||||
//}
|
world.SystemStorage.UpdateSystems();
|
||||||
|
|
||||||
|
foreach (var script in world.QueryScript())
|
||||||
|
{
|
||||||
|
script.Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Stop()
|
public static void Shutdown()
|
||||||
{
|
{
|
||||||
|
for (var i = 0; i < World.WorldCount; i++)
|
||||||
|
{
|
||||||
|
var world = World.GetWorld(i);
|
||||||
|
foreach (var script in world.QueryScript())
|
||||||
|
{
|
||||||
|
script.Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_isRunning = false;
|
_isRunning = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,11 @@
|
|||||||
global using GenerationID = System.UInt16;
|
global using GenerationID = System.UInt16;
|
||||||
global using WorldID = System.UInt16;
|
global using WorldID = System.UInt16;
|
||||||
|
|
||||||
|
using Ghost.Core.Attributes;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Ghost.Engine")]
|
[assembly: InternalsVisibleTo("Ghost.Engine")]
|
||||||
[assembly: InternalsVisibleTo("Ghost.Editor.Core")]
|
[assembly: InternalsVisibleTo("Ghost.Editor.Core")]
|
||||||
[assembly: InternalsVisibleTo("Ghost.UnitTest")]
|
[assembly: InternalsVisibleTo("Ghost.UnitTest")]
|
||||||
|
|
||||||
|
[assembly: EngineAssembly]
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using Ghost.Core.Attributes;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Ghost.Engine")]
|
[assembly: InternalsVisibleTo("Ghost.Engine")]
|
||||||
[assembly: InternalsVisibleTo("Ghost.Editor")]
|
[assembly: InternalsVisibleTo("Ghost.Editor")]
|
||||||
[assembly: InternalsVisibleTo("Ghost.Editor.Core")]
|
[assembly: InternalsVisibleTo("Ghost.Editor.Core")]
|
||||||
[assembly: InternalsVisibleTo("Ghost.UnitTest")]
|
[assembly: InternalsVisibleTo("Ghost.UnitTest")]
|
||||||
|
|
||||||
|
[assembly: EngineAssembly]
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
namespace Ghost.Graphics.Contracts;
|
using Ghost.Graphics.Data;
|
||||||
|
using Win32.Graphics.Direct3D12;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
public interface ICommandBuffer
|
public interface ICommandBuffer
|
||||||
{
|
{
|
||||||
|
public void DrawMesh(Mesh mesh);
|
||||||
public void CopyResource(IResource dstResource, uint dstOffset, IResource srcResource, uint srcOffset, uint size);
|
public void CopyResource(IResource dstResource, uint dstOffset, IResource srcResource, uint srcOffset, uint size);
|
||||||
|
public void BarrierTransition(IResource resource, ResourceStates beforeState, ResourceStates afterState);
|
||||||
}
|
}
|
||||||
@@ -15,4 +15,6 @@ internal interface IGraphicsDevice : IDisposable
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IRenderer CreateRenderer(in SwapChainPresenter swapChainSurface);
|
public IRenderer CreateRenderer(in SwapChainPresenter swapChainSurface);
|
||||||
|
public void RemoveRenderer(IRenderer renderer);
|
||||||
|
public void InitializePendingRenderers();
|
||||||
}
|
}
|
||||||
5
Ghost.Graphics/Contracts/IPipelineResource.cs
Normal file
5
Ghost.Graphics/Contracts/IPipelineResource.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
namespace Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
|
internal interface IPipelineResource : IDisposable
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ internal interface IRenderer : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void ExecutePendingResize();
|
public void ExecutePendingResize();
|
||||||
|
|
||||||
|
public void Initialize();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Renders the current content to the output target.
|
/// Renders the current content to the output target.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ public interface IResource : IDisposable
|
|||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TempResource
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetData<T>(Span<T> data)
|
public void SetData<T>(Span<T> data)
|
||||||
where T : unmanaged;
|
where T : unmanaged;
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
using Vortice.Direct3D12;
|
using Win32.Graphics.Direct3D12;
|
||||||
|
|
||||||
namespace Ghost.Graphics.Contracts;
|
namespace Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
internal unsafe interface IResourceAllocator : IDisposable
|
internal unsafe interface IResourceAllocator : IDisposable
|
||||||
{
|
{
|
||||||
public abstract static IResourceAllocator Create();
|
public IResource CreateUploadBuffer(uint sizeInBytes, bool tempResource = false, ResourceFlags flags = ResourceFlags.None);
|
||||||
|
public IResource CreateCopyDestinationBuffer(uint sizeInBytes, bool tempResource = false, ResourceFlags flags = ResourceFlags.None);
|
||||||
|
|
||||||
public IResource CreateUploadBuffer(uint sizeInBytes, ResourceFlags flags = ResourceFlags.None);
|
public void ReleaseTempResource();
|
||||||
public IResource CreateCopyDestinationBuffer(uint sizeInBytes, ResourceFlags flags = ResourceFlags.None);
|
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,13 @@
|
|||||||
|
|
||||||
namespace Ghost.Graphics.Contracts;
|
namespace Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
|
||||||
|
public unsafe readonly struct ISwapChainPanelNative
|
||||||
|
{
|
||||||
[ComImport]
|
[ComImport]
|
||||||
[Guid("63aad0b8-7c24-40ff-85a8-640d944cc325")]
|
[Guid("63aad0b8-7c24-40ff-85a8-640d944cc325")]
|
||||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||||
internal interface ISwapChainPanelNativeRaw
|
internal interface Interface
|
||||||
{
|
{
|
||||||
// IUnknown: QueryInterface, AddRef, Release
|
// IUnknown: QueryInterface, AddRef, Release
|
||||||
void QueryInterface(in Guid riid, out IntPtr ppvObject);
|
void QueryInterface(in Guid riid, out IntPtr ppvObject);
|
||||||
@@ -16,9 +19,6 @@ internal interface ISwapChainPanelNativeRaw
|
|||||||
int SetSwapChain(IntPtr swapChainPtr);
|
int SetSwapChain(IntPtr swapChainPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
|
|
||||||
public unsafe readonly struct ISwapChainPanelNative
|
|
||||||
{
|
|
||||||
private readonly IntPtr _nativePtr;
|
private readonly IntPtr _nativePtr;
|
||||||
public readonly IntPtr NativePointer => _nativePtr;
|
public readonly IntPtr NativePointer => _nativePtr;
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ public unsafe readonly struct ISwapChainPanelNative
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Query for ISwapChainPanelNative
|
// Query for ISwapChainPanelNative
|
||||||
var iid = typeof(ISwapChainPanelNativeRaw).GUID;
|
var iid = typeof(Interface).GUID;
|
||||||
var result = Marshal.QueryInterface(unknown, in iid, out var nativePtr);
|
var result = Marshal.QueryInterface(unknown, in iid, out var nativePtr);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
{
|
{
|
||||||
@@ -51,10 +51,9 @@ public unsafe readonly struct ISwapChainPanelNative
|
|||||||
|
|
||||||
public int SetSwapChain(IntPtr swapChainPtr)
|
public int SetSwapChain(IntPtr swapChainPtr)
|
||||||
{
|
{
|
||||||
var raw = (ISwapChainPanelNativeRaw)Marshal.GetObjectForIUnknown(_nativePtr);
|
var vtbl = *(void***)_nativePtr;
|
||||||
var hr = raw.SetSwapChain(swapChainPtr);
|
var setSwapChainFn = (delegate* unmanaged<IntPtr, IntPtr, int>)vtbl[3];
|
||||||
Marshal.ReleaseComObject(raw);
|
return setSwapChainFn(_nativePtr, swapChainPtr);
|
||||||
return hr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() => Marshal.Release(_nativePtr);
|
public void Dispose() => Marshal.Release(_nativePtr);
|
||||||
|
|||||||
42
Ghost.Graphics/D3D12/D3D12CommandBuffer.cs
Normal file
42
Ghost.Graphics/D3D12/D3D12CommandBuffer.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using Ghost.Core;
|
||||||
|
using Ghost.Graphics.Contracts;
|
||||||
|
using Ghost.Graphics.Data;
|
||||||
|
using Win32.Graphics.Direct3D;
|
||||||
|
using Win32.Graphics.Direct3D12;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
|
internal unsafe class D3D12CommandBuffer : ICommandBuffer
|
||||||
|
{
|
||||||
|
private readonly ConstPtr<ID3D12GraphicsCommandList10> _commandList;
|
||||||
|
|
||||||
|
internal ConstPtr<ID3D12GraphicsCommandList10> CommandList => _commandList;
|
||||||
|
|
||||||
|
public D3D12CommandBuffer(ID3D12GraphicsCommandList10* commandList)
|
||||||
|
{
|
||||||
|
_commandList = commandList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DrawMesh(Mesh mesh)
|
||||||
|
{
|
||||||
|
_commandList.Ptr->IASetPrimitiveTopology(PrimitiveTopology.TriangleList);
|
||||||
|
_commandList.Ptr->IASetVertexBuffers(0, 1, mesh.VertexBufferView);
|
||||||
|
_commandList.Ptr->IASetIndexBuffer(mesh.IndexBufferView);
|
||||||
|
|
||||||
|
_commandList.Ptr->DrawIndexedInstanced(mesh.IndexCount, 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyResource(IResource dstResource, uint dstOffset, IResource srcResource, uint srcOffset, uint size)
|
||||||
|
{
|
||||||
|
var dstDXResource = (D3D12Resource)dstResource;
|
||||||
|
var srcDXResource = (D3D12Resource)srcResource;
|
||||||
|
|
||||||
|
_commandList.Ptr->CopyBufferRegion(dstDXResource.NativeResource, dstOffset, srcDXResource.NativeResource, srcOffset, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BarrierTransition(IResource resource, ResourceStates beforeState, ResourceStates afterState)
|
||||||
|
{
|
||||||
|
var dxResource = (D3D12Resource)resource;
|
||||||
|
_commandList.Ptr->ResourceBarrierTransition(dxResource.NativeResource.Ptr, beforeState, afterState);
|
||||||
|
}
|
||||||
|
}
|
||||||
35
Ghost.Graphics/D3D12/D3D12DebugLayer.cs
Normal file
35
Ghost.Graphics/D3D12/D3D12DebugLayer.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using Ghost.Graphics.Contracts;
|
||||||
|
using Win32;
|
||||||
|
using Win32.Graphics.Direct3D12;
|
||||||
|
using Win32.Graphics.Dxgi;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
|
internal unsafe class D3D12DebugLayer : IDebugLayer
|
||||||
|
{
|
||||||
|
private readonly ComPtr<ID3D12Debug6> _d3d12Debug;
|
||||||
|
private readonly ComPtr<IDXGIDebug1> _dxgiDebug;
|
||||||
|
private readonly ComPtr<IDXGIInfoQueue> _dxgiInfoQueue;
|
||||||
|
|
||||||
|
public D3D12DebugLayer()
|
||||||
|
{
|
||||||
|
D3D12GetDebugInterface(__uuidof<ID3D12Debug6>(), _d3d12Debug.GetVoidAddressOf());
|
||||||
|
_d3d12Debug.Get()->EnableDebugLayer();
|
||||||
|
|
||||||
|
DXGIGetDebugInterface1(0u, __uuidof<IDXGIDebug1>(), _dxgiDebug.GetVoidAddressOf());
|
||||||
|
_dxgiDebug.Get()->EnableLeakTrackingForThread();
|
||||||
|
|
||||||
|
DXGIGetDebugInterface1(0u, __uuidof<IDXGIInfoQueue>(), _dxgiInfoQueue.GetVoidAddressOf());
|
||||||
|
_dxgiInfoQueue.Get()->SetBreakOnSeverity(DXGI_DEBUG_ALL, InfoQueueMessageSeverity.Error, true);
|
||||||
|
_dxgiInfoQueue.Get()->SetBreakOnSeverity(DXGI_DEBUG_ALL, InfoQueueMessageSeverity.Corruption, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_dxgiDebug.Get()->ReportLiveObjects(DXGI_DEBUG_ALL, ReportLiveObjectFlags.Detail | ReportLiveObjectFlags.IgnoreInternal);
|
||||||
|
|
||||||
|
_d3d12Debug.Dispose();
|
||||||
|
_dxgiDebug.Dispose();
|
||||||
|
_dxgiInfoQueue.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,61 +6,56 @@ using Win32;
|
|||||||
using Win32.Graphics.Direct3D;
|
using Win32.Graphics.Direct3D;
|
||||||
using Win32.Graphics.Direct3D12;
|
using Win32.Graphics.Direct3D12;
|
||||||
using Win32.Graphics.Dxgi;
|
using Win32.Graphics.Dxgi;
|
||||||
using static Win32.Apis;
|
|
||||||
using static Win32.Graphics.Direct3D12.Apis;
|
|
||||||
using static Win32.Graphics.Dxgi.Apis;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.DX12;
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
internal unsafe class DX12GraphicsDevice : IGraphicsDevice
|
internal unsafe class D3D12GraphicsDevice : IGraphicsDevice
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
private readonly DX12DebugLayer _debugLayer;
|
private readonly D3D12DebugLayer _debugLayer;
|
||||||
#endif
|
#endif
|
||||||
private readonly ComPtr<IDXGIFactory7> _dxgiFactory;
|
private ComPtr<IDXGIFactory7> _dxgiFactory;
|
||||||
private readonly ComPtr<ID3D12Device14> _device;
|
private ComPtr<ID3D12Device14> _device;
|
||||||
private readonly ComPtr<ID3D12CommandQueue> _commandQueue;
|
private ComPtr<ID3D12CommandQueue> _commandQueue;
|
||||||
|
|
||||||
|
private ImmutableArray<IRenderer> _initializeQueue;
|
||||||
private ImmutableArray<IRenderer> _renderers;
|
private ImmutableArray<IRenderer> _renderers;
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
public static GraphicsAPI TargetAPI => GraphicsAPI.DX12;
|
public static GraphicsAPI TargetAPI => GraphicsAPI.D3D12;
|
||||||
|
public ReadOnlySpan<IRenderer> InitializeQueue => _initializeQueue.AsSpan();
|
||||||
public ReadOnlySpan<IRenderer> Renderers => _renderers.AsSpan();
|
public ReadOnlySpan<IRenderer> Renderers => _renderers.AsSpan();
|
||||||
|
|
||||||
public ConstPtr<ID3D12Device14> NativeDevice => new(_device.Get());
|
public ConstPtr<ID3D12Device14> NativeDevice => new(_device.Get());
|
||||||
public ConstPtr<IDXGIFactory7> DXGIFactory => new(_dxgiFactory.Get());
|
public ConstPtr<IDXGIFactory7> DXGIFactory => new(_dxgiFactory.Get());
|
||||||
public ConstPtr<ID3D12CommandQueue> CommandQueue => new(_commandQueue.Get());
|
public ConstPtr<ID3D12CommandQueue> CommandQueue => new(_commandQueue.Get());
|
||||||
|
|
||||||
public DX12GraphicsDevice()
|
public D3D12GraphicsDevice()
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
_debugLayer = new DX12DebugLayer();
|
_debugLayer = new D3D12DebugLayer();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
InitializeDevice();
|
InitializeDevice();
|
||||||
InitializeCommandQueue();
|
InitializeCommandQueue();
|
||||||
|
|
||||||
|
_initializeQueue = ImmutableArray<IRenderer>.Empty;
|
||||||
_renderers = ImmutableArray<IRenderer>.Empty;
|
_renderers = ImmutableArray<IRenderer>.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeDevice()
|
private void InitializeDevice()
|
||||||
{
|
{
|
||||||
fixed (void* factoryPtr = &_dxgiFactory)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
CreateDXGIFactory2(true, __uuidof<IDXGIFactory2>(), &factoryPtr);
|
CreateDXGIFactory2(true, __uuidof<IDXGIFactory7>(), _dxgiFactory.GetVoidAddressOf());
|
||||||
//factory = DXGI.CreateDXGIFactory2<IDXGIFactory7>(true);
|
|
||||||
#else
|
#else
|
||||||
//factory = DXGI.CreateDXGIFactory2<IDXGIFactory7>(false);
|
CreateDXGIFactory2(false, __uuidof<IDXGIFactory7>(), _dxgiFactory.GetVoidAddressOf());
|
||||||
CreateDXGIFactory2(false, __uuidof<IDXGIFactory2>(), &factoryPtr);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
|
|
||||||
using ComPtr<IDXGIAdapter1> adapter = default;
|
using ComPtr<IDXGIAdapter1> adapter = default;
|
||||||
|
|
||||||
for (uint adapterIndex = 0;
|
for (uint adapterIndex = 0;
|
||||||
_dxgiFactory.Get()->EnumAdapterByGpuPreference(adapterIndex, GpuPreference.HighPerformance, __uuidof<IDXGIAdapter1>(), (void**)adapter.ReleaseAndGetAddressOf()).Success;
|
_dxgiFactory.Get()->EnumAdapterByGpuPreference(adapterIndex, GpuPreference.HighPerformance, __uuidof<IDXGIAdapter1>(), adapter.ReleaseAndGetVoidAddressOf()).Success;
|
||||||
adapterIndex++)
|
adapterIndex++)
|
||||||
{
|
{
|
||||||
AdapterDescription1 desc = default;
|
AdapterDescription1 desc = default;
|
||||||
@@ -72,14 +67,11 @@ internal unsafe class DX12GraphicsDevice : IGraphicsDevice
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
fixed (void* devicePtr = &_device)
|
if (D3D12CreateDevice((IUnknown*)adapter.Get(), FeatureLevel.Level_11_0, __uuidof<ID3D12Device14>(), _device.GetVoidAddressOf()).Success)
|
||||||
{
|
|
||||||
if (D3D12CreateDevice((IUnknown*)adapter.Get(), FeatureLevel.Level_11_0, __uuidof<ID3D12Device>(), (void**)devicePtr).Success)
|
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (_device.Get() == null)
|
if (_device.Get() == null)
|
||||||
{
|
{
|
||||||
@@ -98,26 +90,51 @@ internal unsafe class DX12GraphicsDevice : IGraphicsDevice
|
|||||||
|
|
||||||
fixed (void* queuePtr = &_commandQueue)
|
fixed (void* queuePtr = &_commandQueue)
|
||||||
{
|
{
|
||||||
_device.Get()->CreateCommandQueue(&queueDesc, __uuidof<ID3D12CommandQueue>(), &queuePtr);
|
_device.Get()->CreateCommandQueue(&queueDesc, __uuidof<ID3D12CommandQueue>(), (void**)queuePtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRenderer CreateRenderer(in SwapChainPresenter presenter)
|
public IRenderer CreateRenderer(in SwapChainPresenter presenter)
|
||||||
{
|
{
|
||||||
var renderView = new DX12Renderer(this, in presenter);
|
var renderView = new D3D12Renderer(this, in presenter);
|
||||||
ImmutableInterlocked.Update(ref _renderers, old => old.Add(renderView));
|
ImmutableInterlocked.Update(ref _initializeQueue, old => old.Add(renderView));
|
||||||
|
|
||||||
return renderView;
|
return renderView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveRenderer(IRenderer renderer)
|
public void RemoveRenderer(IRenderer renderer)
|
||||||
{
|
{
|
||||||
if (renderer is DX12Renderer dx12RenderView)
|
if (renderer is D3D12Renderer dx12RenderView)
|
||||||
{
|
{
|
||||||
dx12RenderView.Dispose();
|
dx12RenderView.Dispose();
|
||||||
|
|
||||||
|
var index = _initializeQueue.IndexOf(dx12RenderView);
|
||||||
|
if (index > -1)
|
||||||
|
{
|
||||||
|
ImmutableInterlocked.Update(ref _initializeQueue, old => old.RemoveAt(index));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
ImmutableInterlocked.Update(ref _renderers, old => old.Remove(dx12RenderView));
|
ImmutableInterlocked.Update(ref _renderers, old => old.Remove(dx12RenderView));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializePendingRenderers()
|
||||||
|
{
|
||||||
|
if (_initializeQueue.IsEmpty)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var renderer in _initializeQueue.AsSpan())
|
||||||
|
{
|
||||||
|
renderer.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableInterlocked.Update(ref _renderers, old => old.AddRange(_initializeQueue));
|
||||||
|
_initializeQueue = _initializeQueue.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
359
Ghost.Graphics/D3D12/D3D12Renderer.cs
Normal file
359
Ghost.Graphics/D3D12/D3D12Renderer.cs
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
using Ghost.Graphics.Contracts;
|
||||||
|
using Ghost.Graphics.D3D12.Utilities;
|
||||||
|
using Ghost.Graphics.Data;
|
||||||
|
using Ghost.Graphics.RenderPasses;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using Win32;
|
||||||
|
using Win32.Graphics.Direct3D12;
|
||||||
|
using Win32.Graphics.Dxgi;
|
||||||
|
using Win32.Graphics.Dxgi.Common;
|
||||||
|
using Win32.Numerics;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
|
// TODO: We should split the renderer and swap chain into different classes to allow for more flexibility in rendering pipelines.
|
||||||
|
// Each renderer can have a render target (swap chain or texture).
|
||||||
|
// When render target is null, skip the render pass execution.
|
||||||
|
internal unsafe class D3D12Renderer : IRenderer
|
||||||
|
{
|
||||||
|
private struct FrameResource : IDisposable
|
||||||
|
{
|
||||||
|
public ComPtr<ID3D12CommandAllocator> commandAllocator;
|
||||||
|
public ComPtr<ID3D12GraphicsCommandList10> commandList;
|
||||||
|
public ComPtr<ID3D12Resource> backBuffer;
|
||||||
|
|
||||||
|
public ICommandBuffer commandBuffer;
|
||||||
|
public uint backBufferDescriptorIndexes;
|
||||||
|
public ulong fenceValue;
|
||||||
|
|
||||||
|
public FrameResource(D3D12Renderer renderer, uint index)
|
||||||
|
{
|
||||||
|
renderer._graphicsDevice.NativeDevice.Ptr->CreateCommandAllocator(CommandListType.Direct, __uuidof<ID3D12CommandAllocator>(), commandAllocator.GetVoidAddressOf());
|
||||||
|
renderer._graphicsDevice.NativeDevice.Ptr->CreateCommandList(0u, CommandListType.Direct, commandAllocator.Get(), null, __uuidof<ID3D12GraphicsCommandList10>(), commandList.GetVoidAddressOf());
|
||||||
|
|
||||||
|
commandBuffer = new D3D12CommandBuffer(commandList.Get());
|
||||||
|
backBufferDescriptorIndexes = renderer.CreateBackBufferResource(index, backBuffer.GetAddressOf());
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly void ResetCommandBuffer()
|
||||||
|
{
|
||||||
|
commandAllocator.Get()->Reset();
|
||||||
|
commandList.Get()->Reset(commandAllocator.Get(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly void ExecuteCommandBuffer(ID3D12CommandQueue* queue)
|
||||||
|
{
|
||||||
|
commandList.Get()->Close();
|
||||||
|
var commandListPtr = (ID3D12CommandList*)commandList.Get();
|
||||||
|
queue->ExecuteCommandLists(1, &commandListPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void IncrementFenceValue()
|
||||||
|
{
|
||||||
|
fenceValue++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
commandAllocator.Dispose();
|
||||||
|
commandList.Dispose();
|
||||||
|
backBuffer.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int _RENDER_TARGET_VIEW_HEAP_SIZE = 1024;
|
||||||
|
private const int _DEPTH_STENCIL_VIEW_HEAP_SIZE = 256;
|
||||||
|
|
||||||
|
private readonly D3D12GraphicsDevice _graphicsDevice;
|
||||||
|
private readonly SwapChainPresenter _swapChainPresenter;
|
||||||
|
|
||||||
|
private ComPtr<IDXGISwapChain4> _swapChain = default;
|
||||||
|
private ComPtr<ID3D12Fence1> _fence = default;
|
||||||
|
private uint _backBufferIndex;
|
||||||
|
|
||||||
|
private readonly FrameResource[] _frameResources;
|
||||||
|
private readonly AutoResetEvent _fenceEvent;
|
||||||
|
|
||||||
|
private D3D12DescriptorAllocator _rtvHeap;
|
||||||
|
|
||||||
|
private ImmutableArray<IRenderPass> _renderPasses;
|
||||||
|
|
||||||
|
private readonly Lock _lock = new();
|
||||||
|
private uint _viewPortWidth;
|
||||||
|
private uint _viewPortHeight;
|
||||||
|
private uint _pendingWidth;
|
||||||
|
private uint _pendingHeight;
|
||||||
|
private bool _resizeRequested;
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
public ReadOnlySpan<IRenderPass> RenderPasses => _renderPasses.AsSpan();
|
||||||
|
|
||||||
|
public D3D12Renderer(D3D12GraphicsDevice graphicsDevice, in SwapChainPresenter swapChainSurface)
|
||||||
|
{
|
||||||
|
_graphicsDevice = graphicsDevice;
|
||||||
|
_swapChainPresenter = swapChainSurface;
|
||||||
|
_viewPortWidth = swapChainSurface.Width;
|
||||||
|
_viewPortHeight = swapChainSurface.Height;
|
||||||
|
|
||||||
|
_rtvHeap = new(_graphicsDevice.NativeDevice, DescriptorHeapType.Rtv, _RENDER_TARGET_VIEW_HEAP_SIZE);
|
||||||
|
_fenceEvent = new(false);
|
||||||
|
_renderPasses = [new MeshRenderPass()];
|
||||||
|
|
||||||
|
InitializeSwapChain();
|
||||||
|
InitializeFrameResource(out _frameResources);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeSwapChain()
|
||||||
|
{
|
||||||
|
var swapChainDesc = new SwapChainDescription1
|
||||||
|
{
|
||||||
|
Width = _swapChainPresenter.Width,
|
||||||
|
Height = _swapChainPresenter.Height,
|
||||||
|
Format = D3D12PipelineResource.SWAP_CHAIN_BACK_BUFFER_FORMAT,
|
||||||
|
SampleDesc = new SampleDescription(1, 0),
|
||||||
|
BufferUsage = Usage.BackBuffer | Usage.RenderTargetOutput,
|
||||||
|
BufferCount = GraphicsPipeline._FRAME_COUNT,
|
||||||
|
Scaling = Scaling.Stretch,
|
||||||
|
SwapEffect = SwapEffect.FlipDiscard,
|
||||||
|
AlphaMode = AlphaMode.Ignore,
|
||||||
|
Flags = SwapChainFlags.AllowTearing,
|
||||||
|
Stereo = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
using ComPtr<IDXGISwapChain1> tempSwapChain = default;
|
||||||
|
switch (_swapChainPresenter.Type)
|
||||||
|
{
|
||||||
|
case SwapChainPresenter.TargetType.Composition:
|
||||||
|
_graphicsDevice.DXGIFactory.Ptr->CreateSwapChainForComposition((IUnknown*)_graphicsDevice.CommandQueue.Ptr, &swapChainDesc, null, tempSwapChain.GetAddressOf());
|
||||||
|
break;
|
||||||
|
case SwapChainPresenter.TargetType.Hwnd:
|
||||||
|
var swapChainFullscreenDesc = new SwapChainFullscreenDescription
|
||||||
|
{
|
||||||
|
Windowed = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
_graphicsDevice.DXGIFactory.Ptr->CreateSwapChainForHwnd(
|
||||||
|
(IUnknown*)_graphicsDevice.CommandQueue.Ptr,
|
||||||
|
_swapChainPresenter.Hwnd,
|
||||||
|
&swapChainDesc,
|
||||||
|
&swapChainFullscreenDesc,
|
||||||
|
null,
|
||||||
|
tempSwapChain.GetAddressOf());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("Unsupported swap chain surface type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempSwapChain.Get()->QueryInterface(__uuidof<IDXGISwapChain4>(), _swapChain.GetVoidAddressOf()).Failure)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Failed to create IDXGISwapChain4 interface.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_swapChainPresenter.SwapChainPanelNative.SetSwapChain((IntPtr)_swapChain.Get());
|
||||||
|
_backBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeFrameResource(out FrameResource[] frameResources)
|
||||||
|
{
|
||||||
|
frameResources = new FrameResource[GraphicsPipeline._FRAME_COUNT];
|
||||||
|
for (var i = 0u; i < GraphicsPipeline._FRAME_COUNT; i++)
|
||||||
|
{
|
||||||
|
frameResources[i] = new FrameResource(this, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 1u; i < GraphicsPipeline._FRAME_COUNT; i++)
|
||||||
|
{
|
||||||
|
ref var frameResource = ref frameResources[i];
|
||||||
|
frameResource.commandList.Get()->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
_graphicsDevice.NativeDevice.Ptr->CreateFence(0, FenceFlags.None, __uuidof<ID3D12Fence1>(), _fence.GetVoidAddressOf());
|
||||||
|
frameResources[0].IncrementFenceValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RequestResize(uint width, uint height)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_pendingWidth == width && _pendingHeight == height)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_resizeRequested = true;
|
||||||
|
_pendingWidth = width;
|
||||||
|
_pendingHeight = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint CreateBackBufferResource(uint i, ID3D12Resource** backBuffer)
|
||||||
|
{
|
||||||
|
_swapChain.Get()->GetBuffer(i, __uuidof<ID3D12Resource>(), (void**)backBuffer);
|
||||||
|
(*backBuffer)->SetName($"BackBuffer_{i}");
|
||||||
|
var index = _rtvHeap.AllocateDescriptor();
|
||||||
|
var rtvHandle = _rtvHeap.GetCpuHandle(index);
|
||||||
|
_graphicsDevice.NativeDevice.Ptr->CreateRenderTargetView(*backBuffer, null, rtvHandle);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExecutePendingResize()
|
||||||
|
{
|
||||||
|
if (!_resizeRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint newWidth;
|
||||||
|
uint newHeight;
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
newWidth = _pendingWidth;
|
||||||
|
newHeight = _pendingHeight;
|
||||||
|
_resizeRequested = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitIdle();
|
||||||
|
|
||||||
|
for (var i = 0; i < GraphicsPipeline._FRAME_COUNT; i++)
|
||||||
|
{
|
||||||
|
ref var frameResource = ref _frameResources[i];
|
||||||
|
if (frameResource.backBuffer.Get() is not null)
|
||||||
|
{
|
||||||
|
frameResource.backBuffer.Reset();
|
||||||
|
_rtvHeap.ReleaseDescriptor(frameResource.backBufferDescriptorIndexes);
|
||||||
|
}
|
||||||
|
|
||||||
|
frameResource.fenceValue = _frameResources[_backBufferIndex].fenceValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_swapChain.Get()->ResizeBuffers(GraphicsPipeline._FRAME_COUNT, newWidth, newHeight, Format.B8G8R8A8Unorm, SwapChainFlags.AllowTearing).Failure)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Failed to resize swap chain buffers.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0u; i < GraphicsPipeline._FRAME_COUNT; i++)
|
||||||
|
{
|
||||||
|
var index = CreateBackBufferResource(i, _frameResources[i].backBuffer.GetAddressOf());
|
||||||
|
_frameResources[i].backBufferDescriptorIndexes = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
_backBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||||
|
_viewPortWidth = newWidth;
|
||||||
|
_viewPortHeight = newHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
ref var frameResource = ref _frameResources[_backBufferIndex];
|
||||||
|
|
||||||
|
foreach (var pass in _renderPasses)
|
||||||
|
{
|
||||||
|
pass.Initialize(frameResource.commandBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
frameResource.ExecuteCommandBuffer(_graphicsDevice.CommandQueue);
|
||||||
|
WaitIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Render()
|
||||||
|
{
|
||||||
|
_backBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
||||||
|
|
||||||
|
ref var frameResource = ref _frameResources[_backBufferIndex];
|
||||||
|
var cpuHandle = _rtvHeap.GetCpuHandle(frameResource.backBufferDescriptorIndexes);
|
||||||
|
|
||||||
|
frameResource.ResetCommandBuffer();
|
||||||
|
frameResource.commandList.Get()->ResourceBarrierTransition(frameResource.backBuffer.Get(), ResourceStates.Present, ResourceStates.RenderTarget);
|
||||||
|
|
||||||
|
var clearColor = stackalloc float[4] { 1.0f, 0.0f, 1.0f, 1.0f };
|
||||||
|
frameResource.commandList.Get()->ClearRenderTargetView(cpuHandle, clearColor, 0, null);
|
||||||
|
|
||||||
|
var viewPort = new Viewport(_viewPortWidth, _viewPortHeight);
|
||||||
|
var rect = new Rect(0, 0, (int)_viewPortWidth, (int)_viewPortHeight);
|
||||||
|
frameResource.commandList.Get()->RSSetViewports(1, &viewPort);
|
||||||
|
frameResource.commandList.Get()->RSSetScissorRects(1, &rect);
|
||||||
|
|
||||||
|
frameResource.commandList.Get()->OMSetRenderTargets(1, &cpuHandle, false, null);
|
||||||
|
|
||||||
|
foreach (var pass in _renderPasses)
|
||||||
|
{
|
||||||
|
pass.Execute(frameResource.commandBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
frameResource.commandList.Get()->ResourceBarrierTransition(frameResource.backBuffer.Get(), ResourceStates.RenderTarget, ResourceStates.Present);
|
||||||
|
|
||||||
|
frameResource.ExecuteCommandBuffer(_graphicsDevice.CommandQueue.Ptr);
|
||||||
|
if (_swapChain.Get()->Present(1, PresentFlags.None).Failure)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Failed to present swap chain.");
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitNextFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WaitNextFrame()
|
||||||
|
{
|
||||||
|
ref var resource = ref _frameResources[_backBufferIndex];
|
||||||
|
if (_graphicsDevice.CommandQueue.Ptr->Signal((ID3D12Fence*)_fence.Get(), resource.fenceValue).Failure)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var handle = new Handle((void*)_fenceEvent.SafeWaitHandle.DangerousGetHandle());
|
||||||
|
if (_fence.Get()->GetCompletedValue() < resource.fenceValue
|
||||||
|
&& _fence.Get()->SetEventOnCompletion(resource.fenceValue, handle).Success)
|
||||||
|
{
|
||||||
|
_fenceEvent.WaitOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.IncrementFenceValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WaitIdle()
|
||||||
|
{
|
||||||
|
ref var resource = ref _frameResources[_backBufferIndex];
|
||||||
|
_graphicsDevice.CommandQueue.Ptr->Signal((ID3D12Fence*)_fence.Get(), resource.fenceValue);
|
||||||
|
|
||||||
|
var handle = new Handle((void*)_fenceEvent.SafeWaitHandle.DangerousGetHandle());
|
||||||
|
if (_fence.Get()->SetEventOnCompletion(resource.fenceValue, handle).Success)
|
||||||
|
{
|
||||||
|
_fenceEvent.WaitOne();
|
||||||
|
resource.IncrementFenceValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitIdle();
|
||||||
|
|
||||||
|
_swapChainPresenter.SwapChainPanelNative.SetSwapChain(IntPtr.Zero);
|
||||||
|
|
||||||
|
foreach (var pass in _renderPasses)
|
||||||
|
{
|
||||||
|
pass.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var frameResource in _frameResources)
|
||||||
|
{
|
||||||
|
frameResource.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_swapChain.Dispose();
|
||||||
|
|
||||||
|
_fence.Dispose();
|
||||||
|
_fenceEvent.Dispose();
|
||||||
|
|
||||||
|
_rtvHeap.Dispose();
|
||||||
|
|
||||||
|
_backBufferIndex = 0;
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
74
Ghost.Graphics/D3D12/D3D12Resource.cs
Normal file
74
Ghost.Graphics/D3D12/D3D12Resource.cs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
using Ghost.Core;
|
||||||
|
using Ghost.Graphics.Contracts;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Win32;
|
||||||
|
using Win32.Graphics.Direct3D12;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
|
public unsafe class D3D12Resource : IResource
|
||||||
|
{
|
||||||
|
private ComPtr<ID3D12Resource> _nativeResource
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
private string _name = string.Empty;
|
||||||
|
|
||||||
|
internal ConstPtr<ID3D12Resource> NativeResource => new(_nativeResource.Get());
|
||||||
|
|
||||||
|
public ulong GPUAddress => _nativeResource.Get()->GetGPUVirtualAddress();
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => _name;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_name = value;
|
||||||
|
_nativeResource.Get()->SetName(_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TempResource
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal D3D12Resource(ComPtr<ID3D12Resource> nativeResource, bool temp)
|
||||||
|
{
|
||||||
|
_nativeResource = nativeResource;
|
||||||
|
TempResource = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void SetData<T>(Span<T> data)
|
||||||
|
where T : unmanaged
|
||||||
|
{
|
||||||
|
var size = (uint)(data.Length * sizeof(T));
|
||||||
|
var range = new Win32.Graphics.Direct3D12.Range(0, size);
|
||||||
|
|
||||||
|
fixed (T* ptr = data)
|
||||||
|
{
|
||||||
|
var hr = _nativeResource.Get()->Map(0, &range, (void**)&ptr);
|
||||||
|
if (hr.Failure)
|
||||||
|
{
|
||||||
|
var message = hr.ToString();
|
||||||
|
throw new InvalidOperationException($"Failed to map resource: {message}");
|
||||||
|
}
|
||||||
|
_nativeResource.Get()->Unmap(0, &range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void DisposeInternal()
|
||||||
|
{
|
||||||
|
var c = _nativeResource.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!TempResource)
|
||||||
|
{
|
||||||
|
DisposeInternal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
308
Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs
Normal file
308
Ghost.Graphics/D3D12/D3D12ResourceAllocator.cs
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
using Ghost.Graphics.Contracts;
|
||||||
|
using Win32;
|
||||||
|
using Win32.Graphics.Direct3D12;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.D3D12;
|
||||||
|
|
||||||
|
internal unsafe class D3D12ResourceAllocator : IResourceAllocator
|
||||||
|
{
|
||||||
|
private readonly struct TempResourceAllocInfo
|
||||||
|
{
|
||||||
|
public readonly D3D12Resource resource;
|
||||||
|
public readonly uint cpuFenceValue;
|
||||||
|
|
||||||
|
public TempResourceAllocInfo(D3D12Resource resource, uint cpuFenceValue)
|
||||||
|
{
|
||||||
|
this.resource = resource;
|
||||||
|
this.cpuFenceValue = cpuFenceValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TempResourceAllocInfo(D3D12Resource resource)
|
||||||
|
: this(resource, GraphicsPipeline.CPUFenceValue + 1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const ResourceStates _INITIALCOPYTARGETSTATE = ResourceStates.Common;
|
||||||
|
private const ResourceStates _INITIALREADTARGETSTATE = ResourceStates.Common;
|
||||||
|
private const ResourceStates _INITIALUAVTARGETSTATE = ResourceStates.Common;
|
||||||
|
|
||||||
|
private const uint _MAX_BYTES = D3D12_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u;
|
||||||
|
|
||||||
|
private readonly Queue<TempResourceAllocInfo> _temResources = new();
|
||||||
|
|
||||||
|
//public static ID3D12Resource CreateStaticBuffer<T>(
|
||||||
|
// ID3D12Device device,
|
||||||
|
// D3D12ResourceUploadBatch resourceUpload,
|
||||||
|
// T[] data, ResourceStates afterState,
|
||||||
|
// ResourceFlags flags = ResourceFlags.None)
|
||||||
|
// where T : unmanaged
|
||||||
|
//{
|
||||||
|
// Span<T> span = data;
|
||||||
|
// return CreateStaticBuffer(device, resourceUpload, span, afterState, flags);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public static ID3D12Resource CreateStaticBuffer<T>(
|
||||||
|
// ID3D12Device device,
|
||||||
|
// D3D12ResourceUploadBatch resourceUpload,
|
||||||
|
// Span<T> data,
|
||||||
|
// ResourceStates afterState,
|
||||||
|
// ResourceFlags flags = ResourceFlags.None)
|
||||||
|
// where T : unmanaged
|
||||||
|
//{
|
||||||
|
// var sizeInBytes = (uint)(sizeof(T) * data.Length);
|
||||||
|
// if (sizeInBytes > _MAX_BYTES)
|
||||||
|
// {
|
||||||
|
// throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var buffer = device.CreateCommittedResource(
|
||||||
|
// HeapType.Default,
|
||||||
|
// HeapFlags.None,
|
||||||
|
// ResourceDescription.Buffer(sizeInBytes, flags),
|
||||||
|
// _INITIALCOPYTARGETSTATE
|
||||||
|
// );
|
||||||
|
|
||||||
|
// fixed (T* dataPtr = data)
|
||||||
|
// {
|
||||||
|
// SubresourceData initData = new()
|
||||||
|
// {
|
||||||
|
// pData = dataPtr,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// resourceUpload.Upload(buffer, 0, &initData, 1);
|
||||||
|
// resourceUpload.Transition(buffer, ResourceStates.CopyDest, afterState);
|
||||||
|
|
||||||
|
// return buffer;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public static ID3D12Resource CreateUploadBuffer<T>(
|
||||||
|
// ID3D12Device device,
|
||||||
|
// T[] data,
|
||||||
|
// ResourceFlags flags = ResourceFlags.None)
|
||||||
|
// where T : unmanaged
|
||||||
|
//{
|
||||||
|
// var sizeInBytes = (uint)(sizeof(T) * data.Length);
|
||||||
|
// fixed (T* dataPtr = data)
|
||||||
|
// {
|
||||||
|
// return CreateUploadBuffer(device, sizeInBytes, dataPtr, flags);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public static ID3D12Resource CreateUploadBuffer<T>(
|
||||||
|
// ID3D12Device device,
|
||||||
|
// Span<T> data,
|
||||||
|
// ResourceFlags flags = ResourceFlags.None)
|
||||||
|
// where T : unmanaged
|
||||||
|
//{
|
||||||
|
// var sizeInBytes = (uint)(sizeof(T) * data.Length);
|
||||||
|
// fixed (T* dataPtr = data)
|
||||||
|
// {
|
||||||
|
// return CreateUploadBuffer(device, sizeInBytes, dataPtr, flags);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public static ID3D12Resource CreateUploadBuffer(
|
||||||
|
// ID3D12Device device,
|
||||||
|
// uint sizeInBytes,
|
||||||
|
// void* data = default,
|
||||||
|
// ResourceFlags flags = ResourceFlags.None)
|
||||||
|
//{
|
||||||
|
// if (sizeInBytes > _MAX_BYTES)
|
||||||
|
// {
|
||||||
|
// throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var buffer = device.CreateCommittedResource(
|
||||||
|
// HeapType.Upload,
|
||||||
|
// HeapFlags.None,
|
||||||
|
// ResourceDescription.Buffer(sizeInBytes, flags),
|
||||||
|
// ResourceStates.GenericRead
|
||||||
|
// );
|
||||||
|
|
||||||
|
// if (data is not null)
|
||||||
|
// {
|
||||||
|
// void* mappedPtr = default;
|
||||||
|
// buffer.Map(0, null, &mappedPtr).CheckError();
|
||||||
|
// Unsafe.CopyBlock(data, mappedPtr, sizeInBytes);
|
||||||
|
// buffer.Unmap(0, null);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return buffer;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public static ID3D12Resource CreateReadbackBuffer(
|
||||||
|
// ID3D12Device device,
|
||||||
|
// uint sizeInBytes,
|
||||||
|
// ResourceFlags flags = ResourceFlags.None)
|
||||||
|
//{
|
||||||
|
// if (sizeInBytes > _MAX_BYTES)
|
||||||
|
// {
|
||||||
|
// throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||||
|
// }
|
||||||
|
// var buffer = device.CreateCommittedResource(
|
||||||
|
// HeapType.Readback,
|
||||||
|
// HeapFlags.None,
|
||||||
|
// ResourceDescription.Buffer(sizeInBytes, flags),
|
||||||
|
// _INITIALREADTARGETSTATE
|
||||||
|
// );
|
||||||
|
// return buffer;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public static ID3D12Resource CreateUAVBuffer(ID3D12Device device, uint bufferSize,
|
||||||
|
// ResourceStates initialState = ResourceStates.Common,
|
||||||
|
// ResourceFlags flags = ResourceFlags.None)
|
||||||
|
//{
|
||||||
|
// if (bufferSize > _MAX_BYTES)
|
||||||
|
// {
|
||||||
|
// throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {bufferSize})");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var buffer = device.CreateCommittedResource(
|
||||||
|
// HeapType.Default,
|
||||||
|
// HeapFlags.None,
|
||||||
|
// ResourceDescription.Buffer(bufferSize, ResourceFlags.AllowUnorderedAccess | flags),
|
||||||
|
// _INITIALCOPYTARGETSTATE
|
||||||
|
// );
|
||||||
|
|
||||||
|
// return buffer;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public static ID3D12Resource CreateTexture2D<T>(
|
||||||
|
// ID3D12Device device,
|
||||||
|
// D3D12ResourceUploadBatch resourceUpload,
|
||||||
|
// uint width, uint height, Format format,
|
||||||
|
// Span<T> data,
|
||||||
|
// bool generateMips = false,
|
||||||
|
// ResourceStates afterState = ResourceStates.PixelShaderResource,
|
||||||
|
// ResourceFlags flags = ResourceFlags.None)
|
||||||
|
// where T : unmanaged
|
||||||
|
//{
|
||||||
|
// if (width > D3D12.RequestTexture2DUOrVDimension || height > D3D12.RequestTexture2DUOrVDimension)
|
||||||
|
// {
|
||||||
|
// throw new InvalidOperationException($"ERROR: Resource dimensions too large for DirectX 12 (2D: size {width} by {height})");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ushort mipLevels = 1;
|
||||||
|
// if (generateMips)
|
||||||
|
// {
|
||||||
|
// generateMips = resourceUpload.IsSupportedForGenerateMips(format);
|
||||||
|
// if (generateMips)
|
||||||
|
// {
|
||||||
|
// mipLevels = (ushort)TextureUtility.CountMips(width, height);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// var texture = device.CreateCommittedResource(
|
||||||
|
// HeapType.Default,
|
||||||
|
// HeapFlags.None,
|
||||||
|
// ResourceDescription.Texture2D(format, width, height, 1, mipLevels, 1, 0, flags),
|
||||||
|
// _INITIALCOPYTARGETSTATE
|
||||||
|
// );
|
||||||
|
|
||||||
|
// fixed (T* dataPtr = data)
|
||||||
|
// {
|
||||||
|
// FormatHelper.GetSurfaceInfo(format, width, height, out var rowPitch, out var slicePitch);
|
||||||
|
// SubresourceData initData = new()
|
||||||
|
// {
|
||||||
|
// pData = dataPtr,
|
||||||
|
// RowPitch = (nint)rowPitch,
|
||||||
|
// SlicePitch = (nint)slicePitch
|
||||||
|
// };
|
||||||
|
|
||||||
|
// resourceUpload.Upload(texture, 0, &initData, 1);
|
||||||
|
// resourceUpload.Transition(texture, ResourceStates.CopyDest, afterState);
|
||||||
|
|
||||||
|
// if (generateMips)
|
||||||
|
// {
|
||||||
|
// resourceUpload.GenerateMips(texture);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return texture;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
public IResource CreateUploadBuffer(uint sizeInBytes, bool tempResource = false, ResourceFlags flags = ResourceFlags.None)
|
||||||
|
{
|
||||||
|
if (sizeInBytes > _MAX_BYTES)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||||
|
}
|
||||||
|
|
||||||
|
var device = GraphicsPipeline.GetGraphicsDevice<D3D12GraphicsDevice>();
|
||||||
|
var heapProperties = new HeapProperties(HeapType.Upload);
|
||||||
|
var resourceDescription = ResourceDescription.Buffer(sizeInBytes, flags);
|
||||||
|
|
||||||
|
ComPtr<ID3D12Resource> buffer = default;
|
||||||
|
device.NativeDevice.Ptr->CreateCommittedResource(
|
||||||
|
&heapProperties,
|
||||||
|
HeapFlags.None,
|
||||||
|
&resourceDescription,
|
||||||
|
ResourceStates.Common,
|
||||||
|
null,
|
||||||
|
__uuidof<ID3D12Resource>(),
|
||||||
|
buffer.GetVoidAddressOf()
|
||||||
|
);
|
||||||
|
|
||||||
|
var resource = new D3D12Resource(buffer.Move(), tempResource);
|
||||||
|
if (tempResource)
|
||||||
|
{
|
||||||
|
_temResources.Enqueue(new(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IResource CreateCopyDestinationBuffer(uint sizeInBytes, bool tempResource = false, ResourceFlags flags = ResourceFlags.None)
|
||||||
|
{
|
||||||
|
if (sizeInBytes > _MAX_BYTES)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
||||||
|
}
|
||||||
|
|
||||||
|
var device = GraphicsPipeline.GetGraphicsDevice<D3D12GraphicsDevice>();
|
||||||
|
var heapProperties = new HeapProperties(HeapType.Default);
|
||||||
|
var resourceDescription = ResourceDescription.Buffer(sizeInBytes, flags);
|
||||||
|
|
||||||
|
ComPtr<ID3D12Resource> buffer = default;
|
||||||
|
device.NativeDevice.Ptr->CreateCommittedResource(
|
||||||
|
&heapProperties,
|
||||||
|
HeapFlags.None,
|
||||||
|
&resourceDescription,
|
||||||
|
ResourceStates.Common,
|
||||||
|
null,
|
||||||
|
__uuidof<ID3D12Resource>(),
|
||||||
|
buffer.GetVoidAddressOf()
|
||||||
|
);
|
||||||
|
|
||||||
|
var resource = new D3D12Resource(buffer.Move(), tempResource);
|
||||||
|
if (tempResource)
|
||||||
|
{
|
||||||
|
_temResources.Enqueue(new(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReleaseTempResource()
|
||||||
|
{
|
||||||
|
while (_temResources.Count > 0)
|
||||||
|
{
|
||||||
|
var info = _temResources.Peek();
|
||||||
|
if (info.cpuFenceValue > GraphicsPipeline.GPUFenceValue)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.resource.DisposeInternal();
|
||||||
|
_temResources.Dequeue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
ReleaseTempResource();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +1,24 @@
|
|||||||
using System.Diagnostics;
|
using Ghost.Core;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Vortice.Direct3D12;
|
using Win32;
|
||||||
|
using Win32.Graphics.Direct3D12;
|
||||||
using DescriptorIndex = System.UInt32;
|
using DescriptorIndex = System.UInt32;
|
||||||
|
|
||||||
namespace Ghost.Graphics.DX12.Utilities;
|
namespace Ghost.Graphics.D3D12.Utilities;
|
||||||
|
|
||||||
internal class D3D12DescriptorAllocator : IDisposable
|
internal unsafe struct D3D12DescriptorAllocator : IDisposable
|
||||||
{
|
{
|
||||||
private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u;
|
private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u;
|
||||||
|
|
||||||
private readonly ID3D12Device _device;
|
private readonly ConstPtr<ID3D12Device14> _device;
|
||||||
private readonly Lock _lock = new();
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
private ID3D12DescriptorHeap? _heap;
|
private ComPtr<ID3D12DescriptorHeap> _heap;
|
||||||
private ID3D12DescriptorHeap? _shaderVisibleHeap;
|
private ComPtr<ID3D12DescriptorHeap> _shaderVisibleHeap;
|
||||||
private CpuDescriptorHandle _startCpuHandle = default;
|
private CpuDescriptorHandle _startCpuHandle;
|
||||||
private CpuDescriptorHandle _startCpuHandleShaderVisible = default;
|
private CpuDescriptorHandle _startCpuHandleShaderVisible;
|
||||||
private GpuDescriptorHandle _startGpuHandleShaderVisible = default;
|
private GpuDescriptorHandle _startGpuHandleShaderVisible;
|
||||||
private DescriptorIndex _searchStart;
|
private DescriptorIndex _searchStart;
|
||||||
private bool[] _allocatedDescriptors = [];
|
private bool[] _allocatedDescriptors = [];
|
||||||
|
|
||||||
@@ -45,16 +47,16 @@ internal class D3D12DescriptorAllocator : IDisposable
|
|||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ID3D12DescriptorHeap Heap => _heap!;
|
public readonly ConstPtr<ID3D12DescriptorHeap> Heap => new(_heap.Get());
|
||||||
public ID3D12DescriptorHeap? ShaderVisibleHeap => _shaderVisibleHeap;
|
public readonly ConstPtr<ID3D12DescriptorHeap> ShaderVisibleHeap => new(_shaderVisibleHeap.Get());
|
||||||
|
|
||||||
public D3D12DescriptorAllocator(ID3D12Device device, DescriptorHeapType type, uint numDescriptors)
|
public D3D12DescriptorAllocator(ConstPtr<ID3D12Device14> device, DescriptorHeapType type, uint numDescriptors)
|
||||||
{
|
{
|
||||||
_device = device;
|
_device = device;
|
||||||
HeapType = type;
|
HeapType = type;
|
||||||
NumDescriptors = numDescriptors;
|
NumDescriptors = numDescriptors;
|
||||||
ShaderVisible = type == DescriptorHeapType.ConstantBufferViewShaderResourceViewUnorderedAccessView || type == DescriptorHeapType.Sampler;
|
ShaderVisible = type == DescriptorHeapType.CbvSrvUav || type == DescriptorHeapType.Sampler;
|
||||||
Stride = device.GetDescriptorHandleIncrementSize(type);
|
Stride = device.Ptr->GetDescriptorHandleIncrementSize(type);
|
||||||
|
|
||||||
var success = AllocateResources(numDescriptors);
|
var success = AllocateResources(numDescriptors);
|
||||||
Debug.Assert(success);
|
Debug.Assert(success);
|
||||||
@@ -164,45 +166,50 @@ internal class D3D12DescriptorAllocator : IDisposable
|
|||||||
|
|
||||||
public void CopyToShaderVisibleHeap(DescriptorIndex index, uint count = 1)
|
public void CopyToShaderVisibleHeap(DescriptorIndex index, uint count = 1)
|
||||||
{
|
{
|
||||||
_device.CopyDescriptorsSimple(count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
|
_device.Ptr->CopyDescriptorsSimple(count, GetCpuHandleShaderVisible(index), GetCpuHandle(index), HeapType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AllocateResources(uint numDescriptors)
|
private bool AllocateResources(uint numDescriptors)
|
||||||
{
|
{
|
||||||
NumDescriptors = numDescriptors;
|
NumDescriptors = numDescriptors;
|
||||||
_heap?.Dispose();
|
_heap.Dispose();
|
||||||
_shaderVisibleHeap?.Dispose();
|
_shaderVisibleHeap.Dispose();
|
||||||
|
|
||||||
DescriptorHeapDescription heapDesc = new()
|
DescriptorHeapDescription heapDesc = new()
|
||||||
{
|
{
|
||||||
Type = HeapType,
|
Type = HeapType,
|
||||||
DescriptorCount = numDescriptors,
|
NumDescriptors = numDescriptors,
|
||||||
Flags = DescriptorHeapFlags.None,
|
Flags = DescriptorHeapFlags.None,
|
||||||
NodeMask = 0
|
NodeMask = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
var hr = _device.CreateDescriptorHeap(in heapDesc, out _heap);
|
fixed (void* heapPtr = &_heap)
|
||||||
|
{
|
||||||
|
var hr = _device.Ptr->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
|
||||||
if (hr.Failure)
|
if (hr.Failure)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_startCpuHandle = _heap!.GetCPUDescriptorHandleForHeapStart();
|
_startCpuHandle = _heap.Get()->GetCPUDescriptorHandleForHeapStart();
|
||||||
Array.Resize(ref _allocatedDescriptors, (int)numDescriptors);
|
Array.Resize(ref _allocatedDescriptors, (int)numDescriptors);
|
||||||
|
|
||||||
if (ShaderVisible)
|
if (ShaderVisible)
|
||||||
{
|
{
|
||||||
heapDesc.Flags = DescriptorHeapFlags.ShaderVisible;
|
heapDesc.Flags = DescriptorHeapFlags.ShaderVisible;
|
||||||
|
|
||||||
hr = _device.CreateDescriptorHeap(in heapDesc, out _shaderVisibleHeap);
|
fixed (void* heapPtr = &_shaderVisibleHeap)
|
||||||
|
{
|
||||||
|
var hr = _device.Ptr->CreateDescriptorHeap(&heapDesc, __uuidof<ID3D12DescriptorHeap>(), (void**)heapPtr);
|
||||||
if (hr.Failure)
|
if (hr.Failure)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_startCpuHandleShaderVisible = _shaderVisibleHeap!.GetCPUDescriptorHandleForHeapStart();
|
_startCpuHandleShaderVisible = _shaderVisibleHeap.Get()->GetCPUDescriptorHandleForHeapStart();
|
||||||
_startGpuHandleShaderVisible = _shaderVisibleHeap!.GetGPUDescriptorHandleForHeapStart();
|
_startGpuHandleShaderVisible = _shaderVisibleHeap.Get()->GetGPUDescriptorHandleForHeapStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -220,11 +227,11 @@ internal class D3D12DescriptorAllocator : IDisposable
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_device.CopyDescriptorsSimple(oldSize, _startCpuHandle, oldHeap!.GetCPUDescriptorHandleForHeapStart(), HeapType);
|
_device.Ptr->CopyDescriptorsSimple(oldSize, _startCpuHandle, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||||
|
|
||||||
if (_shaderVisibleHeap is not null)
|
if (_shaderVisibleHeap.Get() is not null)
|
||||||
{
|
{
|
||||||
_device.CopyDescriptorsSimple(oldSize, _startCpuHandleShaderVisible, oldHeap.GetCPUDescriptorHandleForHeapStart(), HeapType);
|
_device.Ptr->CopyDescriptorsSimple(oldSize, _startCpuHandleShaderVisible, oldHeap.Get()->GetCPUDescriptorHandleForHeapStart(), HeapType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -233,7 +240,7 @@ internal class D3D12DescriptorAllocator : IDisposable
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_heap?.Dispose();
|
_heap.Dispose();
|
||||||
_shaderVisibleHeap?.Dispose();
|
_shaderVisibleHeap.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
25
Ghost.Graphics/D3D12/Utilities/D3D12PipelineResource.cs
Normal file
25
Ghost.Graphics/D3D12/Utilities/D3D12PipelineResource.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Ghost.Graphics.Data;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Win32.Graphics.Direct3D12;
|
||||||
|
using Win32.Graphics.Dxgi.Common;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.D3D12.Utilities;
|
||||||
|
|
||||||
|
internal unsafe static class D3D12PipelineResource
|
||||||
|
{
|
||||||
|
private readonly static InputElementDescription[] s_inputElementDescs = [
|
||||||
|
new InputElementDescription{ SemanticName = Vertex.Semantic.PositionName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 0u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||||
|
new InputElementDescription{ SemanticName = Vertex.Semantic.NormalName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 16u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||||
|
new InputElementDescription{ SemanticName = Vertex.Semantic.TangentName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 32u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||||
|
new InputElementDescription{ SemanticName = Vertex.Semantic.ColorName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 48u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 },
|
||||||
|
new InputElementDescription{ SemanticName = Vertex.Semantic.UVName, SemanticIndex = 0u, Format = Format.R32G32B32A32Float, InputSlot = 0u, AlignedByteOffset = 64u, InputSlotClass = InputClassification.PerVertexData, InstanceDataStepRate = 0 }
|
||||||
|
];
|
||||||
|
|
||||||
|
public const Format SWAP_CHAIN_BACK_BUFFER_FORMAT = Format.B8G8R8A8Unorm;
|
||||||
|
|
||||||
|
public static InputLayoutDescription InputLayoutDescription => new()
|
||||||
|
{
|
||||||
|
pInputElementDescs = (InputElementDescription*)Unsafe.AsPointer(ref s_inputElementDescs[0]),
|
||||||
|
NumElements = (uint)s_inputElementDescs.Length
|
||||||
|
};
|
||||||
|
}
|
||||||
5
Ghost.Graphics/D3D12/Utilities/D3D12Utility.cs
Normal file
5
Ghost.Graphics/D3D12/Utilities/D3D12Utility.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
namespace Ghost.Graphics.D3D12.Utilities;
|
||||||
|
|
||||||
|
internal static class D3D12Utility
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using Ghost.Graphics.Contracts;
|
|
||||||
using Ghost.Graphics.Data;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Vortice.Direct3D12;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.DX12;
|
|
||||||
|
|
||||||
internal class DX12CommandBuffer : ICommandBuffer
|
|
||||||
{
|
|
||||||
private ID3D12GraphicsCommandList10 _commandList;
|
|
||||||
|
|
||||||
public DX12CommandBuffer(ID3D12GraphicsCommandList10 commandList)
|
|
||||||
{
|
|
||||||
_commandList = commandList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CopyResource(IResource dstResource, uint dstOffset, IResource srcResource, uint srcOffset, uint size)
|
|
||||||
{
|
|
||||||
GraphicsPipeline.CheckAPI(GraphicsAPI.DX12).EnsureSuccess();
|
|
||||||
|
|
||||||
var dstDXResource = Unsafe.As<DX12Resource>(dstResource);
|
|
||||||
var srcDXResource = Unsafe.As<DX12Resource>(srcResource);
|
|
||||||
|
|
||||||
_commandList.CopyBufferRegion(dstDXResource.NativeResource, dstOffset, srcDXResource.NativeResource, srcOffset, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using Ghost.Graphics.Contracts;
|
|
||||||
using Vortice.Direct3D12;
|
|
||||||
using Vortice.Direct3D12.Debug;
|
|
||||||
using Vortice.DXGI;
|
|
||||||
using Vortice.DXGI.Debug;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.DX12;
|
|
||||||
|
|
||||||
internal class DX12DebugLayer : IDebugLayer
|
|
||||||
{
|
|
||||||
private readonly ID3D12Debug6 _d3d12Debug;
|
|
||||||
private readonly IDXGIDebug1 _dxgiDebug;
|
|
||||||
private readonly IDXGIInfoQueue? _dxgiInfoQueue;
|
|
||||||
|
|
||||||
public DX12DebugLayer()
|
|
||||||
{
|
|
||||||
_d3d12Debug = D3D12.D3D12GetDebugInterface<ID3D12Debug6>();
|
|
||||||
_d3d12Debug.EnableDebugLayer();
|
|
||||||
|
|
||||||
_dxgiDebug = DXGI.DXGIGetDebugInterface1<IDXGIDebug1>();
|
|
||||||
_dxgiDebug.EnableLeakTrackingForThread();
|
|
||||||
|
|
||||||
_dxgiInfoQueue = DXGI.DXGIGetDebugInterface1<IDXGIInfoQueue>();
|
|
||||||
_dxgiInfoQueue.SetBreakOnSeverity(DXGI.DebugAll, InfoQueueMessageSeverity.Error, true);
|
|
||||||
_dxgiInfoQueue.SetBreakOnSeverity(DXGI.DebugAll, InfoQueueMessageSeverity.Corruption, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_dxgiDebug.ReportLiveObjects(DXGI.DebugAll, ReportLiveObjectFlags.Detail | ReportLiveObjectFlags.IgnoreInternal);
|
|
||||||
|
|
||||||
_d3d12Debug.Dispose();
|
|
||||||
_dxgiDebug.Dispose();
|
|
||||||
_dxgiInfoQueue?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,324 +0,0 @@
|
|||||||
using Ghost.Graphics.Contracts;
|
|
||||||
using Ghost.Graphics.Data;
|
|
||||||
using Ghost.Graphics.DX12.Utilities;
|
|
||||||
using System.Collections.Immutable;
|
|
||||||
using Win32;
|
|
||||||
using Win32.Graphics.Direct3D12;
|
|
||||||
using Win32.Graphics.Dxgi;
|
|
||||||
using Win32.Graphics.Dxgi.Common;
|
|
||||||
using static Win32.Apis;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.DX12;
|
|
||||||
|
|
||||||
internal unsafe class DX12Renderer : IRenderer
|
|
||||||
{
|
|
||||||
private class FrameResource : IDisposable
|
|
||||||
{
|
|
||||||
public readonly ID3D12CommandAllocator commandAllocator;
|
|
||||||
public readonly ID3D12GraphicsCommandList10 commandList;
|
|
||||||
public readonly ICommandBuffer commandBuffer;
|
|
||||||
public ID3D12Resource backBuffer;
|
|
||||||
public uint backBufferDescriptorIndexes;
|
|
||||||
public ulong fenceValue;
|
|
||||||
|
|
||||||
public FrameResource(DX12Renderer renderer, uint index)
|
|
||||||
{
|
|
||||||
commandAllocator = renderer._graphicsDevice.NativeDevice.CreateCommandAllocator(CommandListType.Direct);
|
|
||||||
commandList = renderer._graphicsDevice.NativeDevice.CreateCommandList<ID3D12GraphicsCommandList10>(CommandListType.Direct, commandAllocator);
|
|
||||||
commandBuffer = new DX12CommandBuffer(commandList);
|
|
||||||
|
|
||||||
renderer.CreateBackBufferResource(index, out backBuffer, out backBufferDescriptorIndexes);
|
|
||||||
|
|
||||||
commandList.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetCommandList()
|
|
||||||
{
|
|
||||||
commandAllocator.Reset();
|
|
||||||
commandList.Reset(commandAllocator, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void IncrementFenceValue()
|
|
||||||
{
|
|
||||||
fenceValue++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
commandAllocator.Dispose();
|
|
||||||
commandList.Dispose();
|
|
||||||
backBuffer?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const int _RENDER_TARGET_VIEW_HEAP_SIZE = 1024;
|
|
||||||
private const int _DEPTH_STENCIL_VIEW_HEAP_SIZE = 256;
|
|
||||||
|
|
||||||
private readonly DX12GraphicsDevice _graphicsDevice;
|
|
||||||
private readonly SwapChainPresenter _swapChainPresenter;
|
|
||||||
|
|
||||||
private readonly ComPtr<IDXGISwapChain4> _swapChain;
|
|
||||||
private readonly FrameResource[] _frameResources;
|
|
||||||
private uint _backBufferIndex;
|
|
||||||
|
|
||||||
private readonly ComPtr<ID3D12Fence1> _fence;
|
|
||||||
private readonly AutoResetEvent _fenceEvent;
|
|
||||||
|
|
||||||
private readonly D3D12DescriptorAllocator _rtvHeap;
|
|
||||||
|
|
||||||
private ImmutableArray<IRenderPass> _renderPasses;
|
|
||||||
|
|
||||||
private readonly Lock _lock = new();
|
|
||||||
private uint _pendingWidth;
|
|
||||||
private uint _pendingHeight;
|
|
||||||
private bool _resizeRequested;
|
|
||||||
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
public ReadOnlySpan<IRenderPass> RenderPasses => _renderPasses.AsSpan();
|
|
||||||
|
|
||||||
public DX12Renderer(DX12GraphicsDevice graphicsDevice, in SwapChainPresenter swapChainSurface)
|
|
||||||
{
|
|
||||||
_graphicsDevice = graphicsDevice;
|
|
||||||
_swapChainPresenter = swapChainSurface;
|
|
||||||
|
|
||||||
_rtvHeap = new D3D12DescriptorAllocator(_graphicsDevice.NativeDevice, DescriptorHeapType.Rtv, _RENDER_TARGET_VIEW_HEAP_SIZE);
|
|
||||||
_fenceEvent = new(false);
|
|
||||||
_renderPasses = ImmutableArray<IRenderPass>.Empty;
|
|
||||||
|
|
||||||
InitializeSwapChain();
|
|
||||||
InitializeCommandObjects(out _frameResources, out _fence);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeSwapChain()
|
|
||||||
{
|
|
||||||
var swapChainDesc = new SwapChainDescription1
|
|
||||||
{
|
|
||||||
Width = _swapChainPresenter.Width,
|
|
||||||
Height = _swapChainPresenter.Height,
|
|
||||||
Format = Format.B8G8R8A8Unorm,
|
|
||||||
Stereo = false,
|
|
||||||
SampleDesc = new(1, 0),
|
|
||||||
BufferUsage = Usage.BackBuffer | Usage.RenderTargetOutput,
|
|
||||||
BufferCount = GraphicsPipeline._FRAME_COUNT,
|
|
||||||
Scaling = Scaling.Stretch,
|
|
||||||
SwapEffect = SwapEffect.FlipDiscard,
|
|
||||||
AlphaMode = AlphaMode.Ignore,
|
|
||||||
Flags = SwapChainFlags.AllowTearing
|
|
||||||
};
|
|
||||||
|
|
||||||
using ComPtr<IDXGISwapChain1> tempSwapChain = default;
|
|
||||||
switch (_swapChainPresenter.Type)
|
|
||||||
{
|
|
||||||
case SwapChainPresenter.TargetType.Composition:
|
|
||||||
{
|
|
||||||
_graphicsDevice.DXGIFactory.Ptr->
|
|
||||||
CreateSwapChainForComposition(
|
|
||||||
(IUnknown*)_graphicsDevice.CommandQueue.Ptr,
|
|
||||||
&swapChainDesc, null,
|
|
||||||
(IDXGISwapChain1**)&tempSwapChain);
|
|
||||||
|
|
||||||
fixed (void* swapChainPtr = &_swapChain)
|
|
||||||
{
|
|
||||||
tempSwapChain.Get()->QueryInterface(__uuidof<IDXGISwapChain4>(), &swapChainPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
_swapChainPresenter.SwapChainPanelNative.SetSwapChain((IntPtr)_swapChain.Get());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SwapChainPresenter.TargetType.Hwnd:
|
|
||||||
{
|
|
||||||
var swapChainFullscreenDesc = new SwapChainFullscreenDescription
|
|
||||||
{
|
|
||||||
Windowed = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
_graphicsDevice.DXGIFactory.Ptr->
|
|
||||||
CreateSwapChainForHwnd(
|
|
||||||
(IUnknown*)_graphicsDevice.CommandQueue.Ptr,
|
|
||||||
_swapChainPresenter.Hwnd,
|
|
||||||
&swapChainDesc,
|
|
||||||
&swapChainFullscreenDesc,
|
|
||||||
null,
|
|
||||||
(IDXGISwapChain1**)&tempSwapChain);
|
|
||||||
|
|
||||||
fixed (void* swapChainPtr = &_swapChain)
|
|
||||||
{
|
|
||||||
tempSwapChain.Get()->QueryInterface(__uuidof<IDXGISwapChain4>(), &swapChainPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new ArgumentException("Unsupported swap chain surface type.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_backBufferIndex = _swapChain.Get()->GetCurrentBackBufferIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitializeCommandObjects(out FrameResource[] frameResources)
|
|
||||||
{
|
|
||||||
frameResources = new FrameResource[GraphicsPipeline._FRAME_COUNT];
|
|
||||||
for (var i = 0u; i < GraphicsPipeline._FRAME_COUNT; i++)
|
|
||||||
{
|
|
||||||
frameResources[i] = new FrameResource(this, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed (void* fencePtr = &_fence)
|
|
||||||
{
|
|
||||||
_graphicsDevice.NativeDevice.Ptr->CreateFence(0, FenceFlags.None, __uuidof<ID3D12Fence1>(), &fencePtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
frameResources[0].IncrementFenceValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RequestResize(uint width, uint height)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
if (_pendingWidth == width && _pendingHeight == height)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_resizeRequested = true;
|
|
||||||
_pendingWidth = width;
|
|
||||||
_pendingHeight = height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateBackBufferResource(uint i, out ID3D12Resource backBuffer, out uint index)
|
|
||||||
{
|
|
||||||
backBuffer = _swapChain.GetBuffer<ID3D12Resource>(i);
|
|
||||||
backBuffer.Name = $"BackBuffer_{i}";
|
|
||||||
index = _rtvHeap.AllocateDescriptor();
|
|
||||||
var rtvHandle = _rtvHeap.GetCpuHandle(index);
|
|
||||||
_graphicsDevice.NativeDevice.CreateRenderTargetView(backBuffer, null, rtvHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ExecutePendingResize()
|
|
||||||
{
|
|
||||||
if (!_resizeRequested)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint newWidth;
|
|
||||||
uint newHeight;
|
|
||||||
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
newWidth = _pendingWidth;
|
|
||||||
newHeight = _pendingHeight;
|
|
||||||
_resizeRequested = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
WaitIdle();
|
|
||||||
|
|
||||||
for (var i = 0; i < GraphicsPipeline._FRAME_COUNT; i++)
|
|
||||||
{
|
|
||||||
var backBuffer = _frameResources[i].backBuffer;
|
|
||||||
if (backBuffer is not null)
|
|
||||||
{
|
|
||||||
backBuffer.Dispose();
|
|
||||||
_rtvHeap.ReleaseDescriptor(_frameResources[i].backBufferDescriptorIndexes);
|
|
||||||
}
|
|
||||||
|
|
||||||
_frameResources[i].fenceValue = _frameResources[_backBufferIndex].fenceValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_swapChain.ResizeBuffers(GraphicsPipeline._FRAME_COUNT, newWidth, newHeight, Format.B8G8R8A8_UNorm, SwapChainFlags.AllowTearing).CheckError();
|
|
||||||
|
|
||||||
for (var i = 0u; i < GraphicsPipeline._FRAME_COUNT; i++)
|
|
||||||
{
|
|
||||||
CreateBackBufferResource(i, out var backBuffer, out var index);
|
|
||||||
|
|
||||||
_frameResources[i].backBuffer = backBuffer;
|
|
||||||
_frameResources[i].backBufferDescriptorIndexes = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Render()
|
|
||||||
{
|
|
||||||
_backBufferIndex = _swapChain.CurrentBackBufferIndex;
|
|
||||||
|
|
||||||
var frameResource = _frameResources[_backBufferIndex];
|
|
||||||
frameResource.ResetCommandList();
|
|
||||||
frameResource.commandList.ResourceBarrierTransition(_frameResources[_backBufferIndex].backBuffer!, ResourceStates.Present, ResourceStates.RenderTarget);
|
|
||||||
|
|
||||||
foreach (var pass in _renderPasses)
|
|
||||||
{
|
|
||||||
pass.Execute(frameResource.commandBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
frameResource.commandList.ResourceBarrierTransition(_frameResources[_backBufferIndex].backBuffer!, ResourceStates.RenderTarget, ResourceStates.Present);
|
|
||||||
frameResource.commandList.Close();
|
|
||||||
|
|
||||||
_graphicsDevice.CommandQueue.ExecuteCommandList(frameResource.commandList);
|
|
||||||
|
|
||||||
_swapChain.Present(1, PresentFlags.None).CheckError();
|
|
||||||
|
|
||||||
WaitNextFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WaitNextFrame()
|
|
||||||
{
|
|
||||||
var resource = _frameResources[_backBufferIndex];
|
|
||||||
if (_graphicsDevice.CommandQueue.Signal(_fence, resource.fenceValue).Failure)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_fence.CompletedValue < resource.fenceValue
|
|
||||||
&& _fence.SetEventOnCompletion(resource.fenceValue, _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Success)
|
|
||||||
{
|
|
||||||
_fenceEvent.WaitOne();
|
|
||||||
}
|
|
||||||
|
|
||||||
resource.IncrementFenceValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WaitIdle()
|
|
||||||
{
|
|
||||||
var resource = _frameResources[_backBufferIndex];
|
|
||||||
if (_graphicsDevice.CommandQueue.Signal(_fence, resource.fenceValue).Success
|
|
||||||
&& _fence.SetEventOnCompletion(resource.fenceValue, _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Success)
|
|
||||||
{
|
|
||||||
_fenceEvent.WaitOne();
|
|
||||||
resource.IncrementFenceValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
WaitIdle();
|
|
||||||
|
|
||||||
_swapChainPresenter.SwapChainPanelNative?.SetSwapChain(null);
|
|
||||||
|
|
||||||
foreach (var pass in _renderPasses)
|
|
||||||
{
|
|
||||||
pass.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var frameResource in _frameResources)
|
|
||||||
{
|
|
||||||
frameResource.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_swapChain.Dispose();
|
|
||||||
|
|
||||||
_fence.Dispose();
|
|
||||||
_fenceEvent.Dispose();
|
|
||||||
|
|
||||||
_rtvHeap.Dispose();
|
|
||||||
|
|
||||||
_backBufferIndex = 0;
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using Ghost.Graphics.Contracts;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Vortice.Direct3D12;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.DX12;
|
|
||||||
|
|
||||||
public unsafe class DX12Resource : IResource
|
|
||||||
{
|
|
||||||
private readonly ID3D12Resource _nativeResource;
|
|
||||||
|
|
||||||
internal ID3D12Resource NativeResource => _nativeResource;
|
|
||||||
|
|
||||||
public ulong GPUAddress => _nativeResource.GPUVirtualAddress;
|
|
||||||
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get => _nativeResource.Name;
|
|
||||||
set => _nativeResource.Name = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DX12Resource(ID3D12Resource nativeResource)
|
|
||||||
{
|
|
||||||
_nativeResource = nativeResource;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void SetData<T>(Span<T> data)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
_nativeResource.WriteToSubresource(0, data, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_nativeResource.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,252 +0,0 @@
|
|||||||
using Ghost.Graphics.Contracts;
|
|
||||||
using Ghost.Graphics.DX12.Utilities;
|
|
||||||
using Ghost.Graphics.Utilities;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Vortice.Direct3D12;
|
|
||||||
using Vortice.DXGI;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.DX12;
|
|
||||||
|
|
||||||
internal unsafe class DX12ResourceAllocator : IResourceAllocator
|
|
||||||
{
|
|
||||||
private const ResourceStates _INITIALCOPYTARGETSTATE = ResourceStates.Common;
|
|
||||||
private const ResourceStates _INITIALREADTARGETSTATE = ResourceStates.Common;
|
|
||||||
private const ResourceStates _INITIALUAVTARGETSTATE = ResourceStates.Common;
|
|
||||||
|
|
||||||
private const uint _MAX_BYTES = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
|
|
||||||
|
|
||||||
public static ID3D12Resource CreateStaticBuffer<T>(
|
|
||||||
ID3D12Device device,
|
|
||||||
D3D12ResourceUploadBatch resourceUpload,
|
|
||||||
T[] data, ResourceStates afterState,
|
|
||||||
ResourceFlags flags = ResourceFlags.None)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
Span<T> span = data;
|
|
||||||
return CreateStaticBuffer(device, resourceUpload, span, afterState, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ID3D12Resource CreateStaticBuffer<T>(
|
|
||||||
ID3D12Device device,
|
|
||||||
D3D12ResourceUploadBatch resourceUpload,
|
|
||||||
Span<T> data,
|
|
||||||
ResourceStates afterState,
|
|
||||||
ResourceFlags flags = ResourceFlags.None)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
var sizeInBytes = (uint)(sizeof(T) * data.Length);
|
|
||||||
if (sizeInBytes > _MAX_BYTES)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer = device.CreateCommittedResource(
|
|
||||||
HeapType.Default,
|
|
||||||
HeapFlags.None,
|
|
||||||
ResourceDescription.Buffer(sizeInBytes, flags),
|
|
||||||
_INITIALCOPYTARGETSTATE
|
|
||||||
);
|
|
||||||
|
|
||||||
fixed (T* dataPtr = data)
|
|
||||||
{
|
|
||||||
SubresourceData initData = new()
|
|
||||||
{
|
|
||||||
pData = dataPtr,
|
|
||||||
};
|
|
||||||
|
|
||||||
resourceUpload.Upload(buffer, 0, &initData, 1);
|
|
||||||
resourceUpload.Transition(buffer, ResourceStates.CopyDest, afterState);
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ID3D12Resource CreateUploadBuffer<T>(
|
|
||||||
ID3D12Device device,
|
|
||||||
T[] data,
|
|
||||||
ResourceFlags flags = ResourceFlags.None)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
var sizeInBytes = (uint)(sizeof(T) * data.Length);
|
|
||||||
fixed (T* dataPtr = data)
|
|
||||||
{
|
|
||||||
return CreateUploadBuffer(device, sizeInBytes, dataPtr, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ID3D12Resource CreateUploadBuffer<T>(
|
|
||||||
ID3D12Device device,
|
|
||||||
Span<T> data,
|
|
||||||
ResourceFlags flags = ResourceFlags.None)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
var sizeInBytes = (uint)(sizeof(T) * data.Length);
|
|
||||||
fixed (T* dataPtr = data)
|
|
||||||
{
|
|
||||||
return CreateUploadBuffer(device, sizeInBytes, dataPtr, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ID3D12Resource CreateUploadBuffer(
|
|
||||||
ID3D12Device device,
|
|
||||||
uint sizeInBytes,
|
|
||||||
void* data = default,
|
|
||||||
ResourceFlags flags = ResourceFlags.None)
|
|
||||||
{
|
|
||||||
if (sizeInBytes > _MAX_BYTES)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer = device.CreateCommittedResource(
|
|
||||||
HeapType.Upload,
|
|
||||||
HeapFlags.None,
|
|
||||||
ResourceDescription.Buffer(sizeInBytes, flags),
|
|
||||||
ResourceStates.GenericRead
|
|
||||||
);
|
|
||||||
|
|
||||||
if (data is not null)
|
|
||||||
{
|
|
||||||
void* mappedPtr = default;
|
|
||||||
buffer.Map(0, null, &mappedPtr).CheckError();
|
|
||||||
Unsafe.CopyBlock(data, mappedPtr, sizeInBytes);
|
|
||||||
buffer.Unmap(0, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ID3D12Resource CreateReadbackBuffer(
|
|
||||||
ID3D12Device device,
|
|
||||||
uint sizeInBytes,
|
|
||||||
ResourceFlags flags = ResourceFlags.None)
|
|
||||||
{
|
|
||||||
if (sizeInBytes > _MAX_BYTES)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
|
||||||
}
|
|
||||||
var buffer = device.CreateCommittedResource(
|
|
||||||
HeapType.Readback,
|
|
||||||
HeapFlags.None,
|
|
||||||
ResourceDescription.Buffer(sizeInBytes, flags),
|
|
||||||
_INITIALREADTARGETSTATE
|
|
||||||
);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ID3D12Resource CreateUAVBuffer(ID3D12Device device, uint bufferSize,
|
|
||||||
ResourceStates initialState = ResourceStates.Common,
|
|
||||||
ResourceFlags flags = ResourceFlags.None)
|
|
||||||
{
|
|
||||||
if (bufferSize > _MAX_BYTES)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {bufferSize})");
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer = device.CreateCommittedResource(
|
|
||||||
HeapType.Default,
|
|
||||||
HeapFlags.None,
|
|
||||||
ResourceDescription.Buffer(bufferSize, ResourceFlags.AllowUnorderedAccess | flags),
|
|
||||||
_INITIALCOPYTARGETSTATE
|
|
||||||
);
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ID3D12Resource CreateTexture2D<T>(
|
|
||||||
ID3D12Device device,
|
|
||||||
D3D12ResourceUploadBatch resourceUpload,
|
|
||||||
uint width, uint height, Format format,
|
|
||||||
Span<T> data,
|
|
||||||
bool generateMips = false,
|
|
||||||
ResourceStates afterState = ResourceStates.PixelShaderResource,
|
|
||||||
ResourceFlags flags = ResourceFlags.None)
|
|
||||||
where T : unmanaged
|
|
||||||
{
|
|
||||||
if (width > D3D12.RequestTexture2DUOrVDimension || height > D3D12.RequestTexture2DUOrVDimension)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"ERROR: Resource dimensions too large for DirectX 12 (2D: size {width} by {height})");
|
|
||||||
}
|
|
||||||
|
|
||||||
ushort mipLevels = 1;
|
|
||||||
if (generateMips)
|
|
||||||
{
|
|
||||||
generateMips = resourceUpload.IsSupportedForGenerateMips(format);
|
|
||||||
if (generateMips)
|
|
||||||
{
|
|
||||||
mipLevels = (ushort)TextureUtility.CountMips(width, height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var texture = device.CreateCommittedResource(
|
|
||||||
HeapType.Default,
|
|
||||||
HeapFlags.None,
|
|
||||||
ResourceDescription.Texture2D(format, width, height, 1, mipLevels, 1, 0, flags),
|
|
||||||
_INITIALCOPYTARGETSTATE
|
|
||||||
);
|
|
||||||
|
|
||||||
fixed (T* dataPtr = data)
|
|
||||||
{
|
|
||||||
FormatHelper.GetSurfaceInfo(format, width, height, out var rowPitch, out var slicePitch);
|
|
||||||
SubresourceData initData = new()
|
|
||||||
{
|
|
||||||
pData = dataPtr,
|
|
||||||
RowPitch = (nint)rowPitch,
|
|
||||||
SlicePitch = (nint)slicePitch
|
|
||||||
};
|
|
||||||
|
|
||||||
resourceUpload.Upload(texture, 0, &initData, 1);
|
|
||||||
resourceUpload.Transition(texture, ResourceStates.CopyDest, afterState);
|
|
||||||
|
|
||||||
if (generateMips)
|
|
||||||
{
|
|
||||||
resourceUpload.GenerateMips(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IResourceAllocator Create() => new DX12ResourceAllocator();
|
|
||||||
|
|
||||||
public IResource CreateUploadBuffer(uint sizeInBytes, ResourceFlags flags = ResourceFlags.None)
|
|
||||||
{
|
|
||||||
if (sizeInBytes > _MAX_BYTES)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
|
||||||
}
|
|
||||||
|
|
||||||
var device = GraphicsPipeline.GetRenderer<DX12GraphicsDevice>();
|
|
||||||
var buffer = device.NativeDevice.CreateCommittedResource(
|
|
||||||
HeapType.Upload,
|
|
||||||
HeapFlags.None,
|
|
||||||
ResourceDescription.Buffer(sizeInBytes, flags),
|
|
||||||
ResourceStates.GenericRead
|
|
||||||
);
|
|
||||||
|
|
||||||
return new DX12Resource(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IResource CreateCopyDestinationBuffer(uint sizeInBytes, ResourceFlags flags = ResourceFlags.None)
|
|
||||||
{
|
|
||||||
if (sizeInBytes > _MAX_BYTES)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
|
|
||||||
}
|
|
||||||
|
|
||||||
var device = GraphicsPipeline.GetRenderer<DX12GraphicsDevice>();
|
|
||||||
var buffer = device.NativeDevice.CreateCommittedResource(
|
|
||||||
HeapType.Default,
|
|
||||||
HeapFlags.None,
|
|
||||||
ResourceDescription.Buffer(sizeInBytes, flags),
|
|
||||||
ResourceStates.CopyDest
|
|
||||||
);
|
|
||||||
|
|
||||||
return new DX12Resource(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,5 +0,0 @@
|
|||||||
namespace Ghost.Graphics.DX12.Utilities;
|
|
||||||
|
|
||||||
internal static class D3D12Utility
|
|
||||||
{\
|
|
||||||
}
|
|
||||||
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
|
public enum GraphicsAPI
|
||||||
{
|
{
|
||||||
None,
|
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.Collections;
|
||||||
using Misaki.HighPerformance.Unsafe.Helpers;
|
using Misaki.HighPerformance.Unsafe.Helpers;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Vortice.Direct3D12;
|
using Win32.Graphics.Direct3D12;
|
||||||
using Vortice.DXGI;
|
using Win32.Graphics.Dxgi.Common;
|
||||||
using Vortice.Mathematics;
|
|
||||||
|
|
||||||
namespace Ghost.Graphics.Data;
|
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<Vertex> _vertices = new(initialVertexCapacity, Allocator.Persistent);
|
||||||
private UnsafeList<int> _indices = new(initialIndexCapacity, Allocator.Persistent);
|
private UnsafeList<int> _indices = new(initialIndexCapacity, Allocator.Persistent);
|
||||||
|
|
||||||
private BoundingBox _bounds;
|
private Bounds _boundingBox;
|
||||||
|
|
||||||
private IResource? _vertexBuffer;
|
private IResource? _vertexBuffer;
|
||||||
private IResource? _indexBuffer;
|
private IResource? _indexBuffer;
|
||||||
@@ -23,9 +23,13 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
|||||||
|
|
||||||
public Span<Vertex> Vertices => _vertices.AsSpan();
|
public Span<Vertex> Vertices => _vertices.AsSpan();
|
||||||
public Span<int> Indices => _indices.AsSpan();
|
public Span<int> Indices => _indices.AsSpan();
|
||||||
public BoundingBox Bounds => _bounds;
|
public Bounds BoundingBox => _boundingBox;
|
||||||
public int VertexCount => _vertices.Count;
|
|
||||||
public int IndexCount => _indices.Count;
|
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()
|
~Mesh()
|
||||||
{
|
{
|
||||||
@@ -181,7 +185,7 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
|||||||
{
|
{
|
||||||
if (_vertices.Count == 0)
|
if (_vertices.Count == 0)
|
||||||
{
|
{
|
||||||
_bounds = BoundingBox.Zero;
|
_boundingBox = Bounds.Zero;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,14 +198,13 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
|||||||
max = Vector3.Max(max, pos);
|
max = Vector3.Max(max, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
_bounds = new BoundingBox(min, max);
|
_boundingBox = new Bounds(min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uploads the mesh data to GPU resources.
|
/// Uploads the mesh data to GPU resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="device">The Direct3D 12 device.</param>
|
/// <param name="cmb">The command buffer to record the upload commands.</param>
|
||||||
/// <param name="commandList">The Direct3D 12 command list to record the upload commands.</param>
|
|
||||||
public unsafe void UploadMeshData(ICommandBuffer cmb)
|
public unsafe void UploadMeshData(ICommandBuffer cmb)
|
||||||
{
|
{
|
||||||
if (VertexCount == 0 || IndexCount == 0)
|
if (VertexCount == 0 || IndexCount == 0)
|
||||||
@@ -213,12 +216,12 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
|||||||
_indexBuffer?.Dispose();
|
_indexBuffer?.Dispose();
|
||||||
|
|
||||||
var vertexBufferSize = (uint)(VertexCount * sizeof(Vertex));
|
var vertexBufferSize = (uint)(VertexCount * sizeof(Vertex));
|
||||||
var indexBufferSize = (uint)(IndexCount * sizeof(int));
|
var indexBufferSize = IndexCount * sizeof(int);
|
||||||
_vertexBuffer = GraphicsPipeline.ResourceAllocator.CreateCopyDestinationBuffer(vertexBufferSize);
|
_vertexBuffer = GraphicsPipeline.ResourceAllocator.CreateCopyDestinationBuffer(vertexBufferSize);
|
||||||
_indexBuffer = GraphicsPipeline.ResourceAllocator.CreateCopyDestinationBuffer(indexBufferSize);
|
_indexBuffer = GraphicsPipeline.ResourceAllocator.CreateCopyDestinationBuffer(indexBufferSize);
|
||||||
|
|
||||||
using var vertexUploadBuffer = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(vertexBufferSize);
|
var vertexUploadBuffer = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(vertexBufferSize, true);
|
||||||
using var indexUploadBuffer = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(indexBufferSize);
|
var indexUploadBuffer = GraphicsPipeline.ResourceAllocator.CreateUploadBuffer(indexBufferSize, true);
|
||||||
|
|
||||||
vertexUploadBuffer.SetData(_vertices.AsSpan());
|
vertexUploadBuffer.SetData(_vertices.AsSpan());
|
||||||
indexUploadBuffer.SetData(_indices.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(_vertexBuffer, 0, vertexUploadBuffer, 0, vertexBufferSize);
|
||||||
cmb.CopyResource(_indexBuffer, 0, indexUploadBuffer, 0, indexBufferSize);
|
cmb.CopyResource(_indexBuffer, 0, indexUploadBuffer, 0, indexBufferSize);
|
||||||
|
|
||||||
|
cmb.BarrierTransition(_vertexBuffer, ResourceStates.CopyDest, ResourceStates.VertexAndConstantBuffer);
|
||||||
|
cmb.BarrierTransition(_indexBuffer, ResourceStates.CopyDest, ResourceStates.IndexBuffer);
|
||||||
|
|
||||||
_vertexBufferView = new VertexBufferView
|
_vertexBufferView = new VertexBufferView
|
||||||
{
|
{
|
||||||
BufferLocation = _vertexBuffer.GPUAddress,
|
BufferLocation = _vertexBuffer.GPUAddress,
|
||||||
@@ -237,7 +243,7 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
|||||||
{
|
{
|
||||||
BufferLocation = _indexBuffer.GPUAddress,
|
BufferLocation = _indexBuffer.GPUAddress,
|
||||||
SizeInBytes = indexBufferSize,
|
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;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly IntPtr Hwnd
|
public readonly nint Hwnd
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
@@ -39,12 +39,12 @@ internal readonly struct SwapChainPresenter
|
|||||||
{
|
{
|
||||||
Type = TargetType.Composition;
|
Type = TargetType.Composition;
|
||||||
SwapChainPanelNative = swapChainPanelNative;
|
SwapChainPanelNative = swapChainPanelNative;
|
||||||
Hwnd = IntPtr.Zero;
|
Hwnd = nint.Zero;
|
||||||
Width = width;
|
Width = width;
|
||||||
Height = height;
|
Height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwapChainPresenter(IntPtr hwnd, uint width, uint height)
|
public SwapChainPresenter(nint hwnd, uint width, uint height)
|
||||||
{
|
{
|
||||||
Type = TargetType.Hwnd;
|
Type = TargetType.Hwnd;
|
||||||
Hwnd = hwnd;
|
Hwnd = hwnd;
|
||||||
|
|||||||
@@ -1,9 +1,29 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using Win32.Graphics.Dxgi.Common;
|
||||||
|
|
||||||
namespace Ghost.Graphics.Data;
|
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
|
public Vector4 Position
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
@@ -22,7 +42,7 @@ public struct Vertex(Vector4 position, Vector4 normal, Vector4 tangent, Color32
|
|||||||
set;
|
set;
|
||||||
} = tangent;
|
} = tangent;
|
||||||
|
|
||||||
public Color32 Color
|
public Color128 Color
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Vortice.Direct3D12" Version="3.6.2" />
|
|
||||||
<PackageReference Include="Vortice.Win32.Graphics.D3D12MemoryAllocator" Version="2.2.7" />
|
<PackageReference Include="Vortice.Win32.Graphics.D3D12MemoryAllocator" Version="2.2.7" />
|
||||||
<PackageReference Include="Vortice.WinUI" Version="3.6.2" />
|
<PackageReference Include="Vortice.Win32.Graphics.Direct3D.Dxc" Version="2.2.7" />
|
||||||
|
<PackageReference Include="Vortice.Win32.Graphics.Direct3D.Fxc" Version="2.2.7" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -32,4 +32,22 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Using Include="Win32.Apis">
|
||||||
|
<Static>True</Static>
|
||||||
|
</Using>
|
||||||
|
<Using Include="Win32.Graphics.Dxgi.Apis">
|
||||||
|
<Static>True</Static>
|
||||||
|
</Using>
|
||||||
|
<Using Include="Win32.Graphics.Direct3D12.Apis">
|
||||||
|
<Static>True</Static>
|
||||||
|
</Using>
|
||||||
|
<Using Include="Win32.Graphics.Direct3D.Fxc.Apis">
|
||||||
|
<Static>True</Static>
|
||||||
|
</Using>
|
||||||
|
<Using Include="Win32.Graphics.Direct3D.Dxc.Apis">
|
||||||
|
<Static>True</Static>
|
||||||
|
</Using>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Ghost.Core;
|
using Ghost.Core;
|
||||||
using Ghost.Graphics.Contracts;
|
using Ghost.Graphics.Contracts;
|
||||||
|
using Ghost.Graphics.D3D12;
|
||||||
using Ghost.Graphics.Data;
|
using Ghost.Graphics.Data;
|
||||||
using Ghost.Graphics.DX12;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ghost.Graphics;
|
namespace Ghost.Graphics;
|
||||||
@@ -14,9 +14,19 @@ public static class GraphicsPipeline
|
|||||||
private static IResourceAllocator? _resourceAllocator;
|
private static IResourceAllocator? _resourceAllocator;
|
||||||
|
|
||||||
private static Thread? _renderThread;
|
private static Thread? _renderThread;
|
||||||
|
private static AutoResetEvent[]? _cpuReadyEvent;
|
||||||
|
private static AutoResetEvent[]? _gpuReadyEvent;
|
||||||
|
|
||||||
|
private static uint _cpuFenceValue;
|
||||||
|
private static uint _gpuFenceValue;
|
||||||
|
|
||||||
|
private static bool _initialized;
|
||||||
private static bool _isRunning;
|
private static bool _isRunning;
|
||||||
|
|
||||||
|
internal static uint CPUFenceValue => _cpuFenceValue;
|
||||||
|
internal static uint GPUFenceValue => _gpuFenceValue;
|
||||||
|
internal static bool IsRunning => _isRunning;
|
||||||
|
|
||||||
internal static IGraphicsDevice GraphicsDevice
|
internal static IGraphicsDevice GraphicsDevice
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -53,17 +63,32 @@ public static class GraphicsPipeline
|
|||||||
{
|
{
|
||||||
switch (api)
|
switch (api)
|
||||||
{
|
{
|
||||||
case GraphicsAPI.DX12:
|
case GraphicsAPI.D3D12:
|
||||||
_graphicsDevice = new DX12GraphicsDevice();
|
_graphicsDevice = new D3D12GraphicsDevice();
|
||||||
_resourceAllocator = new DX12ResourceAllocator();
|
_resourceAllocator = new D3D12ResourceAllocator();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new NotSupportedException($"Graphics API {api} is not supported.");
|
throw new NotSupportedException($"Graphics API {api} is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderThread = new Thread(RenderLoop);
|
_renderThread = new Thread(RenderLoop)
|
||||||
|
{
|
||||||
|
IsBackground = true,
|
||||||
|
Name = "Graphics Render Thread",
|
||||||
|
Priority = ThreadPriority.Normal
|
||||||
|
};
|
||||||
|
|
||||||
|
_cpuReadyEvent = new AutoResetEvent[_FRAME_COUNT];
|
||||||
|
_gpuReadyEvent = new AutoResetEvent[_FRAME_COUNT];
|
||||||
|
for (var i = 0; i < _FRAME_COUNT; i++)
|
||||||
|
{
|
||||||
|
_cpuReadyEvent[i] = new(false);
|
||||||
|
_gpuReadyEvent[i] = new(true);
|
||||||
|
}
|
||||||
|
|
||||||
CurrentAPI = api;
|
CurrentAPI = api;
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RenderLoop()
|
private static void RenderLoop()
|
||||||
@@ -75,28 +100,62 @@ public static class GraphicsPipeline
|
|||||||
throw new ArgumentException("Renderer has been disposed or is not initialized.");
|
throw new ArgumentException("Renderer has been disposed or is not initialized.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var eventIndex = (int)(_gpuFenceValue % _FRAME_COUNT);
|
||||||
|
_cpuReadyEvent![eventIndex].WaitOne();
|
||||||
|
|
||||||
|
_graphicsDevice.InitializePendingRenderers();
|
||||||
|
|
||||||
foreach (var renderer in _graphicsDevice.Renderers)
|
foreach (var renderer in _graphicsDevice.Renderers)
|
||||||
{
|
{
|
||||||
renderer.ExecutePendingResize();
|
renderer.ExecutePendingResize();
|
||||||
renderer.Render();
|
renderer.Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_gpuFenceValue++;
|
||||||
|
_gpuReadyEvent![eventIndex].Set();
|
||||||
|
|
||||||
|
_resourceAllocator!.ReleaseTempResource();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Start()
|
internal static bool IsGpuReady()
|
||||||
{
|
{
|
||||||
if (_isRunning)
|
return _gpuFenceValue >= _cpuFenceValue;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_graphicsDevice == null || _renderThread == null)
|
|
||||||
|
internal static void WaitForGPUReady()
|
||||||
|
{
|
||||||
|
if (_gpuReadyEvent == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Graphics pipeline is not initialized.");
|
throw new InvalidOperationException("Graphics pipeline is not initialized.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var eventIndex = (int)(_cpuFenceValue % _FRAME_COUNT);
|
||||||
|
_gpuReadyEvent[eventIndex].WaitOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void SignalCPUReady()
|
||||||
|
{
|
||||||
|
if (_cpuReadyEvent == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Graphics pipeline is not initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventIndex = (int)(_cpuFenceValue % _FRAME_COUNT);
|
||||||
|
_cpuFenceValue++;
|
||||||
|
_cpuReadyEvent[eventIndex].Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void Start()
|
||||||
|
{
|
||||||
|
if (_isRunning || !_initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_isRunning = true;
|
_isRunning = true;
|
||||||
_renderThread.Start();
|
_renderThread!.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Stop()
|
internal static void Stop()
|
||||||
@@ -114,10 +173,11 @@ public static class GraphicsPipeline
|
|||||||
|
|
||||||
_graphicsDevice = null;
|
_graphicsDevice = null;
|
||||||
_renderThread = null;
|
_renderThread = null;
|
||||||
|
_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal static T GetRenderer<T>()
|
internal static T GetGraphicsDevice<T>()
|
||||||
where T : class, IGraphicsDevice
|
where T : class, IGraphicsDevice
|
||||||
{
|
{
|
||||||
if (T.TargetAPI != CurrentAPI)
|
if (T.TargetAPI != CurrentAPI)
|
||||||
|
|||||||
130
Ghost.Graphics/RenderPasses/MeshRenderPass.cs
Normal file
130
Ghost.Graphics/RenderPasses/MeshRenderPass.cs
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
using Ghost.Graphics.Contracts;
|
||||||
|
using Ghost.Graphics.D3D12;
|
||||||
|
using Ghost.Graphics.D3D12.Utilities;
|
||||||
|
using Ghost.Graphics.Data;
|
||||||
|
using Ghost.Graphics.Utilities;
|
||||||
|
using System.Drawing;
|
||||||
|
using Win32;
|
||||||
|
using Win32.Graphics.Direct3D;
|
||||||
|
using Win32.Graphics.Direct3D12;
|
||||||
|
using Win32.Graphics.Dxgi.Common;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.RenderPasses;
|
||||||
|
|
||||||
|
internal unsafe class MeshRenderPass : IRenderPass
|
||||||
|
{
|
||||||
|
private const string _HLSL_SOURCE = @"
|
||||||
|
struct VertexInput
|
||||||
|
{
|
||||||
|
float3 position : POSITION;
|
||||||
|
float4 color : COLOR;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PixelInput
|
||||||
|
{
|
||||||
|
float4 position : SV_POSITION;
|
||||||
|
float4 color : COLOR;
|
||||||
|
};
|
||||||
|
|
||||||
|
PixelInput VSMain(VertexInput input)
|
||||||
|
{
|
||||||
|
PixelInput output;
|
||||||
|
output.position = float4(input.position, 1.0f);
|
||||||
|
output.color = input.color;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 PSMain(PixelInput input) : SV_TARGET
|
||||||
|
{
|
||||||
|
return float4(1.0, 1.0, 1.0, 1.0);
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
private Mesh? _mesh;
|
||||||
|
|
||||||
|
private ComPtr<ID3D12RootSignature> _rootSignature;
|
||||||
|
private ComPtr<ID3D12PipelineState> _pipelineState;
|
||||||
|
|
||||||
|
public void Initialize(ICommandBuffer cmb)
|
||||||
|
{
|
||||||
|
_mesh = MeshBuilder.CreateCube(0.25f, new(Color.AliceBlue));
|
||||||
|
_mesh.UploadMeshData(cmb);
|
||||||
|
|
||||||
|
CreateRootSignature();
|
||||||
|
CreatePipelineStateObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateRootSignature()
|
||||||
|
{
|
||||||
|
var rootSignatureDesc = new RootSignatureDescription(0u, null)
|
||||||
|
{
|
||||||
|
Flags = RootSignatureFlags.AllowInputAssemblerInputLayout
|
||||||
|
};
|
||||||
|
|
||||||
|
using ComPtr<ID3DBlob> signature = default;
|
||||||
|
using ComPtr<ID3DBlob> error = default;
|
||||||
|
|
||||||
|
var hr = D3D12SerializeRootSignature(&rootSignatureDesc, RootSignatureVersion.V1_0, signature.GetAddressOf(), error.GetAddressOf());
|
||||||
|
if (hr.Failure)
|
||||||
|
{
|
||||||
|
var errorMessage = System.Text.Encoding.ASCII.GetString((byte*)error.Get()->GetBufferPointer(), (int)error.Get()->GetBufferSize());
|
||||||
|
throw new InvalidOperationException($"Failed to serialize root signature: {errorMessage}");
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphicsPipeline.GetGraphicsDevice<D3D12GraphicsDevice>().NativeDevice.Ptr->CreateRootSignature(0, signature.Get()->GetBufferPointer(), signature.Get()->GetBufferSize(), __uuidof<ID3D12RootSignature>(), _rootSignature.GetVoidAddressOf());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreatePipelineStateObject()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var vertexShaderBytecode = Shader.CompileShader(_HLSL_SOURCE, "VSMain", "vs_5_0");
|
||||||
|
var pixelShaderBytecode = Shader.CompileShader(_HLSL_SOURCE, "PSMain", "ps_5_0");
|
||||||
|
|
||||||
|
fixed (byte* vsPtr = vertexShaderBytecode)
|
||||||
|
fixed (byte* psPtr = pixelShaderBytecode)
|
||||||
|
{
|
||||||
|
var psoDesc = new GraphicsPipelineStateDescription
|
||||||
|
{
|
||||||
|
pRootSignature = _rootSignature.Get(),
|
||||||
|
VS = new ShaderBytecode(vsPtr, (nuint)vertexShaderBytecode.Length),
|
||||||
|
PS = new ShaderBytecode(psPtr, (nuint)pixelShaderBytecode.Length),
|
||||||
|
InputLayout = D3D12PipelineResource.InputLayoutDescription,
|
||||||
|
RasterizerState = RasterizerDescription.CullNone,
|
||||||
|
BlendState = BlendDescription.Opaque,
|
||||||
|
DepthStencilState = DepthStencilDescription.Default,
|
||||||
|
SampleMask = uint.MaxValue,
|
||||||
|
PrimitiveTopologyType = PrimitiveTopologyType.Triangle,
|
||||||
|
NumRenderTargets = 1,
|
||||||
|
SampleDesc = new SampleDescription(1, 0),
|
||||||
|
DSVFormat = Format.Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
|
psoDesc.RTVFormats[0] = D3D12PipelineResource.SWAP_CHAIN_BACK_BUFFER_FORMAT;
|
||||||
|
|
||||||
|
// Create the PSO
|
||||||
|
GraphicsPipeline.GetGraphicsDevice<D3D12GraphicsDevice>().NativeDevice.Ptr->CreateGraphicsPipelineState(&psoDesc, __uuidof<ID3D12PipelineState>(), _pipelineState.GetVoidAddressOf());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(ICommandBuffer cmb)
|
||||||
|
{
|
||||||
|
var dx12Cmb = (D3D12CommandBuffer)cmb;
|
||||||
|
dx12Cmb.CommandList.Ptr->SetGraphicsRootSignature(_rootSignature.Get());
|
||||||
|
dx12Cmb.CommandList.Ptr->SetPipelineState(_pipelineState.Get());
|
||||||
|
|
||||||
|
cmb.DrawMesh(_mesh!);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_mesh?.Dispose();
|
||||||
|
_rootSignature.Dispose();
|
||||||
|
_pipelineState.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,15 +8,17 @@ public static class MeshBuilder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a unit cube centered at the origin with size 1.
|
/// Creates a unit cube centered at the origin with size 1.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Mesh CreateCube(float size = 1.0f, Color32 color = default)
|
public static Mesh CreateCube(float size = 1.0f, Color128 color = default, Vector3 offset = default)
|
||||||
{
|
{
|
||||||
var half = size * 0.5f;
|
var half = size * 0.5f;
|
||||||
var mesh = new Mesh(24, 36);
|
var mesh = new Mesh(24, 36);
|
||||||
|
|
||||||
var corners = new Vector3[]
|
var corners = new Vector3[]
|
||||||
{
|
{
|
||||||
new(-half, -half, -half), new( half, -half, -half), new( half, half, -half), new(-half, half, -half),
|
new Vector3(-half, -half, -half) + offset, new Vector3( half, -half, -half) + offset,
|
||||||
new(-half, -half, half), new( half, -half, half), new( half, half, half), new(-half, half, half)
|
new Vector3( half, half, -half) + offset, new Vector3(-half, half, -half) + offset,
|
||||||
|
new Vector3(-half, -half, half) + offset, new Vector3( half, -half, half) + offset,
|
||||||
|
new Vector3( half, half, half) + offset, new Vector3(-half, half, half) + offset
|
||||||
};
|
};
|
||||||
|
|
||||||
int[][] faces =
|
int[][] faces =
|
||||||
@@ -47,8 +49,8 @@ public static class MeshBuilder
|
|||||||
mesh.AddVertex(new(corners[face[i]].AsVector4(), Vector4.Zero, Vector4.Zero, color, uvs[i].AsVector4()));
|
mesh.AddVertex(new(corners[face[i]].AsVector4(), Vector4.Zero, Vector4.Zero, color, uvs[i].AsVector4()));
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh.AddTriangle(baseIndex + 0, baseIndex + 1, baseIndex + 2);
|
mesh.AddTriangle((int)baseIndex + 0, (int)baseIndex + 1, (int)baseIndex + 2);
|
||||||
mesh.AddTriangle(baseIndex + 0, baseIndex + 2, baseIndex + 3);
|
mesh.AddTriangle((int)baseIndex + 0, (int)baseIndex + 2, (int)baseIndex + 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh.ComputeNormal();
|
mesh.ComputeNormal();
|
||||||
@@ -59,7 +61,7 @@ public static class MeshBuilder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a plane on the XZ axis centered at the origin.
|
/// Creates a plane on the XZ axis centered at the origin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Mesh CreatePlane(float width = 1.0f, float depth = 1.0f, Color32 color = default)
|
public static Mesh CreatePlane(float width = 1.0f, float depth = 1.0f, Color128 color = default)
|
||||||
{
|
{
|
||||||
var hw = width * 0.5f;
|
var hw = width * 0.5f;
|
||||||
var hd = depth * 0.5f;
|
var hd = depth * 0.5f;
|
||||||
@@ -81,7 +83,7 @@ public static class MeshBuilder
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a UV sphere centered at the origin.
|
/// Creates a UV sphere centered at the origin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Mesh CreateSphere(int latitudeSegments = 16, int longitudeSegments = 24, float radius = 0.5f, Color32 color = default)
|
public static Mesh CreateSphere(int latitudeSegments = 16, int longitudeSegments = 24, float radius = 0.5f, Color128 color = default)
|
||||||
{
|
{
|
||||||
var mesh = new Mesh((latitudeSegments + 1) * (longitudeSegments + 1), latitudeSegments * longitudeSegments * 6);
|
var mesh = new Mesh((latitudeSegments + 1) * (longitudeSegments + 1), latitudeSegments * longitudeSegments * 6);
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Data", "Ghost.Data\Gh
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Entities", "Ghost.Entities\Ghost.Entities.csproj", "{8A1C494B-2888-4D0D-8325-9F5C8D1D1955}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Entities", "Ghost.Entities\Ghost.Entities.csproj", "{8A1C494B-2888-4D0D-8325-9F5C8D1D1955}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Graphics", "Ghost.Graphics\Ghost.Graphics.csproj", "{9B0B2CA8-B200-4F19-9D09-A7B99F98BB44}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Generator", "Ghost.Generator\Ghost.Generator.csproj", "{996ABECC-1C5A-4F07-B8AC-D063F91962CB}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Generator", "Ghost.Generator\Ghost.Generator.csproj", "{996ABECC-1C5A-4F07-B8AC-D063F91962CB}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.UnitTest", "Ghost.UnitTest\Ghost.UnitTest.csproj", "{4179873E-8174-4D17-9584-8C223BA71366}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.UnitTest", "Ghost.UnitTest\Ghost.UnitTest.csproj", "{4179873E-8174-4D17-9584-8C223BA71366}"
|
||||||
@@ -21,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Core", "Ghost.Core\Gh
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Editor.Core", "Ghost.Editor.Core\Ghost.Editor.Core.csproj", "{222A4E83-D902-423A-8E99-8321BBFC604C}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Editor.Core", "Ghost.Editor.Core\Ghost.Editor.Core.csproj", "{222A4E83-D902-423A-8E99-8321BBFC604C}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Graphics", "Ghost.Graphics\Ghost.Graphics.csproj", "{F55831B1-2ADE-4CEB-8023-F92C7ABF57FE}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|ARM64 = Debug|ARM64
|
Debug|ARM64 = Debug|ARM64
|
||||||
@@ -85,18 +85,6 @@ Global
|
|||||||
{8A1C494B-2888-4D0D-8325-9F5C8D1D1955}.Release|x64.Build.0 = Release|Any CPU
|
{8A1C494B-2888-4D0D-8325-9F5C8D1D1955}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{8A1C494B-2888-4D0D-8325-9F5C8D1D1955}.Release|x86.ActiveCfg = Release|Any CPU
|
{8A1C494B-2888-4D0D-8325-9F5C8D1D1955}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{8A1C494B-2888-4D0D-8325-9F5C8D1D1955}.Release|x86.Build.0 = Release|Any CPU
|
{8A1C494B-2888-4D0D-8325-9F5C8D1D1955}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{9B0B2CA8-B200-4F19-9D09-A7B99F98BB44}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
|
||||||
{9B0B2CA8-B200-4F19-9D09-A7B99F98BB44}.Debug|ARM64.Build.0 = Debug|Any CPU
|
|
||||||
{9B0B2CA8-B200-4F19-9D09-A7B99F98BB44}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{9B0B2CA8-B200-4F19-9D09-A7B99F98BB44}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{9B0B2CA8-B200-4F19-9D09-A7B99F98BB44}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{9B0B2CA8-B200-4F19-9D09-A7B99F98BB44}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{9B0B2CA8-B200-4F19-9D09-A7B99F98BB44}.Release|ARM64.ActiveCfg = Release|Any CPU
|
|
||||||
{9B0B2CA8-B200-4F19-9D09-A7B99F98BB44}.Release|ARM64.Build.0 = Release|Any CPU
|
|
||||||
{9B0B2CA8-B200-4F19-9D09-A7B99F98BB44}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{9B0B2CA8-B200-4F19-9D09-A7B99F98BB44}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{9B0B2CA8-B200-4F19-9D09-A7B99F98BB44}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{9B0B2CA8-B200-4F19-9D09-A7B99F98BB44}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{996ABECC-1C5A-4F07-B8AC-D063F91962CB}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
{996ABECC-1C5A-4F07-B8AC-D063F91962CB}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||||
{996ABECC-1C5A-4F07-B8AC-D063F91962CB}.Debug|ARM64.Build.0 = Debug|Any CPU
|
{996ABECC-1C5A-4F07-B8AC-D063F91962CB}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||||
{996ABECC-1C5A-4F07-B8AC-D063F91962CB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
{996ABECC-1C5A-4F07-B8AC-D063F91962CB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
@@ -151,6 +139,18 @@ Global
|
|||||||
{222A4E83-D902-423A-8E99-8321BBFC604C}.Release|x64.Build.0 = Release|Any CPU
|
{222A4E83-D902-423A-8E99-8321BBFC604C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{222A4E83-D902-423A-8E99-8321BBFC604C}.Release|x86.ActiveCfg = Release|Any CPU
|
{222A4E83-D902-423A-8E99-8321BBFC604C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{222A4E83-D902-423A-8E99-8321BBFC604C}.Release|x86.Build.0 = Release|Any CPU
|
{222A4E83-D902-423A-8E99-8321BBFC604C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{F55831B1-2ADE-4CEB-8023-F92C7ABF57FE}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||||
|
{F55831B1-2ADE-4CEB-8023-F92C7ABF57FE}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||||
|
{F55831B1-2ADE-4CEB-8023-F92C7ABF57FE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{F55831B1-2ADE-4CEB-8023-F92C7ABF57FE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{F55831B1-2ADE-4CEB-8023-F92C7ABF57FE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{F55831B1-2ADE-4CEB-8023-F92C7ABF57FE}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{F55831B1-2ADE-4CEB-8023-F92C7ABF57FE}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||||
|
{F55831B1-2ADE-4CEB-8023-F92C7ABF57FE}.Release|ARM64.Build.0 = Release|Any CPU
|
||||||
|
{F55831B1-2ADE-4CEB-8023-F92C7ABF57FE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{F55831B1-2ADE-4CEB-8023-F92C7ABF57FE}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{F55831B1-2ADE-4CEB-8023-F92C7ABF57FE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{F55831B1-2ADE-4CEB-8023-F92C7ABF57FE}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
Reference in New Issue
Block a user