#include #include #include #include "dxgi_adapter.h" #include "dxgi_device.h" #include "dxgi_enums.h" #include "dxgi_factory.h" #include "dxgi_format.h" #include "dxgi_options.h" #include "dxgi_output.h" namespace dxvk { DxgiAdapter::DxgiAdapter( DxgiFactory* factory, const Rc& adapter) : m_factory (factory), m_adapter (adapter), m_formats (adapter) { } DxgiAdapter::~DxgiAdapter() { } HRESULT STDMETHODCALLTYPE DxgiAdapter::QueryInterface(REFIID riid, void** ppvObject) { *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(IDXGIObject) || riid == __uuidof(IDXGIAdapter) || riid == __uuidof(IDXGIAdapter1) || riid == __uuidof(IDXGIAdapter2) || riid == __uuidof(IDXGIAdapter3) || riid == __uuidof(IDXGIVkAdapter)) { *ppvObject = ref(this); return S_OK; } Logger::warn("DxgiAdapter::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE DxgiAdapter::GetParent(REFIID riid, void** ppParent) { return m_factory->QueryInterface(riid, ppParent); } HRESULT STDMETHODCALLTYPE DxgiAdapter::CheckInterfaceSupport( REFGUID InterfaceName, LARGE_INTEGER* pUMDVersion) { const DxgiOptions* options = m_factory->GetOptions(); if (pUMDVersion != nullptr) *pUMDVersion = LARGE_INTEGER(); if (options->d3d10Enable) { if (InterfaceName == __uuidof(ID3D10Device) || InterfaceName == __uuidof(ID3D10Device1)) return S_OK; } Logger::err("DXGI: CheckInterfaceSupport: Unsupported interface"); Logger::err(str::format(InterfaceName)); return DXGI_ERROR_UNSUPPORTED; } HRESULT STDMETHODCALLTYPE DxgiAdapter::EnumOutputs( UINT Output, IDXGIOutput** ppOutput) { InitReturnPtr(ppOutput); if (ppOutput == nullptr) return DXGI_ERROR_INVALID_CALL; if (Output > 0) { *ppOutput = nullptr; return DXGI_ERROR_NOT_FOUND; } // TODO support multiple monitors HMONITOR monitor = ::MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY); *ppOutput = ref(new DxgiOutput(this, monitor)); return S_OK; } HRESULT STDMETHODCALLTYPE DxgiAdapter::GetDesc(DXGI_ADAPTER_DESC* pDesc) { if (pDesc == nullptr) return DXGI_ERROR_INVALID_CALL; DXGI_ADAPTER_DESC2 desc; HRESULT hr = GetDesc2(&desc); if (SUCCEEDED(hr)) { std::memcpy(pDesc->Description, desc.Description, sizeof(pDesc->Description)); pDesc->VendorId = desc.VendorId; pDesc->DeviceId = desc.DeviceId; pDesc->SubSysId = desc.SubSysId; pDesc->Revision = desc.Revision; pDesc->DedicatedVideoMemory = desc.DedicatedVideoMemory; pDesc->DedicatedSystemMemory = desc.DedicatedSystemMemory; pDesc->SharedSystemMemory = desc.SharedSystemMemory; pDesc->AdapterLuid = desc.AdapterLuid; } return hr; } HRESULT STDMETHODCALLTYPE DxgiAdapter::GetDesc1(DXGI_ADAPTER_DESC1* pDesc) { if (pDesc == nullptr) return DXGI_ERROR_INVALID_CALL; DXGI_ADAPTER_DESC2 desc; HRESULT hr = GetDesc2(&desc); if (SUCCEEDED(hr)) { std::memcpy(pDesc->Description, desc.Description, sizeof(pDesc->Description)); pDesc->VendorId = desc.VendorId; pDesc->DeviceId = desc.DeviceId; pDesc->SubSysId = desc.SubSysId; pDesc->Revision = desc.Revision; pDesc->DedicatedVideoMemory = desc.DedicatedVideoMemory; pDesc->DedicatedSystemMemory = desc.DedicatedSystemMemory; pDesc->SharedSystemMemory = desc.SharedSystemMemory; pDesc->AdapterLuid = desc.AdapterLuid; pDesc->Flags = desc.Flags; } return hr; } HRESULT STDMETHODCALLTYPE DxgiAdapter::GetDesc2(DXGI_ADAPTER_DESC2* pDesc) { if (pDesc == nullptr) return DXGI_ERROR_INVALID_CALL; const DxgiOptions* options = m_factory->GetOptions(); auto deviceProp = m_adapter->deviceProperties(); auto memoryProp = m_adapter->memoryProperties(); // Custom Vendor / Device ID if (options->customVendorId >= 0) deviceProp.vendorID = options->customVendorId; if (options->customDeviceId >= 0) deviceProp.deviceID = options->customDeviceId; // Convert device name std::memset(pDesc->Description, 0, sizeof(pDesc->Description)); ::MultiByteToWideChar(CP_UTF8, 0, deviceProp.deviceName, -1, pDesc->Description, sizeof(pDesc->Description)); // Get amount of video memory // based on the Vulkan heaps VkDeviceSize deviceMemory = 0; VkDeviceSize sharedMemory = 0; for (uint32_t i = 0; i < memoryProp.memoryHeapCount; i++) { VkMemoryHeap heap = memoryProp.memoryHeaps[i]; if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) deviceMemory += heap.size; else sharedMemory += heap.size; } // Some games are silly and need their memory limited if (options->maxDeviceMemory > 0 && options->maxDeviceMemory < deviceMemory) deviceMemory = options->maxDeviceMemory; if (options->maxSharedMemory > 0 && options->maxSharedMemory < sharedMemory) sharedMemory = options->maxSharedMemory; #ifndef _WIN64 // The value returned by DXGI is a 32-bit value // on 32-bit platforms, so we need to clamp it VkDeviceSize maxMemory = 0xC0000000; deviceMemory = std::min(deviceMemory, maxMemory); sharedMemory = std::min(sharedMemory, maxMemory); #endif pDesc->VendorId = deviceProp.vendorID; pDesc->DeviceId = deviceProp.deviceID; pDesc->SubSysId = 0; pDesc->Revision = 0; pDesc->DedicatedVideoMemory = deviceMemory; pDesc->DedicatedSystemMemory = 0; pDesc->SharedSystemMemory = sharedMemory; pDesc->AdapterLuid = LUID { 0, 0 }; // TODO implement pDesc->Flags = 0; pDesc->GraphicsPreemptionGranularity = DXGI_GRAPHICS_PREEMPTION_DMA_BUFFER_BOUNDARY; pDesc->ComputePreemptionGranularity = DXGI_COMPUTE_PREEMPTION_DMA_BUFFER_BOUNDARY; return S_OK; } HRESULT STDMETHODCALLTYPE DxgiAdapter::QueryVideoMemoryInfo( UINT NodeIndex, DXGI_MEMORY_SEGMENT_GROUP MemorySegmentGroup, DXGI_QUERY_VIDEO_MEMORY_INFO* pVideoMemoryInfo) { if (NodeIndex > 0 || !pVideoMemoryInfo) return DXGI_ERROR_INVALID_CALL; if (MemorySegmentGroup != DXGI_MEMORY_SEGMENT_GROUP_LOCAL && MemorySegmentGroup != DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL) return DXGI_ERROR_INVALID_CALL; DxvkAdapterMemoryInfo memInfo = m_adapter->getMemoryHeapInfo(); VkMemoryHeapFlags heapFlagMask = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT; VkMemoryHeapFlags heapFlags = 0; if (MemorySegmentGroup == DXGI_MEMORY_SEGMENT_GROUP_LOCAL) heapFlags |= VK_MEMORY_HEAP_DEVICE_LOCAL_BIT; pVideoMemoryInfo->Budget = 0; pVideoMemoryInfo->CurrentUsage = 0; for (uint32_t i = 0; i < memInfo.heapCount; i++) { if ((memInfo.heaps[i].heapFlags & heapFlagMask) != heapFlags) continue; pVideoMemoryInfo->Budget += memInfo.heaps[i].memoryAvailable; pVideoMemoryInfo->CurrentUsage += memInfo.heaps[i].memoryAllocated; } // We don't implement reservation, but the observable // behaviour should match that of Windows drivers uint32_t segmentId = uint32_t(MemorySegmentGroup); pVideoMemoryInfo->AvailableForReservation = pVideoMemoryInfo->Budget / 2; pVideoMemoryInfo->CurrentReservation = m_memReservation[segmentId]; return S_OK; } HRESULT STDMETHODCALLTYPE DxgiAdapter::SetVideoMemoryReservation( UINT NodeIndex, DXGI_MEMORY_SEGMENT_GROUP MemorySegmentGroup, UINT64 Reservation) { DXGI_QUERY_VIDEO_MEMORY_INFO info; HRESULT hr = QueryVideoMemoryInfo( NodeIndex, MemorySegmentGroup, &info); if (FAILED(hr)) return hr; if (Reservation > info.AvailableForReservation) return DXGI_ERROR_INVALID_CALL; uint32_t segmentId = uint32_t(MemorySegmentGroup); m_memReservation[segmentId] = Reservation; return S_OK; } HRESULT STDMETHODCALLTYPE DxgiAdapter::RegisterHardwareContentProtectionTeardownStatusEvent( HANDLE hEvent, DWORD* pdwCookie) { Logger::err("DxgiAdapter::RegisterHardwareContentProtectionTeardownStatusEvent: Not implemented"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE DxgiAdapter::RegisterVideoMemoryBudgetChangeNotificationEvent( HANDLE hEvent, DWORD* pdwCookie) { Logger::err("DxgiAdapter::RegisterVideoMemoryBudgetChangeNotificationEvent: Not implemented"); return E_NOTIMPL; } void STDMETHODCALLTYPE DxgiAdapter::UnregisterHardwareContentProtectionTeardownStatus( DWORD dwCookie) { Logger::err("DxgiAdapter::UnregisterHardwareContentProtectionTeardownStatus: Not implemented"); } void STDMETHODCALLTYPE DxgiAdapter::UnregisterVideoMemoryBudgetChangeNotification( DWORD dwCookie) { Logger::err("DxgiAdapter::UnregisterVideoMemoryBudgetChangeNotification: Not implemented"); } Rc STDMETHODCALLTYPE DxgiAdapter::GetDXVKAdapter() { return m_adapter; } HRESULT STDMETHODCALLTYPE DxgiAdapter::CreateDevice( IDXGIObject* pContainer, const DxvkDeviceFeatures* pFeatures, IDXGIVkDevice** ppDevice) { InitReturnPtr(ppDevice); try { *ppDevice = new dxvk::DxgiDevice(pContainer, this, m_factory->GetOptions(), pFeatures); return S_OK; } catch (const dxvk::DxvkError& e) { dxvk::Logger::err(e.message()); return DXGI_ERROR_UNSUPPORTED; } } DXGI_VK_FORMAT_INFO STDMETHODCALLTYPE DxgiAdapter::LookupFormat( DXGI_FORMAT Format, DXGI_VK_FORMAT_MODE Mode) { return m_formats.GetFormatInfo(Format, Mode); } DXGI_VK_FORMAT_FAMILY STDMETHODCALLTYPE DxgiAdapter::LookupFormatFamily( DXGI_FORMAT Format, DXGI_VK_FORMAT_MODE Mode) { return m_formats.GetFormatFamily(Format, Mode); } HRESULT DxgiAdapter::GetOutputFromMonitor( HMONITOR Monitor, IDXGIOutput** ppOutput) { if (ppOutput == nullptr) return DXGI_ERROR_INVALID_CALL; for (uint32_t i = 0; SUCCEEDED(EnumOutputs(i, ppOutput)); i++) { DXGI_OUTPUT_DESC outputDesc; (*ppOutput)->GetDesc(&outputDesc); if (outputDesc.Monitor == Monitor) return S_OK; (*ppOutput)->Release(); (*ppOutput) = nullptr; } // No such output found return DXGI_ERROR_NOT_FOUND; } HRESULT DxgiAdapter::GetOutputData( HMONITOR Monitor, DXGI_VK_OUTPUT_DATA* pOutputData) { std::lock_guard lock(m_outputMutex); auto entry = m_outputData.find(Monitor); if (entry == m_outputData.end()) return DXGI_ERROR_NOT_FOUND; if (pOutputData == nullptr) return S_FALSE; *pOutputData = entry->second; return S_OK; } HRESULT DxgiAdapter::SetOutputData( HMONITOR Monitor, const DXGI_VK_OUTPUT_DATA* pOutputData) { std::lock_guard lock(m_outputMutex); m_outputData.insert_or_assign(Monitor, *pOutputData); return S_OK; } }