Torque3D/Engine/source/gfx/D3D11/gfxD3D11Device.cpp
2017-12-22 17:11:31 +01:00

1869 lines
60 KiB
C++

//-----------------------------------------------------------------------------
// Copyright (c) 2015 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "core/stream/fileStream.h"
#include "core/strings/unicode.h"
#include "core/util/journal/process.h"
#include "gfx/D3D11/gfxD3D11Device.h"
#include "gfx/D3D11/gfxD3D11CardProfiler.h"
#include "gfx/D3D11/gfxD3D11VertexBuffer.h"
#include "gfx/D3D11/gfxD3D11EnumTranslate.h"
#include "gfx/D3D11/gfxD3D11QueryFence.h"
#include "gfx/D3D11/gfxD3D11OcclusionQuery.h"
#include "gfx/D3D11/gfxD3D11Shader.h"
#include "gfx/D3D11/gfxD3D11Target.h"
#include "platformWin32/platformWin32.h"
#include "windowManager/win32/win32Window.h"
#include "windowManager/platformWindow.h"
#include "gfx/D3D11/screenshotD3D11.h"
#include "materials/shaderData.h"
#include "shaderGen/shaderGen.h"
#ifdef TORQUE_DEBUG
#include "d3d11sdklayers.h"
#endif
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d11.lib")
class GFXPCD3D11RegisterDevice
{
public:
GFXPCD3D11RegisterDevice()
{
GFXInit::getRegisterDeviceSignal().notify(&GFXD3D11Device::enumerateAdapters);
}
};
static GFXPCD3D11RegisterDevice pPCD3D11RegisterDevice;
//-----------------------------------------------------------------------------
/// Parse command line arguments for window creation
//-----------------------------------------------------------------------------
static void sgPCD3D11DeviceHandleCommandLine(S32 argc, const char **argv)
{
// useful to pass parameters by command line for d3d (e.g. -dx9 -dx11)
for (U32 i = 1; i < argc; i++)
{
argv[i];
}
}
// Register the command line parsing hook
static ProcessRegisterCommandLine sgCommandLine(sgPCD3D11DeviceHandleCommandLine);
GFXAdapter::CreateDeviceInstanceDelegate GFXD3D11Device::mCreateDeviceInstance(GFXD3D11Device::createInstance);
GFXDevice *GFXD3D11Device::createInstance(U32 adapterIndex)
{
GFXD3D11Device* dev = new GFXD3D11Device(adapterIndex);
return dev;
}
GFXD3D11Device::GFXD3D11Device(U32 index)
{
mDeviceSwizzle32 = &Swizzles::bgra;
GFXVertexColor::setSwizzle(mDeviceSwizzle32);
mDeviceSwizzle24 = &Swizzles::bgr;
mAdapterIndex = index;
mD3DDevice = NULL;
mD3DDeviceContext = NULL;
mD3DDevice1 = NULL;
mD3DDeviceContext1 = NULL;
mUserAnnotation = NULL;
mVolatileVB = NULL;
mCurrentPB = NULL;
mDynamicPB = NULL;
mLastVertShader = NULL;
mLastPixShader = NULL;
mCanCurrentlyRender = false;
mTextureManager = NULL;
mCurrentStateBlock = NULL;
mResourceListHead = NULL;
mPixVersion = 0.0;
mVertexShaderTarget = String::EmptyString;
mPixelShaderTarget = String::EmptyString;
mShaderModel = String::EmptyString;
mDrawInstancesCount = 0;
mCardProfiler = NULL;
mDeviceDepthStencil = NULL;
mDeviceBackbuffer = NULL;
mDeviceBackBufferView = NULL;
mDeviceDepthStencilView = NULL;
mCreateFenceType = -1; // Unknown, test on first allocate
mCurrentConstBuffer = NULL;
mOcclusionQuerySupported = false;
mCbufferPartialSupported = false;
mDebugLayers = false;
for (U32 i = 0; i < GS_COUNT; ++i)
mModelViewProjSC[i] = NULL;
// Set up the Enum translation tables
GFXD3D11EnumTranslate::init();
}
GFXD3D11Device::~GFXD3D11Device()
{
// Release our refcount on the current stateblock object
mCurrentStateBlock = NULL;
releaseDefaultPoolResources();
mD3DDeviceContext->ClearState();
mD3DDeviceContext->Flush();
// Free the sampler states
SamplerMap::Iterator sampIter = mSamplersMap.begin();
for (; sampIter != mSamplersMap.end(); ++sampIter)
SAFE_RELEASE(sampIter->value);
// Free the vertex declarations.
VertexDeclMap::Iterator iter = mVertexDecls.begin();
for (; iter != mVertexDecls.end(); ++iter)
delete iter->value;
// Forcibly clean up the pools
mVolatileVBList.setSize(0);
mDynamicPB = NULL;
// And release our D3D resources.
SAFE_RELEASE(mDeviceDepthStencilView);
SAFE_RELEASE(mDeviceBackBufferView);
SAFE_RELEASE(mDeviceDepthStencil);
SAFE_RELEASE(mDeviceBackbuffer);
SAFE_RELEASE(mUserAnnotation);
SAFE_RELEASE(mD3DDeviceContext1);
SAFE_RELEASE(mD3DDeviceContext);
SAFE_DELETE(mCardProfiler);
SAFE_DELETE(gScreenShot);
#ifdef TORQUE_DEBUG
if (mDebugLayers)
{
ID3D11Debug *pDebug = NULL;
mD3DDevice->QueryInterface(IID_PPV_ARGS(&pDebug));
AssertFatal(pDebug, "~GFXD3D11Device- Failed to get debug layer");
pDebug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
SAFE_RELEASE(pDebug);
}
#endif
SAFE_RELEASE(mSwapChain);
SAFE_RELEASE(mD3DDevice1);
SAFE_RELEASE(mD3DDevice);
}
GFXFormat GFXD3D11Device::selectSupportedFormat(GFXTextureProfile *profile, const Vector<GFXFormat> &formats, bool texture, bool mustblend, bool mustfilter)
{
U32 features = 0;
if(texture)
features |= D3D11_FORMAT_SUPPORT_TEXTURE2D;
if(mustblend)
features |= D3D11_FORMAT_SUPPORT_BLENDABLE;
if(mustfilter)
features |= D3D11_FORMAT_SUPPORT_SHADER_SAMPLE;
for(U32 i = 0; i < formats.size(); i++)
{
if(GFXD3D11TextureFormat[formats[i]] == DXGI_FORMAT_UNKNOWN)
continue;
U32 supportFlag = 0;
mD3DDevice->CheckFormatSupport(GFXD3D11TextureFormat[formats[i]],&supportFlag);
if(supportFlag & features)
return formats[i];
}
return GFXFormatR8G8B8A8;
}
DXGI_SWAP_CHAIN_DESC GFXD3D11Device::setupPresentParams(const GFXVideoMode &mode, const HWND &hwnd)
{
DXGI_SWAP_CHAIN_DESC d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
DXGI_SAMPLE_DESC sampleDesc;
sampleDesc.Count = 1;
sampleDesc.Quality = 0;
mMultisampleDesc = sampleDesc;
d3dpp.BufferCount = !smDisableVSync ? 2 : 1; // triple buffering when vsync is on.
d3dpp.BufferDesc.Width = mode.resolution.x;
d3dpp.BufferDesc.Height = mode.resolution.y;
d3dpp.BufferDesc.Format = GFXD3D11TextureFormat[GFXFormatR8G8B8A8_SRGB];
d3dpp.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
d3dpp.OutputWindow = hwnd;
d3dpp.SampleDesc = sampleDesc;
d3dpp.Windowed = !mode.fullScreen;
d3dpp.BufferDesc.RefreshRate.Numerator = mode.refreshRate;
d3dpp.BufferDesc.RefreshRate.Denominator = 1;
d3dpp.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
if (mode.fullScreen)
{
d3dpp.BufferDesc.Scaling = DXGI_MODE_SCALING_CENTERED;
d3dpp.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
d3dpp.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
}
return d3dpp;
}
void GFXD3D11Device::enumerateAdapters(Vector<GFXAdapter*> &adapterList)
{
IDXGIAdapter1* EnumAdapter;
IDXGIFactory1* DXGIFactory;
CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&DXGIFactory));
for(U32 adapterIndex = 0; DXGIFactory->EnumAdapters1(adapterIndex, &EnumAdapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex)
{
GFXAdapter *toAdd = new GFXAdapter;
toAdd->mType = Direct3D11;
toAdd->mIndex = adapterIndex;
toAdd->mCreateDeviceInstanceDelegate = mCreateDeviceInstance;
toAdd->mShaderModel = 5.0f;
DXGI_ADAPTER_DESC1 desc;
EnumAdapter->GetDesc1(&desc);
// LUID identifies adapter for oculus rift
dMemcpy(&toAdd->mLUID, &desc.AdapterLuid, sizeof(toAdd->mLUID));
size_t size=wcslen(desc.Description);
char *str = new char[size+1];
wcstombs(str, desc.Description,size);
str[size]='\0';
String Description=str;
SAFE_DELETE_ARRAY(str);
dStrncpy(toAdd->mName, Description.c_str(), GFXAdapter::MaxAdapterNameLen);
dStrncat(toAdd->mName, " (D3D11)", GFXAdapter::MaxAdapterNameLen);
IDXGIOutput* pOutput = NULL;
HRESULT hr;
hr = EnumAdapter->EnumOutputs(adapterIndex, &pOutput);
if(hr == DXGI_ERROR_NOT_FOUND)
{
SAFE_RELEASE(EnumAdapter);
break;
}
if(FAILED(hr))
AssertFatal(false, "GFXD3D11Device::enumerateAdapters -> EnumOutputs call failure");
UINT numModes = 0;
DXGI_MODE_DESC* displayModes = NULL;
DXGI_FORMAT format = GFXD3D11TextureFormat[GFXFormatR8G8B8A8_SRGB];
// Get the number of elements
hr = pOutput->GetDisplayModeList(format, 0, &numModes, NULL);
if(FAILED(hr))
AssertFatal(false, "GFXD3D11Device::enumerateAdapters -> GetDisplayModeList call failure");
displayModes = new DXGI_MODE_DESC[numModes];
// Get the list
hr = pOutput->GetDisplayModeList(format, 0, &numModes, displayModes);
if(FAILED(hr))
AssertFatal(false, "GFXD3D11Device::enumerateAdapters -> GetDisplayModeList call failure");
for(U32 numMode = 0; numMode < numModes; ++numMode)
{
GFXVideoMode vmAdd;
vmAdd.fullScreen = true;
vmAdd.bitDepth = 32;
vmAdd.refreshRate = displayModes[numMode].RefreshRate.Numerator / displayModes[numMode].RefreshRate.Denominator;
vmAdd.resolution.x = displayModes[numMode].Width;
vmAdd.resolution.y = displayModes[numMode].Height;
toAdd->mAvailableModes.push_back(vmAdd);
}
//Check adapater can handle feature level 10
D3D_FEATURE_LEVEL deviceFeature;
ID3D11Device *pTmpDevice = nullptr;
// Create temp Direct3D11 device.
bool suitable = true;
UINT createDeviceFlags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT;
hr = D3D11CreateDevice(EnumAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, createDeviceFlags, NULL, 0, D3D11_SDK_VERSION, &pTmpDevice, &deviceFeature, NULL);
if (FAILED(hr))
suitable = false;
if (deviceFeature < D3D_FEATURE_LEVEL_10_0)
suitable = false;
//double check we support required bgra format for LEVEL_10_0 & LEVEL_10_1
if (deviceFeature == D3D_FEATURE_LEVEL_10_0 || deviceFeature == D3D_FEATURE_LEVEL_10_1)
{
U32 formatSupported = 0;
pTmpDevice->CheckFormatSupport(DXGI_FORMAT_B8G8R8A8_UNORM, &formatSupported);
U32 flagsRequired = D3D11_FORMAT_SUPPORT_RENDER_TARGET | D3D11_FORMAT_SUPPORT_DISPLAY;
if (!(formatSupported && flagsRequired))
{
Con::printf("DXGI adapter: %s does not support BGRA", Description.c_str());
suitable = false;
}
}
delete[] displayModes;
SAFE_RELEASE(pTmpDevice);
SAFE_RELEASE(pOutput);
SAFE_RELEASE(EnumAdapter);
if (suitable)
{
adapterList.push_back(toAdd);
}
else
{
Con::printf("DXGI adapter: %s does not support D3D11 feature level 10 or better", Description.c_str());
delete toAdd;
}
}
SAFE_RELEASE(DXGIFactory);
}
void GFXD3D11Device::enumerateVideoModes()
{
mVideoModes.clear();
IDXGIAdapter1* EnumAdapter;
IDXGIFactory1* DXGIFactory;
HRESULT hr;
hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&DXGIFactory));
if (FAILED(hr))
AssertFatal(false, "GFXD3D11Device::enumerateVideoModes -> CreateDXGIFactory1 call failure");
for(U32 adapterIndex = 0; DXGIFactory->EnumAdapters1(adapterIndex, &EnumAdapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex)
{
IDXGIOutput* pOutput = NULL;
hr = EnumAdapter->EnumOutputs(adapterIndex, &pOutput);
if(hr == DXGI_ERROR_NOT_FOUND)
{
SAFE_RELEASE(EnumAdapter);
break;
}
if(FAILED(hr))
AssertFatal(false, "GFXD3D11Device::enumerateVideoModes -> EnumOutputs call failure");
UINT numModes = 0;
DXGI_MODE_DESC* displayModes = NULL;
DXGI_FORMAT format = GFXD3D11TextureFormat[GFXFormatR8G8B8A8_SRGB];
// Get the number of elements
hr = pOutput->GetDisplayModeList(format, 0, &numModes, NULL);
if(FAILED(hr))
AssertFatal(false, "GFXD3D11Device::enumerateVideoModes -> GetDisplayModeList call failure");
displayModes = new DXGI_MODE_DESC[numModes];
// Get the list
hr = pOutput->GetDisplayModeList(format, 0, &numModes, displayModes);
if(FAILED(hr))
AssertFatal(false, "GFXD3D11Device::enumerateVideoModes -> GetDisplayModeList call failure");
for(U32 numMode = 0; numMode < numModes; ++numMode)
{
GFXVideoMode toAdd;
toAdd.fullScreen = false;
toAdd.bitDepth = 32;
toAdd.refreshRate = displayModes[numMode].RefreshRate.Numerator / displayModes[numMode].RefreshRate.Denominator;
toAdd.resolution.x = displayModes[numMode].Width;
toAdd.resolution.y = displayModes[numMode].Height;
mVideoModes.push_back(toAdd);
}
delete[] displayModes;
SAFE_RELEASE(pOutput);
SAFE_RELEASE(EnumAdapter);
}
SAFE_RELEASE(DXGIFactory);
}
void GFXD3D11Device::init(const GFXVideoMode &mode, PlatformWindow *window)
{
AssertFatal(window, "GFXD3D11Device::init - must specify a window!");
HWND winHwnd = (HWND)window->getSystemWindow( PlatformWindow::WindowSystem_Windows );
UINT createDeviceFlags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#ifdef TORQUE_DEBUG
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
mDebugLayers = true;
#endif
DXGI_SWAP_CHAIN_DESC d3dpp = setupPresentParams(mode, winHwnd);
// TODO support at least feature level 10 to match GL
D3D_FEATURE_LEVEL pFeatureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 };
U32 nFeatureCount = ARRAYSIZE(pFeatureLevels);
D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_HARDWARE;// use D3D_DRIVER_TYPE_REFERENCE for reference device
// create a device, device context and swap chain using the information in the d3dpp struct
HRESULT hres = D3D11CreateDeviceAndSwapChain(NULL,
driverType,
NULL,
createDeviceFlags,
pFeatureLevels,
nFeatureCount,
D3D11_SDK_VERSION,
&d3dpp,
&mSwapChain,
&mD3DDevice,
&mFeatureLevel,
&mD3DDeviceContext);
if(FAILED(hres))
{
#ifdef TORQUE_DEBUG
//try again without debug device layer enabled
createDeviceFlags &= ~D3D11_CREATE_DEVICE_DEBUG;
hres = D3D11CreateDeviceAndSwapChain(NULL, driverType,NULL,createDeviceFlags,NULL, 0,
D3D11_SDK_VERSION,
&d3dpp,
&mSwapChain,
&mD3DDevice,
&mFeatureLevel,
&mD3DDeviceContext);
//if we failed again than we definitely have a problem
if (FAILED(hres))
AssertFatal(false, "GFXD3D11Device::init - D3D11CreateDeviceAndSwapChain failed!");
Con::warnf("GFXD3D11Device::init - Debug layers not detected!");
mDebugLayers = false;
#else
AssertFatal(false, "GFXD3D11Device::init - D3D11CreateDeviceAndSwapChain failed!");
#endif
}
// Grab DX 11.1 device and context if available and also ID3DUserDefinedAnnotation
hres = mD3DDevice->QueryInterface(__uuidof(ID3D11Device1), reinterpret_cast<void**>(&mD3DDevice1));
if (SUCCEEDED(hres))
{
//11.1 context
mD3DDeviceContext->QueryInterface(__uuidof(ID3D11DeviceContext1), reinterpret_cast<void**>(&mD3DDeviceContext1));
// ID3DUserDefinedAnnotation
mD3DDeviceContext->QueryInterface(IID_PPV_ARGS(&mUserAnnotation));
//Check what is supported, windows 7 supports very little from 11.1
D3D11_FEATURE_DATA_D3D11_OPTIONS options;
mD3DDevice1->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &options,
sizeof(D3D11_FEATURE_DATA_D3D11_OPTIONS));
//Cbuffer partial updates
if (options.ConstantBufferOffsetting && options.ConstantBufferPartialUpdate)
mCbufferPartialSupported = true;
}
//set the fullscreen state here if we need to
if(mode.fullScreen)
{
hres = mSwapChain->SetFullscreenState(TRUE, NULL);
if(FAILED(hres))
{
AssertFatal(false, "GFXD3D11Device::init- Failed to set fullscreen state!");
}
}
mTextureManager = new GFXD3D11TextureManager();
// Now reacquire all the resources we trashed earlier
reacquireDefaultPoolResources();
//set vert/pixel shader targets
switch (mFeatureLevel)
{
case D3D_FEATURE_LEVEL_11_1:
case D3D_FEATURE_LEVEL_11_0:
mVertexShaderTarget = "vs_5_0";
mPixelShaderTarget = "ps_5_0";
mPixVersion = 5.0f;
mShaderModel = "50";
break;
case D3D_FEATURE_LEVEL_10_1:
mVertexShaderTarget = "vs_4_1";
mPixelShaderTarget = "ps_4_1";
mPixVersion = 4.1f;
mShaderModel = "41";
break;
case D3D_FEATURE_LEVEL_10_0:
mVertexShaderTarget = "vs_4_0";
mPixelShaderTarget = "ps_4_0";
mPixVersion = 4.0f;
mShaderModel = "40";
break;
default:
AssertFatal(false, "GFXD3D11Device::init - We don't support this feature level");
}
D3D11_QUERY_DESC queryDesc;
queryDesc.Query = D3D11_QUERY_OCCLUSION;
queryDesc.MiscFlags = 0;
ID3D11Query *testQuery = NULL;
// detect occlusion query support
if (SUCCEEDED(mD3DDevice->CreateQuery(&queryDesc, &testQuery))) mOcclusionQuerySupported = true;
SAFE_RELEASE(testQuery);
Con::printf("Hardware occlusion query detected: %s", mOcclusionQuerySupported ? "Yes" : "No");
mCardProfiler = new GFXD3D11CardProfiler();
mCardProfiler->init();
D3D11_TEXTURE2D_DESC desc;
desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
desc.CPUAccessFlags = 0;
desc.Format = GFXD3D11TextureFormat[GFXFormatD24S8];
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.Width = mode.resolution.x;
desc.Height = mode.resolution.y;
desc.SampleDesc.Count =1;
desc.SampleDesc.Quality =0;
desc.MiscFlags = 0;
HRESULT hr = mD3DDevice->CreateTexture2D(&desc, NULL, &mDeviceDepthStencil);
if(FAILED(hr))
{
AssertFatal(false, "GFXD3D11Device::init - couldn't create device's depth-stencil surface.");
}
D3D11_DEPTH_STENCIL_VIEW_DESC depthDesc;
depthDesc.Format = GFXD3D11TextureFormat[GFXFormatD24S8];
depthDesc.Flags =0 ;
depthDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
depthDesc.Texture2D.MipSlice = 0;
hr = mD3DDevice->CreateDepthStencilView(mDeviceDepthStencil, &depthDesc, &mDeviceDepthStencilView);
if(FAILED(hr))
{
AssertFatal(false, "GFXD3D11Device::init - couldn't create depth stencil view");
}
hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&mDeviceBackbuffer);
if(FAILED(hr))
AssertFatal(false, "GFXD3D11Device::init - coudln't retrieve backbuffer ref");
//create back buffer view
D3D11_RENDER_TARGET_VIEW_DESC RTDesc;
RTDesc.Format = GFXD3D11TextureFormat[GFXFormatR8G8B8A8_SRGB];
RTDesc.Texture2D.MipSlice = 0;
RTDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
hr = mD3DDevice->CreateRenderTargetView(mDeviceBackbuffer, &RTDesc, &mDeviceBackBufferView);
if(FAILED(hr))
AssertFatal(false, "GFXD3D11Device::init - couldn't create back buffer target view");
#ifdef TORQUE_DEBUG
String backBufferName = "MainBackBuffer";
String depthSteniclName = "MainDepthStencil";
String backBuffViewName = "MainBackBuffView";
String depthStencViewName = "MainDepthView";
mDeviceBackbuffer->SetPrivateData(WKPDID_D3DDebugObjectName, backBufferName.size(), backBufferName.c_str());
mDeviceDepthStencil->SetPrivateData(WKPDID_D3DDebugObjectName, depthSteniclName.size(), depthSteniclName.c_str());
mDeviceDepthStencilView->SetPrivateData(WKPDID_D3DDebugObjectName, depthStencViewName.size(), depthStencViewName.c_str());
mDeviceBackBufferView->SetPrivateData(WKPDID_D3DDebugObjectName, backBuffViewName.size(), backBuffViewName.c_str());
_suppressDebugMessages();
#endif
gScreenShot = new ScreenShotD3D11;
mInitialized = true;
deviceInited();
}
// Supress any debug layer messages we don't want to see
void GFXD3D11Device::_suppressDebugMessages()
{
if (mDebugLayers)
{
ID3D11Debug *pDebug = NULL;
if (SUCCEEDED(mD3DDevice->QueryInterface(__uuidof(ID3D11Debug), (void**)&pDebug)))
{
ID3D11InfoQueue *pInfoQueue = NULL;
if (SUCCEEDED(pDebug->QueryInterface(__uuidof(ID3D11InfoQueue), (void**)&pInfoQueue)))
{
//Disable breaking on error or corruption, this can be handy when using VS graphics debugging
pInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, false);
pInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, false);
D3D11_MESSAGE_ID hide[] =
{
//this is harmless and no need to spam the console
D3D11_MESSAGE_ID_QUERY_BEGIN_ABANDONING_PREVIOUS_RESULTS
};
D3D11_INFO_QUEUE_FILTER filter;
memset(&filter, 0, sizeof(filter));
filter.DenyList.NumIDs = _countof(hide);
filter.DenyList.pIDList = hide;
pInfoQueue->AddStorageFilterEntries(&filter);
SAFE_RELEASE(pInfoQueue);
}
SAFE_RELEASE(pDebug);
}
}
}
bool GFXD3D11Device::beginSceneInternal()
{
mCanCurrentlyRender = true;
return mCanCurrentlyRender;
}
GFXWindowTarget * GFXD3D11Device::allocWindowTarget(PlatformWindow *window)
{
AssertFatal(window,"GFXD3D11Device::allocWindowTarget - no window provided!");
// Allocate the device.
init(window->getVideoMode(), window);
// Set up a new window target...
GFXD3D11WindowTarget *gdwt = new GFXD3D11WindowTarget();
gdwt->mWindow = window;
gdwt->mSize = window->getClientExtent();
gdwt->initPresentationParams();
gdwt->registerResourceWithDevice(this);
return gdwt;
}
GFXTextureTarget* GFXD3D11Device::allocRenderToTextureTarget()
{
GFXD3D11TextureTarget *targ = new GFXD3D11TextureTarget();
targ->registerResourceWithDevice(this);
return targ;
}
void GFXD3D11Device::reset(DXGI_SWAP_CHAIN_DESC &d3dpp)
{
if (!mD3DDevice)
return;
mInitialized = false;
// Clean up some commonly dangling state. This helps prevents issues with
// items that are destroyed by the texture manager callbacks and recreated
// later, but still left bound.
setVertexBuffer(NULL);
setPrimitiveBuffer(NULL);
for (S32 i = 0; i<getNumSamplers(); i++)
setTexture(i, NULL);
mD3DDeviceContext->ClearState();
DXGI_MODE_DESC displayModes;
displayModes.Format = d3dpp.BufferDesc.Format;
displayModes.Height = d3dpp.BufferDesc.Height;
displayModes.Width = d3dpp.BufferDesc.Width;
displayModes.RefreshRate = d3dpp.BufferDesc.RefreshRate;
displayModes.Scaling = d3dpp.BufferDesc.Scaling;
displayModes.ScanlineOrdering = d3dpp.BufferDesc.ScanlineOrdering;
HRESULT hr;
if (!d3dpp.Windowed)
{
hr = mSwapChain->ResizeTarget(&displayModes);
if (FAILED(hr))
{
AssertFatal(false, "D3D11Device::reset - failed to resize target!");
}
}
// First release all the stuff we allocated from D3DPOOL_DEFAULT
releaseDefaultPoolResources();
//release the backbuffer, depthstencil, and their views
SAFE_RELEASE(mDeviceBackBufferView);
SAFE_RELEASE(mDeviceBackbuffer);
SAFE_RELEASE(mDeviceDepthStencilView);
SAFE_RELEASE(mDeviceDepthStencil);
hr = mSwapChain->ResizeBuffers(d3dpp.BufferCount, d3dpp.BufferDesc.Width, d3dpp.BufferDesc.Height, d3dpp.BufferDesc.Format, d3dpp.Windowed ? 0 : DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);
if (FAILED(hr))
{
AssertFatal(false, "D3D11Device::reset - failed to resize back buffer!");
}
//recreate backbuffer view. depth stencil view and texture
D3D11_TEXTURE2D_DESC desc;
desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
desc.CPUAccessFlags = 0;
desc.Format = GFXD3D11TextureFormat[GFXFormatD24S8];
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.Width = d3dpp.BufferDesc.Width;
desc.Height = d3dpp.BufferDesc.Height;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.MiscFlags = 0;
hr = mD3DDevice->CreateTexture2D(&desc, NULL, &mDeviceDepthStencil);
if (FAILED(hr))
{
AssertFatal(false, "GFXD3D11Device::reset - couldn't create device's depth-stencil surface.");
}
D3D11_DEPTH_STENCIL_VIEW_DESC depthDesc;
depthDesc.Format = GFXD3D11TextureFormat[GFXFormatD24S8];
depthDesc.Flags = 0;
depthDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
depthDesc.Texture2D.MipSlice = 0;
hr = mD3DDevice->CreateDepthStencilView(mDeviceDepthStencil, &depthDesc, &mDeviceDepthStencilView);
if (FAILED(hr))
{
AssertFatal(false, "GFXD3D11Device::reset - couldn't create depth stencil view");
}
hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&mDeviceBackbuffer);
if (FAILED(hr))
AssertFatal(false, "GFXD3D11Device::reset - coudln't retrieve backbuffer ref");
//create back buffer view
D3D11_RENDER_TARGET_VIEW_DESC RTDesc;
RTDesc.Format = GFXD3D11TextureFormat[GFXFormatR8G8B8A8_SRGB];
RTDesc.Texture2D.MipSlice = 0;
RTDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
hr = mD3DDevice->CreateRenderTargetView(mDeviceBackbuffer, &RTDesc, &mDeviceBackBufferView);
if (FAILED(hr))
AssertFatal(false, "GFXD3D11Device::reset - couldn't create back buffer target view");
mD3DDeviceContext->OMSetRenderTargets(1, &mDeviceBackBufferView, mDeviceDepthStencilView);
hr = mSwapChain->SetFullscreenState(!d3dpp.Windowed, NULL);
if (FAILED(hr))
{
AssertFatal(false, "D3D11Device::reset - failed to change screen states!");
}
//Microsoft recommend this, see DXGI documentation
if (!d3dpp.Windowed)
{
displayModes.RefreshRate.Numerator = 0;
displayModes.RefreshRate.Denominator = 0;
hr = mSwapChain->ResizeTarget(&displayModes);
if (FAILED(hr))
{
AssertFatal(false, "D3D11Device::reset - failed to resize target!");
}
}
mInitialized = true;
// Now re aquire all the resources we trashed earlier
reacquireDefaultPoolResources();
//set last bound shaders
mD3DDeviceContext->PSSetShader(mLastPixShader, NULL, 0);
mD3DDeviceContext->VSSetShader(mLastVertShader, NULL, 0);
// Mark everything dirty and flush to card, for sanity.
updateStates(true);
}
void GFXD3D11Device::setupGenericShaders(GenericShaderType type)
{
AssertFatal(type != GSTargetRestore, ""); //not used
if(mGenericShader[GSColor] == NULL)
{
ShaderData *shaderData;
//shader model 4.0 is enough for the generic shaders
const char* shaderModel = "4.0";
shaderData = new ShaderData();
shaderData->setField("DXVertexShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/colorV.hlsl"));
shaderData->setField("DXPixelShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/colorP.hlsl"));
shaderData->setField("pixVersion", shaderModel);
shaderData->registerObject();
mGenericShader[GSColor] = shaderData->getShader();
mGenericShaderBuffer[GSColor] = mGenericShader[GSColor]->allocConstBuffer();
mModelViewProjSC[GSColor] = mGenericShader[GSColor]->getShaderConstHandle("$modelView");
Sim::getRootGroup()->addObject(shaderData);
shaderData = new ShaderData();
shaderData->setField("DXVertexShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/modColorTextureV.hlsl"));
shaderData->setField("DXPixelShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/modColorTextureP.hlsl"));
shaderData->setField("pixVersion", shaderModel);
shaderData->registerObject();
mGenericShader[GSModColorTexture] = shaderData->getShader();
mGenericShaderBuffer[GSModColorTexture] = mGenericShader[GSModColorTexture]->allocConstBuffer();
mModelViewProjSC[GSModColorTexture] = mGenericShader[GSModColorTexture]->getShaderConstHandle("$modelView");
Sim::getRootGroup()->addObject(shaderData);
shaderData = new ShaderData();
shaderData->setField("DXVertexShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/addColorTextureV.hlsl"));
shaderData->setField("DXPixelShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/addColorTextureP.hlsl"));
shaderData->setField("pixVersion", shaderModel);
shaderData->registerObject();
mGenericShader[GSAddColorTexture] = shaderData->getShader();
mGenericShaderBuffer[GSAddColorTexture] = mGenericShader[GSAddColorTexture]->allocConstBuffer();
mModelViewProjSC[GSAddColorTexture] = mGenericShader[GSAddColorTexture]->getShaderConstHandle("$modelView");
Sim::getRootGroup()->addObject(shaderData);
shaderData = new ShaderData();
shaderData->setField("DXVertexShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/textureV.hlsl"));
shaderData->setField("DXPixelShaderFile", ShaderGen::smCommonShaderPath + String("/fixedFunction/textureP.hlsl"));
shaderData->setField("pixVersion", shaderModel);
shaderData->registerObject();
mGenericShader[GSTexture] = shaderData->getShader();
mGenericShaderBuffer[GSTexture] = mGenericShader[GSTexture]->allocConstBuffer();
mModelViewProjSC[GSTexture] = mGenericShader[GSTexture]->getShaderConstHandle("$modelView");
Sim::getRootGroup()->addObject(shaderData);
//Force an update
mViewportDirty = true;
_updateRenderTargets();
}
MatrixF tempMatrix = mProjectionMatrix * mViewMatrix * mWorldMatrix[mWorldStackSize];
mGenericShaderBuffer[type]->setSafe(mModelViewProjSC[type], tempMatrix);
setShader(mGenericShader[type]);
setShaderConstBuffer(mGenericShaderBuffer[type]);
}
//-----------------------------------------------------------------------------
/// Creates a state block object based on the desc passed in. This object
/// represents an immutable state.
GFXStateBlockRef GFXD3D11Device::createStateBlockInternal(const GFXStateBlockDesc& desc)
{
return GFXStateBlockRef(new GFXD3D11StateBlock(desc));
}
/// Activates a stateblock
void GFXD3D11Device::setStateBlockInternal(GFXStateBlock* block, bool force)
{
AssertFatal(static_cast<GFXD3D11StateBlock*>(block), "Incorrect stateblock type for this device!");
GFXD3D11StateBlock* d3dBlock = static_cast<GFXD3D11StateBlock*>(block);
GFXD3D11StateBlock* d3dCurrent = static_cast<GFXD3D11StateBlock*>(mCurrentStateBlock.getPointer());
if (force)
d3dCurrent = NULL;
d3dBlock->activate(d3dCurrent);
}
/// Called by base GFXDevice to actually set a const buffer
void GFXD3D11Device::setShaderConstBufferInternal(GFXShaderConstBuffer* buffer)
{
if (buffer)
{
PROFILE_SCOPE(GFXD3D11Device_setShaderConstBufferInternal);
AssertFatal(static_cast<GFXD3D11ShaderConstBuffer*>(buffer), "Incorrect shader const buffer type for this device!");
GFXD3D11ShaderConstBuffer* d3dBuffer = static_cast<GFXD3D11ShaderConstBuffer*>(buffer);
d3dBuffer->activate(mCurrentConstBuffer);
mCurrentConstBuffer = d3dBuffer;
}
else
{
mCurrentConstBuffer = NULL;
}
}
//-----------------------------------------------------------------------------
void GFXD3D11Device::clear(U32 flags, const LinearColorF& color, F32 z, U32 stencil)
{
// Make sure we have flushed our render target state.
_updateRenderTargets();
UINT depthstencilFlag = 0;
ID3D11RenderTargetView* rtView = NULL;
ID3D11DepthStencilView* dsView = NULL;
mD3DDeviceContext->OMGetRenderTargets(1, &rtView, &dsView);
const FLOAT clearColor[4] = { color.red, color.green, color.blue, color.alpha };
if (flags & GFXClearTarget && rtView)
mD3DDeviceContext->ClearRenderTargetView(rtView, clearColor);
if (flags & GFXClearZBuffer)
depthstencilFlag |= D3D11_CLEAR_DEPTH;
if (flags & GFXClearStencil)
depthstencilFlag |= D3D11_CLEAR_STENCIL;
if (depthstencilFlag && dsView)
mD3DDeviceContext->ClearDepthStencilView(dsView, depthstencilFlag, z, stencil);
SAFE_RELEASE(rtView);
SAFE_RELEASE(dsView);
}
void GFXD3D11Device::endSceneInternal()
{
mCanCurrentlyRender = false;
}
void GFXD3D11Device::_updateRenderTargets()
{
if (mRTDirty || (mCurrentRT && mCurrentRT->isPendingState()))
{
if (mRTDeactivate)
{
mRTDeactivate->deactivate();
mRTDeactivate = NULL;
}
// NOTE: The render target changes are not really accurate
// as the GFXTextureTarget supports MRT internally. So when
// we activate a GFXTarget it could result in multiple calls
// to SetRenderTarget on the actual device.
mDeviceStatistics.mRenderTargetChanges++;
mCurrentRT->activate();
mRTDirty = false;
}
if (mViewportDirty)
{
D3D11_VIEWPORT viewport;
viewport.TopLeftX = mViewport.point.x;
viewport.TopLeftY = mViewport.point.y;
viewport.Width = mViewport.extent.x;
viewport.Height = mViewport.extent.y;
viewport.MinDepth = 0.0f;
viewport.MaxDepth = 1.0f;
mD3DDeviceContext->RSSetViewports(1, &viewport);
mViewportDirty = false;
}
}
void GFXD3D11Device::releaseDefaultPoolResources()
{
// Release all the dynamic vertex buffer arrays
// Forcibly clean up the pools
for(U32 i=0; i<mVolatileVBList.size(); i++)
{
SAFE_RELEASE(mVolatileVBList[i]->vb);
mVolatileVBList[i] = NULL;
}
mVolatileVBList.setSize(0);
// We gotta clear the current const buffer else the next
// activate may erroneously think the device is still holding
// this state and fail to set it.
mCurrentConstBuffer = NULL;
// Set current VB to NULL and set state dirty
for (U32 i=0; i < VERTEX_STREAM_COUNT; i++)
{
mCurrentVertexBuffer[i] = NULL;
mVertexBufferDirty[i] = true;
mVertexBufferFrequency[i] = 0;
mVertexBufferFrequencyDirty[i] = true;
}
// Release dynamic index buffer
if(mDynamicPB != NULL)
{
SAFE_RELEASE(mDynamicPB->ib);
}
// Set current PB/IB to NULL and set state dirty
mCurrentPrimitiveBuffer = NULL;
mCurrentPB = NULL;
mPrimitiveBufferDirty = true;
// Zombify texture manager (for D3D this only modifies default pool textures)
if( mTextureManager )
mTextureManager->zombify();
// Set global dirty state so the IB/PB and VB get reset
mStateDirty = true;
// Walk the resource list and zombify everything.
GFXResource *walk = mResourceListHead;
while(walk)
{
walk->zombify();
walk = walk->getNextResource();
}
}
void GFXD3D11Device::reacquireDefaultPoolResources()
{
// Now do the dynamic index buffers
if( mDynamicPB == NULL )
mDynamicPB = new GFXD3D11PrimitiveBuffer(this, 0, 0, GFXBufferTypeDynamic);
D3D11_BUFFER_DESC desc;
desc.ByteWidth = sizeof(U16) * MAX_DYNAMIC_INDICES;
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
desc.StructureByteStride = 0;
HRESULT hr = D3D11DEVICE->CreateBuffer(&desc, NULL, &mDynamicPB->ib);
if(FAILED(hr))
{
AssertFatal(false, "Failed to allocate dynamic IB");
}
// Walk the resource list and zombify everything.
GFXResource *walk = mResourceListHead;
while(walk)
{
walk->resurrect();
walk = walk->getNextResource();
}
if(mTextureManager)
mTextureManager->resurrect();
}
GFXD3D11VertexBuffer* GFXD3D11Device::findVBPool( const GFXVertexFormat *vertexFormat, U32 vertsNeeded )
{
PROFILE_SCOPE( GFXD3D11Device_findVBPool );
for( U32 i=0; i<mVolatileVBList.size(); i++ )
if( mVolatileVBList[i]->mVertexFormat.isEqual( *vertexFormat ) )
return mVolatileVBList[i];
return NULL;
}
GFXD3D11VertexBuffer * GFXD3D11Device::createVBPool( const GFXVertexFormat *vertexFormat, U32 vertSize )
{
PROFILE_SCOPE( GFXD3D11Device_createVBPool );
// this is a bit funky, but it will avoid problems with (lack of) copy constructors
// with a push_back() situation
mVolatileVBList.increment();
StrongRefPtr<GFXD3D11VertexBuffer> newBuff;
mVolatileVBList.last() = new GFXD3D11VertexBuffer();
newBuff = mVolatileVBList.last();
newBuff->mNumVerts = 0;
newBuff->mBufferType = GFXBufferTypeVolatile;
newBuff->mVertexFormat.copy( *vertexFormat );
newBuff->mVertexSize = vertSize;
newBuff->mDevice = this;
// Requesting it will allocate it.
vertexFormat->getDecl();
D3D11_BUFFER_DESC desc;
desc.ByteWidth = vertSize * MAX_DYNAMIC_VERTS;
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
desc.StructureByteStride = 0;
HRESULT hr = D3D11DEVICE->CreateBuffer(&desc, NULL, &newBuff->vb);
if(FAILED(hr))
{
AssertFatal(false, "Failed to allocate dynamic VB");
}
return newBuff;
}
//-----------------------------------------------------------------------------
void GFXD3D11Device::setClipRect( const RectI &inRect )
{
// We transform the incoming rect by the view
// matrix first, so that it can be used to pan
// and scale the clip rect.
//
// This is currently used to take tiled screenshots.
Point3F pos( inRect.point.x, inRect.point.y, 0.0f );
Point3F extent( inRect.extent.x, inRect.extent.y, 0.0f );
getViewMatrix().mulP( pos );
getViewMatrix().mulV( extent );
RectI rect( pos.x, pos.y, extent.x, extent.y );
// Clip the rect against the renderable size.
Point2I size = mCurrentRT->getSize();
RectI maxRect(Point2I(0,0), size);
rect.intersect(maxRect);
mClipRect = rect;
F32 l = F32( mClipRect.point.x );
F32 r = F32( mClipRect.point.x + mClipRect.extent.x );
F32 b = F32( mClipRect.point.y + mClipRect.extent.y );
F32 t = F32( mClipRect.point.y );
// Set up projection matrix,
static Point4F pt;
pt.set(2.0f / (r - l), 0.0f, 0.0f, 0.0f);
mTempMatrix.setColumn(0, pt);
pt.set(0.0f, 2.0f/(t - b), 0.0f, 0.0f);
mTempMatrix.setColumn(1, pt);
pt.set(0.0f, 0.0f, 1.0f, 0.0f);
mTempMatrix.setColumn(2, pt);
pt.set((l+r)/(l-r), (t+b)/(b-t), 1.0f, 1.0f);
mTempMatrix.setColumn(3, pt);
setProjectionMatrix( mTempMatrix );
// Set up world/view matrix
mTempMatrix.identity();
setWorldMatrix( mTempMatrix );
setViewport( mClipRect );
}
void GFXD3D11Device::setVertexStream( U32 stream, GFXVertexBuffer *buffer )
{
GFXD3D11VertexBuffer *d3dBuffer = static_cast<GFXD3D11VertexBuffer*>( buffer );
if ( stream == 0 )
{
// Set the volatile buffer which is used to
// offset the start index when doing draw calls.
if ( d3dBuffer && d3dBuffer->mVolatileStart > 0 )
mVolatileVB = d3dBuffer;
else
mVolatileVB = NULL;
}
// NOTE: We do not use the stream offset here for stream 0
// as that feature is *supposedly* not as well supported as
// using the start index in drawPrimitive.
//
// If we can verify that this is not the case then we should
// start using this method exclusively for all streams.
U32 strides[1] = { d3dBuffer ? d3dBuffer->mVertexSize : 0 };
U32 offset = d3dBuffer && stream != 0 ? d3dBuffer->mVolatileStart * d3dBuffer->mVertexSize : 0;
ID3D11Buffer* buff = d3dBuffer ? d3dBuffer->vb : NULL;
getDeviceContext()->IASetVertexBuffers(stream, 1, &buff, strides, &offset);
}
void GFXD3D11Device::setVertexStreamFrequency( U32 stream, U32 frequency )
{
if (stream == 0)
mDrawInstancesCount = frequency; // instances count
}
void GFXD3D11Device::_setPrimitiveBuffer( GFXPrimitiveBuffer *buffer )
{
mCurrentPB = static_cast<GFXD3D11PrimitiveBuffer *>( buffer );
mD3DDeviceContext->IASetIndexBuffer(mCurrentPB->ib, DXGI_FORMAT_R16_UINT, 0);
}
U32 GFXD3D11Device::primCountToIndexCount(GFXPrimitiveType primType, U32 primitiveCount)
{
switch (primType)
{
case GFXPointList:
return primitiveCount;
break;
case GFXLineList:
return primitiveCount * 2;
break;
case GFXLineStrip:
return primitiveCount + 1;
break;
case GFXTriangleList:
return primitiveCount * 3;
break;
case GFXTriangleStrip:
return 2 + primitiveCount;
break;
default:
AssertFatal(false, "GFXGLDevice::primCountToIndexCount - unrecognized prim type");
break;
}
return 0;
}
void GFXD3D11Device::drawPrimitive( GFXPrimitiveType primType, U32 vertexStart, U32 primitiveCount )
{
// This is done to avoid the function call overhead if possible
if( mStateDirty )
updateStates();
if (mCurrentShaderConstBuffer)
setShaderConstBufferInternal(mCurrentShaderConstBuffer);
if ( mVolatileVB )
vertexStart += mVolatileVB->mVolatileStart;
mD3DDeviceContext->IASetPrimitiveTopology(GFXD3D11PrimType[primType]);
if ( mDrawInstancesCount )
mD3DDeviceContext->DrawInstanced(primCountToIndexCount(primType, primitiveCount), mDrawInstancesCount, vertexStart, 0);
else
mD3DDeviceContext->Draw(primCountToIndexCount(primType, primitiveCount), vertexStart);
mDeviceStatistics.mDrawCalls++;
if ( mVertexBufferFrequency[0] > 1 )
mDeviceStatistics.mPolyCount += primitiveCount * mVertexBufferFrequency[0];
else
mDeviceStatistics.mPolyCount += primitiveCount;
}
void GFXD3D11Device::drawIndexedPrimitive( GFXPrimitiveType primType,
U32 startVertex,
U32 minIndex,
U32 numVerts,
U32 startIndex,
U32 primitiveCount )
{
// This is done to avoid the function call overhead if possible
if( mStateDirty )
updateStates();
if (mCurrentShaderConstBuffer)
setShaderConstBufferInternal(mCurrentShaderConstBuffer);
AssertFatal( mCurrentPB != NULL, "Trying to call drawIndexedPrimitive with no current index buffer, call setIndexBuffer()" );
if ( mVolatileVB )
startVertex += mVolatileVB->mVolatileStart;
mD3DDeviceContext->IASetPrimitiveTopology(GFXD3D11PrimType[primType]);
if ( mDrawInstancesCount )
mD3DDeviceContext->DrawIndexedInstanced(primCountToIndexCount(primType, primitiveCount), mDrawInstancesCount, mCurrentPB->mVolatileStart + startIndex, startVertex, 0);
else
mD3DDeviceContext->DrawIndexed(primCountToIndexCount(primType,primitiveCount), mCurrentPB->mVolatileStart + startIndex, startVertex);
mDeviceStatistics.mDrawCalls++;
if ( mVertexBufferFrequency[0] > 1 )
mDeviceStatistics.mPolyCount += primitiveCount * mVertexBufferFrequency[0];
else
mDeviceStatistics.mPolyCount += primitiveCount;
}
GFXShader* GFXD3D11Device::createShader()
{
GFXD3D11Shader* shader = new GFXD3D11Shader();
shader->registerResourceWithDevice( this );
return shader;
}
//-----------------------------------------------------------------------------
// Set shader - this function exists to make sure this is done in one place,
// and to make sure redundant shader states are not being
// sent to the card.
//-----------------------------------------------------------------------------
void GFXD3D11Device::setShader(GFXShader *shader, bool force)
{
if(shader)
{
GFXD3D11Shader *d3dShader = static_cast<GFXD3D11Shader*>(shader);
if (d3dShader->mPixShader != mLastPixShader || force)
{
mD3DDeviceContext->PSSetShader( d3dShader->mPixShader, NULL, 0);
mLastPixShader = d3dShader->mPixShader;
}
if (d3dShader->mVertShader != mLastVertShader || force)
{
mD3DDeviceContext->VSSetShader( d3dShader->mVertShader, NULL, 0);
mLastVertShader = d3dShader->mVertShader;
}
}
else
{
setupGenericShaders();
}
}
GFXPrimitiveBuffer * GFXD3D11Device::allocPrimitiveBuffer(U32 numIndices, U32 numPrimitives, GFXBufferType bufferType, void *data )
{
// Allocate a buffer to return
GFXD3D11PrimitiveBuffer * res = new GFXD3D11PrimitiveBuffer(this, numIndices, numPrimitives, bufferType);
// Determine usage flags
D3D11_USAGE usage = D3D11_USAGE_DEFAULT;
// Assumptions:
// - static buffers are write once, use many
// - dynamic buffers are write many, use many
// - volatile buffers are write once, use once
// You may never read from a buffer.
//TODO: enable proper support for D3D11_USAGE_IMMUTABLE
switch(bufferType)
{
case GFXBufferTypeImmutable:
case GFXBufferTypeStatic:
usage = D3D11_USAGE_DEFAULT; //D3D11_USAGE_IMMUTABLE;
break;
case GFXBufferTypeDynamic:
case GFXBufferTypeVolatile:
usage = D3D11_USAGE_DYNAMIC;
break;
}
// Register resource
res->registerResourceWithDevice(this);
// Create d3d index buffer
if(bufferType == GFXBufferTypeVolatile)
{
// Get it from the pool if it's a volatile...
AssertFatal(numIndices < MAX_DYNAMIC_INDICES, "Cannot allocate that many indices in a volatile buffer, increase MAX_DYNAMIC_INDICES.");
res->ib = mDynamicPB->ib;
res->mVolatileBuffer = mDynamicPB;
}
else
{
// Otherwise, get it as a seperate buffer...
D3D11_BUFFER_DESC desc;
desc.ByteWidth = sizeof(U16) * numIndices;
desc.Usage = usage;
if(bufferType == GFXBufferTypeDynamic)
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // We never allow reading from a primitive buffer.
else
desc.CPUAccessFlags = 0;
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
desc.MiscFlags = 0;
desc.StructureByteStride = 0;
HRESULT hr = D3D11DEVICE->CreateBuffer(&desc, NULL, &res->ib);
if(FAILED(hr))
{
AssertFatal(false, "Failed to allocate an index buffer.");
}
}
if (data)
{
void* dest;
res->lock(0, numIndices, &dest);
dMemcpy(dest, data, sizeof(U16) * numIndices);
res->unlock();
}
return res;
}
GFXVertexBuffer * GFXD3D11Device::allocVertexBuffer(U32 numVerts, const GFXVertexFormat *vertexFormat, U32 vertSize, GFXBufferType bufferType, void *data)
{
PROFILE_SCOPE( GFXD3D11Device_allocVertexBuffer );
GFXD3D11VertexBuffer *res = new GFXD3D11VertexBuffer( this,
numVerts,
vertexFormat,
vertSize,
bufferType );
// Determine usage flags
D3D11_USAGE usage = D3D11_USAGE_DEFAULT;
res->mNumVerts = 0;
// Assumptions:
// - static buffers are write once, use many
// - dynamic buffers are write many, use many
// - volatile buffers are write once, use once
// You may never read from a buffer.
//TODO: enable proper support for D3D11_USAGE_IMMUTABLE
switch(bufferType)
{
case GFXBufferTypeImmutable:
case GFXBufferTypeStatic:
usage = D3D11_USAGE_DEFAULT;
break;
case GFXBufferTypeDynamic:
case GFXBufferTypeVolatile:
usage = D3D11_USAGE_DYNAMIC;
break;
}
// Register resource
res->registerResourceWithDevice(this);
// Create vertex buffer
if(bufferType == GFXBufferTypeVolatile)
{
// NOTE: Volatile VBs are pooled and will be allocated at lock time.
AssertFatal(numVerts <= MAX_DYNAMIC_VERTS, "GFXD3D11Device::allocVertexBuffer - Volatile vertex buffer is too big... see MAX_DYNAMIC_VERTS!");
}
else
{
// Requesting it will allocate it.
vertexFormat->getDecl(); //-ALEX disabled to postpone until after shader is actually set...
// Get a new buffer...
D3D11_BUFFER_DESC desc;
desc.ByteWidth = vertSize * numVerts;
desc.Usage = usage;
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
if(bufferType == GFXBufferTypeDynamic)
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // We never allow reading from a vertex buffer.
else
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
desc.StructureByteStride = 0;
HRESULT hr = D3D11DEVICE->CreateBuffer(&desc, NULL, &res->vb);
if(FAILED(hr))
{
AssertFatal(false, "Failed to allocate VB");
}
}
res->mNumVerts = numVerts;
if (data)
{
void* dest;
res->lock(0, numVerts, &dest);
dMemcpy(dest, data, vertSize * numVerts);
res->unlock();
}
return res;
}
String GFXD3D11Device::_createTempShaderInternal(const GFXVertexFormat *vertexFormat)
{
U32 elemCount = vertexFormat->getElementCount();
//Input data
StringBuilder inputData;
inputData.append("struct VertIn {");
//Output data
StringBuilder outputData;
outputData.append("struct VertOut {");
// Shader main body data
StringBuilder mainBodyData;
//make shader
mainBodyData.append("VertOut main(VertIn IN){VertOut OUT;");
for (U32 i = 0; i < elemCount; i++)
{
const GFXVertexElement &element = vertexFormat->getElement(i);
String semantic = element.getSemantic();
String semanticOut = semantic;
String type;
if (element.isSemantic(GFXSemantic::POSITION))
{
semantic = "POSITION";
semanticOut = "SV_Position";
}
else if (element.isSemantic(GFXSemantic::NORMAL))
{
semantic = "NORMAL";
semanticOut = semantic;
}
else if (element.isSemantic(GFXSemantic::COLOR))
{
semantic = "COLOR";
semanticOut = semantic;
}
else if (element.isSemantic(GFXSemantic::TANGENT))
{
semantic = "TANGENT";
semanticOut = semantic;
}
else if (element.isSemantic(GFXSemantic::BINORMAL))
{
semantic = "BINORMAL";
semanticOut = semantic;
}
else if (element.isSemantic(GFXSemantic::BLENDINDICES))
{
semantic = String::ToString("BLENDINDICES%d", element.getSemanticIndex());
semanticOut = semantic;
}
else if (element.isSemantic(GFXSemantic::BLENDWEIGHT))
{
semantic = String::ToString("BLENDWEIGHT%d", element.getSemanticIndex());
semanticOut = semantic;
}
else
{
//Anything that falls thru to here will be a texture coord.
semantic = String::ToString("TEXCOORD%d", element.getSemanticIndex());
semanticOut = semantic;
}
switch (GFXD3D11DeclType[element.getType()])
{
case DXGI_FORMAT_R32_FLOAT:
type = "float";
break;
case DXGI_FORMAT_R32G32_FLOAT:
type = "float2";
break;
case DXGI_FORMAT_R32G32B32_FLOAT:
type = "float3";
break;
case DXGI_FORMAT_R32G32B32A32_FLOAT:
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_UNORM:
type = "float4";
break;
case DXGI_FORMAT_R8G8B8A8_UINT:
type = "uint4";
break;
}
StringBuilder in;
in.format("%s %s%d : %s;", type.c_str(), "var", i, semantic.c_str());
inputData.append(in.data());
//SV_Position must be float4
if (semanticOut == String("SV_Position"))
{
StringBuilder out;
out.format("float4 %s%d : %s;", "var", i, semanticOut.c_str());
outputData.append(out.data());
StringBuilder body;
body.format("OUT.%s%d = float4(IN.%s%d.xyz,1);", "var", i, "var", i);
mainBodyData.append(body.data());
}
else
{
StringBuilder out;
out.format("%s %s%d : %s;", type.c_str(), "var", i, semanticOut.c_str());
outputData.append(out.data());
StringBuilder body;
body.format("OUT.%s%d = IN.%s%d;", "var", i, "var", i);
mainBodyData.append(body.data());
}
}
inputData.append("};");
outputData.append("};");
mainBodyData.append("return OUT;}");
//final data
StringBuilder finalData;
finalData.append(inputData.data());
finalData.append(outputData.data());
finalData.append(mainBodyData.data());
return String(finalData.data());
}
GFXVertexDecl* GFXD3D11Device::allocVertexDecl( const GFXVertexFormat *vertexFormat )
{
PROFILE_SCOPE( GFXD3D11Device_allocVertexDecl );
// First check the map... you shouldn't allocate VBs very often
// if you want performance. The map lookup should never become
// a performance bottleneck.
D3D11VertexDecl *decl = mVertexDecls[vertexFormat->getDescription()];
if ( decl )
return decl;
U32 elemCount = vertexFormat->getElementCount();
ID3DBlob* code = NULL;
// We have to generate a temporary shader here for now since the input layout creation
// expects a shader to be already compiled to verify the vertex layout structure. The problem
// is that most of the time the regular shaders are compiled AFTER allocVertexDecl is called.
if(!decl)
{
//TODO: Perhaps save/cache the ID3DBlob for later use on identical vertex formats,save creating/compiling the temp shader everytime
String shaderData = _createTempShaderInternal(vertexFormat);
#ifdef TORQUE_DEBUG
U32 flags = D3DCOMPILE_DEBUG | D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS;
#else
U32 flags = D3DCOMPILE_ENABLE_STRICTNESS;
#endif
ID3DBlob *errorBlob = NULL;
HRESULT hr = D3DCompile(shaderData.c_str(), shaderData.length(), NULL, NULL, NULL, "main", "vs_5_0", flags, 0, &code, &errorBlob);
StringBuilder error;
if(errorBlob)
{
error.append((char*)errorBlob->GetBufferPointer(), errorBlob->GetBufferSize());
AssertFatal(hr, error.data());
}
SAFE_RELEASE(errorBlob);
}
AssertFatal(code, "D3D11Device::allocVertexDecl - compiled vert shader code missing!");
// Setup the declaration struct.
U32 stream;
D3D11_INPUT_ELEMENT_DESC *vd = new D3D11_INPUT_ELEMENT_DESC[ elemCount];
S32 elemIndex = 0;
for (S32 i = 0; i < elemCount; i++, elemIndex++)
{
const GFXVertexElement &element = vertexFormat->getElement(elemIndex);
stream = element.getStreamIndex();
vd[i].InputSlot = stream;
vd[i].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
vd[i].Format = GFXD3D11DeclType[element.getType()];
// If instancing is enabled, the per instance data is only used on stream 1.
if (vertexFormat->hasInstancing() && stream == 1)
{
vd[i].InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA;
vd[i].InstanceDataStepRate = 1;
}
else
{
vd[i].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
vd[i].InstanceDataStepRate = 0;
}
// We force the usage index of 0 for everything but
// texture coords for now... this may change later.
vd[i].SemanticIndex = 0;
if (element.isSemantic(GFXSemantic::POSITION))
vd[i].SemanticName = "POSITION";
else if (element.isSemantic(GFXSemantic::NORMAL))
vd[i].SemanticName = "NORMAL";
else if (element.isSemantic(GFXSemantic::COLOR))
vd[i].SemanticName = "COLOR";
else if (element.isSemantic(GFXSemantic::TANGENT))
vd[i].SemanticName = "TANGENT";
else if (element.isSemantic(GFXSemantic::BINORMAL))
vd[i].SemanticName = "BINORMAL";
else if (element.isSemantic(GFXSemantic::BLENDWEIGHT))
{
vd[i].SemanticName = "BLENDWEIGHT";
vd[i].SemanticIndex = element.getSemanticIndex();
}
else if (element.isSemantic(GFXSemantic::BLENDINDICES))
{
vd[i].SemanticName = "BLENDINDICES";
vd[i].SemanticIndex = element.getSemanticIndex();
}
else
{
//Anything that falls thru to here will be a texture coord.
vd[i].SemanticName = "TEXCOORD";
vd[i].SemanticIndex = element.getSemanticIndex();
}
}
decl = new D3D11VertexDecl();
HRESULT hr = mD3DDevice->CreateInputLayout(vd, elemCount,code->GetBufferPointer(), code->GetBufferSize(), &decl->decl);
if (FAILED(hr))
{
AssertFatal(false, "GFXD3D11Device::allocVertexDecl - Failed to create vertex input layout!");
}
delete [] vd;
SAFE_RELEASE(code);
// Store it in the cache.
mVertexDecls[vertexFormat->getDescription()] = decl;
return decl;
}
void GFXD3D11Device::setVertexDecl( const GFXVertexDecl *decl )
{
ID3D11InputLayout *dx11Decl = NULL;
if (decl)
dx11Decl = static_cast<const D3D11VertexDecl*>(decl)->decl;
mD3DDeviceContext->IASetInputLayout(dx11Decl);
}
//-----------------------------------------------------------------------------
// This function should ONLY be called from GFXDevice::updateStates() !!!
//-----------------------------------------------------------------------------
void GFXD3D11Device::setTextureInternal( U32 textureUnit, const GFXTextureObject *texture)
{
if( texture == NULL )
{
ID3D11ShaderResourceView *pView = NULL;
mD3DDeviceContext->PSSetShaderResources(textureUnit, 1, &pView);
return;
}
GFXD3D11TextureObject *tex = (GFXD3D11TextureObject*)(texture);
mD3DDeviceContext->PSSetShaderResources(textureUnit, 1, tex->getSRViewPtr());
}
GFXFence *GFXD3D11Device::createFence()
{
// Figure out what fence type we should be making if we don't know
if( mCreateFenceType == -1 )
{
D3D11_QUERY_DESC desc;
desc.MiscFlags = 0;
desc.Query = D3D11_QUERY_EVENT;
ID3D11Query *testQuery = NULL;
HRESULT hRes = mD3DDevice->CreateQuery(&desc, &testQuery);
if(FAILED(hRes))
{
mCreateFenceType = true;
}
else
{
mCreateFenceType = false;
}
SAFE_RELEASE(testQuery);
}
// Cool, use queries
if(!mCreateFenceType)
{
GFXFence* fence = new GFXD3D11QueryFence( this );
fence->registerResourceWithDevice(this);
return fence;
}
// CodeReview: At some point I would like a specialized implementation of
// the method used by the general fence, only without the overhead incurred
// by using the GFX constructs. Primarily the lock() method on texture handles
// will do a data copy, and this method doesn't require a copy, just a lock
// [5/10/2007 Pat]
GFXFence* fence = new GFXGeneralFence( this );
fence->registerResourceWithDevice(this);
return fence;
}
GFXOcclusionQuery* GFXD3D11Device::createOcclusionQuery()
{
GFXOcclusionQuery *query;
if (mOcclusionQuerySupported)
query = new GFXD3D11OcclusionQuery( this );
else
return NULL;
query->registerResourceWithDevice(this);
return query;
}
GFXCubemap * GFXD3D11Device::createCubemap()
{
GFXD3D11Cubemap* cube = new GFXD3D11Cubemap();
cube->registerResourceWithDevice(this);
return cube;
}
// Debug events
//------------------------------------------------------------------------------
void GFXD3D11Device::enterDebugEvent(ColorI color, const char *name)
{
if (mUserAnnotation)
{
WCHAR eventName[260];
MultiByteToWideChar(CP_ACP, 0, name, -1, eventName, 260);
mUserAnnotation->BeginEvent(eventName);
}
}
//------------------------------------------------------------------------------
void GFXD3D11Device::leaveDebugEvent()
{
if (mUserAnnotation)
mUserAnnotation->EndEvent();
}
//------------------------------------------------------------------------------
void GFXD3D11Device::setDebugMarker(ColorI color, const char *name)
{
if (mUserAnnotation)
{
WCHAR eventName[260];
MultiByteToWideChar(CP_ACP, 0, name, -1, eventName, 260);
mUserAnnotation->SetMarker(eventName);
}
}