CodeWalker/Rendering/DirectX/DXManager.cs

354 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using Color = SharpDX.Color;
using Device = SharpDX.Direct3D11.Device;
using Buffer = SharpDX.Direct3D11.Buffer;
using DriverType = SharpDX.Direct3D.DriverType;
using System.Threading;
using System.Windows.Forms;
using SharpDX;
using SharpDX.Direct3D;
namespace CodeWalker.Rendering
{
public class DXManager
{
private DXForm dxform;
public Device device { get; private set; }
public DeviceContext context { get; private set; }
public SwapChain swapchain { get; private set; }
public Texture2D backbuffer { get; private set; }
public Texture2D depthbuffer { get; private set; }
public RenderTargetView targetview { get; private set; }
public DepthStencilView depthview { get; private set; }
private volatile bool Running = false;
private volatile bool Rendering = false;
private volatile bool Resizing = false;
private object syncroot = new object(); //for thread safety
public int multisamplecount { get; private set; } = 4; //should be a setting..
public int multisamplequality { get; private set; } = 0; //should be a setting...
public Color clearcolour { get; private set; } = new Color(0.2f, 0.4f, 0.6f, 1.0f); //gross
private System.Drawing.Size beginSize;
private ViewportF Viewport;
private bool autoStartLoop = false;
public bool Init(DXForm form, bool autostart = true)
{
dxform = form;
autoStartLoop = autostart;
try
{
SwapChainDescription scd = new SwapChainDescription()
{
BufferCount = 2,
Flags = SwapChainFlags.None,
IsWindowed = true,
ModeDescription = new ModeDescription(
form.Form.ClientSize.Width,
form.Form.ClientSize.Height,
new Rational(0, 0),
Format.R8G8B8A8_UNorm),
OutputHandle = form.Form.Handle,
SampleDescription = new SampleDescription(multisamplecount, multisamplequality),
SwapEffect = SwapEffect.Discard,
Usage = Usage.RenderTargetOutput
};
FeatureLevel[] levels = new FeatureLevel[] { FeatureLevel.Level_11_0, FeatureLevel.Level_10_1, FeatureLevel.Level_10_0 };
DeviceCreationFlags flags = DeviceCreationFlags.None;
//#if DEBUG
// flags = DeviceCreationFlags.Debug;
//#endif
Device dev = null;
SwapChain sc = null;
Exception exc = null;
bool success = false;
try
{
Device.CreateWithSwapChain(DriverType.Hardware, flags, levels, scd, out dev, out sc);
success = true;
}
catch(Exception ex) { exc = ex; }
if (!success)
{
multisamplecount = 1;
multisamplequality = 0;
scd.SampleDescription = new SampleDescription(1, 0); //try no AA
try
{
Device.CreateWithSwapChain(DriverType.Hardware, flags, levels, scd, out dev, out sc);
success = true;
}
catch (Exception ex) { exc = ex; }
}
if (!success)
{
var msg = "CodeWalker was unable to initialise the graphics device. Please ensure your system meets the minimum requirements and that your graphics drivers and DirectX are up to date.";
if (exc != null)
{
msg += "\n\nException info: " + exc.ToString();
}
throw new Exception(msg);
}
device = dev;
swapchain = sc;
var factory = swapchain.GetParent<Factory>(); //ignore windows events...
factory.MakeWindowAssociation(form.Form.Handle, WindowAssociationFlags.IgnoreAll);
context = device.ImmediateContext;
CreateRenderBuffers();
dxform.Form.Load += Dxform_Load;
dxform.Form.FormClosing += Dxform_FormClosing;
dxform.Form.ClientSizeChanged += Dxform_ClientSizeChanged;
dxform.Form.ResizeBegin += DxForm_ResizeBegin;
dxform.Form.ResizeEnd += DxForm_ResizeEnd;
if (autostart)
{
dxform.InitScene(device);
}
return true;
}
catch (Exception ex)
{
MessageBox.Show("Unable to initialise DirectX11.\n" + ex.Message, "CodeWalker - Error!");
return false;
}
}
private void Cleanup()
{
Running = false;
int count = 0;
while (Rendering && (count < 1000))
{
Thread.Sleep(1); //try to gracefully exit...
count++;
}
dxform.CleanupScene();
//dipose of all objects
if (depthview != null) depthview.Dispose();
if (depthbuffer != null) depthbuffer.Dispose();
if (targetview != null) targetview.Dispose();
if (backbuffer != null) backbuffer.Dispose();
if (swapchain != null) swapchain.Dispose();
if (device != null) device.Dispose();
GC.Collect();
}
private void CreateRenderBuffers()
{
if (targetview != null) targetview.Dispose();
if (backbuffer != null) backbuffer.Dispose();
if (depthview != null) depthview.Dispose();
if (depthbuffer != null) depthbuffer.Dispose();
backbuffer = Texture2D.FromSwapChain<Texture2D>(swapchain, 0);
targetview = new RenderTargetView(device, backbuffer);
depthbuffer = new Texture2D(device, new Texture2DDescription()
{
Format = Format.D32_Float,
ArraySize = 1,
MipLevels = 1,
Width = backbuffer.Description.Width,
Height = backbuffer.Description.Height,
SampleDescription = new SampleDescription(multisamplecount, 0),
Usage = ResourceUsage.Default,
BindFlags = BindFlags.DepthStencil,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None
});
depthview = new DepthStencilView(device, depthbuffer);
Viewport.Width = (float)backbuffer.Description.Width;
Viewport.Height = (float)backbuffer.Description.Height;
Viewport.MinDepth = 0.0f;
Viewport.MaxDepth = 1.0f;
Viewport.X = 0;
Viewport.Y = 0;
}
private void Resize()
{
if (Resizing) return;
Monitor.Enter(syncroot);
int width = dxform.Form.ClientSize.Width;
int height = dxform.Form.ClientSize.Height;
if (targetview != null) targetview.Dispose();
if (backbuffer != null) backbuffer.Dispose();
swapchain.ResizeBuffers(1, width, height, Format.Unknown, SwapChainFlags.AllowModeSwitch);
CreateRenderBuffers();
Monitor.Exit(syncroot);
dxform.BuffersResized(width, height);
}
private void Dxform_Load(object sender, EventArgs e)
{
if (autoStartLoop)
{
StartRenderLoop();
}
}
private void Dxform_FormClosing(object sender, FormClosingEventArgs e)
{
if (!e.Cancel)
{
if (!dxform.ConfirmQuit())
{
e.Cancel = true;
}
}
if (!e.Cancel)
{
Cleanup();
}
}
private void Dxform_ClientSizeChanged(object sender, EventArgs e)
{
Resize();
}
private void DxForm_ResizeBegin(object sender, EventArgs e)
{
beginSize = dxform.Form.ClientSize;
Resizing = true;
}
private void DxForm_ResizeEnd(object sender, EventArgs e)
{
Resizing = false;
if (dxform.Form.ClientSize != beginSize)
{
Resize();
}
}
public void Start()
{
dxform.InitScene(device);
StartRenderLoop();
}
private void StartRenderLoop()
{
Running = true;
new Thread(new ThreadStart(RenderLoop)).Start();
}
private void RenderLoop()
{
while (Running)
{
while (Resizing)
{
swapchain.Present(1, PresentFlags.None); //just flip buffers when resizing; don't draw
}
while (dxform.Form.WindowState == FormWindowState.Minimized)
{
Thread.Sleep(10); //don't hog CPU when minimised
if (dxform.Form.IsDisposed) return; //if closed while minimised
}
Rendering = true;
if(!Monitor.TryEnter(syncroot, 50))
{
Thread.Sleep(10); //don't hog CPU when not able to render...
continue;
}
bool ok = true;
try
{
context.OutputMerger.SetRenderTargets(depthview, targetview);
context.Rasterizer.SetViewport(0, 0, dxform.Form.ClientSize.Width, dxform.Form.ClientSize.Height);
}
catch (Exception ex)
{
MessageBox.Show("Error setting main render target!\n" + ex.ToString());
ok = false;
}
if (ok)
{
if (dxform.Form.IsDisposed)
{
Monitor.Exit(syncroot);
Rendering = false;
return; //the form was closed... stop!!
}
dxform.RenderScene(context);
try
{
swapchain.Present(1, PresentFlags.None);
}
catch (Exception ex)
{
MessageBox.Show("Error presenting swap chain!\n" + ex.ToString());
}
}
Monitor.Exit(syncroot);
Rendering = false;
}
}
public void ClearRenderTarget(DeviceContext ctx)
{
ctx.ClearRenderTargetView(targetview, clearcolour);
ctx.ClearDepthStencilView(depthview, DepthStencilClearFlags.Depth, 0.0f, 0);
}
public void ClearDepth(DeviceContext ctx)
{
ctx.ClearDepthStencilView(depthview, DepthStencilClearFlags.Depth, 0.0f, 0);
}
public void SetDefaultRenderTarget(DeviceContext ctx)
{
ctx.OutputMerger.SetRenderTargets(depthview, targetview);
ctx.Rasterizer.SetViewport(Viewport);
//ctx.Rasterizer.State = RasterizerStateSolid;
}
}
}