From c709270a9f0533ac802a0b3b9a143d1981966b1a Mon Sep 17 00:00:00 2001 From: Nicklas Lindgren Date: Thu, 29 Dec 2016 19:15:07 +0100 Subject: [PATCH 1/3] Fix crash for DOF blur at default DOF resolution Initialization of GAUSS (the DOF blur filter) would fail and cause a crash at startup if dofBlurAmount was set to anything larger than zero and the dofOverrideResolution was zero or unspecified. This change treats the DOF resolution as 360 when configured as zero for the purpose of initializing the blur filter and detecting the DOF render targets. --- RenderstateManager.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/RenderstateManager.cpp b/RenderstateManager.cpp index 634fe1a..518c2ee 100644 --- a/RenderstateManager.cpp +++ b/RenderstateManager.cpp @@ -15,15 +15,26 @@ 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(); + unsigned dofRes = getDOFResolution(); if(Settings::get().getAAQuality()) { if(Settings::get().getAAType() == "SMAA") { smaa = new SMAA(d3ddev, rw, rh, (SMAA::Preset)(Settings::get().getAAQuality()-1)); @@ -581,7 +592,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 +765,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; From 3e53a69b13300dac700e2d20f8d607aff9810c7b Mon Sep 17 00:00:00 2001 From: Nicklas Lindgren Date: Mon, 2 Jan 2017 06:37:11 +0100 Subject: [PATCH 2/3] Fix drawing of sun and lens flares Whether the sun and lens flares are drawn are determined by the result of an occlusion query. If the query returns a larger number of pixels drawn than expected, which often happens when running in a higher resolution than the default, the sun and lens flares might not be drawn. This change wraps the IDirect3DQuery9 objects used for occlusion queries and modifies the returned number of pixels drawn to correspond with the default resolution, which makes the sun and lens flares appear as expected. --- DSfix.vcxproj | 2 ++ RenderstateManager.cpp | 1 + RenderstateManager.h | 5 ++++- d3d9.h | 1 + d3d9dev.cpp | 7 ++++++- d3d9query.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++ d3d9query.h | 19 +++++++++++++++++ 7 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 d3d9query.cpp create mode 100644 d3d9query.h 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 518c2ee..cb2ad8d 100644 --- a/RenderstateManager.cpp +++ b/RenderstateManager.cpp @@ -34,6 +34,7 @@ namespace { void RSManager::initResources() { SDLOG(0, "RenderstateManager resource initialization started\n"); unsigned rw = Settings::get().getRenderWidth(), rh = Settings::get().getRenderHeight(); + areaScale = rw * rh / 1024.0 / 576.0; unsigned dofRes = getDOFResolution(); if(Settings::get().getAAQuality()) { if(Settings::get().getAAType() == "SMAA") { diff --git a/RenderstateManager.h b/RenderstateManager.h index 63c950c..2b1b6b8 100644 --- a/RenderstateManager.h +++ b/RenderstateManager.h @@ -117,6 +117,7 @@ class RSManager { void dumpShader(UINT32 hash, const char *directory, LPD3DXBUFFER pBuffer); bool getOverrideShader(UINT32 hash, const char *directory, LPD3DXBUFFER *ppBuffer); + float areaScale; private: ~RSManager(); @@ -127,7 +128,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), areaScale(1) { #define TEXTURE(_name, _hash) ++numKnownTextures; #include "Textures.def" #undef TEXTURE @@ -195,4 +196,6 @@ class RSManager { HRESULT redirectSetRenderState(D3DRENDERSTATETYPE State, DWORD Value); HRESULT redirectCreatePixelShader(CONST DWORD *pfunction, IDirect3DPixelShader9 **ppShader); HRESULT redirectCreateVertexShader(CONST DWORD *pfunction, IDirect3DVertexShader9 **ppShader); + + float getAreaScale() const { return areaScale; } }; 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..6c3e478 --- /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 (result == D3D_OK) { + auto pixelsDrawn = reinterpret_cast(pData); + pixelsDrawn[0] = static_cast(pixelsDrawn[0] / RSManager::get().getAreaScale()); + } + 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 From 9b834cce9cc0027f13f79b0863b0f1d1a3ec6b6d Mon Sep 17 00:00:00 2001 From: Nicklas Lindgren Date: Sat, 7 Jan 2017 21:45:13 +0100 Subject: [PATCH 3/3] Fix occlusion queries affected by antialiasing Occlusion queries might return larger values than expected if driver-enforced antialiasing is enabled. This change makes an occlusion query for a square of a known size to determine what result that should be expected. This is used by the IDirect3DQuery9 wrapper to scale the result of further queries appropriately, considering both the rendering resolution and unexpected multisampling enforced by the driver. --- RenderstateManager.cpp | 148 ++++++++++++++++++++++++++++++++++++++++- RenderstateManager.h | 10 ++- d3d9query.cpp | 4 +- 3 files changed, 156 insertions(+), 6 deletions(-) diff --git a/RenderstateManager.cpp b/RenderstateManager.cpp index cb2ad8d..66775d9 100644 --- a/RenderstateManager.cpp +++ b/RenderstateManager.cpp @@ -1,5 +1,6 @@ #include "RenderstateManager.h" +#include #include #include #include @@ -34,7 +35,8 @@ namespace { void RSManager::initResources() { SDLOG(0, "RenderstateManager resource initialization started\n"); unsigned rw = Settings::get().getRenderWidth(), rh = Settings::get().getRenderHeight(); - areaScale = rw * rh / 1024.0 / 576.0; + haveOcclusionScale = false; + occlusionScale = 1; unsigned dofRes = getDOFResolution(); if(Settings::get().getAAQuality()) { if(Settings::get().getAAType() == "SMAA") { @@ -290,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) @@ -442,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()) { diff --git a/RenderstateManager.h b/RenderstateManager.h index 2b1b6b8..64957f6 100644 --- a/RenderstateManager.h +++ b/RenderstateManager.h @@ -117,7 +117,11 @@ class RSManager { void dumpShader(UINT32 hash, const char *directory, LPD3DXBUFFER pBuffer); bool getOverrideShader(UINT32 hash, const char *directory, LPD3DXBUFFER *ppBuffer); - float areaScale; + bool haveOcclusionScale; + float occlusionScale; + + void measureOcclusionScale(); + private: ~RSManager(); @@ -128,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), areaScale(1) { + 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 @@ -197,5 +201,5 @@ class RSManager { HRESULT redirectCreatePixelShader(CONST DWORD *pfunction, IDirect3DPixelShader9 **ppShader); HRESULT redirectCreateVertexShader(CONST DWORD *pfunction, IDirect3DVertexShader9 **ppShader); - float getAreaScale() const { return areaScale; } + float getOcclusionScale() const { return occlusionScale; } }; diff --git a/d3d9query.cpp b/d3d9query.cpp index 6c3e478..83b53c4 100644 --- a/d3d9query.cpp +++ b/d3d9query.cpp @@ -38,9 +38,9 @@ HRESULT APIENTRY hkIDirect3DQuery9::Issue(DWORD dwIssueFlags) { HRESULT APIENTRY hkIDirect3DQuery9::GetData(void* pData, DWORD dwSize, DWORD dwGetDataFlags) { auto result = m_pD3Dquery->GetData(pData, dwSize, dwGetDataFlags); - if (result == D3D_OK) { + if (SUCCEEDED(result)) { auto pixelsDrawn = reinterpret_cast(pData); - pixelsDrawn[0] = static_cast(pixelsDrawn[0] / RSManager::get().getAreaScale()); + pixelsDrawn[0] = static_cast(pixelsDrawn[0] / RSManager::get().getOcclusionScale()); } return result; } \ No newline at end of file