static ULONG __stdcall myRelease(IDirect3DDevice9 *idd) { Mutex m; DevMapType::iterator it = devMap.find(idd); DevState *ds = it != devMap.end() ? it->second : NULL; if (ds) { // Release is called very often. Thus, we do not want to always log here. #ifdef EXTENDED_OVERLAY_DEBUGOUTPUT ods("D3D9: Using own Refcount implementation for call to Release."); #endif if (ds->dwMyThread == GetCurrentThreadId()) { ds->myRefCount--; return ds->initRefCount + ds->refCount; } else { ds->refCount--; } if (ds->refCount <= 1) { ds->disconnect(); } if (ds->refCount >= 0) { return ds->initRefCount + ds->refCount; } ods("D3D9: Final release is following. MyRefs = %d, Tot = %d", ds->myRefCount, ds->refCount); if (ds->dwMyThread != 0) { ods("D3D9: finalRelease from other thread"); } // Codeblock for stashing threadid { Stash<DWORD> stashThread(&(ds->dwMyThread), GetCurrentThreadId()); ds->releaseAll(); } ods("D3D9: Final release of %p. MyRefs = %d Tot = %d", idd, ds->myRefCount, ds->refCount); devMap.erase(it); delete ds; ds = NULL; } //TODO: Move logic to HardHook. // Call base without active hook in case of no trampoline. ReleaseType oRelease = (ReleaseType) hhRelease.call; hhRelease.restore(); ULONG res = oRelease(idd); hhRelease.inject(); // Release is called very often. Thus, we do not want to always log here. #ifdef EXTENDED_OVERLAY_DEBUGOUTPUT ods("D3D9: Chained Release with result %d", res); #endif return res; }
static ULONG __stdcall myWin8AddRef(IDirect3DDevice9 *idd) { Mutex m; DevMapType::iterator it = devMap.find(idd); DevState *ds = it != devMap.end() ? it->second : NULL; if (ds && ds->dwMyThread == GetCurrentThreadId()) { // AddRef is called very often. Thus, we do not want to always log here. #ifdef EXTENDED_OVERLAY_DEBUGOUTPUT ods("D3D9: Using own Refcount implementation for call to AddRef (Win8)."); #endif ds->myRefCount++; return ds->refCount; } //TODO: Move logic to HardHook. // Call base without active hook in case of no trampoline. AddRefType oAddRef = (AddRefType) hhAddRef.call; hhAddRef.restore(); ULONG res = oAddRef(idd); hhAddRef.inject(); if (ds) ds->refCount = res; // AddRef is called very often. Thus, we do not want to always log here. #ifdef EXTENDED_OVERLAY_DEBUGOUTPUT ods("D3D9: Chained AddRef (Win8) with result %d", res); #endif return res; }
static HRESULT __stdcall myResetEx(IDirect3DDevice9Ex *idd, D3DPRESENT_PARAMETERS *param, D3DDISPLAYMODEEX *param2) { ods("D3D9: Chaining ResetEx"); DevMapType::iterator it = devMap.find(idd); DevState *ds = it != devMap.end() ? it->second : NULL; if (ds) { if (ds->dwMyThread) { ods("D3D9: myResetEx from other thread"); } Stash<DWORD> stashThread(&(ds->dwMyThread), GetCurrentThreadId()); ds->releaseAll(); } //TODO: Move logic to HardHook. // Call base without active hook in case of no trampoline. ResetExType oResetEx = (ResetExType) hhResetEx.call; hhResetEx.restore(); HRESULT hr = oResetEx(idd, param, param2); hhResetEx.inject(); if (ds) ds->createCleanState(); return hr; }
static void doPresent(IDirect3DDevice9 *idd) { DevMapType::iterator it = devMap.find(idd); DevState *ds = it != devMap.end() ? it->second : NULL; if (ds && ds->pSB) { if (ds->dwMyThread != 0) { ods("D3D9: doPresent from other thread"); } Stash<DWORD> stashThread(&(ds->dwMyThread), GetCurrentThreadId()); IDirect3DSurface9 *pTarget = NULL; IDirect3DSurface9 *pRenderTarget = NULL; idd->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pTarget); idd->GetRenderTarget(0, &pRenderTarget); // Present is called for each frame. Thus, we do not want to always log here. #ifdef EXTENDED_OVERLAY_DEBUGOUTPUT ods("D3D9: doPresent BackB %p RenderT %p", pTarget, pRenderTarget); #endif IDirect3DStateBlock9* pStateBlock = NULL; idd->CreateStateBlock(D3DSBT_ALL, &pStateBlock); pStateBlock->Capture(); ds->pSB->Apply(); if (pTarget != pRenderTarget) idd->SetRenderTarget(0, pTarget); idd->BeginScene(); ds->draw(); idd->EndScene(); pStateBlock->Apply(); pStateBlock->Release(); pRenderTarget->Release(); pTarget->Release(); // ods("D3D9: Finished ref is %d %d", ds->myRefCount, ds->refCount); } }
static HRESULT __stdcall myCreateDeviceEx(IDirect3D9Ex *id3d, UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS *pPresentationParameters, D3DDISPLAYMODEEX *pFullscreenDisplayMode, IDirect3DDevice9Ex **ppReturnedDeviceInterface) { Mutex m; ods("D3D9: Chaining CreateDeviceEx"); // BehaviorFlags &= ~D3DCREATE_PUREDEVICE; //TODO: Move logic to HardHook. // Call base without active hook in case of no trampoline. CreateDeviceExType oCreateDeviceEx = (CreateDeviceExType) hhCreateDeviceEx.call; hhCreateDeviceEx.restore(); HRESULT hr = oCreateDeviceEx(id3d, Adapter, DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters, pFullscreenDisplayMode, ppReturnedDeviceInterface); hhCreateDeviceEx.inject(); if (FAILED(hr)) return hr; IDirect3DDevice9Ex *idd = *ppReturnedDeviceInterface; DevState *ds = new DevState; ds->dev = idd; idd->AddRef(); ds->initRefCount = idd->Release(); DevMapType::iterator it = devMap.find(idd); if (it != devMap.end()) { ods("Device exists in devMap already - canceling injection into device. Thread prev: %d ; new: %d", it->second->dwMyThread, GetCurrentThreadId()); delete ds; return hr; } devMap[idd] = ds; // The offsets are dependent on the declaration order of the struct. // See IDirect3DDevice9 (2nd, 3rd, 17th, 18th functions) const unsigned int offsetAddref = 1; const unsigned int offsetRelease = 2; const unsigned int offsetReset = 16; const unsigned int offsetPresent = 17; // On IDirect3DDevice9Ex const unsigned int offsetPresentEx = 121; const unsigned int offsetResetEx = 132; if (bIsWin8) { hhAddRef.setupInterface(idd, offsetAddref, reinterpret_cast<voidFunc>(myWin8AddRef)); hhRelease.setupInterface(idd, offsetRelease, reinterpret_cast<voidFunc>(myWin8Release)); } else { hhAddRef.setupInterface(idd, offsetAddref, reinterpret_cast<voidFunc>(myAddRef)); hhRelease.setupInterface(idd, offsetRelease, reinterpret_cast<voidFunc>(myRelease)); } hhReset.setupInterface(idd, offsetReset, reinterpret_cast<voidFunc>(myReset)); hhResetEx.setupInterface(idd, offsetResetEx, reinterpret_cast<voidFunc>(myResetEx)); hhPresent.setupInterface(idd, offsetPresent, reinterpret_cast<voidFunc>(myPresent)); hhPresentEx.setupInterface(idd, offsetPresentEx, reinterpret_cast<voidFunc>(myPresentEx)); IDirect3DSwapChain9 *pSwap = NULL; idd->GetSwapChain(0, &pSwap); if (pSwap) { // The offset is dependent on the declaration order of the struct. // See IDirect3DSwapChain9 (Present is the fourth function) const unsigned int offsetPresent = 3; hhSwapPresent.setupInterface(pSwap, offsetPresent, reinterpret_cast<voidFunc>(mySwapPresent)); pSwap->Release(); } else { ods("D3D9: Failed to get swapchain for DevEx"); } ds->createCleanState(); return hr; }
/// Present the overlay. /// /// If called via IDirect3DDevice9::present() or IDirect3DDevice9Ex::present(), /// idd will be non-NULL and ids ill be NULL. /// /// If called via IDirect3DSwapChain9::present(), both idd and ids will be /// non-NULL. /// /// The doPresent function expects the following assumptions to be valid: /// /// - Only one swap chain used at the same time. /// - Windowed? IDirect3D9SwapChain::present() is used. ("Additional swap chain" is used) /// - Full screen? IDirect3D9Device::present() is used. (Implicit swap chain for IDirect3D9Device is used) /// /// It's either/or. /// /// If doPresent is called multiple times per frame (say, for different swap chains), /// the overlay will break badly when DevState::draw() is called. FPS counting will be off, /// different render targets with different sizes will cause a size-renegotiation with Mumble /// every frame, etc. static void doPresent(IDirect3DDevice9 *idd, IDirect3DSwapChain9 *ids) { DevMapType::iterator it = devMap.find(idd); DevState *ds = it != devMap.end() ? it->second : NULL; HRESULT hres; if (ds && ds->pSB) { if (ds->dwMyThread != 0) { ods("D3D9: doPresent from other thread"); } Stash<DWORD> stashThread(&(ds->dwMyThread), GetCurrentThreadId()); // Get the back buffer. // If we're called via IDirect3DSwapChain9, acquire it via the swap chain object. // Otherwise, acquire it via the device itself. IDirect3DSurface9 *pTarget = NULL; if (ids) { hres = ids->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pTarget); if (FAILED(hres)) { if (hres == D3DERR_INVALIDCALL) { ods("D3D9: IDirect3DSwapChain9::GetBackBuffer failed. BackBuffer index equals or exceeds the total number of back buffers"); } else { ods("D3D9: IDirect3DSwapChain9::GetBackBuffer failed"); } } } else { hres = idd->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pTarget); if (FAILED(hres)) { if (hres == D3DERR_INVALIDCALL) { ods("D3D9: IDirect3DDevice9::GetBackBuffer failed. BackBuffer index equals or exceeds the total number of back buffers"); } else { ods("D3D9: IDirect3DDevice9::GetBackBuffer failed"); } } } IDirect3DSurface9 *pRenderTarget = NULL; hres = idd->GetRenderTarget(0, &pRenderTarget); if (FAILED(hres)) { if (hres == D3DERR_NOTFOUND) { ods("D3D9: IDirect3DDevice9::GetRenderTarget failed. There is no render target with the specified index"); } else if (hres == D3DERR_INVALIDCALL) { ods("D3D9: IDirect3DDevice9::GetRenderTarget failed. One of the passed arguments was invalid"); } else { ods("D3D9: IDirect3DDevice9::GetRenderTarget failed"); } } // Present is called for each frame. Thus, we do not want to always log here. #ifdef EXTENDED_OVERLAY_DEBUGOUTPUT ods("D3D9: doPresent BackB %p RenderT %p", pTarget, pRenderTarget); #endif IDirect3DStateBlock9* pStateBlock = NULL; idd->CreateStateBlock(D3DSBT_ALL, &pStateBlock); pStateBlock->Capture(); ds->pSB->Apply(); if (pTarget != pRenderTarget) { hres = idd->SetRenderTarget(0, pTarget); if (FAILED(hres)) { ods("D3D9: IDirect3DDevice9::SetRenderTarget failed"); } } // If we're called via IDirect3DSwapChain9::present(), we have to // get the size of the back buffer and manually set the viewport size // to match it. // // Although the call to IDirect3DDevice9::SetRenderTarget() above is // documented as updating the device's viewport: // // "Setting a new render target will cause the viewport (see Viewports // and Clipping (Direct3D 9)) to be set to the full size of the new // render target." // // (via https://msdn.microsoft.com/en-us/library/windows/desktop/bb174455(v=vs.85).aspx) // // ...this doesn't happen. At least for some programs such as Final Fantasy XIV // and Battle.net Launcher. For these programs, we get a viewport of 1x1. // // The viewport we set here is used in the call below to DevState::draw() // as the full size of the screen/window. if (ids) { D3DPRESENT_PARAMETERS pp; hres = ids->GetPresentParameters(&pp); if (FAILED(hres)) { ods("D3D9: IDirect3DSwapChain9::GetPresentParameters failed"); } else { if (pp.BackBufferWidth != 0 && pp.BackBufferHeight != 0) { D3DVIEWPORT9 vp; vp.X = 0; vp.Y = 0; vp.Width = pp.BackBufferWidth; vp.Height = pp.BackBufferHeight; vp.MinZ = 0.0f; vp.MaxZ = 1.0f; idd->SetViewport(&vp); } } } idd->BeginScene(); ds->draw(); idd->EndScene(); pStateBlock->Apply(); pStateBlock->Release(); pRenderTarget->Release(); pTarget->Release(); // ods("D3D9: Finished ref is %d %d", ds->myRefCount, ds->refCount); } }