/** * Looking Glass * Copyright © 2017-2026 The Looking Glass Authors * https://looking-glass.io * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "display/CDisplayConfiguration.h" #include "CDebug.h" #include "CSRWLock.h" #include "RefreshRate.h" #include #include #ifdef HAS_IDDCX_110 static inline IDDCX_WIRE_BITS_PER_COMPONENT GetWireBitsPerComponent(bool hdr) { IDDCX_WIRE_BITS_PER_COMPONENT bits = {}; // This describes the virtual monitor wire, not the swap-chain format. // HDR uses a 10-bpc PQ wire while CAN_PROCESS_FP16 requests the FP16/scRGB // source surface that Looking Glass converts for transport. bits.Rgb = IDDCX_BITS_PER_COMPONENT_8; if (hdr) bits.Rgb = (IDDCX_BITS_PER_COMPONENT)(bits.Rgb | IDDCX_BITS_PER_COMPONENT_10); bits.YCbCr444 = IDDCX_BITS_PER_COMPONENT_NONE; bits.YCbCr422 = IDDCX_BITS_PER_COMPONENT_NONE; bits.YCbCr420 = IDDCX_BITS_PER_COMPONENT_NONE; return bits; } #endif CDisplayConfiguration::CDisplayConfiguration(CSettings& settings) : m_settings(settings) { } bool CDisplayConfiguration::LoadModes(const FrameCaps& caps) { const CSettings::DisplayModes configuredModes = m_settings.LoadModes(); // Build the new mode list into a local first so readers never observe a // reallocation of the live container. Publishing it is a pointer swap. CSettings::DisplayModes newModes; newModes.reserve(configuredModes.size()); bool hasPreferred = false; for (const auto& configuredMode : configuredModes) { const FrameMode frameMode = { configuredMode.width, configuredMode.height, configuredMode.refresh100uHz, }; if (!frameMode.width || !frameMode.height || !frameMode.refresh100uHz) { DEBUG_WARN("Filtering invalid %s mode %ux%u@%.4f", configuredMode.extraMode ? "extra" : "configured", configuredMode.width, configuredMode.height, configuredMode.refresh100uHz / static_cast(LG_REFRESH_RATE_SCALE)); continue; } if (!caps.CanUseMode(frameMode)) { DEBUG_WARN( "Filtering unsupported %s mode %ux%u@%.4f", configuredMode.extraMode ? "extra" : "configured", configuredMode.width, configuredMode.height, configuredMode.refresh100uHz / static_cast(LG_REFRESH_RATE_SCALE)); continue; } CSettings::DisplayMode mode = configuredMode; if (mode.preferred) { mode.preferred = !hasPreferred; hasPreferred = true; } newModes.push_back(mode); } if (newModes.empty()) { DEBUG_ERROR("No configured display modes are supported"); return false; } // ExtraMode may have been the preferred mode. If it did not fit, promote // the first remaining mode so the list still has a valid preference. if (!hasPreferred) newModes.front().preferred = true; CSRWExclusiveLock lock(m_modeLock); m_modes = std::move(newModes); return true; } bool CDisplayConfiguration::Load(const FrameCaps& caps) { return LoadModes(caps); } bool CDisplayConfiguration::ReloadSettings( const FrameCaps& caps) { bool modesLoaded = false; { CSRWExclusiveLock reloadLock(m_reloadLock); bool settingsUpdated = true; CSettings::DisplayMode extraMode = {}; if (m_settings.GetExtraMode(extraMode)) { const unsigned refresh100uHz = m_settings.GetDefaultRefresh100uHz(); if (extraMode.refresh100uHz != refresh100uHz) { extraMode.refresh100uHz = refresh100uHz; settingsUpdated = m_settings.SetExtraMode(extraMode); } } if (settingsUpdated) modesLoaded = LoadModes(caps); } if (!modesLoaded) DEBUG_ERROR("Failed to reload the display mode list"); return modesLoaded; } CDisplayConfiguration::ResolutionResult CDisplayConfiguration::SetResolution( uint32_t width, uint32_t height, const FrameCaps& caps) { ResolutionResult result; CSettings::DisplayMode mode = {}; mode.width = width; mode.height = height; mode.refresh100uHz = m_settings.GetDefaultRefresh100uHz(); mode.preferred = true; if (!mode.width || !mode.height || !mode.refresh100uHz) { DEBUG_WARN("Ignoring invalid resolution request: %ux%u", width, height); return result; } const FrameMode frameMode = { mode.width, mode.height, mode.refresh100uHz, }; if (!caps.CanUseMode(frameMode, &result.requiredMiB)) { if (result.requiredMiB) { result.status = ResolutionStatus::TOO_LARGE; DEBUG_WARN( "Refusing resolution %ux%u: configured frame capacity must be at " "least %u MiB", width, height, result.requiredMiB); } else { result.status = ResolutionStatus::UNSUPPORTED; DEBUG_WARN("Refusing unsupported resolution: %ux%u", width, height); } return result; } { CSRWExclusiveLock reloadLock(m_reloadLock); if (!m_settings.SetExtraMode(mode)) result.status = ResolutionStatus::SETTINGS_FAILED; else if (!LoadModes(caps)) result.status = ResolutionStatus::MODES_FAILED; else { result.status = ResolutionStatus::SUCCESS; result.mode = mode; } } if (result.status != ResolutionStatus::SUCCESS) DEBUG_ERROR("Failed to rebuild the display mode list"); return result; } void CDisplayConfiguration::InitializeEdid(bool hdr) { CSRWExclusiveLock lock(m_modeLock); if (m_edid.Size()) return; m_edid.Build(hdr); m_hdrEnabled = hdr; } void CDisplayConfiguration::RebuildEdid(bool hdr) { CSRWExclusiveLock lock(m_modeLock); m_edid.Build(hdr); m_hdrEnabled = hdr; } CDisplayConfiguration::Description CDisplayConfiguration::GetDescription() const { Description result; CSRWSharedLock lock(m_modeLock); result.modeCount = m_modes.size(); if (m_edid.Size()) result.edid.assign(m_edid.Data(), m_edid.Data() + m_edid.Size()); return result; } CSettings::DisplayModes CDisplayConfiguration::SnapshotModes( bool * hdrEnabled) const { CSRWSharedLock lock(m_modeLock); if (hdrEnabled) *hdrEnabled = m_hdrEnabled; return m_modes; } static UINT64 GreatestCommonDivisor(UINT64 a, UINT64 b) { while (b) { const UINT64 remainder = a % b; a = b; b = remainder; } return a; } static void SetSignalRate(DISPLAYCONFIG_RATIONAL& rate, UINT64 numerator, UINT32 denominator) { const UINT64 divisor = GreatestCommonDivisor(numerator, denominator); numerator /= divisor; denominator /= (UINT32)divisor; if (numerator <= UINT32_MAX) { rate.Numerator = (UINT32)numerator; rate.Denominator = denominator; return; } rate.Numerator = (UINT32)((numerator + denominator / 2) / denominator); rate.Denominator = 1; } static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO& signal, const CSettings::DisplayMode& mode, bool monitorMode) { CEdid::Timing timing; if (!CEdid::GetTiming(timing, mode)) return; signal.activeSize.cx = timing.hActive; signal.activeSize.cy = timing.vActive; signal.totalSize.cx = timing.hActive + timing.hBlank; signal.totalSize.cy = timing.vActive + timing.vBlank; signal.AdditionalSignalInfo.vSyncFreqDivider = monitorMode ? 0 : 1; signal.AdditionalSignalInfo.videoStandard = 255; SetSignalRate(signal.vSyncFreq, mode.refresh100uHz, LG_REFRESH_RATE_SCALE); SetSignalRate(signal.hSyncFreq, (UINT64)mode.refresh100uHz * signal.totalSize.cy, LG_REFRESH_RATE_SCALE); signal.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE; signal.pixelRate = timing.pixelClock; } NTSTATUS CDisplayConfiguration::ParseMonitorDescription( const IDARG_IN_PARSEMONITORDESCRIPTION * inArgs, IDARG_OUT_PARSEMONITORDESCRIPTION * outArgs) const { const CSettings::DisplayModes modes = SnapshotModes(); outArgs->MonitorModeBufferOutputCount = (UINT)modes.size(); outArgs->PreferredMonitorModeIdx = 0; if (inArgs->MonitorModeBufferInputCount < (UINT)modes.size()) return inArgs->MonitorModeBufferInputCount > 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; auto * mode = inArgs->pMonitorModes; for (auto it = modes.cbegin(); it != modes.cend(); ++it, ++mode) { mode->Size = sizeof(IDDCX_MONITOR_MODE); mode->Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR; FillSignalInfo(mode->MonitorVideoSignalInfo, *it, true); if (it->preferred) outArgs->PreferredMonitorModeIdx = (UINT)std::distance(modes.cbegin(), it); } return STATUS_SUCCESS; } NTSTATUS CDisplayConfiguration::MonitorGetDefaultModes( const IDARG_IN_GETDEFAULTDESCRIPTIONMODES * inArgs, IDARG_OUT_GETDEFAULTDESCRIPTIONMODES * outArgs) const { const CSettings::DisplayModes modes = SnapshotModes(); outArgs->DefaultMonitorModeBufferOutputCount = (UINT)modes.size(); outArgs->PreferredMonitorModeIdx = 0; if (inArgs->DefaultMonitorModeBufferInputCount < (UINT)modes.size()) return inArgs->DefaultMonitorModeBufferInputCount > 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; auto * mode = inArgs->pDefaultMonitorModes; for (auto it = modes.cbegin(); it != modes.cend(); ++it, ++mode) { mode->Size = sizeof(IDDCX_MONITOR_MODE); mode->Origin = IDDCX_MONITOR_MODE_ORIGIN_DRIVER; FillSignalInfo(mode->MonitorVideoSignalInfo, *it, true); if (it->preferred) outArgs->PreferredMonitorModeIdx = (UINT)std::distance(modes.cbegin(), it); } return STATUS_SUCCESS; } NTSTATUS CDisplayConfiguration::MonitorQueryTargetModes( const IDARG_IN_QUERYTARGETMODES * inArgs, IDARG_OUT_QUERYTARGETMODES * outArgs) const { const CSettings::DisplayModes modes = SnapshotModes(); outArgs->TargetModeBufferOutputCount = (UINT)modes.size(); if (inArgs->TargetModeBufferInputCount < (UINT)modes.size()) return inArgs->TargetModeBufferInputCount > 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; auto * mode = inArgs->pTargetModes; for (auto it = modes.cbegin(); it != modes.cend(); ++it, ++mode) { mode->Size = sizeof(IDDCX_TARGET_MODE); FillSignalInfo( mode->TargetVideoSignalInfo.targetVideoSignalInfo, *it, false); } return STATUS_SUCCESS; } #ifdef HAS_IDDCX_110 NTSTATUS CDisplayConfiguration::ParseMonitorDescription2( const IDARG_IN_PARSEMONITORDESCRIPTION2 * inArgs, IDARG_OUT_PARSEMONITORDESCRIPTION * outArgs) const { bool hdrEnabled = false; const CSettings::DisplayModes modes = SnapshotModes(&hdrEnabled); outArgs->MonitorModeBufferOutputCount = (UINT)modes.size(); outArgs->PreferredMonitorModeIdx = 0; if (inArgs->MonitorModeBufferInputCount < (UINT)modes.size()) return inArgs->MonitorModeBufferInputCount > 0 ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS; auto * mode = inArgs->pMonitorModes; for (auto it = modes.cbegin(); it != modes.cend(); ++it, ++mode) { ZeroMemory(mode, sizeof(*mode)); mode->Size = sizeof(IDDCX_MONITOR_MODE2); mode->Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR; FillSignalInfo(mode->MonitorVideoSignalInfo, *it, true); mode->BitsPerComponent = GetWireBitsPerComponent(hdrEnabled); if (it->preferred) outArgs->PreferredMonitorModeIdx = (UINT)std::distance(modes.cbegin(), it); } return STATUS_SUCCESS; } NTSTATUS CDisplayConfiguration::MonitorQueryTargetModes2( const IDARG_IN_QUERYTARGETMODES2 * inArgs, IDARG_OUT_QUERYTARGETMODES * outArgs) const { bool hdrEnabled = false; const CSettings::DisplayModes modes = SnapshotModes(&hdrEnabled); outArgs->TargetModeBufferOutputCount = (UINT)modes.size(); if (inArgs->TargetModeBufferInputCount < (UINT)modes.size()) return STATUS_SUCCESS; if (!inArgs->pTargetModes) return STATUS_INVALID_PARAMETER; auto * mode = inArgs->pTargetModes; for (auto it = modes.cbegin(); it != modes.cend(); ++it, ++mode) { ZeroMemory(mode, sizeof(*mode)); mode->Size = sizeof(IDDCX_TARGET_MODE2); FillSignalInfo( mode->TargetVideoSignalInfo.targetVideoSignalInfo, *it, false); mode->BitsPerComponent = GetWireBitsPerComponent(hdrEnabled); } return STATUS_SUCCESS; } #endif