#include "xna/graphics/adapter.hpp" #include "xna/graphics/displaymode.hpp" #include "xna/game/gdevicemanager.hpp" #include "xna/xna-dx.hpp" namespace xna { static void setOutputVars(comptr const& adapter, String& deviceName, intptr_t& monitorHandle); static size_t getDisplayModesCount(IDXGIAdapter* adapter); static sptr createDisplayModeCollection(std::vector const& source); static void setCurrentDisplayMode(DisplayModeCollection& displayModes, SurfaceFormat surfaceFormat, Uint width, Uint height, sptr& currentDisplayMode); static sptr getSupportedDisplayModes(comptr& dxAdapter); GraphicsAdapter::GraphicsAdapter() { impl = unew(); } uptr GraphicsAdapter::DefaultAdapter() { comptr pFactory = nullptr; if FAILED(CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)pFactory.GetAddressOf())) Exception::Throw(Exception::FAILED_TO_CREATE); comptr pAdapter = nullptr; if (pFactory->EnumAdapters1(0, pAdapter.GetAddressOf()) != DXGI_ERROR_NOT_FOUND) { auto adp = uptr(new GraphicsAdapter()); adp->impl->dxAdapter = pAdapter; adp->impl->dxFactory = pFactory; DXGI_ADAPTER_DESC1 desc{}; pAdapter->GetDesc1(&desc); adp->description = XnaHelper::ToString(desc.Description); adp->deviceId = static_cast(desc.DeviceId); adp->isDefault = true; adp->revision = static_cast(desc.Revision); adp->subSystemId = static_cast(desc.SubSysId); adp->vendorId = static_cast(desc.VendorId); setOutputVars(pAdapter, adp->deviceName, adp->monitorHandle); setCurrentDisplayMode(*adp->supportedDisplayModes, SurfaceFormat::Color, GraphicsDeviceManager::DefaultBackBufferWidth, GraphicsDeviceManager::DefaultBackBufferHeight, adp->currentDisplayMode); return adp; } return nullptr; } void GraphicsAdapter::Adapters(std::vector>& adapters) { comptr pFactory = nullptr; if FAILED(CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)pFactory.GetAddressOf())) Exception::Throw(Exception::FAILED_TO_CREATE); comptr pAdapter = nullptr; for (UINT count = 0; pFactory->EnumAdapters1(count, pAdapter.GetAddressOf()) != DXGI_ERROR_NOT_FOUND; ++count) { auto adp = uptr(new GraphicsAdapter()); adp->impl->dxAdapter = pAdapter; adp->impl->dxFactory = pFactory; DXGI_ADAPTER_DESC1 desc{}; pAdapter->GetDesc1(&desc); adp->description = XnaHelper::ToString(desc.Description); adp->deviceId = static_cast(desc.DeviceId); adp->isDefault = count == 0; adp->revision = static_cast(desc.Revision); adp->subSystemId = static_cast(desc.SubSysId); adp->vendorId = static_cast(desc.VendorId); setOutputVars(pAdapter, adp->deviceName, adp->monitorHandle); setCurrentDisplayMode(*adp->supportedDisplayModes, SurfaceFormat::Color, GraphicsDeviceManager::DefaultBackBufferWidth, GraphicsDeviceManager::DefaultBackBufferHeight, adp->currentDisplayMode); adp->supportedDisplayModes = getSupportedDisplayModes(pAdapter); adapters.push_back(std::move(adp)); } } //INTERNAL FUNCTIONS sptr getSupportedDisplayModes(comptr& dxAdapter) { const auto totalDisplay = getDisplayModesCount(dxAdapter.Get()); if (totalDisplay == 0) return nullptr; comptr pOutput = nullptr; comptr pOutput1 = nullptr; UINT bufferOffset = 0; std::vector buffer(totalDisplay); if (dxAdapter->EnumOutputs(0, pOutput.GetAddressOf()) != DXGI_ERROR_NOT_FOUND) { for (size_t f = 0; f < SURFACE_FORMAT_COUNT; ++f) { const auto currentSurface = static_cast(f); DXGI_FORMAT format = DxHelpers::SurfaceFormatToDx(currentSurface); UINT numModes = 0; if (!pOutput1) { pOutput->QueryInterface(IID_IDXGIOutput1, (void**)pOutput1.GetAddressOf()); } //See ref: https://learn.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgioutput-getdisplaymodelist?redirectedfrom=MSDN pOutput1->GetDisplayModeList1(format, 0, &numModes, nullptr); if (numModes == 0) continue; pOutput1->GetDisplayModeList1(format, 0, &numModes, buffer.data() + bufferOffset); bufferOffset += numModes; } } if (!pOutput) return nullptr; return createDisplayModeCollection(buffer); } void setCurrentDisplayMode(DisplayModeCollection& displayModes, SurfaceFormat surfaceFormat, Uint width, Uint height, sptr& currentDisplayMode) { auto modes = displayModes.Query(surfaceFormat); for (size_t i = 0; i < modes.size(); ++i) { auto& m = modes[i]; if (m->Format() == surfaceFormat && m->Width() == width && m->Height() == height) { currentDisplayMode = m; } else if (i + 1 == modes.size()) { currentDisplayMode = m; } } } size_t getDisplayModesCount(IDXGIAdapter* adapter) { comptr pOutput = nullptr; comptr pOutput1 = nullptr; size_t numModes = 0; if (adapter->EnumOutputs(0, pOutput.GetAddressOf()) != DXGI_ERROR_NOT_FOUND) { for (size_t f = 0; f < SURFACE_FORMAT_COUNT; ++f) { const auto currentSurface = static_cast(f); DXGI_FORMAT format = DxHelpers::SurfaceFormatToDx(currentSurface); UINT num = 0; if (!pOutput1) { pOutput->QueryInterface(IID_IDXGIOutput1, (void**)pOutput1.GetAddressOf()); } //See ref: https://learn.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgioutput-getdisplaymodelist?redirectedfrom=MSDN pOutput1->GetDisplayModeList1(format, 0, &num, nullptr); numModes += num; } } return numModes; } sptr createDisplayModeCollection(std::vector const& source) { auto collection = snew(); std::vector> displayList; sptr pDisplay = nullptr; for (size_t i = 0; i < source.size(); ++i) { auto& modedesc = source[i]; DisplayModeRate rate; rate.RefreshRate.Denominator = modedesc.RefreshRate.Denominator; rate.RefreshRate.Numerator = modedesc.RefreshRate.Numerator; rate.Scaling = static_cast(modedesc.Scaling); rate.ScanlineOrdering = static_cast(modedesc.ScanlineOrdering); if (pDisplay && pDisplay->Width() == modedesc.Width && pDisplay->Height() == modedesc.Height && pDisplay->Format() == DxHelpers::SurfaceFormatToXna(modedesc.Format)) { pDisplay->Rates.push_back(rate); } else { pDisplay = snew( modedesc.Width, modedesc.Height, DxHelpers::SurfaceFormatToXna(modedesc.Format)); pDisplay->Rates.push_back(rate); displayList.push_back(pDisplay); } } collection->DisplayModes = displayList; return collection; } static void setOutputVars(comptr const& adapter, String& deviceName, intptr_t& monitorHandle) { comptr pOutput = nullptr; if (adapter->EnumOutputs(0, pOutput.GetAddressOf()) != DXGI_ERROR_NOT_FOUND) { DXGI_OUTPUT_DESC outputDesc; pOutput->GetDesc(&outputDesc); deviceName = XnaHelper::ToString(outputDesc.DeviceName); monitorHandle = reinterpret_cast(outputDesc.Monitor); } } }