2010-06-13 19:50:06 +00:00
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
# include "D3DBase.h"
# include "D3DTexture.h"
# include "D3DShader.h"
# include "D3Dcompiler.h"
# include "VideoConfig.h"
# include "Render.h"
# include "XFStructs.h"
# include <map>
# include <string>
# include <algorithm>
using namespace std ;
namespace D3D
{
ID3D11Device * device = NULL ;
ID3D11DeviceContext * context = NULL ;
IDXGISwapChain * swapchain = NULL ;
D3D_FEATURE_LEVEL featlevel ;
D3DTexture2D * backbuf = NULL ;
HWND hWnd ;
2010-06-14 22:38:47 +00:00
# define NUM_SUPPORTED_FEATURE_LEVELS 3
const D3D_FEATURE_LEVEL supported_feature_levels [ NUM_SUPPORTED_FEATURE_LEVELS ] = {
D3D_FEATURE_LEVEL_11_0 ,
D3D_FEATURE_LEVEL_10_1 ,
D3D_FEATURE_LEVEL_10_0
} ;
2010-06-13 19:50:06 +00:00
unsigned int xres , yres ;
bool bFrameInProgress = false ;
EmuGfxState : : EmuGfxState ( ) : vertexshader ( NULL ) , vsbytecode ( NULL ) , pixelshader ( NULL ) , psbytecode ( NULL )
{
for ( unsigned int k = 0 ; k < 8 ; k + + )
{
float border [ 4 ] = { 0.f , 0.f , 0.f , 0.f } ;
shader_resources [ k ] = NULL ;
samplerdesc [ k ] = CD3D11_SAMPLER_DESC ( D3D11_FILTER_MIN_MAG_MIP_LINEAR , D3D11_TEXTURE_ADDRESS_CLAMP , D3D11_TEXTURE_ADDRESS_CLAMP , D3D11_TEXTURE_ADDRESS_CLAMP , 0.f , 16 , D3D11_COMPARISON_ALWAYS , border , - D3D11_FLOAT32_MAX , D3D11_FLOAT32_MAX ) ;
}
blenddesc . AlphaToCoverageEnable = FALSE ;
blenddesc . IndependentBlendEnable = FALSE ;
blenddesc . RenderTarget [ 0 ] . BlendEnable = FALSE ;
blenddesc . RenderTarget [ 0 ] . RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL ;
blenddesc . RenderTarget [ 0 ] . SrcBlend = D3D11_BLEND_ONE ;
blenddesc . RenderTarget [ 0 ] . DestBlend = D3D11_BLEND_ZERO ;
blenddesc . RenderTarget [ 0 ] . BlendOp = D3D11_BLEND_OP_ADD ;
blenddesc . RenderTarget [ 0 ] . SrcBlendAlpha = D3D11_BLEND_ONE ;
blenddesc . RenderTarget [ 0 ] . DestBlendAlpha = D3D11_BLEND_ZERO ;
blenddesc . RenderTarget [ 0 ] . BlendOpAlpha = D3D11_BLEND_OP_ADD ;
depthdesc . DepthEnable = TRUE ;
depthdesc . DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL ;
depthdesc . DepthFunc = D3D11_COMPARISON_LESS ;
depthdesc . StencilEnable = FALSE ;
depthdesc . StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK ;
depthdesc . StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK ;
// this probably must be changed once multisampling support gets added
2010-06-15 21:19:09 +00:00
rastdesc = CD3D11_RASTERIZER_DESC ( D3D11_FILL_SOLID , D3D11_CULL_NONE , false , 0 , 0.f , 0 , false , true , false , false ) ;
2010-06-13 19:50:06 +00:00
pscbuf = NULL ;
vscbuf = NULL ;
vshaderchanged = false ;
inp_layout = NULL ;
num_inp_elems = 0 ;
pscbufchanged = false ;
vscbufchanged = false ;
}
EmuGfxState : : ~ EmuGfxState ( )
{
for ( unsigned int k = 0 ; k < 8 ; k + + )
SAFE_RELEASE ( shader_resources [ k ] )
SAFE_RELEASE ( vsbytecode ) ;
SAFE_RELEASE ( psbytecode ) ;
SAFE_RELEASE ( vertexshader ) ;
SAFE_RELEASE ( pixelshader ) ;
SAFE_RELEASE ( pscbuf ) ;
SAFE_RELEASE ( vscbuf ) ;
SAFE_RELEASE ( inp_layout ) ;
}
// TODO: No need to store the whole bytecode, signature might be enough (?)
2010-06-16 23:25:19 +00:00
void EmuGfxState : : SetVShader ( ID3D11VertexShader * shader , D3DBlob * bcode )
2010-06-13 19:50:06 +00:00
{
// TODO: vshaderchanged actually just needs to be true if the signature changed
2010-06-16 23:25:19 +00:00
if ( bcode & & vsbytecode ! = bcode ) vshaderchanged = true ;
2010-06-13 19:50:06 +00:00
SAFE_RELEASE ( vsbytecode ) ;
SAFE_RELEASE ( vertexshader ) ;
if ( shader & & bcode )
{
vertexshader = shader ;
shader - > AddRef ( ) ;
vsbytecode = bcode ;
bcode - > AddRef ( ) ;
}
else if ( shader | | bcode )
{
PanicAlert ( " Invalid parameters! \n " ) ;
}
}
void EmuGfxState : : SetPShader ( ID3D11PixelShader * shader )
{
if ( pixelshader ) pixelshader - > Release ( ) ;
pixelshader = shader ;
if ( shader ) shader - > AddRef ( ) ;
}
void EmuGfxState : : SetInputElements ( const D3D11_INPUT_ELEMENT_DESC * elems , UINT num )
{
num_inp_elems = num ;
memcpy ( inp_elems , elems , num * sizeof ( D3D11_INPUT_ELEMENT_DESC ) ) ;
}
void EmuGfxState : : SetShaderResource ( int stage , ID3D11ShaderResourceView * srv )
{
if ( shader_resources [ stage ] ) shader_resources [ stage ] - > Release ( ) ;
shader_resources [ stage ] = srv ;
if ( srv ) srv - > AddRef ( ) ;
}
void EmuGfxState : : SetAlphaBlendEnable ( bool enable )
{
blenddesc . RenderTarget [ 0 ] . BlendEnable = enable ;
}
void EmuGfxState : : SetRenderTargetWriteMask ( UINT8 mask )
{
blenddesc . RenderTarget [ 0 ] . RenderTargetWriteMask = mask ;
}
void EmuGfxState : : ApplyState ( )
{
HRESULT hr ;
// input layout (only needs to be updated if the vertex shader signature changed)
if ( vshaderchanged )
{
if ( inp_layout ) inp_layout - > Release ( ) ;
2010-06-16 23:25:19 +00:00
hr = D3D : : device - > CreateInputLayout ( inp_elems , num_inp_elems , vsbytecode - > Data ( ) , vsbytecode - > Size ( ) , & inp_layout ) ;
2010-06-13 19:50:06 +00:00
if ( FAILED ( hr ) ) PanicAlert ( " Failed to create input layout, %s %d \n " , __FILE__ , __LINE__ ) ;
SetDebugObjectName ( ( ID3D11DeviceChild * ) inp_layout , " an input layout of EmuGfxState " ) ;
vshaderchanged = false ;
}
D3D : : context - > IASetInputLayout ( inp_layout ) ;
// vertex shader
// TODO: divide the global variables of the generated shaders into about 5 constant buffers
// TODO: improve interaction between EmuGfxState and global state management, so that we don't need to set the constant buffers every time
if ( ! vscbuf )
{
unsigned int size = ( ( sizeof ( float ) * 952 ) & ( ~ 0xffff ) ) + 0x10000 ; // must be a multiple of 16
D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC ( size , D3D11_BIND_CONSTANT_BUFFER , D3D11_USAGE_DYNAMIC , D3D11_CPU_ACCESS_WRITE ) ;
2010-06-18 18:40:58 +00:00
hr = device - > CreateBuffer ( & cbdesc , NULL , & vscbuf ) ;
CHECK ( hr = = S_OK , " Create vertex shader constant buffer (size=%u) " , size ) ;
2010-06-13 19:50:06 +00:00
SetDebugObjectName ( ( ID3D11DeviceChild * ) vscbuf , " a vertex shader constant buffer of EmuGfxState " ) ;
}
if ( vscbufchanged )
{
D3D11_MAPPED_SUBRESOURCE map ;
context - > Map ( vscbuf , 0 , D3D11_MAP_WRITE_DISCARD , 0 , & map ) ;
memcpy ( map . pData , vsconstants , sizeof ( vsconstants ) ) ;
context - > Unmap ( vscbuf , 0 ) ;
}
D3D : : context - > VSSetConstantBuffers ( 0 , 1 , & vscbuf ) ;
// pixel shader
if ( ! pscbuf )
{
unsigned int size = ( ( sizeof ( float ) * 116 ) & ( ~ 0xffff ) ) + 0x10000 ; // must be a multiple of 16
D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC ( size , D3D11_BIND_CONSTANT_BUFFER , D3D11_USAGE_DYNAMIC , D3D11_CPU_ACCESS_WRITE ) ;
device - > CreateBuffer ( & cbdesc , NULL , & pscbuf ) ;
2010-06-18 18:40:58 +00:00
CHECK ( hr = = S_OK , " Create pixel shader constant buffer (size=%u) " , size ) ;
2010-06-13 19:50:06 +00:00
SetDebugObjectName ( ( ID3D11DeviceChild * ) pscbuf , " a pixel shader constant buffer of EmuGfxState " ) ;
}
if ( pscbufchanged )
{
D3D11_MAPPED_SUBRESOURCE map ;
context - > Map ( pscbuf , 0 , D3D11_MAP_WRITE_DISCARD , 0 , & map ) ;
memcpy ( map . pData , psconstants , sizeof ( psconstants ) ) ;
context - > Unmap ( pscbuf , 0 ) ;
pscbufchanged = false ;
}
D3D : : context - > PSSetConstantBuffers ( 0 , 1 , & pscbuf ) ;
ID3D11SamplerState * samplerstate [ 8 ] ;
for ( unsigned int stage = 0 ; stage < 8 ; stage + + )
{
if ( shader_resources [ stage ] )
{
hr = D3D : : device - > CreateSamplerState ( & samplerdesc [ stage ] , & samplerstate [ stage ] ) ;
if ( FAILED ( hr ) ) PanicAlert ( " Fail %s %d, stage=%d \n " , __FILE__ , __LINE__ , stage ) ;
else SetDebugObjectName ( ( ID3D11DeviceChild * ) samplerstate [ stage ] , " a sampler state of EmuGfxState " ) ;
}
else samplerstate [ stage ] = NULL ;
}
D3D : : context - > PSSetSamplers ( 0 , 8 , samplerstate ) ;
for ( unsigned int stage = 0 ; stage < 8 ; stage + + )
SAFE_RELEASE ( samplerstate [ stage ] ) ;
ID3D11BlendState * blstate ;
hr = device - > CreateBlendState ( & blenddesc , & blstate ) ;
if ( FAILED ( hr ) ) PanicAlert ( " Failed to create blend state at %s %d \n " , __FILE__ , __LINE__ ) ;
context - > OMSetBlendState ( blstate , NULL , 0xFFFFFFFF ) ;
SetDebugObjectName ( ( ID3D11DeviceChild * ) blstate , " a blend state of EmuGfxState " ) ;
blstate - > Release ( ) ;
rastdesc . FillMode = ( g_ActiveConfig . bWireFrame ) ? D3D11_FILL_WIREFRAME : D3D11_FILL_SOLID ;
ID3D11RasterizerState * raststate ;
hr = device - > CreateRasterizerState ( & rastdesc , & raststate ) ;
if ( FAILED ( hr ) ) PanicAlert ( " Failed to create rasterizer state at %s %d \n " , __FILE__ , __LINE__ ) ;
SetDebugObjectName ( ( ID3D11DeviceChild * ) raststate , " a rasterizer state of EmuGfxState " ) ;
context - > RSSetState ( raststate ) ;
raststate - > Release ( ) ;
ID3D11DepthStencilState * depth_state ;
hr = device - > CreateDepthStencilState ( & depthdesc , & depth_state ) ;
if ( SUCCEEDED ( hr ) ) SetDebugObjectName ( ( ID3D11DeviceChild * ) depth_state , " a depth-stencil state of EmuGfxState " ) ;
else PanicAlert ( " Failed to create depth state at %s %d \n " , __FILE__ , __LINE__ ) ;
context - > OMSetDepthStencilState ( depth_state , 0 ) ;
depth_state - > Release ( ) ;
context - > PSSetShader ( pixelshader , NULL , 0 ) ;
context - > VSSetShader ( vertexshader , NULL , 0 ) ;
context - > PSSetShaderResources ( 0 , 8 , shader_resources ) ;
}
void EmuGfxState : : AlphaPass ( )
{
context - > PSSetShader ( pixelshader , NULL , 0 ) ;
ID3D11BlendState * blstate ;
D3D11_BLEND_DESC desc = blenddesc ;
desc . RenderTarget [ 0 ] . RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALPHA ;
desc . RenderTarget [ 0 ] . BlendEnable = FALSE ;
HRESULT hr = device - > CreateBlendState ( & desc , & blstate ) ;
if ( FAILED ( hr ) ) PanicAlert ( " Failed to create blend state at %s %d \n " , __FILE__ , __LINE__ ) ;
SetDebugObjectName ( ( ID3D11DeviceChild * ) blstate , " a blend state of EmuGfxState (created during alpha pass) " ) ;
context - > OMSetBlendState ( blstate , NULL , 0xFFFFFFFF ) ;
blstate - > Release ( ) ;
}
void EmuGfxState : : ResetShaderResources ( )
{
for ( unsigned int k = 0 ; k < 8 ; k + + )
SAFE_RELEASE ( shader_resources [ k ] ) ;
}
EmuGfxState * gfxstate = NULL ;
HRESULT Create ( HWND wnd )
{
hWnd = wnd ;
HRESULT hr ;
RECT client ;
GetClientRect ( hWnd , & client ) ;
xres = client . right - client . left ;
yres = client . bottom - client . top ;
2010-06-14 19:20:41 +00:00
IDXGIFactory * factory ;
IDXGIAdapter * adapter ;
IDXGIOutput * output ;
hr = CreateDXGIFactory ( __uuidof ( IDXGIFactory ) , ( void * * ) & factory ) ;
if ( FAILED ( hr ) ) MessageBox ( wnd , _T ( " Failed to create IDXGIFactory object " ) , _T ( " Dolphin Direct3D 11 plugin " ) , MB_OK | MB_ICONERROR ) ;
hr = factory - > EnumAdapters ( g_ActiveConfig . iAdapter , & adapter ) ;
if ( FAILED ( hr ) )
{
// try using the first one
hr = factory - > EnumAdapters ( 0 , & adapter ) ;
2010-06-16 10:12:57 +00:00
if ( FAILED ( hr ) ) MessageBox ( wnd , _T ( " Failed to enumerate adapters " ) , _T ( " Dolphin Direct3D 11 plugin " ) , MB_OK | MB_ICONERROR ) ;
2010-06-14 19:20:41 +00:00
}
// TODO: Make this configurable
hr = adapter - > EnumOutputs ( 0 , & output ) ;
if ( FAILED ( hr ) )
{
// try using the first one
hr = adapter - > EnumOutputs ( 0 , & output ) ;
2010-06-16 10:12:57 +00:00
if ( FAILED ( hr ) ) MessageBox ( wnd , _T ( " Failed to enumerate outputs " ) , _T ( " Dolphin Direct3D 11 plugin " ) , MB_OK | MB_ICONERROR ) ;
2010-06-14 19:20:41 +00:00
}
// this will need to be changed once multisampling gets implemented
DXGI_SWAP_CHAIN_DESC swap_chain_desc ;
memset ( & swap_chain_desc , 0 , sizeof ( swap_chain_desc ) ) ;
swap_chain_desc . BufferCount = 1 ;
swap_chain_desc . BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT ;
swap_chain_desc . OutputWindow = wnd ;
swap_chain_desc . SampleDesc . Count = 1 ;
swap_chain_desc . SampleDesc . Quality = 0 ;
swap_chain_desc . Windowed = TRUE ;
DXGI_MODE_DESC mode_desc ;
memset ( & mode_desc , 0 , sizeof ( mode_desc ) ) ;
mode_desc . Width = xres ;
mode_desc . Height = yres ;
mode_desc . Format = DXGI_FORMAT_R8G8B8A8_UNORM ;
mode_desc . Scaling = DXGI_MODE_SCALING_UNSPECIFIED ;
hr = output - > FindClosestMatchingMode ( & mode_desc , & swap_chain_desc . BufferDesc , NULL ) ;
if ( FAILED ( hr ) ) MessageBox ( wnd , _T ( " Failed to find a supported video mode " ) , _T ( " Dolphin Direct3D 11 plugin " ) , MB_OK | MB_ICONERROR ) ;
2010-06-14 21:31:23 +00:00
// forcing buffer resolution to xres and yres.. TODO: The new video mode might not actually be supported!
swap_chain_desc . BufferDesc . Width = xres ;
swap_chain_desc . BufferDesc . Height = yres ;
2010-06-13 19:50:06 +00:00
# if defined(_DEBUG) || defined(DEBUGFAST)
D3D11_CREATE_DEVICE_FLAG device_flags = ( D3D11_CREATE_DEVICE_FLAG ) ( D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_SINGLETHREADED ) ;
# else
D3D11_CREATE_DEVICE_FLAG device_flags = D3D11_CREATE_DEVICE_SINGLETHREADED ;
# endif
2010-06-14 22:38:47 +00:00
hr = D3D11CreateDeviceAndSwapChain ( adapter , D3D_DRIVER_TYPE_UNKNOWN , NULL , device_flags ,
supported_feature_levels , NUM_SUPPORTED_FEATURE_LEVELS ,
D3D11_SDK_VERSION , & swap_chain_desc , & swapchain , & device ,
& featlevel , & context ) ;
2010-06-13 19:50:06 +00:00
if ( FAILED ( hr ) | | ! device | | ! context | | ! swapchain )
{
2010-06-14 22:38:47 +00:00
MessageBox ( wnd , _T ( " Failed to initialize Direct3D. \n Make sure your video card supports at least D3D 10.0 " ) , _T ( " Dolphin Direct3D 11 plugin " ) , MB_OK | MB_ICONERROR ) ;
2010-06-13 19:50:06 +00:00
SAFE_RELEASE ( device ) ;
SAFE_RELEASE ( context ) ;
SAFE_RELEASE ( swapchain ) ;
return E_FAIL ;
}
SetDebugObjectName ( ( ID3D11DeviceChild * ) context , " device context " ) ;
2010-06-14 19:20:41 +00:00
factory - > Release ( ) ;
output - > Release ( ) ;
adapter - > Release ( ) ;
2010-06-13 19:50:06 +00:00
ID3D11Texture2D * buf ;
hr = swapchain - > GetBuffer ( 0 , IID_ID3D11Texture2D , ( void * * ) & buf ) ;
if ( FAILED ( hr ) )
{
MessageBox ( wnd , _T ( " Failed to get swapchain buffer " ) , _T ( " Dolphin Direct3D 11 plugin " ) , MB_OK | MB_ICONERROR ) ;
device - > Release ( ) ;
context - > Release ( ) ;
swapchain - > Release ( ) ;
return E_FAIL ;
}
backbuf = new D3DTexture2D ( buf , D3D11_BIND_RENDER_TARGET ) ;
buf - > Release ( ) ;
2010-06-18 18:40:58 +00:00
CHECK ( backbuf ! = NULL , " Create back buffer texture " ) ;
2010-06-13 19:50:06 +00:00
SetDebugObjectName ( ( ID3D11DeviceChild * ) backbuf - > GetTex ( ) , " backbuffer texture " ) ;
SetDebugObjectName ( ( ID3D11DeviceChild * ) backbuf - > GetRTV ( ) , " backbuffer render target view " ) ;
context - > OMSetRenderTargets ( 1 , & backbuf - > GetRTV ( ) , NULL ) ;
gfxstate = new EmuGfxState ;
return S_OK ;
}
void Close ( )
{
// release all bound resources
context - > ClearState ( ) ;
SAFE_RELEASE ( backbuf ) ;
2010-06-17 10:42:57 +00:00
if ( gfxstate ) delete gfxstate ;
2010-06-13 19:50:06 +00:00
SAFE_RELEASE ( swapchain ) ;
2010-06-17 10:42:57 +00:00
context - > Flush ( ) ; // immediately destroy device objects
SAFE_RELEASE ( context ) ;
2010-06-13 19:50:06 +00:00
ULONG references = device - > Release ( ) ;
if ( references )
{
ERROR_LOG ( VIDEO , " Unreleased references: %i. " , references ) ;
}
else
{
NOTICE_LOG ( VIDEO , " Successfully released all device references! " ) ;
}
device = NULL ;
}
/* just returning the 4_0 ones here */
const char * VertexShaderVersionString ( ) { return " vs_4_0 " ; }
const char * PixelShaderVersionString ( ) { return " ps_4_0 " ; }
D3DTexture2D * & GetBackBuffer ( ) { return backbuf ; }
unsigned int GetBackBufferWidth ( ) { return xres ; }
unsigned int GetBackBufferHeight ( ) { return yres ; }
void Reset ( )
{
2010-06-17 10:42:57 +00:00
// release all back buffer references
SAFE_RELEASE ( backbuf ) ;
// resize swapchain buffers
RECT client ;
GetClientRect ( hWnd , & client ) ;
xres = client . right - client . left ;
yres = client . bottom - client . top ;
D3D : : swapchain - > ResizeBuffers ( 1 , xres , yres , DXGI_FORMAT_R8G8B8A8_UNORM , 0 ) ;
// recreate back buffer texture
ID3D11Texture2D * buf ;
HRESULT hr = swapchain - > GetBuffer ( 0 , IID_ID3D11Texture2D , ( void * * ) & buf ) ;
if ( FAILED ( hr ) )
{
MessageBox ( hWnd , _T ( " Failed to get swapchain buffer " ) , _T ( " Dolphin Direct3D 11 plugin " ) , MB_OK | MB_ICONERROR ) ;
device - > Release ( ) ;
context - > Release ( ) ;
swapchain - > Release ( ) ;
return ;
}
backbuf = new D3DTexture2D ( buf , D3D11_BIND_RENDER_TARGET ) ;
buf - > Release ( ) ;
2010-06-18 18:40:58 +00:00
CHECK ( backbuf ! = NULL , " Create back buffer texture " ) ;
2010-06-17 10:42:57 +00:00
SetDebugObjectName ( ( ID3D11DeviceChild * ) backbuf - > GetTex ( ) , " backbuffer texture " ) ;
SetDebugObjectName ( ( ID3D11DeviceChild * ) backbuf - > GetRTV ( ) , " backbuffer render target view " ) ;
2010-06-13 19:50:06 +00:00
}
bool BeginFrame ( )
{
if ( bFrameInProgress )
{
PanicAlert ( " BeginFrame called although a frame is already in progress " ) ;
return false ;
}
bFrameInProgress = true ;
return ( device ! = NULL ) ;
}
void EndFrame ( )
{
if ( ! bFrameInProgress )
{
PanicAlert ( " EndFrame called although no frame is in progress " ) ;
return ;
}
bFrameInProgress = false ;
}
void Present ( )
{
// TODO: Is 1 the correct value for vsyncing?
swapchain - > Present ( ( UINT ) g_ActiveConfig . bVSync , 0 ) ;
}
} // namespace