diff --git a/DSfix.vcxproj b/DSfix.vcxproj index ce32a8a..346d85e 100644 --- a/DSfix.vcxproj +++ b/DSfix.vcxproj @@ -251,6 +251,7 @@ WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL + @@ -313,6 +314,7 @@ + diff --git a/RenderstateManager.cpp b/RenderstateManager.cpp index 634fe1a..66775d9 100644 --- a/RenderstateManager.cpp +++ b/RenderstateManager.cpp @@ -1,5 +1,6 @@ #include "RenderstateManager.h" +#include #include #include #include @@ -15,15 +16,28 @@ RSManager RSManager::instance; -static const char *PIXEL_SHADER_DUMP_DIR = "dsfix/pixelshader_dump"; -static const char *PIXEL_SHADER_OVERRIDE_DIR = "dsfix/pixelshader_override"; -static const char *VERTEX_SHADER_DUMP_DIR = "dsfix/vertexshader_dump"; -static const char *VERTEX_SHADER_OVERRIDE_DIR = "dsfix/vertexshader_override"; +namespace { + const char *PIXEL_SHADER_DUMP_DIR = "dsfix/pixelshader_dump"; + const char *PIXEL_SHADER_OVERRIDE_DIR = "dsfix/pixelshader_override"; + const char *VERTEX_SHADER_DUMP_DIR = "dsfix/vertexshader_dump"; + const char *VERTEX_SHADER_OVERRIDE_DIR = "dsfix/vertexshader_override"; + + unsigned getDOFResolution() { + unsigned setting = Settings::get().getDOFOverrideResolution(); + if (setting == 0) { + return 360; + } else { + return setting; + } + } +} void RSManager::initResources() { SDLOG(0, "RenderstateManager resource initialization started\n"); unsigned rw = Settings::get().getRenderWidth(), rh = Settings::get().getRenderHeight(); - unsigned dofRes = Settings::get().getDOFOverrideResolution(); + haveOcclusionScale = false; + occlusionScale = 1; + unsigned dofRes = getDOFResolution(); if(Settings::get().getAAQuality()) { if(Settings::get().getAAType() == "SMAA") { smaa = new SMAA(d3ddev, rw, rh, (SMAA::Preset)(Settings::get().getAAQuality()-1)); @@ -278,6 +292,9 @@ HRESULT RSManager::redirectSetRenderTarget(DWORD RenderTargetIndex, IDirect3DSur // store it for later use mainRT = pRenderTarget; SDLOG(0, "Storing RT as main RT: %p\n", mainRT); + if (!haveOcclusionScale) { + measureOcclusionScale(); + } } if(nrts == 11) { // we are switching to the RT used to store the Z value in the 24 RGB bits (among other things) @@ -430,6 +447,147 @@ HRESULT RSManager::redirectSetRenderTarget(DWORD RenderTargetIndex, IDirect3DSur return d3ddev->SetRenderTarget(RenderTargetIndex, pRenderTarget); } +// Measure the occlusion query result of drawing a square of a known +// size. +// +// The result is affected by the rendering resolution which is +// known, but might also be affected by driver-enforced antialiasing. +// This result is used to scale the result of further occlusion +// queries into the expected range of values. +void RSManager::measureOcclusionScale() { + static const D3DVERTEXELEMENT9 vertexElements[2] = { + { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, + D3DDECL_END() + }; + CComPtr vertexDeclaration; + CComPtr errorBuffer; + static const char vertexShaderSource[] = "vs_3_0 \n dcl_position v0 \n dcl_position o0 \n mov o0, v0"; + CComPtr vertexShaderBuffer; + CComPtr vertexShader; + static const char pixelShaderSource[] = "ps_3_0 \n def c0, 0, 0, 0, 0 \n mov_pp oC0, c0.x"; + CComPtr pixelShaderBuffer; + CComPtr pixelShader; + CComPtr query; + DWORD pixelsVisible = 0; + HRESULT hr; + + haveOcclusionScale = true; + + float width = 24.0 / 1024; + float height = 24.0 / 720; + const float vertexData[4][3] = { + { -width, -height, 0.5 }, + { width, -height, 0.5 }, + { width, height, 0.5 }, + { -width, height, 0.5 }, + }; + + if (FAILED(d3ddev->Clear(0, nullptr, D3DCLEAR_TARGET, 0, 1, 0))) { + SDLOG(0, "measureOcclusionScale: Clear failed\n"); + return; + } + + if (FAILED(d3ddev->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE))) { + SDLOG(0, "measureOcclusionScale: SetRenderState ZENABLE failed\n"); + return; + } + + if (FAILED(d3ddev->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL))) { + SDLOG(0, "measureOcclusionScale: SetRenderState ZFUNC failed\n"); + return; + } + + if (FAILED(d3ddev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE))) { + SDLOG(0, "measureOcclusionScale: SetRenderState CULLMODE failed\n"); + return; + } + + if (FAILED(d3ddev->CreateVertexDeclaration(vertexElements, &vertexDeclaration))) { + SDLOG(0, "measureOcclusionScale: CreateVertexDeclaration failed\n"); + return; + } + + if (FAILED(d3ddev->SetVertexDeclaration(vertexDeclaration))) { + SDLOG(0, "measureOcclusionScale: SetVertexDeclaration failed\n"); + return; + } + + if (FAILED(D3DXAssembleShader(vertexShaderSource, sizeof(vertexShaderSource), nullptr, nullptr, 0, &vertexShaderBuffer, &errorBuffer))) { + SDLOG(0, "measureOcclusionScale: D3DXAssembleShader failed:\n%s\n", errorBuffer->GetBufferPointer()); + return; + } + + if (FAILED(d3ddev->CreateVertexShader(reinterpret_cast(vertexShaderBuffer->GetBufferPointer()), &vertexShader))) { + SDLOG(0, "measureOcclusionScale: CreateVertexShader failed\n"); + return; + } + + if (FAILED(d3ddev->SetVertexShader(vertexShader))) { + SDLOG(0, "measureOcclusionScale: SetVertexShader failed\n"); + return; + } + + if (FAILED(D3DXAssembleShader(pixelShaderSource, sizeof(pixelShaderSource), nullptr, nullptr, 0, &pixelShaderBuffer, &errorBuffer))) { + SDLOG(0, "measureOcclusionScale: D3DXAssembleShader failed:\n%s\n", errorBuffer->GetBufferPointer()); + return; + } + + if (FAILED(d3ddev->CreatePixelShader(reinterpret_cast(pixelShaderBuffer->GetBufferPointer()), &pixelShader))) { + SDLOG(0, "measureOcclusionScale: CreatePixelShader failed\n"); + return; + } + + if (FAILED(d3ddev->SetPixelShader(pixelShader))) { + SDLOG(0, "measureOcclusionScale: SetPixelShader failed\n"); + return; + } + + if (FAILED(d3ddev->CreateQuery(D3DQUERYTYPE_OCCLUSION, &query))) { + SDLOG(0, "measureOcclusionScale: CreateQuery failed\n"); + return; + } + + if (FAILED(d3ddev->BeginScene())) { + SDLOG(0, "measureOcclusionScale: BeginScene failed\n"); + return; + } + + if (FAILED(query->Issue(D3DISSUE_BEGIN))) { + SDLOG(0, "measureOcclusionScale: Issue BEGIN failed\n"); + return; + } + + if (FAILED(d3ddev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, vertexData, sizeof(vertexData[0])))) { + SDLOG(0, "measureOcclusionScale: DrawPrimitiveUP failed\n"); + return; + } + + if (FAILED(query->Issue(D3DISSUE_END))) { + SDLOG(0, "measureOcclusionScale: Issue END failed\n"); + return; + } + + while ((hr = query->GetData(&pixelsVisible, sizeof(pixelsVisible), D3DGETDATA_FLUSH)) == S_FALSE); + if (FAILED(hr)) { + SDLOG(0, "measureOcclusionScale: GetData failed\n"); + return; + } + + if (pixelsVisible == 0) { + occlusionScale = 1; + } else { + occlusionScale = pixelsVisible / 576.0; + } + + SDLOG(2, "measureOcclusionScale: pixelsVisible = %d\n", pixelsVisible); + SDLOG(2, "measureOcclusionScale: occlusionScale = %f\n", occlusionScale); + + if (FAILED(d3ddev->EndScene())) { + SDLOG(0, "measureOcclusionScale: EndScene failed\n"); + return; + } +} + HRESULT RSManager::redirectStretchRect(IDirect3DSurface9* pSourceSurface, CONST RECT* pSourceRect, IDirect3DSurface9* pDestSurface, CONST RECT* pDestRect, D3DTEXTUREFILTERTYPE Filter) { //SurfSurfMap::iterator it = renderTexSurfTargets.find(pSourceSurface); //if(it != renderTexSurfTargets.end()) { @@ -581,7 +739,7 @@ void RSManager::reloadScao() { void RSManager::reloadGauss() { SAFEDELETE(gauss); - gauss = new GAUSS(d3ddev, Settings::get().getDOFOverrideResolution()*16/9, Settings::get().getDOFOverrideResolution()); + gauss = new GAUSS(d3ddev, getDOFResolution()*16/9, getDOFResolution()); SDLOG(0, "Reloaded GAUSS\n"); } @@ -754,7 +912,7 @@ bool RSManager::isTextureText(IDirect3DBaseTexture9* t) { } unsigned RSManager::isDof(unsigned width, unsigned height) { - unsigned topWidth = Settings::get().getDOFOverrideResolution()*16/9, topHeight = Settings::get().getDOFOverrideResolution(); + unsigned topWidth = getDOFResolution()*16/9, topHeight = getDOFResolution(); if(width == topWidth && height == topHeight) return 1; if(width == topWidth/2 && height == topHeight/2) return 2; return 0; diff --git a/RenderstateManager.h b/RenderstateManager.h index 63c950c..64957f6 100644 --- a/RenderstateManager.h +++ b/RenderstateManager.h @@ -117,6 +117,11 @@ class RSManager { void dumpShader(UINT32 hash, const char *directory, LPD3DXBUFFER pBuffer); bool getOverrideShader(UINT32 hash, const char *directory, LPD3DXBUFFER *ppBuffer); + bool haveOcclusionScale; + float occlusionScale; + + void measureOcclusionScale(); + private: ~RSManager(); @@ -127,7 +132,7 @@ class RSManager { RSManager() : smaa(NULL), fxaa(NULL), ssao(NULL), gauss(NULL), rgbaBuffer1Surf(NULL), rgbaBuffer1Tex(NULL), inited(false), doAA(true), doSsao(true), doDofGauss(true), doHud(true), captureNextFrame(false), capturing(false), hudStarted(false), takeScreenshot(false), hideHud(false), - mainRenderTexIndex(0), mainRenderSurfIndex(0), dumpCaptureIndex(0), numKnownTextures(0), foundKnownTextures(0), skippedPresents(0) { + mainRenderTexIndex(0), mainRenderSurfIndex(0), dumpCaptureIndex(0), numKnownTextures(0), foundKnownTextures(0), skippedPresents(0), haveOcclusionScale(false), occlusionScale(1) { #define TEXTURE(_name, _hash) ++numKnownTextures; #include "Textures.def" #undef TEXTURE @@ -195,4 +200,6 @@ class RSManager { HRESULT redirectSetRenderState(D3DRENDERSTATETYPE State, DWORD Value); HRESULT redirectCreatePixelShader(CONST DWORD *pfunction, IDirect3DPixelShader9 **ppShader); HRESULT redirectCreateVertexShader(CONST DWORD *pfunction, IDirect3DVertexShader9 **ppShader); + + float getOcclusionScale() const { return occlusionScale; } }; diff --git a/d3d9.h b/d3d9.h index d512f4b..2d142e9 100644 --- a/d3d9.h +++ b/d3d9.h @@ -9,5 +9,6 @@ #include #include "d3d9int.h" #include "d3d9dev.h" +#include "d3d9query.h" IDirect3D9 *APIENTRY hkDirect3DCreate9(UINT SDKVersion); diff --git a/d3d9dev.cpp b/d3d9dev.cpp index 4d1f86a..291258d 100644 --- a/d3d9dev.cpp +++ b/d3d9dev.cpp @@ -186,7 +186,12 @@ HRESULT APIENTRY hkIDirect3DDevice9::CreatePixelShader(CONST DWORD* pFunction,ID } HRESULT APIENTRY hkIDirect3DDevice9::CreateQuery(D3DQUERYTYPE Type,IDirect3DQuery9** ppQuery) { - return m_pD3Ddev->CreateQuery(Type, ppQuery); + auto result = m_pD3Ddev->CreateQuery(Type, ppQuery); + if (Type == D3DQUERYTYPE_OCCLUSION && result == D3D_OK) { + new hkIDirect3DQuery9(ppQuery); + // These instances will leak, but there are only a set number of them created + } + return result; } HRESULT APIENTRY hkIDirect3DDevice9::CreateRenderTarget(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle) { diff --git a/d3d9query.cpp b/d3d9query.cpp new file mode 100644 index 0000000..83b53c4 --- /dev/null +++ b/d3d9query.cpp @@ -0,0 +1,46 @@ +#include "d3d9.h" +#include "main.h" +#include "RenderstateManager.h" +#include "Settings.h" + +hkIDirect3DQuery9::hkIDirect3DQuery9(IDirect3DQuery9 **ppReturnedQueryInterface) { + m_pD3Dquery = *ppReturnedQueryInterface; + *ppReturnedQueryInterface = this; +} + +HRESULT APIENTRY hkIDirect3DQuery9::QueryInterface(REFIID riid, void** ppvObj) { + return m_pD3Dquery->QueryInterface(riid, ppvObj); +} + +ULONG APIENTRY hkIDirect3DQuery9::AddRef() { + return m_pD3Dquery->AddRef(); +} + +ULONG APIENTRY hkIDirect3DQuery9::Release() { + return m_pD3Dquery->Release(); +} + +HRESULT APIENTRY hkIDirect3DQuery9::GetDevice(IDirect3DDevice9** ppDevice) { + return m_pD3Dquery->GetDevice(ppDevice); +} + +D3DQUERYTYPE APIENTRY hkIDirect3DQuery9::GetType() { + return m_pD3Dquery->GetType(); +} + +DWORD APIENTRY hkIDirect3DQuery9::GetDataSize() { + return m_pD3Dquery->GetDataSize(); +} + +HRESULT APIENTRY hkIDirect3DQuery9::Issue(DWORD dwIssueFlags) { + return m_pD3Dquery->Issue(dwIssueFlags); +} + +HRESULT APIENTRY hkIDirect3DQuery9::GetData(void* pData, DWORD dwSize, DWORD dwGetDataFlags) { + auto result = m_pD3Dquery->GetData(pData, dwSize, dwGetDataFlags); + if (SUCCEEDED(result)) { + auto pixelsDrawn = reinterpret_cast(pData); + pixelsDrawn[0] = static_cast(pixelsDrawn[0] / RSManager::get().getOcclusionScale()); + } + return result; +} \ No newline at end of file diff --git a/d3d9query.h b/d3d9query.h new file mode 100644 index 0000000..41c6224 --- /dev/null +++ b/d3d9query.h @@ -0,0 +1,19 @@ +#pragma once + +#include "d3d9.h" + +interface hkIDirect3DQuery9 : public IDirect3DQuery9 +{ + hkIDirect3DQuery9(IDirect3DQuery9 **ppReturnedQueryInterface); + + IDirect3DQuery9* m_pD3Dquery; + + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj); + STDMETHOD_(ULONG, AddRef)(THIS); + STDMETHOD_(ULONG, Release)(THIS); + STDMETHOD(GetDevice)(THIS_ IDirect3DDevice9** ppDevice); + STDMETHOD_(D3DQUERYTYPE, GetType)(THIS); + STDMETHOD_(DWORD, GetDataSize)(THIS); + STDMETHOD(Issue)(THIS_ DWORD dwIssueFlags); + STDMETHOD(GetData)(THIS_ void* pData, DWORD dwSize, DWORD dwGetDataFlags); +}; \ No newline at end of file