//----------------------------------------------------------------------------- // 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" #include //d3dperf #include "gfxD3D11TextureArray.h" #ifdef TORQUE_DEBUG #include "d3d11sdklayers.h" #endif #pragma comment(lib, "dxgi.lib") #pragma comment(lib, "d3d9.lib") //d3dperf #pragma comment(lib, "d3d11.lib") class GFXD3D11TextureArray; 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; mSwapChain = NULL; mD3DDevice = NULL; mD3DDeviceContext = NULL; mVolatileVB = NULL; mCurrentPB = NULL; mDynamicPB = NULL; mLastVertShader = NULL; mLastPixShader = NULL; mCanCurrentlyRender = false; mTextureManager = NULL; mCurrentStateBlock = NULL; mResourceListHead = NULL; mPixVersion = 0.0; mFeatureLevel = D3D_FEATURE_LEVEL_9_1; //lowest listed. should be overridden by init 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; mMultisampleDesc.Count = 0; mMultisampleDesc.Quality = 0; mOcclusionQuerySupported = 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(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(mD3DDevice); } GFXFormat GFXD3D11Device::selectSupportedFormat(GFXTextureProfile *profile, const Vector &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 = smEnableVSync ? 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 &adapterList) { IDXGIAdapter1* EnumAdapter; IDXGIFactory1* DXGIFactory; CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast(&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_1) suitable = false; //double check we support required bgra format for LEVEL_10_1 if (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(&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 ); //SetFocus(winHwnd); UINT createDeviceFlags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT; #ifdef TORQUE_DEBUG createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; mDebugLayers = true; #endif // 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 }; 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 = D3D11CreateDevice(NULL, driverType, NULL, createDeviceFlags, pFeatureLevels, nFeatureCount, D3D11_SDK_VERSION, &mD3DDevice, &mFeatureLevel, &mD3DDeviceContext); if(FAILED(hres)) { #ifdef TORQUE_DEBUG //try again without debug device layer enabled createDeviceFlags &= ~D3D11_CREATE_DEVICE_DEBUG; hres = D3D11CreateDevice(NULL, driverType, NULL, createDeviceFlags, pFeatureLevels, nFeatureCount, D3D11_SDK_VERSION, &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 } //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!"); } } #ifdef TORQUE_DEBUG _suppressDebugMessages(); #endif 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; 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(); 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!"); // Set up a new window target... GFXD3D11WindowTarget *gdwt = new GFXD3D11WindowTarget(); gdwt->mWindow = window; gdwt->mSize = window->getClientExtent(); gdwt->initPresentationParams(); if (!mInitialized) { gdwt->mSecondaryWindow = false; // Allocate the device. init(window->getVideoMode(), window); gdwt->initPresentationParams(); gdwt->createSwapChain(); gdwt->createBuffersAndViews(); mSwapChain = gdwt->getSwapChain(); mDeviceBackbuffer = gdwt->getBackBuffer(); mDeviceDepthStencil = gdwt->getDepthStencil(); mDeviceBackBufferView = gdwt->getBackBufferView(); mDeviceDepthStencilView = gdwt->getDepthStencilView(); } else //additional window/s { gdwt->mSecondaryWindow = true; gdwt->initPresentationParams(); gdwt->createSwapChain(); gdwt->createBuffersAndViews(); } return gdwt; } GFXTextureTarget* GFXD3D11Device::allocRenderToTextureTarget(bool genMips) { GFXD3D11TextureTarget *targ = new GFXD3D11TextureTarget(genMips); targ->registerResourceWithDevice(this); return targ; } void GFXD3D11Device::beginReset() { if (!mD3DDevice) return; mInitialized = false; releaseDefaultPoolResources(); // 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; iClearState(); //release old buffers and views SAFE_RELEASE(mDeviceDepthStencilView); SAFE_RELEASE(mDeviceBackBufferView); SAFE_RELEASE(mDeviceDepthStencil); SAFE_RELEASE(mDeviceBackbuffer); } void GFXD3D11Device::endReset(GFXD3D11WindowTarget* windowTarget) { //grab new references mDeviceBackbuffer = windowTarget->getBackBuffer(); mDeviceDepthStencil = windowTarget->getDepthStencil(); mDeviceBackBufferView = windowTarget->getBackBufferView(); mDeviceDepthStencilView = windowTarget->getDepthStencilView(); mD3DDeviceContext->OMSetRenderTargets(1, &mDeviceBackBufferView, mDeviceDepthStencilView); // Now reacquire all the resources we trashed earlier reacquireDefaultPoolResources(); mD3DDeviceContext->PSSetShader(mLastPixShader, NULL, 0); mD3DDeviceContext->VSSetShader(mLastVertShader, NULL, 0); mInitialized = true; // 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(block), "Incorrect stateblock type for this device!"); GFXD3D11StateBlock* d3dBlock = static_cast(block); GFXD3D11StateBlock* d3dCurrent = static_cast(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(buffer), "Incorrect shader const buffer type for this device!"); GFXD3D11ShaderConstBuffer* d3dBuffer = static_cast(buffer); d3dBuffer->activate(mCurrentConstBuffer); mCurrentConstBuffer = d3dBuffer; } else { mCurrentConstBuffer = NULL; } } //----------------------------------------------------------------------------- void GFXD3D11Device::copyResource(GFXTextureObject *pDst, GFXCubemap *pSrc, const U32 face) { AssertFatal(pDst, "GFXD3D11Device::copyResource: Destination texture is null"); AssertFatal(pSrc, "GFXD3D11Device::copyResource: Source cubemap is null"); GFXD3D11TextureObject *pD3DDst = static_cast(pDst); GFXD3D11Cubemap *pD3DSrc = static_cast(pSrc); const U32 mipLevels = pD3DSrc->getMipMapLevels(); for (U32 mip = 0; mip < mipLevels; mip++) { const U32 srcSubResource = D3D11CalcSubresource(mip, face, mipLevels); const U32 dstSubResource = D3D11CalcSubresource(mip, 0, mipLevels); mD3DDeviceContext->CopySubresourceRegion(pD3DDst->get2DTex(), dstSubResource, 0, 0, 0, pD3DSrc->get2DTex(), srcSubResource, 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; //TODO: current support is 5 render targets, clean this up ID3D11RenderTargetView* rtView[5] = { NULL }; ID3D11DepthStencilView* dsView = NULL; mD3DDeviceContext->OMGetRenderTargets(5, rtView, &dsView); const FLOAT clearColor[4] = { color.red, color.green, color.blue, color.alpha }; if (flags & GFXClearTarget && rtView) { for (U32 i = 0; i < 5; i++) { if (rtView[i]) mD3DDeviceContext->ClearRenderTargetView(rtView[i], clearColor); } } if (flags & GFXClearZBuffer) depthstencilFlag |= D3D11_CLEAR_DEPTH; if (flags & GFXClearStencil) depthstencilFlag |= D3D11_CLEAR_STENCIL; if (depthstencilFlag && dsView) mD3DDeviceContext->ClearDepthStencilView(dsView, depthstencilFlag, z, stencil); for (U32 i = 0; i < 5; i++) SAFE_RELEASE(rtView[i]); SAFE_RELEASE(dsView); } void GFXD3D11Device::clearColorAttachment(const U32 attachment, const LinearColorF& color) { GFXD3D11TextureTarget *pTarget = static_cast(mCurrentRT.getPointer()); ID3D11RenderTargetView* rtView = NULL; if (!pTarget) { rtView = mDeviceBackBufferView;// we are using the default backbuffer } else { //attachment + 1 to skip past DepthStencil which is first in the list rtView = static_cast(pTarget->mTargetViews[attachment + 1]); } const FLOAT clearColor[4] = { color.red, color.green, color.blue, color.alpha }; mD3DDeviceContext->ClearRenderTargetView(rtView, clearColor); } 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; ivb); 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) * GFX_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; imVertexFormat.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 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 * GFX_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( 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( 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(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 < GFX_MAX_DYNAMIC_INDICES, "Cannot allocate that many indices in a volatile buffer, increase GFX_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 <= GFX_MAX_DYNAMIC_VERTS, "GFXD3D11Device::allocVertexBuffer - Volatile vertex buffer is too big... see GFX_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(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; } GFXCubemapArray * GFXD3D11Device::createCubemapArray() { GFXD3D11CubemapArray* cubeArray = new GFXD3D11CubemapArray(); cubeArray->registerResourceWithDevice(this); return cubeArray; } GFXTextureArray * GFXD3D11Device::createTextureArray() { GFXD3D11TextureArray* textureArray = new GFXD3D11TextureArray(); textureArray->registerResourceWithDevice(this); return textureArray; } // Debug events //------------------------------------------------------------------------------ void GFXD3D11Device::enterDebugEvent(ColorI color, const char *name) { // BJGFIX WCHAR eventName[260]; MultiByteToWideChar(CP_ACP, 0, name, -1, eventName, 260); D3DPERF_BeginEvent(D3DCOLOR_ARGB(color.alpha, color.red, color.green, color.blue), (LPCWSTR)&eventName); } //------------------------------------------------------------------------------ void GFXD3D11Device::leaveDebugEvent() { D3DPERF_EndEvent(); } //------------------------------------------------------------------------------ void GFXD3D11Device::setDebugMarker(ColorI color, const char *name) { // BJGFIX WCHAR eventName[260]; MultiByteToWideChar(CP_ACP, 0, name, -1, eventName, 260); D3DPERF_SetMarker(D3DCOLOR_ARGB(color.alpha, color.red, color.green, color.blue), (LPCWSTR)&eventName); } const char* GFXD3D11Device::interpretDebugResult(long result) { const char* error; switch (result) { case S_OK: error = "S_OK"; break; case S_FALSE: error = "S_FALSE"; break; //generics case E_UNEXPECTED: error = "E_UNEXPECTED"; break; case E_NOTIMPL: error = "E_NOTIMPL"; break; case E_OUTOFMEMORY: error = "E_OUTOFMEMORY"; break; case E_INVALIDARG: error = "E_INVALIDARG"; break; case E_NOINTERFACE: error = "E_NOINTERFACE"; break; case E_POINTER: error = "E_POINTER"; break; case E_HANDLE: error = "E_HANDLE"; break; case E_ABORT: error = "E_ABORT"; break; case E_FAIL: error = "E_FAIL"; break; case E_ACCESSDENIED: error = "E_ACCESSDENIED"; break; //graphics specific case DXGI_ERROR_INVALID_CALL: error = "DXGI_ERROR_INVALID_CALL"; break; case DXGI_ERROR_WAS_STILL_DRAWING: error = "DXGI_ERROR_WAS_STILL_DRAWING"; break; //dx11 specific case D3D11_ERROR_FILE_NOT_FOUND: error = "D3D11_ERROR_FILE_NOT_FOUND"; break; case D3D11_ERROR_TOO_MANY_UNIQUE_STATE_OBJECTS: error = "D3D11_ERROR_TOO_MANY_UNIQUE_STATE_OBJECTS"; break; case D3D11_ERROR_TOO_MANY_UNIQUE_VIEW_OBJECTS: error = "D3D11_ERROR_TOO_MANY_UNIQUE_VIEW_OBJECTS"; break; case D3D11_ERROR_DEFERRED_CONTEXT_MAP_WITHOUT_INITIAL_DISCARD: error = "D3D11_ERROR_DEFERRED_CONTEXT_MAP_WITHOUT_INITIAL_DISCARD"; break; default: error = "UNKNOWN"; break; } return error; }