forked from Misaki/GhostEngine
Refactor AppState and rendering pipeline components
Changed the `AppStateMachine` to implement `IDisposable` and `IAsyncDisposable` for better resource management. Changed the `IAppState` interface to include asynchronous methods for state transitions. Changed the `App` class to start the host asynchronously and added an `OnClosed` method for proper shutdown. Changed the `EditorState` class to ensure the window closes correctly when exiting the state. Changed the `LandingState` class to improve window activation and deactivation management. Changed the `HostHelper` class to register `LandingWindow` and `EngineEditorWindow` as singletons for better performance. Changed the `ScenePage` class to utilize a new interface for swap chain management. Changed the `OpenProjectPage` and `CreateProjectPage` classes to enhance navigation handling. Changed the `ConsoleViewModel` to improve log update handling with a new context structure. Changed the `OpenProjectViewModel` to clear project lists when navigating away. Changed the `EngineCore` class to start the graphics pipeline asynchronously. Changed the `Logger` class to use a new context structure for log changes. Added the `ICommandBuffer`, `IGraphicsDevice`, and `IRenderView` interfaces to enhance the rendering pipeline. Changed the `DX12CommandBuffer`, `DX12GraphicsDevice`, and `DX12RenderView` classes for improved resource management and rendering efficiency. Refactored the `Mesh` class to use a new `Vertex` structure for simplified vertex management. Added the `TextureUtility` class for texture management utilities, including mip count calculation. Changed the `launchSettings.json` to include a new profile for the graphics project with native debugging enabled. Changed the `MeshBuilder` class to utilize the new `Vertex` structure for vertex creation.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
namespace Ghost.Editor.Core.AppState;
|
namespace Ghost.Editor.Core.AppState;
|
||||||
|
|
||||||
internal class AppStateMachine
|
internal partial class AppStateMachine : IDisposable, IAsyncDisposable
|
||||||
{
|
{
|
||||||
private Dictionary<StateKey, Lazy<IAppState>> s_states = new();
|
private Dictionary<StateKey, Lazy<IAppState>> s_states = new();
|
||||||
private IAppState? s_current;
|
private IAppState? s_current;
|
||||||
@@ -13,22 +13,43 @@ internal class AppStateMachine
|
|||||||
public async Task TransitionToAsync(StateKey stateKey, object? parameter = null)
|
public async Task TransitionToAsync(StateKey stateKey, object? parameter = null)
|
||||||
{
|
{
|
||||||
var previous = s_current;
|
var previous = s_current;
|
||||||
var next = s_states[stateKey].Value;
|
if (!s_states.TryGetValue(stateKey, out var next))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"State '{stateKey}' is not registered.");
|
||||||
|
}
|
||||||
|
|
||||||
if (previous != null)
|
if (previous != null)
|
||||||
{
|
{
|
||||||
await previous.OnExitingAsync();
|
await previous.OnExitingAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
await next.OnEnteringAsync(parameter);
|
await next.Value.OnEnteringAsync(parameter);
|
||||||
|
|
||||||
if (previous != null)
|
if (previous != null)
|
||||||
{
|
{
|
||||||
await previous.OnExitedAsync();
|
await previous.OnExitedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
await next.OnEnteredAsync(parameter);
|
await next.Value.OnEnteredAsync(parameter);
|
||||||
|
|
||||||
s_current = next;
|
s_current = next.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
s_states.Clear();
|
||||||
|
|
||||||
|
s_current?.OnExitingAsync().GetAwaiter().GetResult();
|
||||||
|
s_current?.OnExitedAsync().GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
s_states.Clear();
|
||||||
|
if (s_current != null)
|
||||||
|
{
|
||||||
|
await s_current.OnExitingAsync();
|
||||||
|
await s_current.OnExitedAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
using System.Threading.Tasks;
|
namespace Ghost.Editor.Core.AppState;
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.AppState;
|
|
||||||
|
|
||||||
internal interface IAppState
|
internal interface IAppState
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ public partial class App : Application
|
|||||||
{
|
{
|
||||||
if (Current is App app)
|
if (Current is App app)
|
||||||
{
|
{
|
||||||
|
// HACK: As far as I can tell, there is no proper application shutdown event in WinUI 3.
|
||||||
|
app._window?.Closed -= app.OnClosed;
|
||||||
app._window = value;
|
app._window = value;
|
||||||
|
app._window?.Closed += app.OnClosed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +89,7 @@ public partial class App : Application
|
|||||||
{
|
{
|
||||||
base.OnLaunched(args);
|
base.OnLaunched(args);
|
||||||
|
|
||||||
Host.Start();
|
await Host.StartAsync();
|
||||||
ActivationHandler.Handle(args);
|
ActivationHandler.Handle(args);
|
||||||
|
|
||||||
var stateMachine = GetService<AppStateMachine>();
|
var stateMachine = GetService<AppStateMachine>();
|
||||||
@@ -96,6 +99,12 @@ public partial class App : Application
|
|||||||
await stateMachine.TransitionToAsync(StateKey.Landing);
|
await stateMachine.TransitionToAsync(StateKey.Landing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnClosed(object? sender, WindowEventArgs args)
|
||||||
|
{
|
||||||
|
Host.StopAsync().GetAwaiter().GetResult();
|
||||||
|
Host.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||||
{
|
{
|
||||||
Logger.LogError(e.Exception);
|
Logger.LogError(e.Exception);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ internal class EditorState : IAppState
|
|||||||
{
|
{
|
||||||
App.Window = null;
|
App.Window = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,11 +45,6 @@ internal class EditorState : IAppState
|
|||||||
await _engineCore.ShutDownAsync();
|
await _engineCore.ShutDownAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (App.Window == _window)
|
|
||||||
{
|
|
||||||
App.Window = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_window?.Close();
|
_window?.Close();
|
||||||
_window = null;
|
_window = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using Ghost.Editor;
|
using Ghost.Editor.View.Windows;
|
||||||
using Ghost.Editor.View.Windows;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ghost.Editor.Core.AppState;
|
namespace Ghost.Editor.Core.AppState;
|
||||||
|
|
||||||
@@ -14,27 +12,25 @@ internal class LandingState : IAppState
|
|||||||
{
|
{
|
||||||
App.Window = null;
|
App.Window = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task OnEnteringAsync(object? parameter)
|
public Task OnEnteringAsync(object? parameter)
|
||||||
{
|
{
|
||||||
_window = App.GetService<LandingWindow>();
|
_window = App.GetService<LandingWindow>();
|
||||||
|
_window.Activate();
|
||||||
|
|
||||||
App.Window = _window;
|
App.Window = _window;
|
||||||
|
|
||||||
_window.Activate();
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task OnExitedAsync()
|
public Task OnExitedAsync()
|
||||||
{
|
{
|
||||||
if (App.Window == _window)
|
|
||||||
{
|
|
||||||
App.Window = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_window?.Close();
|
_window?.Close();
|
||||||
_window = null;
|
_window = null;
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"Ghost.Editor (Package)": {
|
"Ghost.Editor (Package)": {
|
||||||
"commandName": "MsixPackage"
|
"commandName": "MsixPackage",
|
||||||
|
"nativeDebugging": false
|
||||||
},
|
},
|
||||||
"Ghost.Editor (Unpackaged)": {
|
"Ghost.Editor (Unpackaged)": {
|
||||||
"commandName": "Project"
|
"commandName": "Project"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ internal static partial class HostHelper
|
|||||||
{
|
{
|
||||||
public static void AddLandingScope(HostBuilderContext context, IServiceCollection services)
|
public static void AddLandingScope(HostBuilderContext context, IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddTransient<LandingWindow>();
|
services.AddSingleton<LandingWindow>();
|
||||||
|
|
||||||
services.AddTransient<CreateProjectPage>();
|
services.AddTransient<CreateProjectPage>();
|
||||||
services.AddTransient<CreateProjectViewModel>();
|
services.AddTransient<CreateProjectViewModel>();
|
||||||
@@ -30,8 +30,8 @@ internal static partial class HostHelper
|
|||||||
{
|
{
|
||||||
services.AddSingleton<EngineCore>();
|
services.AddSingleton<EngineCore>();
|
||||||
|
|
||||||
services.AddTransient<EngineEditorWindow>();
|
services.AddSingleton<EngineEditorWindow>();
|
||||||
services.AddTransient<EngineEditorViewModel>();
|
services.AddSingleton<EngineEditorViewModel>();
|
||||||
|
|
||||||
services.AddTransient<ScenePage>();
|
services.AddTransient<ScenePage>();
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ using Ghost.Editor.Controls.Internal;
|
|||||||
using Ghost.Graphics;
|
using Ghost.Graphics;
|
||||||
using Ghost.Graphics.Contracts;
|
using Ghost.Graphics.Contracts;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Media;
|
using Vortice.WinUI;
|
||||||
using SharpGen.Runtime;
|
|
||||||
using WinRT;
|
using WinRT;
|
||||||
|
|
||||||
namespace Ghost.Editor.View.Pages.EngineEditor;
|
namespace Ghost.Editor.View.Pages.EngineEditor;
|
||||||
@@ -11,6 +10,7 @@ namespace Ghost.Editor.View.Pages.EngineEditor;
|
|||||||
internal sealed partial class ScenePage : NavigationTabPage
|
internal sealed partial class ScenePage : NavigationTabPage
|
||||||
{
|
{
|
||||||
private IRenderView? _renderer;
|
private IRenderView? _renderer;
|
||||||
|
private ISwapChainPanelNative2? _swapChainPanelNative;
|
||||||
|
|
||||||
public ScenePage()
|
public ScenePage()
|
||||||
{
|
{
|
||||||
@@ -28,19 +28,19 @@ internal sealed partial class ScenePage : NavigationTabPage
|
|||||||
|
|
||||||
private void SwapChainPanel_Loaded(object sender, RoutedEventArgs e)
|
private void SwapChainPanel_Loaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var guid = typeof(Vortice.WinUI.ISwapChainPanelNative).GUID;
|
var guid = typeof(ISwapChainPanelNative2).GUID;
|
||||||
Result result = ((IWinRTObject)SwapChainPanel).NativeObject.TryAs(guid, out var swapChainPanelNativeHandle);
|
((IWinRTObject)SwapChainPanel).NativeObject.TryAs(guid, out var swapChainPanelNativeHandle);
|
||||||
result.CheckError();
|
|
||||||
|
|
||||||
var swapChainPanelNative = new Vortice.WinUI.ISwapChainPanelNative(swapChainPanelNativeHandle);
|
_swapChainPanelNative = new ISwapChainPanelNative2(swapChainPanelNativeHandle);
|
||||||
_renderer = GraphicsPipeline.GraphicsDevice.CreateRenderView(new(swapChainPanelNative, (uint)SwapChainPanel.ActualWidth, (uint)SwapChainPanel.ActualHeight));
|
_renderer = GraphicsPipeline.GraphicsDevice.CreateRenderView(new(_swapChainPanelNative, (uint)SwapChainPanel.ActualWidth, (uint)SwapChainPanel.ActualHeight));
|
||||||
|
|
||||||
CompositionTarget.Rendering += OnRendering;
|
//CompositionTarget.Rendering += OnRendering;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SwapChainPanel_Unloaded(object sender, RoutedEventArgs e)
|
private void SwapChainPanel_Unloaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
CompositionTarget.Rendering -= OnRendering;
|
//CompositionTarget.Rendering -= OnRendering;
|
||||||
|
_swapChainPanelNative?.Dispose();
|
||||||
_renderer?.Dispose();
|
_renderer?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ internal sealed partial class ScenePage : NavigationTabPage
|
|||||||
{
|
{
|
||||||
if (e.NewSize.Width > 8.0 && e.NewSize.Height > 8.0)
|
if (e.NewSize.Width > 8.0 && e.NewSize.Height > 8.0)
|
||||||
{
|
{
|
||||||
_renderer?.Resize((uint)e.NewSize.Width, (uint)e.NewSize.Height);
|
_renderer?.RequestResize((uint)e.NewSize.Width, (uint)e.NewSize.Height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,4 +23,10 @@ internal sealed partial class CreateProjectPage : Page
|
|||||||
base.OnNavigatedTo(e);
|
base.OnNavigatedTo(e);
|
||||||
ViewModel.OnNavigatedTo(e.Parameter);
|
ViewModel.OnNavigatedTo(e.Parameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnNavigatedFrom(e);
|
||||||
|
ViewModel.OnNavigatedFrom();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using Ghost.Editor.ViewModels.Pages.Landing;
|
using Ghost.Data.Models;
|
||||||
using Ghost.Data.Models;
|
using Ghost.Editor.ViewModels.Pages.Landing;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.UI.Xaml.Navigation;
|
using Microsoft.UI.Xaml.Navigation;
|
||||||
@@ -26,7 +26,7 @@ internal sealed partial class OpenProjectPage : Page
|
|||||||
ViewModel.OnNavigatedTo(e.Parameter);
|
ViewModel.OnNavigatedTo(e.Parameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
override protected void OnNavigatedFrom(NavigationEventArgs e)
|
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnNavigatedFrom(e);
|
base.OnNavigatedFrom(e);
|
||||||
ViewModel.OnNavigatedFrom();
|
ViewModel.OnNavigatedFrom();
|
||||||
@@ -65,7 +65,8 @@ internal sealed partial class OpenProjectPage : Page
|
|||||||
{
|
{
|
||||||
if (e.ClickedItem is ProjectMetadataInfo project)
|
if (e.ClickedItem is ProjectMetadataInfo project)
|
||||||
{
|
{
|
||||||
await ViewModel.LoadProject(project);
|
await Task.Yield();
|
||||||
|
await ViewModel.OpenProjectAsync(project);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,6 @@ using Ghost.Editor.Core.Notifications;
|
|||||||
using Ghost.Editor.Core.Progress;
|
using Ghost.Editor.Core.Progress;
|
||||||
using Ghost.Editor.ViewModels.Windows;
|
using Ghost.Editor.ViewModels.Windows;
|
||||||
using Ghost.Engine.Resources;
|
using Ghost.Engine.Resources;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using WinUIEx;
|
using WinUIEx;
|
||||||
|
|
||||||
// To learn more about WinUI, the WinUI project structure,
|
// To learn more about WinUI, the WinUI project structure,
|
||||||
@@ -15,8 +14,6 @@ namespace Ghost.Editor.View.Windows;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed partial class EngineEditorWindow : WindowEx
|
internal sealed partial class EngineEditorWindow : WindowEx
|
||||||
{
|
{
|
||||||
private IServiceScope? _editorScope;
|
|
||||||
|
|
||||||
private readonly NotificationService _notificationService;
|
private readonly NotificationService _notificationService;
|
||||||
private readonly ProgressService _progressService;
|
private readonly ProgressService _progressService;
|
||||||
|
|
||||||
@@ -45,17 +42,12 @@ internal sealed partial class EngineEditorWindow : WindowEx
|
|||||||
{
|
{
|
||||||
Bindings.Update();
|
Bindings.Update();
|
||||||
|
|
||||||
_editorScope?.Dispose();
|
|
||||||
_editorScope = App.CreateScope();
|
|
||||||
|
|
||||||
_notificationService.SetReference(InfoBar, NotificationQueue);
|
_notificationService.SetReference(InfoBar, NotificationQueue);
|
||||||
_progressService.SetReference(ProgressBarContainer);
|
_progressService.SetReference(ProgressBarContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
|
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
|
||||||
{
|
{
|
||||||
_editorScope?.Dispose();
|
|
||||||
|
|
||||||
_notificationService.ClearReference();
|
_notificationService.ClearReference();
|
||||||
_progressService.ClearReference();
|
_progressService.ClearReference();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using Ghost.Editor.Core.Notifications;
|
using Ghost.Editor.Core.Notifications;
|
||||||
using Ghost.Editor.View.Pages.Landing;
|
using Ghost.Editor.View.Pages.Landing;
|
||||||
using Ghost.Engine.Resources;
|
using Ghost.Engine.Resources;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.UI.Xaml.Media.Animation;
|
using Microsoft.UI.Xaml.Media.Animation;
|
||||||
using WinUIEx;
|
using WinUIEx;
|
||||||
@@ -11,8 +10,6 @@ namespace Ghost.Editor.View.Windows;
|
|||||||
|
|
||||||
internal sealed partial class LandingWindow : WindowEx
|
internal sealed partial class LandingWindow : WindowEx
|
||||||
{
|
{
|
||||||
private IServiceScope? _landingScope;
|
|
||||||
|
|
||||||
private readonly NotificationService _notificationService;
|
private readonly NotificationService _notificationService;
|
||||||
|
|
||||||
private int _previousSelectedIndex;
|
private int _previousSelectedIndex;
|
||||||
@@ -34,14 +31,11 @@ internal sealed partial class LandingWindow : WindowEx
|
|||||||
|
|
||||||
private void WindowEx_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args)
|
private void WindowEx_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args)
|
||||||
{
|
{
|
||||||
_landingScope?.Dispose();
|
|
||||||
_landingScope = App.CreateScope();
|
|
||||||
_notificationService.SetReference(InfoBar, NotificationQueue);
|
_notificationService.SetReference(InfoBar, NotificationQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
|
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
|
||||||
{
|
{
|
||||||
_landingScope?.Dispose();
|
|
||||||
_notificationService.ClearReference();
|
_notificationService.ClearReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +52,7 @@ internal sealed partial class LandingWindow : WindowEx
|
|||||||
var slideNavigationTransitionEffect = currentSelectedIndex - _previousSelectedIndex > 0 ?
|
var slideNavigationTransitionEffect = currentSelectedIndex - _previousSelectedIndex > 0 ?
|
||||||
SlideNavigationTransitionEffect.FromRight : SlideNavigationTransitionEffect.FromLeft;
|
SlideNavigationTransitionEffect.FromRight : SlideNavigationTransitionEffect.FromLeft;
|
||||||
|
|
||||||
ContentFrame.Navigate(pageType, _landingScope, new SlideNavigationTransitionInfo() { Effect = slideNavigationTransitionEffect });
|
ContentFrame.Navigate(pageType, null, new SlideNavigationTransitionInfo() { Effect = slideNavigationTransitionEffect });
|
||||||
|
|
||||||
_previousSelectedIndex = currentSelectedIndex;
|
_previousSelectedIndex = currentSelectedIndex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,17 +59,17 @@ internal partial class ConsoleViewModel : ObservableObject
|
|||||||
Logger.OnLogsUpdate -= UpdateLogs;
|
Logger.OnLogsUpdate -= UpdateLogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateLogs(LogChangeType updateType)
|
private void UpdateLogs(LogChangeContext ctx)
|
||||||
{
|
{
|
||||||
switch (updateType)
|
switch (ctx.changeType)
|
||||||
{
|
{
|
||||||
case LogChangeType.LogAdded:
|
case LogChangeType.LogAdded:
|
||||||
Logs.Add(Logger.Logs[^1]);
|
Logs.Add(Logger.Logs[ctx.index]);
|
||||||
break;
|
break;
|
||||||
case LogChangeType.LogRemoved:
|
case LogChangeType.LogRemoved:
|
||||||
if (Logs.Count > 0)
|
if (Logs.Count > 0)
|
||||||
{
|
{
|
||||||
Logs.RemoveAt(0);
|
Logs.RemoveAt(ctx.index);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LogChangeType.LogsCleared:
|
case LogChangeType.LogsCleared:
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ internal partial class OpenProjectViewModel(ProjectService projectService, INoti
|
|||||||
|
|
||||||
public async void OnNavigatedTo(object? parameter)
|
public async void OnNavigatedTo(object? parameter)
|
||||||
{
|
{
|
||||||
projects.Clear();
|
|
||||||
await foreach (var projectInfo in projectService.GetAllProjectAsync())
|
await foreach (var projectInfo in projectService.GetAllProjectAsync())
|
||||||
{
|
{
|
||||||
var metadata = await ProjectService.LoadMetadataAsync(projectInfo.MetadataPath);
|
var metadata = await ProjectService.LoadMetadataAsync(projectInfo.MetadataPath);
|
||||||
@@ -54,6 +53,7 @@ internal partial class OpenProjectViewModel(ProjectService projectService, INoti
|
|||||||
|
|
||||||
public void OnNavigatedFrom()
|
public void OnNavigatedFrom()
|
||||||
{
|
{
|
||||||
|
projects.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ContentDrop(DataPackageView dataView)
|
public async Task ContentDrop(DataPackageView dataView)
|
||||||
@@ -89,7 +89,7 @@ internal partial class OpenProjectViewModel(ProjectService projectService, INoti
|
|||||||
UpdateEmptyPlaceHolderVisibility();
|
UpdateEmptyPlaceHolderVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoadProject(ProjectMetadataInfo project)
|
public async Task OpenProjectAsync(ProjectMetadataInfo project)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ internal class EngineCore
|
|||||||
{
|
{
|
||||||
ActivationHandler.Handle(args);
|
ActivationHandler.Handle(args);
|
||||||
GraphicsPipeline.Initialize(GraphicsAPI.DX12);
|
GraphicsPipeline.Initialize(GraphicsAPI.DX12);
|
||||||
|
GraphicsPipeline.Start();
|
||||||
|
|
||||||
Logger.LogInfo("Engine started successfully.");
|
Logger.LogInfo("Engine started successfully.");
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ internal enum LogChangeType
|
|||||||
LogsCleared
|
LogsCleared
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal readonly struct LogChangeContext(LogChangeType type, int index)
|
||||||
|
{
|
||||||
|
public readonly LogChangeType changeType = type;
|
||||||
|
public readonly int index = index;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Logger
|
public static class Logger
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -18,31 +24,30 @@ public static class Logger
|
|||||||
private static readonly List<LogMessage> _logs = new();
|
private static readonly List<LogMessage> _logs = new();
|
||||||
internal static IReadOnlyList<LogMessage> Logs => _logs;
|
internal static IReadOnlyList<LogMessage> Logs => _logs;
|
||||||
|
|
||||||
internal static event Action<LogChangeType>? OnLogsUpdate;
|
internal static event Action<LogChangeContext>? OnLogsUpdate;
|
||||||
|
|
||||||
internal static bool HasStackTrace
|
internal static bool HasStackTrace
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void LogInternal(LogLevel level, string? message, int skipFrame)
|
private static StackTrace? CaptureStackTrace()
|
||||||
|
{
|
||||||
|
return HasStackTrace ? new StackTrace(1, true) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LogInternal(LogLevel level, object? message, StackTrace? stackTrace)
|
||||||
{
|
{
|
||||||
if (_logs.Count >= _MAX_LOGS)
|
if (_logs.Count >= _MAX_LOGS)
|
||||||
{
|
{
|
||||||
_logs.RemoveAt(0);
|
_logs.RemoveAt(0);
|
||||||
OnLogsUpdate?.Invoke(LogChangeType.LogRemoved);
|
OnLogsUpdate?.Invoke(new(LogChangeType.LogRemoved, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
StackTrace? stackTrace = null;
|
var logMessage = new LogMessage(level, message?.ToString(), stackTrace?.ToString());
|
||||||
if (HasStackTrace)
|
|
||||||
{
|
|
||||||
stackTrace = new StackTrace(skipFrame, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
var logMessage = new LogMessage(level, message, stackTrace?.ToString());
|
|
||||||
_logs.Add(logMessage);
|
_logs.Add(logMessage);
|
||||||
|
|
||||||
OnLogsUpdate?.Invoke(LogChangeType.LogAdded);
|
OnLogsUpdate?.Invoke(new(LogChangeType.LogAdded, _logs.Count - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void LogExceptionInternal(Exception ex)
|
private static void LogExceptionInternal(Exception ex)
|
||||||
@@ -50,33 +55,33 @@ public static class Logger
|
|||||||
if (_logs.Count >= _MAX_LOGS)
|
if (_logs.Count >= _MAX_LOGS)
|
||||||
{
|
{
|
||||||
_logs.RemoveAt(0);
|
_logs.RemoveAt(0);
|
||||||
OnLogsUpdate?.Invoke(LogChangeType.LogRemoved);
|
OnLogsUpdate?.Invoke(new(LogChangeType.LogRemoved, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
var logMessage = new LogMessage(LogLevel.Error, ex.Message, ex.StackTrace);
|
var logMessage = new LogMessage(LogLevel.Error, ex.Message, ex.StackTrace);
|
||||||
_logs.Add(logMessage);
|
_logs.Add(logMessage);
|
||||||
|
|
||||||
OnLogsUpdate?.Invoke(LogChangeType.LogAdded);
|
OnLogsUpdate?.Invoke(new(LogChangeType.LogAdded, _logs.Count - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Log(LogLevel level, string? message)
|
public static void Log(LogLevel level, object? message)
|
||||||
{
|
{
|
||||||
LogInternal(level, message, 2);
|
LogInternal(level, message, CaptureStackTrace());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LogInfo(string? message)
|
public static void LogInfo(object? message)
|
||||||
{
|
{
|
||||||
LogInternal(LogLevel.Info, message, 3);
|
LogInternal(LogLevel.Info, message, CaptureStackTrace());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LogWarning(string? message)
|
public static void LogWarning(object? message)
|
||||||
{
|
{
|
||||||
LogInternal(LogLevel.Warning, message, 3);
|
LogInternal(LogLevel.Warning, message, CaptureStackTrace());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LogError(string? message)
|
public static void LogError(object? message)
|
||||||
{
|
{
|
||||||
LogInternal(LogLevel.Error, message, 3);
|
LogInternal(LogLevel.Error, message, CaptureStackTrace());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LogError(Exception ex)
|
public static void LogError(Exception ex)
|
||||||
@@ -84,17 +89,17 @@ public static class Logger
|
|||||||
LogExceptionInternal(ex);
|
LogExceptionInternal(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Assert(bool condition, string? message = null)
|
public static void Assert(bool condition, object? message = null)
|
||||||
{
|
{
|
||||||
if (!condition)
|
if (!condition)
|
||||||
{
|
{
|
||||||
LogInternal(LogLevel.Error, message ?? "Assertion failed", 3);
|
LogInternal(LogLevel.Error, message ?? "Assertion failed", CaptureStackTrace());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Clear()
|
internal static void Clear()
|
||||||
{
|
{
|
||||||
_logs.Clear();
|
_logs.Clear();
|
||||||
OnLogsUpdate?.Invoke(LogChangeType.LogsCleared);
|
OnLogsUpdate?.Invoke(new(LogChangeType.LogsCleared, -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
5
Ghost.Graphics/Contracts/ICommandBuffer.cs
Normal file
5
Ghost.Graphics/Contracts/ICommandBuffer.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
namespace Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
|
public interface ICommandBuffer
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace Ghost.Graphics.Contracts;
|
namespace Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
internal interface IGraphicsDevice : IDisposable
|
public interface IGraphicsDevice : IDisposable
|
||||||
{
|
{
|
||||||
public static abstract IGraphicsDevice Create();
|
public static abstract IGraphicsDevice Create();
|
||||||
|
|
||||||
public IRenderView CreateRenderView(in SwapChainSurface swapChainSurface);
|
public IRenderView CreateRenderView(in SwapChainPresenter swapChainSurface);
|
||||||
public void OnRender();
|
public void OnRender();
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,42 @@
|
|||||||
namespace Ghost.Graphics.Contracts;
|
namespace Ghost.Graphics.Contracts;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the contract for a render view in the graphics pipeline.
|
||||||
|
/// </summary>
|
||||||
internal interface IRenderView : IDisposable
|
internal interface IRenderView : IDisposable
|
||||||
{
|
{
|
||||||
public void Resize(uint width, uint height);
|
/// <summary>
|
||||||
public void Render();
|
/// Requests a resize of the render view.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="width">The new width of the render view.</param>
|
||||||
|
/// <param name="height">The new height of the render view.</param>
|
||||||
|
/// <remarks>This only submits a resize request without executing it. May overwrite last request if next request issued before next frame.</remarks>
|
||||||
|
public void RequestResize(uint width, uint height);
|
||||||
|
/// <summary>
|
||||||
|
/// Executes any pending resize operations for the current context.
|
||||||
|
/// </summary>
|
||||||
|
public void ExecutePendingResize();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Begins a render operation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An ICommandBuffer instance to manage render commands.</returns>
|
||||||
|
public ICommandBuffer BeginRender();
|
||||||
|
/// <summary>
|
||||||
|
/// Renders the current content to the output target.
|
||||||
|
/// </summary>
|
||||||
|
public void Render();
|
||||||
|
/// <summary>
|
||||||
|
/// Ends the current rendering operation and finalizes any pending rendering tasks.
|
||||||
|
/// </summary>
|
||||||
|
public void EndRender();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the next frame to be ready for rendering.
|
||||||
|
/// </summary>
|
||||||
public void WaitNextFrame();
|
public void WaitNextFrame();
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the rendering operations to complete and the GPU to be idle.
|
||||||
|
/// </summary>
|
||||||
public void WaitIdle();
|
public void WaitIdle();
|
||||||
public void Flush();
|
|
||||||
}
|
}
|
||||||
14
Ghost.Graphics/DX12/DX12CommandBuffer.cs
Normal file
14
Ghost.Graphics/DX12/DX12CommandBuffer.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Ghost.Graphics.Contracts;
|
||||||
|
using Vortice.Direct3D12;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.DX12;
|
||||||
|
|
||||||
|
internal class DX12CommandBuffer : ICommandBuffer
|
||||||
|
{
|
||||||
|
private ID3D12GraphicsCommandList10 _commandList;
|
||||||
|
|
||||||
|
public DX12CommandBuffer(ID3D12GraphicsCommandList10 commandList)
|
||||||
|
{
|
||||||
|
_commandList = commandList;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,29 +8,29 @@ namespace Ghost.Graphics.DX12;
|
|||||||
|
|
||||||
internal class DX12DebugLayer : IDebugLayer
|
internal class DX12DebugLayer : IDebugLayer
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
private readonly ID3D12Debug6 _d3d12Debug;
|
private readonly ID3D12Debug6 _d3d12Debug;
|
||||||
private readonly IDXGIDebug1 _dxgiDebug;
|
private readonly IDXGIDebug1 _dxgiDebug;
|
||||||
#endif
|
private readonly IDXGIInfoQueue? _dxgiInfoQueue;
|
||||||
|
|
||||||
public DX12DebugLayer()
|
public DX12DebugLayer()
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
_d3d12Debug = D3D12.D3D12GetDebugInterface<ID3D12Debug6>();
|
_d3d12Debug = D3D12.D3D12GetDebugInterface<ID3D12Debug6>();
|
||||||
_d3d12Debug.EnableDebugLayer();
|
_d3d12Debug.EnableDebugLayer();
|
||||||
|
|
||||||
_dxgiDebug = DXGI.DXGIGetDebugInterface1<IDXGIDebug1>();
|
_dxgiDebug = DXGI.DXGIGetDebugInterface1<IDXGIDebug1>();
|
||||||
_dxgiDebug.EnableLeakTrackingForThread();
|
_dxgiDebug.EnableLeakTrackingForThread();
|
||||||
#endif
|
|
||||||
|
_dxgiInfoQueue = DXGI.DXGIGetDebugInterface1<IDXGIInfoQueue>();
|
||||||
|
_dxgiInfoQueue.SetBreakOnSeverity(DXGI.DebugAll, InfoQueueMessageSeverity.Error, true);
|
||||||
|
_dxgiInfoQueue.SetBreakOnSeverity(DXGI.DebugAll, InfoQueueMessageSeverity.Corruption, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
_dxgiDebug.ReportLiveObjects(DXGI.DebugAll, ReportLiveObjectFlags.Detail | ReportLiveObjectFlags.IgnoreInternal);
|
_dxgiDebug.ReportLiveObjects(DXGI.DebugAll, ReportLiveObjectFlags.Detail | ReportLiveObjectFlags.IgnoreInternal);
|
||||||
|
|
||||||
_d3d12Debug?.Dispose();
|
_d3d12Debug.Dispose();
|
||||||
_dxgiDebug?.Dispose();
|
_dxgiDebug.Dispose();
|
||||||
#endif
|
_dxgiInfoQueue?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,11 +13,14 @@ internal class DX12GraphicsDevice : IGraphicsDevice
|
|||||||
private readonly ID3D12CommandQueue _commandQueue;
|
private readonly ID3D12CommandQueue _commandQueue;
|
||||||
|
|
||||||
private readonly List<IRenderView> _renderViews = new();
|
private readonly List<IRenderView> _renderViews = new();
|
||||||
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
private readonly IDebugLayer _debugLayer;
|
private readonly DX12DebugLayer _debugLayer;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
public ID3D12Device14 Device => _device;
|
public ID3D12Device14 Device => _device;
|
||||||
public IDXGIFactory7 DXGIFactory => _dxgiFactory;
|
public IDXGIFactory7 DXGIFactory => _dxgiFactory;
|
||||||
public ID3D12CommandQueue CommandQueue => _commandQueue;
|
public ID3D12CommandQueue CommandQueue => _commandQueue;
|
||||||
@@ -61,6 +64,8 @@ internal class DX12GraphicsDevice : IGraphicsDevice
|
|||||||
adapter.Dispose();
|
adapter.Dispose();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adapter.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d3d12Device == null)
|
if (d3d12Device == null)
|
||||||
@@ -83,35 +88,52 @@ internal class DX12GraphicsDevice : IGraphicsDevice
|
|||||||
queue = _device.CreateCommandQueue(queueDesc);
|
queue = _device.CreateCommandQueue(queueDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRenderView CreateRenderView(in SwapChainSurface swapChainSurface)
|
public IRenderView CreateRenderView(in SwapChainPresenter swapChainSurface)
|
||||||
{
|
{
|
||||||
var renderView = new DX12RenderView(this, swapChainSurface);
|
var renderView = new DX12RenderView(this, swapChainSurface);
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
_renderViews.Add(renderView);
|
_renderViews.Add(renderView);
|
||||||
|
}
|
||||||
|
|
||||||
return renderView;
|
return renderView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnRender()
|
public void OnRender()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
{
|
{
|
||||||
foreach (var renderView in _renderViews)
|
foreach (var renderView in _renderViews)
|
||||||
{
|
{
|
||||||
|
renderView.ExecutePendingResize();
|
||||||
|
|
||||||
|
renderView.BeginRender();
|
||||||
renderView.Render();
|
renderView.Render();
|
||||||
|
renderView.EndRender();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var renderView in _renderViews)
|
foreach (var renderView in _renderViews)
|
||||||
{
|
{
|
||||||
renderView.Dispose();
|
renderView.Dispose();
|
||||||
}
|
}
|
||||||
_renderViews.Clear();
|
_renderViews.Clear();
|
||||||
|
|
||||||
_commandQueue?.Dispose();
|
_commandQueue.Release();
|
||||||
_device?.Dispose();
|
_device.Release();
|
||||||
_dxgiFactory?.Dispose();
|
_dxgiFactory.Release();
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
_debugLayer.Dispose();
|
_debugLayer.Dispose();
|
||||||
#endif
|
#endif
|
||||||
|
_disposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
using Ghost.Graphics.Contracts;
|
using Ghost.Graphics.Contracts;
|
||||||
using Ghost.Graphics.Data;
|
using Ghost.Graphics.Data;
|
||||||
using Ghost.Graphics.DX12.Utilities;
|
using Ghost.Graphics.DX12.Utilities;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Vortice.Direct3D12;
|
using Vortice.Direct3D12;
|
||||||
using Vortice.DXGI;
|
using Vortice.DXGI;
|
||||||
|
|
||||||
@@ -13,6 +12,7 @@ internal class DX12RenderView : IRenderView
|
|||||||
private const int _DEPTH_STENCIL_VIEW_HEAP_SIZE = 256;
|
private const int _DEPTH_STENCIL_VIEW_HEAP_SIZE = 256;
|
||||||
|
|
||||||
private readonly DX12GraphicsDevice _graphicsDevice;
|
private readonly DX12GraphicsDevice _graphicsDevice;
|
||||||
|
private readonly SwapChainPresenter _swapChainPresenter;
|
||||||
|
|
||||||
private readonly IDXGISwapChain4 _swapChain;
|
private readonly IDXGISwapChain4 _swapChain;
|
||||||
private readonly ID3D12Resource[] _renderTargets;
|
private readonly ID3D12Resource[] _renderTargets;
|
||||||
@@ -20,7 +20,7 @@ internal class DX12RenderView : IRenderView
|
|||||||
private uint _backBufferIndex;
|
private uint _backBufferIndex;
|
||||||
|
|
||||||
private readonly ID3D12CommandAllocator[] _commandAllocators;
|
private readonly ID3D12CommandAllocator[] _commandAllocators;
|
||||||
private readonly ID3D12GraphicsCommandList7 _commandList;
|
private readonly ID3D12GraphicsCommandList10 _commandList;
|
||||||
|
|
||||||
private readonly ID3D12Fence1 _fence;
|
private readonly ID3D12Fence1 _fence;
|
||||||
private readonly AutoResetEvent _fenceEvent;
|
private readonly AutoResetEvent _fenceEvent;
|
||||||
@@ -28,9 +28,19 @@ internal class DX12RenderView : IRenderView
|
|||||||
|
|
||||||
private readonly D3D12DescriptorAllocator _rtvHeap;
|
private readonly D3D12DescriptorAllocator _rtvHeap;
|
||||||
|
|
||||||
public DX12RenderView(DX12GraphicsDevice pipelineContext, in SwapChainSurface swapChainSurface)
|
private readonly ICommandBuffer _commandBuffer;
|
||||||
|
|
||||||
|
private readonly Lock _lock = new();
|
||||||
|
private uint _pendingWidth;
|
||||||
|
private uint _pendingHeight;
|
||||||
|
private bool _resizeRequested;
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
public DX12RenderView(DX12GraphicsDevice graphicsDevice, in SwapChainPresenter swapChainSurface)
|
||||||
{
|
{
|
||||||
_graphicsDevice = pipelineContext;
|
_graphicsDevice = graphicsDevice;
|
||||||
|
_swapChainPresenter = swapChainSurface;
|
||||||
|
|
||||||
_rtvHeap = new(_graphicsDevice.Device, DescriptorHeapType.RenderTargetView, _RENDER_TARGET_VIEW_HEAP_SIZE);
|
_rtvHeap = new(_graphicsDevice.Device, DescriptorHeapType.RenderTargetView, _RENDER_TARGET_VIEW_HEAP_SIZE);
|
||||||
|
|
||||||
@@ -39,21 +49,23 @@ internal class DX12RenderView : IRenderView
|
|||||||
_fenceValues = new ulong[GraphicsPipeline.FRAME_COUNT];
|
_fenceValues = new ulong[GraphicsPipeline.FRAME_COUNT];
|
||||||
_renderTargetDescriptorIndexes = new uint[GraphicsPipeline.FRAME_COUNT];
|
_renderTargetDescriptorIndexes = new uint[GraphicsPipeline.FRAME_COUNT];
|
||||||
|
|
||||||
InitializeSwapChain(swapChainSurface, out _swapChain);
|
InitializeSwapChain(out _swapChain);
|
||||||
InitializeCommandObjects(out _commandAllocators, out _commandList, out _fence);
|
InitializeCommandObjects(out _commandAllocators, out _commandList, out _fence);
|
||||||
CreateRenderTargets();
|
CreateRenderTargets();
|
||||||
|
|
||||||
|
_commandBuffer = new DX12CommandBuffer(_commandList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeSwapChain(in SwapChainSurface swapChainSurface, out IDXGISwapChain4 swapChain)
|
private void InitializeSwapChain(out IDXGISwapChain4 swapChain)
|
||||||
{
|
{
|
||||||
var swapChainDesc = new SwapChainDescription1
|
var swapChainDesc = new SwapChainDescription1
|
||||||
{
|
{
|
||||||
Width = swapChainSurface.Width,
|
Width = _swapChainPresenter.Width,
|
||||||
Height = swapChainSurface.Height,
|
Height = _swapChainPresenter.Height,
|
||||||
Format = Format.B8G8R8A8_UNorm,
|
Format = Format.B8G8R8A8_UNorm,
|
||||||
Stereo = false,
|
Stereo = false,
|
||||||
SampleDescription = new SampleDescription(1, 0),
|
SampleDescription = new SampleDescription(1, 0),
|
||||||
BufferUsage = Usage.RenderTargetOutput,
|
BufferUsage = Usage.Backbuffer | Usage.RenderTargetOutput,
|
||||||
BufferCount = GraphicsPipeline.FRAME_COUNT,
|
BufferCount = GraphicsPipeline.FRAME_COUNT,
|
||||||
Scaling = Scaling.Stretch,
|
Scaling = Scaling.Stretch,
|
||||||
SwapEffect = SwapEffect.FlipDiscard,
|
SwapEffect = SwapEffect.FlipDiscard,
|
||||||
@@ -61,36 +73,37 @@ internal class DX12RenderView : IRenderView
|
|||||||
Flags = SwapChainFlags.AllowTearing
|
Flags = SwapChainFlags.AllowTearing
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE: Not going to need it for now, this is for standalone applications.
|
switch (_swapChainPresenter.Type)
|
||||||
|
{
|
||||||
|
case SwapChainPresenter.TargetType.Composition:
|
||||||
|
var swapChain1 = _graphicsDevice.DXGIFactory.CreateSwapChainForComposition(_graphicsDevice.CommandQueue, swapChainDesc);
|
||||||
|
swapChain = swapChain1.QueryInterface<IDXGISwapChain4>();
|
||||||
|
swapChain1.Dispose();
|
||||||
|
|
||||||
|
_backBufferIndex = swapChain.CurrentBackBufferIndex;
|
||||||
|
_swapChainPresenter.SwapChainPanelNative!.SetSwapChain(swapChain);
|
||||||
|
break;
|
||||||
|
case SwapChainPresenter.TargetType.Hwnd:
|
||||||
var swapChainFullscreenDesc = new SwapChainFullscreenDescription
|
var swapChainFullscreenDesc = new SwapChainFullscreenDescription
|
||||||
{
|
{
|
||||||
Windowed = true,
|
Windowed = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (swapChainSurface.Type)
|
|
||||||
{
|
|
||||||
case SwapChainSurface.TargetType.Composition:
|
|
||||||
var swapChain1 = _graphicsDevice.DXGIFactory.CreateSwapChainForComposition(_graphicsDevice.CommandQueue, swapChainDesc);
|
|
||||||
swapChain = swapChain1.QueryInterface<IDXGISwapChain4>();
|
|
||||||
|
|
||||||
_backBufferIndex = swapChain.CurrentBackBufferIndex;
|
|
||||||
swapChainSurface.SwapChainPanelNative!.SetSwapChain(swapChain);
|
|
||||||
break;
|
|
||||||
case SwapChainSurface.TargetType.Hwnd:
|
|
||||||
var swapChain2 = _graphicsDevice.DXGIFactory.CreateSwapChainForHwnd(
|
var swapChain2 = _graphicsDevice.DXGIFactory.CreateSwapChainForHwnd(
|
||||||
_graphicsDevice.CommandQueue,
|
_graphicsDevice.CommandQueue,
|
||||||
swapChainSurface.Hwnd,
|
_swapChainPresenter.Hwnd,
|
||||||
swapChainDesc,
|
swapChainDesc,
|
||||||
swapChainFullscreenDesc,
|
swapChainFullscreenDesc,
|
||||||
null);
|
null);
|
||||||
swapChain = swapChain2.QueryInterface<IDXGISwapChain4>();
|
swapChain = swapChain2.QueryInterface<IDXGISwapChain4>();
|
||||||
|
swapChain2.Dispose();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException("Unsupported swap chain surface type.");
|
throw new ArgumentException("Unsupported swap chain surface type.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeCommandObjects(out ID3D12CommandAllocator[] commandAllocator, out ID3D12GraphicsCommandList7 commandList, out ID3D12Fence1 fence)
|
private void InitializeCommandObjects(out ID3D12CommandAllocator[] commandAllocator, out ID3D12GraphicsCommandList10 commandList, out ID3D12Fence1 fence)
|
||||||
{
|
{
|
||||||
commandAllocator = new ID3D12CommandAllocator[GraphicsPipeline.FRAME_COUNT];
|
commandAllocator = new ID3D12CommandAllocator[GraphicsPipeline.FRAME_COUNT];
|
||||||
for (var i = 0; i < GraphicsPipeline.FRAME_COUNT; i++)
|
for (var i = 0; i < GraphicsPipeline.FRAME_COUNT; i++)
|
||||||
@@ -98,10 +111,10 @@ internal class DX12RenderView : IRenderView
|
|||||||
commandAllocator[i] = _graphicsDevice.Device.CreateCommandAllocator(CommandListType.Direct);
|
commandAllocator[i] = _graphicsDevice.Device.CreateCommandAllocator(CommandListType.Direct);
|
||||||
}
|
}
|
||||||
|
|
||||||
commandList = _graphicsDevice.Device.CreateCommandList<ID3D12GraphicsCommandList7>(CommandListType.Direct, commandAllocator[0], null!);
|
commandList = _graphicsDevice.Device.CreateCommandList<ID3D12GraphicsCommandList10>(CommandListType.Direct, commandAllocator[0], null!);
|
||||||
|
commandList.Close();
|
||||||
fence = _graphicsDevice.Device.CreateFence<ID3D12Fence1>(_fenceValues[_backBufferIndex], FenceFlags.None);
|
fence = _graphicsDevice.Device.CreateFence<ID3D12Fence1>(_fenceValues[_backBufferIndex], FenceFlags.None);
|
||||||
|
|
||||||
_commandList.Close();
|
|
||||||
_fenceValues[_backBufferIndex]++;
|
_fenceValues[_backBufferIndex]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,32 +131,80 @@ internal class DX12RenderView : IRenderView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Resize(uint width, uint height)
|
public void RequestResize(uint width, uint height)
|
||||||
{
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_pendingWidth == width && _pendingHeight == height)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_resizeRequested = true;
|
||||||
|
_pendingWidth = width;
|
||||||
|
_pendingHeight = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExecutePendingResize()
|
||||||
|
{
|
||||||
|
if (!_resizeRequested)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint newWidth;
|
||||||
|
uint newHeight;
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
newWidth = _pendingWidth;
|
||||||
|
newHeight = _pendingHeight;
|
||||||
|
_resizeRequested = false;
|
||||||
|
}
|
||||||
|
|
||||||
WaitIdle();
|
WaitIdle();
|
||||||
|
|
||||||
for (var i = 0; i < GraphicsPipeline.FRAME_COUNT; i++)
|
for (var i = 0; i < GraphicsPipeline.FRAME_COUNT; i++)
|
||||||
|
{
|
||||||
|
if (_renderTargets[i] is not null)
|
||||||
{
|
{
|
||||||
_renderTargets[i].Dispose();
|
_renderTargets[i].Dispose();
|
||||||
_rtvHeap.ReleaseDescriptor(_renderTargetDescriptorIndexes[i]);
|
_rtvHeap.ReleaseDescriptor(_renderTargetDescriptorIndexes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
_fenceValues[i] = _fenceValues[_backBufferIndex];
|
_fenceValues[i] = _fenceValues[_backBufferIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
_swapChain.ResizeBuffers(GraphicsPipeline.FRAME_COUNT, width, height, Format.B8G8R8A8_UNorm, SwapChainFlags.AllowTearing).CheckError();
|
_swapChain.ResizeBuffers(GraphicsPipeline.FRAME_COUNT, newWidth, newHeight, Format.B8G8R8A8_UNorm, SwapChainFlags.AllowTearing).CheckError();
|
||||||
|
|
||||||
CreateRenderTargets();
|
CreateRenderTargets();
|
||||||
_backBufferIndex = _swapChain.CurrentBackBufferIndex;
|
_backBufferIndex = _swapChain.CurrentBackBufferIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Render()
|
public ICommandBuffer BeginRender()
|
||||||
{
|
{
|
||||||
|
_backBufferIndex = _swapChain.CurrentBackBufferIndex;
|
||||||
|
|
||||||
var commandAllocator = _commandAllocators[_backBufferIndex];
|
var commandAllocator = _commandAllocators[_backBufferIndex];
|
||||||
commandAllocator.Reset();
|
commandAllocator.Reset();
|
||||||
_commandList.Reset(commandAllocator, null);
|
_commandList.Reset(commandAllocator, null);
|
||||||
|
|
||||||
|
_commandList.ResourceBarrierTransition(_renderTargets[_backBufferIndex], ResourceStates.Present, ResourceStates.RenderTarget);
|
||||||
|
|
||||||
|
return _commandBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Render()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndRender()
|
||||||
|
{
|
||||||
|
_commandList.ResourceBarrierTransition(_renderTargets[_backBufferIndex], ResourceStates.RenderTarget, ResourceStates.Present);
|
||||||
_commandList.Close();
|
_commandList.Close();
|
||||||
_graphicsDevice.CommandQueue.ExecuteCommandLists([_commandList]);
|
|
||||||
|
_graphicsDevice.CommandQueue.ExecuteCommandLists(new[] { _commandList });
|
||||||
|
|
||||||
_swapChain.Present(1, PresentFlags.None).CheckError();
|
_swapChain.Present(1, PresentFlags.None).CheckError();
|
||||||
|
|
||||||
@@ -159,7 +220,6 @@ internal class DX12RenderView : IRenderView
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_backBufferIndex = _swapChain.CurrentBackBufferIndex;
|
|
||||||
if (_fence.CompletedValue < _fenceValues[_backBufferIndex]
|
if (_fence.CompletedValue < _fenceValues[_backBufferIndex]
|
||||||
&& _fence.SetEventOnCompletion(_fenceValues[_backBufferIndex], _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Success)
|
&& _fence.SetEventOnCompletion(_fenceValues[_backBufferIndex], _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Success)
|
||||||
{
|
{
|
||||||
@@ -172,38 +232,36 @@ internal class DX12RenderView : IRenderView
|
|||||||
public void WaitIdle()
|
public void WaitIdle()
|
||||||
{
|
{
|
||||||
var fenceValue = _fenceValues[_backBufferIndex];
|
var fenceValue = _fenceValues[_backBufferIndex];
|
||||||
if (_graphicsDevice.CommandQueue.Signal(_fence, fenceValue).Failure
|
if (_graphicsDevice.CommandQueue.Signal(_fence, fenceValue).Success
|
||||||
|| _fence.SetEventOnCompletion(fenceValue, _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Failure)
|
&& _fence.SetEventOnCompletion(fenceValue, _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Success)
|
||||||
{
|
{
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_fenceEvent.WaitOne();
|
_fenceEvent.WaitOne();
|
||||||
_fenceValues[_backBufferIndex]++;
|
_fenceValues[_backBufferIndex]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Flush()
|
|
||||||
{
|
|
||||||
for (var i = 0; i < GraphicsPipeline.FRAME_COUNT; i++)
|
|
||||||
{
|
|
||||||
WaitIdle();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Flush();
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitIdle();
|
||||||
|
|
||||||
|
_swapChainPresenter.SwapChainPanelNative?.SetSwapChain(null);
|
||||||
|
|
||||||
foreach (var commandAllocator in _commandAllocators)
|
foreach (var commandAllocator in _commandAllocators)
|
||||||
{
|
{
|
||||||
commandAllocator.Dispose();
|
commandAllocator.Dispose();
|
||||||
}
|
}
|
||||||
|
_commandAllocators.AsSpan().Clear();
|
||||||
|
|
||||||
foreach (var renderTarget in _renderTargets)
|
foreach (var renderTarget in _renderTargets)
|
||||||
{
|
{
|
||||||
renderTarget.Dispose();
|
renderTarget.Dispose();
|
||||||
}
|
}
|
||||||
|
_renderTargets.AsSpan().Clear();
|
||||||
|
|
||||||
_swapChain.Dispose();
|
_swapChain.Dispose();
|
||||||
_commandList.Dispose();
|
_commandList.Dispose();
|
||||||
@@ -215,5 +273,7 @@ internal class DX12RenderView : IRenderView
|
|||||||
|
|
||||||
_backBufferIndex = 0;
|
_backBufferIndex = 0;
|
||||||
_fenceValues.AsSpan().Clear();
|
_fenceValues.AsSpan().Clear();
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ internal class D3D12DescriptorAllocator : IDisposable
|
|||||||
private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u;
|
private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u;
|
||||||
|
|
||||||
private readonly ID3D12Device _device;
|
private readonly ID3D12Device _device;
|
||||||
private readonly Lock _mutex = new();
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
private ID3D12DescriptorHeap? _heap;
|
private ID3D12DescriptorHeap? _heap;
|
||||||
private ID3D12DescriptorHeap? _shaderVisibleHeap;
|
private ID3D12DescriptorHeap? _shaderVisibleHeap;
|
||||||
@@ -56,14 +56,15 @@ internal class D3D12DescriptorAllocator : IDisposable
|
|||||||
ShaderVisible = type == DescriptorHeapType.ConstantBufferViewShaderResourceViewUnorderedAccessView || type == DescriptorHeapType.Sampler;
|
ShaderVisible = type == DescriptorHeapType.ConstantBufferViewShaderResourceViewUnorderedAccessView || type == DescriptorHeapType.Sampler;
|
||||||
Stride = device.GetDescriptorHandleIncrementSize(type);
|
Stride = device.GetDescriptorHandleIncrementSize(type);
|
||||||
|
|
||||||
Debug.Assert(AllocateResources(numDescriptors));
|
var success = AllocateResources(numDescriptors);
|
||||||
|
Debug.Assert(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DescriptorIndex AllocateDescriptor() => AllocateDescriptors(1);
|
public DescriptorIndex AllocateDescriptor() => AllocateDescriptors(1);
|
||||||
|
|
||||||
public DescriptorIndex AllocateDescriptors(uint count)
|
public DescriptorIndex AllocateDescriptors(uint count)
|
||||||
{
|
{
|
||||||
lock (_mutex)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
DescriptorIndex foundIndex = 0;
|
DescriptorIndex foundIndex = 0;
|
||||||
uint freeCount = 0;
|
uint freeCount = 0;
|
||||||
@@ -120,7 +121,7 @@ internal class D3D12DescriptorAllocator : IDisposable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_mutex)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
for (var index = baseIndex; index < baseIndex + count; index++)
|
for (var index = baseIndex; index < baseIndex + count; index++)
|
||||||
{
|
{
|
||||||
|
|||||||
679
Ghost.Graphics/DX12/Utilities/D3D12ResourceUploadBatch.cs
Normal file
679
Ghost.Graphics/DX12/Utilities/D3D12ResourceUploadBatch.cs
Normal file
File diff suppressed because one or more lines are too long
234
Ghost.Graphics/DX12/Utilities/D3D12ResourceUtils.cs
Normal file
234
Ghost.Graphics/DX12/Utilities/D3D12ResourceUtils.cs
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
using Ghost.Graphics.Utilities;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Vortice.Direct3D12;
|
||||||
|
using Vortice.DXGI;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.DX12.Utilities;
|
||||||
|
|
||||||
|
internal unsafe class D3D12ResourceUtils
|
||||||
|
{
|
||||||
|
private const ResourceStates _INITIALCOPYTARGETSTATE = ResourceStates.Common;
|
||||||
|
private const ResourceStates _INITIALREADTARGETSTATE = ResourceStates.Common;
|
||||||
|
private const ResourceStates _INITIALUAVTARGETSTATE = ResourceStates.Common;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
|
||||||
|
|
||||||
|
if (sizeInBytes > c_maxBytes)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
|
||||||
|
|
||||||
|
if (sizeInBytes > c_maxBytes)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
|
||||||
|
if (sizeInBytes > c_maxBytes)
|
||||||
|
{
|
||||||
|
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 CreateCPUDestinationBuffer(
|
||||||
|
ID3D12Device device,
|
||||||
|
uint sizeInBytes,
|
||||||
|
ResourceFlags flags = ResourceFlags.None)
|
||||||
|
{
|
||||||
|
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
|
||||||
|
if (sizeInBytes > c_maxBytes)
|
||||||
|
{
|
||||||
|
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),
|
||||||
|
ResourceStates.CopyDest
|
||||||
|
);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ID3D12Resource CreateUAVBuffer(ID3D12Device device, uint bufferSize,
|
||||||
|
ResourceStates initialState = ResourceStates.Common,
|
||||||
|
ResourceFlags flags = ResourceFlags.None)
|
||||||
|
{
|
||||||
|
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
|
||||||
|
|
||||||
|
if (bufferSize > c_maxBytes)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +1,31 @@
|
|||||||
using Misaki.HighPerformance.Unsafe.Collections;
|
using Ghost.Graphics.DX12.Utilities;
|
||||||
|
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 Vortice.Direct3D12;
|
||||||
|
using Vortice.DXGI;
|
||||||
|
using Vortice.Mathematics;
|
||||||
|
|
||||||
namespace Ghost.Graphics.Data;
|
namespace Ghost.Graphics.Data;
|
||||||
|
|
||||||
public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapacity = 512) : IDisposable
|
public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapacity = 512) : IDisposable
|
||||||
{
|
{
|
||||||
private UnsafeList<Vector3> _vertices = new(initialVertexCapacity, Allocator.Persistent);
|
private UnsafeList<Vertex> _vertices = new(initialVertexCapacity, Allocator.Persistent);
|
||||||
private UnsafeList<Vector3> _normals = new(initialVertexCapacity, Allocator.Persistent);
|
|
||||||
private UnsafeList<Vector4> _tangents = new(initialVertexCapacity, Allocator.Persistent);
|
|
||||||
private UnsafeList<Color32> _colors = new(initialVertexCapacity, Allocator.Persistent);
|
|
||||||
private UnsafeList<Vector2> _uvs = new(initialVertexCapacity, Allocator.Persistent);
|
|
||||||
private UnsafeList<int> _indices = new(initialIndexCapacity, Allocator.Persistent);
|
private UnsafeList<int> _indices = new(initialIndexCapacity, Allocator.Persistent);
|
||||||
|
|
||||||
public Span<Vector3> Vertices => _vertices.AsSpan();
|
private BoundingBox _bounds;
|
||||||
public Span<Vector3> Normals => _normals.AsSpan();
|
|
||||||
public Span<Vector4> Tangents => _tangents.AsSpan();
|
|
||||||
public Span<Color32> Colors => _colors.AsSpan();
|
|
||||||
public Span<Vector2> UVs => _uvs.AsSpan();
|
|
||||||
public Span<int> Indices => _indices.AsSpan();
|
|
||||||
|
|
||||||
|
private ID3D12Resource? _vertexBuffer;
|
||||||
|
private ID3D12Resource? _indexBuffer;
|
||||||
|
private VertexBufferView _vertexBufferView;
|
||||||
|
private IndexBufferView _indexBufferView;
|
||||||
|
|
||||||
|
public Span<Vertex> Vertices => _vertices.AsSpan();
|
||||||
|
public Span<int> Indices => _indices.AsSpan();
|
||||||
|
public BoundingBox Bounds => _bounds;
|
||||||
public int VertexCount => _vertices.Count;
|
public int VertexCount => _vertices.Count;
|
||||||
|
public int IndexCount => _indices.Count;
|
||||||
|
|
||||||
~Mesh()
|
~Mesh()
|
||||||
{
|
{
|
||||||
@@ -30,21 +35,11 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a vertex to the mesh with the specified attributes.
|
/// Adds a vertex to the mesh with the specified attributes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>This method adds the vertex attributes to their respective collections, allowing the mesh
|
/// <param name="vertex">The data to add</param>
|
||||||
/// to be constructed with detailed vertex data. Ensure that all parameters are provided with valid values to
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
/// avoid incomplete or incorrect mesh data.</remarks>
|
public void AddVertex(Vertex vertex)
|
||||||
/// <param name="position">The position of the vertex in 3D space.</param>
|
|
||||||
/// <param name="normal">The normal vector at the vertex.</param>
|
|
||||||
/// <param name="tangent">The tangent vector at the vertex.</param>
|
|
||||||
/// <param name="color">The color of the vertex.</param>
|
|
||||||
/// <param name="uv">The UV coordinates of the vertex.</param>
|
|
||||||
public void AddVertex(Vector3 position, Vector3 normal, Vector4 tangent, Color32 color, Vector2 uv)
|
|
||||||
{
|
{
|
||||||
_vertices.Add(position);
|
_vertices.Add(vertex);
|
||||||
_normals.Add(normal);
|
|
||||||
_tangents.Add(tangent);
|
|
||||||
_colors.Add(color);
|
|
||||||
_uvs.Add(uv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -66,13 +61,12 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
|||||||
_indices.Add(index2);
|
_indices.Add(index2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reduces the memory usage of the internal collections by resizing them to match their current element count.
|
||||||
|
/// </summary>
|
||||||
public void TrimExcess()
|
public void TrimExcess()
|
||||||
{
|
{
|
||||||
_vertices.Resize(_vertices.Count);
|
_vertices.Resize(_vertices.Count);
|
||||||
_normals.Resize(_normals.Count);
|
|
||||||
_tangents.Resize(_tangents.Count);
|
|
||||||
_colors.Resize(_colors.Count);
|
|
||||||
_uvs.Resize(_uvs.Count);
|
|
||||||
_indices.Resize(_indices.Count);
|
_indices.Resize(_indices.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,16 +84,6 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_normals.IsCreated)
|
|
||||||
{
|
|
||||||
_normals = new(_vertices.Count, Allocator.Persistent);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_normals.Clear();
|
|
||||||
_normals.Resize(_vertices.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < _indices.Count; i += 3)
|
for (var i = 0; i < _indices.Count; i += 3)
|
||||||
{
|
{
|
||||||
var i0 = _indices[i];
|
var i0 = _indices[i];
|
||||||
@@ -110,18 +94,18 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
|||||||
var v1 = _vertices[i1];
|
var v1 = _vertices[i1];
|
||||||
var v2 = _vertices[i2];
|
var v2 = _vertices[i2];
|
||||||
|
|
||||||
var edge1 = v1 - v0;
|
var edge1 = v1.Position - v0.Position;
|
||||||
var edge2 = v2 - v0;
|
var edge2 = v2.Position - v0.Position;
|
||||||
var faceNormal = Vector3.Cross(edge1, edge2);
|
var faceNormal = Vector3.Cross(edge1.AsVector3(), edge2.AsVector3());
|
||||||
|
|
||||||
_normals[i0] += faceNormal;
|
_vertices[i0].Normal += faceNormal.AsVector4();
|
||||||
_normals[i1] += faceNormal;
|
_vertices[i1].Normal += faceNormal.AsVector4();
|
||||||
_normals[i2] += faceNormal;
|
_vertices[i2].Normal += faceNormal.AsVector4();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < _normals.Count; i++)
|
for (var i = 0; i < _vertices.Count; i++)
|
||||||
{
|
{
|
||||||
_normals[i] = Vector3.Normalize(_normals[i]);
|
_vertices[i].Normal = Vector4.Normalize(_vertices[i].Normal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,29 +117,7 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public void ComputeTangents()
|
public void ComputeTangents()
|
||||||
{
|
{
|
||||||
if (!_vertices.IsCreated || _vertices.Count < 3
|
var bitangents = new Vector4[_vertices.Count];
|
||||||
|| !_indices.IsCreated || _indices.Count < 3
|
|
||||||
|| !_uvs.IsCreated || _uvs.Count < _vertices.Count)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_normals.IsCreated || _normals.Count != _vertices.Count)
|
|
||||||
{
|
|
||||||
ComputeNormal();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_tangents.IsCreated)
|
|
||||||
{
|
|
||||||
_tangents = new(_vertices.Count, Allocator.Persistent);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_tangents.Clear();
|
|
||||||
_tangents.Resize(_vertices.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
var bitangents = new Vector3[_vertices.Count];
|
|
||||||
|
|
||||||
for (var i = 0; i < _indices.Count; i += 3)
|
for (var i = 0; i < _indices.Count; i += 3)
|
||||||
{
|
{
|
||||||
@@ -166,12 +128,13 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
|||||||
var v0 = _vertices[i0];
|
var v0 = _vertices[i0];
|
||||||
var v1 = _vertices[i1];
|
var v1 = _vertices[i1];
|
||||||
var v2 = _vertices[i2];
|
var v2 = _vertices[i2];
|
||||||
var uv0 = _uvs[i0];
|
|
||||||
var uv1 = _uvs[i1];
|
|
||||||
var uv2 = _uvs[i2];
|
|
||||||
|
|
||||||
var deltaPos1 = v1 - v0;
|
var uv0 = _vertices[i0].UV;
|
||||||
var deltaPos2 = v2 - v0;
|
var uv1 = _vertices[i1].UV;
|
||||||
|
var uv2 = _vertices[i2].UV;
|
||||||
|
|
||||||
|
var deltaPos1 = v1.Position - v0.Position;
|
||||||
|
var deltaPos2 = v2.Position - v0.Position;
|
||||||
var deltaUV1 = uv1 - uv0;
|
var deltaUV1 = uv1 - uv0;
|
||||||
var deltaUV2 = uv2 - uv0;
|
var deltaUV2 = uv2 - uv0;
|
||||||
|
|
||||||
@@ -182,8 +145,8 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
|||||||
for (var j = 0; j < 3; j++)
|
for (var j = 0; j < 3; j++)
|
||||||
{
|
{
|
||||||
var idx = _indices[i + j];
|
var idx = _indices[i + j];
|
||||||
var t = _tangents[idx];
|
var t = _vertices[idx].Tangent;
|
||||||
_tangents[idx] = new Vector4(
|
_vertices[idx].Tangent = new Vector4(
|
||||||
t.X + tangent.X,
|
t.X + tangent.X,
|
||||||
t.Y + tangent.Y,
|
t.Y + tangent.Y,
|
||||||
t.Z + tangent.Z,
|
t.Z + tangent.Z,
|
||||||
@@ -196,38 +159,119 @@ public sealed class Mesh(int initialVertexCapacity = 256, int initialIndexCapaci
|
|||||||
|
|
||||||
for (var i = 0; i < _vertices.Count; i++)
|
for (var i = 0; i < _vertices.Count; i++)
|
||||||
{
|
{
|
||||||
var n = _normals![i];
|
var n = _vertices[i].Normal;
|
||||||
var t = _tangents[i];
|
var t = _vertices[i].Tangent;
|
||||||
var t3 = new Vector3(t.X, t.Y, t.Z);
|
var n3 = n.AsVector3();
|
||||||
|
var t3 = t.AsVector3();
|
||||||
|
|
||||||
var proj = n * Vector3.Dot(n, t3);
|
var proj = n3 * Vector3.Dot(n3, t3);
|
||||||
t3 = Vector3.Normalize(t3 - proj);
|
t3 = Vector3.Normalize(t3 - proj);
|
||||||
|
|
||||||
var b = bitangents[i];
|
var b = bitangents[i];
|
||||||
var w = Vector3.Dot(Vector3.Cross(n, t3), b) < 0.0f ? -1.0f : 1.0f;
|
var w = Vector3.Dot(Vector3.Cross(n3, t3), b.AsVector3()) < 0.0f ? -1.0f : 1.0f;
|
||||||
|
|
||||||
_tangents[i] = new Vector4(t3.X, t3.Y, t3.Z, w);
|
_vertices[i].Tangent = new Vector4(t3.X, t3.Y, t3.Z, w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the bounding box of the mesh based on its vertices.
|
||||||
|
/// </summary>
|
||||||
|
public void ComputeBounds()
|
||||||
|
{
|
||||||
|
if (_vertices.Count == 0)
|
||||||
|
{
|
||||||
|
_bounds = BoundingBox.Zero;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var min = new Vector3(float.MaxValue);
|
||||||
|
var max = new Vector3(float.MinValue);
|
||||||
|
foreach (var vertex in _vertices)
|
||||||
|
{
|
||||||
|
var pos = vertex.Position.AsVector3();
|
||||||
|
min = Vector3.Min(min, pos);
|
||||||
|
max = Vector3.Max(max, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
_bounds = new BoundingBox(min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uploads the mesh data to GPU resources immediately.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="device">The Direct3D 12 device.</param>
|
||||||
|
/// <param name="commandList">The Direct3D 12 command list to record the upload commands.</param>
|
||||||
|
public unsafe void UploadMeshData(ID3D12Device device, ID3D12GraphicsCommandList commandList)
|
||||||
|
{
|
||||||
|
if (VertexCount == 0 || IndexCount == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_vertexBuffer?.Dispose();
|
||||||
|
_indexBuffer?.Dispose();
|
||||||
|
|
||||||
|
var vertexBufferSize = (uint)(VertexCount * sizeof(Vertex));
|
||||||
|
var indexBufferSize = (uint)(IndexCount * sizeof(int));
|
||||||
|
_vertexBuffer = D3D12ResourceUtils.CreateCPUDestinationBuffer(device, vertexBufferSize);
|
||||||
|
_indexBuffer = D3D12ResourceUtils.CreateCPUDestinationBuffer(device, indexBufferSize);
|
||||||
|
|
||||||
|
using var vertexUploadBuffer = D3D12ResourceUtils.CreateUploadBuffer(device, vertexBufferSize);
|
||||||
|
using var indexUploadBuffer = D3D12ResourceUtils.CreateUploadBuffer(device, indexBufferSize);
|
||||||
|
|
||||||
|
void* vertexData;
|
||||||
|
vertexUploadBuffer.Map(0, null, &vertexData);
|
||||||
|
Unsafe.CopyBlock(vertexData, _vertices.GetUnsafePtr(), vertexBufferSize);
|
||||||
|
vertexUploadBuffer.Unmap(0);
|
||||||
|
|
||||||
|
void* indexData;
|
||||||
|
indexUploadBuffer.Map(0, null, &indexData);
|
||||||
|
Unsafe.CopyBlock(indexData, _indices.GetUnsafePtr(), indexBufferSize);
|
||||||
|
indexUploadBuffer.Unmap(0);
|
||||||
|
|
||||||
|
commandList.CopyBufferRegion(_vertexBuffer, 0, vertexUploadBuffer, 0, vertexBufferSize);
|
||||||
|
commandList.CopyBufferRegion(_indexBuffer, 0, indexUploadBuffer, 0, indexBufferSize);
|
||||||
|
|
||||||
|
_vertexBufferView = new VertexBufferView
|
||||||
|
{
|
||||||
|
BufferLocation = _vertexBuffer.GPUVirtualAddress,
|
||||||
|
SizeInBytes = vertexBufferSize,
|
||||||
|
StrideInBytes = (uint)sizeof(Vertex)
|
||||||
|
};
|
||||||
|
|
||||||
|
_indexBufferView = new IndexBufferView
|
||||||
|
{
|
||||||
|
BufferLocation = _indexBuffer.GPUVirtualAddress,
|
||||||
|
SizeInBytes = indexBufferSize,
|
||||||
|
Format = Format.R32_UInt
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisposeGpuResources()
|
||||||
|
{
|
||||||
|
_vertexBuffer?.Release();
|
||||||
|
_vertexBuffer = null;
|
||||||
|
|
||||||
|
_indexBuffer?.Release();
|
||||||
|
_indexBuffer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all vertex and index data and releases associated GPU resources.
|
||||||
|
/// </summar>
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
_vertices.Clear();
|
_vertices.Clear();
|
||||||
_normals.Clear();
|
|
||||||
_tangents.Clear();
|
|
||||||
_colors.Clear();
|
|
||||||
_uvs.Clear();
|
|
||||||
_indices.Clear();
|
_indices.Clear();
|
||||||
|
DisposeGpuResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_vertices.Dispose();
|
_vertices.Dispose();
|
||||||
_normals.Dispose();
|
|
||||||
_tangents.Dispose();
|
|
||||||
_colors.Dispose();
|
|
||||||
_uvs.Dispose();
|
|
||||||
_indices.Dispose();
|
_indices.Dispose();
|
||||||
|
DisposeGpuResources();
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|||||||
25
Ghost.Graphics/Data/ResourceView.cs
Normal file
25
Ghost.Graphics/Data/ResourceView.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
namespace Ghost.Graphics.Data;
|
||||||
|
|
||||||
|
internal abstract unsafe class ResourceView
|
||||||
|
{
|
||||||
|
public GraphicsResource Resource
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void* CpuDescriptorHandle
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ResourceView(GraphicsResource resource, void* descriptor)
|
||||||
|
{
|
||||||
|
Resource = resource;
|
||||||
|
CpuDescriptorHandle = descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ResourceView(GraphicsResource resource, IntPtr descriptor) :
|
||||||
|
this(resource, (void*)descriptor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Ghost.Graphics.Data;
|
namespace Ghost.Graphics.Data;
|
||||||
|
|
||||||
internal readonly struct SwapChainSurface
|
internal readonly struct SwapChainPresenter
|
||||||
{
|
{
|
||||||
public enum TargetType
|
public enum TargetType
|
||||||
{
|
{
|
||||||
@@ -33,7 +33,7 @@ internal readonly struct SwapChainSurface
|
|||||||
get;
|
get;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwapChainSurface(Vortice.WinUI.ISwapChainPanelNative swapChainPanelNative, uint width, uint height)
|
public SwapChainPresenter(Vortice.WinUI.ISwapChainPanelNative swapChainPanelNative, uint width, uint height)
|
||||||
{
|
{
|
||||||
Type = TargetType.Composition;
|
Type = TargetType.Composition;
|
||||||
SwapChainPanelNative = swapChainPanelNative;
|
SwapChainPanelNative = swapChainPanelNative;
|
||||||
@@ -42,7 +42,7 @@ internal readonly struct SwapChainSurface
|
|||||||
Height = height;
|
Height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwapChainSurface(IntPtr hwnd, uint width, uint height)
|
public SwapChainPresenter(IntPtr hwnd, uint width, uint height)
|
||||||
{
|
{
|
||||||
Type = TargetType.Hwnd;
|
Type = TargetType.Hwnd;
|
||||||
SwapChainPanelNative = null;
|
SwapChainPanelNative = null;
|
||||||
36
Ghost.Graphics/Data/Vertex.cs
Normal file
36
Ghost.Graphics/Data/Vertex.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Ghost.Graphics.Data;
|
||||||
|
|
||||||
|
public struct Vertex(Vector4 position, Vector4 normal, Vector4 tangent, Color32 color, Vector4 uv)
|
||||||
|
{
|
||||||
|
public Vector4 Position
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
} = position;
|
||||||
|
|
||||||
|
public Vector4 Normal
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
} = normal;
|
||||||
|
|
||||||
|
public Vector4 Tangent
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
} = tangent;
|
||||||
|
|
||||||
|
public Color32 Color
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
} = color;
|
||||||
|
|
||||||
|
public Vector4 UV
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
} = uv;
|
||||||
|
}
|
||||||
@@ -3,9 +3,9 @@ using Ghost.Graphics.Data;
|
|||||||
|
|
||||||
namespace Ghost.Graphics;
|
namespace Ghost.Graphics;
|
||||||
|
|
||||||
internal static class GraphicsPipeline
|
public static class GraphicsPipeline
|
||||||
{
|
{
|
||||||
public const int FRAME_COUNT = 2;
|
internal const int FRAME_COUNT = 2;
|
||||||
|
|
||||||
private static IGraphicsDevice? _graphicsDevice;
|
private static IGraphicsDevice? _graphicsDevice;
|
||||||
private static Thread? _renderThread;
|
private static Thread? _renderThread;
|
||||||
@@ -24,7 +24,7 @@ internal static class GraphicsPipeline
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Initialize(GraphicsAPI api)
|
internal static void Initialize(GraphicsAPI api)
|
||||||
{
|
{
|
||||||
_graphicsDevice = api switch
|
_graphicsDevice = api switch
|
||||||
{
|
{
|
||||||
@@ -43,7 +43,7 @@ internal static class GraphicsPipeline
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Start()
|
internal static void Start()
|
||||||
{
|
{
|
||||||
if (_isRunning)
|
if (_isRunning)
|
||||||
{
|
{
|
||||||
@@ -59,13 +59,13 @@ internal static class GraphicsPipeline
|
|||||||
_renderThread.Start();
|
_renderThread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Stop()
|
internal static void Stop()
|
||||||
{
|
{
|
||||||
_isRunning = false;
|
_isRunning = false;
|
||||||
_renderThread?.Join();
|
_renderThread?.Join();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Shutdown()
|
internal static void Shutdown()
|
||||||
{
|
{
|
||||||
Stop();
|
Stop();
|
||||||
|
|
||||||
|
|||||||
8
Ghost.Graphics/Properties/launchSettings.json
Normal file
8
Ghost.Graphics/Properties/launchSettings.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"Ghost.Graphics": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"nativeDebugging": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,7 +36,15 @@ public static class MeshBuilder
|
|||||||
var baseIndex = mesh.VertexCount;
|
var baseIndex = mesh.VertexCount;
|
||||||
for (var i = 0; i < 4; i++)
|
for (var i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
mesh.AddVertex(corners[face[i]], Vector3.Zero, Vector4.Zero, color, uvs[i]);
|
var vertex = new Vertex
|
||||||
|
{
|
||||||
|
Position = corners[face[i]].AsVector4(),
|
||||||
|
Normal = Vector4.Zero,
|
||||||
|
Tangent = Vector4.Zero,
|
||||||
|
Color = color,
|
||||||
|
UV = 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(baseIndex + 0, baseIndex + 1, baseIndex + 2);
|
||||||
@@ -57,10 +65,10 @@ public static class MeshBuilder
|
|||||||
var hd = depth * 0.5f;
|
var hd = depth * 0.5f;
|
||||||
var mesh = new Mesh(4, 6);
|
var mesh = new Mesh(4, 6);
|
||||||
|
|
||||||
mesh.AddVertex(new(-hw, 0, -hd), Vector3.Zero, Vector4.Zero, color, new(0, 0));
|
mesh.AddVertex(new(new(-hw, 0.0f, -hd, 0.0f), Vector4.Zero, Vector4.Zero, color, new(0.0f)));
|
||||||
mesh.AddVertex(new(hw, 0, -hd), Vector3.Zero, Vector4.Zero, color, new(1, 0));
|
mesh.AddVertex(new(new(hw, 0.0f, -hd, 0.0f), Vector4.Zero, Vector4.Zero, color, new(1.0f, 0.0f, 0.0f, 0.0f)));
|
||||||
mesh.AddVertex(new(hw, 0, hd), Vector3.Zero, Vector4.Zero, color, new(1, 1));
|
mesh.AddVertex(new(new(hw, 0.0f, hd, 0.0f), Vector4.Zero, Vector4.Zero, color, new(1.0f, 1.0f, 0.0f, 0.0f)));
|
||||||
mesh.AddVertex(new(-hw, 0, hd), Vector3.Zero, Vector4.Zero, color, new(0, 1));
|
mesh.AddVertex(new(new(-hw, 0.0f, hd, 0.0f), Vector4.Zero, Vector4.Zero, color, new(0.0f, 1.0f, 0.0f, 0.0f)));
|
||||||
|
|
||||||
mesh.AddTriangle(0, 1, 2);
|
mesh.AddTriangle(0, 1, 2);
|
||||||
mesh.AddTriangle(0, 2, 3);
|
mesh.AddTriangle(0, 2, 3);
|
||||||
@@ -95,12 +103,14 @@ public static class MeshBuilder
|
|||||||
var z = sinPhi * sinTheta;
|
var z = sinPhi * sinTheta;
|
||||||
|
|
||||||
mesh.AddVertex(
|
mesh.AddVertex(
|
||||||
position: new Vector3(x, y, z) * radius,
|
new()
|
||||||
normal: Vector3.Zero,
|
{
|
||||||
tangent: Vector4.Zero,
|
Position = new Vector4(x, y, z, 0.0f) * radius,
|
||||||
color: color,
|
Normal = Vector4.Zero,
|
||||||
uv: new Vector2((float)lon / longitudeSegments, (float)lat / latitudeSegments)
|
Tangent = Vector4.Zero,
|
||||||
);
|
Color = color,
|
||||||
|
UV = new Vector4((float)lon / longitudeSegments, (float)lat / latitudeSegments, 0.0f, 0.0f)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
21
Ghost.Graphics/Utilities/TextureUtility.cs
Normal file
21
Ghost.Graphics/Utilities/TextureUtility.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
namespace Ghost.Graphics.Utilities;
|
||||||
|
public class TextureUtility
|
||||||
|
{
|
||||||
|
public static uint CountMips(uint width, uint height)
|
||||||
|
{
|
||||||
|
if (width == 0 || height == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint count = 1;
|
||||||
|
while (width > 1 || height > 1)
|
||||||
|
{
|
||||||
|
width >>= 1;
|
||||||
|
height >>= 1;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user