diff options
author | Kissaki <kissaki@gmx.de> | 2013-12-01 01:00:56 +0400 |
---|---|---|
committer | Kissaki <kissaki@gmx.de> | 2014-01-11 01:51:11 +0400 |
commit | 9544bec1f927cf2df793e4856deaa9894ea8ffc5 (patch) | |
tree | 5b93dd9cee0e8312facd948e3651262c0e21de80 /overlay/d3d11.cpp | |
parent | da6c2444188432bc48edc6be99d8ac5014f54e0a (diff) |
Overlay: Add Direct3D 11 support
* Based on an initial patch by Benjamin Jemlich
* Effects11 code based on changes by nyet
Diffstat (limited to 'overlay/d3d11.cpp')
-rw-r--r-- | overlay/d3d11.cpp | 799 |
1 files changed, 799 insertions, 0 deletions
diff --git a/overlay/d3d11.cpp b/overlay/d3d11.cpp new file mode 100644 index 000000000..d162ae4c2 --- /dev/null +++ b/overlay/d3d11.cpp @@ -0,0 +1,799 @@ +/* Copyright (C) 2010-2013, Benjamin Jemlich <pcgod@users.sourceforge.net> + Copyright (C) 2011-2013, Nye Liu <nyet@nyet.org> + Copyright (C) 2011-2013, Kissaki <kissaki@gmx.de> + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Mumble Developers nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "lib.h" +#include "D11StateBlock.h" +#include "overlay11.hex" +#include "d3dx11effect.h" +#include <d3d11.h> +#include <d3dx11.h> +#include <time.h> + +D3D11Data *d3d11 = NULL; + +static bool bHooked = false; +static HardHook hhPresent; +static HardHook hhResize; +static HardHook hhAddRef; +static HardHook hhRelease; + +typedef HRESULT(__stdcall *CreateDXGIFactory1Type)(REFIID, void **); +typedef HRESULT(__stdcall *D3D11CreateDeviceAndSwapChainType)(IDXGIAdapter *, D3D_DRIVER_TYPE, HMODULE Software, UINT Flags, const D3D_FEATURE_LEVEL *, UINT, UINT, const DXGI_SWAP_CHAIN_DESC *, IDXGISwapChain **, ID3D11Device **, D3D_FEATURE_LEVEL *, ID3D11DeviceContext **); + +typedef ULONG(__stdcall *AddRefType)(ID3D11Device *); +typedef ULONG(__stdcall *ReleaseType)(ID3D11Device *); + +struct SimpleVertex { + D3DXVECTOR3 Pos; + D3DXVECTOR2 Tex; +}; + +static const int VERTEXBUFFER_SIZE = 4 * sizeof(SimpleVertex); + +class D11State: protected Pipe { + public: + ULONG lHighMark; + + LONG initRefCount; + LONG refCount; + LONG myRefCount; + + D3D11_VIEWPORT vp; + + ID3D11Device *pDevice; + ID3D11DeviceContext *pDeviceContext; + bool bDeferredContext; + IDXGISwapChain *pSwapChain; + + D11StateBlock *pOrigStateBlock; + D11StateBlock *pMyStateBlock; + ID3D11RenderTargetView *pRTV; + ID3DX11Effect *pEffect; + ID3DX11EffectTechnique *pTechnique; + ID3DX11EffectShaderResourceVariable * pDiffuseTexture; + ID3D11InputLayout *pVertexLayout; + ID3D11Buffer *pVertexBuffer; + ID3D11Buffer *pIndexBuffer; + ID3D11BlendState *pBlendState; + + ID3D11Texture2D *pTexture; + ID3D11ShaderResourceView *pSRView; + + clock_t timeT; + unsigned int frameCount; + + D11State(IDXGISwapChain *, ID3D11Device *); + virtual ~D11State(); + bool init(); + void draw(); + + virtual void blit(unsigned int x, unsigned int y, unsigned int w, unsigned int h); + virtual void setRect(); + virtual void newTexture(unsigned int w, unsigned int h); +}; + +typedef map<IDXGISwapChain *, D11State *> SwapchainMap; +SwapchainMap chains; +typedef map<ID3D11Device *, D11State *> DeviceMap; +DeviceMap devices; + +D11State::D11State(IDXGISwapChain *pSwapChain, ID3D11Device *pDevice) + : bDeferredContext(false) { + this->pSwapChain = pSwapChain; + this->pDevice = pDevice; + + lHighMark = initRefCount = refCount = myRefCount = 0; + + ZeroMemory(&vp, sizeof(vp)); + + pOrigStateBlock = NULL; + pMyStateBlock = NULL; + pRTV = NULL; + pEffect = NULL; + pTechnique = NULL; + pDiffuseTexture = NULL; + pVertexLayout = NULL; + pVertexBuffer = NULL; + pIndexBuffer = NULL; + pBlendState = NULL; + pTexture = NULL; + pSRView = NULL; + pDeviceContext = NULL; + + timeT = clock(); + frameCount = 0; + + pDevice->AddRef(); + initRefCount = pDevice->Release(); +} + +void D11State::blit(unsigned int x, unsigned int y, unsigned int w, unsigned int h) { + HRESULT hr; + + ods("D3D11: Blit %d %d %d %d", x, y, w, h); + + if (! pTexture || ! pSRView) + return; + + D3D11_MAPPED_SUBRESOURCE mappedTex; + hr = pDeviceContext->Map(pTexture, D3D11CalcSubresource(0, 0, 1), D3D11_MAP_WRITE_DISCARD, 0, &mappedTex); + if (FAILED(hr)) { + ods("D3D11: Failed map"); + } + + UCHAR* pTexels = (UCHAR*)mappedTex.pData; + + for (unsigned int r=0;r< uiHeight; ++r) { + unsigned char *sptr = a_ucTexture + r * uiWidth * 4; + unsigned char *dptr = reinterpret_cast<unsigned char *>(pTexels) + r * mappedTex.RowPitch; + memcpy(dptr, sptr, uiWidth * 4); + } + + pDeviceContext->Unmap(pTexture, D3D11CalcSubresource(0, 0, 1)); +} + +void D11State::setRect() { + HRESULT hr; + + ods("D3D11: SetRect"); + + float w = static_cast<float>(uiWidth); + float h = static_cast<float>(uiHeight); + + float left = static_cast<float>(uiLeft) - 0.5f; + float top = static_cast<float>(uiTop) - 0.5f; + float right = static_cast<float>(uiRight) + 0.5f; + float bottom = static_cast<float>(uiBottom) + 0.5f; + + float texl = (left) / w; + float text = (top) / h; + float texr = (right + 1.0f) / w; + float texb = (bottom + 1.0f) / h; + + left = 2.0f * (left / vp.Width) - 1.0f; + right = 2.0f * (right / vp.Width) - 1.0f; + top = -2.0f * (top / vp.Height) + 1.0f; + bottom = -2.0f * (bottom / vp.Height) + 1.0f; + + ods("D3D11: Vertex (%f %f) (%f %f)", left, top, right, bottom); + + // Create vertex buffer + SimpleVertex vertices[] = { + { D3DXVECTOR3(left, top, 0.5f), D3DXVECTOR2(texl, text) }, + { D3DXVECTOR3(right, top, 0.5f), D3DXVECTOR2(texr, text) }, + { D3DXVECTOR3(right, bottom, 0.5f), D3DXVECTOR2(texr, texb) }, + { D3DXVECTOR3(left, bottom, 0.5f), D3DXVECTOR2(texl, texb) }, + }; + + // map/unmap to temporarily deny GPU access to the resource pVertexBuffer + D3D11_MAPPED_SUBRESOURCE res; + hr = pDeviceContext->Map(pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &res); + + const int verticesSize = sizeof(vertices); + static_assert(verticesSize == VERTEXBUFFER_SIZE, "The vertex buffer size differs from the locally created vertex buffer."); + memcpy(res.pData, vertices, verticesSize); + ods("D3D11: Map: %lx %d", hr, sizeof(vertices)); + pDeviceContext->Unmap(pVertexBuffer, 0); +} + +void D11State::newTexture(unsigned int w, unsigned int h) { + HRESULT hr; + + ods("D3D11: newTex %d %d", w, h); + + if (pTexture) { + pTexture->Release(); + pTexture = NULL; + } + if (pSRView) { + pSRView->Release(); + pSRView = NULL; + } + + D3D11_TEXTURE2D_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + + desc.Width = w; + desc.Height = h; + desc.MipLevels = desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + hr = pDevice->CreateTexture2D(&desc, NULL, &pTexture); + + if (FAILED(hr)) { + pTexture = NULL; + ods("D3D11: Failed to create texture."); + return; + } + + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + ZeroMemory(&srvDesc, sizeof(srvDesc)); + srvDesc.Format = desc.Format; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MostDetailedMip = 0; + srvDesc.Texture2D.MipLevels = desc.MipLevels; + + hr = pDevice->CreateShaderResourceView(pTexture, &srvDesc, &pSRView); + if (FAILED(hr)) { + pSRView = NULL; + pTexture->Release(); + pTexture = NULL; + ods("D3D11: Failed to create resource view."); + return; + } +} + +bool D11State::init() { + HRESULT hr; + + ID3D11Texture2D* pBackBuffer = NULL; + hr = pSwapChain->GetBuffer(0, __uuidof(*pBackBuffer), (LPVOID*)&pBackBuffer); + if (FAILED(hr)) { + ods("D3D11: pSwapChain->GetBuffer failure!"); + return false; + } + + hr = pDevice->CreateDeferredContext(0, &pDeviceContext); + // Depending on the device settings a deferred context may not be createable + // for example if it is a SINGLETHREADED device. + // (See http://msdn.microsoft.com/en-us/library/windows/desktop/ff476505%28v=vs.85%29.aspx) + // We handle the expected failure and failure fallback in the same way - + // by trying to use an immediate context. + if (FAILED(hr) || !pDeviceContext) { + ods("D3D11: Failed to create DeferredContext (0x%x). Getting ImmediateContext", hr); + pDevice->GetImmediateContext(&pDeviceContext); + D11CreateStateBlock(pDeviceContext, &pOrigStateBlock); + D11CreateStateBlock(pDeviceContext, &pMyStateBlock); + + pOrigStateBlock->Capture(); + bDeferredContext = false; + } else { + bDeferredContext = true; + } + + D3D11_TEXTURE2D_DESC backBufferSurfaceDesc; + pBackBuffer->GetDesc(&backBufferSurfaceDesc); + + ZeroMemory(&vp, sizeof(vp)); + vp.Width = static_cast<float>(backBufferSurfaceDesc.Width); + vp.Height = static_cast<float>(backBufferSurfaceDesc.Height); + vp.MinDepth = 0; + vp.MaxDepth = 1; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + pDeviceContext->RSSetViewports(1, &vp); + + hr = pDevice->CreateRenderTargetView(pBackBuffer, NULL, &pRTV); + if (FAILED(hr)) { + ods("D3D11: pDevice->CreateRenderTargetView failed!"); + return false; + } + + pDeviceContext->OMSetRenderTargets(1, &pRTV, NULL); + + // Settings for an "over" operation. + // https://en.wikipedia.org/w/index.php?title=Alpha_compositing&oldid=580659153#Description + D3D11_BLEND_DESC blend; + ZeroMemory(&blend, sizeof(blend)); + blend.RenderTarget[0].BlendEnable = TRUE; + blend.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + blend.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + blend.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blend.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blend.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + blend.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + blend.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + + pDevice->CreateBlendState(&blend, &pBlendState); + if (FAILED(hr)) { + ods("D3D11: pDevice->CreateBlendState failed!"); + return false; + } + + pDeviceContext->OMSetBlendState(pBlendState, NULL, 0xffffffff); + + hr = D3DX11CreateEffectFromMemory((LPCVOID) g_main, sizeof(g_main), 0, pDevice, &pEffect); + if (FAILED(hr)) { + ods("D3D11: D3DX11CreateEffectFromMemory failed!"); + return false; + } + + pTechnique = pEffect->GetTechniqueByName("Render"); + if (pTechnique == NULL) { + ods("D3D11: Could not get technique for name 'Render'"); + return false; + } + + + pDiffuseTexture = pEffect->GetVariableByName("txDiffuse")->AsShaderResource(); + if (pDiffuseTexture == NULL) { + ods("D3D11: Could not get variable by name 'txDiffuse'"); + return false; + } + + pTexture = NULL; + pSRView = NULL; + + // Define the input layout + D3D11_INPUT_ELEMENT_DESC layout[] = { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + + // Create the input layout + D3DX11_PASS_DESC PassDesc; + hr = pTechnique->GetPassByIndex(0)->GetDesc(&PassDesc); + if (FAILED(hr)) { + ods("D3D11: Couldn't get pass description for technique"); + return false; + } + + hr = pDevice->CreateInputLayout(layout, ARRAY_NUM_ELEMENTS(layout), PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &pVertexLayout); + if (FAILED(hr)) { + ods("D3D11: pDevice->CreateInputLayout failure!"); + return false; + } + + pDeviceContext->IASetInputLayout(pVertexLayout); + + D3D11_BUFFER_DESC bd; + ZeroMemory(&bd, sizeof(bd)); + bd.Usage = D3D11_USAGE_DYNAMIC; + bd.ByteWidth = VERTEXBUFFER_SIZE; + bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + bd.MiscFlags = 0; + + hr = pDevice->CreateBuffer(&bd, NULL, &pVertexBuffer); + if (FAILED(hr)) { + ods("D3D11: pDevice->CreateBuffer failure!"); + return false; + } + + DWORD indices[] = { + 0,1,3, + 1,2,3, + }; + + ZeroMemory(&bd, sizeof(bd)); + bd.Usage = D3D11_USAGE_IMMUTABLE; + bd.ByteWidth = sizeof(DWORD) * 6; + bd.BindFlags = D3D11_BIND_INDEX_BUFFER; + bd.CPUAccessFlags = 0; + bd.MiscFlags = 0; + D3D11_SUBRESOURCE_DATA InitData; + ZeroMemory(&InitData, sizeof(InitData)); + InitData.pSysMem = indices; + + hr = pDevice->CreateBuffer(&bd, &InitData, &pIndexBuffer); + if (FAILED(hr)) { + ods("D3D11: pDevice->CreateBuffer failure!"); + return false; + } + + // Set index buffer + pDeviceContext->IASetIndexBuffer(pIndexBuffer, DXGI_FORMAT_R32_UINT, 0); + + // Set primitive topology + pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + if (!bDeferredContext) { + pMyStateBlock->Capture(); + pOrigStateBlock->Apply(); + } + pBackBuffer->Release(); + + return true; +} + +D11State::~D11State() { + if (pBlendState) + pBlendState->Release(); + if (pVertexBuffer) + pVertexBuffer->Release(); + if (pIndexBuffer) + pIndexBuffer->Release(); + if (pVertexLayout) + pVertexLayout->Release(); + if (pEffect) + pEffect->Release(); + if (pRTV) + pRTV->Release(); + + if (pTexture) + pTexture->Release(); + if (pSRView) + pSRView->Release(); + + delete pMyStateBlock; + pMyStateBlock = NULL; + delete pOrigStateBlock; + pOrigStateBlock = NULL; + + if (pDeviceContext) + pDeviceContext->Release(); +} + +void D11State::draw() { + clock_t t = clock(); + float elapsed = static_cast<float>(t - timeT) / CLOCKS_PER_SEC; + ++frameCount; + if (elapsed > OVERLAY_FPS_INTERVAL) { + OverlayMsg om; + om.omh.uiMagic = OVERLAY_MAGIC_NUMBER; + om.omh.uiType = OVERLAY_MSGTYPE_FPS; + om.omh.iLength = sizeof(OverlayMsgFps); + om.omf.fps = frameCount / elapsed; + + sendMessage(om); + + frameCount = 0; + timeT = t; + } + + checkMessage(static_cast<unsigned int>(vp.Width), static_cast<unsigned int>(vp.Height)); + + if (a_ucTexture && pSRView && (uiLeft != uiRight)) { + if (!bDeferredContext) { + pOrigStateBlock->Capture(); + pMyStateBlock->Apply(); + } + + D3DX11_TECHNIQUE_DESC techDesc; + pTechnique->GetDesc(&techDesc); + + // Set vertex buffer + UINT stride = sizeof(SimpleVertex); + UINT offset = 0; + pDeviceContext->IASetVertexBuffers(0, 1, &pVertexBuffer, &stride, &offset); + + HRESULT hr = pDiffuseTexture->SetResource(pSRView); + if (FAILED(hr)) + ods("D3D11: Failed to set resource"); + + for (UINT p = 0; p < techDesc.Passes; ++p) { + pTechnique->GetPassByIndex(p)->Apply(0, pDeviceContext); + pDeviceContext->DrawIndexed(6, 0, 0); + } + + if (bDeferredContext) { + ID3D11CommandList *pCommandList; + pDeviceContext->FinishCommandList(TRUE, &pCommandList); + ID3D11DeviceContext *ctx = NULL; + pDevice->GetImmediateContext(&ctx); + ctx->ExecuteCommandList(pCommandList, TRUE); + ctx->Release(); + pCommandList->Release(); + } else { + pDeviceContext->Flush(); + pMyStateBlock->Capture(); + pOrigStateBlock->Apply(); + } + } +} + + +// D3D11 specific logic for the Present function. +extern void presentD3D11(IDXGISwapChain *pSwapChain) { + + ID3D11Device *pDevice = NULL; + HRESULT hr = pSwapChain->GetDevice(__uuidof(ID3D11Device), (void **) &pDevice); + if (SUCCEEDED(hr) && pDevice) { + SwapchainMap::iterator it = chains.find(pSwapChain); + D11State *ds = it != chains.end() ? it->second : NULL; + if (ds && ds->pDevice != pDevice) { + ods("D3D11: SwapChain device changed"); + devices.erase(ds->pDevice); + delete ds; + ds = NULL; + } + if (ds == NULL) { + ods("D3D11: New state"); + ds = new D11State(pSwapChain, pDevice); + if (!ds->init()) { + pDevice->Release(); + delete ds; + return; + } + + chains[pSwapChain] = ds; + devices[pDevice] = ds; + } + + ds->draw(); + pDevice->Release(); + } else { + #ifdef EXTENDED_OVERLAY_DEBUGOUTPUT + // DXGI is used for multiple D3D versions. Thus, this is expected if + // another version is used (like D3D10). + ods("D3D11: Could not draw because ID3D11Device could not be retrieved."); + #endif + } +} + +extern void resizeD3D11(IDXGISwapChain *pSwapChain) { + // Remove the D11State from our "cache" (= Invalidate) + SwapchainMap::iterator it = chains.find(pSwapChain); + if (it != chains.end()) { + D11State *ds = it->second; + devices.erase(ds->pDevice); + chains.erase(pSwapChain); + delete ds; + } +} + +static ULONG __stdcall myAddRef(ID3D11Device *pDevice) { + //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(pDevice); + hhAddRef.inject(); + + Mutex m; + DeviceMap::iterator it = devices.find(pDevice); + if (it != devices.end()) { + it->second->lHighMark = res; + } + + return res; +} + +static ULONG __stdcall myRelease(ID3D11Device *pDevice) { + //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(pDevice); + hhRelease.inject(); + + Mutex m; + DeviceMap::iterator it = devices.find(pDevice); + if (it != devices.end()) { + D11State *ds = it->second; + // If we are receiving a lot of subsequent releases lets eagerly drop + // our state object. If the device presents something again a state + // object is created again just fine anyway. + if (res < ds->lHighMark / 2) { + ods("D3D11: Deleting resources %u < 0.5 * %u", res, ds->lHighMark); + devices.erase(it); + chains.erase(ds->pSwapChain); + delete ds; + ods("D3D11: Deleted"); + ds = NULL; + } + } + + return res; +} + +static void HookAddRelease(voidFunc vfAdd, voidFunc vfRelease) { + ods("D3D11: Injecting device add/remove"); + hhAddRef.setup(vfAdd, reinterpret_cast<voidFunc>(myAddRef)); + hhRelease.setup(vfRelease, reinterpret_cast<voidFunc>(myRelease)); +} + +void hookD3D11(HMODULE hD3D11, bool preonly); + +void checkDXGI11Hook(bool preonly) { + static bool bCheckHookActive = false; + if (bCheckHookActive) { + ods("D3D11: Recursion in checkDXGI11Hook"); + return; + } + + if (d3d11->iOffsetAddRef == 0 || d3d11->iOffsetRelease == 0) { + return; + } + + bCheckHookActive = true; + + HMODULE hDXGI = GetModuleHandleW(L"DXGI.DLL"); + HMODULE hD3D11 = GetModuleHandleW(L"D3D11.DLL"); + + if (hDXGI && hD3D11) { + if (! bHooked) { + hookD3D11(hD3D11, preonly); + } + } else { + #ifdef EXTENDED_OVERLAY_DEBUGOUTPUT + if (hDXGI) { + ods("D3D11: No DXGI.DLL found as loaded. No hooking at this point."); + } else { + ods("D3D11: No D3D11.DLL found as loaded. No hooking at this point."); + } + #endif + } + + bCheckHookActive = false; +} + +/// @param hD3D11 must be a valid module handle +void hookD3D11(HMODULE hD3D11, bool preonly) { + + // Add a ref to ourselves; we do NOT want to get unloaded directly from this process. + HMODULE hTempSelf = NULL; + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<char *>(&hookD3D11), &hTempSelf); + + bHooked = true; + + const int modulenamesize = MODULEFILEPATH_BUFLEN; + wchar_t modulename[modulenamesize]; + GetModuleFileNameW(hD3D11, modulename, modulenamesize); + + if (_wcsicmp(d3d11->wcFileName, modulename) == 0) { + unsigned char *raw = (unsigned char *) hD3D11; + HookAddRelease((voidFunc)(raw + d3d11->iOffsetAddRef), (voidFunc)(raw + d3d11->iOffsetRelease)); + } else if (! preonly) { + ods("D3D11: Interface changed, can't rawpatch. Current: %ls ; Previously: %ls", modulename, d3d11->wcFileName); + } else { + bHooked = false; + } +} + +/// Prepares DXGI and D3D11 data by trying to determine the module filepath +/// and function offsets in memory. +/// (This data can later be used for hooking / code injection.) +/// +/// Adjusts the data behind the global variables dxgi and d3d11. +void PrepareDXGI11(IDXGIAdapter1* pAdapter, bool initializeDXGIData) { + + if (!dxgi || !d3d11 || !pAdapter) + return; + + ods("D3D11: Preparing static data for DXGI and D3D11 Injection"); + + d3d11->wcFileName[0] = 0; + d3d11->iOffsetAddRef = 0; + d3d11->iOffsetRelease = 0; + + HMODULE hD3D11 = LoadLibrary("D3D11.DLL"); + + if (hD3D11 != NULL) { + + HWND hwnd = CreateWindowW(L"STATIC", L"Mumble DXGI Window", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, 0, + NULL, NULL, 0); + + D3D11CreateDeviceAndSwapChainType pD3D11CreateDeviceAndSwapChain = reinterpret_cast<D3D11CreateDeviceAndSwapChainType>(GetProcAddress(hD3D11, "D3D11CreateDeviceAndSwapChain")); + + DXGI_SWAP_CHAIN_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + + RECT rcWnd; + BOOL success = GetClientRect(hwnd, &rcWnd); + if (success) { + desc.BufferDesc.Width = rcWnd.right - rcWnd.left; + desc.BufferDesc.Height = rcWnd.bottom - rcWnd.top; + + ods("D3D11: Got ClientRect W %d H %d", desc.BufferDesc.Width, desc.BufferDesc.Height); + + desc.BufferDesc.RefreshRate.Numerator = 60; + desc.BufferDesc.RefreshRate.Denominator = 1; + desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + desc.BufferDesc.Scaling = DXGI_MODE_SCALING_CENTERED; + + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + + desc.BufferCount = 2; + + desc.OutputWindow = hwnd; + + desc.Windowed = true; + + desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + + IDXGISwapChain *pSwapChain = NULL; + ID3D11Device *pDevice = NULL; + D3D_FEATURE_LEVEL featureLevel; + ID3D11DeviceContext *pDeviceContext = NULL; + HRESULT hr = pD3D11CreateDeviceAndSwapChain(pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, NULL, 0, D3D11_SDK_VERSION, &desc, &pSwapChain, &pDevice, &featureLevel, &pDeviceContext); + if (FAILED(hr)) + ods("D3D11: pD3D11CreateDeviceAndSwapChain failure!"); + + if (pDevice && pDeviceContext && pSwapChain) { + + // For VC++ the vtable is located at the base addr. of the object and each function entry is a single pointer. Since p.e. the base classes + // of IDXGISwapChain have a total of 8 functions the 8+Xth entry points to the Xth added function in the derived interface. + + void ***vtbl = (void ***) pSwapChain; + + void *pPresent = (*vtbl)[8]; + int offset = GetFnOffsetInModule(reinterpret_cast<voidFunc>(pPresent), dxgi->wcFileName, ARRAY_NUM_ELEMENTS(dxgi->wcFileName), "D3D11", "Present"); + if (offset >= 0) { + if (initializeDXGIData) { + dxgi->iOffsetPresent = offset; + ods("D3D11: Successfully found Present offset: %ls: %d", dxgi->wcFileName, dxgi->iOffsetPresent); + } else { + if (dxgi->iOffsetPresent == offset) { + ods("D3D11: Successfully verified Present offset: %ls: %d", dxgi->wcFileName, dxgi->iOffsetPresent); + } else { + ods("D3D11: Failed to verify Present offset for %ls. Found %d, but previously found %d.", dxgi->wcFileName, offset, dxgi->iOffsetPresent); + } + } + } + + void *pResize = (*vtbl)[13]; + offset = GetFnOffsetInModule(reinterpret_cast<voidFunc>(pResize), dxgi->wcFileName, ARRAY_NUM_ELEMENTS(dxgi->wcFileName), "D3D11", "ResizeBuffers"); + if (offset >= 0) { + if (initializeDXGIData) { + dxgi->iOffsetResize = offset; + ods("D3D11: Successfully found ResizeBuffers offset: %ls: %d", dxgi->wcFileName, dxgi->iOffsetResize); + } else { + if (dxgi->iOffsetResize == offset) { + ods("D3D11: Successfully verified ResizeBuffers offset: %ls: %d", dxgi->wcFileName, dxgi->iOffsetResize); + } else { + ods("D3D11: Failed to verify ResizeBuffers offset for %ls. Found %d, but previously found %d.", dxgi->wcFileName, offset, dxgi->iOffsetResize); + } + } + } + + vtbl = (void ***) pDevice; + + void *pAddRef = (*vtbl)[1]; + offset = GetFnOffsetInModule(reinterpret_cast<voidFunc>(pAddRef), d3d11->wcFileName, ARRAY_NUM_ELEMENTS(d3d11->wcFileName), "D3D11", "AddRef"); + if (offset >= 0) { + d3d11->iOffsetAddRef = offset; + ods("D3D11: Successfully found AddRef offset: %ls: %d", d3d11->wcFileName, d3d11->iOffsetAddRef); + } + + void *pRelease = (*vtbl)[2]; + offset = GetFnOffsetInModule(reinterpret_cast<voidFunc>(pRelease), d3d11->wcFileName, ARRAY_NUM_ELEMENTS(d3d11->wcFileName), "D3D11", "Release"); + if (offset >= 0) { + d3d11->iOffsetRelease = offset; + ods("D3D11: Successfully found Release offset: %ls: %d", d3d11->wcFileName, d3d11->iOffsetRelease); + } + } + + if (pDevice) + pDevice->Release(); + if (pDeviceContext) + pDeviceContext->Release(); + if (pSwapChain) + pSwapChain->Release(); + + } else { + FreeLibrary(hD3D11); + } + + DestroyWindow(hwnd); + } else { + FreeLibrary(hD3D11); + } +} |