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(); //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(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) { 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; } } }