[D3D] - 用PerfHUD来调试商业游戏
來(lái)源:http://blog.csdn.net/RAINini/archive/2008/10/28/3162112.aspx
?
?? ? ? ? PerfHUD是NV一個(gè)非常好用的工具,可以用于查看游戲的運(yùn)行效率,找出瓶頸,也可以用于分析游戲渲染流程,看每個(gè)DPC的渲染操作,包括渲染狀態(tài),所用的shader等,非常強(qiáng)大。
?????????PerfHUD正常的用途是用于調(diào)試自己寫(xiě)的程序,但是有時(shí)候看到別的游戲一些好的效果,也想了解是怎么實(shí)現(xiàn)的,這時(shí),PerfHUD也可以派上用場(chǎng)。
?? ? ? ? 要使用PerfHUD,就要對(duì)自己的D3D程序進(jìn)行修改,在CreateDevice時(shí),Adapter這個(gè)參數(shù)不要選用D3DADAPTER_DEFAULT,而是枚舉所有的Adapter,選中Description中帶有“PerfHUD”字符串的那個(gè)Adapter,一般來(lái)說(shuō),對(duì)于單顯卡的機(jī)器,通常都是第二個(gè)Adapter,我沒(méi)有多顯卡,所以不知道多顯卡情況下是不是往后順移。
?????????同時(shí),DeviceType不要選D3DDEVTYPE_HAL,而要選D3DDEVTYPE_REF。
?????????通過(guò)這樣來(lái)創(chuàng)建Device的程序,才能用PerfHUD來(lái)掛接并分析,更詳細(xì)的創(chuàng)建過(guò)程,查看PerfHUD的幫助文檔吧。
?????????由上可以看出,對(duì)于別人的程序,Device都不是自己創(chuàng)建的,似乎不能用PerfHUD來(lái)分析了,但經(jīng)過(guò)對(duì)要分析的程序做一些手腳后,也是可以的,以下拿《魔獸世界》來(lái)舉例。
?????????首先,打開(kāi)VC(我用的2005,2003大同小異,VC6沒(méi)試過(guò)),確保Options里的這個(gè)選項(xiàng)是選中的,
?
?
?
?????????只有選中它了,才能用函數(shù)名稱來(lái)作為斷點(diǎn)斷住DLL里的函數(shù)。
?? ? ? ? 然后用VC來(lái)把要分析的游戲的exe執(zhí)行起來(lái),點(diǎn)擊VC的File->Open ->Project,在彈出的對(duì)話框中選中要分析的exe文件,如下圖:
?
?
?
?????????把文件加進(jìn)來(lái)后,在Solution下可以看到該文件,右鍵選Debug->Step into new instance,啟動(dòng)程序,然后下一個(gè)函數(shù)斷點(diǎn),點(diǎn)擊菜單中的Debug->New Breakpoint->Break at function,在彈出的對(duì)話框中寫(xiě)入Direct3DCreate9,如下圖:
?
?
?
?????????點(diǎn)OK后,就下好斷點(diǎn)了,如果一個(gè)程序用的D3D,那么應(yīng)該會(huì)在這個(gè)斷點(diǎn)斷住,要靠它來(lái)獲取IDirect3D9這個(gè)interface的指針。
?????????做完以上步驟后,按F5讓程序繼續(xù)運(yùn)行,應(yīng)該會(huì)斷在剛才那個(gè)函數(shù)斷點(diǎn),如下圖:
?
?
?
?????????這時(shí)按Shift+F11,退出這個(gè)函數(shù)體,如果創(chuàng)建成功,應(yīng)該就獲取到了IDirect3D9,因?yàn)檫@個(gè)函數(shù)的返回值就是IDirect3D9的指針,只需看eax這個(gè)寄存器的值,就能找到這個(gè)指針,因?yàn)閑ax是存放函數(shù)返回值。
?????????在watch窗口里打入eax,當(dāng)前eax的值是0x0016CFC0。
?????????打開(kāi)一個(gè)memory窗口,把0x0016CFC0這個(gè)值敲進(jìn)去,然后點(diǎn)在memory窗口下點(diǎn)右鍵,用4-byte Integer來(lái)查看內(nèi)存,把0x0016CFC0這塊內(nèi)存的前4個(gè)字節(jié)的值拷貝下來(lái),我機(jī)器上顯示的值是4b641a98,為什么要拷貝這個(gè)值呢,因?yàn)橐粋€(gè)帶虛擬函數(shù)的類的指針?biāo)赶虻牡刂?#xff0c;最開(kāi)頭的4個(gè)字節(jié)就是虛函數(shù)表的指針,通過(guò)它能找到各個(gè)虛函數(shù)的函數(shù)地址。
?????????在memory窗口里把這個(gè)值加上h后敲進(jìn)去,同樣用4-byte Integer來(lái)查看,得到就是IDirect3D9這個(gè)接口的各個(gè)函數(shù)的入口地址,真正要找的就是CreateDevice這個(gè)函數(shù)的地址,這時(shí),打開(kāi)你裝的D3D SDK的d3d.h,搜索IDirect3D9這個(gè)接口的聲明,應(yīng)該會(huì)找到如下代碼:
?
DECLARE_INTERFACE_(IDirect3D9, IUnknown){
/*** IUnknown methods ***/
STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
/*** IDirect3D9 methods ***/
STDMETHOD(RegisterSoftwareDevice)(THIS_ void* pInitializeFunction) PURE;
STDMETHOD_(UINT, GetAdapterCount)(THIS) PURE;
STDMETHOD(GetAdapterIdentifier)(THIS_ UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER9* pIdentifier) PURE;
STDMETHOD_(UINT, GetAdapterModeCount)(THIS_ UINT Adapter,D3DFORMAT Format) PURE;
STDMETHOD(EnumAdapterModes)(THIS_ UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode) PURE;
STDMETHOD(GetAdapterDisplayMode)(THIS_ UINT Adapter,D3DDISPLAYMODE* pMode) PURE;
STDMETHOD(CheckDeviceType)(THIS_ UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed) PURE;
STDMETHOD(CheckDeviceFormat)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) PURE;
STDMETHOD(CheckDeviceMultiSampleType)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels) PURE;
STDMETHOD(CheckDepthStencilMatch)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) PURE;
STDMETHOD(CheckDeviceFormatConversion)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SourceFormat,D3DFORMAT TargetFormat) PURE;
STDMETHOD(GetDeviceCaps)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS9* pCaps) PURE;
STDMETHOD_(HMONITOR, GetAdapterMonitor)(THIS_ UINT Adapter) PURE;
STDMETHOD(CreateDevice)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) PURE;
#ifdef D3D_DEBUG_INFO
LPCWSTR Version;
#endif
};
?
?????????數(shù)了一下,發(fā)現(xiàn)CreateDevice是第17個(gè)函數(shù),所以應(yīng)該在虛函數(shù)表里的第17個(gè)元素,所以在4b641a98h所指向的內(nèi)存往后數(shù),第17個(gè)指針就是CreateDevice的函數(shù)入口點(diǎn),我機(jī)器上的值是4b6c1670,應(yīng)該說(shuō),在沒(méi)做特別處理的情況下,同個(gè)D3D的版本,某個(gè)函數(shù)的函數(shù)地址都是一樣的。
?????????在Disassembly代碼窗口頂部的Address里輸入4b6c1670h這個(gè)值,然后敲回車,就能定位到CreateDevice的函數(shù)體,按F9下個(gè)斷點(diǎn),然后按F5讓程序繼續(xù)運(yùn)行,應(yīng)該能斷到這個(gè)函數(shù),如下圖:
?
?
??? ? ? ? 這時(shí),查看esp的值,找出調(diào)用這個(gè)函數(shù)的地方,當(dāng)前esp的值是005a4c4b,在剛進(jìn)入函數(shù)體時(shí),esp保存的是這個(gè)函數(shù)返回后要繼續(xù)執(zhí)行的下條指令的地址,所以在這條指令的上方,就是調(diào)用CreateDevice的代碼了,同樣,在Disassembly窗口里輸入005a4c4bh,定位到該條指令,如下圖:
?
?
?
?????????斷點(diǎn)上方的那條call指令就是調(diào)用CreateDevice的地方
?????????上圖中,從那條call指令往上數(shù),第一個(gè)push是push this指針,也就是IDirect3D接口的指針push進(jìn)去,第二個(gè)push就push第一個(gè)參數(shù),可以看到,它原來(lái)是直接push 0的,也就是用D3DADAPTER_DEFAULT來(lái)作為第一個(gè)參數(shù)傳給CreateDevice,再上面的一個(gè)push就是放入第二個(gè)參數(shù)的,可以看到是以下指令完成的,
?
mov ebx,1push ebx
?
也就是說(shuō)把1做為第二個(gè)參數(shù),其實(shí)也就是D3DDEVTYPE_HAL,所以,《魔獸世界》調(diào)用CreateDevice是中規(guī)中矩的。這時(shí)要做的就是把修改這段代碼,從mov ebx,1這條指令開(kāi)始替換,把它變成push 2,push 1,也就是把傳給CreateDevice的第一個(gè)參數(shù)改成1,代表第二個(gè)Adapter,把第二個(gè)參數(shù)改成2,也就是D3DDEVTYPE_REF所代表的值。
?????????上圖里的這三條指令就是要替換掉的指令:
?
005A4C3D BB 01 00 00 00 mov ebx,1005A4C42 53 push ebx
005A4C43 6A 00 push 0
?
?????????要替換成
?
Push 2Push 1
?
?????????這兩條指令,一共是要替換8個(gè)字節(jié)的機(jī)器碼。
?????????這時(shí)可以通過(guò)直接修改exe文件本身來(lái)達(dá)到目的,先把wow.exe復(fù)制一份,我把復(fù)制后的exe起名為wow_m.exe,然后用UltraEdit打開(kāi)該exe文件,查找要修改的指令的機(jī)器碼,從mov ebx,1這條指令的機(jī)器碼搜索起,可以看到,該條指令的機(jī)器碼是BB 01 00 00 00,一般來(lái)說(shuō),搜索的字節(jié)越多,越準(zhǔn)確,因?yàn)槟闼训锰?#xff0c;極有可能有好多個(gè)地方調(diào)用了同條指令,所以我直接把這條指令后面的機(jī)器碼也加上,如下圖:
?
?
?
?????????記得把“查找?ASCII”這個(gè)勾去掉,然后點(diǎn)下一個(gè),就能定位到該指令所在的地方。一定要確保找到的地方只有一處,如果有多處,就用再多一些的機(jī)器碼來(lái)搜索。最終定位到的地方如下圖:
?
?
?
?????????從BB這個(gè)字節(jié)開(kāi)始替換,把光標(biāo)定位在第一個(gè)B處,然后順序輸入6A 02 6A 01,也就是push 2和push 1的機(jī)器碼。這時(shí)還剩下4個(gè)字節(jié),可全部替換成90,替換后的代碼如下:
?
?
?
?????????修改工作到此結(jié)束,保存該exe,然后就可以用PerfHUD來(lái)調(diào)試了,下圖就是掛接上PerfHUD后的《魔獸世界》運(yùn)行截圖:
?
?
?
?
總結(jié)以上步驟:
1.在程序啟動(dòng)時(shí)下斷點(diǎn),斷到執(zhí)行Direct3DCreate9的地方,獲取到IDirect3D指針。
2.用IDirect3D指針獲取到虛函數(shù)表指針,查找到CreateDevice的函數(shù)體。
3.在該函數(shù)體處下斷點(diǎn),斷住后用esp來(lái)找到調(diào)用該函數(shù)的地方。
4.找到給CreateDevice傳參數(shù)的代碼,并修改成PerfHUD所要求的參數(shù)。
只要是沒(méi)加過(guò)密的D3D9程序,應(yīng)該都可以用以上方法來(lái)修改,我只試過(guò)《Crysis》,《WOW》,《PES2009》等少數(shù)游戲。
希望此文能幫助各位有興趣探索別的游戲制作方法的朋友。
轉(zhuǎn)載于:https://www.cnblogs.com/hcbin/archive/2010/07/23/1783671.html
總結(jié)
以上是生活随笔為你收集整理的[D3D] - 用PerfHUD来调试商业游戏的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: [转]第(前)k大数问题
- 下一篇: 总结XX餐饮收银项目中的得与失