2017-09-21 18:33:05 +08:00
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
} ;
2019-12-04 17:14:11 +08:00
FeatureLevel [ ] levels = new FeatureLevel [ ] { FeatureLevel . Level_11_0 , FeatureLevel . Level_10_1 , FeatureLevel . Level_10_0 } ;
2017-09-21 18:33:05 +08:00
DeviceCreationFlags flags = DeviceCreationFlags . None ;
//#if DEBUG
// flags = DeviceCreationFlags.Debug;
//#endif
Device dev = null ;
SwapChain sc = null ;
2018-03-14 09:36:43 +08:00
Exception exc = null ;
2017-09-21 18:33:05 +08:00
bool success = false ;
try
{
Device . CreateWithSwapChain ( DriverType . Hardware , flags , levels , scd , out dev , out sc ) ;
success = true ;
}
2018-03-14 09:36:43 +08:00
catch ( Exception ex ) { exc = ex ; }
2017-09-21 18:33:05 +08:00
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 ;
}
2018-03-14 09:36:43 +08:00
catch ( Exception ex ) { exc = ex ; }
2017-09-21 18:33:05 +08:00
}
if ( ! success )
{
2018-03-14 09:36:43 +08:00
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 ) ;
2017-09-21 18:33:05 +08:00
}
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 )
{
2018-03-14 09:36:43 +08:00
MessageBox . Show ( "Unable to initialise DirectX11.\n" + ex . Message , "CodeWalker - Error!" ) ;
2017-09-21 18:33:05 +08:00
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 )
{
2020-01-14 14:56:11 +08:00
if ( ! e . Cancel )
{
if ( ! dxform . ConfirmQuit ( ) )
{
e . Cancel = true ;
}
}
if ( ! e . Cancel )
{
Cleanup ( ) ;
}
2017-09-21 18:33:05 +08:00
}
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 ) ;
2019-12-01 17:17:14 +08:00
ctx . ClearDepthStencilView ( depthview , DepthStencilClearFlags . Depth , 0.0f , 0 ) ;
2017-09-21 18:33:05 +08:00
}
public void ClearDepth ( DeviceContext ctx )
{
2019-12-01 17:17:14 +08:00
ctx . ClearDepthStencilView ( depthview , DepthStencilClearFlags . Depth , 0.0f , 0 ) ;
2017-09-21 18:33:05 +08:00
}
public void SetDefaultRenderTarget ( DeviceContext ctx )
{
ctx . OutputMerger . SetRenderTargets ( depthview , targetview ) ;
ctx . Rasterizer . SetViewport ( Viewport ) ;
//ctx.Rasterizer.State = RasterizerStateSolid;
}
}
}