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