blob: 33dfa673d0547b65c3cad3ff3d0ce5f9768eafea [file] [log] [blame]
##########################################################################
#
# 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<void *, UINT> MappingKey;')
print('static std::map<MappingKey, void *> _maps;')
print()
Retracer.retraceApi(self, api)
def invokeFunction(self, function):
if function.name.startswith('Direct3DCreate9'):
print(r' if (retrace::debug >= 3 && !g_szD3D9DllName && LoadLibraryA("d3d9d.dll")) {')
print(r' /*')
print(r' * D3D9D only works for simple applications, it will often report bogus errors')
print(r' * on complex traces, or traces which use unofficial D3D9 features.')
print(r' */')
print(r' g_szD3D9DllName = "d3d9d.dll";')
print(r' SDKVersion |= 0x80000000;')
print(r' } else {')
print(r' SDKVersion &= ~0x80000000;')
print(r' }')
# d3d8d.dll can be found in the Aug 2007 DXSDK. It works on XP, but
# not on Windows 7.
if function.name.startswith('Direct3DCreate8'):
print(r' if (retrace::debug >= 3 && !g_szD3D8DllName && LoadLibraryA("d3d8d.dll")) {')
print(r' g_szD3D8DllName = "d3d8d.dll";')
print(r' }')
if function.name.startswith('Direct3DCreate9'):
print(r' // 0: default')
print(r' // 1: force discrete')
print(r' // 2/3: force integrated')
print(r' UINT uHybrid = 0;')
print(r' if (retrace::driver == retrace::DRIVER_DISCRETE) {')
print(r' uHybrid = 1;')
print(r' }')
print(r' if (retrace::driver == retrace::DRIVER_INTEGRATED) {')
print(r' uHybrid = 2;')
print(r' }')
print(r' if (uHybrid != 0) {')
print(r' HMODULE hD3D9 = LoadLibraryA("D3D9");')
print(r' assert(hD3D9);')
print(r' typedef void (WINAPI *PFNDIRECT3D9FORCEHYBRIDENUMERATION)(UINT);')
print(r' PFNDIRECT3D9FORCEHYBRIDENUMERATION pfnDirect3D9ForceHybridEnumeration =')
print(r' (PFNDIRECT3D9FORCEHYBRIDENUMERATION)GetProcAddress(hD3D9, MAKEINTRESOURCEA(16));')
print(r' if (pfnDirect3D9ForceHybridEnumeration) {')
print(r' pfnDirect3D9ForceHybridEnumeration(uHybrid);')
print(r' }')
print(r' }')
Retracer.invokeFunction(self, function)
if function.name.startswith('Direct3DCreate'):
print(r' if (retrace::driver == retrace::DRIVER_DISCRETE ||')
print(r' retrace::driver == retrace::DRIVER_INTEGRATED) {')
if function.name == 'Direct3DCreate9Ex':
print(r' auto pD3D = SUCCEEDED(_result) ? *ppD3D : nullptr;')
else:
print(r' auto pD3D = _result;')
if function.name.startswith('Direct3DCreate9'):
print(r' D3DADAPTER_IDENTIFIER9 Identifier;')
else:
assert function.name.startswith('Direct3DCreate8')
print(r' D3DADAPTER_IDENTIFIER8 Identifier;')
print(r' if (pD3D) {')
print(r' if (SUCCEEDED(pD3D->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &Identifier))) {')
print(r' std::cerr << "info: using " << Identifier.Description << std::endl;')
print(r' }')
print(r' }')
print(r' }')
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);')
if method.name in self.createDeviceMethodNames:
# override the device type
print(r' switch (retrace::driver) {')
print(r' case retrace::DRIVER_HARDWARE:')
print(r' case retrace::DRIVER_DISCRETE:')
print(r' case retrace::DRIVER_INTEGRATED:')
print(r' DeviceType = D3DDEVTYPE_HAL;')
print(r' break;')
print(r' case retrace::DRIVER_SOFTWARE:')
print(r' BehaviorFlags &= ~D3DCREATE_PUREDEVICE;')
print(r' BehaviorFlags &= ~D3DCREATE_HARDWARE_VERTEXPROCESSING;')
print(r' BehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;')
print(r' break;')
print(r' case retrace::DRIVER_REFERENCE:')
print(r' DeviceType = D3DDEVTYPE_REF;')
print(r' BehaviorFlags &= ~D3DCREATE_PUREDEVICE;')
print(r' BehaviorFlags &= ~D3DCREATE_HARDWARE_VERTEXPROCESSING;')
print(r' BehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;')
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' }')
# 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 ('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)
# SetClipPlane methods were broken until cf519352c
if method.name == 'SetClipPlane':
print(r' if (retrace::parser->getVersion() < 6) {')
print(r' const trace::Array *_pPlane = call.arg(2).toArray();')
print(r' if (_pPlane && _pPlane->values.size() < 4) {')
print(r' static bool _warned = false;')
print(r' if (!_warned) {')
print(r' retrace::warning(call) << "ignoring incomplete SetClipPlane plane\n";')
print(r' _warned = true;')
print(r' }')
print(r' static float _zeroPlane[4] = { 0.0f, 0.0f, 0.0f, 0.0f };')
print(r' pPlane = _zeroPlane;')
print(r' }')
print(r' }')
Retracer.invokeInterfaceMethod(self, interface, method)
# process events after presents
if method.name == 'Present':
print(r' d3dretrace::processEvents();')
def mapping_subkey():
# A single texture object might have multiple mappings. This key
# allows to tell them apart.
if 'FaceType' in method.argNames():
return ('static_cast<UINT>(FaceType) + Level*6',)
elif 'Level' in method.argNames():
return ('Level',)
else:
return ('0',)
if method.name in ('Lock', 'LockRect', 'LockBox'):
print(' VOID *_pbData = nullptr;')
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 = nullptr;')
print(' MappingKey _mappingKey(_this, %s);' % mapping_subkey())
print(' _pbData = _maps[_mappingKey];')
print(' if (_pbData) {')
print(' retrace::delRegionByPointer(_pbData);')
print(' _maps[_mappingKey] = nullptr;')
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] = nullptr;')
print(' }')
def handleFailure(self, interface, methodOrFunction):
if methodOrFunction.name in self.createDeviceMethodNames:
print(r' exit(EXIT_FAILURE);')
return
# https://msdn.microsoft.com/en-us/library/windows/desktop/bb324479.aspx
if methodOrFunction.name in ('Present', 'PresentEx'):
print(r' if (_result == D3DERR_DEVICELOST) {')
print(r' exit(EXIT_FAILURE);')
print(r' }')
Retracer.handleFailure(self, interface, methodOrFunction)
def main():
print(r'#include <string.h>')
print()
print(r'#include <iostream>')
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<IDirect3DDevice9> d3d9Dumper;''')
print('''static d3dretrace::D3DDumper<IDirect3DSwapChain9> 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<IDirect3DDevice8> d3d8Dumper;''')
print()
else:
assert False
retracer = D3DRetracer()
retracer.table_name = 'd3dretrace::%s_callbacks' % moduleName
retracer.retraceApi(api)
if __name__ == '__main__':
main()