[idd] project: organize driver sources by responsibility

Group the IDD sources and Visual Studio filters by subsystem.

Split the device and swap-chain implementations into focused units,
rename the context classes, and reduce header coupling.
This commit is contained in:
Geoffrey McRae
2026-08-07 14:38:36 +10:00
parent 3ddc199bec
commit 30a1383d5e
76 changed files with 5067 additions and 3923 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,350 +0,0 @@
/**
* 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 <Windows.h>
#include <wdf.h>
#include <IddCx.h>
#include <atomic>
#include <vector>
#include "CIVSHMEM.h"
#include "CSettings.h"
#include "CEdid.h"
#include "CPostProcessor.h"
#include "CFrameScheduler.h"
extern "C" {
#include "lgmp/host.h"
}
#include "common/KVMFR.h"
// IddCx 1.10 HDR/WCG types are only visible when the WDK targets
// (NTDDI >= 0x0A000005) *and* the build flags IDDCX_VERSION_MAJOR/MINOR are set to >= 1.10.
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
(IDDCX_VERSION_MAJOR > 1 || (IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10)) && \
NTDDI_VERSION >= 0x0A000005
#define HAS_IDDCX_110
#endif
#define MAX_POINTER_SIZE (sizeof(KVMFRCursor) + (512 * 512 * 4))
#define COLOR_TRANSFORM_BUFFERS 3
#define POINTER_SHAPE_BUFFERS 3
//FIXME: this should not really be done here, this is a hack
#pragma warning(push)
#pragma warning(disable: 4200)
struct FrameBuffer
{
volatile uint32_t wp;
uint8_t data[0];
};
#pragma warning(pop)
class CIndirectDeviceContext
{
private:
WDFDEVICE m_wdfDevice;
IDDCX_ADAPTER m_adapter = nullptr;
IDDCX_MONITOR m_monitor = nullptr;
LUID m_preferredRenderAdapter = {};
bool m_havePreferredRenderAdapter = false;
bool m_replugMonitor = false;
bool m_replugPending = false;
bool m_monitorDeparted = false;
bool m_swapChainAssigned = false;
bool m_swapChainReady = false;
bool m_waitForSwapChainRelease = false;
// Guards the adapter/monitor init handshake and the replug state machine
// (monitor/replug/swap-chain state, m_doSetMode, m_setMode). These are
// touched from the IddCx callback threads, swap-chain thread and LGMP timer.
SRWLOCK m_stateLock = SRWLOCK_INIT;
// Retry state for InitAdapter. At boot the IVSHMEM device may not have
// enumerated yet; if so we re-attempt from a timer instead of giving up.
WDFTIMER m_initTimer = nullptr;
bool m_ivshmemOpened = false;
std::atomic<LONG> m_initInProgress = 0;
CIVSHMEM m_ivshmem;
PLGMPHost m_lgmp = nullptr;
WDFTIMER m_lgmpTimer = nullptr;
PLGMPHostQueue m_frameQueue = nullptr;
PLGMPHostQueue m_frameOwnerQueue[LGMP_Q_FRAME_LEN] = {};
SRWLOCK m_lgmpProcessLock = SRWLOCK_INIT;
CFrameScheduler m_frameScheduler;
PLGMPHostQueue m_pointerQueue = nullptr;
PLGMPMemory m_pointerMemory [LGMP_Q_POINTER_LEN ] = {};
PLGMPMemory m_pointerShapeMemory[POINTER_SHAPE_BUFFERS] = {};
PLGMPMemory m_pointerTransformMemory[COLOR_TRANSFORM_BUFFERS] = {};
PLGMPMemory m_pointerShape = nullptr;
int m_pointerMemoryIndex = 0;
int m_pointerShapeIndex = 0;
int m_pointerTransformIndex = 0;
bool m_cursorVisible = false;
int m_cursorX = 0, m_cursorY = 0;
size_t m_alignSize = 0;
size_t m_frameMemoryOffset = 0;
size_t m_maxFrameSize = 0;
// LGMP publication precedes copy completion. Replay only completed frames;
// the deferred index tracks the newest frame still owed to the owner.
std::atomic<LONG> m_submittedFrameIndex = -1;
std::atomic<LONG> m_readyFrameIndex = -1;
LONG m_deferredOwnerFrameIndex = -1;
std::atomic<bool> m_frameInFlight[LGMP_Q_FRAME_BUFFER_LEN] = {};
bool m_frameCompleted[LGMP_Q_FRAME_BUFFER_LEN] = {};
SRWLOCK m_framePublishLock = SRWLOCK_INIT;
uint64_t m_framePublishSequence = 0;
uint64_t m_frameLastPublishSequence[LGMP_Q_FRAME_BUFFER_LEN] = {};
enum SharedFramePostResult
{
SHARED_FRAME_FAILED,
SHARED_FRAME_IDLE,
SHARED_FRAME_POSTED,
};
struct FrameDelivery
{
uint64_t sharedOwnerToken = 0;
unsigned ownerQueueMask = 0;
uint32_t sharedOwnerClientID = 0;
bool sharedOwnerPending = false;
bool sharedPending = false;
};
struct OwnerDelivery
{
uint64_t token = 0;
uint32_t clientID = 0;
unsigned frameIndex = 0;
bool active = false;
};
FrameDelivery m_frameDelivery[LGMP_Q_FRAME_BUFFER_LEN] = {};
OwnerDelivery m_ownerDelivery[LGMP_Q_FRAME_LEN] = {};
uint32_t m_formatVer = 0;
uint32_t m_frameSerial = 0;
PLGMPMemory m_frameMemory[LGMP_Q_FRAME_BUFFER_LEN] = {};
KVMFRFrame * m_frame [LGMP_Q_FRAME_BUFFER_LEN] = {};
FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_BUFFER_LEN] = {};
unsigned m_width = 0;
unsigned m_height = 0;
unsigned m_frameWidth = 0;
unsigned m_frameHeight = 0;
unsigned m_pitch = 0;
DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN;
FrameType m_frameType = FRAME_TYPE_INVALID;
UINT m_iddCxVersion = 0;
bool m_hasIddCx110DDIs = false;
bool m_canProcessFP16 = false;
bool m_softwareMode = true;
// Previous HDR metadata used to detect changes for formatVer bumps
uint16_t m_lastHDRDisplayPrimary[3][2] = {};
uint16_t m_lastHDRWhitePoint[2] = {};
uint32_t m_lastHDRMaxDisplayLuminance = 0;
uint32_t m_lastHDRMinDisplayLuminance = 0;
uint32_t m_lastHDRMaxContentLightLevel = 0;
uint32_t m_lastHDRMaxFrameAverageLightLevel = 0;
uint32_t m_lastSDRWhiteLevel = 0;
bool m_lastHDRActive = false;
bool m_lastHDRMetadata = false;
// Windows display calibration transform. The callback publishes immutable
// snapshots so the swap-chain thread never observes a partially updated
// matrix or LUT.
mutable SRWLOCK m_colorTransformLock = SRWLOCK_INIT;
std::shared_ptr<const D12ColorTransform> m_colorTransform;
void QueryIddCxCapabilities();
void ScheduleInitRetry();
void StopInitRetry();
bool InitializeLGMP();
void DeInitLGMP();
void LGMPTimer();
void ProcessFrameDeliveries();
bool FrameBufferReferenced(unsigned frameIndex) const;
int FindAvailableFrameBuffer(bool allowReady) const;
int FindNewestCompletedFrame(unsigned excludeFrameIndex) const;
int FindAvailableOwnerQueue(unsigned preferredIndex) const;
unsigned CountOwnerDeliveries(uint32_t clientID) const;
bool HasMatchingOwnerDelivery(uint32_t clientID, unsigned frameIndex,
uint64_t token) const;
SharedFramePostResult PostSharedFrame(unsigned frameIndex,
uint32_t excludeClientID, uint64_t now);
bool PostSharedOwnerFrame(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule);
void ResendCursor();
void SendColorTransform();
void InitializeEdid();
bool GetResolutionMemoryRequirements(uint32_t width, uint32_t height,
UINT64 alignment, UINT64& frameSize, UINT64& ivshmemSize) const;
// Guards m_displayModes and m_edid. The mode list is rebuilt on the LGMP
// timer thread (SetResolution) while IddCx concurrently enumerates it on its
// own callback threads. The EDID is initialized once and remains immutable.
// Never held across an IddCx API call - snapshot then call.
mutable SRWLOCK m_modeLock = SRWLOCK_INIT;
// Serializes registry-backed mode changes with rebuilding m_displayModes.
// Reload requests arrive on the pipe thread while dynamic resolution
// requests arrive on the LGMP timer thread.
SRWLOCK m_modeReloadLock = SRWLOCK_INIT;
CSettings::DisplayModes m_displayModes;
CEdid m_edid;
CSettings::DisplayMode m_setMode = {};
bool m_doSetMode = false;
// Set by ReplugMonitor after a departure to rebuild the monitor from the LGMP
// timer, off the IddCx callback thread.
std::atomic<LONG> m_finishInitQueued = 0;
std::atomic<LONG> m_replugQueued = 0;
public:
CIndirectDeviceContext(_In_ WDFDEVICE wdfDevice) :
m_wdfDevice(wdfDevice) {};
virtual ~CIndirectDeviceContext() { DeInitLGMP(); }
bool SetupLGMP(size_t alignSize);
bool PopulateDefaultModes();
void InitAdapter();
void FinishAdapterInit(UINT connectorIndex);
void FinishInit(UINT connectorIndex);
void ReloadSettings();
void ReplugMonitor();
void OnMonitorDestroyed(IDDCX_MONITOR monitor);
void OnSwapChainAssigned();
void OnSwapChainReleased();
void OnSwapChainReady();
NTSTATUS ParseMonitorDescription(
const IDARG_IN_PARSEMONITORDESCRIPTION* inArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs);
NTSTATUS MonitorGetDefaultModes(
const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* inArgs, IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* outArgs);
NTSTATUS MonitorQueryTargetModes(
const IDARG_IN_QUERYTARGETMODES* inArgs, IDARG_OUT_QUERYTARGETMODES* outArgs);
#ifdef HAS_IDDCX_110
NTSTATUS ParseMonitorDescription2(
const IDARG_IN_PARSEMONITORDESCRIPTION2* inArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs);
NTSTATUS MonitorQueryTargetModes2(
const IDARG_IN_QUERYTARGETMODES2* inArgs, IDARG_OUT_QUERYTARGETMODES* outArgs);
#endif
void SetResolution(uint32_t width, uint32_t height);
size_t GetAlignSize () const { return m_alignSize ; }
size_t GetMaxFrameSize() const { return m_maxFrameSize ; }
bool HasIddCx110DDIs() const { return m_hasIddCx110DDIs; }
bool CanProcessFP16 () const { return m_canProcessFP16; }
bool IsSoftwareMode () const { return m_softwareMode ; }
struct PreparedFrameBuffer
{
unsigned frameIndex;
uint8_t* mem;
bool fullCopy;
};
bool FrameBufferAvailable(const CFrameScheduler::Schedule& schedule,
bool allowReadyReplacement = true);
bool HasPublishedFrame() const
{
return m_readyFrameIndex.load(std::memory_order_acquire) >= 0;
}
void ProcessFrameQueue();
bool GetSharedFrameTarget(uint64_t now, uint64_t& target);
bool ReplaySharedFrame(uint64_t now, bool& retry);
PreparedFrameBuffer PrepareFrameBuffer(unsigned pitch,
const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat,
const RECT * dirtyRects, unsigned nbDirtyRects,
const CFrameScheduler::Schedule& schedule,
bool allowReadyReplacement = true);
bool PublishFrameBuffer(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule, bool& deliveredToOwner);
bool RepublishFrameBuffer(const CFrameScheduler::Schedule& schedule);
bool TryFrameSubmitted(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule);
void CommitFrameBuffer(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule, bool periodic,
bool deliveredToOwner);
void AbortFrameBuffer(unsigned frameIndex);
void FailFrameBuffer(unsigned frameIndex);
void CompleteFrameBuffer(unsigned frameIndex, bool succeeded);
void SetFrameTiming(unsigned frameIndex, uint64_t captureTime,
uint64_t postProcessTime, uint64_t copyTime, uint64_t readyTime,
uint64_t holdTime, const CFrameScheduler::Schedule& schedule,
uint64_t completedAt);
void WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) const;
void WriteFrameBufferRows(unsigned frameIndex, void * src,
size_t offset, size_t rowBytes, size_t pitch, unsigned rows) const;
void FinalizeFrameBuffer(unsigned frameIndex) const;
void ObserveFrame(uint64_t now);
void ForceFrame();
bool GetPublishTarget(uint64_t now, uint64_t& target,
CFrameScheduler::Schedule& schedule, bool& periodic, bool& republish);
void FrameMissed(const CFrameScheduler::Schedule& schedule,
uint64_t now, bool periodic);
void FrameSuperseded();
HANDLE GetFrameScheduleEvent() const
{
return m_frameScheduler.GetWakeEvent();
}
void TryRecordFrameTiming(uint64_t duration);
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR & info, const BYTE * data,
UINT sdrWhiteLevel);
void SetColorTransform(std::shared_ptr<const D12ColorTransform> transform);
std::shared_ptr<const D12ColorTransform> GetColorTransform() const;
CIVSHMEM &GetIVSHMEM() { return m_ivshmem; }
};
struct CIndirectDeviceContextWrapper
{
CIndirectDeviceContext* context;
void Cleanup()
{
delete context;
context = nullptr;
}
};
WDF_DECLARE_CONTEXT_TYPE(CIndirectDeviceContextWrapper);

View File

@@ -1,71 +0,0 @@
/**
* 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 <Windows.h>
#include <wdf.h>
#include <wrl.h>
#include <memory>
#include "CD3D11Device.h"
#include "CD3D12Device.h"
#include "CD3D12CommandQueue.h"
using namespace Microsoft::WRL;
#define LG_MAX_DIRTY_RECTS 256
class CInteropResource
{
private:
std::shared_ptr<CD3D11Device> m_dx11Device;
std::shared_ptr<CD3D12Device> m_dx12Device;
/* this value is likely released, it is only used to check if the texture supplied
is different, do not rely on it pointing to valid memory */
void * m_srcTex;
ComPtr<ID3D12Resource > m_d12Res;
D3D11_TEXTURE2D_DESC m_format;
ComPtr<ID3D11Fence > m_d11Fence;
ComPtr<ID3D12Fence > m_d12Fence;
UINT64 m_fenceValue;
bool m_ready;
RECT m_dirtyRects[LG_MAX_DIRTY_RECTS];
unsigned m_nbDirtyRects;
public:
bool Init(std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device, ComPtr<ID3D11Texture2D> srcTex);
void Reset();
bool IsReady() { return m_ready; }
bool Compare(const ComPtr<ID3D11Texture2D>& srcTex);
bool Signal();
bool Sync(CD3D12CommandSlot& slot);
void SetFullDamage();
void SetDirtyRects(const RECT * dirtyRects, unsigned nbDirtyRects);
const ComPtr<ID3D12Resource>& GetRes() { return m_d12Res; }
const D3D11_TEXTURE2D_DESC& GetFormat() { return m_format; }
const RECT * GetDirtyRects() { return m_dirtyRects; }
unsigned GetDirtyRectCount() { return m_nbDirtyRects; }
};

View File

@@ -32,10 +32,13 @@
#include <utility>
#include "CDebug.h"
#include "CIndirectDeviceContext.h"
#include "CIndirectMonitorContext.h"
#include "CPipeServer.h"
#include "CSettings.h"
#include "display/CDisplayConfiguration.h"
#include "display/IddCxCompat.h"
#include "display/device/CDeviceContext.h"
#include "display/monitor/Context.h"
#include "transport/CLGMPControl.h"
#include "transport/CPipeServer.h"
#include "config/CSettings.h"
WDFDEVICE l_wdfDevice = nullptr;
@@ -65,7 +68,7 @@ NTSTATUS LGIddDeviceD0Entry(WDFDEVICE device, WDF_POWER_DEVICE_STATE previousSta
UNREFERENCED_PARAMETER(previousState);
DEBUG_INFO("Device entered D0, starting adapter initialization");
auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(device);
auto * wrapper = WdfObjectGet_CDeviceContextWrapper(device);
wrapper->context->InitAdapter();
DEBUG_INFO("Device D0 entry completed");
@@ -77,7 +80,7 @@ NTSTATUS LGIddAdapterInitFinished(IDDCX_ADAPTER adapter, const IDARG_IN_ADAPTER_
DEBUG_INFO("Adapter initialization callback completed with status 0x%08x",
args->AdapterInitStatus);
auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(adapter);
auto * wrapper = WdfObjectGet_CDeviceContextWrapper(adapter);
if (!NT_SUCCESS(args->AdapterInitStatus))
{
DEBUG_ERROR_HR(args->AdapterInitStatus,
@@ -110,22 +113,27 @@ NTSTATUS LGIddParseMonitorDescription(const IDARG_IN_PARSEMONITORDESCRIPTION* in
if (!l_wdfDevice)
return STATUS_INVALID_PARAMETER;
auto* wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(l_wdfDevice);
return wrapper->context->ParseMonitorDescription(inArgs, outArgs);
auto * wrapper = WdfObjectGet_CDeviceContextWrapper(l_wdfDevice);
return wrapper->context->GetDisplayConfiguration().ParseMonitorDescription(
inArgs, outArgs);
}
NTSTATUS LGIddMonitorGetDefaultModes(IDDCX_MONITOR monitor, const IDARG_IN_GETDEFAULTDESCRIPTIONMODES * inArgs,
IDARG_OUT_GETDEFAULTDESCRIPTIONMODES * outArgs)
{
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
return wrapper->context->GetDeviceContext()->MonitorGetDefaultModes(inArgs, outArgs);
auto * wrapper = WdfObjectGet_CMonitorContextWrapper(monitor);
auto * context = wrapper->context->GetDeviceContext();
return context->GetDisplayConfiguration().MonitorGetDefaultModes(
inArgs, outArgs);
}
NTSTATUS LGIddMonitorQueryTargetModes(IDDCX_MONITOR monitor, const IDARG_IN_QUERYTARGETMODES * inArgs,
IDARG_OUT_QUERYTARGETMODES * outArgs)
{
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
return wrapper->context->GetDeviceContext()->MonitorQueryTargetModes(inArgs, outArgs);
auto * wrapper = WdfObjectGet_CMonitorContextWrapper(monitor);
auto * context = wrapper->context->GetDeviceContext();
return context->GetDisplayConfiguration().MonitorQueryTargetModes(
inArgs, outArgs);
}
@@ -136,8 +144,9 @@ NTSTATUS LGIddParseMonitorDescription2(const IDARG_IN_PARSEMONITORDESCRIPTION2*
if (!l_wdfDevice)
return STATUS_INVALID_PARAMETER;
auto* wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(l_wdfDevice);
return wrapper->context->ParseMonitorDescription2(inArgs, outArgs);
auto * wrapper = WdfObjectGet_CDeviceContextWrapper(l_wdfDevice);
return wrapper->context->GetDisplayConfiguration().ParseMonitorDescription2(
inArgs, outArgs);
}
NTSTATUS LGIddAdapterQueryTargetInfo(IDDCX_ADAPTER adapter,
@@ -145,7 +154,7 @@ NTSTATUS LGIddAdapterQueryTargetInfo(IDDCX_ADAPTER adapter,
{
UNREFERENCED_PARAMETER(inArgs);
auto* wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(adapter);
auto * wrapper = WdfObjectGet_CDeviceContextWrapper(adapter);
const bool hdr = wrapper && wrapper->context &&
wrapper->context->CanProcessFP16();
@@ -182,19 +191,20 @@ NTSTATUS LGIddMonitorSetDefaultHdrMetadata(IDDCX_MONITOR monitor,
NTSTATUS LGIddMonitorSetGammaRamp(IDDCX_MONITOR monitor, const IDARG_IN_SET_GAMMARAMP* inArgs)
{
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
auto* ctx = wrapper->context->GetDeviceContext();
auto * wrapper = WdfObjectGet_CMonitorContextWrapper(monitor);
auto * ctx = wrapper->context->GetDeviceContext();
auto & control = ctx->GetLGMPControl();
if (ctx->IsSoftwareMode())
{
ctx->SetColorTransform(nullptr);
control.SetColorTransform(nullptr);
DEBUG_INFO("Ignoring display color transform in software mode");
return STATUS_SUCCESS;
}
if (inArgs->Type == IDDCX_GAMMARAMP_TYPE_DEFAULT)
{
ctx->SetColorTransform(nullptr);
control.SetColorTransform(nullptr);
DEBUG_INFO("Display color transform reset to default");
return STATUS_SUCCESS;
}
@@ -214,7 +224,7 @@ NTSTATUS LGIddMonitorSetGammaRamp(IDDCX_MONITOR monitor, const IDARG_IN_SET_GAMM
return STATUS_INVALID_PARAMETER;
}
const auto* input = static_cast<
const auto * input = static_cast<
const IDDCX_GAMMARAMP_3X4_COLORSPACE_TRANSFORM*>(
inArgs->pGammaRampData);
auto transform = std::make_shared<D12ColorTransform>();
@@ -233,11 +243,11 @@ NTSTATUS LGIddMonitorSetGammaRamp(IDDCX_MONITOR monitor, const IDARG_IN_SET_GAMM
if (IsIdentityColorTransform(*transform))
{
ctx->SetColorTransform(nullptr);
control.SetColorTransform(nullptr);
return STATUS_SUCCESS;
}
ctx->SetColorTransform(std::move(transform));
control.SetColorTransform(std::move(transform));
DEBUG_INFO("Display color transform updated (matrix:%d lut:%d)",
input->MatrixEnabled, input->LutEnabled);
return STATUS_SUCCESS;
@@ -246,15 +256,17 @@ NTSTATUS LGIddMonitorSetGammaRamp(IDDCX_MONITOR monitor, const IDARG_IN_SET_GAMM
NTSTATUS LGIddMonitorQueryTargetModes2(IDDCX_MONITOR monitor, const IDARG_IN_QUERYTARGETMODES2* inArgs,
IDARG_OUT_QUERYTARGETMODES* outArgs)
{
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
return wrapper->context->GetDeviceContext()->MonitorQueryTargetModes2(inArgs, outArgs);
auto * wrapper = WdfObjectGet_CMonitorContextWrapper(monitor);
auto * context = wrapper->context->GetDeviceContext();
return context->GetDisplayConfiguration().MonitorQueryTargetModes2(
inArgs, outArgs);
}
#endif
NTSTATUS LGIddMonitorAssignSwapChain(IDDCX_MONITOR monitor, const IDARG_IN_SETSWAPCHAIN* inArgs)
{
DEBUG_INFO("Swap chain assigned to monitor %p", monitor);
auto * wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
auto * wrapper = WdfObjectGet_CMonitorContextWrapper(monitor);
return wrapper->context->AssignSwapChain(
inArgs->hSwapChain, inArgs->RenderAdapterLuid, inArgs->hNextSurfaceAvailable);
}
@@ -262,7 +274,7 @@ NTSTATUS LGIddMonitorAssignSwapChain(IDDCX_MONITOR monitor, const IDARG_IN_SETSW
NTSTATUS LGIddMonitorUnassignSwapChain(IDDCX_MONITOR monitor)
{
DEBUG_INFO("Swap chain unassigned from monitor %p", monitor);
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
auto * wrapper = WdfObjectGet_CMonitorContextWrapper(monitor);
wrapper->context->UnassignSwapChain();
return STATUS_SUCCESS;
}
@@ -316,10 +328,10 @@ NTSTATUS LGIddCreateDevice(_Inout_ PWDFDEVICE_INIT deviceInit)
return status;
WDF_OBJECT_ATTRIBUTES deviceAttributes;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, CIndirectDeviceContextWrapper);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, CDeviceContextWrapper);
deviceAttributes.EvtCleanupCallback = [](WDFOBJECT object)
{
auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(object);
auto * wrapper = WdfObjectGet_CDeviceContextWrapper(object);
if (wrapper)
{
g_pipe.SetDeviceContext(nullptr);
@@ -339,8 +351,8 @@ NTSTATUS LGIddCreateDevice(_Inout_ PWDFDEVICE_INIT deviceInit)
* callbacks that resolve the context via l_wdfDevice (down-level IddCx that
* provides no adapter/monitor context) must never observe a null context.
*/
auto wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(device);
wrapper->context = new CIndirectDeviceContext(device);
auto wrapper = WdfObjectGet_CDeviceContextWrapper(device);
wrapper->context = new CDeviceContext(device);
l_wdfDevice = device;

View File

@@ -22,9 +22,9 @@
#include "driver.tmh"
#include "CDebug.h"
#include "CPlatformInfo.h"
#include "platform/CPlatformInfo.h"
#include "VersionInfo.h"
#include "CPipeServer.h"
#include "transport/CPipeServer.h"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
@@ -98,4 +98,4 @@ VOID LGIddEvtDriverContextCleanup(_In_ WDFOBJECT DriverObject)
#else
WPP_CLEANUP(WdfDriverWdmGetDriverObject((WDFDRIVER)DriverObject));
#endif
}
}

View File

@@ -24,68 +24,85 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
<ClCompile Include="CD3D12CommandQueue.cpp" />
<ClCompile Include="CEdid.cpp" />
<ClCompile Include="CFrameBufferPool.cpp" />
<ClCompile Include="CFrameBufferResource.cpp" />
<ClCompile Include="CFrameProcessor.cpp" />
<ClCompile Include="CFrameProcessorUtil.cpp" />
<ClCompile Include="CFrameScheduler.cpp" />
<ClCompile Include="CHardwareFrameProcessor.cpp" />
<ClCompile Include="CIndirectDeviceContext.cpp" />
<ClCompile Include="CIndirectMonitorContext.cpp" />
<ClCompile Include="CInteropResourcePool.cpp" />
<ClCompile Include="CInteropResource.cpp" />
<ClCompile Include="CIVSHMEM.cpp" />
<ClCompile Include="CPipeServer.cpp" />
<ClCompile Include="CPlatformInfo.cpp" />
<ClCompile Include="CPostProcessor.cpp" />
<ClCompile Include="effect\CColorTransformEffect.cpp" />
<ClCompile Include="effect\CComputeEffect.cpp" />
<ClCompile Include="effect\CDownsampleEffect.cpp" />
<ClCompile Include="effect\CHDR16to10Effect.cpp" />
<ClCompile Include="effect\CRGB24Effect.cpp" />
<ClCompile Include="CSettings.cpp" />
<ClCompile Include="CSoftwareFrameProcessor.cpp" />
<ClCompile Include="CSwapChainProcessor.cpp" />
<ClCompile Include="CD3D12Device.cpp" />
<ClCompile Include="$(SolutionDir)LGCommon\*.cpp" />
<ClCompile Include="Device.cpp" />
<ClCompile Include="CD3D11Device.cpp" />
<ClCompile Include="Driver.cpp" />
<ClCompile Include="display\CDisplayConfiguration.cpp" />
<ClCompile Include="display\CEdid.cpp" />
<ClCompile Include="display\CMonitorManager.cpp" />
<ClCompile Include="display\device\CDeviceContext.cpp" />
<ClCompile Include="display\monitor\Context.cpp" />
<ClCompile Include="capture\CFrameBufferPool.cpp" />
<ClCompile Include="capture\CFrameBufferResource.cpp" />
<ClCompile Include="capture\CFrameProcessor.cpp" />
<ClCompile Include="capture\CFrameProcessorUtil.cpp" />
<ClCompile Include="capture\CFrameScheduler.cpp" />
<ClCompile Include="capture\CHardwareFrameProcessor.cpp" />
<ClCompile Include="capture\CSoftwareFrameProcessor.cpp" />
<ClCompile Include="capture\CSwapChainCursor.cpp" />
<ClCompile Include="capture\CSwapChainProcessor.cpp" />
<ClCompile Include="capture\CSwapChainPublisher.cpp" />
<ClCompile Include="d3d\CD3D11Device.cpp" />
<ClCompile Include="d3d\CD3D12CommandQueue.cpp" />
<ClCompile Include="d3d\CD3D12Device.cpp" />
<ClCompile Include="d3d\CInteropResource.cpp" />
<ClCompile Include="d3d\CInteropResourcePool.cpp" />
<ClCompile Include="postprocess\CPostProcessor.cpp" />
<ClCompile Include="postprocess\effect\CColorTransformEffect.cpp" />
<ClCompile Include="postprocess\effect\CComputeEffect.cpp" />
<ClCompile Include="postprocess\effect\CDownsampleEffect.cpp" />
<ClCompile Include="postprocess\effect\CHDR16to10Effect.cpp" />
<ClCompile Include="postprocess\effect\CRGB24Effect.cpp" />
<ClCompile Include="transport\CFrameTransport.cpp" />
<ClCompile Include="transport\CIVSHMEM.cpp" />
<ClCompile Include="transport\CLGMPControl.cpp" />
<ClCompile Include="transport\CLGMPHost.cpp" />
<ClCompile Include="transport\CPipeServer.cpp" />
<ClCompile Include="config\CSettings.cpp" />
<ClCompile Include="platform\CPlatformInfo.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
<ClInclude Include="CD3D12CommandQueue.h" />
<ClInclude Include="CEdid.h" />
<ClInclude Include="CFrameBufferPool.h" />
<ClInclude Include="CFrameBufferResource.h" />
<ClInclude Include="CFrameProcessor.h" />
<ClInclude Include="CFrameProcessorUtil.h" />
<ClInclude Include="CFrameScheduler.h" />
<ClInclude Include="CHardwareFrameProcessor.h" />
<ClInclude Include="CIndirectMonitorContext.h" />
<ClInclude Include="CInteropResourcePool.h" />
<ClInclude Include="CInteropResource.h" />
<ClInclude Include="CIVSHMEM.h" />
<ClInclude Include="CPipeServer.h" />
<ClInclude Include="CPlatformInfo.h" />
<ClInclude Include="CPostProcessor.h" />
<ClInclude Include="effect\CColorTransformEffect.h" />
<ClInclude Include="effect\CComputeEffect.h" />
<ClInclude Include="effect\CDownsampleEffect.h" />
<ClInclude Include="effect\CHDR16to10Effect.h" />
<ClInclude Include="effect\CRGB24Effect.h" />
<ClInclude Include="CSettings.h" />
<ClInclude Include="CSoftwareFrameProcessor.h" />
<ClInclude Include="CSwapChainProcessor.h" />
<ClInclude Include="CD3D12Device.h" />
<ClInclude Include="$(SolutionDir)LGCommon\*.h" />
<ClInclude Include="Device.h" />
<ClInclude Include="CD3D11Device.h" />
<ClInclude Include="Driver.h" />
<ClInclude Include="CIndirectDeviceContext.h" />
<ClInclude Include="Public.h" />
<ClInclude Include="Trace.h" />
<ClInclude Include="display\CDisplayConfiguration.h" />
<ClInclude Include="display\CEdid.h" />
<ClInclude Include="display\CMonitorManager.h" />
<ClInclude Include="display\IddCxCompat.h" />
<ClInclude Include="display\device\CDeviceContext.h" />
<ClInclude Include="display\monitor\Context.h" />
<ClInclude Include="capture\CFrameBufferPool.h" />
<ClInclude Include="capture\CFrameBufferResource.h" />
<ClInclude Include="capture\FrameBufferTypes.h" />
<ClInclude Include="capture\CFrameProcessor.h" />
<ClInclude Include="capture\CFrameProcessorUtil.h" />
<ClInclude Include="capture\CFrameScheduler.h" />
<ClInclude Include="capture\CHardwareFrameProcessor.h" />
<ClInclude Include="capture\CSoftwareFrameProcessor.h" />
<ClInclude Include="capture\CSwapChainProcessor.h" />
<ClInclude Include="d3d\CD3D11Device.h" />
<ClInclude Include="d3d\CD3D12CommandQueue.h" />
<ClInclude Include="d3d\CD3D12Device.h" />
<ClInclude Include="d3d\CInteropResource.h" />
<ClInclude Include="d3d\CInteropResourcePool.h" />
<ClInclude Include="postprocess\CPostProcessor.h" />
<ClInclude Include="postprocess\D12FrameFormat.h" />
<ClInclude Include="postprocess\effect\CColorTransformEffect.h" />
<ClInclude Include="postprocess\effect\CComputeEffect.h" />
<ClInclude Include="postprocess\effect\CDownsampleEffect.h" />
<ClInclude Include="postprocess\effect\CHDR16to10Effect.h" />
<ClInclude Include="postprocess\effect\CRGB24Effect.h" />
<ClInclude Include="transport\CFrameTransport.h" />
<ClInclude Include="transport\CIVSHMEM.h" />
<ClInclude Include="transport\CLGMPControl.h" />
<ClInclude Include="transport\CLGMPHost.h" />
<ClInclude Include="transport\CPipeServer.h" />
<ClInclude Include="transport\FrameMemoryLimits.h" />
<ClInclude Include="config\CSettings.h" />
<ClInclude Include="platform\CPlatformInfo.h" />
<ClInclude Include="util\CSRWLock.h" />
</ItemGroup>
<ItemGroup>
<Inf Include="LGIdd.inf" />
@@ -198,7 +215,7 @@
<WppRecorderEnabled>true</WppRecorderEnabled>
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
<AdditionalOptions>/EHsc /D_ATL_NO_WIN_SUPPORT /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=10 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\repos\LGMP\lgmp\include;$(ProjectDir)..\..\vendor;$(ProjectDir)..\..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(SolutionDir)LGCommon;$(ProjectDir)..\..\repos\LGMP\lgmp\include;$(ProjectDir)..\..\vendor;$(ProjectDir)..\..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;d3d12.lib;d3dcompiler.lib</AdditionalDependencies>
@@ -213,7 +230,7 @@
<WppRecorderEnabled>true</WppRecorderEnabled>
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
<AdditionalOptions>/EHsc /D_ATL_NO_WIN_SUPPORT /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=10 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\repos\LGMP\lgmp\include;$(ProjectDir)..\..\vendor;$(ProjectDir)..\..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(SolutionDir)LGCommon;$(ProjectDir)..\..\repos\LGMP\lgmp\include;$(ProjectDir)..\..\vendor;$(ProjectDir)..\..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;d3d12.lib;d3dcompiler.lib</AdditionalDependencies>
@@ -228,7 +245,7 @@
<WppRecorderEnabled>true</WppRecorderEnabled>
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
<AdditionalOptions>/EHsc /D_ATL_NO_WIN_SUPPORT /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=10 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;$(SolutionDir)..\repos\LGMP\lgmp\include;$(SolutionDir)..\vendor;$(SolutionDir)..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(SolutionDir)LGCommon;$(ProjectDir)..\..\repos\LGMP\lgmp\include;$(ProjectDir)..\..\vendor;$(ProjectDir)..\..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;d3d12.lib;d3dcompiler.lib</AdditionalDependencies>
@@ -243,7 +260,7 @@
<WppRecorderEnabled>true</WppRecorderEnabled>
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
<AdditionalOptions>/EHsc /D_ATL_NO_WIN_SUPPORT /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=10 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;$(SolutionDir)..\repos\LGMP\lgmp\include;$(SolutionDir)..\vendor;$(SolutionDir)..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir);$(SolutionDir)LGCommon;$(ProjectDir)..\..\repos\LGMP\lgmp\include;$(ProjectDir)..\..\vendor;$(ProjectDir)..\..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;d3d12.lib;d3dcompiler.lib</AdditionalDependencies>

View File

@@ -1,27 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<Filter Include="Driver">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
<Filter Include="Display">
<UniqueIdentifier>{D2C16E51-6087-4E08-8DA4-5AD1A60EF58E}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
<Filter Include="Display\Device">
<UniqueIdentifier>{83AAC4D0-9E4C-4B25-BD54-9186713B2010}</UniqueIdentifier>
</Filter>
<Filter Include="Driver Files">
<UniqueIdentifier>{8E41214B-6785-4CFE-B992-037D68949A14}</UniqueIdentifier>
<Extensions>inf;inv;inx;mof;mc;</Extensions>
<Filter Include="Display\Monitor">
<UniqueIdentifier>{3AF4227D-EBC0-45FB-9670-192E371313D5}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\effect">
<Filter Include="Capture">
<UniqueIdentifier>{202B79B2-5CCF-4AD4-B4C5-F5602419683D}</UniqueIdentifier>
</Filter>
<Filter Include="D3D">
<UniqueIdentifier>{4B3C58D3-5F40-49AB-894E-B83EB44E6CF5}</UniqueIdentifier>
</Filter>
<Filter Include="Post-processing">
<UniqueIdentifier>{A2E40D7C-854F-42E2-8730-A1BFD37A6BB0}</UniqueIdentifier>
</Filter>
<Filter Include="Post-processing\Effects">
<UniqueIdentifier>{52F1286F-0C64-41B9-92A9-4453C80B1001}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\effect">
<UniqueIdentifier>{DB623F3B-5D6A-4F8C-8884-83588A1B58D2}</UniqueIdentifier>
<Filter Include="Transport">
<UniqueIdentifier>{22FDF1F6-0A8C-4B96-9E74-53D74733CC0A}</UniqueIdentifier>
</Filter>
<Filter Include="Configuration">
<UniqueIdentifier>{82810F1C-E51C-4DBD-86B2-1CDE8C16A2A9}</UniqueIdentifier>
</Filter>
<Filter Include="Platform">
<UniqueIdentifier>{1C677205-7587-4037-9E36-92DFE600B985}</UniqueIdentifier>
</Filter>
<Filter Include="Common">
<UniqueIdentifier>{938E49D6-F954-4EBE-80AB-E0F67E677F27}</UniqueIdentifier>
</Filter>
<Filter Include="Utilities">
<UniqueIdentifier>{98768720-86D6-4A83-9EBD-2C8BFB51D793}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
@@ -30,187 +48,242 @@
</ItemGroup>
<ItemGroup>
<Inf Include="LGIdd.inf">
<Filter>Driver Files</Filter>
<Filter>Driver</Filter>
</Inf>
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(SolutionDir)LGCommon\*.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="Device.h">
<Filter>Header Files</Filter>
<Filter>Driver</Filter>
</ClInclude>
<ClInclude Include="Driver.h">
<Filter>Header Files</Filter>
<Filter>Driver</Filter>
</ClInclude>
<ClInclude Include="Public.h">
<Filter>Header Files</Filter>
<Filter>Driver</Filter>
</ClInclude>
<ClInclude Include="Trace.h">
<Filter>Header Files</Filter>
<Filter>Driver</Filter>
</ClInclude>
<ClInclude Include="CIndirectDeviceContext.h">
<Filter>Header Files</Filter>
<ClInclude Include="display\CDisplayConfiguration.h">
<Filter>Display</Filter>
</ClInclude>
<ClInclude Include="CIndirectMonitorContext.h">
<Filter>Header Files</Filter>
<ClInclude Include="display\CEdid.h">
<Filter>Display</Filter>
</ClInclude>
<ClInclude Include="CSwapChainProcessor.h">
<Filter>Header Files</Filter>
<ClInclude Include="display\CMonitorManager.h">
<Filter>Display</Filter>
</ClInclude>
<ClInclude Include="CIVSHMEM.h">
<Filter>Header Files</Filter>
<ClInclude Include="display\IddCxCompat.h">
<Filter>Display</Filter>
</ClInclude>
<ClInclude Include="CPlatformInfo.h">
<Filter>Header Files</Filter>
<ClInclude Include="display\device\CDeviceContext.h">
<Filter>Display\Device</Filter>
</ClInclude>
<ClInclude Include="CD3D12CommandQueue.h">
<Filter>Header Files</Filter>
<ClInclude Include="display\monitor\Context.h">
<Filter>Display\Monitor</Filter>
</ClInclude>
<ClInclude Include="CInteropResourcePool.h">
<Filter>Header Files</Filter>
<ClInclude Include="capture\CFrameBufferPool.h">
<Filter>Capture</Filter>
</ClInclude>
<ClInclude Include="CInteropResource.h">
<Filter>Header Files</Filter>
<ClInclude Include="capture\CFrameBufferResource.h">
<Filter>Capture</Filter>
</ClInclude>
<ClInclude Include="CD3D12Device.h">
<Filter>Header Files</Filter>
<ClInclude Include="capture\FrameBufferTypes.h">
<Filter>Capture</Filter>
</ClInclude>
<ClInclude Include="CD3D11Device.h">
<Filter>Header Files</Filter>
<ClInclude Include="capture\CFrameProcessor.h">
<Filter>Capture</Filter>
</ClInclude>
<ClInclude Include="CFrameBufferResource.h">
<Filter>Header Files</Filter>
<ClInclude Include="capture\CFrameProcessorUtil.h">
<Filter>Capture</Filter>
</ClInclude>
<ClInclude Include="CFrameBufferPool.h">
<Filter>Header Files</Filter>
<ClInclude Include="capture\CFrameScheduler.h">
<Filter>Capture</Filter>
</ClInclude>
<ClInclude Include="CFrameProcessor.h">
<Filter>Header Files</Filter>
<ClInclude Include="capture\CHardwareFrameProcessor.h">
<Filter>Capture</Filter>
</ClInclude>
<ClInclude Include="CFrameProcessorUtil.h">
<Filter>Header Files</Filter>
<ClInclude Include="capture\CSoftwareFrameProcessor.h">
<Filter>Capture</Filter>
</ClInclude>
<ClInclude Include="CHardwareFrameProcessor.h">
<Filter>Header Files</Filter>
<ClInclude Include="capture\CSwapChainProcessor.h">
<Filter>Capture</Filter>
</ClInclude>
<ClInclude Include="CFrameScheduler.h">
<Filter>Header Files</Filter>
<ClInclude Include="d3d\CD3D11Device.h">
<Filter>D3D</Filter>
</ClInclude>
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
<ClInclude Include="CPipeServer.h">
<Filter>Header Files</Filter>
<ClInclude Include="d3d\CD3D12CommandQueue.h">
<Filter>D3D</Filter>
</ClInclude>
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
<ClInclude Include="CSettings.h">
<Filter>Header Files</Filter>
<ClInclude Include="d3d\CD3D12Device.h">
<Filter>D3D</Filter>
</ClInclude>
<ClInclude Include="CPostProcessor.h">
<Filter>Header Files</Filter>
<ClInclude Include="d3d\CInteropResource.h">
<Filter>D3D</Filter>
</ClInclude>
<ClInclude Include="CSoftwareFrameProcessor.h">
<Filter>Header Files</Filter>
<ClInclude Include="d3d\CInteropResourcePool.h">
<Filter>D3D</Filter>
</ClInclude>
<ClInclude Include="effect\CColorTransformEffect.h">
<Filter>Header Files</Filter>
<ClInclude Include="postprocess\CPostProcessor.h">
<Filter>Post-processing</Filter>
</ClInclude>
<ClInclude Include="effect\CComputeEffect.h">
<Filter>Header Files\effect</Filter>
<ClInclude Include="postprocess\D12FrameFormat.h">
<Filter>Post-processing</Filter>
</ClInclude>
<ClInclude Include="effect\CDownsampleEffect.h">
<Filter>Header Files\effect</Filter>
<ClInclude Include="postprocess\effect\CColorTransformEffect.h">
<Filter>Post-processing\Effects</Filter>
</ClInclude>
<ClInclude Include="effect\CHDR16to10Effect.h">
<Filter>Header Files\effect</Filter>
<ClInclude Include="postprocess\effect\CComputeEffect.h">
<Filter>Post-processing\Effects</Filter>
</ClInclude>
<ClInclude Include="effect\CRGB24Effect.h">
<Filter>Header Files\effect</Filter>
<ClInclude Include="postprocess\effect\CDownsampleEffect.h">
<Filter>Post-processing\Effects</Filter>
</ClInclude>
<ClInclude Include="postprocess\effect\CHDR16to10Effect.h">
<Filter>Post-processing\Effects</Filter>
</ClInclude>
<ClInclude Include="postprocess\effect\CRGB24Effect.h">
<Filter>Post-processing\Effects</Filter>
</ClInclude>
<ClInclude Include="transport\CFrameTransport.h">
<Filter>Transport</Filter>
</ClInclude>
<ClInclude Include="transport\CIVSHMEM.h">
<Filter>Transport</Filter>
</ClInclude>
<ClInclude Include="transport\CLGMPControl.h">
<Filter>Transport</Filter>
</ClInclude>
<ClInclude Include="transport\CLGMPHost.h">
<Filter>Transport</Filter>
</ClInclude>
<ClInclude Include="transport\CPipeServer.h">
<Filter>Transport</Filter>
</ClInclude>
<ClInclude Include="transport\FrameMemoryLimits.h">
<Filter>Transport</Filter>
</ClInclude>
<ClInclude Include="config\CSettings.h">
<Filter>Configuration</Filter>
</ClInclude>
<ClInclude Include="platform\CPlatformInfo.h">
<Filter>Platform</Filter>
</ClInclude>
<ClInclude Include="util\CSRWLock.h">
<Filter>Utilities</Filter>
</ClInclude>
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Device.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="$(SolutionDir)LGCommon\*.cpp">
<Filter>Common</Filter>
</ClCompile>
<ClCompile Include="Driver.cpp">
<Filter>Source Files</Filter>
<Filter>Driver</Filter>
</ClCompile>
<ClCompile Include="CIndirectDeviceContext.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="Device.cpp">
<Filter>Driver</Filter>
</ClCompile>
<ClCompile Include="CFrameScheduler.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="display\CDisplayConfiguration.cpp">
<Filter>Display</Filter>
</ClCompile>
<ClCompile Include="CIndirectMonitorContext.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="display\CEdid.cpp">
<Filter>Display</Filter>
</ClCompile>
<ClCompile Include="CSwapChainProcessor.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="display\CMonitorManager.cpp">
<Filter>Display</Filter>
</ClCompile>
<ClCompile Include="CIVSHMEM.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="display\device\CDeviceContext.cpp">
<Filter>Display\Device</Filter>
</ClCompile>
<ClCompile Include="CPlatformInfo.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="display\monitor\Context.cpp">
<Filter>Display\Monitor</Filter>
</ClCompile>
<ClCompile Include="CD3D12CommandQueue.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="capture\CFrameBufferPool.cpp">
<Filter>Capture</Filter>
</ClCompile>
<ClCompile Include="CInteropResourcePool.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="capture\CFrameBufferResource.cpp">
<Filter>Capture</Filter>
</ClCompile>
<ClCompile Include="CInteropResource.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="capture\CFrameProcessor.cpp">
<Filter>Capture</Filter>
</ClCompile>
<ClCompile Include="CD3D12Device.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="capture\CFrameProcessorUtil.cpp">
<Filter>Capture</Filter>
</ClCompile>
<ClCompile Include="CD3D11Device.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="capture\CFrameScheduler.cpp">
<Filter>Capture</Filter>
</ClCompile>
<ClCompile Include="CFrameBufferResource.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="capture\CHardwareFrameProcessor.cpp">
<Filter>Capture</Filter>
</ClCompile>
<ClCompile Include="CFrameBufferPool.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="capture\CSoftwareFrameProcessor.cpp">
<Filter>Capture</Filter>
</ClCompile>
<ClCompile Include="CFrameProcessor.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="capture\CSwapChainCursor.cpp">
<Filter>Capture</Filter>
</ClCompile>
<ClCompile Include="CFrameProcessorUtil.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="capture\CSwapChainProcessor.cpp">
<Filter>Capture</Filter>
</ClCompile>
<ClCompile Include="CHardwareFrameProcessor.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="capture\CSwapChainPublisher.cpp">
<Filter>Capture</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
<ClCompile Include="CPipeServer.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="d3d\CD3D11Device.cpp">
<Filter>D3D</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
<ClCompile Include="CSettings.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="d3d\CD3D12CommandQueue.cpp">
<Filter>D3D</Filter>
</ClCompile>
<ClCompile Include="CPostProcessor.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="d3d\CD3D12Device.cpp">
<Filter>D3D</Filter>
</ClCompile>
<ClCompile Include="CSoftwareFrameProcessor.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="d3d\CInteropResource.cpp">
<Filter>D3D</Filter>
</ClCompile>
<ClCompile Include="effect\CColorTransformEffect.cpp">
<Filter>Source Files</Filter>
<ClCompile Include="d3d\CInteropResourcePool.cpp">
<Filter>D3D</Filter>
</ClCompile>
<ClCompile Include="effect\CComputeEffect.cpp">
<Filter>Source Files\effect</Filter>
<ClCompile Include="postprocess\CPostProcessor.cpp">
<Filter>Post-processing</Filter>
</ClCompile>
<ClCompile Include="effect\CDownsampleEffect.cpp">
<Filter>Source Files\effect</Filter>
<ClCompile Include="postprocess\effect\CColorTransformEffect.cpp">
<Filter>Post-processing\Effects</Filter>
</ClCompile>
<ClCompile Include="effect\CHDR16to10Effect.cpp">
<Filter>Source Files\effect</Filter>
<ClCompile Include="postprocess\effect\CComputeEffect.cpp">
<Filter>Post-processing\Effects</Filter>
</ClCompile>
<ClCompile Include="effect\CRGB24Effect.cpp">
<Filter>Source Files\effect</Filter>
<ClCompile Include="postprocess\effect\CDownsampleEffect.cpp">
<Filter>Post-processing\Effects</Filter>
</ClCompile>
<ClCompile Include="postprocess\effect\CHDR16to10Effect.cpp">
<Filter>Post-processing\Effects</Filter>
</ClCompile>
<ClCompile Include="postprocess\effect\CRGB24Effect.cpp">
<Filter>Post-processing\Effects</Filter>
</ClCompile>
<ClCompile Include="transport\CFrameTransport.cpp">
<Filter>Transport</Filter>
</ClCompile>
<ClCompile Include="transport\CIVSHMEM.cpp">
<Filter>Transport</Filter>
</ClCompile>
<ClCompile Include="transport\CLGMPControl.cpp">
<Filter>Transport</Filter>
</ClCompile>
<ClCompile Include="transport\CLGMPHost.cpp">
<Filter>Transport</Filter>
</ClCompile>
<ClCompile Include="transport\CPipeServer.cpp">
<Filter>Transport</Filter>
</ClCompile>
<ClCompile Include="config\CSettings.cpp">
<Filter>Configuration</Filter>
</ClCompile>
<ClCompile Include="platform\CPlatformInfo.cpp">
<Filter>Platform</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
</ItemGroup>
</Project>

View File

@@ -18,15 +18,15 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CFrameBufferPool.h"
#include "capture/CFrameBufferPool.h"
#include <stdint.h>
void CFrameBufferPool::Init(
CIndirectDeviceContext * device, CD3D12Device * dx12)
CFrameTransport * transport, CD3D12Device * dx12)
{
m_device = device;
m_dx12 = dx12;
m_transport = transport;
m_dx12 = dx12;
}
void CFrameBufferPool::Reset()
@@ -36,14 +36,14 @@ void CFrameBufferPool::Reset()
}
CFrameBufferResource * CFrameBufferPool::Get(
const CIndirectDeviceContext::PreparedFrameBuffer& buffer,
const PreparedFrameBuffer& buffer,
size_t minSize, const D3D12_RESOURCE_DESC * textureDesc)
{
if (buffer.frameIndex > ARRAYSIZE(m_buffers) - 1)
return nullptr;
CFrameBufferResource * fbr = &m_buffers[buffer.frameIndex];
if (!fbr->Init(m_device, m_dx12, buffer.frameIndex, buffer.mem,
if (!fbr->Init(m_transport, m_dx12, buffer.frameIndex, buffer.mem,
minSize, textureDesc))
return nullptr;

View File

@@ -20,25 +20,26 @@
#pragma once
#include "CFrameBufferResource.h"
#include "CIndirectDeviceContext.h"
#include "capture/CFrameBufferResource.h"
#include "capture/FrameBufferTypes.h"
#include "common/KVMFR.h"
struct CD3D12Device;
class CFrameTransport;
class CFrameBufferPool
{
CIndirectDeviceContext * m_device = nullptr;
CD3D12Device * m_dx12 = nullptr;
private:
CFrameTransport * m_transport = nullptr;
CD3D12Device * m_dx12 = nullptr;
CFrameBufferResource m_buffers[LGMP_Q_FRAME_BUFFER_LEN];
public:
void Init(CIndirectDeviceContext * device, CD3D12Device * dx12);
void Reset();
public:
void Init(CFrameTransport * transport, CD3D12Device * dx12);
void Reset();
CFrameBufferResource * Get(
const CIndirectDeviceContext::PreparedFrameBuffer& buffer,
size_t minSize,
const D3D12_RESOURCE_DESC * textureDesc = nullptr);
CFrameBufferResource * Get(const PreparedFrameBuffer& buffer,
size_t minSize,
const D3D12_RESOURCE_DESC * textureDesc = nullptr);
};

View File

@@ -18,21 +18,22 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CFrameBufferResource.h"
#include "CFrameProcessorUtil.h"
#include "CD3D12Device.h"
#include "CIndirectDeviceContext.h"
#include "capture/CFrameBufferResource.h"
#include "capture/CFrameProcessorUtil.h"
#include "d3d/CD3D12Device.h"
#include "transport/CFrameTransport.h"
#include "transport/CIVSHMEM.h"
#include "CDebug.h"
#include <cstring>
bool CFrameBufferResource::Init(CIndirectDeviceContext * device,
bool CFrameBufferResource::Init(CFrameTransport * transport,
CD3D12Device * dx12, unsigned frameIndex, uint8_t * base, size_t size,
const D3D12_RESOURCE_DESC * textureDesc)
{
m_frameIndex = frameIndex;
if (size > device->GetMaxFrameSize())
if (size > transport->GetMaxFrameSize())
{
DEBUG_ERROR("Frame size of %llu is too large to fit in shared ram",
(unsigned long long)size);
@@ -131,14 +132,14 @@ bool CFrameBufferResource::Init(CIndirectDeviceContext * device,
{
const UINT64 heapOffset =
(uintptr_t)base -
(uintptr_t)device->GetIVSHMEM().GetMem();
(uintptr_t)transport->GetIVSHMEM().GetMem();
const D3D12_RESOURCE_ALLOCATION_INFO allocation =
dx12->GetDevice()->GetResourceAllocationInfo(0, 1, &desc);
allocationSize = allocation.SizeInBytes;
const D3D12_HEAP_DESC heapDesc = dx12->GetHeap()->GetDesc();
if (!allocation.Alignment ||
heapOffset % allocation.Alignment ||
allocation.SizeInBytes > device->GetMaxFrameSize() ||
allocation.SizeInBytes > transport->GetMaxFrameSize() ||
heapOffset > heapDesc.SizeInBytes ||
allocation.SizeInBytes > heapDesc.SizeInBytes - heapOffset)
{

View File

@@ -27,11 +27,11 @@
#include <atomic>
#include <stdint.h>
#include "CFrameScheduler.h"
#include "CInteropResource.h"
#include "capture/CFrameScheduler.h"
#include "d3d/CInteropResource.h"
struct CD3D12Device;
class CIndirectDeviceContext;
class CFrameTransport;
using namespace Microsoft::WRL;
@@ -70,7 +70,7 @@ class CFrameBufferResource
void * m_map = nullptr;
public:
bool Init(CIndirectDeviceContext * device, CD3D12Device * dx12,
bool Init(CFrameTransport * transport, CD3D12Device * dx12,
unsigned frameIndex, uint8_t * base, size_t size,
const D3D12_RESOURCE_DESC * textureDesc = nullptr);
void Reset();

View File

@@ -18,27 +18,27 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CFrameProcessor.h"
#include "CFrameProcessorUtil.h"
#include "CHardwareFrameProcessor.h"
#include "CSoftwareFrameProcessor.h"
#include "capture/CFrameProcessor.h"
#include "capture/CFrameProcessorUtil.h"
#include "capture/CHardwareFrameProcessor.h"
#include "capture/CSoftwareFrameProcessor.h"
#include <cstring>
#include <new>
#include <utility>
CFrameProcessor::CFrameProcessor(CIndirectDeviceContext * device,
CFrameProcessor::CFrameProcessor(CFrameTransport * transport,
std::shared_ptr<CD3D12Device> dx12,
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
SRWLOCK * pipelineLock, HANDLE terminateEvent) :
m_device(device),
m_transport(transport),
m_dx12(std::move(dx12)),
m_postProcessors(postProcessors),
m_pipelineLock(pipelineLock),
m_terminateEvent(terminateEvent)
{
m_readyEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
m_frameBuffers.Init(m_device, m_dx12.get());
m_frameBuffers.Init(m_transport, m_dx12.get());
}
bool CFrameProcessor::IsValid() const
@@ -154,7 +154,7 @@ void CFrameProcessor::GetPreviousDamage(
}
std::unique_ptr<CFrameProcessor> CreateFrameProcessor(
bool software, CIndirectDeviceContext * device,
bool software, CFrameTransport * transport,
std::shared_ptr<CD3D12Device> dx12,
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
SRWLOCK * pipelineLock, HANDLE terminateEvent)
@@ -162,11 +162,11 @@ std::unique_ptr<CFrameProcessor> CreateFrameProcessor(
std::unique_ptr<CFrameProcessor> processor;
if (software)
processor.reset(new (std::nothrow) CSoftwareFrameProcessor(
device, std::move(dx12), postProcessors,
transport, std::move(dx12), postProcessors,
pipelineLock, terminateEvent));
else
processor.reset(new (std::nothrow) CHardwareFrameProcessor(
device, std::move(dx12), postProcessors,
transport, std::move(dx12), postProcessors,
pipelineLock, terminateEvent));
if (!processor || !processor->IsValid())

View File

@@ -20,17 +20,18 @@
#pragma once
#include "CD3D12Device.h"
#include "CFrameBufferPool.h"
#include "CIndirectDeviceContext.h"
#include "CInteropResource.h"
#include "CPostProcessor.h"
#include "d3d/CD3D12Device.h"
#include "capture/CFrameBufferPool.h"
#include "d3d/CInteropResource.h"
#include "postprocess/CPostProcessor.h"
#include <Windows.h>
#include <memory>
using namespace Microsoft::WRL;
class CFrameTransport;
struct FrameSubmission
{
CInteropResource * source;
@@ -45,13 +46,13 @@ struct FrameSubmission
class CFrameProcessor
{
protected:
CIndirectDeviceContext * m_device;
std::shared_ptr<CD3D12Device> m_dx12;
CPostProcessor * m_postProcessors;
SRWLOCK * m_pipelineLock;
HANDLE m_terminateEvent;
CFrameBufferPool m_frameBuffers;
Wrappers::Event m_readyEvent;
CFrameTransport * m_transport;
std::shared_ptr<CD3D12Device> m_dx12;
CPostProcessor * m_postProcessors;
SRWLOCK * m_pipelineLock;
HANDLE m_terminateEvent;
CFrameBufferPool m_frameBuffers;
Wrappers::Event m_readyEvent;
mutable SRWLOCK m_damageLock = SRWLOCK_INIT;
RECT m_previousDamage[LG_MAX_DIRTY_RECTS] = {};
@@ -71,7 +72,7 @@ protected:
virtual void SetFullDamageLocked();
public:
CFrameProcessor(CIndirectDeviceContext * device,
CFrameProcessor(CFrameTransport * transport,
std::shared_ptr<CD3D12Device> dx12,
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
SRWLOCK * pipelineLock, HANDLE terminateEvent);
@@ -93,7 +94,7 @@ public:
};
std::unique_ptr<CFrameProcessor> CreateFrameProcessor(
bool software, CIndirectDeviceContext * device,
bool software, CFrameTransport * transport,
std::shared_ptr<CD3D12Device> dx12,
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
SRWLOCK * pipelineLock, HANDLE terminateEvent);

View File

@@ -18,7 +18,9 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CFrameProcessorUtil.h"
#include "capture/CFrameProcessorUtil.h"
#include "d3d/CInteropResource.h"
#include "postprocess/CPostProcessor.h"
#include <cstring>

View File

@@ -20,25 +20,9 @@
#pragma once
#include "CInteropResource.h"
#include "CPostProcessor.h"
#include "postprocess/D12FrameFormat.h"
class CFrameProcessorSharedLock
{
private:
SRWLOCK * m_lock;
public:
explicit CFrameProcessorSharedLock(SRWLOCK * lock) : m_lock(lock)
{
AcquireSRWLockShared(m_lock);
}
~CFrameProcessorSharedLock()
{
ReleaseSRWLockShared(m_lock);
}
};
class CPostProcessor;
class CFrameProcessorUtil
{

View File

@@ -18,7 +18,7 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CFrameScheduler.h"
#include "capture/CFrameScheduler.h"
#include "CDebug.h"

View File

@@ -18,8 +18,10 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CHardwareFrameProcessor.h"
#include "CFrameProcessorUtil.h"
#include "capture/CHardwareFrameProcessor.h"
#include "capture/CFrameProcessorUtil.h"
#include "transport/CFrameTransport.h"
#include "util/CSRWLock.h"
#include "CDebug.h"
#include <cstring>
@@ -69,10 +71,10 @@ public:
};
CHardwareFrameProcessor::CHardwareFrameProcessor(
CIndirectDeviceContext * device, std::shared_ptr<CD3D12Device> dx12,
CFrameTransport * transport, std::shared_ptr<CD3D12Device> dx12,
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
SRWLOCK * pipelineLock, HANDLE terminateEvent) :
CFrameProcessor(device, std::move(dx12), postProcessors,
CFrameProcessor(transport, std::move(dx12), postProcessors,
pipelineLock, terminateEvent)
{
m_candidateAvailableEvent.Attach(
@@ -203,7 +205,7 @@ int CHardwareFrameProcessor::AcquireCandidate(
ReleaseSRWLockExclusive(&m_candidateLock);
if (superseded)
m_device->FrameSuperseded();
m_transport->FrameSuperseded();
return selected;
}
@@ -327,10 +329,10 @@ void CHardwareFrameProcessor::CandidateCompletionFunction(
if (!result)
{
processor->SetFullDamage();
processor->m_device->ForceFrame();
processor->m_transport->ForceFrame();
}
else if (forceFrame)
processor->m_device->ForceFrame();
processor->m_transport->ForceFrame();
processor->SignalCandidateState();
}
@@ -343,9 +345,9 @@ void CHardwareFrameProcessor::CompletionFunction(
if (!result)
{
processor->m_device->FailFrameBuffer(fbRes->GetFrameIndex());
processor->m_transport->FailFrameBuffer(fbRes->GetFrameIndex());
processor->SetFullDamage();
processor->m_device->ForceFrame();
processor->m_transport->ForceFrame();
processor->ReleaseCandidate(candidateIndex);
return;
}
@@ -374,7 +376,7 @@ void CHardwareFrameProcessor::CompletionFunction(
if (processor->m_dx12->IsIndirectCopy())
{
const uint64_t indirectCopyStart = CFrameScheduler::Nanotime();
processor->m_device->WriteFrameBuffer(
processor->m_transport->WriteFrameBuffer(
fbRes->GetFrameIndex(), fbRes->GetMap(), 0,
fbRes->GetFrameSize(), false);
indirectCopyTime = CFrameScheduler::Nanotime() - indirectCopyStart;
@@ -401,7 +403,7 @@ void CHardwareFrameProcessor::CompletionFunction(
const uint64_t copyTime = prepareCopyTime + publishCopyTime;
processor->m_device->FinalizeFrameBuffer(fbRes->GetFrameIndex());
processor->m_transport->FinalizeFrameBuffer(fbRes->GetFrameIndex());
const uint64_t publishedAt = CFrameScheduler::Nanotime();
const uint64_t prepareElapsed = prepareReady >= postProcessStart ?
prepareReady - postProcessStart : 0;
@@ -416,10 +418,10 @@ void CHardwareFrameProcessor::CompletionFunction(
const uint64_t holdTime = publishStart >= prepareReady ?
publishStart - prepareReady : 0;
processor->m_device->SetFrameTiming(fbRes->GetFrameIndex(),
processor->m_transport->SetFrameTiming(fbRes->GetFrameIndex(),
fbRes->GetCaptureTime(), postProcessTime, copyTime, readyTime, holdTime,
fbRes->GetSchedule(), publishedAt);
processor->m_device->TryRecordFrameTiming(publishedAt - publishStart);
processor->m_transport->TryRecordFrameTiming(publishedAt - publishStart);
const uint64_t timingToken = fbRes->GetTimingToken();
if (timingToken && timingStart && prepareReady >= timingStart &&
@@ -432,7 +434,7 @@ void CHardwareFrameProcessor::CompletionFunction(
fbRes->IsFullCopy(), totalTime);
}
processor->m_device->CompleteFrameBuffer(fbRes->GetFrameIndex(), true);
processor->m_transport->CompleteFrameBuffer(fbRes->GetFrameIndex(), true);
processor->ReleaseCandidate(candidateIndex);
}
@@ -442,7 +444,7 @@ bool CHardwareFrameProcessor::Publish(
{
CPublishPending publishPending(
&m_copySubmitLock, &m_publishPending, m_copySubmitEvent.Get());
CFrameProcessorSharedLock pipelineLock(m_pipelineLock);
CSRWSharedLock pipelineLock(m_pipelineLock);
int selectedCandidate = -1;
uint64_t newestSequence = 0;
@@ -491,7 +493,7 @@ bool CHardwareFrameProcessor::Publish(
CPostProcessor& postProcessor = m_postProcessors[candidateIndex];
const uint64_t candidateSequence = candidate.sequence;
auto buffer = m_device->PrepareFrameBuffer(
auto buffer = m_transport->PrepareFrameBuffer(
candidate.pitch, candidate.srcFormat, candidate.dstFormat,
candidate.dirtyRects, candidate.nbDirtyRects, schedule);
if (!buffer.mem)
@@ -504,7 +506,7 @@ bool CHardwareFrameProcessor::Publish(
m_frameBuffers.Get(buffer, candidate.frameSize);
if (!fbRes)
{
m_device->AbortFrameBuffer(buffer.frameIndex);
m_transport->AbortFrameBuffer(buffer.frameIndex);
restoreCandidate();
DEBUG_ERROR("Failed to get a CFrameBufferResource from the pool");
SetFullDamage();
@@ -514,7 +516,7 @@ bool CHardwareFrameProcessor::Publish(
CD3D12CommandSlot * copySlot = m_dx12->GetCopySlot(candidateIndex);
if (!copySlot)
{
m_device->AbortFrameBuffer(buffer.frameIndex);
m_transport->AbortFrameBuffer(buffer.frameIndex);
restoreCandidate();
DEBUG_ERROR("Failed to get a copy CommandSlot for publication");
SetFullDamage();
@@ -548,17 +550,17 @@ bool CHardwareFrameProcessor::Publish(
copySlot->EndTiming();
bool deliveredToOwner;
if (!m_device->PublishFrameBuffer(
if (!m_transport->PublishFrameBuffer(
buffer.frameIndex, schedule, deliveredToOwner))
{
copySlot->Cancel();
m_device->AbortFrameBuffer(buffer.frameIndex);
m_transport->AbortFrameBuffer(buffer.frameIndex);
restoreCandidate();
return false;
}
CFrameScheduler::Schedule frameSchedule = schedule;
if (!deliveredToOwner ||
!m_device->TryFrameSubmitted(buffer.frameIndex, schedule))
!m_transport->TryFrameSubmitted(buffer.frameIndex, schedule))
frameSchedule.phaseEligible = false;
fbRes->SetSchedule(frameSchedule);
@@ -591,15 +593,15 @@ bool CHardwareFrameProcessor::Publish(
ReleaseSRWLockShared(&m_candidateLock);
if (callbackPending && !copySlot->HasSubmittedWork())
{
m_device->FailFrameBuffer(buffer.frameIndex);
m_transport->FailFrameBuffer(buffer.frameIndex);
ReleaseCandidate(candidateIndex);
}
m_device->ForceFrame();
m_transport->ForceFrame();
SignalCandidateState();
return false;
}
m_device->CommitFrameBuffer(
m_transport->CommitFrameBuffer(
buffer.frameIndex, schedule, periodic, deliveredToOwner);
unsigned superseded = 0;
@@ -613,7 +615,7 @@ bool CHardwareFrameProcessor::Publish(
}
ReleaseSRWLockExclusive(&m_candidateLock);
for (unsigned i = 0; i < superseded; ++i)
m_device->FrameSuperseded();
m_transport->FrameSuperseded();
SignalCandidateState();
return true;
}
@@ -645,14 +647,14 @@ bool CHardwareFrameProcessor::Submit(const FrameSubmission& submission)
}
if (selectedCandidate < 0)
{
m_device->FrameSuperseded();
m_transport->FrameSuperseded();
return true;
}
const unsigned candidateIndex =
static_cast<unsigned>(selectedCandidate);
FrameCandidate& candidate = m_candidates[candidateIndex];
CFrameProcessorSharedLock pipelineLock(m_pipelineLock);
CSRWSharedLock pipelineLock(m_pipelineLock);
CPostProcessor& postProcessor = m_postProcessors[candidateIndex];
const D12FrameFormat& dstFormat = postProcessor.GetOutputFormat();
@@ -813,7 +815,7 @@ bool CHardwareFrameProcessor::Submit(const FrameSubmission& submission)
ReleaseCandidate(candidateIndex);
}
SetFullDamage();
m_device->ForceFrame();
m_transport->ForceFrame();
return false;
}

View File

@@ -20,7 +20,7 @@
#pragma once
#include "CFrameProcessor.h"
#include "capture/CFrameProcessor.h"
class CHardwareFrameProcessor final : public CFrameProcessor
{
@@ -89,7 +89,7 @@ private:
void SetFullDamageLocked() override;
public:
CHardwareFrameProcessor(CIndirectDeviceContext * device,
CHardwareFrameProcessor(CFrameTransport * transport,
std::shared_ptr<CD3D12Device> dx12,
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
SRWLOCK * pipelineLock, HANDLE terminateEvent);

View File

@@ -18,17 +18,20 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CSoftwareFrameProcessor.h"
#include "CFrameProcessorUtil.h"
#include "capture/CSoftwareFrameProcessor.h"
#include "capture/CFrameProcessorUtil.h"
#include "capture/FrameBufferTypes.h"
#include "transport/CFrameTransport.h"
#include "util/CSRWLock.h"
#include "CDebug.h"
#include <utility>
CSoftwareFrameProcessor::CSoftwareFrameProcessor(
CIndirectDeviceContext * device, std::shared_ptr<CD3D12Device> dx12,
CFrameTransport * transport, std::shared_ptr<CD3D12Device> dx12,
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
SRWLOCK * pipelineLock, HANDLE terminateEvent) :
CFrameProcessor(device, std::move(dx12), postProcessors,
CFrameProcessor(transport, std::move(dx12), postProcessors,
pipelineLock, terminateEvent),
m_directTexture(
!m_dx12->IsIndirectCopy() && m_dx12->CanUseIVSHMEMTexture())
@@ -44,9 +47,9 @@ void CSoftwareFrameProcessor::CompletionFunction(
if (!result)
{
processor->m_device->FailFrameBuffer(fbRes->GetFrameIndex());
processor->m_transport->FailFrameBuffer(fbRes->GetFrameIndex());
processor->SetFullDamage();
processor->m_device->ForceFrame();
processor->m_transport->ForceFrame();
return;
}
@@ -55,7 +58,7 @@ void CSoftwareFrameProcessor::CompletionFunction(
{
const uint64_t indirectCopyStart = CFrameScheduler::Nanotime();
if (fbRes->IsFullCopy())
processor->m_device->WriteFrameBuffer(fbRes->GetFrameIndex(),
processor->m_transport->WriteFrameBuffer(fbRes->GetFrameIndex(),
fbRes->GetMap(), 0, fbRes->GetFrameSize(), false);
else
{
@@ -70,7 +73,7 @@ void CSoftwareFrameProcessor::CompletionFunction(
(size_t)rect->left * bytesPerPixel;
const size_t rowBytes =
(size_t)(rect->right - rect->left) * bytesPerPixel;
processor->m_device->WriteFrameBufferRows(fbRes->GetFrameIndex(),
processor->m_transport->WriteFrameBufferRows(fbRes->GetFrameIndex(),
fbRes->GetMap(), rowOffset, rowBytes, pitch,
(unsigned)(rect->bottom - rect->top));
}
@@ -83,7 +86,7 @@ void CSoftwareFrameProcessor::CompletionFunction(
const uint64_t copyReady = CFrameScheduler::Nanotime();
const bool gpuTimingValid = slot->GetGPUTimes(gpuStart, gpuEnd);
processor->m_device->FinalizeFrameBuffer(fbRes->GetFrameIndex());
processor->m_transport->FinalizeFrameBuffer(fbRes->GetFrameIndex());
const uint64_t publishedAt = CFrameScheduler::Nanotime();
const uint64_t postProcessStart = fbRes->GetPostProcessStart();
const uint64_t copyStart = fbRes->GetCopyStart();
@@ -103,15 +106,15 @@ void CSoftwareFrameProcessor::CompletionFunction(
const uint64_t measured = postProcessTime + copyTime;
const uint64_t readyTime = elapsed > measured ? elapsed - measured : 0;
processor->m_device->SetFrameTiming(fbRes->GetFrameIndex(),
processor->m_transport->SetFrameTiming(fbRes->GetFrameIndex(),
fbRes->GetCaptureTime(), postProcessTime, copyTime, readyTime, 0,
fbRes->GetSchedule(), publishedAt);
processor->m_device->CompleteFrameBuffer(fbRes->GetFrameIndex(), true);
processor->m_transport->CompleteFrameBuffer(fbRes->GetFrameIndex(), true);
}
bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
{
CFrameProcessorSharedLock pipelineLock(m_pipelineLock);
CSRWSharedLock pipelineLock(m_pipelineLock);
CPostProcessor& postProcessor = m_postProcessors[0];
const D12FrameFormat& dstFormat = postProcessor.GetOutputFormat();
@@ -140,7 +143,7 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
&textureDesc, 0, 1, 0, &layout, nullptr, nullptr, nullptr);
const unsigned texturePitch = layout.Footprint.RowPitch;
if (texturePitch && textureDesc.Height <=
m_device->GetMaxFrameSize() / texturePitch)
m_transport->GetMaxFrameSize() / texturePitch)
{
pitch = texturePitch;
frameSize = (size_t)pitch * textureDesc.Height;
@@ -158,7 +161,7 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
DEBUG_WARN("Post-processor output cannot use an IVSHMEM texture");
}
if (!pitch || !frameSize || frameSize > m_device->GetMaxFrameSize())
if (!pitch || !frameSize || frameSize > m_transport->GetMaxFrameSize())
{
DEBUG_ERROR("Software frame does not fit in shared memory");
SetFullDamage();
@@ -170,10 +173,10 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
for (;;)
{
CFrameScheduler::Schedule commitSchedule = {};
CFrameScheduler::Schedule deliverySchedule = {};
CIndirectDeviceContext::PreparedFrameBuffer buffer = {};
CD3D12CommandSlot * copySlot = nullptr;
CFrameScheduler::Schedule commitSchedule = {};
CFrameScheduler::Schedule deliverySchedule = {};
PreparedFrameBuffer buffer = {};
CD3D12CommandSlot * copySlot = nullptr;
RECT currentDirtyRects[LG_MAX_DIRTY_RECTS] = {};
unsigned nbDirtyRects = 0;
bool hasDamage = false;
@@ -181,19 +184,19 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
uint64_t ignoredTarget = 0;
bool ignoredPeriodic = false;
bool ignoredRepublish = false;
m_device->GetPublishTarget(CFrameScheduler::Nanotime(),
m_transport->GetPublishTarget(CFrameScheduler::Nanotime(),
ignoredTarget, commitSchedule, ignoredPeriodic, ignoredRepublish);
deliverySchedule = commitSchedule;
deliverySchedule.deliveryDeadlineSerial = 0;
deliverySchedule.phaseEligible = false;
m_device->ProcessFrameQueue();
if (!m_device->FrameBufferAvailable(
m_transport->ProcessFrameQueue();
if (!m_transport->FrameBufferAvailable(
deliverySchedule, submission.noImageUpdate))
{
if (!submission.noImageUpdate)
{
m_device->FrameSuperseded();
m_transport->FrameSuperseded();
return true;
}
@@ -207,7 +210,7 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
{
if (!submission.noImageUpdate)
{
m_device->FrameSuperseded();
m_transport->FrameSuperseded();
return true;
}
@@ -220,7 +223,7 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
CFrameProcessorUtil::ClipDirtyRects(
currentDirtyRects, &nbDirtyRects,
dstFormat.width, dstFormat.height);
buffer = m_device->PrepareFrameBuffer(
buffer = m_transport->PrepareFrameBuffer(
pitch, submission.sourceFormat, dstFormat,
currentDirtyRects, nbDirtyRects, deliverySchedule,
submission.noImageUpdate);
@@ -231,7 +234,7 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
currentDirtyRects, nbDirtyRects, hasDamage);
if (!submission.noImageUpdate)
{
m_device->FrameSuperseded();
m_transport->FrameSuperseded();
return true;
}
@@ -251,7 +254,7 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
if (FAILED(deviceStatus))
{
copySlot->Cancel();
m_device->AbortFrameBuffer(buffer.frameIndex);
m_transport->AbortFrameBuffer(buffer.frameIndex);
RestorePendingDamage(
currentDirtyRects, nbDirtyRects, hasDamage);
DEBUG_ERROR_HR(deviceStatus,
@@ -272,7 +275,7 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
if (!fbRes)
{
copySlot->Cancel();
m_device->AbortFrameBuffer(buffer.frameIndex);
m_transport->AbortFrameBuffer(buffer.frameIndex);
RestorePendingDamage(
currentDirtyRects, nbDirtyRects, hasDamage);
DEBUG_ERROR("Failed to get a framebuffer for software capture");
@@ -284,7 +287,7 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
!submission.source->Sync(*copySlot))
{
copySlot->Cancel();
m_device->AbortFrameBuffer(buffer.frameIndex);
m_transport->AbortFrameBuffer(buffer.frameIndex);
RestorePendingDamage(
currentDirtyRects, nbDirtyRects, hasDamage);
SetFullDamage();
@@ -322,16 +325,16 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
copySlot->EndTiming();
bool deliveredToOwner;
if (!m_device->PublishFrameBuffer(
if (!m_transport->PublishFrameBuffer(
buffer.frameIndex, deliverySchedule, deliveredToOwner))
{
copySlot->Cancel();
m_device->AbortFrameBuffer(buffer.frameIndex);
m_transport->AbortFrameBuffer(buffer.frameIndex);
RestorePendingDamage(
currentDirtyRects, nbDirtyRects, hasDamage);
if (!submission.noImageUpdate)
{
m_device->FrameSuperseded();
m_transport->FrameSuperseded();
return true;
}
@@ -346,18 +349,18 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission)
const bool submittedWork = copySlot->HasSubmittedWork();
const bool completionHandled = fbRes->CompletionHandled();
if (!submittedWork && !completionHandled)
m_device->FailFrameBuffer(buffer.frameIndex);
m_transport->FailFrameBuffer(buffer.frameIndex);
RestorePendingDamage(
currentDirtyRects, nbDirtyRects, hasDamage);
if (!submittedWork && !completionHandled)
{
SetFullDamage();
m_device->ForceFrame();
m_transport->ForceFrame();
}
return false;
}
m_device->CommitFrameBuffer(
m_transport->CommitFrameBuffer(
buffer.frameIndex, commitSchedule, false, deliveredToOwner);
return true;
}

View File

@@ -20,7 +20,7 @@
#pragma once
#include "CFrameProcessor.h"
#include "capture/CFrameProcessor.h"
class CSoftwareFrameProcessor final : public CFrameProcessor
{
@@ -31,7 +31,7 @@ private:
CD3D12CommandSlot * slot, bool result, void * param1, void * param2);
public:
CSoftwareFrameProcessor(CIndirectDeviceContext * device,
CSoftwareFrameProcessor(CFrameTransport * transport,
std::shared_ptr<CD3D12Device> dx12,
CPostProcessor postProcessors[LGMP_Q_FRAME_LEN],
SRWLOCK * pipelineLock, HANDLE terminateEvent);

View File

@@ -0,0 +1,122 @@
/**
* 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 "capture/CSwapChainProcessor.h"
#include "display/IddCxCompat.h"
#include "display/device/CDeviceContext.h"
#include "transport/CLGMPControl.h"
#include "CDebug.h"
DWORD CALLBACK CSwapChainProcessor::_CursorThread(LPVOID arg)
{
reinterpret_cast<CSwapChainProcessor*>(arg)->CursorThread();
return 0;
}
bool CSwapChainProcessor::QueryHWCursor()
{
IDARG_IN_QUERY_HWCURSOR in = {};
in.LastShapeId = m_lastShapeId;
in.pShapeBuffer = m_shapeBuffer;
in.ShapeBufferSizeInBytes = 512 * 512 * 4;
IDARG_OUT_QUERY_HWCURSOR out = {};
UINT cursorWhiteLevel = m_sdrWhiteLevel.load(std::memory_order_relaxed);
NTSTATUS status;
#ifdef HAS_IDDCX_110
if (m_devContext->HasIddCx110DDIs())
{
IDARG_OUT_QUERY_HWCURSOR3 out3 = {};
status = IddCxMonitorQueryHardwareCursor3(m_monitor, &in, &out3);
out.IsCursorVisible = out3.IsCursorVisible;
out.X = out3.X;
out.Y = out3.Y;
out.IsCursorShapeUpdated = out3.IsCursorShapeUpdated;
out.CursorShapeInfo = out3.CursorShapeInfo;
if (out3.SdrWhiteLevel)
cursorWhiteLevel = out3.SdrWhiteLevel;
}
else
#endif
{
status = IddCxMonitorQueryHardwareCursor(m_monitor, &in, &out);
}
if (FAILED(status))
{
// this occurs if the display went away (ie, screen blanking or disabled)
if (status == STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY)
{
SetEvent(m_terminateEvent.Get());
return false;
}
DEBUG_ERROR("IddCxMonitorQueryHardwareCursor failed (0x%08x)", status);
return false;
}
if (out.IsCursorShapeUpdated)
m_lastShapeId = out.CursorShapeInfo.ShapeId;
m_control.SendCursor(out, m_shapeBuffer, cursorWhiteLevel);
return true;
}
void CSwapChainProcessor::CursorThread()
{
HRESULT hr = 0;
bool running = true;
while (running)
{
HANDLE waitHandles[] =
{
m_cursorDataEvent.Get(),
m_terminateEvent.Get()
};
DWORD waitResult = WaitForMultipleObjects(
ARRAYSIZE(waitHandles), waitHandles, FALSE, 100);
switch (waitResult)
{
case WAIT_TIMEOUT:
continue;
// cursorDataEvent
case WAIT_OBJECT_0:
if (!QueryHWCursor())
return;
continue;
// terminateEvent
case WAIT_OBJECT_0 + 1:
running = false;
continue;
default:
hr = HRESULT_FROM_WIN32(waitResult);
DEBUG_ERROR_HR(hr, "WaitForMultipleObjects");
return;
}
}
}

View File

@@ -18,15 +18,20 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CSwapChainProcessor.h"
#include "CFrameProcessorUtil.h"
#include "CIndirectMonitorContext.h"
#include "CPlatformInfo.h"
#include "capture/CSwapChainProcessor.h"
#include "capture/CFrameProcessorUtil.h"
#include "display/IddCxCompat.h"
#include "display/device/CDeviceContext.h"
#include "display/monitor/Context.h"
#include "platform/CPlatformInfo.h"
#include "transport/CFrameTransport.h"
#include "transport/CLGMPControl.h"
#include "util/CSRWLock.h"
#include <avrt.h>
#include <new>
#include "CDebug.h"
#include "CPipeServer.h"
#include "transport/CPipeServer.h"
#ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x00000002
@@ -34,34 +39,18 @@
static const uint32_t HDR_PQ_MIN_LUMINANCE = 50;
static const uint32_t HDR_PQ_MAX_LUMINANCE = 10000;
static const uint64_t PUBLISH_RETRY_NS = 1000000ULL;
class CSRWExclusiveLock
{
private:
SRWLOCK * m_lock;
public:
explicit CSRWExclusiveLock(SRWLOCK * lock) : m_lock(lock)
{
AcquireSRWLockExclusive(m_lock);
}
~CSRWExclusiveLock()
{
ReleaseSRWLockExclusive(m_lock);
}
};
CSwapChainProcessor::CSwapChainProcessor(CIndirectMonitorContext * monitorContext,
CSwapChainProcessor::CSwapChainProcessor(CMonitorContext * monitorContext,
UINT64 assignmentGeneration, IDDCX_MONITOR monitor,
CIndirectDeviceContext * devContext, IDDCX_SWAPCHAIN hSwapChain,
CDeviceContext * devContext, IDDCX_SWAPCHAIN hSwapChain,
LUID renderAdapter, std::shared_ptr<CD3D11Device> dx11Device,
HANDLE newFrameEvent) :
m_monitorContext(monitorContext),
m_assignmentGeneration(assignmentGeneration),
m_monitor(monitor),
m_devContext(devContext),
m_transport(devContext->GetFrameTransport()),
m_control(devContext->GetLGMPControl()),
m_hSwapChain(hSwapChain),
m_renderAdapter(renderAdapter),
m_dx11Device(dx11Device),
@@ -110,7 +99,7 @@ bool CSwapChainProcessor::InitializePipeline()
UINT64 alignSize = CPlatformInfo::GetPageSize();
auto dx12Device = std::make_shared<CD3D12Device>(m_renderAdapter);
const CD3D12Device::InitResult result = dx12Device->Init(
m_devContext->GetIVSHMEM(), alignSize, !m_dx11Device->IsSoftware());
m_transport.GetIVSHMEM(), alignSize, !m_dx11Device->IsSoftware());
if (result == CD3D12Device::RETRY)
{
const HRESULT deviceStatus =
@@ -175,7 +164,7 @@ bool CSwapChainProcessor::InitializePipeline()
}
m_frameProcessor = CreateFrameProcessor(m_dx11Device->IsSoftware(),
m_devContext, m_dx12Device, m_postProcessors,
&m_transport, m_dx12Device, m_postProcessors,
&m_pipelineLock, m_terminateEvent.Get());
if (!m_frameProcessor)
{
@@ -230,246 +219,6 @@ DWORD CALLBACK CSwapChainProcessor::_SwapChainThread(LPVOID arg)
return 0;
}
static bool ArmPublishTimer(HANDLE timer, uint64_t delay)
{
if (!timer)
return false;
LARGE_INTEGER due = {};
due.QuadPart = -static_cast<LONGLONG>((delay + 99) / 100);
if (!due.QuadPart)
due.QuadPart = -1;
return SetWaitableTimer(timer, &due, 0, nullptr, nullptr, FALSE) != FALSE;
}
DWORD CALLBACK CSwapChainProcessor::_PublisherThread(LPVOID arg)
{
reinterpret_cast<CSwapChainProcessor *>(arg)->PublisherThread();
return 0;
}
void CSwapChainProcessor::PublisherThread()
{
DWORD avTask = 0;
HANDLE avTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &avTask);
if (avTaskHandle &&
!AvSetMmThreadPriority(avTaskHandle, AVRT_PRIORITY_HIGH))
DEBUG_WARN("Failed to raise publisher MMCSS priority: %lu",
GetLastError());
const HANDLE scheduleEvent = m_devContext->GetFrameScheduleEvent();
HANDLE idleHandles[] =
{
m_terminateEvent.Get(),
m_frameProcessor->GetReadyEvent(),
scheduleEvent,
};
HANDLE timerHandles[] =
{
m_terminateEvent.Get(),
m_frameProcessor->GetReadyEvent(),
scheduleEvent,
m_publishTimer.Get(),
};
const bool cadenceEnabled = m_frameProcessor->UsesCadence();
for (;;)
{
const uint64_t now = CFrameScheduler::Nanotime();
uint64_t target;
CFrameScheduler::Schedule schedule;
bool periodic;
bool republish;
m_devContext->GetPublishTarget(
now, target, schedule, periodic, republish);
const bool ready = m_frameProcessor->HasReadyFrame();
if (!ready)
{
m_devContext->ProcessFrameQueue();
if (m_frameProcessor->HasReadyFrame())
continue;
uint64_t current = CFrameScheduler::Nanotime();
uint64_t cadenceTarget = 0;
if (cadenceEnabled && schedule.deliveryDeadlineSerial && periodic)
{
if (schedule.deadline <= current)
{
m_devContext->FrameMissed(schedule, current, periodic);
continue;
}
cadenceTarget = schedule.deadline;
}
if (republish && m_devContext->HasPublishedFrame())
{
if (m_devContext->RepublishFrameBuffer(schedule))
continue;
current = CFrameScheduler::Nanotime();
if (cadenceTarget && cadenceTarget <= current)
{
m_devContext->FrameMissed(schedule, current, periodic);
continue;
}
uint64_t retryTarget = current + PUBLISH_RETRY_NS;
if (cadenceTarget)
retryTarget = min(retryTarget, cadenceTarget);
ArmPublishTimer(m_publishTimer.Get(), retryTarget - current);
if (WaitForMultipleObjects(
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
uint64_t replayTarget;
if (m_devContext->GetSharedFrameTarget(current, replayTarget))
{
bool retry = false;
if (replayTarget <= current)
{
if (m_devContext->ReplaySharedFrame(current, retry))
continue;
current = CFrameScheduler::Nanotime();
if (cadenceTarget && cadenceTarget <= current)
{
m_devContext->FrameMissed(schedule, current, periodic);
continue;
}
if (retry)
replayTarget = current + PUBLISH_RETRY_NS;
else
{
if (cadenceTarget)
replayTarget = cadenceTarget;
else
{
if (m_publishTimer.Get())
CancelWaitableTimer(m_publishTimer.Get());
if (WaitForMultipleObjects(
ARRAYSIZE(idleHandles), idleHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
}
}
if (cadenceTarget)
replayTarget = min(replayTarget, cadenceTarget);
current = CFrameScheduler::Nanotime();
if (cadenceTarget && cadenceTarget <= current)
{
m_devContext->FrameMissed(schedule, current, periodic);
continue;
}
if (replayTarget <= current)
continue;
ArmPublishTimer(m_publishTimer.Get(), replayTarget - current);
if (WaitForMultipleObjects(
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
if (cadenceTarget)
{
current = CFrameScheduler::Nanotime();
if (cadenceTarget <= current)
{
m_devContext->FrameMissed(schedule, current, periodic);
continue;
}
ArmPublishTimer(m_publishTimer.Get(), cadenceTarget - current);
if (WaitForMultipleObjects(
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
if (m_publishTimer.Get())
CancelWaitableTimer(m_publishTimer.Get());
if (WaitForMultipleObjects(
ARRAYSIZE(idleHandles), idleHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
uint64_t current = CFrameScheduler::Nanotime();
uint64_t replayTarget;
if (m_devContext->GetSharedFrameTarget(current, replayTarget) &&
replayTarget < target)
{
if (replayTarget <= current)
{
m_devContext->ProcessFrameQueue();
current = CFrameScheduler::Nanotime();
bool retry = false;
if (m_devContext->ReplaySharedFrame(current, retry))
continue;
current = CFrameScheduler::Nanotime();
if (retry)
replayTarget = current + PUBLISH_RETRY_NS;
else
replayTarget = target;
}
replayTarget = min(replayTarget, target);
current = CFrameScheduler::Nanotime();
if (target > current)
{
if (replayTarget <= current)
continue;
ArmPublishTimer(m_publishTimer.Get(), replayTarget - current);
if (WaitForMultipleObjects(
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
}
current = CFrameScheduler::Nanotime();
if (target > current)
{
ArmPublishTimer(m_publishTimer.Get(), target - current);
if (WaitForMultipleObjects(
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
const uint64_t publishStart = CFrameScheduler::Nanotime();
m_devContext->ProcessFrameQueue();
if (!m_devContext->FrameBufferAvailable(schedule) ||
!m_frameProcessor->Publish(schedule, periodic, publishStart))
{
ArmPublishTimer(m_publishTimer.Get(), PUBLISH_RETRY_NS);
if (WaitForMultipleObjects(
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
}
}
if (avTaskHandle)
AvRevertMmThreadCharacteristics(avTaskHandle);
}
void CSwapChainProcessor::SwapChainThread()
{
DWORD avTask = 0;
@@ -813,7 +562,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
D3D12_RESOURCE_DESC srcDesc = srcRes->GetRes()->GetDesc();
if (!noImageUpdate)
{
m_devContext->ObserveFrame(postProcessStart);
m_transport.ObserveFrame(postProcessStart);
m_frameProcessor->AccumulateDamage(
srcRes->GetDirtyRects(), srcRes->GetDirtyRectCount());
}
@@ -824,7 +573,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
srcFormat.height = srcDesc.Height;
srcFormat.format = CFrameProcessorUtil::GetFrameType(srcDesc.Format);
srcFormat.sdrWhiteLevel = sdrWhiteLevel;
srcFormat.colorTransform = m_devContext->GetColorTransform();
srcFormat.colorTransform = m_control.GetColorTransform();
switch (colorSpace)
{
@@ -973,7 +722,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
}
if (needsReconfigure || postProcessFormatChanged || frameMetadataChanged)
m_devContext->ForceFrame();
m_transport.ForceFrame();
const FrameSubmission submission =
{
@@ -987,98 +736,3 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
};
return m_frameProcessor->Submit(submission);
}
DWORD CALLBACK CSwapChainProcessor::_CursorThread(LPVOID arg)
{
reinterpret_cast<CSwapChainProcessor*>(arg)->CursorThread();
return 0;
}
bool CSwapChainProcessor::QueryHWCursor()
{
IDARG_IN_QUERY_HWCURSOR in = {};
in.LastShapeId = m_lastShapeId;
in.pShapeBuffer = m_shapeBuffer;
in.ShapeBufferSizeInBytes = 512 * 512 * 4;
IDARG_OUT_QUERY_HWCURSOR out = {};
UINT cursorWhiteLevel = m_sdrWhiteLevel.load(std::memory_order_relaxed);
NTSTATUS status;
#ifdef HAS_IDDCX_110
if (m_devContext->HasIddCx110DDIs())
{
IDARG_OUT_QUERY_HWCURSOR3 out3 = {};
status = IddCxMonitorQueryHardwareCursor3(m_monitor, &in, &out3);
out.IsCursorVisible = out3.IsCursorVisible;
out.X = out3.X;
out.Y = out3.Y;
out.IsCursorShapeUpdated = out3.IsCursorShapeUpdated;
out.CursorShapeInfo = out3.CursorShapeInfo;
if (out3.SdrWhiteLevel)
cursorWhiteLevel = out3.SdrWhiteLevel;
}
else
#endif
{
status = IddCxMonitorQueryHardwareCursor(m_monitor, &in, &out);
}
if (FAILED(status))
{
// this occurs if the display went away (ie, screen blanking or disabled)
if (status == STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY)
{
SetEvent(m_terminateEvent.Get());
return false;
}
DEBUG_ERROR("IddCxMonitorQueryHardwareCursor failed (0x%08x)", status);
return false;
}
if (out.IsCursorShapeUpdated)
m_lastShapeId = out.CursorShapeInfo.ShapeId;
m_devContext->SendCursor(out, m_shapeBuffer, cursorWhiteLevel);
return true;
}
void CSwapChainProcessor::CursorThread()
{
HRESULT hr = 0;
bool running = true;
while (running)
{
HANDLE waitHandles[] =
{
m_cursorDataEvent.Get(),
m_terminateEvent.Get()
};
DWORD waitResult = WaitForMultipleObjects(
ARRAYSIZE(waitHandles), waitHandles, FALSE, 100);
switch (waitResult)
{
case WAIT_TIMEOUT:
continue;
// cursorDataEvent
case WAIT_OBJECT_0:
if (!QueryHWCursor())
return;
continue;
// terminateEvent
case WAIT_OBJECT_0 + 1:
running = false;
continue;
default:
hr = HRESULT_FROM_WIN32(waitResult);
DEBUG_ERROR_HR(hr, "WaitForMultipleObjects");
return;
}
}
}

View File

@@ -20,41 +20,46 @@
#pragma once
#include "CD3D11Device.h"
#include "CD3D12Device.h"
#include "CIndirectDeviceContext.h"
#include "CInteropResourcePool.h"
#include "CFrameProcessor.h"
#include "CPostProcessor.h"
#include "d3d/CD3D11Device.h"
#include "d3d/CD3D12Device.h"
#include "display/IddCxCompat.h"
#include "d3d/CInteropResourcePool.h"
#include "capture/CFrameProcessor.h"
#include "common/KVMFR.h"
#include "postprocess/CPostProcessor.h"
#include <Windows.h>
#include <wrl.h>
#include <IddCx.h>
#include <atomic>
#include <memory>
using namespace Microsoft::WRL;
class CIndirectMonitorContext;
class CMonitorContext;
class CDeviceContext;
class CFrameTransport;
class CLGMPControl;
class CSwapChainProcessor
{
private:
CIndirectMonitorContext * m_monitorContext;
UINT64 m_assignmentGeneration;
IDDCX_MONITOR m_monitor;
CIndirectDeviceContext * m_devContext;
IDDCX_SWAPCHAIN m_hSwapChain;
LUID m_renderAdapter;
std::shared_ptr<CD3D11Device> m_dx11Device;
std::shared_ptr<CD3D12Device> m_dx12Device;
HANDLE m_newFrameEvent;
CMonitorContext * m_monitorContext;
UINT64 m_assignmentGeneration;
IDDCX_MONITOR m_monitor;
CDeviceContext * m_devContext;
CFrameTransport & m_transport;
CLGMPControl & m_control;
IDDCX_SWAPCHAIN m_hSwapChain;
LUID m_renderAdapter;
std::shared_ptr<CD3D11Device> m_dx11Device;
std::shared_ptr<CD3D12Device> m_dx12Device;
HANDLE m_newFrameEvent;
CInteropResourcePool m_resPool;
CPostProcessor m_postProcessors[LGMP_Q_FRAME_LEN];
CInteropResourcePool m_resPool;
CPostProcessor m_postProcessors[LGMP_Q_FRAME_LEN];
std::unique_ptr<CFrameProcessor> m_frameProcessor;
// Reconfiguration is exclusive while per-candidate recording is shared.
SRWLOCK m_pipelineLock = SRWLOCK_INIT;
SRWLOCK m_pipelineLock = SRWLOCK_INIT;
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_thread[3];
Wrappers::Event m_terminateEvent;
@@ -94,8 +99,9 @@ private:
uint64_t captureStart, bool duplicateFrame);
public:
CSwapChainProcessor(CIndirectMonitorContext * monitorContext, UINT64 assignmentGeneration,
IDDCX_MONITOR monitor, CIndirectDeviceContext * devContext, IDDCX_SWAPCHAIN hSwapChain,
CSwapChainProcessor(CMonitorContext * monitorContext,
UINT64 assignmentGeneration, IDDCX_MONITOR monitor,
CDeviceContext * devContext, IDDCX_SWAPCHAIN hSwapChain,
LUID renderAdapter, std::shared_ptr<CD3D11Device> dx11Device,
HANDLE newFrameEvent);
~CSwapChainProcessor();

View File

@@ -0,0 +1,268 @@
/**
* 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 "capture/CSwapChainProcessor.h"
#include "transport/CFrameTransport.h"
#include <avrt.h>
#include "CDebug.h"
static const uint64_t PUBLISH_RETRY_NS = 1000000ULL;
static bool ArmPublishTimer(HANDLE timer, uint64_t delay)
{
if (!timer)
return false;
LARGE_INTEGER due = {};
due.QuadPart = -static_cast<LONGLONG>((delay + 99) / 100);
if (!due.QuadPart)
due.QuadPart = -1;
return SetWaitableTimer(timer, &due, 0, nullptr, nullptr, FALSE) != FALSE;
}
DWORD CALLBACK CSwapChainProcessor::_PublisherThread(LPVOID arg)
{
reinterpret_cast<CSwapChainProcessor *>(arg)->PublisherThread();
return 0;
}
void CSwapChainProcessor::PublisherThread()
{
DWORD avTask = 0;
HANDLE avTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &avTask);
if (avTaskHandle &&
!AvSetMmThreadPriority(avTaskHandle, AVRT_PRIORITY_HIGH))
DEBUG_WARN("Failed to raise publisher MMCSS priority: %lu",
GetLastError());
const HANDLE scheduleEvent = m_transport.GetFrameScheduleEvent();
HANDLE idleHandles[] =
{
m_terminateEvent.Get(),
m_frameProcessor->GetReadyEvent(),
scheduleEvent,
};
HANDLE timerHandles[] =
{
m_terminateEvent.Get(),
m_frameProcessor->GetReadyEvent(),
scheduleEvent,
m_publishTimer.Get(),
};
const bool cadenceEnabled = m_frameProcessor->UsesCadence();
for (;;)
{
const uint64_t now = CFrameScheduler::Nanotime();
uint64_t target;
CFrameScheduler::Schedule schedule;
bool periodic;
bool republish;
m_transport.GetPublishTarget(
now, target, schedule, periodic, republish);
const bool ready = m_frameProcessor->HasReadyFrame();
if (!ready)
{
m_transport.ProcessFrameQueue();
if (m_frameProcessor->HasReadyFrame())
continue;
uint64_t current = CFrameScheduler::Nanotime();
uint64_t cadenceTarget = 0;
if (cadenceEnabled && schedule.deliveryDeadlineSerial && periodic)
{
if (schedule.deadline <= current)
{
m_transport.FrameMissed(schedule, current, periodic);
continue;
}
cadenceTarget = schedule.deadline;
}
if (republish && m_transport.HasPublishedFrame())
{
if (m_transport.RepublishFrameBuffer(schedule))
continue;
current = CFrameScheduler::Nanotime();
if (cadenceTarget && cadenceTarget <= current)
{
m_transport.FrameMissed(schedule, current, periodic);
continue;
}
uint64_t retryTarget = current + PUBLISH_RETRY_NS;
if (cadenceTarget)
retryTarget = min(retryTarget, cadenceTarget);
ArmPublishTimer(m_publishTimer.Get(), retryTarget - current);
if (WaitForMultipleObjects(
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
uint64_t replayTarget;
if (m_transport.GetSharedFrameTarget(current, replayTarget))
{
bool retry = false;
if (replayTarget <= current)
{
if (m_transport.ReplaySharedFrame(current, retry))
continue;
current = CFrameScheduler::Nanotime();
if (cadenceTarget && cadenceTarget <= current)
{
m_transport.FrameMissed(schedule, current, periodic);
continue;
}
if (retry)
replayTarget = current + PUBLISH_RETRY_NS;
else
{
if (cadenceTarget)
replayTarget = cadenceTarget;
else
{
if (m_publishTimer.Get())
CancelWaitableTimer(m_publishTimer.Get());
if (WaitForMultipleObjects(
ARRAYSIZE(idleHandles), idleHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
}
}
if (cadenceTarget)
replayTarget = min(replayTarget, cadenceTarget);
current = CFrameScheduler::Nanotime();
if (cadenceTarget && cadenceTarget <= current)
{
m_transport.FrameMissed(schedule, current, periodic);
continue;
}
if (replayTarget <= current)
continue;
ArmPublishTimer(m_publishTimer.Get(), replayTarget - current);
if (WaitForMultipleObjects(
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
if (cadenceTarget)
{
current = CFrameScheduler::Nanotime();
if (cadenceTarget <= current)
{
m_transport.FrameMissed(schedule, current, periodic);
continue;
}
ArmPublishTimer(m_publishTimer.Get(), cadenceTarget - current);
if (WaitForMultipleObjects(
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
if (m_publishTimer.Get())
CancelWaitableTimer(m_publishTimer.Get());
if (WaitForMultipleObjects(
ARRAYSIZE(idleHandles), idleHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
uint64_t current = CFrameScheduler::Nanotime();
uint64_t replayTarget;
if (m_transport.GetSharedFrameTarget(current, replayTarget) &&
replayTarget < target)
{
if (replayTarget <= current)
{
m_transport.ProcessFrameQueue();
current = CFrameScheduler::Nanotime();
bool retry = false;
if (m_transport.ReplaySharedFrame(current, retry))
continue;
current = CFrameScheduler::Nanotime();
if (retry)
replayTarget = current + PUBLISH_RETRY_NS;
else
replayTarget = target;
}
replayTarget = min(replayTarget, target);
current = CFrameScheduler::Nanotime();
if (target > current)
{
if (replayTarget <= current)
continue;
ArmPublishTimer(m_publishTimer.Get(), replayTarget - current);
if (WaitForMultipleObjects(
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
}
current = CFrameScheduler::Nanotime();
if (target > current)
{
ArmPublishTimer(m_publishTimer.Get(), target - current);
if (WaitForMultipleObjects(
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
continue;
}
const uint64_t publishStart = CFrameScheduler::Nanotime();
m_transport.ProcessFrameQueue();
if (!m_transport.FrameBufferAvailable(schedule) ||
!m_frameProcessor->Publish(schedule, periodic, publishStart))
{
ArmPublishTimer(m_publishTimer.Get(), PUBLISH_RETRY_NS);
if (WaitForMultipleObjects(
ARRAYSIZE(timerHandles), timerHandles, FALSE, INFINITE) ==
WAIT_OBJECT_0)
break;
}
}
if (avTaskHandle)
AvRevertMmThreadCharacteristics(avTaskHandle);
}

View File

@@ -0,0 +1,40 @@
/**
* 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 <stdint.h>
// FrameBuffer overlays LGMP shared memory and has a variable-length payload.
#pragma warning(push)
#pragma warning(disable: 4200)
struct FrameBuffer
{
volatile uint32_t wp;
uint8_t data[0];
};
#pragma warning(pop)
struct PreparedFrameBuffer
{
unsigned frameIndex;
uint8_t * mem;
bool fullCopy;
};

View File

@@ -18,7 +18,7 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CSettings.h"
#include "config/CSettings.h"
#include "CDebug.h"
#include "DefaultDisplayModes.h"
#include "RefreshRate.h"

View File

@@ -18,7 +18,7 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CD3D11Device.h"
#include "d3d/CD3D11Device.h"
#include "CDebug.h"
HRESULT CD3D11Device::Init()
@@ -72,4 +72,4 @@ HRESULT CD3D11Device::Init()
return hr;
return S_OK;
}
}

View File

@@ -18,7 +18,7 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CD3D12CommandQueue.h"
#include "d3d/CD3D12CommandQueue.h"
#include "CDebug.h"
static uint64_t ScaleTicks(uint64_t ticks, uint64_t targetFrequency,

View File

@@ -18,7 +18,7 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CD3D12Device.h"
#include "d3d/CD3D12Device.h"
#include "CDebug.h"
bool CD3D12Device::m_indirectCopy = false;

View File

@@ -26,8 +26,8 @@
#include <dxgi1_5.h>
#include <d3d12.h>
#include "CIVSHMEM.h"
#include "CD3D12CommandQueue.h"
#include "transport/CIVSHMEM.h"
#include "d3d/CD3D12CommandQueue.h"
using namespace Microsoft::WRL;

View File

@@ -18,10 +18,13 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CInteropResource.h"
#include "d3d/CInteropResource.h"
#include "CDebug.h"
bool CInteropResource::Init(std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device, ComPtr<ID3D11Texture2D> srcTex)
bool CInteropResource::Init(
std::shared_ptr<CD3D11Device> dx11Device,
std::shared_ptr<CD3D12Device> dx12Device,
ComPtr<ID3D11Texture2D> srcTex)
{
HRESULT hr;
@@ -112,14 +115,15 @@ void CInteropResource::Reset()
m_dx11Device.reset();
}
bool CInteropResource::Compare(const ComPtr<ID3D11Texture2D>& srcTex)
bool CInteropResource::Compare(
const ComPtr<ID3D11Texture2D>& srcTex) const
{
if (srcTex.Get() != m_srcTex)
return false;
D3D11_TEXTURE2D_DESC format;
srcTex->GetDesc(&format);
return
m_format.Width == format.Width &&
m_format.Height == format.Height &&
@@ -163,6 +167,7 @@ void CInteropResource::SetFullDamage()
m_dirtyRects[0].bottom = m_format.Height;
m_nbDirtyRects = 1;
}
void CInteropResource::SetDirtyRects(const RECT * dirtyRects, unsigned nbDirtyRects)
{
if (nbDirtyRects > LG_MAX_DIRTY_RECTS)

View File

@@ -0,0 +1,73 @@
/**
* 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 <Windows.h>
#include <wdf.h>
#include <wrl.h>
#include <memory>
#include "d3d/CD3D11Device.h"
#include "d3d/CD3D12Device.h"
#include "d3d/CD3D12CommandQueue.h"
using namespace Microsoft::WRL;
#define LG_MAX_DIRTY_RECTS 256
class CInteropResource
{
private:
std::shared_ptr<CD3D11Device> m_dx11Device;
std::shared_ptr<CD3D12Device> m_dx12Device;
// This value is likely released. It is only used to check if the supplied
// texture is different; do not rely on it pointing to valid memory.
void * m_srcTex;
ComPtr<ID3D12Resource > m_d12Res;
D3D11_TEXTURE2D_DESC m_format;
ComPtr<ID3D11Fence > m_d11Fence;
ComPtr<ID3D12Fence > m_d12Fence;
UINT64 m_fenceValue;
bool m_ready;
RECT m_dirtyRects[LG_MAX_DIRTY_RECTS];
unsigned m_nbDirtyRects;
public:
bool Init(std::shared_ptr<CD3D11Device> dx11Device,
std::shared_ptr<CD3D12Device> dx12Device,
ComPtr<ID3D11Texture2D> srcTex);
void Reset();
bool IsReady() const { return m_ready; }
bool Compare(const ComPtr<ID3D11Texture2D>& srcTex) const;
bool Signal();
bool Sync(CD3D12CommandSlot& slot);
void SetFullDamage();
void SetDirtyRects(const RECT * dirtyRects, unsigned nbDirtyRects);
const ComPtr<ID3D12Resource>& GetRes() const { return m_d12Res; }
const D3D11_TEXTURE2D_DESC& GetFormat() const { return m_format; }
const RECT * GetDirtyRects() const { return m_dirtyRects; }
unsigned GetDirtyRectCount() const { return m_nbDirtyRects; }
};

View File

@@ -18,10 +18,12 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CInteropResourcePool.h"
#include "d3d/CInteropResourcePool.h"
#include "CDebug.h"
void CInteropResourcePool::Init(std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device)
void CInteropResourcePool::Init(
std::shared_ptr<CD3D11Device> dx11Device,
std::shared_ptr<CD3D12Device> dx12Device)
{
Reset();
m_dx11Device = dx11Device;
@@ -36,7 +38,8 @@ void CInteropResourcePool::Reset()
m_dx12Device.reset();
}
CInteropResource* CInteropResourcePool::Get(ComPtr<ID3D11Texture2D> srcTex)
CInteropResource * CInteropResourcePool::Get(
ComPtr<ID3D11Texture2D> srcTex)
{
CInteropResource * res;
unsigned freeSlot = POOL_SIZE;
@@ -64,4 +67,4 @@ CInteropResource* CInteropResourcePool::Get(ComPtr<ID3D11Texture2D> srcTex)
return nullptr;
return res;
}
}

View File

@@ -24,23 +24,24 @@
#include <wdf.h>
#include <wrl.h>
#include <d3d11_4.h>
#include "CInteropResource.h"
#include "d3d/CInteropResource.h"
using namespace Microsoft::WRL;
#define POOL_SIZE 10
class CInteropResourcePool
{
private:
CInteropResource m_pool[POOL_SIZE];
std::shared_ptr<CD3D11Device> m_dx11Device;
std::shared_ptr<CD3D12Device> m_dx12Device;
public:
void Init(std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device);
void Reset();
private:
static constexpr unsigned POOL_SIZE = 10;
CInteropResource* Get(ComPtr<ID3D11Texture2D> srcTex);
};
CInteropResource m_pool[POOL_SIZE];
std::shared_ptr<CD3D11Device> m_dx11Device;
std::shared_ptr<CD3D12Device> m_dx12Device;
public:
void Init(std::shared_ptr<CD3D11Device> dx11Device,
std::shared_ptr<CD3D12Device> dx12Device);
void Reset();
CInteropResource * Get(ComPtr<ID3D11Texture2D> srcTex);
};

View File

@@ -0,0 +1,496 @@
/**
* 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 "common/LGMPConfig.h"
#include "util/CSRWLock.h"
#include <d3d12.h>
#include <iterator>
#include <utility>
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& sharedSize)
{
frameSize = 0;
sharedSize = 0;
if (!alignment || !limits.frameMemoryOffset ||
!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;
sharedSize = frameMemoryStart +
frameAllocationSize * LGMP_Q_FRAME_BUFFER_LEN;
return true;
}
uint32_t CDisplayConfiguration::RecommendedIVSHMEMSizeMiB(
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)
{
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 FrameMemoryLimits& limits)
{
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());
const UINT64 alignment = limits.alignment ? limits.alignment :
D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
bool hasPreferred = false;
for (const auto& configuredMode : configuredModes)
{
UINT64 frameSize;
UINT64 requiredIVSHMEMSize;
if (!GetResolutionMemoryRequirements(configuredMode.width,
configuredMode.height, alignment, limits, frameSize,
requiredIVSHMEMSize))
{
DEBUG_WARN("Filtering invalid %s mode %ux%u@%.3f",
configuredMode.extraMode ? "extra" : "configured",
configuredMode.width, configuredMode.height,
configuredMode.refreshMilliHz / 1000.0);
continue;
}
if (requiredIVSHMEMSize > limits.sharedSize)
{
DEBUG_WARN(
"Filtering %s mode %ux%u@%.3f: requires %llu bytes of IVSHMEM, only %llu bytes are available",
configuredMode.extraMode ? "extra" : "configured",
configuredMode.width, configuredMode.height,
configuredMode.refreshMilliHz / 1000.0,
(unsigned long long)requiredIVSHMEMSize,
(unsigned long long)limits.sharedSize);
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 fit in IVSHMEM");
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 FrameMemoryLimits& limits)
{
return LoadModes(limits);
}
bool CDisplayConfiguration::ReloadSettings(
const FrameMemoryLimits& limits)
{
bool modesLoaded = false;
{
CSRWExclusiveLock reloadLock(&m_reloadLock);
bool settingsUpdated = true;
CSettings::DisplayMode extraMode = {};
if (m_settings.GetExtraMode(extraMode))
{
const unsigned refreshMilliHz =
m_settings.GetDefaultRefreshMilliHz();
if (extraMode.refreshMilliHz != refreshMilliHz)
{
extraMode.refreshMilliHz = refreshMilliHz;
settingsUpdated = m_settings.SetExtraMode(extraMode);
}
}
if (settingsUpdated)
modesLoaded = LoadModes(limits);
}
if (!modesLoaded)
DEBUG_ERROR("Failed to reload the display mode list");
return modesLoaded;
}
CDisplayConfiguration::ResolutionResult
CDisplayConfiguration::SetResolution(
uint32_t width, uint32_t height, const FrameMemoryLimits& limits)
{
ResolutionResult result;
UINT64 frameSize;
UINT64 requiredIVSHMEMSize;
if (!GetResolutionMemoryRequirements(width, height, limits.alignment,
limits, frameSize, requiredIVSHMEMSize))
{
DEBUG_WARN("Ignoring invalid resolution request: %ux%u", width, height);
return result;
}
if (requiredIVSHMEMSize > limits.sharedSize)
{
result.status = ResolutionStatus::TOO_LARGE;
result.requiredMiB = RecommendedIVSHMEMSizeMiB(requiredIVSHMEMSize);
DEBUG_WARN(
"Refusing resolution %ux%u: frame requires %llu bytes, only %llu bytes are available; IVSHMEM 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;
{
CSRWExclusiveLock reloadLock(&m_reloadLock);
if (!m_settings.SetExtraMode(mode))
result.status = ResolutionStatus::SETTINGS_FAILED;
else if (!LoadModes(limits))
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.refreshMilliHz, 1000);
SetSignalRate(signal.hSyncFreq,
(UINT64)mode.refreshMilliHz * signal.totalSize.cy, 1000);
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

View File

@@ -0,0 +1,113 @@
/**
* 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 "config/CSettings.h"
#include "display/CEdid.h"
#include "display/IddCxCompat.h"
#include "transport/FrameMemoryLimits.h"
#include <stddef.h>
#include <stdint.h>
#include <vector>
class CDisplayConfiguration
{
public:
enum class ResolutionStatus
{
SUCCESS,
INVALID,
TOO_LARGE,
SETTINGS_FAILED,
MODES_FAILED,
};
struct ResolutionResult
{
ResolutionStatus status = ResolutionStatus::INVALID;
CSettings::DisplayMode mode = {};
uint32_t requiredMiB = 0;
};
struct Description
{
size_t modeCount = 0;
std::vector<BYTE> edid;
};
private:
CSettings& m_settings;
// Registry-backed changes are serialized before publishing a replacement
// mode list. Readers only hold m_modeLock long enough to take a snapshot.
SRWLOCK m_reloadLock = SRWLOCK_INIT;
mutable SRWLOCK m_modeLock = SRWLOCK_INIT;
CSettings::DisplayModes m_modes;
CEdid m_edid;
bool m_hdrEnabled = false;
bool LoadModes(const FrameMemoryLimits& limits);
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& sharedSize);
static uint32_t RecommendedIVSHMEMSizeMiB(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);
ResolutionResult SetResolution(uint32_t width, uint32_t height,
const FrameMemoryLimits& limits);
void InitializeEdid(bool hdr);
void RebuildEdid(bool hdr);
Description GetDescription() const;
NTSTATUS ParseMonitorDescription(
const IDARG_IN_PARSEMONITORDESCRIPTION * inArgs,
IDARG_OUT_PARSEMONITORDESCRIPTION * outArgs) const;
NTSTATUS MonitorGetDefaultModes(
const IDARG_IN_GETDEFAULTDESCRIPTIONMODES * inArgs,
IDARG_OUT_GETDEFAULTDESCRIPTIONMODES * outArgs) const;
NTSTATUS MonitorQueryTargetModes(
const IDARG_IN_QUERYTARGETMODES * inArgs,
IDARG_OUT_QUERYTARGETMODES * outArgs) const;
#ifdef HAS_IDDCX_110
NTSTATUS ParseMonitorDescription2(
const IDARG_IN_PARSEMONITORDESCRIPTION2 * inArgs,
IDARG_OUT_PARSEMONITORDESCRIPTION * outArgs) const;
NTSTATUS MonitorQueryTargetModes2(
const IDARG_IN_QUERYTARGETMODES2 * inArgs,
IDARG_OUT_QUERYTARGETMODES * outArgs) const;
#endif
};

View File

@@ -18,7 +18,7 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CEdid.h"
#include "display/CEdid.h"
#include <algorithm>
#include <string.h>

View File

@@ -24,7 +24,7 @@
#include <stdint.h>
#include <vector>
#include "CSettings.h"
#include "config/CSettings.h"
class CEdid
{

View File

@@ -0,0 +1,252 @@
/**
* 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/CMonitorManager.h"
#include "display/monitor/Context.h"
#include "CDebug.h"
void CMonitorManager::Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
std::vector<BYTE> edid, CDeviceContext * owner)
{
DEBUG_INFO("Creating monitor on connector %u", connectorIndex);
// We support a single monitor; never create a second one if one already
// exists (a replug must clear m_monitor via departure first).
AcquireSRWLockExclusive(&m_lock);
const bool haveMonitor = m_monitor != WDF_NO_HANDLE;
ReleaseSRWLockExclusive(&m_lock);
if (haveMonitor)
{
DEBUG_WARN("FinishInit skipped: a monitor already exists");
return;
}
WDF_OBJECT_ATTRIBUTES attr;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, CMonitorContextWrapper);
DEBUG_INFO("Using %llu-byte monitor EDID",
(unsigned long long)edid.size());
IDDCX_MONITOR_INFO info = {};
info.Size = sizeof(info);
info.MonitorType = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI;
info.ConnectorIndex = connectorIndex;
info.MonitorDescription.Size = sizeof(info.MonitorDescription);
info.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID;
info.MonitorDescription.DataSize = (UINT)edid.size();
info.MonitorDescription.pData = edid.empty() ? nullptr : edid.data();
HRESULT hr = CoCreateGuid(&info.MonitorContainerId);
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to create the monitor container ID");
return;
}
IDARG_IN_MONITORCREATE create = {};
create.ObjectAttributes = &attr;
create.pMonitorInfo = &info;
IDARG_OUT_MONITORCREATE createOut = {};
NTSTATUS status = IddCxMonitorCreate(adapter, &create, &createOut);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR_HR(status, "IddCxMonitorCreate Failed");
return;
}
DEBUG_INFO("Monitor object created (%p)", createOut.MonitorObject);
AcquireSRWLockExclusive(&m_lock);
m_monitor = createOut.MonitorObject;
ReleaseSRWLockExclusive(&m_lock);
auto * wrapper = WdfObjectGet_CMonitorContextWrapper(m_monitor);
wrapper->context = new CMonitorContext(m_monitor, owner);
IDARG_OUT_MONITORARRIVAL out = {};
status = IddCxMonitorArrival(m_monitor, &out);
if (FAILED(status))
{
DEBUG_ERROR_HR(status, "IddCxMonitorArrival Failed");
return;
}
DEBUG_INFO("Monitor arrival reported successfully");
}
CMonitorManager::ReplugAction CMonitorManager::Replug()
{
AcquireSRWLockExclusive(&m_lock);
if (m_replugMonitor || (m_swapChainAssigned && !m_swapChainReady))
{
// Coalesce changes received while a swap chain is being initialized, the
// old one is draining, or its replacement is being initialized.
m_replugPending = true;
ReleaseSRWLockExclusive(&m_lock);
return ReplugAction::NONE;
}
IDDCX_MONITOR monitor = m_monitor;
if (monitor == WDF_NO_HANDLE)
{
m_replugMonitor = true;
m_monitorDeparted = true;
ReleaseSRWLockExclusive(&m_lock);
// Either no monitor yet, or one is already pending; build it now and
// cancel any queued rebuild so we do not create two.
m_createQueued.store(0);
return ReplugAction::CREATE;
}
// Clear the handle before departing so nothing calls an IddCx monitor API
// on a departing/destroyed handle. Create publishes the new one.
m_replugMonitor = true;
m_monitorDeparted = false;
m_waitForSwapChainRelease = m_swapChainAssigned;
m_monitor = nullptr;
ReleaseSRWLockExclusive(&m_lock);
DEBUG_TRACE("ReplugMonitor");
NTSTATUS status = IddCxMonitorDeparture(monitor);
if (!NT_SUCCESS(status))
{
AcquireSRWLockExclusive(&m_lock);
m_replugMonitor = false;
m_replugPending = false;
m_monitorDeparted = false;
m_waitForSwapChainRelease = false;
m_monitor = monitor;
ReleaseSRWLockExclusive(&m_lock);
DEBUG_ERROR("IddCxMonitorDeparture Failed (0x%08x)", status);
return ReplugAction::NONE;
}
AcquireSRWLockExclusive(&m_lock);
m_monitorDeparted = true;
const bool rebuild = !m_waitForSwapChainRelease;
ReleaseSRWLockExclusive(&m_lock);
// If there was no swap chain there will be no unassign callback to queue
// the rebuild. Otherwise OnSwapChainReleased does so after teardown drains.
if (rebuild)
m_createQueued.store(1);
return ReplugAction::NONE;
}
void CMonitorManager::RequestMode(const CSettings::DisplayMode& mode)
{
AcquireSRWLockExclusive(&m_lock);
m_setMode = mode;
m_doSetMode = true;
ReleaseSRWLockExclusive(&m_lock);
}
void CMonitorManager::OnDestroyed(IDDCX_MONITOR monitor)
{
AcquireSRWLockExclusive(&m_lock);
if (m_monitor == monitor)
m_monitor = nullptr;
ReleaseSRWLockExclusive(&m_lock);
}
void CMonitorManager::OnSwapChainAssigned()
{
AcquireSRWLockExclusive(&m_lock);
m_swapChainAssigned = true;
m_swapChainReady = false;
ReleaseSRWLockExclusive(&m_lock);
}
void CMonitorManager::OnSwapChainReleased()
{
bool rebuild = false;
AcquireSRWLockExclusive(&m_lock);
m_swapChainAssigned = false;
m_swapChainReady = false;
if (m_replugMonitor && m_waitForSwapChainRelease)
{
m_waitForSwapChainRelease = false;
rebuild = m_monitorDeparted;
}
ReleaseSRWLockExclusive(&m_lock);
if (rebuild)
m_createQueued.store(1);
}
CMonitorManager::ReadyAction CMonitorManager::OnSwapChainReady()
{
ReadyAction action = {};
bool replug = false;
AcquireSRWLockExclusive(&m_lock);
m_swapChainReady = true;
if (m_replugMonitor)
{
m_replugMonitor = false;
m_monitorDeparted = false;
if (m_replugPending)
{
m_replugPending = false;
replug = true;
}
}
else if (m_replugPending)
{
m_replugPending = false;
replug = true;
}
// Do not consume the requested mode on an intermediate replacement swap
// chain. The last coalesced replug must be the one that applies it.
if (!replug && m_doSetMode)
{
action.mode = m_setMode;
m_doSetMode = false;
action.setMode = true;
}
ReleaseSRWLockExclusive(&m_lock);
action.replug = replug;
return action;
}
void CMonitorManager::QueueReplug()
{
m_replugQueued.store(1);
}
CMonitorManager::DeferredAction CMonitorManager::TakeDeferredAction()
{
if (m_createQueued.exchange(0))
return DeferredAction::CREATE;
if (m_replugQueued.exchange(0))
return DeferredAction::REPLUG;
return DeferredAction::NONE;
}

View File

@@ -0,0 +1,89 @@
/**
* 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 <Windows.h>
#include <wdf.h>
#include <IddCx.h>
#include <atomic>
#include <vector>
#include "config/CSettings.h"
class CDeviceContext;
class CMonitorManager
{
public:
enum class ReplugAction
{
NONE,
CREATE,
};
enum class DeferredAction
{
NONE,
CREATE,
REPLUG,
};
struct ReadyAction
{
CSettings::DisplayMode mode = {};
bool setMode = false;
bool replug = false;
};
private:
IDDCX_MONITOR m_monitor = nullptr;
// Guards the monitor/replug/swap-chain state. These values are touched by
// IddCx callback threads, the swap-chain thread, and the LGMP timer.
SRWLOCK m_lock = SRWLOCK_INIT;
bool m_replugMonitor = false;
bool m_replugPending = false;
bool m_monitorDeparted = false;
bool m_swapChainAssigned = false;
bool m_swapChainReady = false;
bool m_waitForSwapChainRelease = false;
CSettings::DisplayMode m_setMode = {};
bool m_doSetMode = false;
std::atomic<LONG> m_createQueued = 0;
std::atomic<LONG> m_replugQueued = 0;
public:
void Create(UINT connectorIndex, IDDCX_ADAPTER adapter,
std::vector<BYTE> edid, CDeviceContext * owner);
ReplugAction Replug();
void RequestMode(const CSettings::DisplayMode& mode);
void OnDestroyed(IDDCX_MONITOR monitor);
void OnSwapChainAssigned();
void OnSwapChainReleased();
ReadyAction OnSwapChainReady();
void QueueReplug();
DeferredAction TakeDeferredAction();
};

View File

@@ -0,0 +1,34 @@
/**
* 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 <Windows.h>
#include <wdf.h>
#include <IddCx.h>
// IddCx 1.10 HDR/WCG types are only visible when the WDK targets
// (NTDDI >= 0x0A000005) and the build flags select IddCx 1.10 or newer.
#if defined(IDDCX_VERSION_MAJOR) && defined(IDDCX_VERSION_MINOR) && \
(IDDCX_VERSION_MAJOR > 1 || \
(IDDCX_VERSION_MAJOR == 1 && IDDCX_VERSION_MINOR >= 10)) && \
NTDDI_VERSION >= 0x0A000005
#define HAS_IDDCX_110
#endif

View File

@@ -0,0 +1,588 @@
/**
* 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/device/CDeviceContext.h"
#include "display/IddCxCompat.h"
#include "transport/CPipeServer.h"
#include "CDebug.h"
#include <dxgi1_2.h>
#include <utility>
// Adapter and monitor lifecycle
static const UINT IDDCX_VERSION_1_10 = 0x1A00;
CDeviceContext::CDeviceContext(WDFDEVICE wdfDevice) :
m_wdfDevice(wdfDevice),
m_lgmpControl(m_lgmpHost),
m_frameTransport(m_lgmpHost, m_ivshmem),
m_displayConfiguration(g_settings)
{
}
CDeviceContext::~CDeviceContext()
{
// Both callbacks dereference this context. Drain them before the subsystem
// members are destroyed in frame, control, host order.
if (m_initTimer)
{
WdfTimerStop(m_initTimer, TRUE);
m_initTimer = nullptr;
}
if (m_lgmpTimer)
{
WdfTimerStop(m_lgmpTimer, TRUE);
m_lgmpTimer = nullptr;
}
}
void CDeviceContext::QueryIddCxCapabilities()
{
IDARG_OUT_GETVERSION ver = {};
NTSTATUS status = IddCxGetVersion(&ver);
if (!NT_SUCCESS(status))
{
m_iddCxVersion = 0;
m_hasIddCx110DDIs = false;
m_canProcessFP16 = false;
DEBUG_ERROR_HR(status, "IddCxGetVersion Failed");
return;
}
m_iddCxVersion = ver.IddCxVersion;
#ifdef HAS_IDDCX_110
const bool hasIddCx110DDIs =
!!IDD_IS_FUNCTION_AVAILABLE(IddCxSwapChainReleaseAndAcquireBuffer2) &&
!!IDD_IS_FUNCTION_AVAILABLE(IddCxMonitorQueryHardwareCursor3) &&
!!IDD_IS_FUNCTION_AVAILABLE(IddCxMonitorUpdateModes2) &&
IDD_IS_FIELD_AVAILABLE(IDD_CX_CLIENT_CONFIG, EvtIddCxAdapterQueryTargetInfo) &&
IDD_IS_FIELD_AVAILABLE(IDD_CX_CLIENT_CONFIG, EvtIddCxAdapterCommitModes2) &&
IDD_IS_FIELD_AVAILABLE(IDD_CX_CLIENT_CONFIG, EvtIddCxParseMonitorDescription2) &&
IDD_IS_FIELD_AVAILABLE(IDD_CX_CLIENT_CONFIG, EvtIddCxMonitorQueryTargetModes2) &&
IDD_IS_FIELD_AVAILABLE(IDD_CX_CLIENT_CONFIG, EvtIddCxMonitorSetDefaultHdrMetaData) &&
IDD_IS_FIELD_AVAILABLE(IDD_CX_CLIENT_CONFIG, EvtIddCxMonitorSetGammaRamp);
#else
const bool hasIddCx110DDIs = false;
#endif
m_hasIddCx110DDIs =
m_iddCxVersion >= IDDCX_VERSION_1_10 && hasIddCx110DDIs;
m_canProcessFP16 = !m_softwareMode && m_hasIddCx110DDIs;
DEBUG_INFO("IddCx version: 0x%04x", m_iddCxVersion);
DEBUG_INFO("IddCx 1.10 HDR/WCG DDIs: %s",
m_hasIddCx110DDIs ? "available" : "unavailable");
if (m_softwareMode && m_hasIddCx110DDIs)
DEBUG_INFO("HDR/WCG disabled for software rendering");
}
void CDeviceContext::ScheduleInitRetry()
{
// Create the retry timer once; if it already exists it is either running or
// will be (re)started below.
if (!m_initTimer)
{
WDF_TIMER_CONFIG config;
WDF_TIMER_CONFIG_INIT_PERIODIC(&config,
[](WDFTIMER timer) -> void
{
WDFOBJECT parent = WdfTimerGetParentObject(timer);
auto wrapper = WdfObjectGet_CDeviceContextWrapper(parent);
wrapper->context->InitAdapter();
},
500);
config.AutomaticSerialization = FALSE;
WDF_OBJECT_ATTRIBUTES attribs;
WDF_OBJECT_ATTRIBUTES_INIT(&attribs);
attribs.ParentObject = m_wdfDevice;
attribs.ExecutionLevel = WdfExecutionLevelDispatch;
NTSTATUS status = WdfTimerCreate(&config, &attribs, &m_initTimer);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR_HR(status, "Init retry timer creation failed");
m_initTimer = nullptr;
return;
}
}
WdfTimerStart(m_initTimer, WDF_REL_TIMEOUT_IN_MS(500));
}
void CDeviceContext::StopInitRetry()
{
if (m_initTimer)
WdfTimerStop(m_initTimer, FALSE);
}
void CDeviceContext::InitAdapter()
{
DEBUG_TRACE("InitAdapter");
// The adapter only needs to be created once. D0Entry and the retry timer can
// both land here, so guard against re-entrancy and repeated creation.
if (m_adapter)
{
DEBUG_TRACE("Adapter initialization skipped: adapter already exists");
return;
}
LONG initExpected = 0;
if (!m_initInProgress.compare_exchange_strong(initExpected, 1))
{
DEBUG_TRACE("Adapter initialization skipped: initialization already in progress");
return;
}
// At boot the IVSHMEM PCI device may not have enumerated yet. Rather than
// silently abandoning the adapter (leaving the device loaded but with no
// monitor), retry from a timer until the shared memory becomes available.
if (!m_ivshmemOpened)
{
if (!m_ivshmem.Init() || !m_ivshmem.Open())
{
DEBUG_WARN("IVSHMEM not available yet, scheduling init retry");
ScheduleInitRetry();
m_initInProgress.store(0);
return;
}
m_ivshmemOpened = true;
}
// Select the render adapter before advertising capabilities. If no hardware
// adapter is available, this is a software-rendered display and must remain
// SDR-only; the software path must never depend on compute processing.
m_havePreferredRenderAdapter = false;
m_preferredRenderAdapter = {};
IDXGIFactory1 * factory = NULL;
HRESULT factoryStatus = CreateDXGIFactory1(
__uuidof(IDXGIFactory1), (void **)&factory);
if (FAILED(factoryStatus))
DEBUG_ERROR_HR(factoryStatus, "CreateDXGIFactory Failed");
else
{
for (UINT i = 0;; ++i)
{
IDXGIAdapter1 * dxgiAdapter = nullptr;
HRESULT enumStatus = factory->EnumAdapters1(i, &dxgiAdapter);
if (enumStatus == DXGI_ERROR_NOT_FOUND)
break;
if (FAILED(enumStatus))
{
DEBUG_ERROR_HR(enumStatus, "Failed to enumerate DXGI adapter %u", i);
break;
}
DXGI_ADAPTER_DESC1 adapterDesc = {};
HRESULT descStatus = dxgiAdapter->GetDesc1(&adapterDesc);
dxgiAdapter->Release();
if (FAILED(descStatus))
{
DEBUG_ERROR_HR(descStatus, "Failed to query DXGI adapter %u", i);
continue;
}
if ((adapterDesc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) ||
(adapterDesc.VendorId == 0x1414 && adapterDesc.DeviceId == 0x008c))
{
DEBUG_INFO("Ignoring software render adapter %ls", adapterDesc.Description);
continue;
}
if ((adapterDesc.VendorId == 0x1b36 && adapterDesc.DeviceId == 0x000d) || // QXL
(adapterDesc.VendorId == 0x1234 && adapterDesc.DeviceId == 0x1111)) // QEMU Standard VGA
{
DEBUG_INFO("Ignoring display-only adapter %ls (vendor 0x%04x, device 0x%04x)",
adapterDesc.Description, adapterDesc.VendorId, adapterDesc.DeviceId);
continue;
}
DEBUG_INFO("Selected render adapter %ls (vendor 0x%04x, device 0x%04x)",
adapterDesc.Description, adapterDesc.VendorId, adapterDesc.DeviceId);
m_preferredRenderAdapter = adapterDesc.AdapterLuid;
m_havePreferredRenderAdapter = true;
break;
}
factory->Release();
}
m_softwareMode = !m_havePreferredRenderAdapter;
if (m_softwareMode)
DEBUG_INFO("No hardware render adapter available; using SDR software mode");
QueryIddCxCapabilities();
DEBUG_TRACE("Initializing LGMP metadata");
if (!InitializeLGMP())
{
m_initInProgress.store(0);
return;
}
DEBUG_TRACE("Loading configured display modes");
if (!m_displayConfiguration.Load(m_frameTransport.GetMemoryLimits()))
{
m_initInProgress.store(0);
return;
}
DEBUG_TRACE("Initializing monitor EDID");
m_displayConfiguration.InitializeEdid(CanProcessFP16());
const CDisplayConfiguration::Description description =
m_displayConfiguration.GetDescription();
DEBUG_INFO("Initializing adapter with %llu modes and a %u-byte EDID",
(unsigned long long)description.modeCount,
(UINT)description.edid.size());
IDDCX_ADAPTER_CAPS caps = {};
caps.Size = sizeof(caps);
/**
* For some reason if we do not set this flag sometimes windows will
* refuse to enumerate our virtual monitor. Intel also noted in their
* sources that if this is not set dynamic resolution changes from this
* driver will not work. This behaviour is not documented by Microsoft.
*/
caps.Flags = IDDCX_ADAPTER_FLAGS_USE_SMALLEST_MODE;
#ifdef HAS_IDDCX_110
if (CanProcessFP16())
caps.Flags |= IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16;
#endif
caps.MaxMonitorsSupported = 1;
caps.StaticDesktopReencodeFrameCount = 1;
caps.EndPointDiagnostics.Size = sizeof(caps.EndPointDiagnostics);
caps.EndPointDiagnostics.GammaSupport = IDDCX_FEATURE_IMPLEMENTATION_NONE;
caps.EndPointDiagnostics.TransmissionType = IDDCX_TRANSMISSION_TYPE_OTHER;
caps.EndPointDiagnostics.pEndPointFriendlyName = L"Looking Glass IDD Driver";
caps.EndPointDiagnostics.pEndPointManufacturerName = L"Looking Glass";
caps.EndPointDiagnostics.pEndPointModelName = L"Looking Glass";
IDDCX_ENDPOINT_VERSION ver = {};
ver.Size = sizeof(ver);
ver.MajorVer = 1;
caps.EndPointDiagnostics.pFirmwareVersion = &ver;
caps.EndPointDiagnostics.pHardwareVersion = &ver;
WDF_OBJECT_ATTRIBUTES attr;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, CDeviceContextWrapper);
IDARG_IN_ADAPTER_INIT init = {};
init.WdfDevice = m_wdfDevice;
init.pCaps = &caps;
init.ObjectAttributes = &attr;
IDARG_OUT_ADAPTER_INIT initOut = {};
DEBUG_INFO("Calling IddCxAdapterInitAsync with flags 0x%08x",
caps.Flags);
NTSTATUS status = IddCxAdapterInitAsync(&init, &initOut);
if (!NT_SUCCESS(status) && CanProcessFP16())
{
DEBUG_WARN(
"IddCxAdapterInitAsync rejected FP16 adapter capabilities (0x%08x), retrying without HDR/WCG",
status);
m_canProcessFP16 = false;
// The monitor has not been created yet, so replace the provisional HDR
// EDID before Windows can observe it.
m_displayConfiguration.RebuildEdid(false);
caps.Flags = (IDDCX_ADAPTER_FLAGS)(caps.Flags & ~IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16);
ZeroMemory(&initOut, sizeof(initOut));
status = IddCxAdapterInitAsync(&init, &initOut);
}
if (!NT_SUCCESS(status))
{
DEBUG_ERROR_HR(status, "IddCxAdapterInitAsync Failed");
m_initInProgress.store(0);
return;
}
m_adapter = initOut.AdapterObject;
if (!m_adapter)
{
DEBUG_ERROR("IddCxAdapterInitAsync succeeded without returning an adapter object");
m_initInProgress.store(0);
return;
}
auto * wrapper = WdfObjectGet_CDeviceContextWrapper(m_adapter);
wrapper->context = this;
DEBUG_INFO("IddCxAdapterInitAsync started successfully (adapter %p)",
m_adapter);
DEBUG_INFO("Adapter context attached; waiting for initialization callback");
// Adapter is up; no need to keep retrying.
StopInitRetry();
m_initInProgress.store(0);
DEBUG_INFO("Adapter initialization request complete; returning to IddCx");
}
void CDeviceContext::FinishAdapterInit(UINT connectorIndex)
{
// Try to co-exist with the virtual video device by telling IddCx which
// hardware adapter we prefer to render on. Do this only after the adapter
// has finished initializing, but before adding its monitor.
if (m_havePreferredRenderAdapter)
{
IDARG_IN_ADAPTERSETRENDERADAPTER args = {};
args.PreferredRenderAdapter = m_preferredRenderAdapter;
IddCxAdapterSetRenderAdapter(m_adapter, &args);
DEBUG_INFO("Preferred render adapter set");
}
FinishInit(connectorIndex);
}
void CDeviceContext::FinishInit(UINT connectorIndex)
{
CDisplayConfiguration::Description description =
m_displayConfiguration.GetDescription();
m_monitorManager.Create(
connectorIndex, m_adapter, std::move(description.edid), this);
}
void CDeviceContext::ReplugMonitor()
{
if (m_monitorManager.Replug() ==
CMonitorManager::ReplugAction::CREATE)
FinishInit(0);
}
void CDeviceContext::ReloadSettings()
{
if (!m_displayConfiguration.ReloadSettings(
m_frameTransport.GetMemoryLimits()))
return;
ReplugMonitor();
}
void CDeviceContext::OnMonitorDestroyed(IDDCX_MONITOR monitor)
{
m_monitorManager.OnDestroyed(monitor);
}
void CDeviceContext::OnSwapChainAssigned()
{
m_monitorManager.OnSwapChainAssigned();
}
void CDeviceContext::OnSwapChainReleased()
{
m_monitorManager.OnSwapChainReleased();
}
void CDeviceContext::OnSwapChainReady()
{
const CMonitorManager::ReadyAction action =
m_monitorManager.OnSwapChainReady();
// Do not expose the context to pipe reload requests until the initial swap
// chain has reached the same ready state used by the replug gate.
g_pipe.SetDeviceContext(this);
if (action.replug)
m_monitorManager.QueueReplug();
else if (action.setMode)
g_pipe.SetDisplayMode(
action.mode.width, action.mode.height, action.mode.refreshMilliHz);
}
// Display configuration
void CDeviceContext::SetResolution(uint32_t width, uint32_t height)
{
const CDisplayConfiguration::ResolutionResult result =
m_displayConfiguration.SetResolution(
width, height, m_frameTransport.GetMemoryLimits());
switch (result.status)
{
case CDisplayConfiguration::ResolutionStatus::SUCCESS:
m_monitorManager.RequestMode(result.mode);
// IddCxMonitorUpdateModes[2] does not invalidate Windows' cached mode
// list, so depart and re-arrive the monitor to rebuild the topology.
ReplugMonitor();
break;
case CDisplayConfiguration::ResolutionStatus::TOO_LARGE:
g_pipe.ResolutionRejected(width, height, result.requiredMiB);
break;
default:
break;
}
}
// LGMP transport
bool CDeviceContext::InitializeLGMP()
{
if (m_lgmpHost.IsInitialized())
return true;
if (!m_lgmpHost.Initialize(m_ivshmem))
return false;
// Preserve the shared-memory layout: frame queues precede the pointer queue
// and its retained cursor and color-transform allocations.
if (!m_frameTransport.Initialize() || !m_lgmpControl.Initialize())
return false;
m_frameTransport.SealMemoryLayout();
return true;
}
bool CDeviceContext::SetupLGMP(size_t alignSize)
{
// Frame buffers cannot be allocated until the GPU-specific alignment is
// known. The swap-chain path may call this again after setup completed.
if (m_frameTransport.GetMaxFrameSize())
return true;
if (!InitializeLGMP() || !m_frameTransport.Setup(alignSize))
return false;
WDF_TIMER_CONFIG config;
WDF_TIMER_CONFIG_INIT_PERIODIC(&config,
[](WDFTIMER timer) -> void
{
WDFOBJECT parent = WdfTimerGetParentObject(timer);
auto wrapper = WdfObjectGet_CDeviceContextWrapper(parent);
wrapper->context->LGMPTimer();
},
10);
config.AutomaticSerialization = FALSE;
/**
* Documentation states that Dispatch is not available under UMDF, however
* using Passive returns a not-supported error and Dispatch works.
*/
WDF_OBJECT_ATTRIBUTES attribs;
WDF_OBJECT_ATTRIBUTES_INIT(&attribs);
attribs.ParentObject = m_wdfDevice;
attribs.ExecutionLevel = WdfExecutionLevelDispatch;
NTSTATUS status = WdfTimerCreate(
&config, &attribs, &m_lgmpTimer);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR_HR(status, "Timer creation failed");
return false;
}
WdfTimerStart(m_lgmpTimer, WDF_REL_TIMEOUT_IN_MS(10));
return true;
}
void CDeviceContext::LGMPTimer()
{
// Monitor work is deferred off IddCx callback threads.
switch (m_monitorManager.TakeDeferredAction())
{
case CMonitorManager::DeferredAction::CREATE:
FinishInit(0);
return;
case CMonitorManager::DeferredAction::REPLUG:
ReplugMonitor();
return;
case CMonitorManager::DeferredAction::NONE:
break;
}
const LGMP_STATUS processStatus = m_lgmpHost.Process();
if (processStatus != LGMP_OK)
{
if (processStatus == LGMP_ERR_CORRUPTED)
{
DEBUG_WARN(
"LGMP reported the shared memory has been corrupted, attempting to recover\n");
// TODO: reinitialize LGMP.
return;
}
DEBUG_ERROR("lgmpHostProcess Failed: %s",
lgmpStatusString(processStatus));
// TODO: shut down LGMP.
return;
}
const uint64_t now = CFrameScheduler::Nanotime();
// Take the frame subscriber snapshot before processing scheduling messages,
// then publish both updates together just as the original timer did.
const CFrameTransport::SubscriberSnapshot subscribers =
m_frameTransport.SnapshotSubscribers();
uint8_t data[LGMP_MSGS_SIZE];
size_t size;
uint32_t sourceClientID;
LGMP_STATUS status;
while ((status = m_lgmpControl.ReadDataWithSource(
data, &size, &sourceClientID)) == LGMP_OK)
{
KVMFRMessage * msg = reinterpret_cast<KVMFRMessage *>(data);
switch (msg->type)
{
case KVMFR_MESSAGE_SETCURSORPOS:
{
KVMFRSetCursorPos * position =
reinterpret_cast<KVMFRSetCursorPos *>(msg);
g_pipe.SetCursorPos(position->x, position->y);
break;
}
case KVMFR_MESSAGE_WINDOWSIZE:
{
KVMFRWindowSize * window =
reinterpret_cast<KVMFRWindowSize *>(msg);
SetResolution(window->w, window->h);
break;
}
case KVMFR_MESSAGE_FRAME_SCHEDULE:
{
const KVMFRFrameSchedule * schedule =
reinterpret_cast<KVMFRFrameSchedule *>(msg);
const bool valid = size == sizeof(*schedule) &&
m_frameTransport.UpdateSchedule(
sourceClientID, *schedule, now);
if (!valid)
DEBUG_WARN("Ignoring invalid KVMFR frame schedule");
break;
}
}
m_lgmpControl.AckData();
}
m_frameTransport.FinalizeSubscribers(subscribers, now);
if (m_lgmpControl.HasNewSubscribers())
m_lgmpControl.ResendState();
}

View File

@@ -0,0 +1,126 @@
/**
* 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 <Windows.h>
#include <wdf.h>
#include <IddCx.h>
#include <atomic>
#include <stddef.h>
#include <stdint.h>
#include "display/CDisplayConfiguration.h"
#include "display/CMonitorManager.h"
#include "transport/CFrameTransport.h"
#include "transport/CIVSHMEM.h"
#include "transport/CLGMPControl.h"
#include "transport/CLGMPHost.h"
class CDeviceContext
{
private:
WDFDEVICE m_wdfDevice;
IDDCX_ADAPTER m_adapter = nullptr;
LUID m_preferredRenderAdapter = {};
bool m_havePreferredRenderAdapter = false;
// At boot IVSHMEM may not have enumerated yet. The retry timer and atomic
// gate keep adapter creation single-threaded until it becomes available.
WDFTIMER m_initTimer = nullptr;
bool m_ivshmemOpened = false;
std::atomic<LONG> m_initInProgress = 0;
CIVSHMEM m_ivshmem;
CLGMPHost m_lgmpHost;
CLGMPControl m_lgmpControl;
CFrameTransport m_frameTransport;
CDisplayConfiguration m_displayConfiguration;
CMonitorManager m_monitorManager;
WDFTIMER m_lgmpTimer = nullptr;
UINT m_iddCxVersion = 0;
bool m_hasIddCx110DDIs = false;
bool m_canProcessFP16 = false;
bool m_softwareMode = true;
void QueryIddCxCapabilities();
void ScheduleInitRetry();
void StopInitRetry();
bool InitializeLGMP();
void LGMPTimer();
void SetResolution(uint32_t width, uint32_t height);
public:
explicit CDeviceContext(_In_ WDFDEVICE wdfDevice);
~CDeviceContext();
CDeviceContext(const CDeviceContext&) = delete;
CDeviceContext& operator=(const CDeviceContext&) = delete;
bool SetupLGMP(size_t alignSize);
void InitAdapter();
void FinishAdapterInit(UINT connectorIndex);
void FinishInit(UINT connectorIndex);
void ReloadSettings();
void ReplugMonitor();
void OnMonitorDestroyed(IDDCX_MONITOR monitor);
void OnSwapChainAssigned();
void OnSwapChainReleased();
void OnSwapChainReady();
bool HasIddCx110DDIs() const { return m_hasIddCx110DDIs; }
bool CanProcessFP16 () const { return m_canProcessFP16; }
bool IsSoftwareMode () const { return m_softwareMode; }
CFrameTransport& GetFrameTransport()
{
return m_frameTransport;
}
CLGMPControl& GetLGMPControl()
{
return m_lgmpControl;
}
CDisplayConfiguration& GetDisplayConfiguration()
{
return m_displayConfiguration;
}
};
struct CDeviceContextWrapper
{
CDeviceContext * context;
void Cleanup()
{
delete context;
context = nullptr;
}
};
WDF_DECLARE_CONTEXT_TYPE(CDeviceContextWrapper);

View File

@@ -18,23 +18,26 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CIndirectMonitorContext.h"
#include "display/monitor/Context.h"
#include "display/device/CDeviceContext.h"
#include "capture/CSwapChainProcessor.h"
#include "d3d/CD3D11Device.h"
#include "CDebug.h"
#include "CPipeServer.h"
CIndirectMonitorContext::CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIndirectDeviceContext * device) :
CMonitorContext::CMonitorContext(
_In_ IDDCX_MONITOR monitor, CDeviceContext * device) :
m_monitor(monitor),
m_devContext(device)
{
}
CIndirectMonitorContext::~CIndirectMonitorContext()
CMonitorContext::~CMonitorContext()
{
UnassignSwapChain();
m_devContext->OnMonitorDestroyed(m_monitor);
}
NTSTATUS CIndirectMonitorContext::AssignSwapChain(
NTSTATUS CMonitorContext::AssignSwapChain(
IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent)
{
std::lock_guard<std::mutex> assignGuard(m_assignMutex);
@@ -89,7 +92,7 @@ NTSTATUS CIndirectMonitorContext::AssignSwapChain(
return STATUS_SUCCESS;
}
void CIndirectMonitorContext::DetachSwapChain()
void CMonitorContext::DetachSwapChain()
{
// Invalidate setup in progress before waiting for m_lock. This also lets a
// worker about to call SetDevice observe an unassign whose callback is
@@ -115,7 +118,7 @@ void CIndirectMonitorContext::DetachSwapChain()
m_devContext->OnSwapChainReleased();
}
void CIndirectMonitorContext::UnassignSwapChain()
void CMonitorContext::UnassignSwapChain()
{
DetachSwapChain();
}

View File

@@ -27,12 +27,12 @@
#include <atomic>
#include <memory>
#include <mutex>
#include "CIndirectDeviceContext.h"
#include "CSwapChainProcessor.h"
using namespace Microsoft::WRL;
struct CD3D11Device;
class CDeviceContext;
class CSwapChainProcessor;
class CIndirectMonitorContext
class CMonitorContext
{
private:
IDDCX_MONITOR m_monitor;
@@ -48,7 +48,7 @@ private:
std::mutex m_assignMutex;
std::shared_ptr<CD3D11Device> m_dx11Device;
CIndirectDeviceContext * m_devContext;
CDeviceContext * m_devContext;
std::unique_ptr<CSwapChainProcessor> m_swapChain;
// Incremented whenever the current assignment is replaced or unassigned.
@@ -59,10 +59,11 @@ private:
void DetachSwapChain();
public:
CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIndirectDeviceContext * device);
CMonitorContext(
_In_ IDDCX_MONITOR monitor, CDeviceContext * device);
virtual ~CMonitorContext();
virtual ~CIndirectMonitorContext();
NTSTATUS AssignSwapChain(
IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent);
void UnassignSwapChain();
@@ -71,12 +72,12 @@ public:
return m_assignmentGeneration.load(std::memory_order_acquire) == generation;
}
CIndirectDeviceContext * GetDeviceContext() { return m_devContext; }
CDeviceContext * GetDeviceContext() { return m_devContext; }
};
struct CIndirectMonitorContextWrapper
struct CMonitorContextWrapper
{
CIndirectMonitorContext* context;
CMonitorContext * context;
void Cleanup()
{
@@ -85,4 +86,4 @@ struct CIndirectMonitorContextWrapper
}
};
WDF_DECLARE_CONTEXT_TYPE(CIndirectMonitorContextWrapper);
WDF_DECLARE_CONTEXT_TYPE(CMonitorContextWrapper);

View File

@@ -18,7 +18,7 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CPlatformInfo.h"
#include "platform/CPlatformInfo.h"
#include "CDebug.h"
#include <Windows.h>
@@ -310,4 +310,4 @@ void CPlatformInfo::InitCPUInfo()
}
_freea(buffer);
}
}

View File

@@ -48,4 +48,4 @@ public:
inline static int GetCoreCount() { return m_cores; }
inline static int GetProcCount() { return m_procs; }
inline static int GetSocketCount() { return m_sockets; }
};
};

View File

@@ -18,14 +18,14 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CPostProcessor.h"
#include "postprocess/CPostProcessor.h"
#include "CD3D12Device.h"
#include "d3d/CD3D12Device.h"
#include "CDebug.h"
#include "effect/CColorTransformEffect.h"
#include "effect/CDownsampleEffect.h"
#include "effect/CHDR16to10Effect.h"
#include "effect/CRGB24Effect.h"
#include "postprocess/effect/CColorTransformEffect.h"
#include "postprocess/effect/CDownsampleEffect.h"
#include "postprocess/effect/CHDR16to10Effect.h"
#include "postprocess/effect/CRGB24Effect.h"
#include <cstring>
#include <limits>

View File

@@ -23,15 +23,12 @@
#include <Windows.h>
#include <wrl/client.h>
#include <d3d12.h>
#include <dxgi1_5.h>
#include <memory>
#include <vector>
struct CD3D12Device;
#include "postprocess/D12FrameFormat.h"
extern "C" {
#include "common/types.h"
}
struct CD3D12Device;
using namespace Microsoft::WRL;
@@ -42,46 +39,6 @@ enum class PostProcessStatus
FAILED
};
struct D12ColorTransform
{
bool matrixEnabled = false;
float matrix[3][4] = {};
float scalar = 1.0f;
bool lutEnabled = false;
float lut[4096][4] = {};
};
bool IsIdentityColorTransform(const D12ColorTransform& transform);
struct D12FrameFormat
{
D3D12_RESOURCE_DESC desc = {};
unsigned dataWidth = 0;
unsigned dataHeight = 0;
unsigned pitch = 0;
unsigned width = 0;
unsigned height = 0;
FrameType format = FRAME_TYPE_INVALID;
bool hdr = false;
bool hdrPQ = false;
bool hdrMetadata = false;
uint32_t sdrWhiteLevel = LG_SDR_WHITE_LEVEL_DEFAULT;
std::shared_ptr<const D12ColorTransform> colorTransform;
// HDR static metadata (SMPTE ST 2086)
// Display color primaries in 0.00002 units (xy coordinates)
uint16_t displayPrimary[3][2];
// White point in 0.00002 units
uint16_t whitePoint[2];
// Max mastering display luminance in whole cd/m²
uint32_t maxDisplayLuminance;
// Min mastering display luminance in 0.0001 cd/m² units
uint32_t minDisplayLuminance;
// MaxCLL and MaxFALL in cd/m²
uint32_t maxContentLightLevel;
uint32_t maxFrameAverageLightLevel;
};
class CPostProcessEffect
{
public:

View File

@@ -0,0 +1,70 @@
/**
* 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 <Windows.h>
#include <d3d12.h>
#include <memory>
#include <stdint.h>
extern "C" {
#include "common/types.h"
}
struct D12ColorTransform
{
bool matrixEnabled = false;
float matrix[3][4] = {};
float scalar = 1.0f;
bool lutEnabled = false;
float lut[4096][4] = {};
};
bool IsIdentityColorTransform(const D12ColorTransform& transform);
struct D12FrameFormat
{
D3D12_RESOURCE_DESC desc = {};
unsigned dataWidth = 0;
unsigned dataHeight = 0;
unsigned pitch = 0;
unsigned width = 0;
unsigned height = 0;
FrameType format = FRAME_TYPE_INVALID;
bool hdr = false;
bool hdrPQ = false;
bool hdrMetadata = false;
uint32_t sdrWhiteLevel = LG_SDR_WHITE_LEVEL_DEFAULT;
std::shared_ptr<const D12ColorTransform> colorTransform;
// HDR static metadata (SMPTE ST 2086)
// Display color primaries in 0.00002 units (xy coordinates)
uint16_t displayPrimary[3][2];
// White point in 0.00002 units
uint16_t whitePoint[2];
// Max mastering display luminance in whole cd/m²
uint32_t maxDisplayLuminance;
// Min mastering display luminance in 0.0001 cd/m² units
uint32_t minDisplayLuminance;
// MaxCLL and MaxFALL in cd/m²
uint32_t maxContentLightLevel;
uint32_t maxFrameAverageLightLevel;
};

View File

@@ -20,7 +20,7 @@
#pragma once
#include "../CPostProcessor.h"
#include "postprocess/CPostProcessor.h"
#define POST_PROCESS_THREADS_STR "8"

View File

@@ -21,7 +21,7 @@
#include "CDownsampleEffect.h"
#include "CDebug.h"
#include "../CSettings.h"
#include "config/CSettings.h"
#include <algorithm>
#include <cmath>

View File

@@ -21,7 +21,7 @@
#include "CRGB24Effect.h"
#include "CDebug.h"
#include "../CSettings.h"
#include "config/CSettings.h"
#include "common/LGMPConfig.h"
#include <algorithm>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,209 @@
/**
* 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 <Windows.h>
#include <atomic>
#include <stdint.h>
extern "C" {
#include "lgmp/host.h"
}
#include "capture/CFrameScheduler.h"
#include "capture/FrameBufferTypes.h"
#include "common/KVMFR.h"
#include "postprocess/D12FrameFormat.h"
#include "transport/FrameMemoryLimits.h"
class CIVSHMEM;
class CLGMPHost;
class CFrameTransport
{
public:
struct SubscriberSnapshot
{
uint32_t clientIDs [LGMP_MAX_CLIENTS] = {};
uint32_t ownerClientIDs[LGMP_MAX_CLIENTS] = {};
unsigned clientCount = 0;
unsigned ownerClientCount = 0;
LGMP_STATUS status = LGMP_OK;
};
private:
enum SharedFramePostResult
{
SHARED_FRAME_FAILED,
SHARED_FRAME_IDLE,
SHARED_FRAME_POSTED,
};
struct FrameDelivery
{
uint64_t sharedOwnerToken = 0;
unsigned ownerQueueMask = 0;
uint32_t sharedOwnerClientID = 0;
bool sharedOwnerPending = false;
bool sharedPending = false;
};
struct OwnerDelivery
{
uint64_t token = 0;
uint32_t clientID = 0;
unsigned frameIndex = 0;
bool active = false;
};
CLGMPHost& m_host;
CIVSHMEM& m_ivshmem;
PLGMPHostQueue m_frameQueue = nullptr;
PLGMPHostQueue m_frameOwnerQueue[LGMP_Q_FRAME_LEN] = {};
CFrameScheduler m_frameScheduler;
size_t m_alignSize = 0;
size_t m_frameMemoryOffset = 0;
size_t m_maxFrameSize = 0;
// LGMP publication precedes copy completion. Replay only completed frames;
// the deferred index tracks the newest frame still owed to the owner.
std::atomic<LONG> m_submittedFrameIndex = -1;
std::atomic<LONG> m_readyFrameIndex = -1;
LONG m_deferredOwnerFrameIndex = -1;
std::atomic<bool> m_frameInFlight[LGMP_Q_FRAME_BUFFER_LEN] = {};
bool m_frameCompleted[LGMP_Q_FRAME_BUFFER_LEN] = {};
SRWLOCK m_framePublishLock = SRWLOCK_INIT;
uint64_t m_framePublishSequence = 0;
uint64_t m_frameLastPublishSequence[LGMP_Q_FRAME_BUFFER_LEN] = {};
FrameDelivery m_frameDelivery[LGMP_Q_FRAME_BUFFER_LEN] = {};
OwnerDelivery m_ownerDelivery[LGMP_Q_FRAME_LEN] = {};
uint32_t m_formatVer = 0;
uint32_t m_frameSerial = 0;
PLGMPMemory m_frameMemory[LGMP_Q_FRAME_BUFFER_LEN] = {};
KVMFRFrame * m_frame [LGMP_Q_FRAME_BUFFER_LEN] = {};
FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_BUFFER_LEN] = {};
unsigned m_width = 0;
unsigned m_height = 0;
unsigned m_frameWidth = 0;
unsigned m_frameHeight = 0;
unsigned m_pitch = 0;
DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN;
FrameType m_frameType = FRAME_TYPE_INVALID;
// Previous HDR metadata used to detect changes for formatVer bumps.
uint16_t m_lastHDRDisplayPrimary[3][2] = {};
uint16_t m_lastHDRWhitePoint[2] = {};
uint32_t m_lastHDRMaxDisplayLuminance = 0;
uint32_t m_lastHDRMinDisplayLuminance = 0;
uint32_t m_lastHDRMaxContentLightLevel = 0;
uint32_t m_lastHDRMaxFrameAverageLightLevel = 0;
uint32_t m_lastSDRWhiteLevel = 0;
bool m_lastHDRActive = false;
bool m_lastHDRMetadata = false;
void ProcessFrameDeliveries();
bool FrameBufferReferenced(unsigned frameIndex) const;
int FindAvailableFrameBuffer(bool allowReady) const;
int FindNewestCompletedFrame(unsigned excludeFrameIndex) const;
int FindAvailableOwnerQueue(unsigned preferredIndex) const;
unsigned CountOwnerDeliveries(uint32_t clientID) const;
bool HasMatchingOwnerDelivery(uint32_t clientID, unsigned frameIndex,
uint64_t token) const;
SharedFramePostResult PostSharedFrame(unsigned frameIndex,
uint32_t excludeClientID, uint64_t now);
bool PostSharedOwnerFrame(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule);
public:
CFrameTransport(CLGMPHost& host, CIVSHMEM& ivshmem);
~CFrameTransport();
CFrameTransport(const CFrameTransport&) = delete;
CFrameTransport& operator=(const CFrameTransport&) = delete;
bool Initialize();
void SealMemoryLayout();
bool Setup(size_t alignSize);
void DeInit();
FrameMemoryLimits GetMemoryLimits() const;
size_t GetMaxFrameSize() const { return m_maxFrameSize; }
CIVSHMEM& GetIVSHMEM() { return m_ivshmem; }
SubscriberSnapshot SnapshotSubscribers() const;
void FinalizeSubscribers(
const SubscriberSnapshot& snapshot, uint64_t now);
bool UpdateSchedule(uint32_t sourceClientID,
const KVMFRFrameSchedule& schedule, uint64_t now);
bool FrameBufferAvailable(const CFrameScheduler::Schedule& schedule,
bool allowReadyReplacement = true);
bool HasPublishedFrame() const
{
return m_readyFrameIndex.load(std::memory_order_acquire) >= 0;
}
void ProcessFrameQueue();
bool GetSharedFrameTarget(uint64_t now, uint64_t& target);
bool ReplaySharedFrame(uint64_t now, bool& retry);
PreparedFrameBuffer PrepareFrameBuffer(unsigned pitch,
const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat,
const RECT * dirtyRects, unsigned nbDirtyRects,
const CFrameScheduler::Schedule& schedule,
bool allowReadyReplacement = true);
bool PublishFrameBuffer(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule, bool& deliveredToOwner);
bool RepublishFrameBuffer(const CFrameScheduler::Schedule& schedule);
bool TryFrameSubmitted(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule);
void CommitFrameBuffer(unsigned frameIndex,
const CFrameScheduler::Schedule& schedule, bool periodic,
bool deliveredToOwner);
void AbortFrameBuffer(unsigned frameIndex);
void FailFrameBuffer(unsigned frameIndex);
void CompleteFrameBuffer(unsigned frameIndex, bool succeeded);
void SetFrameTiming(unsigned frameIndex, uint64_t captureTime,
uint64_t postProcessTime, uint64_t copyTime, uint64_t readyTime,
uint64_t holdTime, const CFrameScheduler::Schedule& schedule,
uint64_t completedAt);
void WriteFrameBuffer(unsigned frameIndex, void * src, size_t offset,
size_t len, bool setWritePos) const;
void WriteFrameBufferRows(unsigned frameIndex, void * src,
size_t offset, size_t rowBytes, size_t pitch, unsigned rows) const;
void FinalizeFrameBuffer(unsigned frameIndex) const;
void ObserveFrame(uint64_t now);
void ForceFrame();
bool GetPublishTarget(uint64_t now, uint64_t& target,
CFrameScheduler::Schedule& schedule, bool& periodic, bool& republish);
void FrameMissed(const CFrameScheduler::Schedule& schedule,
uint64_t now, bool periodic);
void FrameSuperseded();
HANDLE GetFrameScheduleEvent() const
{
return m_frameScheduler.GetWakeEvent();
}
void TryRecordFrameTiming(uint64_t duration);
};

View File

@@ -18,7 +18,7 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CIVSHMEM.h"
#include "transport/CIVSHMEM.h"
#include <Windows.h>
#include <SetupAPI.h>
@@ -205,4 +205,4 @@ void CIVSHMEM::Close()
m_size = 0;
m_mem = nullptr;
}
}

View File

@@ -0,0 +1,301 @@
/**
* 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/CLGMPControl.h"
#include "CDebug.h"
#include <string.h>
#include <utility>
static const uint32_t MAX_POINTER_SIZE =
(uint32_t)(sizeof(KVMFRCursor) + (512 * 512 * 4));
static const struct LGMPQueueConfig POINTER_QUEUE_CONFIG =
{
LGMP_Q_POINTER, //queueID
LGMP_Q_POINTER_LEN, //numMesages
1000 //subTimeout
};
CLGMPControl::~CLGMPControl()
{
DeInit();
}
bool CLGMPControl::Initialize()
{
if (m_pointerQueue)
return true;
LGMP_STATUS status;
if ((status = m_host.CreateQueue(
POINTER_QUEUE_CONFIG, &m_pointerQueue)) != LGMP_OK)
{
DEBUG_ERROR("lgmpHostQueueCreate Failed (Pointer): %s",
lgmpStatusString(status));
return false;
}
for (int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
{
if ((status = m_host.Allocate(
MAX_POINTER_SIZE, &m_pointerMemory[i])) != LGMP_OK)
{
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer): %s",
lgmpStatusString(status));
return false;
}
memset(lgmpHostMemPtr(m_pointerMemory[i]), 0, MAX_POINTER_SIZE);
}
for (int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
{
if ((status = m_host.Allocate(
MAX_POINTER_SIZE, &m_pointerShapeMemory[i])) != LGMP_OK)
{
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer Shapes): %s",
lgmpStatusString(status));
return false;
}
memset(lgmpHostMemPtr(m_pointerShapeMemory[i]), 0, MAX_POINTER_SIZE);
}
for (int i = 0; i < COLOR_TRANSFORM_BUFFERS; ++i)
{
if ((status = m_host.Allocate(
sizeof(KVMFRCursor) + sizeof(KVMFRColorTransform),
&m_pointerTransformMemory[i])) != LGMP_OK)
{
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer Transform): %s",
lgmpStatusString(status));
return false;
}
memset(lgmpHostMemPtr(m_pointerTransformMemory[i]), 0,
sizeof(KVMFRCursor) + sizeof(KVMFRColorTransform));
}
return true;
}
void CLGMPControl::DeInit()
{
for (int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
lgmpHostMemFree(&m_pointerMemory[i]);
for (int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
lgmpHostMemFree(&m_pointerShapeMemory[i]);
for (int i = 0; i < COLOR_TRANSFORM_BUFFERS; ++i)
lgmpHostMemFree(&m_pointerTransformMemory[i]);
m_pointerQueue = nullptr;
m_pointerShape = nullptr;
m_pointerMemoryIndex = 0;
m_pointerShapeIndex = 0;
m_pointerTransformIndex = 0;
}
LGMP_STATUS CLGMPControl::ReadDataWithSource(void * data, size_t * size,
uint32_t * sourceClientID)
{
return lgmpHostReadDataWithSource(
m_pointerQueue, data, size, sourceClientID);
}
LGMP_STATUS CLGMPControl::AckData()
{
return lgmpHostAckData(m_pointerQueue);
}
bool CLGMPControl::HasNewSubscribers()
{
return lgmpHostQueueNewSubs(m_pointerQueue) != 0;
}
void CLGMPControl::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
const BYTE * data, UINT sdrWhiteLevel)
{
PLGMPMemory mem;
if (info.CursorShapeInfo.CursorType == IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
{
mem = m_pointerMemory[m_pointerMemoryIndex];
if (++m_pointerMemoryIndex == LGMP_Q_POINTER_LEN)
m_pointerMemoryIndex = 0;
}
else
{
mem = m_pointerShapeMemory[m_pointerShapeIndex];
if (++m_pointerShapeIndex == POINTER_SHAPE_BUFFERS)
m_pointerShapeIndex = 0;
}
KVMFRCursor * cursor = (KVMFRCursor *)lgmpHostMemPtr(mem);
cursor->sdrWhiteLevel = sdrWhiteLevel ?
sdrWhiteLevel : KVMFR_SDR_WHITE_LEVEL_DEFAULT;
m_cursorVisible = info.IsCursorVisible;
uint32_t flags = CURSOR_FLAG_VISIBLE_VALID;
if (info.IsCursorVisible)
{
m_cursorX = info.X;
m_cursorY = info.Y;
cursor->x = (int16_t)info.X;
cursor->y = (int16_t)info.Y;
flags |= CURSOR_FLAG_POSITION | CURSOR_FLAG_VISIBLE;
}
if (info.CursorShapeInfo.CursorType != IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
{
memcpy(cursor + 1, data,
(size_t)info.CursorShapeInfo.Height * info.CursorShapeInfo.Pitch);
cursor->hx = (int8_t )info.CursorShapeInfo.XHot;
cursor->hy = (int8_t )info.CursorShapeInfo.YHot;
cursor->width = (uint32_t)info.CursorShapeInfo.Width;
cursor->height = (uint32_t)info.CursorShapeInfo.Height;
cursor->pitch = (uint32_t)info.CursorShapeInfo.Pitch;
switch (info.CursorShapeInfo.CursorType)
{
case IDDCX_CURSOR_SHAPE_TYPE_ALPHA:
cursor->type = CURSOR_TYPE_COLOR;
break;
case IDDCX_CURSOR_SHAPE_TYPE_MASKED_COLOR:
cursor->type = CURSOR_TYPE_MASKED_COLOR;
break;
}
flags |= CURSOR_FLAG_SHAPE;
m_pointerShape = mem;
}
LGMP_STATUS status;
while ((status = lgmpHostQueuePost(
m_pointerQueue, flags, mem)) != LGMP_OK)
{
if (status == LGMP_ERR_QUEUE_FULL)
{
Sleep(1);
continue;
}
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s",
lgmpStatusString(status));
break;
}
}
void CLGMPControl::SetColorTransform(
std::shared_ptr<const D12ColorTransform> transform)
{
AcquireSRWLockExclusive(&m_colorTransformLock);
m_colorTransform = std::move(transform);
ReleaseSRWLockExclusive(&m_colorTransformLock);
SendColorTransform();
}
std::shared_ptr<const D12ColorTransform>
CLGMPControl::GetColorTransform() const
{
AcquireSRWLockShared(&m_colorTransformLock);
std::shared_ptr<const D12ColorTransform> transform = m_colorTransform;
ReleaseSRWLockShared(&m_colorTransformLock);
return transform;
}
void CLGMPControl::SendColorTransform()
{
if (!m_pointerQueue || !m_pointerTransformMemory[0])
return;
PLGMPMemory mem = m_pointerTransformMemory[m_pointerTransformIndex];
if (++m_pointerTransformIndex == COLOR_TRANSFORM_BUFFERS)
m_pointerTransformIndex = 0;
KVMFRCursor * cursor = (KVMFRCursor *)lgmpHostMemPtr(mem);
KVMFRColorTransform * output =
(KVMFRColorTransform *)(cursor + 1);
const auto transform = GetColorTransform();
output->flags = 0;
if (transform)
{
if (transform->matrixEnabled)
output->flags |= KVMFR_COLOR_TRANSFORM_MATRIX;
if (transform->lutEnabled)
output->flags |= KVMFR_COLOR_TRANSFORM_LUT;
memcpy(output->matrix, transform->matrix, sizeof(output->matrix));
output->scalar = transform->scalar;
memcpy(output->lut, transform->lut, sizeof(output->lut));
}
LGMP_STATUS status;
while ((status = lgmpHostQueuePost(m_pointerQueue,
CURSOR_FLAG_COLOR_TRANSFORM, mem)) != LGMP_OK)
{
if (status == LGMP_ERR_QUEUE_FULL)
{
Sleep(1);
continue;
}
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer Transform): %s",
lgmpStatusString(status));
break;
}
}
void CLGMPControl::ResendCursor()
{
PLGMPMemory mem = m_pointerShape;
if (!mem)
return;
KVMFRCursor* cursor = (KVMFRCursor*)lgmpHostMemPtr(mem);
cursor->x = (int16_t)m_cursorX;
cursor->y = (int16_t)m_cursorY;
const uint32_t flags =
CURSOR_FLAG_POSITION | CURSOR_FLAG_SHAPE | CURSOR_FLAG_VISIBLE_VALID |
(m_cursorVisible ? CURSOR_FLAG_VISIBLE : 0);
LGMP_STATUS status;
while ((status = lgmpHostQueuePost(
m_pointerQueue, flags, mem)) != LGMP_OK)
{
if (status == LGMP_ERR_QUEUE_FULL)
{
Sleep(1);
continue;
}
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s",
lgmpStatusString(status));
break;
}
}
void CLGMPControl::ResendState()
{
ResendCursor();
SendColorTransform();
}

View File

@@ -0,0 +1,82 @@
/**
* 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 "transport/CLGMPHost.h"
#include "postprocess/D12FrameFormat.h"
#include "common/KVMFR.h"
#include <Windows.h>
#include <wdf.h>
#include <IddCx.h>
#include <memory>
class CLGMPControl
{
private:
static constexpr int POINTER_SHAPE_BUFFERS = 3;
static constexpr int COLOR_TRANSFORM_BUFFERS = 3;
CLGMPHost& m_host;
PLGMPHostQueue m_pointerQueue = nullptr;
PLGMPMemory m_pointerMemory[LGMP_Q_POINTER_LEN] = {};
PLGMPMemory m_pointerShapeMemory[POINTER_SHAPE_BUFFERS] = {};
PLGMPMemory m_pointerTransformMemory[COLOR_TRANSFORM_BUFFERS] = {};
PLGMPMemory m_pointerShape = nullptr;
int m_pointerMemoryIndex = 0;
int m_pointerShapeIndex = 0;
int m_pointerTransformIndex = 0;
bool m_cursorVisible = false;
int m_cursorX = 0;
int m_cursorY = 0;
mutable SRWLOCK m_colorTransformLock = SRWLOCK_INIT;
std::shared_ptr<const D12ColorTransform> m_colorTransform;
void SendColorTransform();
void ResendCursor();
public:
explicit CLGMPControl(CLGMPHost& host) :
m_host(host) {}
~CLGMPControl();
CLGMPControl(const CLGMPControl&) = delete;
CLGMPControl& operator=(const CLGMPControl&) = delete;
bool Initialize();
void DeInit();
LGMP_STATUS ReadDataWithSource(void * data, size_t * size,
uint32_t * sourceClientID);
LGMP_STATUS AckData();
bool HasNewSubscribers();
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, const BYTE * data,
UINT sdrWhiteLevel);
void SetColorTransform(
std::shared_ptr<const D12ColorTransform> transform);
std::shared_ptr<const D12ColorTransform> GetColorTransform() const;
void ResendState();
};

View File

@@ -0,0 +1,165 @@
/**
* 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/CLGMPHost.h"
#include "transport/CIVSHMEM.h"
#include "platform/CPlatformInfo.h"
#include "CDebug.h"
#include "VersionInfo.h"
#include "common/KVMFR.h"
#include <stdlib.h>
#include <string.h>
#include <sstream>
#include <string>
CLGMPHost::~CLGMPHost()
{
DeInit();
}
bool CLGMPHost::Initialize(CIVSHMEM& ivshmem)
{
if (m_host)
return true;
std::stringstream ss;
{
KVMFR kvmfr = {};
memcpy_s(kvmfr.magic, sizeof(kvmfr.magic), KVMFR_MAGIC, sizeof(KVMFR_MAGIC) - 1);
kvmfr.version = KVMFR_VERSION;
kvmfr.features =
KVMFR_FEATURE_SETCURSORPOS |
KVMFR_FEATURE_WINDOWSIZE |
KVMFR_FEATURE_FRAME_SCHEDULE;
strncpy_s(kvmfr.hostver, LG_VERSION_STR, sizeof(kvmfr.hostver) - 1);
ss.write(reinterpret_cast<const char *>(&kvmfr), sizeof(kvmfr));
}
{
const std::string & model = CPlatformInfo::GetCPUModel();
KVMFRRecord_VMInfo * vmInfo = static_cast<KVMFRRecord_VMInfo *>(calloc(1, sizeof(*vmInfo)));
if (!vmInfo)
{
DEBUG_ERROR("Failed to allocate KVMFRRecord_VMInfo");
return false;
}
vmInfo->cpus = static_cast<uint8_t>(CPlatformInfo::GetProcCount ());
vmInfo->cores = static_cast<uint8_t>(CPlatformInfo::GetCoreCount ());
vmInfo->sockets = static_cast<uint8_t>(CPlatformInfo::GetSocketCount());
const uint8_t * uuid = CPlatformInfo::GetUUID();
memcpy_s (vmInfo->uuid, sizeof(vmInfo->uuid), uuid, 16);
strncpy_s(vmInfo->capture, "Looking Glass IDD Driver", sizeof(vmInfo->capture));
KVMFRRecord * record = static_cast<KVMFRRecord *>(calloc(1, sizeof(*record)));
if (!record)
{
DEBUG_ERROR("Failed to allocate KVMFRRecord");
return false;
}
record->type = KVMFR_RECORD_VMINFO;
record->size = sizeof(*vmInfo) + (uint32_t)model.length() + 1;
ss.write(reinterpret_cast<const char*>(record ), sizeof(*record));
ss.write(reinterpret_cast<const char*>(vmInfo ), sizeof(*vmInfo));
ss.write(reinterpret_cast<const char*>(model.c_str()), model.length() + 1);
}
{
KVMFRRecord_OSInfo * osInfo = static_cast<KVMFRRecord_OSInfo *>(calloc(1, sizeof(*osInfo)));
if (!osInfo)
{
DEBUG_ERROR("Failed to allocate KVMFRRecord_OSInfo");
return false;
}
osInfo->os = KVMFR_OS_WINDOWS;
const std::string & osName = CPlatformInfo::GetProductName();
KVMFRRecord* record = static_cast<KVMFRRecord*>(calloc(1, sizeof(*record)));
if (!record)
{
DEBUG_ERROR("Failed to allocate KVMFRRecord");
return false;
}
record->type = KVMFR_RECORD_OSINFO;
record->size = sizeof(*osInfo) + (uint32_t)osName.length() + 1;
ss.write(reinterpret_cast<const char*>(record), sizeof(*record));
ss.write(reinterpret_cast<const char*>(osInfo), sizeof(*osInfo));
ss.write(reinterpret_cast<const char*>(osName.c_str()), osName.length() + 1);
}
LGMP_STATUS status;
std::string udata = ss.str();
if ((status = lgmpHostInit(ivshmem.GetMem(), (uint32_t)ivshmem.GetSize(),
&m_host, (uint32_t)udata.size(), (uint8_t*)&udata[0])) != LGMP_OK)
{
DEBUG_ERROR("lgmpHostInit Failed: %s", lgmpStatusString(status));
return false;
}
return true;
}
void CLGMPHost::DeInit()
{
if (m_host)
lgmpHostFree(&m_host);
}
LGMP_STATUS CLGMPHost::Process()
{
AcquireSRWLockExclusive(&m_processLock);
const LGMP_STATUS status = lgmpHostProcess(m_host);
ReleaseSRWLockExclusive(&m_processLock);
return status;
}
LGMP_STATUS CLGMPHost::CreateQueue(
const struct LGMPQueueConfig& config, PLGMPHostQueue * queue)
{
return lgmpHostQueueNew(m_host, config, queue);
}
LGMP_STATUS CLGMPHost::Allocate(uint32_t size, PLGMPMemory * memory)
{
return lgmpHostMemAlloc(m_host, size, memory);
}
LGMP_STATUS CLGMPHost::AllocateAligned(uint32_t size,
uint32_t alignment, PLGMPMemory * memory)
{
return lgmpHostMemAllocAligned(m_host, size, alignment, memory);
}
size_t CLGMPHost::Available() const
{
return lgmpHostMemAvail(m_host);
}

View File

@@ -0,0 +1,59 @@
/**
* 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 <Windows.h>
#include <stddef.h>
#include <stdint.h>
extern "C" {
#include "lgmp/host.h"
}
class CIVSHMEM;
class CLGMPHost
{
private:
PLGMPHost m_host = nullptr;
SRWLOCK m_processLock = SRWLOCK_INIT;
public:
CLGMPHost() = default;
~CLGMPHost();
CLGMPHost(const CLGMPHost&) = delete;
CLGMPHost& operator=(const CLGMPHost&) = delete;
bool Initialize(CIVSHMEM& ivshmem);
void DeInit();
bool IsInitialized() const { return m_host != nullptr; }
LGMP_STATUS Process();
LGMP_STATUS CreateQueue(const struct LGMPQueueConfig& config,
PLGMPHostQueue * queue);
LGMP_STATUS Allocate(uint32_t size, PLGMPMemory * memory);
LGMP_STATUS AllocateAligned(uint32_t size, uint32_t alignment,
PLGMPMemory * memory);
size_t Available() const;
};

View File

@@ -18,9 +18,9 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "CPipeServer.h"
#include "transport/CPipeServer.h"
#include "CDebug.h"
#include "CIndirectDeviceContext.h"
#include "display/device/CDeviceContext.h"
CPipeServer g_pipe;
@@ -266,7 +266,7 @@ void CPipeServer::HandleReloadSettings()
ReleaseSRWLockShared(&m_deviceContextLock);
}
void CPipeServer::SetDeviceContext(CIndirectDeviceContext* context)
void CPipeServer::SetDeviceContext(CDeviceContext * context)
{
AcquireSRWLockExclusive(&m_deviceContextLock);
m_deviceContext = context;

View File

@@ -32,7 +32,7 @@ using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace Microsoft::WRL::Wrappers::HandleTraits;
class CIndirectDeviceContext;
class CDeviceContext;
class CPipeServer
{
@@ -45,8 +45,8 @@ class CPipeServer
bool m_running = false;
bool m_connected = false;
SRWLOCK m_deviceContextLock = SRWLOCK_INIT;
CIndirectDeviceContext* m_deviceContext = nullptr;
SRWLOCK m_deviceContextLock = SRWLOCK_INIT;
CDeviceContext * m_deviceContext = nullptr;
void _DeInit();
@@ -63,7 +63,7 @@ class CPipeServer
bool Init();
void DeInit();
void SetDeviceContext(CIndirectDeviceContext* context);
void SetDeviceContext(CDeviceContext * context);
void SetCursorPos(uint32_t x, uint32_t y);
void SetDisplayMode(

View File

@@ -0,0 +1,31 @@
/**
* 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 <stdint.h>
struct FrameMemoryLimits
{
uint64_t sharedSize = 0;
uint64_t frameMemoryOffset = 0;
uint64_t alignment = 0;
uint64_t maxFrameSize = 0;
};

63
idd/LGIdd/util/CSRWLock.h Normal file
View File

@@ -0,0 +1,63 @@
/**
* 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 <Windows.h>
class CSRWSharedLock
{
private:
SRWLOCK * m_lock;
public:
explicit CSRWSharedLock(SRWLOCK * lock) : m_lock(lock)
{
AcquireSRWLockShared(m_lock);
}
~CSRWSharedLock()
{
ReleaseSRWLockShared(m_lock);
}
CSRWSharedLock(const CSRWSharedLock&) = delete;
CSRWSharedLock& operator=(const CSRWSharedLock&) = delete;
};
class CSRWExclusiveLock
{
private:
SRWLOCK * m_lock;
public:
explicit CSRWExclusiveLock(SRWLOCK * lock) : m_lock(lock)
{
AcquireSRWLockExclusive(m_lock);
}
~CSRWExclusiveLock()
{
ReleaseSRWLockExclusive(m_lock);
}
CSRWExclusiveLock(const CSRWExclusiveLock&) = delete;
CSRWExclusiveLock& operator=(const CSRWExclusiveLock&) = delete;
};