Add native K/M/RR inference engines, HIP kernels, replay and validation harnesses, and Windows DLSS setup and runtime support. Include GPU profiling tools, build and packaging scripts, platform integration patches, and implementation documentation.
138 lines
8.8 KiB
Python
138 lines
8.8 KiB
Python
"""Generate typed command forwarding from public MinGW D3D12 SDK declarations.
|
|
|
|
Run with --header <SDK/include/d3d12.h> --output d3d12_command_hooks.generated.h.
|
|
Every recording method through CommandList10 is forwarded. State setters copy
|
|
their host arguments; COM object arguments retain a reference. SDK slot numbers
|
|
are derived from the public interface inheritance, never a driver binary.
|
|
"""
|
|
import argparse
|
|
import pathlib
|
|
import re
|
|
|
|
# Public additions verified against Microsoft's DirectX-Headers adbd6f3b.
|
|
# The old MinGW SDK has the needed scalar types but lacks the work-graph
|
|
# descriptor names; NewProgramDescription is a typed public-ABI mirror.
|
|
NEWER = [
|
|
(8, 'OMSetFrontAndBackStencilRef', 'UINT front_stencil, UINT back_stencil'),
|
|
(9, 'RSSetDepthBias', 'FLOAT depth_bias, FLOAT clamp, FLOAT slope'),
|
|
(9, 'IASetIndexBufferStripCutValue', 'D3D12_INDEX_BUFFER_STRIP_CUT_VALUE value'),
|
|
(10, 'SetProgram', 'const NewProgramDescription* desc'),
|
|
(10, 'DispatchGraph', 'const void* desc'),
|
|
]
|
|
|
|
POINTER_COUNTS = {
|
|
'RSSetViewports': {'viewports': 'viewport_count'},
|
|
'RSSetScissorRects': {'rects': 'rect_count'},
|
|
'OMSetBlendFactor': {'blend_factor': '4'},
|
|
'SetDescriptorHeaps': {'heaps': 'heap_count'},
|
|
'SetComputeRoot32BitConstants': {'data': 'constant_count'},
|
|
'SetGraphicsRoot32BitConstants': {'data': 'constant_count'},
|
|
'IASetIndexBuffer': {'view': '1'},
|
|
'IASetVertexBuffers': {'views': 'view_count'},
|
|
'SOSetTargets': {'views': 'view_count'},
|
|
'OMSetRenderTargets': {'render_target_descriptors': '(single_descriptor_handle ? 1 : render_target_descriptor_count)', 'depth_stencil_descriptor': '1'},
|
|
'SetSamplePositions': {'sample_positions': 'sample_count * pixel_count'},
|
|
'RSSetShadingRate': {'combiners': '2'},
|
|
}
|
|
SPECIAL = {'Close', 'Reset', 'ClearState', 'ResourceBarrier', 'Barrier',
|
|
'BeginEvent', 'EndEvent', 'BeginRenderPass', 'EndRenderPass',
|
|
'BeginQuery', 'EndQuery', 'ExecuteBundle', 'ExecuteIndirect', 'SetPredication'}
|
|
|
|
|
|
def generate(source):
|
|
classes = []
|
|
for match in re.finditer(r'\n(ID3D12GraphicsCommandList\d*) : public \w+\s*\{(.*?)\n\};', source, re.S):
|
|
name, body = match.groups()
|
|
methods = re.findall(r'virtual\s+(.*?)\s+STDMETHODCALLTYPE\s+(\w+)\s*\((.*?)\)\s*= 0;', body, re.S)
|
|
classes.append((int(name[len('ID3D12GraphicsCommandList'):] or '0'), name, methods))
|
|
if [version for version, _, _ in sorted(classes)] != list(range(8)):
|
|
raise RuntimeError('Expected exactly the public CommandList0..7 interfaces; review a changed SDK')
|
|
lines = ['// Generated by generate_d3d12_hooks.py from the public SDK. Do not edit.', '']
|
|
registrations = []
|
|
slot = 9 # IUnknown(3), ID3D12Object(4), DeviceChild(1), CommandList(1).
|
|
for version, interface, methods in sorted(classes):
|
|
for result, name, arguments in methods:
|
|
# MinGW WINBOOL and Microsoft SDK BOOL have the same ABI.
|
|
result = ' '.join(result.split()).replace('WINBOOL', 'BOOL')
|
|
arguments = ' '.join(arguments.split()).replace('WINBOOL', 'BOOL')
|
|
declarations = [arg.strip() for arg in arguments.split(',')] if arguments else []
|
|
names = []
|
|
for declaration in declarations:
|
|
match = re.search(r'(\w+)\s*(?:\[\d+\])?$', declaration)
|
|
if not match:
|
|
raise RuntimeError(f'Unparsed SDK argument: {name}: {declaration}')
|
|
names.append(match[1])
|
|
signature = ', '.join(['ID3D12GraphicsCommandList* self'] + declarations)
|
|
call = ', '.join(['access.target()'] + names)
|
|
lines.append(f'using Fn_{name} = {result}(STDMETHODCALLTYPE*)({signature});')
|
|
lines.append(f'static Fn_{name} original_{name} = nullptr;')
|
|
if name in SPECIAL:
|
|
lines.append(f'static {result} STDMETHODCALLTYPE hook_{name}({signature});')
|
|
else:
|
|
lines += [f'static {result} STDMETHODCALLTYPE hook_{name}({signature}) {{', ' Access access(self);']
|
|
stateful = name.startswith(('Set', 'IASet', 'RSSet', 'OMSet', 'SOSet')) and name != 'SetMarker'
|
|
if stateful:
|
|
captured = []
|
|
for declaration, argument in zip(declarations, names):
|
|
pointer = '*' in declaration or '[' in declaration
|
|
if pointer and 'ID3D12' in declaration:
|
|
if declaration.count('*') > 1:
|
|
count = POINTER_COUNTS.get(name, {}).get(argument)
|
|
if count is None:
|
|
raise RuntimeError(f'No COM array copy rule: {name}: {declaration}')
|
|
lines.append(f' auto saved_{argument} = retain_values({argument}, {count});')
|
|
captured.append(f'saved_{argument}.raw.data()')
|
|
else:
|
|
lines.append(f' auto saved_{argument} = retain({argument});')
|
|
captured.append(f'saved_{argument}.Get()')
|
|
elif pointer:
|
|
count = POINTER_COUNTS.get(name, {}).get(argument)
|
|
if count is None:
|
|
raise RuntimeError(f'No deep-copy rule for {name}: {declaration}')
|
|
value = f'static_cast<const UINT*>({argument})' if argument == 'data' else argument
|
|
lines.append(f' auto saved_{argument} = copy_values({value}, {count});')
|
|
captured.append(f'values_pointer(saved_{argument})')
|
|
else:
|
|
captured.append(argument)
|
|
lines += [' if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) mutable {',
|
|
f' original_{name}(' + ', '.join(['target'] + captured) + ');', ' });']
|
|
lines += [' access.driver_call();', f' return original_{name}({call});', '}']
|
|
registrations.append(f' attach_method(table[{slot}], reinterpret_cast<void*>(hook_{name}), reinterpret_cast<void**>(&original_{name}), "{name}");')
|
|
registrations[-1] = f' if (version >= {version})' + registrations[-1][3:]
|
|
slot += 1
|
|
lines.append('')
|
|
for version, name, arguments in NEWER:
|
|
declarations = arguments.split(', ')
|
|
names = [re.search(r'(\w+)$', argument)[1] for argument in declarations]
|
|
signature = ', '.join(['ID3D12GraphicsCommandList* self'] + declarations)
|
|
lines += [f'using Fn_{name} = void(STDMETHODCALLTYPE*)({signature});', f'static Fn_{name} original_{name} = nullptr;']
|
|
if name == 'SetProgram':
|
|
lines.append(f'static void STDMETHODCALLTYPE hook_{name}({signature});')
|
|
else:
|
|
lines += [f'static void STDMETHODCALLTYPE hook_{name}({signature}) {{', ' Access access(self);']
|
|
if name != 'DispatchGraph':
|
|
lines += [' if (access.recording) access.recording->state.emplace_back([=](ID3D12GraphicsCommandList* target) {',
|
|
f' original_{name}(' + ', '.join(['target'] + names) + ');', ' });']
|
|
lines += [' access.driver_call();', f' original_{name}(' + ', '.join(['access.target()'] + names) + ');', '}']
|
|
registrations.append(f' if (version >= {version}) attach_method(table[{slot}], reinterpret_cast<void*>(hook_{name}), reinterpret_cast<void**>(&original_{name}), "{name}");')
|
|
slot += 1
|
|
lines.append('')
|
|
lines += ['static void attach_recording_methods(void** table, unsigned version) {'] + registrations + ['}', '']
|
|
device = re.search(r'typedef struct ID3D12DeviceVtbl \{(.*?)\n\} ID3D12DeviceVtbl;', source, re.S)[1]
|
|
fields = list(dict.fromkeys(re.findall(r'\(STDMETHODCALLTYPE \*(\w+)\)', device)))
|
|
lines.append(f'static constexpr unsigned device_signature_slot = {fields.index("CreateCommandSignature")};')
|
|
lines.append(f'static constexpr unsigned device_command_list_slot = {fields.index("CreateCommandList")};')
|
|
queue = re.search(r'typedef struct ID3D12CommandQueueVtbl \{(.*?)\n\} ID3D12CommandQueueVtbl;', source, re.S)[1]
|
|
queue_fields = list(dict.fromkeys(re.findall(r'\(STDMETHODCALLTYPE \*(\w+)\)', queue)))
|
|
for name in ('Signal', 'Wait'):
|
|
lines.append(f'static constexpr unsigned queue_{name.lower()}_slot = {queue_fields.index(name)};')
|
|
return '\n'.join(lines)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--header', type=pathlib.Path, required=True)
|
|
parser.add_argument('--output', type=pathlib.Path, required=True)
|
|
args = parser.parse_args()
|
|
args.output.write_text(generate(args.header.read_text()), encoding='utf-8')
|