Files
2024-12-22 19:44:29 +01:00

328 lines
12 KiB
C#

using System;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
using SharpDX;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using T3.Core.IO;
using T3.Core.Logging;
using T3.Core.Resource;
using T3.Editor.Gui;
using T3.Editor.Gui.Interaction.StartupCheck;
using T3.Editor.Gui.UiHelpers;
using T3.Editor.SystemUi;
using T3.SystemUi;
using Device = SharpDX.Direct3D11.Device;
namespace T3.Editor.App;
internal static class ProgramWindows
{
public static AppWindow Main { get; private set; }
public static AppWindow Viewer { get; private set; } // Required it distinguish 2nd render view in mouse handling
private static Device _device;
private static DeviceContext _deviceContext;
private static Factory _factory;
internal static void SetMainWindowSize(int width, int height)
{
Main.SetSize(width, height);
Main.SetBorderStyleSizable();
}
internal static void SetInteractionDevices(params object[] objects)
{
IWindowsFormsMessageHandler[] messageHandlers = objects.OfType<IWindowsFormsMessageHandler>().ToArray();
ImGuiDx11RenderForm.InputMethods = messageHandlers;
}
public static void SetVertexShader(ShaderResource<VertexShader> resource) => _deviceContext.VertexShader.Set(resource.Shader);
public static void SetPixelShader(ShaderResource<PixelShader> resource) => _deviceContext.PixelShader.Set(resource.Shader);
internal static void HandleFullscreenToggle()
{
if (Main.IsFullScreen == UserSettings.Config.FullScreen)
return;
if (UserSettings.Config.FullScreen)
{
var screenCount = Screen.AllScreens.Length;
Main.SetFullScreen(UserSettings.Config.FullScreenIndexMain < screenCount ? UserSettings.Config.FullScreenIndexMain : 0);
Viewer.SetFullScreen(UserSettings.Config.FullScreenIndexViewer < screenCount ? UserSettings.Config.FullScreenIndexViewer : 0);
}
else
{
Main.SetSizeable();
Viewer.SetSizeable();
}
}
internal static void InitializeMainWindow(string version, out Device device)
{
Main = new("T3 " + version, disableClose: false);
device = null;
try
{
// Create Device and SwapChain
Device.CreateWithSwapChain(DriverType.Hardware,
DeviceCreationFlags.Debug,
Main.SwapChainDescription,
out device,
out var swapchain);
_device = device;
_deviceContext = device.ImmediateContext;
_factory = swapchain.GetParent<Factory>();
foreach (var a in _factory.Adapters)
{
Log.Debug($"using {a.Description.Description}");
break;
}
Main.SetDevice(device, _deviceContext, swapchain);
Main.InitializeWindow(FormWindowState.Maximized, HandleKeyDown, HandleKeyUp, OnCloseMainWindow);
// Ignore all windows events
_factory.MakeWindowAssociation(Main.HwndHandle, WindowAssociationFlags.IgnoreAll);
}
catch (Exception e)
{
if (e.Message.Contains("DXGI_ERROR_SDK_COMPONENT_MISSING"))
{
var result =
EditorUi.Instance
.ShowMessageBox("You need to install Windows Graphics diagnostics tools.\n\nClick Ok to download this Windows component directly from Microsoft.",
"Windows component missing", PopUpButtons.OkCancel);
if (result == PopUpResult.Ok)
{
StartupValidation
.OpenUrl("https://learn.microsoft.com/en-us/windows/uwp/gaming/use-the-directx-runtime-and-visual-studio-graphics-diagnostic-features");
}
}
else
{
EditorUi.Instance.ShowMessageBox("We are sorry but your graphics hardware might not be capable of running Tooll3\n\n" + e.Message, "Oh noooo",
PopUpButtons.Ok);
}
Environment.Exit(0);
}
}
internal static void InitializeSecondaryViewerWindow(string name, int width, int height)
{
Viewer = new(name, disableClose: true);
Viewer.SetDevice(_device, _deviceContext);
Viewer.SetSize(width, height);
Viewer.SetSizeable();
Viewer.InitViewSwapChain(_factory);
Viewer.InitializeWindow(FormWindowState.Normal, null, null, null);
Viewer.Show();
}
private static void OnCloseMainWindow(object sender, CancelEventArgs args)
{
if (T3Ui.UiSymbolData.IsSaving)
{
args.Cancel = true;
Log.Debug($"Cancel closing because save-operation is in progress.");
}
else
{
Log.Debug("Shutting down");
}
}
private static void HandleKeyDown(object sender, KeyEventArgs e)
{
var keyIndex = (int)e.KeyCode;
if (keyIndex >= KeyHandler.PressedKeys.Length)
{
Log.Warning($"Ignoring out of range key code {e.KeyCode} with index {keyIndex}");
}
else
{
KeyHandler.PressedKeys[keyIndex] = true;
}
}
private static void HandleKeyUp(object sender, KeyEventArgs e)
{
var keyIndex = (int)e.KeyCode;
if (keyIndex < KeyHandler.PressedKeys.Length)
{
KeyHandler.PressedKeys[keyIndex] = false;
}
}
public static void Release()
{
Main.Release();
Viewer.Release();
_device.ImmediateContext.ClearState();
_deviceContext.Flush();
_device.Dispose();
_deviceContext.Dispose();
_factory.Dispose();
}
public static void SetRasterizerState(RasterizerState viewWindowRasterizerState)
{
_deviceContext.Rasterizer.State = viewWindowRasterizerState;
}
public static void SetPixelShaderSRV(ShaderResourceView viewWindowBackgroundSrv)
{
_deviceContext.PixelShader.SetShaderResource(0, viewWindowBackgroundSrv);
}
public static void DrawTextureToSecondaryRenderOutput()
{
_deviceContext.Draw(3, 0);
_deviceContext.PixelShader.SetShaderResource(0, null);
}
public static void RefreshViewport()
{
_deviceContext.Rasterizer.SetViewport(new Viewport(0, 0, Main.Width, Main.Height, 0.0f, 1.0f));
_deviceContext.OutputMerger.SetTargets(Main.RenderTargetView);
}
public static void Present(bool useVSync, bool showSecondaryRenderWindow)
{
try
{
Main.SwapChain.Present(useVSync ? 1 : 0, PresentFlags.None);
if (showSecondaryRenderWindow)
Viewer.SwapChain.Present(useVSync ? 1 : 0, PresentFlags.None);
}
catch (SharpDX.SharpDXException e)
{
string description;
var result = _device.DeviceRemovedReason;
if (result == Result.Abort)
{
description = Result.Abort.Description;
}
else if (result == Result.AccessDenied)
{
description = Result.AccessDenied.Description;
}
else if (result == Result.Fail)
{
description = Result.Fail.Description;
}
else if (result == Result.Handle)
{
description = Result.Handle.Description;
}
else if (result == Result.InvalidArg)
{
description = Result.InvalidArg.Description;
}
else if (result == Result.NoInterface)
{
description = Result.NoInterface.Description;
}
else if (result == Result.NotImplemented)
{
description = Result.NotImplemented.Description;
}
else if (result == Result.OutOfMemory)
{
description = Result.OutOfMemory.Description;
}
else if (result == Result.InvalidPointer)
{
description = Result.InvalidPointer.Description;
}
else if (result == Result.UnexpectedFailure)
{
description = Result.UnexpectedFailure.Description;
}
else if (result == Result.WaitAbandoned)
{
description = Result.WaitAbandoned.Description;
}
else if (result == Result.WaitTimeout)
{
description = Result.WaitTimeout.Description;
}
else if (result == Result.Pending)
{
description = Result.Pending.Description;
}
else
{
description = "unknown reason";
}
var resultCode = result.ToString();
throw (new ApplicationException($"Graphics card suspended ({resultCode}: {description}): {e.Message}"));
}
}
public static void RebuildUiCopyTextureIfRequired()
{
var needsRebuild = _uiCopyTexture == null ||
_uiCopyTexture.Description.Width != Main.SwapChain.Description.ModeDescription.Width ||
_uiCopyTexture.Description.Height != Main.SwapChain.Description.ModeDescription.Height;
if (!needsRebuild)
return;
// Create a shader resource-compatible texture
var textureDesc = new Texture2DDescription
{
Width = Main.SwapChain.Description.ModeDescription.Width,
Height = Main.SwapChain.Description.ModeDescription.Height,
MipLevels = 1,
ArraySize = 1,
Format = Main.SwapChain.Description.ModeDescription.Format,
SampleDescription = new SampleDescription(1, 0),
Usage = ResourceUsage.Default,
BindFlags = BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None
};
if (_uiCopyTexture is { IsDisposed: false })
_uiCopyTexture.Dispose();
_uiCopyTexture = new Texture2D(_device, textureDesc);
if (UiCopyTextureSrv is { IsDisposed: false })
UiCopyTextureSrv.Dispose();
UiCopyTextureSrv = new ShaderResourceView(_device, _uiCopyTexture);
}
/// <summary>
/// For things like presentations, demos or certain live performance situations it
/// can be desired to share also T3's UI content on a second display.
///
/// On Windows duplicating a display is extremely expensive. This work around
/// copies the last frame into a texture which is then presented on the second display.
/// </summary>
public static void CopyUiContentToShareTexture()
{
if (_uiCopyTexture == null || _uiCopyTexture.IsDisposed)
{
Log.Warning("Can't use undefined uiCopyTexture");
return;
}
_deviceContext.CopyResource(Main.BackBufferTexture, _uiCopyTexture);
}
private static Texture2D _uiCopyTexture;
public static ShaderResourceView UiCopyTextureSrv { get; private set; }
}