diff --git a/helpers/d3d12pba.hpp b/helpers/d3d12pba.hpp index d10f3cd123..f10f787d10 100644 --- a/helpers/d3d12pba.hpp +++ b/helpers/d3d12pba.hpp @@ -223,15 +223,56 @@ _map_resource(ID3D12Resource* pResource, void* pData) g_D3D12AddressMappings.try_emplace(key, _D3D12_MAPPING_WRITE_WATCH, pData, _getMapSize(pResource)); } -static inline void -_unmap_resource(ID3D12Resource* pResource) +static inline size_t _d3d12_AllocationSize(const void *pAddress) { - D3D12_HEAP_FLAGS flags = _get_heap_flags(pResource); + MEMORY_BASIC_INFORMATION info; - if (!(flags & D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH)) + /* Query the base pointer */ + if (!VirtualQuery(pAddress, &info, sizeof(info))) + return 0; + + /* Allocation base must equal address. */ + if (info.AllocationBase != pAddress) + return 0; + if (info.BaseAddress != info.AllocationBase) + return 0; + + /* All pages must be committed. */ + if (info.State != MEM_COMMIT) + return 0; + + const size_t allocation_size = info.RegionSize; + + /* All pages must have same protections, so there cannot be multiple regions for VirtualQuery. */ + if (VirtualQuery((uint8_t *)pAddress + allocation_size, &info, sizeof(info)) && + info.AllocationBase == pAddress) + return 0; + + return allocation_size; +} + + +static inline void +_map_resource(ID3D12Heap* pResource, void* pData) +{ + SIZE_T allocation_size = _d3d12_AllocationSize(pData); + if (!allocation_size) return; - SIZE_T key = static_cast(reinterpret_cast(pResource)); + SIZE_T key = reinterpret_cast(pResource); + // Assert we're page aligned... If we aren't we need to do more work here. + // TODO(Josh) : Placed resources. + assert(reinterpret_cast(pData) % 4096 == 0); + auto iter = g_D3D12AddressMappings.find(key); + if (iter != g_D3D12AddressMappings.end()) + iter->second.RefCount++; + else + g_D3D12AddressMappings.try_emplace(key, _D3D12_MAPPING_WRITE_WATCH, pData, allocation_size); +} + +static inline void +_unmap_resource(SIZE_T key) +{ auto iter = g_D3D12AddressMappings.find(key); if (iter == g_D3D12AddressMappings.end()) return; @@ -273,6 +314,23 @@ _unmap_resource(ID3D12Resource* pResource) g_D3D12AddressMappings.erase(iter); } +static inline void +_unmap_resource(ID3D12Resource* pResource) +{ + D3D12_HEAP_FLAGS flags = _get_heap_flags(pResource); + + if (!(flags & D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH)) + return; + + _unmap_resource(static_cast(reinterpret_cast(pResource))); +} + +static inline void +_unmap_resource(ID3D12Heap* pResource) +{ + _unmap_resource(static_cast(reinterpret_cast(pResource))); +} + static inline void _flush_mappings() { diff --git a/retrace/dxgiretrace.py b/retrace/dxgiretrace.py index fd7a91a87d..4629e7833d 100755 --- a/retrace/dxgiretrace.py +++ b/retrace/dxgiretrace.py @@ -481,6 +481,13 @@ def invokeInterfaceMethod(self, interface, method): print(' for (UINT i = 0; i < Count; i++)') print(' pParams[i].Dest = g_D3D12AddressSlabs.LookupSlab(pParams[i].Dest);') + if method.name == 'OpenExistingHeapFromAddress': + print(' size_t _AddressSize = call.arg(1).toBlob()->size;') + print(' void *_AlignedAddress = VirtualAlloc(NULL, _AddressSize, MEM_COMMIT, PAGE_READWRITE);') + print(' memcpy(_AlignedAddress, pAddress, _AddressSize);') + print(' pAddress = _AlignedAddress;') + print(' retrace::addRegion(call, (unsigned long long)call.arg(1).toBlob()->base_ptr, _AlignedAddress, _AddressSize);') + if method.name == 'CreatePipelineLibrary': # Make a fake pipeline library, so we can still make the state objects. print(' *ppPipelineLibrary = reinterpret_cast(new _D3D12FakePipelineLibrary(_this));') diff --git a/specs/d3d12.py b/specs/d3d12.py index ee51f3e4fc..a9db467044 100644 --- a/specs/d3d12.py +++ b/specs/d3d12.py @@ -2754,7 +2754,7 @@ ID3D12Device3.methods += [ # TODO(Josh): Implement OpenExistingHeapFromAddress, etc properly # Mark all dirty then VirtualProtect their stuff. Yuck. - StdMethod(HRESULT, 'OpenExistingHeapFromAddress', [(OpaquePointer(Const(Void)), 'pAddress'), (REFIID, 'riid'), Out(Pointer(ObjPointer(Void)), 'ppvHeap')]), + StdMethod(HRESULT, 'OpenExistingHeapFromAddress', [(Blob(Const(Void), '_d3d12_AllocationSize(pAddress)'), 'pAddress'), (REFIID, 'riid'), Out(Pointer(ObjPointer(Void)), 'ppvHeap')]), StdMethod(HRESULT, 'OpenExistingHeapFromFileMapping', [(HANDLE, 'hFileMapping'), (REFIID, 'riid'), Out(Pointer(ObjPointer(Void)), 'ppvHeap')]), StdMethod(HRESULT, 'EnqueueMakeResident', [(D3D12_RESIDENCY_FLAGS, 'Flags'), (UINT, 'NumObjects'), (Array(Const(ObjPointer(ID3D12Pageable)), 'NumObjects'), 'ppObjects'),(ObjPointer(ID3D12Fence), 'pFenceToSignal'), (UINT64, 'FenceValueToSignal')]), ] diff --git a/wrappers/dxgitrace.py b/wrappers/dxgitrace.py index 6bd55ade70..7aa38e7243 100644 --- a/wrappers/dxgitrace.py +++ b/wrappers/dxgitrace.py @@ -144,6 +144,11 @@ def enumWrapperInterfaceVariables(self, interface): ('D3D12_GPU_VIRTUAL_ADDRESS', 'm_FakeAddress', '0'), ('std::mutex', 'm_RefCountMutex', None) ] + if interface.hasBase(d3d12.ID3D12Heap): + variables += [ + ('const void *', 'm_UserPointer', '0'), + ('std::mutex', 'm_RefCountMutex', None) + ] return variables @@ -193,7 +198,7 @@ def implementWrapperInterfaceMethodBody(self, interface, base, method): # Need to unmap the resource if the last public reference is # eliminated. - if interface in (d3d12.ID3D12Resource, d3d12.ID3D12Resource1): + if interface in (d3d12.ID3D12Resource, d3d12.ID3D12Resource1, d3d12.ID3D12Heap, d3d12.ID3D12Heap1): if method.name == 'AddRef': # Need to lock here to avoid another thread potentially # releasing while we are flushing. @@ -210,6 +215,8 @@ def implementWrapperInterfaceMethodBody(self, interface, base, method): # avoid a dangling ptr. print(' std::unique_lock _ordering_lock;') print(' if (_current_ref == 1) {') + if interface in (d3d12.ID3D12Heap, d3d12.ID3D12Heap1): + print(' if (m_UserPointer != nullptr)') print(' _unmap_resource(m_pInstance);') print(' _ordering_lock = std::unique_lock(g_D3D12AddressMappingsMutex);') print(' }') @@ -282,6 +289,13 @@ def implementWrapperInterfaceMethodBody(self, interface, base, method): print(' _MapDesc.Size = 0;') print(' }') + if method.name == 'OpenExistingHeapFromAddress': + print(' if (SUCCEEDED(_result) && pAddress) {') + print(' auto *pHeap = *reinterpret_cast(ppvHeap);') + print(' _map_resource(pHeap->m_pInstance, (void *) pAddress);') + print(' pHeap->m_UserPointer = pAddress;') + print(' }') + if interface.hasBase(d3d11.ID3D11VideoContext) and \ method.name == 'GetDecoderBuffer': print(' if (SUCCEEDED(_result)) {')