########################################################################## # # Copyright 2011 Jose Fonseca # All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # ##########################################################################/ """D3D retracer generator.""" import sys from dllretrace import DllRetracer as Retracer from specs.stdapi import API class D3DRetracer(Retracer): def retraceApi(self, api): print '// Swizzling mapping for lock addresses' print 'typedef std::pair MappingKey;' print 'static std::map _maps;' print Retracer.retraceApi(self, api) def invokeFunction(self, function): if function.name in ('Direct3DCreate9', 'Direct3DCreate9Ex'): print 'if (retrace::debug >= 2 && !g_szD3D9DllName && LoadLibraryA("d3d9d.dll")) {' print ' /*' print ' * D3D9D only works for simple applications, it will often report bogus errors' print ' * on complex traces, or traces which use unofficial D3D9 features.' print ' */' print ' g_szD3D9DllName = "d3d9d.dll";' print ' SDKVersion |= 0x80000000;' print '} else {' print ' SDKVersion &= ~0x80000000;' print '}' # d3d8d.dll can be found in the Aug 2007 DXSDK. It works on XP, but # not on Windows 7. if function.name in ('Direct3DCreate8'): print 'if (retrace::debug >= 2 && !g_szD3D8DllName && LoadLibraryA("d3d8d.dll")) {' print ' g_szD3D8DllName = "d3d8d.dll";' print '}' Retracer.invokeFunction(self, function) createDeviceMethodNames = [ 'CreateDevice', 'CreateDeviceEx', ] def doInvokeInterfaceMethod(self, interface, method): Retracer.doInvokeInterfaceMethod(self, interface, method) # Keep retrying IDirectXVideoDecoder::BeginFrame when returns E_PENDING if interface.name == 'IDirectXVideoDecoder' and method.name == 'BeginFrame': print r' while (_result == E_PENDING) {' print r' Sleep(1);' Retracer.doInvokeInterfaceMethod(self, interface, method) print r' }' def invokeInterfaceMethod(self, interface, method): # keep track of the last used device for state dumping if interface.name in ('IDirect3DDevice9', 'IDirect3DDevice9Ex'): if method.name == 'Release': print r' if (call.ret->toUInt() == 0) {' print r' d3d9Dumper.unbindDevice(_this);' print r' }' else: print r' d3d9Dumper.bindDevice(_this);' if interface.name in ('IDirect3DDevice8', 'IDirect3DDevice8Ex'): if method.name == 'Release': print r' if (call.ret->toUInt() == 0) {' print r' d3d8Dumper.unbindDevice(_this);' print r' }' else: print r' d3d8Dumper.bindDevice(_this);' # create windows as neccessary if method.name in ('CreateDevice', 'CreateDeviceEx', 'CreateAdditionalSwapChain'): print r' HWND hWnd = pPresentationParameters->hDeviceWindow;' if 'hFocusWindow' in method.argNames(): print r' if (hWnd == NULL) {' print r' hWnd = hFocusWindow;' print r' }' print r' hWnd = d3dretrace::createWindow(hWnd, pPresentationParameters->BackBufferWidth, pPresentationParameters->BackBufferHeight);' print r' pPresentationParameters->hDeviceWindow = hWnd;' if 'hFocusWindow' in method.argNames(): print r' hFocusWindow = hWnd;' # force windowed mode print r' if (retrace::forceWindowed) {' print r' pPresentationParameters->Windowed = TRUE;' print r' pPresentationParameters->FullScreen_RefreshRateInHz = 0;' if interface.name.startswith('IDirect3D8'): print r' pPresentationParameters->FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;' print r' }' if 'BehaviorFlags' in method.argNames(): print r' if (retrace::dumpingState) {' print r' BehaviorFlags &= ~D3DCREATE_PUREDEVICE;' print r' }' # On D3D8, ensure we use BackBufferFormat compatible with the # current DisplayFormat. # # TODO: BackBufferFormat doesn't need to be always exectly to # DisplayFormat. For example, if DisplayFormat is D3DFMT_X1R5G5B5, # valid values for BackBufferFormat include D3DFMT_X1R5G5B5 and # D3DFMT_A1R5G5B5, but exclude D3DFMT_R5G6B5. if interface.name.startswith('IDirect3D8'): print r' if (pPresentationParameters->Windowed) {' print r' D3DDISPLAYMODE Mode;' print r' HRESULT hr;' print r' hr = _this->GetAdapterDisplayMode(Adapter, &Mode);' print r' assert(SUCCEEDED(hr));' print r' hr = _this->CheckDeviceType(Adapter, DeviceType, Mode.Format, pPresentationParameters->BackBufferFormat, pPresentationParameters->Windowed);' print r' if (hr == D3DERR_NOTAVAILABLE) {' print r' retrace::warning(call) << "forcing back buffer format to match display mode format\n";' print r' pPresentationParameters->BackBufferFormat = Mode.Format;' print r' }' print r' }' if method.name in self.createDeviceMethodNames: # override the device type print r' switch (retrace::driver) {' print r' case retrace::DRIVER_HARDWARE:' print r' DeviceType = D3DDEVTYPE_HAL;' print r' break;' print r' case retrace::DRIVER_SOFTWARE:' print r' case retrace::DRIVER_REFERENCE:' print r' DeviceType = D3DDEVTYPE_REF;' print r' break;' print r' case retrace::DRIVER_NULL:' if interface.name.startswith('IDirect3D9'): print r' DeviceType = D3DDEVTYPE_NULLREF;' else: print r' retrace::warning(call) << "null driver not supported\n";' print r' break;' print r' case retrace::DRIVER_MODULE:' print r' retrace::warning(call) << "driver module not supported\n";' print r' break;' print r' default:' print r' assert(0);' print r' /* fall-through */' print r' case retrace::DRIVER_DEFAULT:' print r' break;' print r' }' if method.name in ('Reset', 'ResetEx'): # force windowed mode print r' if (retrace::forceWindowed) {' print r' pPresentationParameters->Windowed = TRUE;' print r' pPresentationParameters->FullScreen_RefreshRateInHz = 0;' print r' }' # resize window print r' if (pPresentationParameters->Windowed) {' print r' d3dretrace::resizeWindow(pPresentationParameters->hDeviceWindow, pPresentationParameters->BackBufferWidth, pPresentationParameters->BackBufferHeight);' print r' }' # notify frame has been completed if method.name in ('Present', 'PresentEx'): if interface.name.startswith('IDirect3DSwapChain9'): print r' d3d9scDumper.bindDevice(_this);' print r' retrace::frameComplete(call);' print r' hDestWindowOverride = NULL;' # Ensure textures can be locked when dumping # TODO: Pre-check with CheckDeviceFormat if method.name in ('CreateTexture', 'CreateCubeTexture', 'CreateVolumeTexture'): print r' if (retrace::dumpingState &&' print r' Pool == D3DPOOL_DEFAULT &&' print r' !(Usage & (D3DUSAGE_RENDERTARGET|D3DUSAGE_DEPTHSTENCIL))) {' print r' Usage |= D3DUSAGE_DYNAMIC;' print r' }' # Deal with shared surfaces # https://msdn.microsoft.com/en-us/library/windows/desktop/bb219800.aspx # https://msdn.microsoft.com/en-gb/library/windows/desktop/ee913554.aspx pSharedHandleArg = method.getArgByName('pSharedHandle') if pSharedHandleArg: print r' if (pSharedHandle) {' if method.name == 'CreateTexture': # Some applications (e.g., DOTA2) create shared resources within the same process. # https://msdn.microsoft.com/en-us/library/windows/desktop/bb219800.aspx#Textures print r' if (Pool == D3DPOOL_SYSTEMMEM) {' print r' // Ensure the memory stays around.' print r' trace::Blob *blob = call.arg(%u).toArray()->values[0]->toBlob();' % pSharedHandleArg.index print r' if (blob) {' print r' blob->toPointer(true);' print r' } else {' print r' retrace::warning(call) << "invalid system memory\n";' print r' pSharedHandle = NULL;' print r' }' print r' } else {' print r' retrace::warning(call) << "shared surfaces unsupported\n";' print r' pSharedHandle = NULL;' if method.name == 'CreateTexture': print r' }' print r' }' if method.name in ('Lock', 'LockRect', 'LockBox'): # Reset _DONOTWAIT flags. Otherwise they may fail, and we have no # way to cope with it (other than retry). mapFlagsArg = method.getArgByName('Flags') for flag in mapFlagsArg.type.values: if flag.endswith('_DONOTWAIT'): print r' Flags &= ~%s;' % flag Retracer.invokeInterfaceMethod(self, interface, method) if method.name in self.createDeviceMethodNames: print r' if (FAILED(_result)) {' print r' exit(1);' print r' }' # process events after presents if method.name == 'Present': print r' d3dretrace::processEvents();' def mapping_subkey(): if 'Level' in method.argNames(): return ('Level',) else: return ('0',) if method.name in ('Lock', 'LockRect', 'LockBox'): print ' VOID *_pbData = NULL;' print ' size_t _MappedSize = 0;' if method.name == 'Lock': # Ignore D3DLOCK_READONLY for buffers. # https://github.com/apitrace/apitrace/issues/435 print ' if (true) {' else: print ' if (!(Flags & D3DLOCK_READONLY)) {' print ' _getMapInfo(_this, %s, _pbData, _MappedSize);' % ', '.join(method.argNames()[:-1]) print ' }' print ' if (_MappedSize) {' print ' _maps[MappingKey(_this, %s)] = _pbData;' % mapping_subkey() self.checkPitchMismatch(method) print ' } else {' print ' return;' print ' }' if method.name in ('Unlock', 'UnlockRect', 'UnlockBox'): print ' VOID *_pbData = 0;' print ' MappingKey _mappingKey(_this, %s);' % mapping_subkey() print ' _pbData = _maps[_mappingKey];' print ' if (_pbData) {' print ' retrace::delRegionByPointer(_pbData);' print ' _maps[_mappingKey] = 0;' print ' }' if interface.name == 'IDirectXVideoDecoder': if method.name == 'GetBuffer': print ' if (*ppBuffer && *pBufferSize) {' print ' _maps[MappingKey(_this, BufferType)] = *ppBuffer;' print ' }' if method.name == 'ReleaseBuffer': print ' MappingKey _mappingKey(_this, BufferType);' print ' void *_pBuffer = _maps[_mappingKey];' print ' if (_pBuffer) {' print ' retrace::delRegionByPointer(_pBuffer);' print ' _maps[_mappingKey] = 0;' print ' }' def main(): print r'#include ' print print r'#include ' print print r'#include "d3dretrace.hpp"' print moduleName = sys.argv[1] support = int(sys.argv[2]) api = API() if support: if moduleName == 'd3d9': from specs.d3d9 import d3d9, d3dperf from specs.dxva2 import dxva2 print r'#include "d3d9imports.hpp"' print r'#include "d3d9size.hpp"' print r'#include "dxva2imports.hpp"' d3d9.mergeModule(d3dperf) api.addModule(d3d9) api.addModule(dxva2) print print '''static d3dretrace::D3DDumper d3d9Dumper;''' print '''static d3dretrace::D3DDumper d3d9scDumper;''' print elif moduleName == 'd3d8': from specs.d3d8 import d3d8 print r'#include "d3d8imports.hpp"' print r'#include "d3d8size.hpp"' api.addModule(d3d8) print print '''static d3dretrace::D3DDumper d3d8Dumper;''' print else: assert False retracer = D3DRetracer() retracer.table_name = 'd3dretrace::%s_callbacks' % moduleName retracer.retraceApi(api) if __name__ == '__main__': main()