mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[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:
1296
idd/LGIdd/transport/CFrameTransport.cpp
Normal file
1296
idd/LGIdd/transport/CFrameTransport.cpp
Normal file
File diff suppressed because it is too large
Load Diff
209
idd/LGIdd/transport/CFrameTransport.h
Normal file
209
idd/LGIdd/transport/CFrameTransport.h
Normal 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);
|
||||
};
|
||||
208
idd/LGIdd/transport/CIVSHMEM.cpp
Normal file
208
idd/LGIdd/transport/CIVSHMEM.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* 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/CIVSHMEM.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <SetupAPI.h>
|
||||
#include <algorithm>
|
||||
#include <winioctl.h>
|
||||
|
||||
#include "CDebug.h"
|
||||
#include "ivshmem/ivshmem.h"
|
||||
|
||||
CIVSHMEM::CIVSHMEM()
|
||||
{
|
||||
}
|
||||
|
||||
CIVSHMEM::~CIVSHMEM()
|
||||
{
|
||||
if (m_handle == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
Close();
|
||||
CloseHandle(m_handle);
|
||||
}
|
||||
|
||||
bool CIVSHMEM::Init()
|
||||
{
|
||||
// Init may be called more than once (the adapter init is retried at boot
|
||||
// until IVSHMEM enumerates). Release any handle from a prior attempt so we
|
||||
// do not leak it when re-enumerating.
|
||||
if (m_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Close();
|
||||
CloseHandle(m_handle);
|
||||
m_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
HDEVINFO devInfoSet;
|
||||
SP_DEVINFO_DATA devInfoData;
|
||||
SP_DEVICE_INTERFACE_DATA devInterfaceData;
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA infData = nullptr;
|
||||
|
||||
devInfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IVSHMEM, nullptr, nullptr,
|
||||
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||
|
||||
devInfoData.cbSize = sizeof(devInfoData);
|
||||
devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
m_devices.clear();
|
||||
for (int i = 0; SetupDiEnumDeviceInfo(devInfoSet, i, &devInfoData); ++i)
|
||||
{
|
||||
DWORD bus, addr;
|
||||
if (!SetupDiGetDeviceRegistryProperty(devInfoSet, &devInfoData, SPDRP_BUSNUMBER,
|
||||
nullptr, (BYTE*)&bus, sizeof(bus), nullptr))
|
||||
bus = 0xffff;
|
||||
|
||||
if (!SetupDiGetDeviceRegistryProperty(devInfoSet, &devInfoData, SPDRP_ADDRESS,
|
||||
nullptr, (BYTE*)&addr, sizeof(addr), nullptr))
|
||||
addr = 0xffff;
|
||||
|
||||
IVSHMEMData data;
|
||||
data.busAddr = ((DWORD64)bus) << 32 | addr;
|
||||
memcpy(&data.devInfoData, &devInfoData, sizeof(devInfoData));
|
||||
m_devices.push_back(data);
|
||||
}
|
||||
|
||||
HRESULT hr = GetLastError();
|
||||
if (hr != ERROR_NO_MORE_ITEMS)
|
||||
{
|
||||
m_devices.clear();
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
DEBUG_ERROR_HR(hr, "Enumeration Failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::sort(m_devices.begin(), m_devices.end(),
|
||||
[](const IVSHMEMData & a, const IVSHMEMData & b) -> bool
|
||||
{ return a.busAddr < b.busAddr; });
|
||||
|
||||
|
||||
HKEY hkeyLG;
|
||||
IVSHMEMData * device = nullptr;
|
||||
DWORD shmDevice = 0;
|
||||
|
||||
if (RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Looking Glass", &hkeyLG) == ERROR_SUCCESS)
|
||||
{
|
||||
DWORD dataType;
|
||||
DWORD dataSize = sizeof(shmDevice);
|
||||
if (RegQueryValueExA(hkeyLG, "shmDevice", nullptr, &dataType, (BYTE*)&shmDevice, &dataSize) != ERROR_SUCCESS ||
|
||||
dataType != REG_DWORD)
|
||||
shmDevice = 0;
|
||||
}
|
||||
|
||||
DWORD i = 0;
|
||||
for (auto it = m_devices.begin(); it != m_devices.end(); ++it, ++i)
|
||||
{
|
||||
DWORD bus = it->busAddr >> 32;
|
||||
DWORD addr = it->busAddr & 0xFFFFFFFF;
|
||||
DEBUG_INFO("IVSHMEM %u%c on bus 0x%lx, device 0x%lx, function 0x%lx",
|
||||
i, i == shmDevice ? '*' : ' ', bus, addr >> 16, addr & 0xFFFF);
|
||||
|
||||
if (i == shmDevice)
|
||||
device = &(*it);
|
||||
}
|
||||
|
||||
if (!device)
|
||||
{
|
||||
DEBUG_ERROR("Failed to match a shmDevice");
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SetupDiEnumDeviceInterfaces(devInfoSet, &devInfoData, &GUID_DEVINTERFACE_IVSHMEM, 0, &devInterfaceData) == FALSE)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "SetupDiEnumDeviceInterfaces");
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD reqSize = 0;
|
||||
SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, nullptr, 0, &reqSize, nullptr);
|
||||
if (!reqSize)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "SetupDiGetDeviceInterfaceDetail");
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
return false;
|
||||
}
|
||||
|
||||
infData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)calloc(1, reqSize);
|
||||
infData->cbSize = sizeof(PSP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
if (!SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, infData, reqSize, nullptr, nullptr))
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "SetupDiGetDeviceInterfaceDetail");
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_handle = CreateFile(infData->DevicePath, 0, 0, nullptr, OPEN_EXISTING, 0, 0);
|
||||
if (m_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "CreateFile");
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
return false;
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||
DEBUG_TRACE("IVSHMEM Initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CIVSHMEM::Open()
|
||||
{
|
||||
IVSHMEM_SIZE size;
|
||||
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_REQUEST_SIZE, nullptr, 0, &size, sizeof(size), nullptr, nullptr))
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to request ivshmem size");
|
||||
return false;
|
||||
}
|
||||
|
||||
IVSHMEM_MMAP_CONFIG config = {};
|
||||
IVSHMEM_MMAP map = {};
|
||||
|
||||
config.cacheMode = IVSHMEM_CACHE_WRITECOMBINED;
|
||||
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_REQUEST_MMAP, &config, sizeof(config), &map, sizeof(map), nullptr, nullptr))
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to request ivshmem mmap");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_size = (size_t)size;
|
||||
m_mem = map.ptr;
|
||||
|
||||
DEBUG_INFO("IVSHMEM opened, size: %zu MiB", m_size / 1048576);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CIVSHMEM::Close()
|
||||
{
|
||||
if (m_mem == nullptr)
|
||||
return;
|
||||
|
||||
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_RELEASE_MMAP, nullptr, 0, nullptr, 0, nullptr, nullptr))
|
||||
{
|
||||
DEBUG_ERROR("Failed to release ivshmem mmap");
|
||||
return;
|
||||
}
|
||||
|
||||
m_size = 0;
|
||||
m_mem = nullptr;
|
||||
}
|
||||
51
idd/LGIdd/transport/CIVSHMEM.h
Normal file
51
idd/LGIdd/transport/CIVSHMEM.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 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 <SetupAPI.h>
|
||||
#include <vector>
|
||||
|
||||
class CIVSHMEM
|
||||
{
|
||||
private:
|
||||
struct IVSHMEMData
|
||||
{
|
||||
SP_DEVINFO_DATA devInfoData;
|
||||
DWORD64 busAddr;
|
||||
};
|
||||
|
||||
std::vector<struct IVSHMEMData> m_devices;
|
||||
HANDLE m_handle = INVALID_HANDLE_VALUE;
|
||||
size_t m_size = 0;
|
||||
void * m_mem = nullptr;
|
||||
|
||||
public:
|
||||
CIVSHMEM();
|
||||
~CIVSHMEM();
|
||||
|
||||
bool Init();
|
||||
bool Open();
|
||||
void Close();
|
||||
|
||||
size_t GetSize() const { return m_size; }
|
||||
void * GetMem () { return m_mem; }
|
||||
};
|
||||
301
idd/LGIdd/transport/CLGMPControl.cpp
Normal file
301
idd/LGIdd/transport/CLGMPControl.cpp
Normal 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();
|
||||
}
|
||||
82
idd/LGIdd/transport/CLGMPControl.h
Normal file
82
idd/LGIdd/transport/CLGMPControl.h
Normal 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();
|
||||
};
|
||||
165
idd/LGIdd/transport/CLGMPHost.cpp
Normal file
165
idd/LGIdd/transport/CLGMPHost.cpp
Normal 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);
|
||||
}
|
||||
59
idd/LGIdd/transport/CLGMPHost.h
Normal file
59
idd/LGIdd/transport/CLGMPHost.h
Normal 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;
|
||||
};
|
||||
321
idd/LGIdd/transport/CPipeServer.cpp
Normal file
321
idd/LGIdd/transport/CPipeServer.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
/**
|
||||
* 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/CPipeServer.h"
|
||||
#include "CDebug.h"
|
||||
#include "display/device/CDeviceContext.h"
|
||||
|
||||
CPipeServer g_pipe;
|
||||
|
||||
bool CPipeServer::Init()
|
||||
{
|
||||
_DeInit();
|
||||
|
||||
m_pipe.Attach(CreateNamedPipeA(
|
||||
LG_PIPE_NAME,
|
||||
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||
1,
|
||||
1024,
|
||||
1024,
|
||||
0,
|
||||
NULL));
|
||||
|
||||
if (!m_pipe.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create the named pipe");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_signal.Attach(CreateEvent(NULL, TRUE, FALSE, NULL));
|
||||
if (!m_signal.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create pipe signal event");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_running = true;
|
||||
m_thread.Attach(CreateThread(
|
||||
NULL,
|
||||
0,
|
||||
_pipeThread,
|
||||
(LPVOID)this,
|
||||
0,
|
||||
NULL));
|
||||
|
||||
if (!m_thread.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create the pipe thread");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_TRACE("Pipe Initialized");
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPipeServer::_DeInit()
|
||||
{
|
||||
m_running = false;
|
||||
m_connected = false;
|
||||
if (m_signal.IsValid())
|
||||
SetEvent(m_signal.Get());
|
||||
|
||||
if (m_thread.IsValid())
|
||||
{
|
||||
WaitForSingleObject(m_thread.Get(), INFINITE);
|
||||
m_thread.Close();
|
||||
}
|
||||
|
||||
if (m_pipe.IsValid())
|
||||
{
|
||||
FlushFileBuffers(m_pipe.Get());
|
||||
m_pipe.Close();
|
||||
}
|
||||
|
||||
m_signal.Close();
|
||||
}
|
||||
|
||||
void CPipeServer::DeInit()
|
||||
{
|
||||
DEBUG_TRACE("Pipe Stopping");
|
||||
_DeInit();
|
||||
DEBUG_TRACE("Pipe Stopped");
|
||||
}
|
||||
|
||||
void CPipeServer::Thread()
|
||||
{
|
||||
DEBUG_TRACE("Pipe thread started");
|
||||
|
||||
HandleT<EventTraits> ioEvent(CreateEvent(NULL, TRUE, FALSE, NULL));
|
||||
if (!ioEvent.IsValid())
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Can't create event for overlapped I/O!");
|
||||
WaitForSingleObject(m_signal.Get(), 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
while(m_running)
|
||||
{
|
||||
m_connected = false;
|
||||
|
||||
OVERLAPPED overlapped = { 0 };
|
||||
overlapped.hEvent = ioEvent.Get();
|
||||
|
||||
if (!ConnectNamedPipe(m_pipe.Get(), &overlapped))
|
||||
{
|
||||
DWORD dwError = GetLastError();
|
||||
switch (dwError) {
|
||||
case ERROR_PIPE_CONNECTED:
|
||||
break;
|
||||
case ERROR_IO_PENDING:
|
||||
{
|
||||
HANDLE hWait[] = { ioEvent.Get(), m_signal.Get() };
|
||||
switch (WaitForMultipleObjects(2, hWait, FALSE, INFINITE))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
DEBUG_INFO("Connect interrupted by signal");
|
||||
CancelIo(m_pipe.Get());
|
||||
WaitForSingleObject(ioEvent.Get(), INFINITE);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DEBUG_ERROR_HR(dwError, "Error connecting to the named pipe");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_TRACE("Client connected");
|
||||
|
||||
m_connected = true;
|
||||
|
||||
for (const auto& msg : m_queue)
|
||||
WriteMsg(msg);
|
||||
m_queue.clear();
|
||||
|
||||
while (m_running && m_connected)
|
||||
{
|
||||
LGPipeMsg msg;
|
||||
|
||||
if (!ReadFile(m_pipe.Get(), &msg, sizeof(msg), NULL, &overlapped))
|
||||
{
|
||||
DWORD dwError = GetLastError();
|
||||
if (dwError != ERROR_IO_PENDING)
|
||||
{
|
||||
DEBUG_ERROR_HR(dwError, "ReadFile Failed");
|
||||
break;
|
||||
}
|
||||
|
||||
HANDLE hWait[] = { ioEvent.Get(), m_signal.Get() };
|
||||
switch (WaitForMultipleObjects(2, hWait, FALSE, INFINITE))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
break;
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
DEBUG_INFO("I/O interrupted by signal");
|
||||
CancelIo(m_pipe.Get());
|
||||
WaitForSingleObject(ioEvent.Get(), INFINITE);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD bytesRead;
|
||||
GetOverlappedResult(m_pipe.Get(), &overlapped, &bytesRead, TRUE);
|
||||
|
||||
if (bytesRead != sizeof(msg))
|
||||
{
|
||||
DEBUG_ERROR("Corrupted data, expected %lld bytes, read %lld bytes", sizeof msg, bytesRead);
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg.size != sizeof(msg))
|
||||
{
|
||||
DEBUG_ERROR("Corrupted data, expected %lld bytes, actual message size: %lld bytes", sizeof msg, msg.size);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (msg.type)
|
||||
{
|
||||
case LGPipeMsg::RELOADSETTINGS:
|
||||
HandleReloadSettings();
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unknown message type %d", msg.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_TRACE("Client disconnected");
|
||||
DisconnectNamedPipe(m_pipe.Get());
|
||||
|
||||
if (m_running)
|
||||
ResetEvent(m_signal.Get());
|
||||
}
|
||||
|
||||
end:
|
||||
m_running = false;
|
||||
m_connected = false;
|
||||
DEBUG_TRACE("Pipe thread shutdown");
|
||||
}
|
||||
|
||||
void CPipeServer::WriteMsg(const LGPipeMsg & msg)
|
||||
{
|
||||
if (!m_connected)
|
||||
{
|
||||
// Not connected yet: keep only the latest message of each type. These are
|
||||
// all latest-state-wins messages, so a burst (e.g. display mode changes
|
||||
// while resizing) must collapse to the final state rather than replay every
|
||||
// intermediate value when the helper reconnects.
|
||||
for (auto & queued : m_queue)
|
||||
if (queued.type == msg.type)
|
||||
{
|
||||
queued = msg;
|
||||
return;
|
||||
}
|
||||
m_queue.push_back(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD written;
|
||||
if (!WriteFile(m_pipe.Get(), &msg, sizeof(msg), &written, NULL))
|
||||
{
|
||||
DWORD err = GetLastError();
|
||||
if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA)
|
||||
{
|
||||
DEBUG_WARN_HR(err, "Client disconnected, failed to write");
|
||||
m_connected = false;
|
||||
SetEvent(m_signal.Get());
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_WARN_HR(err, "WriteFile failed on the pipe");
|
||||
return;
|
||||
}
|
||||
|
||||
FlushFileBuffers(m_pipe.Get());
|
||||
}
|
||||
|
||||
void CPipeServer::HandleReloadSettings()
|
||||
{
|
||||
DEBUG_INFO("Reloading settings");
|
||||
|
||||
AcquireSRWLockShared(&m_deviceContextLock);
|
||||
if (m_deviceContext)
|
||||
m_deviceContext->ReloadSettings();
|
||||
ReleaseSRWLockShared(&m_deviceContextLock);
|
||||
}
|
||||
|
||||
void CPipeServer::SetDeviceContext(CDeviceContext * context)
|
||||
{
|
||||
AcquireSRWLockExclusive(&m_deviceContextLock);
|
||||
m_deviceContext = context;
|
||||
ReleaseSRWLockExclusive(&m_deviceContextLock);
|
||||
}
|
||||
|
||||
void CPipeServer::SetCursorPos(uint32_t x, uint32_t y)
|
||||
{
|
||||
// do not send cursor messages if we are not connected or they will end up queued
|
||||
if (!m_connected)
|
||||
return;
|
||||
|
||||
LGPipeMsg msg = {};
|
||||
msg.size = sizeof(msg);
|
||||
msg.type = LGPipeMsg::SETCURSORPOS;
|
||||
msg.curorPos.x = x;
|
||||
msg.curorPos.y = y;
|
||||
WriteMsg(msg);
|
||||
}
|
||||
|
||||
void CPipeServer::SetDisplayMode(
|
||||
uint32_t width, uint32_t height, uint32_t refreshMilliHz)
|
||||
{
|
||||
LGPipeMsg msg = {};
|
||||
msg.size = sizeof(msg);
|
||||
msg.type = LGPipeMsg::SETDISPLAYMODE;
|
||||
msg.displayMode.width = width;
|
||||
msg.displayMode.height = height;
|
||||
msg.displayMode.refreshMilliHz = refreshMilliHz;
|
||||
WriteMsg(msg);
|
||||
}
|
||||
|
||||
void CPipeServer::SetGPUStatus(bool software)
|
||||
{
|
||||
LGPipeMsg msg = {};
|
||||
msg.size = sizeof(msg);
|
||||
msg.type = LGPipeMsg::GPUSTATUS;
|
||||
msg.gpuStatus.software = software;
|
||||
WriteMsg(msg);
|
||||
}
|
||||
|
||||
void CPipeServer::ResolutionRejected(uint32_t width, uint32_t height,
|
||||
uint32_t requiredSizeMiB)
|
||||
{
|
||||
LGPipeMsg msg = {};
|
||||
msg.size = sizeof(msg);
|
||||
msg.type = LGPipeMsg::RESOLUTIONREJECTED;
|
||||
msg.resolutionRejected.width = width;
|
||||
msg.resolutionRejected.height = height;
|
||||
msg.resolutionRejected.requiredSizeMiB = requiredSizeMiB;
|
||||
WriteMsg(msg);
|
||||
}
|
||||
76
idd/LGIdd/transport/CPipeServer.h
Normal file
76
idd/LGIdd/transport/CPipeServer.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* 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 <stdint.h>
|
||||
#include <wrl.h>
|
||||
#include <vector>
|
||||
|
||||
#include "PipeMsg.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
using namespace Microsoft::WRL::Wrappers::HandleTraits;
|
||||
|
||||
class CDeviceContext;
|
||||
|
||||
class CPipeServer
|
||||
{
|
||||
private:
|
||||
HandleT<HANDLETraits> m_pipe;
|
||||
HandleT<HANDLENullTraits> m_thread;
|
||||
HandleT<EventTraits> m_signal;
|
||||
std::vector<LGPipeMsg> m_queue;
|
||||
|
||||
bool m_running = false;
|
||||
bool m_connected = false;
|
||||
|
||||
SRWLOCK m_deviceContextLock = SRWLOCK_INIT;
|
||||
CDeviceContext * m_deviceContext = nullptr;
|
||||
|
||||
void _DeInit();
|
||||
|
||||
static DWORD WINAPI _pipeThread(LPVOID lpParam) { ((CPipeServer*)lpParam)->Thread(); return 0; }
|
||||
void Thread();
|
||||
|
||||
void WriteMsg(const LGPipeMsg & msg);
|
||||
|
||||
void HandleReloadSettings();
|
||||
|
||||
public:
|
||||
~CPipeServer() { DeInit(); }
|
||||
|
||||
bool Init();
|
||||
void DeInit();
|
||||
|
||||
void SetDeviceContext(CDeviceContext * context);
|
||||
|
||||
void SetCursorPos(uint32_t x, uint32_t y);
|
||||
void SetDisplayMode(
|
||||
uint32_t width, uint32_t height, uint32_t refreshMilliHz);
|
||||
void SetGPUStatus(bool software);
|
||||
void ResolutionRejected(uint32_t width, uint32_t height,
|
||||
uint32_t requiredSizeMiB);
|
||||
};
|
||||
|
||||
extern CPipeServer g_pipe;
|
||||
31
idd/LGIdd/transport/FrameMemoryLimits.h
Normal file
31
idd/LGIdd/transport/FrameMemoryLimits.h
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user