From 5b09d4f90a4d0d59e1647ed8d43f3bcf0c7b3d17 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Wed, 12 Aug 2026 23:53:52 +1000 Subject: [PATCH] [idd] transport: intersect frame capabilities --- idd/LGIdd/LGIdd.vcxproj | 4 +- idd/LGIdd/LGIdd.vcxproj.filters | 8 +- idd/LGIdd/display/CDeviceContext.cpp | 11 +- idd/LGIdd/display/CDisplayConfiguration.cpp | 162 ++++++------------ idd/LGIdd/display/CDisplayConfiguration.h | 19 +- idd/LGIdd/transport/CTransportManager.cpp | 72 ++++++-- idd/LGIdd/transport/CTransportManager.h | 8 +- idd/LGIdd/transport/FrameCaps.h | 41 +++++ idd/LGIdd/transport/ITransport.h | 12 +- idd/LGIdd/transport/lgmp/CLGMPFrameCaps.cpp | 112 ++++++++++++ .../CLGMPFrameCaps.h} | 20 ++- .../transport/lgmp/CLGMPFrameTransport.cpp | 15 +- .../transport/lgmp/CLGMPFrameTransport.h | 6 +- idd/LGIdd/transport/lgmp/CLGMPTransport.cpp | 4 +- idd/LGIdd/transport/lgmp/CLGMPTransport.h | 2 +- idd/LGIddHelper/CNotifyWindow.cpp | 14 +- 16 files changed, 334 insertions(+), 176 deletions(-) create mode 100644 idd/LGIdd/transport/FrameCaps.h create mode 100644 idd/LGIdd/transport/lgmp/CLGMPFrameCaps.cpp rename idd/LGIdd/transport/{FrameMemoryLimits.h => lgmp/CLGMPFrameCaps.h} (69%) diff --git a/idd/LGIdd/LGIdd.vcxproj b/idd/LGIdd/LGIdd.vcxproj index 98d168bb..ee52da65 100644 --- a/idd/LGIdd/LGIdd.vcxproj +++ b/idd/LGIdd/LGIdd.vcxproj @@ -69,6 +69,7 @@ + @@ -116,7 +117,7 @@ - + @@ -130,6 +131,7 @@ + diff --git a/idd/LGIdd/LGIdd.vcxproj.filters b/idd/LGIdd/LGIdd.vcxproj.filters index 4940774e..bcbb8cbd 100644 --- a/idd/LGIdd/LGIdd.vcxproj.filters +++ b/idd/LGIdd/LGIdd.vcxproj.filters @@ -169,7 +169,7 @@ Transport - + Transport @@ -211,6 +211,9 @@ Transport\LGMP + + Transport\LGMP + Transport\LGMP @@ -342,6 +345,9 @@ Transport\LGMP + + Transport\LGMP + Transport\LGMP diff --git a/idd/LGIdd/display/CDeviceContext.cpp b/idd/LGIdd/display/CDeviceContext.cpp index a2d37271..58d64b96 100644 --- a/idd/LGIdd/display/CDeviceContext.cpp +++ b/idd/LGIdd/display/CDeviceContext.cpp @@ -272,7 +272,7 @@ void CDeviceContext::InitAdapter() return; } DEBUG_TRACE("Loading configured display modes"); - if (!m_displayConfiguration.Load(m_transport->GetMemoryLimits())) + if (!m_displayConfiguration.Load(*m_transport)) { m_initInProgress.store(0); return; @@ -405,8 +405,7 @@ void CDeviceContext::ReplugMonitor() void CDeviceContext::ReloadSettings() { - if (!m_displayConfiguration.ReloadSettings( - m_transport->GetMemoryLimits())) + if (!m_displayConfiguration.ReloadSettings(*m_transport)) return; ReplugMonitor(); @@ -449,7 +448,7 @@ void CDeviceContext::SetResolution(uint32_t width, uint32_t height) { const CDisplayConfiguration::ResolutionResult result = m_displayConfiguration.SetResolution( - width, height, m_transport->GetMemoryLimits()); + width, height, *m_transport); switch (result.status) { @@ -464,6 +463,10 @@ void CDeviceContext::SetResolution(uint32_t width, uint32_t height) g_pipe.ResolutionRejected(width, height, result.requiredMiB); break; + case CDisplayConfiguration::ResolutionStatus::UNSUPPORTED: + g_pipe.ResolutionRejected(width, height, 0); + break; + default: break; } diff --git a/idd/LGIdd/display/CDisplayConfiguration.cpp b/idd/LGIdd/display/CDisplayConfiguration.cpp index ab6b5d6e..00d66da0 100644 --- a/idd/LGIdd/display/CDisplayConfiguration.cpp +++ b/idd/LGIdd/display/CDisplayConfiguration.cpp @@ -23,77 +23,9 @@ #include "CDebug.h" #include "CSRWLock.h" -#include #include #include -static const UINT64 FRAME_BYTES_PER_PIXEL = 4; - -bool CDisplayConfiguration::AlignUp( - UINT64 value, UINT64 alignment, UINT64& result) -{ - if (!alignment || (alignment & (alignment - 1))) - return false; - - const UINT64 mask = alignment - 1; - result = (value + mask) & ~mask; - return true; -} - -bool CDisplayConfiguration::CalculateFrameSize( - uint32_t width, uint32_t height, UINT64& frameSize) -{ - frameSize = 0; - if (!width || !height) - return false; - - UINT64 pitch; - if (!AlignUp((UINT64)width * FRAME_BYTES_PER_PIXEL, - D3D12_TEXTURE_DATA_PITCH_ALIGNMENT, pitch)) - return false; - - frameSize = pitch * height; - return true; -} - -bool CDisplayConfiguration::GetResolutionMemoryRequirements( - uint32_t width, uint32_t height, UINT64 alignment, - const FrameMemoryLimits& limits, UINT64& frameSize, UINT64& requiredSize) -{ - frameSize = 0; - requiredSize = 0; - - if (!alignment || !limits.frameMemoryOffset || !limits.bufferCount || - !CalculateFrameSize(width, height, frameSize)) - return false; - - UINT64 frameAllocationSize; - if (!AlignUp(frameSize + alignment, alignment, frameAllocationSize)) - return false; - - UINT64 frameMemoryStart; - if (!AlignUp(limits.frameMemoryOffset, alignment, frameMemoryStart)) - return false; - - requiredSize = frameMemoryStart + - frameAllocationSize * limits.bufferCount; - return true; -} - -uint32_t CDisplayConfiguration::RecommendedMemorySizeMiB( - UINT64 requiredSize) -{ - UINT64 sizeMiB = requiredSize / 1048576; - if (requiredSize % 1048576) - ++sizeMiB; - - UINT64 result = 1; - while (result < sizeMiB && result <= UINT32_MAX / 2) - result <<= 1; - - return result < sizeMiB ? UINT32_MAX : (uint32_t)result; -} - #ifdef HAS_IDDCX_110 static inline IDDCX_WIRE_BITS_PER_COMPONENT GetWireBitsPerComponent(bool hdr) { @@ -117,7 +49,7 @@ CDisplayConfiguration::CDisplayConfiguration(CSettings& settings) : { } -bool CDisplayConfiguration::LoadModes(const FrameMemoryLimits& limits) +bool CDisplayConfiguration::LoadModes(const FrameCaps& caps) { const CSettings::DisplayModes configuredModes = m_settings.LoadModes(); @@ -127,17 +59,17 @@ bool CDisplayConfiguration::LoadModes(const FrameMemoryLimits& limits) CSettings::DisplayModes newModes; newModes.reserve(configuredModes.size()); - const UINT64 alignment = limits.alignment ? limits.alignment : - D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; - bool hasPreferred = false; for (const auto& configuredMode : configuredModes) { - UINT64 frameSize; - UINT64 requiredMemorySize; - if (!GetResolutionMemoryRequirements(configuredMode.width, - configuredMode.height, alignment, limits, frameSize, - requiredMemorySize)) + const FrameMode frameMode = + { + configuredMode.width, + configuredMode.height, + configuredMode.refreshMilliHz, + }; + if (!frameMode.width || !frameMode.height || + !frameMode.refreshMilliHz) { DEBUG_WARN("Filtering invalid %s mode %ux%u@%.3f", configuredMode.extraMode ? "extra" : "configured", @@ -145,16 +77,13 @@ bool CDisplayConfiguration::LoadModes(const FrameMemoryLimits& limits) configuredMode.refreshMilliHz / 1000.0); continue; } - - if (requiredMemorySize > limits.capacity) + if (!caps.CanUseMode(frameMode)) { DEBUG_WARN( - "Filtering %s mode %ux%u@%.3f: requires %llu bytes of transport memory, only %llu bytes are available", + "Filtering unsupported %s mode %ux%u@%.3f", configuredMode.extraMode ? "extra" : "configured", configuredMode.width, configuredMode.height, - configuredMode.refreshMilliHz / 1000.0, - (unsigned long long)requiredMemorySize, - (unsigned long long)limits.capacity); + configuredMode.refreshMilliHz / 1000.0); continue; } @@ -169,7 +98,7 @@ bool CDisplayConfiguration::LoadModes(const FrameMemoryLimits& limits) if (newModes.empty()) { - DEBUG_ERROR("No configured display modes fit in transport memory"); + DEBUG_ERROR("No configured display modes are supported"); return false; } @@ -183,13 +112,13 @@ bool CDisplayConfiguration::LoadModes(const FrameMemoryLimits& limits) return true; } -bool CDisplayConfiguration::Load(const FrameMemoryLimits& limits) +bool CDisplayConfiguration::Load(const FrameCaps& caps) { - return LoadModes(limits); + return LoadModes(caps); } bool CDisplayConfiguration::ReloadSettings( - const FrameMemoryLimits& limits) + const FrameCaps& caps) { bool modesLoaded = false; { @@ -209,7 +138,7 @@ bool CDisplayConfiguration::ReloadSettings( } if (settingsUpdated) - modesLoaded = LoadModes(limits); + modesLoaded = LoadModes(caps); } if (!modesLoaded) @@ -219,43 +148,52 @@ bool CDisplayConfiguration::ReloadSettings( CDisplayConfiguration::ResolutionResult CDisplayConfiguration::SetResolution( - uint32_t width, uint32_t height, const FrameMemoryLimits& limits) + uint32_t width, uint32_t height, const FrameCaps& caps) { ResolutionResult result; - UINT64 frameSize; - UINT64 requiredMemorySize; - if (!GetResolutionMemoryRequirements(width, height, limits.alignment, - limits, frameSize, requiredMemorySize)) - { - DEBUG_WARN("Ignoring invalid resolution request: %ux%u", width, height); - return result; - } - - if (requiredMemorySize > limits.capacity) - { - result.status = ResolutionStatus::TOO_LARGE; - result.requiredMiB = RecommendedMemorySizeMiB(requiredMemorySize); - DEBUG_WARN( - "Refusing resolution %ux%u: frame requires %llu bytes, only %llu bytes are available; transport memory must be at least %u MiB", - width, height, - (unsigned long long)frameSize, - (unsigned long long)limits.maxFrameSize, - result.requiredMiB); - return result; - } - CSettings::DisplayMode mode = {}; mode.width = width; mode.height = height; mode.refreshMilliHz = m_settings.GetDefaultRefreshMilliHz(); mode.preferred = true; + if (!mode.width || !mode.height || !mode.refreshMilliHz) + { + DEBUG_WARN("Ignoring invalid resolution request: %ux%u", width, height); + return result; + } + + const FrameMode frameMode = + { + mode.width, + mode.height, + mode.refreshMilliHz, + }; + 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(limits)) + else if (!LoadModes(caps)) result.status = ResolutionStatus::MODES_FAILED; else { diff --git a/idd/LGIdd/display/CDisplayConfiguration.h b/idd/LGIdd/display/CDisplayConfiguration.h index ff7dbdbd..30cc2cae 100644 --- a/idd/LGIdd/display/CDisplayConfiguration.h +++ b/idd/LGIdd/display/CDisplayConfiguration.h @@ -25,7 +25,7 @@ #include "config/CSettings.h" #include "display/CEdid.h" #include "display/IddCxCompat.h" -#include "transport/FrameMemoryLimits.h" +#include "transport/FrameCaps.h" #include #include @@ -39,6 +39,7 @@ public: SUCCESS, INVALID, TOO_LARGE, + UNSUPPORTED, SETTINGS_FAILED, MODES_FAILED, }; @@ -68,27 +69,19 @@ private: CEdid m_edid; bool m_hdrEnabled = false; - bool LoadModes(const FrameMemoryLimits& limits); + bool LoadModes(const FrameCaps& caps); CSettings::DisplayModes SnapshotModes(bool * hdrEnabled = nullptr) const; - static bool AlignUp(UINT64 value, UINT64 alignment, UINT64& result); - static bool CalculateFrameSize(uint32_t width, uint32_t height, - UINT64& frameSize); - static bool GetResolutionMemoryRequirements(uint32_t width, - uint32_t height, UINT64 alignment, const FrameMemoryLimits& limits, - UINT64& frameSize, UINT64& requiredSize); - static uint32_t RecommendedMemorySizeMiB(UINT64 requiredSize); - public: explicit CDisplayConfiguration(CSettings& settings); CDisplayConfiguration(const CDisplayConfiguration&) = delete; CDisplayConfiguration& operator=(const CDisplayConfiguration&) = delete; - bool Load(const FrameMemoryLimits& limits); - bool ReloadSettings(const FrameMemoryLimits& limits); + bool Load(const FrameCaps& caps); + bool ReloadSettings(const FrameCaps& caps); ResolutionResult SetResolution(uint32_t width, uint32_t height, - const FrameMemoryLimits& limits); + const FrameCaps& caps); void InitializeEdid(bool hdr); void RebuildEdid(bool hdr); diff --git a/idd/LGIdd/transport/CTransportManager.cpp b/idd/LGIdd/transport/CTransportManager.cpp index 644355c6..16a452bb 100644 --- a/idd/LGIdd/transport/CTransportManager.cpp +++ b/idd/LGIdd/transport/CTransportManager.cpp @@ -328,6 +328,8 @@ ITransport::OpenResult CTransportManager::OpenEntry(Entry& entry) bool CTransportManager::InitializeEntry(Entry& entry) { std::shared_ptr transport; + uint32_t services = 0; + bool required = false; { CSRWSharedLock entryLock(entry.lock); if (entry.state == State::INITIALIZED || entry.state == State::READY) @@ -335,6 +337,8 @@ bool CTransportManager::InitializeEntry(Entry& entry) if (entry.state != State::OPEN) return false; transport = entry.transport; + services = entry.config.services; + required = entry.required; } if (!transport || !transport->Initialize()) @@ -344,12 +348,30 @@ bool CTransportManager::InitializeEntry(Entry& entry) return false; } - const FrameMemoryLimits limits = transport->GetMemoryLimits(); + std::shared_ptr frameCaps; + if (services & TRANSPORT_SERVICE_FRAME) + { + frameCaps = transport->GetFrameCaps(); + if (!frameCaps && required) + { + CSRWExclusiveLock entryLock(entry.lock); + entry.state = State::FAILED; + return false; + } + } + const bool hasFrameCaps = frameCaps != nullptr; { CSRWExclusiveLock entryLock(entry.lock); - entry.limits = limits; - entry.limitsValid = true; - entry.state = State::INITIALIZED; + // The first successfully initialized instance fixes the advertised + // capability contract. Recreating an instance must not change the mode + // list during a runtime restart. + if (!entry.frameCaps) + entry.frameCaps = std::move(frameCaps); + // A best-effort instance without a capability contract can continue to + // provide its other configured services, but must not receive frames. + entry.frameAbsent = (services & TRANSPORT_SERVICE_FRAME) && + !hasFrameCaps; + entry.state = State::INITIALIZED; } return true; } @@ -627,8 +649,6 @@ void CTransportManager::RetryEntry(Entry& entry, uint64_t now, { CSRWExclusiveLock entryLock(entry.lock); entry.transport.reset(); - entry.limits = FrameMemoryLimits {}; - entry.limitsValid = false; entry.directMemory = DirectFrameBufferMemory {}; entry.directMemoryValid = false; entry.setupDone = false; @@ -1180,20 +1200,42 @@ void CTransportManager::RecoveryStatus(const SourceKey& source, } } -FrameMemoryLimits CTransportManager::GetMemoryLimits() const +bool CTransportManager::CanUseMode(const FrameMode& mode, + uint32_t * requiredSizeMiB) const { + if (requiredSizeMiB) + *requiredSizeMiB = 0; + + std::shared_ptr caps[FRAME_MAX_SINKS]; + unsigned capsCount = 0; + Entry * entries[FRAME_MAX_SINKS] = {}; + const unsigned count = Entries(entries); + for (unsigned i = 0; i < count; ++i) { - CSRWSharedLock managerLock(m_lock); - if (m_stopping || m_stopped) - return FrameMemoryLimits {}; + Entry& entry = *entries[i]; + CSRWSharedLock entryLock(entry.lock); + if (!entry.required || + !(entry.config.services & TRANSPORT_SERVICE_FRAME)) + continue; + if (!entry.frameCaps) + return false; + caps[capsCount++] = entry.frameCaps; } - Entry * primary = Primary(); - if (!primary) - return FrameMemoryLimits {}; + if (!capsCount) + return false; - CSRWSharedLock entryLock(primary->lock); - return primary->limitsValid ? primary->limits : FrameMemoryLimits {}; + bool supported = true; + for (unsigned i = 0; i < capsCount; ++i) + { + uint32_t hint = 0; + if (caps[i]->CanUseMode(mode, capsCount == 1 ? &hint : nullptr)) + continue; + supported = false; + if (requiredSizeMiB && capsCount == 1) + *requiredSizeMiB = hint; + } + return supported; } DirectFrameBufferMemory CTransportManager::GetDirectMemory() const diff --git a/idd/LGIdd/transport/CTransportManager.h b/idd/LGIdd/transport/CTransportManager.h index cc12ed94..7f8c502b 100644 --- a/idd/LGIdd/transport/CTransportManager.h +++ b/idd/LGIdd/transport/CTransportManager.h @@ -32,7 +32,7 @@ static_assert(TRANSPORT_MAX_INSTANCES == FRAME_MAX_SINKS, "The transport and frame limits must match"); -class CTransportManager final +class CTransportManager final : public FrameCaps { public: using CreateFn = std::unique_ptr (*)( @@ -117,8 +117,7 @@ private: bool syncPending = false; bool recoveryPending = false; RecoveryUpdate recovery; - FrameMemoryLimits limits; - bool limitsValid = false; + std::shared_ptr frameCaps; DirectFrameBufferMemory directMemory; bool directMemoryValid = false; std::shared_ptr exposedTransport; @@ -183,7 +182,8 @@ public: uint64_t session, uint32_t serial, bool active, Recovery state, uint32_t error); - FrameMemoryLimits GetMemoryLimits() const; + bool CanUseMode(const FrameMode& mode, + uint32_t * requiredSizeMiB = nullptr) const override; DirectFrameBufferMemory GetDirectMemory() const; IFrameTransport& Frames(); diff --git a/idd/LGIdd/transport/FrameCaps.h b/idd/LGIdd/transport/FrameCaps.h new file mode 100644 index 00000000..62eca7f4 --- /dev/null +++ b/idd/LGIdd/transport/FrameCaps.h @@ -0,0 +1,41 @@ +/** + * 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 + */ + +#pragma once + +#include + +struct FrameMode +{ + uint32_t width = 0; + uint32_t height = 0; + uint32_t refreshMilliHz = 0; +}; + +class FrameCaps +{ +public: + virtual ~FrameCaps() = default; + + // A rejecting implementation may provide a minimum configured-capacity + // hint. Zero means that no useful size recommendation is available. + virtual bool CanUseMode(const FrameMode& mode, + uint32_t * requiredSizeMiB = nullptr) const = 0; +}; diff --git a/idd/LGIdd/transport/ITransport.h b/idd/LGIdd/transport/ITransport.h index 3924f70e..571e67e3 100644 --- a/idd/LGIdd/transport/ITransport.h +++ b/idd/LGIdd/transport/ITransport.h @@ -21,9 +21,10 @@ #pragma once #include "transport/DirectFrameBufferMemory.h" -#include "transport/FrameMemoryLimits.h" +#include "transport/FrameCaps.h" #include "transport/TransportConfig.h" +#include #include #include @@ -89,7 +90,14 @@ public: virtual void RecoveryStatus( uint64_t, uint32_t, bool, Recovery, uint32_t) {} - virtual FrameMemoryLimits GetMemoryLimits() const = 0; + // Frame capabilities describe the configured instance, not transient + // runtime state. CanUseMode answers are immutable for the returned + // object's lifetime, and recreating an instance from the same descriptor + // must provide the same capability contract. + virtual std::shared_ptr GetFrameCaps() const + { + return std::shared_ptr(); + } virtual DirectFrameBufferMemory GetDirectMemory() const = 0; // Component pointers are fixed after Setup and remain valid until Stop. diff --git a/idd/LGIdd/transport/lgmp/CLGMPFrameCaps.cpp b/idd/LGIdd/transport/lgmp/CLGMPFrameCaps.cpp new file mode 100644 index 00000000..cea95e48 --- /dev/null +++ b/idd/LGIdd/transport/lgmp/CLGMPFrameCaps.cpp @@ -0,0 +1,112 @@ +/** + * 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 "transport/lgmp/CLGMPFrameCaps.h" + +#include +#include + +namespace +{ + static const uint64_t FRAME_BYTES_PER_PIXEL = 4; + + bool AlignUp(uint64_t value, uint64_t alignment, uint64_t& result) + { + if (!alignment || (alignment & (alignment - 1))) + return false; + + const uint64_t mask = alignment - 1; + if (value > (std::numeric_limits::max)() - mask) + return false; + result = (value + mask) & ~mask; + return true; + } + + bool CalculateFrameSize( + const FrameMode& mode, uint64_t& frameSize) + { + frameSize = 0; + if (!mode.width || !mode.height || !mode.refreshMilliHz) + return false; + + uint64_t pitch; + if (!AlignUp((uint64_t)mode.width * FRAME_BYTES_PER_PIXEL, + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT, pitch) || + pitch > (std::numeric_limits::max)() / mode.height) + return false; + + frameSize = pitch * mode.height; + return true; + } + + uint32_t RecommendedSizeMiB(uint64_t requiredSize) + { + uint64_t sizeMiB = requiredSize / 1048576; + if (requiredSize % 1048576) + ++sizeMiB; + + uint64_t result = 1; + while (result < sizeMiB && result <= UINT32_MAX / 2) + result <<= 1; + return result < sizeMiB ? UINT32_MAX : (uint32_t)result; + } +} + +CLGMPFrameCaps::CLGMPFrameCaps(uint64_t capacity, + uint64_t frameMemoryOffset, uint32_t bufferCount) : + m_capacity(capacity), + m_frameMemoryOffset(frameMemoryOffset), + m_bufferCount(bufferCount) +{ +} + +bool CLGMPFrameCaps::CanUseMode(const FrameMode& mode, + uint32_t * requiredSizeMiB) const +{ + if (requiredSizeMiB) + *requiredSizeMiB = 0; + + const uint64_t alignment = + D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; + uint64_t frameSize; + if (!m_frameMemoryOffset || !m_bufferCount || + !CalculateFrameSize(mode, frameSize) || + frameSize > (std::numeric_limits::max)() - alignment) + return false; + + uint64_t frameAllocationSize; + uint64_t frameMemoryStart; + if (!AlignUp(frameSize + alignment, alignment, + frameAllocationSize) || + !AlignUp(m_frameMemoryOffset, alignment, frameMemoryStart) || + frameAllocationSize > + ((std::numeric_limits::max)() - frameMemoryStart) / + m_bufferCount) + return false; + + const uint64_t requiredSize = frameMemoryStart + + frameAllocationSize * m_bufferCount; + if (requiredSize <= m_capacity) + return true; + + if (requiredSizeMiB) + *requiredSizeMiB = RecommendedSizeMiB(requiredSize); + return false; +} diff --git a/idd/LGIdd/transport/FrameMemoryLimits.h b/idd/LGIdd/transport/lgmp/CLGMPFrameCaps.h similarity index 69% rename from idd/LGIdd/transport/FrameMemoryLimits.h rename to idd/LGIdd/transport/lgmp/CLGMPFrameCaps.h index 9e7ac6f6..291062df 100644 --- a/idd/LGIdd/transport/FrameMemoryLimits.h +++ b/idd/LGIdd/transport/lgmp/CLGMPFrameCaps.h @@ -20,13 +20,21 @@ #pragma once +#include "transport/FrameCaps.h" + #include -struct FrameMemoryLimits +class CLGMPFrameCaps final : public FrameCaps { - uint64_t capacity = 0; - uint64_t frameMemoryOffset = 0; - uint64_t alignment = 0; - uint64_t maxFrameSize = 0; - unsigned bufferCount = 0; +private: + uint64_t m_capacity; + uint64_t m_frameMemoryOffset; + uint32_t m_bufferCount; + +public: + CLGMPFrameCaps(uint64_t capacity, uint64_t frameMemoryOffset, + uint32_t bufferCount); + + bool CanUseMode(const FrameMode& mode, + uint32_t * requiredSizeMiB = nullptr) const override; }; diff --git a/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.cpp b/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.cpp index 8ca83568..786e3b4a 100644 --- a/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.cpp +++ b/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.cpp @@ -21,10 +21,13 @@ #include "transport/lgmp/CLGMPFrameTransport.h" #include "transport/lgmp/CIVSHMEM.h" +#include "transport/lgmp/CLGMPFrameCaps.h" #include "transport/lgmp/CLGMPHost.h" #include "CDebug.h" #include +#include +#include #pragma warning(push) #pragma warning(disable: 4200) @@ -245,15 +248,11 @@ void CLGMPFrameTransport::DeInit() memset(m_frameOwnerQueue, 0, sizeof(m_frameOwnerQueue)); } -FrameMemoryLimits CLGMPFrameTransport::GetMemoryLimits() const +std::shared_ptr CLGMPFrameTransport::GetFrameCaps() const { - FrameMemoryLimits limits; - limits.capacity = m_ivshmem.GetUsableSize(); - limits.frameMemoryOffset = m_frameMemoryOffset; - limits.alignment = m_alignSize; - limits.maxFrameSize = m_maxFrameSize; - limits.bufferCount = LGMP_Q_FRAME_BUFFER_LEN; - return limits; + return std::shared_ptr(new (std::nothrow) + CLGMPFrameCaps(m_ivshmem.GetUsableSize(), m_frameMemoryOffset, + LGMP_Q_FRAME_BUFFER_LEN)); } CLGMPFrameTransport::SubscriberSnapshot diff --git a/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.h b/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.h index c7cfa732..d5364b58 100644 --- a/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.h +++ b/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.h @@ -33,9 +33,11 @@ extern "C" { #include "common/KVMFR.h" #include "capture/CFrameScheduler.h" #include "capture/FramePipeline.h" -#include "transport/FrameMemoryLimits.h" +#include "transport/FrameCaps.h" #include "transport/IFrameSink.h" +#include + class CIVSHMEM; class CLGMPHost; class CLGMPTransport; @@ -151,7 +153,7 @@ private: void SealMemoryLayout(); bool Setup(size_t alignSize); void DeInit(); - FrameMemoryLimits GetMemoryLimits() const; + std::shared_ptr GetFrameCaps() const; SubscriberSnapshot SnapshotSubscribers() const; void FinalizeSubscribers( const SubscriberSnapshot& snapshot, uint64_t now); diff --git a/idd/LGIdd/transport/lgmp/CLGMPTransport.cpp b/idd/LGIdd/transport/lgmp/CLGMPTransport.cpp index ae847cce..a7aaad06 100644 --- a/idd/LGIdd/transport/lgmp/CLGMPTransport.cpp +++ b/idd/LGIdd/transport/lgmp/CLGMPTransport.cpp @@ -239,9 +239,9 @@ void CLGMPTransport::RecoveryStatus( session, serial, active, wireState, wireError); } -FrameMemoryLimits CLGMPTransport::GetMemoryLimits() const +std::shared_ptr CLGMPTransport::GetFrameCaps() const { - return m_frames.GetMemoryLimits(); + return m_frames.GetFrameCaps(); } DirectFrameBufferMemory CLGMPTransport::GetDirectMemory() const diff --git a/idd/LGIdd/transport/lgmp/CLGMPTransport.h b/idd/LGIdd/transport/lgmp/CLGMPTransport.h index 4ac4104f..668e7475 100644 --- a/idd/LGIdd/transport/lgmp/CLGMPTransport.h +++ b/idd/LGIdd/transport/lgmp/CLGMPTransport.h @@ -61,7 +61,7 @@ public: uint64_t session, uint32_t serial, bool active, Recovery state, uint32_t error) override; - FrameMemoryLimits GetMemoryLimits() const override; + std::shared_ptr GetFrameCaps() const override; DirectFrameBufferMemory GetDirectMemory() const override; IFrameSink * FrameSink() override { return &m_frames; } diff --git a/idd/LGIddHelper/CNotifyWindow.cpp b/idd/LGIddHelper/CNotifyWindow.cpp index 45f76d36..77e6509e 100644 --- a/idd/LGIddHelper/CNotifyWindow.cpp +++ b/idd/LGIddHelper/CNotifyWindow.cpp @@ -320,11 +320,15 @@ void CNotifyWindow::handleResolutionRejected(uint32_t width, uint32_t height, nid.dwInfoFlags = NIIF_WARNING; StringCbCopy(nid.szInfoTitle, sizeof nid.szInfoTitle, L"Resolution change refused"); - StringCbPrintf(nid.szInfo, sizeof nid.szInfo, - L"The requested resolution %ux%u does not fit in shared memory. " - L"Change the total IVSHMEM size to at least %u MiB.", - width, height, - requiredSizeMiB); + if (requiredSizeMiB) + StringCbPrintf(nid.szInfo, sizeof nid.szInfo, + L"The requested resolution %ux%u requires at least %u MiB of " + L"configured frame capacity.", + width, height, requiredSizeMiB); + else + StringCbPrintf(nid.szInfo, sizeof nid.szInfo, + L"The requested resolution %ux%u is not supported by the current " + L"configuration.", width, height); if (!Shell_NotifyIcon(NIM_MODIFY, &nid)) DEBUG_ERROR_HR(GetLastError(), "Shell_NotifyIcon(NIM_MODIFY)");