//----------------------------------------------------------------------------- // 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 "platform/platform.h" #include "gfx/D3D11/gfxD3D11Target.h" #include "gfx/D3D11/gfxD3D11Cubemap.h" #include "gfx/D3D11/gfxD3D11EnumTranslate.h" #include "gfx/gfxDebugEvent.h" #include "gfx/gfxStringEnumTranslate.h" #include "windowManager/win32/win32Window.h" GFXD3D11TextureTarget::GFXD3D11TextureTarget(bool genMips) : mTargetSize( Point2I::Zero ), mTargetFormat( GFXFormatR8G8B8A8 ) { for(S32 i=0; imDeviceDepthStencil; mTargetViews[slot] = D3D11->mDeviceDepthStencilView; mTargets[slot]->AddRef(); mTargetViews[slot]->AddRef(); } else { // Cast the texture object to D3D... AssertFatal(static_cast(tex), "GFXD3D11TextureTarget::attachTexture - invalid texture object."); GFXD3D11TextureObject *d3dto = static_cast(tex); // Grab the surface level. if( slot == DepthStencil ) { mTargets[slot] = d3dto->getSurface(); if ( mTargets[slot] ) mTargets[slot]->AddRef(); mTargetViews[slot] = d3dto->getDSView(); if( mTargetViews[slot]) mTargetViews[slot]->AddRef(); } else { // getSurface will almost always return NULL. It will only return non-NULL // if the surface that it needs to render to is different than the mip level // in the actual texture. This will happen with MSAA. if( d3dto->getSurface() == NULL ) { mTargets[slot] = d3dto->get2DTex(); mTargets[slot]->AddRef(); mTargetViews[slot] = d3dto->getRTView(); mTargetViews[slot]->AddRef(); } else { mTargets[slot] = d3dto->getSurface(); mTargets[slot]->AddRef(); mTargetViews[slot]->AddRef(); // Only assign resolve target if d3dto has a surface to give us. // // That usually means there is an MSAA target involved, which is why // the resolve is needed to get the data out of the target. mResolveTargets[slot] = d3dto; if ( tex && slot == Color0 ) { mTargetSize.set( tex->getSize().x, tex->getSize().y ); mTargetFormat = tex->getFormat(); } } } // Update surface size if(slot == Color0) { ID3D11Texture2D *surface = mTargets[Color0]; if ( surface ) { D3D11_TEXTURE2D_DESC sd; surface->GetDesc(&sd); mTargetSize = Point2I(sd.Width, sd.Height); S32 format = sd.Format; if (format == DXGI_FORMAT_R8G8B8A8_TYPELESS || format == DXGI_FORMAT_B8G8R8A8_TYPELESS) { mTargetFormat = GFXFormatR8G8B8A8; return; } GFXREVERSE_LOOKUP( GFXD3D11TextureFormat, GFXFormat, format ); mTargetFormat = (GFXFormat)format; } } } } void GFXD3D11TextureTarget::attachTexture( RenderSlot slot, GFXCubemap *tex, U32 face, U32 mipLevel/*=0*/ ) { GFXDEBUGEVENT_SCOPE( GFXPCD3D11TextureTarget_attachTexture_Cubemap, ColorI::RED ); AssertFatal(slot < MaxRenderSlotId, "GFXD3D11TextureTarget::attachTexture - out of range slot."); // Mark state as dirty so device can know to update. invalidateState(); // Release what we had, it's definitely going to change. SAFE_RELEASE(mTargetViews[slot]); SAFE_RELEASE(mTargets[slot]); SAFE_RELEASE(mTargetSRViews[slot]); mResolveTargets[slot] = NULL; // Cast the texture object to D3D... AssertFatal(!tex || static_cast(tex), "GFXD3DTextureTarget::attachTexture - invalid cubemap object."); if(slot == Color0) { mTargetSize = Point2I::Zero; mTargetFormat = GFXFormatR8G8B8A8; } // Are we clearing? if(!tex) { // Yup - just exit, it'll stay NULL. return; } GFXD3D11Cubemap *cube = static_cast(tex); mTargets[slot] = cube->get2DTex(); mTargets[slot]->AddRef(); mTargetViews[slot] = cube->getRTView(face, mipLevel); mTargetViews[slot]->AddRef(); mTargetSRViews[slot] = cube->getSRView(); mTargetSRViews[slot]->AddRef(); // Update surface size if(slot == Color0) { ID3D11Texture2D *surface = mTargets[Color0]; if ( surface ) { D3D11_TEXTURE2D_DESC sd; surface->GetDesc(&sd); mTargetSize = Point2I(sd.Width, sd.Height); S32 format = sd.Format; GFXREVERSE_LOOKUP( GFXD3D11TextureFormat, GFXFormat, format ); mTargetFormat = (GFXFormat)format; } } } void GFXD3D11TextureTarget::activate() { GFXDEBUGEVENT_SCOPE( GFXPCD3D11TextureTarget_activate, ColorI::RED ); AssertFatal( mTargets[GFXTextureTarget::Color0], "GFXD3D11TextureTarget::activate() - You can never have a NULL primary render target!" ); // Clear the state indicator. stateApplied(); // Now set all the new surfaces into the appropriate slots. ID3D11RenderTargetView* rtViews[MaxRenderSlotId] = { NULL, NULL, NULL, NULL, NULL, NULL }; ID3D11DepthStencilView* dsView = (ID3D11DepthStencilView*)(mTargetViews[GFXTextureTarget::DepthStencil]); for (U32 i = 0; i < 6; i++) { rtViews[i] = (ID3D11RenderTargetView*)mTargetViews[GFXTextureTarget::Color0 + i]; } D3D11DEVICECONTEXT->OMSetRenderTargets(MaxRenderSlotId, rtViews, dsView); } void GFXD3D11TextureTarget::deactivate() { if (!mGenMips) return; //re-gen mip maps for (U32 i = 0; i < 6; i++) { ID3D11ShaderResourceView* pSRView = mTargetSRViews[GFXTextureTarget::Color0 + i]; if (pSRView) D3D11DEVICECONTEXT->GenerateMips(pSRView); } } void GFXD3D11TextureTarget::resolve() { GFXDEBUGEVENT_SCOPE( GFXPCD3D11TextureTarget_resolve, ColorI::RED ); for (U32 i = 0; i < MaxRenderSlotId; i++) { // We use existance @ mResolveTargets as a flag that we need to copy // data from the rendertarget into the texture. if (mResolveTargets[i]) { D3D11_TEXTURE2D_DESC desc; mTargets[i]->GetDesc(&desc); D3D11DEVICECONTEXT->CopySubresourceRegion(mResolveTargets[i]->get2DTex(), 0, 0, 0, 0, mTargets[i], 0, NULL); } } } void GFXD3D11TextureTarget::resolveTo( GFXTextureObject *tex ) { GFXDEBUGEVENT_SCOPE( GFXPCD3D11TextureTarget_resolveTo, ColorI::RED ); if ( mTargets[Color0] == NULL ) return; D3D11_TEXTURE2D_DESC desc; mTargets[Color0]->GetDesc(&desc); D3D11DEVICECONTEXT->CopySubresourceRegion(((GFXD3D11TextureObject*)(tex))->get2DTex(), 0, 0, 0, 0, mTargets[Color0], 0, NULL); } void GFXD3D11TextureTarget::zombify() { for(U32 i = 0; i < MaxRenderSlotId; i++) attachTexture(RenderSlot(i), NULL); } void GFXD3D11TextureTarget::resurrect() { } GFXD3D11WindowTarget::GFXD3D11WindowTarget() { mWindow = NULL; mBackBuffer = NULL; mDepthStencilView = NULL; mDepthStencil = NULL; mBackBufferView = NULL; mSwapChain = NULL; dMemset(&mPresentationParams, 0, sizeof(mPresentationParams)); mSecondaryWindow = false; } GFXD3D11WindowTarget::~GFXD3D11WindowTarget() { SAFE_RELEASE(mDepthStencilView) SAFE_RELEASE(mDepthStencil); SAFE_RELEASE(mBackBufferView); SAFE_RELEASE(mBackBuffer); SAFE_RELEASE(mSwapChain); } void GFXD3D11WindowTarget::initPresentationParams() { // Get some video mode related info. const GFXVideoMode& vm = mWindow->getVideoMode(); HWND hwnd = (HWND)mWindow->getSystemWindow(PlatformWindow::WindowSystem_Windows); // Do some validation... if (vm.fullScreen && mSecondaryWindow) { AssertFatal(false, "GFXD3D11WindowTarget::initPresentationParams - Cannot go fullscreen with secondary window!"); } mPresentationParams = D3D11->setupPresentParams(vm, hwnd); } const Point2I GFXD3D11WindowTarget::getSize() { return mWindow->getVideoMode().resolution; } GFXFormat GFXD3D11WindowTarget::getFormat() { S32 format = mPresentationParams.BufferDesc.Format; GFXREVERSE_LOOKUP( GFXD3D11TextureFormat, GFXFormat, format ); return (GFXFormat)format; } bool GFXD3D11WindowTarget::present() { HRESULT hr = mSwapChain->Present(D3D11->smEnableVSync, 0); if (hr == DXGI_ERROR_DEVICE_REMOVED) { HRESULT result = D3D11->getDevice()->GetDeviceRemovedReason(); if (result == DXGI_ERROR_DEVICE_HUNG) AssertFatal(false,"DXGI_ERROR_DEVICE_HUNG"); else if (result == DXGI_ERROR_DEVICE_REMOVED) AssertFatal(false, "DXGI_ERROR_DEVICE_REMOVED"); else if (result == DXGI_ERROR_DEVICE_RESET) AssertFatal(false, "DXGI_ERROR_DEVICE_RESET"); else if (result == DXGI_ERROR_DRIVER_INTERNAL_ERROR) AssertFatal(false, "DXGI_ERROR_DRIVER_INTERNAL_ERROR"); else if (result == DXGI_ERROR_INVALID_CALL) AssertFatal(false, "DXGI_ERROR_INVALID_CALL"); } return (hr == S_OK); } void GFXD3D11WindowTarget::createSwapChain() { //create dxgi factory & swapchain IDXGIFactory1* DXGIFactory; HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast(&DXGIFactory)); if (FAILED(hr)) AssertFatal(false, "GFXD3D11WindowTarget::createSwapChain - couldn't create dxgi factory."); hr = DXGIFactory->CreateSwapChain(D3D11DEVICE, &mPresentationParams, &mSwapChain); if (FAILED(hr)) AssertFatal(false, "GFXD3D11WindowTarget::createSwapChain - couldn't create swap chain."); SAFE_RELEASE(DXGIFactory); } void GFXD3D11WindowTarget::createBuffersAndViews() { //release old if they exist SAFE_RELEASE(mDepthStencilView); SAFE_RELEASE(mDepthStencil); SAFE_RELEASE(mBackBufferView); SAFE_RELEASE(mBackBuffer); //grab video mode const GFXVideoMode& vm = mWindow->getVideoMode(); //create depth/stencil 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 = vm.resolution.x; desc.Height = vm.resolution.y; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.MiscFlags = 0; HRESULT hr = D3D11DEVICE->CreateTexture2D(&desc, NULL, &mDepthStencil); if (FAILED(hr)) AssertFatal(false, "GFXD3D11WindowTarget::createBuffersAndViews - 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 = D3D11DEVICE->CreateDepthStencilView(mDepthStencil, &depthDesc, &mDepthStencilView); if (FAILED(hr)) AssertFatal(false, "GFXD3D11WindowTarget::createBuffersAndViews - couldn't create depth stencil view"); setBackBuffer(); //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 = D3D11DEVICE->CreateRenderTargetView(mBackBuffer, &RTDesc, &mBackBufferView); if (FAILED(hr)) AssertFatal(false, "GFXD3D11WindowTarget::createBuffersAndViews - couldn't create back buffer target view"); //debug names #ifdef TORQUE_DEBUG if (!mSecondaryWindow) { String backBufferName = "MainBackBuffer"; String depthSteniclName = "MainDepthStencil"; String backBuffViewName = "MainBackBuffView"; String depthStencViewName = "MainDepthView"; mBackBuffer->SetPrivateData(WKPDID_D3DDebugObjectName, backBufferName.size(), backBufferName.c_str()); mDepthStencil->SetPrivateData(WKPDID_D3DDebugObjectName, depthSteniclName.size(), depthSteniclName.c_str()); mDepthStencilView->SetPrivateData(WKPDID_D3DDebugObjectName, depthStencViewName.size(), depthStencViewName.c_str()); mBackBufferView->SetPrivateData(WKPDID_D3DDebugObjectName, backBuffViewName.size(), backBuffViewName.c_str()); } #endif } void GFXD3D11WindowTarget::resetMode() { HRESULT hr; if (mSwapChain) { // The current video settings. DXGI_SWAP_CHAIN_DESC desc; hr = mSwapChain->GetDesc(&desc); if (FAILED(hr)) AssertFatal(false, "GFXD3D11WindowTarget::resetMode - failed to get swap chain description!"); bool fullscreen = !desc.Windowed; Point2I backbufferSize(desc.BufferDesc.Width, desc.BufferDesc.Height); // The settings we are now applying. const GFXVideoMode& vm = mWindow->getVideoMode(); // Early out if none of the settings which require a device reset // have changed. if (backbufferSize == vm.resolution && fullscreen == vm.fullScreen) return; } //release old buffers and views SAFE_RELEASE(mDepthStencilView) SAFE_RELEASE(mDepthStencil); SAFE_RELEASE(mBackBufferView); SAFE_RELEASE(mBackBuffer); if (!mSecondaryWindow) D3D11->beginReset(); mWindow->setSuppressReset(true); // Setup our presentation params. initPresentationParams(); if (!mPresentationParams.Windowed) { mPresentationParams.BufferDesc.RefreshRate.Numerator = 0; mPresentationParams.BufferDesc.RefreshRate.Denominator = 0; hr = mSwapChain->ResizeTarget(&mPresentationParams.BufferDesc); if (FAILED(hr)) AssertFatal(false, "GFXD3D11WindowTarget::resetMode - failed to resize target!"); } hr = mSwapChain->ResizeBuffers(mPresentationParams.BufferCount, mPresentationParams.BufferDesc.Width, mPresentationParams.BufferDesc.Height, mPresentationParams.BufferDesc.Format, mPresentationParams.Windowed ? 0 : DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH); if (FAILED(hr)) AssertFatal(false, "GFXD3D11WindowTarget::resetMode - failed to resize back buffer!"); hr = mSwapChain->SetFullscreenState(!mPresentationParams.Windowed, NULL); if (FAILED(hr)) AssertFatal(false, "GFXD3D11WindowTarget::resetMode - failed to change screen states!"); // Update our size, too. mSize = Point2I(mPresentationParams.BufferDesc.Width, mPresentationParams.BufferDesc.Height); mWindow->setSuppressReset(false); //re-create buffers and views createBuffersAndViews(); if (!mSecondaryWindow) D3D11->endReset(this); } void GFXD3D11WindowTarget::zombify() { SAFE_RELEASE(mBackBuffer); } void GFXD3D11WindowTarget::resurrect() { setBackBuffer(); } void GFXD3D11WindowTarget::setBackBuffer() { if (!mBackBuffer) mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)& mBackBuffer); } void GFXD3D11WindowTarget::activate() { GFXDEBUGEVENT_SCOPE(GFXPCD3D11WindowTarget_activate, ColorI::RED); //clear ther rendertargets first ID3D11RenderTargetView* rtViews[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; D3D11DEVICECONTEXT->OMSetRenderTargets(8, rtViews, NULL); D3D11DEVICECONTEXT->OMSetRenderTargets(1, &mBackBufferView, mDepthStencilView); DXGI_SWAP_CHAIN_DESC pp; mSwapChain->GetDesc(&pp); // Update our video mode here, too. GFXVideoMode vm; vm = mWindow->getVideoMode(); vm.resolution.x = pp.BufferDesc.Width; vm.resolution.y = pp.BufferDesc.Height; vm.fullScreen = !pp.Windowed; mSize = vm.resolution; } void GFXD3D11WindowTarget::resolveTo(GFXTextureObject *tex) { GFXDEBUGEVENT_SCOPE(GFXPCD3D11WindowTarget_resolveTo, ColorI::RED); D3D11_TEXTURE2D_DESC desc; ID3D11Texture2D* surf = ((GFXD3D11TextureObject*)(tex))->get2DTex(); surf->GetDesc(&desc); D3D11DEVICECONTEXT->ResolveSubresource(surf, 0, mBackBuffer, 0, desc.Format); } IDXGISwapChain* GFXD3D11WindowTarget::getSwapChain() { mSwapChain->AddRef(); return mSwapChain; } ID3D11Texture2D* GFXD3D11WindowTarget::getBackBuffer() { mBackBuffer->AddRef(); return mBackBuffer; } ID3D11Texture2D* GFXD3D11WindowTarget::getDepthStencil() { mDepthStencil->AddRef(); return mDepthStencil; } ID3D11RenderTargetView* GFXD3D11WindowTarget::getBackBufferView() { mBackBufferView->AddRef(); return mBackBufferView; } ID3D11DepthStencilView* GFXD3D11WindowTarget::getDepthStencilView() { mDepthStencilView->AddRef(); return mDepthStencilView; }