Skip to content

Instantly share code, notes, and snippets.

@t-mat
Created December 30, 2011 15:08
Show Gist options
  • Save t-mat/1540248 to your computer and use it in GitHub Desktop.
Save t-mat/1540248 to your computer and use it in GitHub Desktop.
D3D9プロキシDLLの作り方

D3D9プロキシDLLの作り方

知りたいこと

既存の D3D アプリケーションのレンダリング結果に、 自前のポスト処理/オーバーレイを付加したり、スクリーンショット撮ったりする (FXAA とかああいう) のはどーやってるの?

逆順に説明すると、以下のようになる:

  • IDirect3DDevice9::EndScene()IDirect3DSwapChain9::Present() をフックして、レンダリングが終わった後に加工を行う
  • Direct3DCreate9() を実行した際に、COM インターフェースの Vtbl を書き換えて、EndScene() 等をフックする
  • → ニセの d3d9.dll (プロキシDLL) を使って、Direct3DCreate9 をフックする

プロキシ DLL によるフックの仕組み

ターゲットとなる実行ファイルがあるディレクトリに、プロキシ DLL (自前で用意したニセの d3d9.dll) を配置する。

通常の DLL 検索ルール の場合、ターゲットの存在するディレクトリから DLL を探した後、 システムディレクトリの DLL を見に行くので、 ターゲットと同一ディレクトリの d3d9.dll が先に読み込まれる。

プロキシ DLL からは、本物の %SystemRoot%\System32\d3d9.dll 内の関数を呼び出す。 %SystemRoot%\System32GetSystemDirectory() を用いて取得する。

最小限のプロキシ DLL

最小限の、プロキシ DLL の実験

  • 下記のコードをコンパイル
  • 出力された実行ファイルを d3d9.dll にリネーム
  • リネームした d3d9.dll をターゲットの実行ファイルと同じディレクトリに配置
  • DebugView を実行
  • ターゲットの実行ファイルを起動
  • DebugView のウィンドウに OutputDebugString() 内のメッセージ ("My Injection Test : Direct3DCreate9") が出る

ターゲットによっては出ないかも。全然ダメな場合は D3D9 のチュートリアル等で試すこと。

#define _CRT_SECURE_NO_WARNINGS
#include <d3d9.h>
#include <tchar.h>
#pragma comment(linker, "/DLL")

static IDirect3D9* (WINAPI *o_Direct3DCreate9)(UINT);

IDirect3D9* WINAPI Direct3DCreate9(UINT SDKVersion) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    OutputDebugString(_T("My Injection Test : Direct3DCreate9\n"));
    IDirect3D9 *iDirect3D9 = o_Direct3DCreate9(SDKVersion);
    return iDirect3D9;
}

BOOL APIENTRY DllMain(HINSTANCE, DWORD fdwReason, LPVOID) {
    if(fdwReason == DLL_PROCESS_ATTACH) {
        TCHAR fn[MAX_PATH];
        UINT l = GetSystemDirectory(fn, _countof(fn));
        _tcscpy(fn + l, _T("\\d3d9.dll"));
        HMODULE hModule = LoadLibrary(fn);
        * (void**) &o_Direct3DCreate9 = GetProcAddress(hModule, "Direct3DCreate9");
    }
    return TRUE;
}

d3dx9core.hを修正

以下、C++ コードから CINTERFACE を使うので、d3dx9core.h の記述ミスを修正する。 d3dx9core.h の中で

DECLARE_INTERFACE_(ID3DXFont, IUnknown)
{
...
#ifdef __cplusplus

となっているものを

DECLARE_INTERFACE_(ID3DXFont, IUnknown)
{
...
#if defined(__cplusplus) && !defined(CINTERFACE)

と修正する。

IDirect3D9::CreateDevice()をフック

Direct3DCreate9() が返す IDirect3D9 の Vtbl を加工することで、IDirect3D9::CreateDevice() をフックする。

d3d9.h を #include する前に、シンボル CINTERFACE を定義しておくと、COM のインターフェースがより扱いやすい形で見えるようになる。 具体的には、以下のような定義が行われる

struct IDirect3D {
	struct IDirect3DVtbl* lpVtbl;
};
struct IDirect3DVtbl {
	HRESULT (STDMETHODCALLTYPE* QueryInterface)(...);
	...
};

ここで、IDirect3D::lpVtbl が指す構造体 IDirect3DVtbl がターゲットの Vtbl となる。

CINTERFACE 定義済みのまま d3d9.h を用いても良いが、通常の C++ インターフェースが使えずに冗長になるので、 下記のコードでは細工を行っている。 細工の結果として、namespace MyInterface の場合は CINTERFACE 版の IDirect3D にアクセスできるようになる。

あとは MyInterface::IDirect3D::lpVtbl が指す Vtbl を書き換えれば良い。 ただし、このメモリ領域は保護指定が PAGE_EXECUTE_READ (0x0020) になっているので、 まず書き換え可能 PAGE_EXECUTE_WRITECOPY (0x0080) に変更し、書き換え後、元に戻す必要がある。

以下のサンプルを前述のサンプル同様に実行すると、 フックの結果として DebugView にメッセージ ("My Injection Test : IDirect3D9::CreateDevice - SUCCEEDED") が出る。

#define _CRT_SECURE_NO_WARNINGS
#include <d3d9.h>
#include <tchar.h>
#pragma comment(linker, "/DLL")

namespace MyInterface {
#pragma warning(push)
#define CINTERFACE                  // Windows SDK : BaseTypes.h
#pragma warning(disable : 4005)
#define STDMETHOD_(type,method)     type (STDMETHODCALLTYPE * method)
#define STDMETHOD(method)           STDMETHOD_(HRESULT,method)
#define PURE
#define THIS                        INTERFACE FAR* This
#define THIS_                       THIS,
#define DECLARE_INTERFACE(iface)    typedef interface iface { struct iface##Vtbl FAR* lpVtbl; } iface; \
                                    typedef struct iface##Vtbl iface##Vtbl; struct iface##Vtbl
#define DECLARE_INTERFACE_(iface,b) DECLARE_INTERFACE(iface)
#undef _D3D9_H_
#include <d3d9.h>
#pragma warning(pop)
} // namespace MyInterface

static IDirect3D9* (WINAPI *o_Direct3DCreate9)(UINT);
static HRESULT (WINAPI *o_IDirect3D9_CreateDevice)(IDirect3D9*, UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS*, IDirect3DDevice9**);

static HRESULT WINAPI my_IDirect3D9_CreateDevice(IDirect3D9* direct3d, UINT adapter, D3DDEVTYPE type, HWND window, DWORD flag, D3DPRESENT_PARAMETERS* param, IDirect3DDevice9** ppDevice) {
    HRESULT hr = o_IDirect3D9_CreateDevice(direct3d, adapter, type, window, flag, param, ppDevice);
    if(SUCCEEDED(hr)) {
        OutputDebugString(_T("My Injection Test : IDirect3D9::CreateDevice - SUCCEEDED\n"));
    }
    return hr;
}

IDirect3D9* WINAPI Direct3DCreate9(UINT SDKVersion) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    IDirect3D9 *iDirect3D9 = o_Direct3DCreate9(SDKVersion);
    if(iDirect3D9) {
        OutputDebugString(_T("My Injection Test : d3d9.dll::Direct3DCreate9 - SUCCEEDED\n"));
        MyInterface::IDirect3D9Vtbl* p = ((MyInterface::IDirect3D9*) iDirect3D9)->lpVtbl;
        DWORD protect = PAGE_EXECUTE_WRITECOPY;
        VirtualProtect(p, sizeof(*p), protect, &protect);
        * (void**) &o_IDirect3D9_CreateDevice = (void*) p->CreateDevice;
        * (void**) &p->CreateDevice = (void*) my_IDirect3D9_CreateDevice;
        VirtualProtect(p, sizeof(*p), protect, &protect);
    }
    return iDirect3D9;
}

BOOL APIENTRY DllMain(HINSTANCE, DWORD fdwReason, LPVOID) {
    if(fdwReason == DLL_PROCESS_ATTACH) {
        TCHAR fn[MAX_PATH];
        _tcscpy(fn + GetSystemDirectory(fn, _countof(fn)), _T("\\d3d9.dll"));
        HMODULE hModule = LoadLibrary(fn);
        * (void**) &o_Direct3DCreate9 = GetProcAddress(hModule, "Direct3DCreate9");
    }
    return TRUE;
}

実際の描画処理

ここでは IDirect3DDevice9 の以下のメソッドをフックすることで、加工処理を行う

  • IDirect3DDevice9::BeginStateBlock
  • IDirect3DDevice9::EndStateBlock
  • IDirect3DDevice9::Reset
  • IDirect3DDevice9::Present

このうち、実際の描画処理を行うのは IDirect3DDevice9::Present のみで、他のメソッドはフックおよび状態の整合性の維持に用いる。

IDirect3DDevice9::BeginStateBlock および IDirect3DDevice9::EndStateBlockIDirect3DDevice9 の Vtbl を書き換える ので、それぞれの処理後に、Vtbl の値を元に戻す処理を行う。

IDirect3DDevice9::Reset の実行時にはリソースの開放が必要なので、 実際の処理を行う前に、加工用のリソースの開放を行う。

IDirect3DDevice9::Present の実行前に、以下の流れで、加工処理を行う。

  • IDirect3DDevice9::GetBackBuffer を用いてバックバッファを取得する
  • D3DXCreateEffectFromFile を用いて、エフェクトを生成する
  • バックバッファとマッチするテクスチャ/レンダーターゲットを生成する
  • 上記の要素を用いてポスト処理を行う

ポスト処理そのものについては割愛。

本物の d3d9.dll 相当の関数エクスポート

アプリケーションによっては、Direct3DCreate9 以外の関数も LoadLibrary() しているものがあるので、 本物の d3d9.dll 相当の関数をエクスポートする。

VisualC++2010 がインストールされている環境であれば、以下のようなコマンドラインで、 実際の d3d9.dll が持つ関数を知ることができる

call "%VS100COMNTOOLS%..\..\VC\bin\vcvars32.bat"
dumpbin /EXPORTS %SystemRoot%\System32\d3d9.dll

結果は以下のようになる

ordinal hint RVA      name

      4    0 00086E18 D3DPERF_BeginEvent
      5    1 00086E70 D3DPERF_EndEvent
      6    2 00086FF8 D3DPERF_GetStatus
      7    3 00086F60 D3DPERF_QueryRepeatFrame
      8    4 00086EC0 D3DPERF_SetMarker
      9    5 00086FAC D3DPERF_SetOptions
     10    6 00086F10 D3DPERF_SetRegion
     11    7 00087E20 DebugSetLevel
     12    8 0001B800 DebugSetMute
     13    9 000396B0 Direct3DCreate9
     14    A 00002460 Direct3DCreate9Ex
      1    B 0001DCDC Direct3DShaderValidatorCreate9
      2    C 000821A8 PSGPError
      3    D 000820F4 PSGPSampleTexture

それぞれ適切な、何もしないフックを行う。以下は D3DPERF_SetMarker() の例

static void (WINAPI *o_D3DPERF_SetMarker)(D3DCOLOR, LPCWSTR);

void WINAPI D3DPERF_SetMarker(D3DCOLOR col, LPCWSTR wszName) {
#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
    return o_D3DPERF_SetMarker(col, wszName);
}

BOOL APIENTRY DllMain(HINSTANCE, DWORD fdwReason, LPVOID) {
    if(fdwReason == DLL_PROCESS_ATTACH) {
        ...
        * (void**) &o_D3DPERF_SetMarker = GetProcAddress(h, "D3DPERF_SetMarker");
    }
}

全部まとめたもの

  • effect.fx ファイルはプロキシDLLと同じディレクトリに置くこと
  • 実行に成功すると pass1 で r=1.0, pass2 で b=0.0 を書き込み、結果として画面が赤~黄色っぽくなる
	#define _CRT_SECURE_NO_WARNINGS
	#include <d3d9.h>
	#include <d3dx9.h>
	#include <tchar.h>
	#pragma comment(linker, "/DLL")
	#pragma comment(lib, "d3dx9.lib")
	#pragma comment(lib, "dxguid.lib")
	
	namespace MyInterface {
	#pragma warning(push)
	#define CINTERFACE                  // Windows SDK : BaseTypes.h
	#pragma warning(disable : 4005)
	#define STDMETHOD_(type,method)     type (STDMETHODCALLTYPE * method)
	#define STDMETHOD(method)           STDMETHOD_(HRESULT,method)
	#define PURE
	#define THIS                        INTERFACE FAR* This
	#define THIS_                       THIS,
	#define DECLARE_INTERFACE(iface)    typedef interface iface { struct iface##Vtbl FAR* lpVtbl; } iface; \
	                                    typedef struct iface##Vtbl iface##Vtbl; struct iface##Vtbl
	#define DECLARE_INTERFACE_(iface,b) DECLARE_INTERFACE(iface)
	#undef _D3D9_H_
	#include <d3d9.h>
	#pragma warning(pop)
	} // namespace MyInterface
	
	class Effect {
	protected:
		Effect(IDirect3DDevice9* device) : pEffect(0), maxPass(2) {
			memset(image, 0, sizeof(image));
		}
	
		~Effect() {
			clear();
		}
	
		template<typename T> static void safeRelease(T*& p) {
			if(p) {
				p->Release();
				p = 0;
			}
		}
	
		void clear() {
			for(int i = 0; i < _countof(image); ++i) {
				image[i].release();
			}
			safeRelease(pEffect);
		}
	
		void draw(IDirect3DDevice9* d) {
			IDirect3DSurface9* pBackBuffer = 0;
			D3DSURFACE_DESC    bbd = { (D3DFORMAT) 0 };
			if(SUCCEEDED(d->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer))) {
				pBackBuffer->GetDesc(&bbd);
			}
			if(! pEffect) {
				TCHAR mfn[MAX_PATH+1], drive[MAX_PATH+1], dir[MAX_PATH+1], srcFile[MAX_PATH+1];
				GetModuleFileName(0, mfn, _countof(mfn));
				_tsplitpath_s(mfn, drive, _countof(drive), dir, _countof(dir), 0, 0, 0, 0);
				wsprintf(srcFile, _T("%s%s%s"), drive, dir, _T("effect.fx"));
				clear();
				D3DXCreateEffectFromFile(d, srcFile, 0, 0, D3DXFX_NOT_CLONEABLE, 0, &pEffect, 0);
			}
			if(pBackBuffer && pEffect) {
				for(int pass = 0; pass < maxPass; ++pass) {
					Image& m = image[pass];
					if(m.desc.Width != bbd.Width || m.desc.Height != bbd.Height || m.desc.Format != bbd.Format) {
						m.release();
					}
					if(! m.texture) {
						if(SUCCEEDED(d->CreateTexture(bbd.Width, bbd.Height, 1, D3DUSAGE_RENDERTARGET, bbd.Format, D3DPOOL_DEFAULT, &m.texture, 0))) {
							m.desc = bbd;
							m.texture->GetSurfaceLevel(0, &m.surface);
						}
					}
				}
			}
			if(pBackBuffer && pEffect && image[0].texture) {
				struct Vertex {
					float	x, y, z, w, u, v;
				} vertex[] = {
					{	0.0f,	0.0f,	0.0f,	0.0f,	0.0f,	0.0f,	},
					{	1.0f,	0.0f,	0.0f,	0.0f,	1.0f,	0.0f,	},
					{	0.0f,	1.0f,	0.0f,	0.0f,	0.0f,	1.0f,	},
					{	1.0f,	1.0f,	0.0f,	0.0f,	1.0f,	1.0f,	},
				};
				vertex[1].x = vertex[3].x = (float) bbd.Width;
				vertex[2].y = vertex[3].y = (float) bbd.Height;
				if(SUCCEEDED(d->StretchRect(pBackBuffer, 0, image[0].surface, 0, D3DTEXF_NONE))) {
					IDirect3DSurface9* originalRenderTarget = 0;
					d->BeginScene();
					d->GetRenderTarget(0, &originalRenderTarget);
					for(int i = 0; i < maxPass; ++i) {
						char tn[256];
						wsprintfA(tn, "gTexture%d", i);
						pEffect->SetTexture(tn, image[i].texture);
					}
					for(int pass = 0; pass < maxPass; ++pass) {
						char tec[256];
						wsprintfA(tec, "PostProcess%d", pass+1);
						unsigned int passes = 0;
						pEffect->SetTechnique(tec);
						pEffect->Begin(&passes, 0);
						pEffect->BeginPass(0);
						d->SetRenderTarget(0, (pass == maxPass-1) ? originalRenderTarget : image[pass+1].surface);
						d->SetFVF(D3DFVF_TEX1 | D3DFVF_XYZRHW);
						d->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, vertex, sizeof(vertex[0]));
						d->SetRenderTarget(0, 0);
						pEffect->EndPass();
						pEffect->End();
					}
					safeRelease(originalRenderTarget);
					d->EndScene();
				}
			}
			if(pBackBuffer) {
				pBackBuffer->Release();
			}
		}
	
		struct Image {
			IDirect3DTexture9*	texture;
			IDirect3DSurface9*	surface;
			D3DSURFACE_DESC		desc;
			void release() {
				safeRelease(surface);
				safeRelease(texture);
				memset(&desc, 0, sizeof(desc));
			}
		} image[256];
		ID3DXEffect*	pEffect;
		int				maxPass;
	
	public:
		static Effect* instance;
		static void present(IDirect3DDevice9* device) {
			if(! instance) {
				instance = new Effect(device);
			}
			instance->draw(device);
		}
		static void reset(IDirect3DDevice9* device) {
			if(instance) {
				delete instance;
				instance = 0;
			}
		}
	};
	Effect* Effect::instance = NULL;
	
	int         (WINAPI *o_D3DPERF_BeginEvent)(D3DCOLOR, LPCWSTR);
	int         (WINAPI *o_D3DPERF_EndEvent)();
	DWORD       (WINAPI *o_D3DPERF_GetStatus)();
	BOOL        (WINAPI *o_D3DPERF_QueryRepeatFrame)();
	void        (WINAPI *o_D3DPERF_SetMarker)(D3DCOLOR, LPCWSTR);
	void        (WINAPI *o_D3DPERF_SetOptions)(DWORD);
	void        (WINAPI *o_D3DPERF_SetRegion)(D3DCOLOR, LPCWSTR);
	HRESULT     (WINAPI *o_DebugSetLevel)();
	HRESULT     (WINAPI *o_DebugSetMute)(DWORD);
	IDirect3D9* (WINAPI *o_Direct3DCreate9)(UINT);
	HRESULT     (WINAPI *o_Direct3DCreate9Ex)(UINT, IDirect3D9Ex**);
	HRESULT     (WINAPI *o_Direct3DShaderValidatorCreate9)();
	HRESULT     (WINAPI *o_PSGPError)();
	HRESULT     (WINAPI *o_PSGPSampleTexture)();
	HRESULT (WINAPI *o_IDirect3D9_CreateDevice)(IDirect3D9*, UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS*, IDirect3DDevice9**);
	HRESULT (WINAPI *o_IDirect3DDevice9_Reset)(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*);
	HRESULT (WINAPI *o_IDirect3DDevice9_Present)(IDirect3DDevice9*, CONST RECT*, CONST RECT*, HWND, CONST RGNDATA*);
	HRESULT (WINAPI *o_IDirect3DDevice9_BeginStateBlock)(IDirect3DDevice9*);
	HRESULT (WINAPI *o_IDirect3DDevice9_EndStateBlock)(IDirect3DDevice9*, IDirect3DStateBlock9**);
	void store_IDirect3DDevice9_function(IDirect3DDevice9* pDevice);
	
	HRESULT WINAPI my_IDirect3DDevice9_Present(IDirect3DDevice9* device, const RECT* pSourceRect, const RECT* pDestRect, HWND hDestWindowOverride, const RGNDATA* pDirtyRegion) {
		Effect::present(device);
		return o_IDirect3DDevice9_Present(device, pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
	}
	
	HRESULT WINAPI my_IDirect3DDevice9_Reset(IDirect3DDevice9* device, D3DPRESENT_PARAMETERS* pPresentationParameters) {
		Effect::reset(device);
		return o_IDirect3DDevice9_Reset(device, pPresentationParameters);
	}
	
	HRESULT WINAPI my_IDirect3DDevice9_BeginStateBlock(IDirect3DDevice9* device) {
		HRESULT hr = o_IDirect3DDevice9_BeginStateBlock(device);
		store_IDirect3DDevice9_function(device);
		return hr;
	}
	
	HRESULT WINAPI my_IDirect3DDevice9_EndStateBlock(IDirect3DDevice9* device, IDirect3DStateBlock9** ppSB) {
		HRESULT hr = o_IDirect3DDevice9_EndStateBlock(device, ppSB);
		store_IDirect3DDevice9_function(device);
		return hr;
	}
	
	void store_IDirect3DDevice9_function(IDirect3DDevice9* pDevice) {
	    MyInterface::IDirect3DDevice9Vtbl* v = ((MyInterface::IDirect3DDevice9*) pDevice)->lpVtbl;
	    DWORD protect = PAGE_EXECUTE_WRITECOPY;
	    VirtualProtect(v, sizeof(*v), protect, &protect);
	    * (void**) &v->Reset           = (void*) my_IDirect3DDevice9_Reset;
	    * (void**) &v->Present         = (void*) my_IDirect3DDevice9_Present;
	    * (void**) &v->BeginStateBlock = (void*) my_IDirect3DDevice9_BeginStateBlock;
	    * (void**) &v->EndStateBlock   = (void*) my_IDirect3DDevice9_EndStateBlock;
	    VirtualProtect(v, sizeof(*v), protect, &protect);
	}
	
	HRESULT WINAPI my_IDirect3D9_CreateDevice(IDirect3D9* direct3d, UINT adapter, D3DDEVTYPE type, HWND window, DWORD flag, D3DPRESENT_PARAMETERS* param, IDirect3DDevice9** ppDevice) {
	    HRESULT hr = o_IDirect3D9_CreateDevice(direct3d, adapter, type, window, flag, param, ppDevice);
	    if(SUCCEEDED(hr)) {
	        MyInterface::IDirect3DDevice9Vtbl* v = ((MyInterface::IDirect3DDevice9*) (*ppDevice))->lpVtbl;
	        * (void**) &o_IDirect3DDevice9_Reset           = (void*) v->Reset;
	        * (void**) &o_IDirect3DDevice9_Present         = (void*) v->Present;
	        * (void**) &o_IDirect3DDevice9_BeginStateBlock = (void*) v->BeginStateBlock;
	        * (void**) &o_IDirect3DDevice9_EndStateBlock   = (void*) v->EndStateBlock;
	        store_IDirect3DDevice9_function(*ppDevice);
	    }
	    return hr;
	}
	
	IDirect3D9* WINAPI Direct3DCreate9(UINT SDKVersion) {
	#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
	    IDirect3D9 *iDirect3D9 = o_Direct3DCreate9(SDKVersion);
	    if(iDirect3D9) {
	        OutputDebugString(_T("My Injection Test : d3d9.dll::Direct3DCreate9 - SUCCEEDED\n"));
	        MyInterface::IDirect3D9Vtbl* p = ((MyInterface::IDirect3D9*) iDirect3D9)->lpVtbl;
	        DWORD protect = PAGE_EXECUTE_WRITECOPY;
	        VirtualProtect(p, sizeof(*p), protect, &protect);
	        * (void**) &o_IDirect3D9_CreateDevice = (void*) p->CreateDevice;
	        * (void**) &p->CreateDevice = (void*) my_IDirect3D9_CreateDevice;
	        VirtualProtect(p, sizeof(*p), protect, &protect);
	    }
	    return iDirect3D9;
	}
	
	int WINAPI D3DPERF_EndEvent() {
	#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
	    return o_D3DPERF_EndEvent();
	}
	
	DWORD WINAPI D3DPERF_GetStatus() {
	#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
	    return o_D3DPERF_GetStatus();
	}
	
	BOOL WINAPI D3DPERF_QueryRepeatFrame() {
	#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
	    return o_D3DPERF_QueryRepeatFrame();
	}
	
	void WINAPI D3DPERF_SetMarker(D3DCOLOR col, LPCWSTR wszName) {
	#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
	    return o_D3DPERF_SetMarker(col, wszName);
	}
	
	void WINAPI D3DPERF_SetOptions(DWORD dwOptions) {
	#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
	    return o_D3DPERF_SetOptions(dwOptions);
	}
	
	void WINAPI D3DPERF_SetRegion(D3DCOLOR col, LPCWSTR wszName) {
	#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
	    return o_D3DPERF_SetRegion(col, wszName);
	}
	
	HRESULT WINAPI DebugSetLevel() {
	#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
	    return o_DebugSetLevel();
	}
	
	HRESULT WINAPI DebugSetMute(DWORD dw1) {
	#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
	    return o_DebugSetMute(dw1);
	}
	
	HRESULT WINAPI Direct3DShaderValidatorCreate9() {
	#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
	    return o_Direct3DShaderValidatorCreate9();
	}
	
	HRESULT WINAPI PSGPError() {
	#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
	    return o_PSGPError();
	}
	
	HRESULT WINAPI PSGPSampleTexture() {
	#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
	    return o_PSGPSampleTexture();
	}
	
	BOOL APIENTRY DllMain(HINSTANCE, DWORD fdwReason, LPVOID) {
	    if(fdwReason == DLL_PROCESS_ATTACH) {
	        TCHAR fn[MAX_PATH];
	        _tcscpy(fn + GetSystemDirectory(fn, _countof(fn)), _T("\\d3d9.dll"));
	        HMODULE h = LoadLibrary(fn);
	        * (void**) &o_D3DPERF_BeginEvent             = GetProcAddress(h, "D3DPERF_BeginEvent");
	        * (void**) &o_D3DPERF_EndEvent               = GetProcAddress(h, "D3DPERF_EndEvent");
	        * (void**) &o_D3DPERF_GetStatus              = GetProcAddress(h, "D3DPERF_GetStatus");
	        * (void**) &o_D3DPERF_QueryRepeatFrame       = GetProcAddress(h, "D3DPERF_QueryRepeatFrame");
	        * (void**) &o_D3DPERF_SetMarker              = GetProcAddress(h, "D3DPERF_SetMarker");
	        * (void**) &o_D3DPERF_SetOptions             = GetProcAddress(h, "D3DPERF_SetOptions");
	        * (void**) &o_D3DPERF_SetRegion              = GetProcAddress(h, "D3DPERF_SetRegion");
	        * (void**) &o_DebugSetLevel                  = GetProcAddress(h, "DebugSetLevel");
	        * (void**) &o_DebugSetMute                   = GetProcAddress(h, "DebugSetMute");
	        * (void**) &o_Direct3DCreate9                = GetProcAddress(h, "Direct3DCreate9");
	        * (void**) &o_Direct3DCreate9Ex              = GetProcAddress(h, "Direct3DCreate9Ex");
	        * (void**) &o_Direct3DShaderValidatorCreate9 = GetProcAddress(h, "Direct3DShaderValidatorCreate9");
	        * (void**) &o_PSGPError                      = GetProcAddress(h, "PSGPError");
	        * (void**) &o_PSGPSampleTexture              = GetProcAddress(h, "PSGPSampleTexture");
	    }
	    return TRUE;
	}

effect.fx

uniform extern texture gTexture0;
uniform extern texture gTexture1;

sampler pass1Sampler = sampler_state {
    Texture = <gTexture0>;
    MinFilter = POINT;
    MagFilter = POINT;
    MipFilter = POINT;
    AddressU = BORDER;
    AddressV = BORDER;
    SRGBTexture = FALSE;
};
sampler pass2Sampler = sampler_state {
    Texture = <gTexture1>;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = NONE;
    AddressU = BORDER;
    AddressV = BORDER;
    SRGBTexture = FALSE;
};

float4 Pass1Shader(float2 tex0 : TEXCOORD0) : COLOR0 {
    float4 c0 = tex2D(pass1Sampler, tex0);
    c0.x = 1.0;
    return c0;
}

float4 Pass2Shader(float2 tex0 : TEXCOORD0) : COLOR0 {
    float4 c0 = tex2D(pass2Sampler, tex0);
    c0.z = 0.0;
    return c0;
}

technique PostProcess1 {
    pass p1 {
        PixelShader = compile ps_3_0 Pass1Shader();
    }
}

technique PostProcess2 {
    pass p1 {
        PixelShader = compile ps_3_0 Pass2Shader();
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment