mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[idd] transport: abstract LGMP implementation
Introduce transport, frame, and control interfaces with an LGMP factory backend. Move LGMP and IVSHMEM implementation details under transport/lgmp and expose direct frame-buffer memory through a neutral capability.
This commit is contained in:
208
idd/LGIdd/transport/lgmp/CIVSHMEM.cpp
Normal file
208
idd/LGIdd/transport/lgmp/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/lgmp/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/lgmp/CIVSHMEM.h
Normal file
51
idd/LGIdd/transport/lgmp/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 () const { return m_mem; }
|
||||
};
|
||||
301
idd/LGIdd/transport/lgmp/CLGMPControl.cpp
Normal file
301
idd/LGIdd/transport/lgmp/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/lgmp/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();
|
||||
}
|
||||
85
idd/LGIdd/transport/lgmp/CLGMPControl.h
Normal file
85
idd/LGIdd/transport/lgmp/CLGMPControl.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 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/lgmp/CLGMPHost.h"
|
||||
#include "transport/IControlTransport.h"
|
||||
|
||||
#include "common/KVMFR.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <wdf.h>
|
||||
#include <IddCx.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class CLGMPTransport;
|
||||
|
||||
class CLGMPControl final : public IControlTransport
|
||||
{
|
||||
private:
|
||||
friend class CLGMPTransport;
|
||||
|
||||
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();
|
||||
bool Initialize();
|
||||
void DeInit();
|
||||
LGMP_STATUS ReadDataWithSource(void * data, size_t * size,
|
||||
uint32_t * sourceClientID);
|
||||
LGMP_STATUS AckData();
|
||||
bool HasNewSubscribers();
|
||||
void ResendState();
|
||||
|
||||
public:
|
||||
explicit CLGMPControl(CLGMPHost& host) :
|
||||
m_host(host) {}
|
||||
~CLGMPControl() override;
|
||||
|
||||
CLGMPControl(const CLGMPControl&) = delete;
|
||||
CLGMPControl& operator=(const CLGMPControl&) = delete;
|
||||
|
||||
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, const BYTE * data,
|
||||
UINT sdrWhiteLevel) override;
|
||||
void SetColorTransform(
|
||||
std::shared_ptr<const D12ColorTransform> transform) override;
|
||||
std::shared_ptr<const D12ColorTransform>
|
||||
GetColorTransform() const override;
|
||||
};
|
||||
1315
idd/LGIdd/transport/lgmp/CLGMPFrameTransport.cpp
Normal file
1315
idd/LGIdd/transport/lgmp/CLGMPFrameTransport.cpp
Normal file
File diff suppressed because it is too large
Load Diff
215
idd/LGIdd/transport/lgmp/CLGMPFrameTransport.h
Normal file
215
idd/LGIdd/transport/lgmp/CLGMPFrameTransport.h
Normal file
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* 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 "common/KVMFR.h"
|
||||
#include "transport/CFrameScheduler.h"
|
||||
#include "transport/FrameMemoryLimits.h"
|
||||
#include "transport/IFrameTransport.h"
|
||||
|
||||
class CIVSHMEM;
|
||||
class CLGMPHost;
|
||||
class CLGMPTransport;
|
||||
struct LGMPBuffer;
|
||||
|
||||
class CLGMPFrameTransport final : public IFrameTransport
|
||||
{
|
||||
private:
|
||||
friend class CLGMPTransport;
|
||||
|
||||
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] = {};
|
||||
LGMPBuffer * 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);
|
||||
CLGMPFrameTransport(CLGMPHost& host, CIVSHMEM& ivshmem);
|
||||
bool Initialize();
|
||||
void SealMemoryLayout();
|
||||
bool Setup(size_t alignSize);
|
||||
void DeInit();
|
||||
FrameMemoryLimits GetMemoryLimits() const;
|
||||
SubscriberSnapshot SnapshotSubscribers() const;
|
||||
void FinalizeSubscribers(
|
||||
const SubscriberSnapshot& snapshot, uint64_t now);
|
||||
bool UpdateSchedule(uint32_t sourceClientID,
|
||||
const FrameScheduleUpdate& schedule, uint64_t now);
|
||||
|
||||
public:
|
||||
~CLGMPFrameTransport() override;
|
||||
|
||||
CLGMPFrameTransport(const CLGMPFrameTransport&) = delete;
|
||||
CLGMPFrameTransport& operator=(const CLGMPFrameTransport&) = delete;
|
||||
|
||||
size_t GetMaxFrameSize() const override { return m_maxFrameSize; }
|
||||
|
||||
bool FrameBufferAvailable(const CFrameScheduler::Schedule& schedule,
|
||||
bool allowReadyReplacement = true) override;
|
||||
bool HasPublishedFrame() const override
|
||||
{
|
||||
return m_readyFrameIndex.load(std::memory_order_acquire) >= 0;
|
||||
}
|
||||
void ProcessDeliveries() override;
|
||||
bool GetPendingDeliveryTarget(
|
||||
uint64_t now, uint64_t& target) override;
|
||||
bool RetryPendingDelivery(uint64_t now, bool& retry) override;
|
||||
PreparedFrameBuffer PrepareFrameBuffer(unsigned pitch,
|
||||
const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat,
|
||||
const RECT * dirtyRects, unsigned nbDirtyRects,
|
||||
const CFrameScheduler::Schedule& schedule,
|
||||
bool allowReadyReplacement = true) override;
|
||||
bool PublishFrameBuffer(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule,
|
||||
bool& deliveredToOwner) override;
|
||||
bool RepublishFrameBuffer(
|
||||
const CFrameScheduler::Schedule& schedule) override;
|
||||
bool TryFrameSubmitted(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule) override;
|
||||
void CommitFrameBuffer(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule, bool periodic,
|
||||
bool deliveredToOwner) override;
|
||||
void AbortFrameBuffer(unsigned frameIndex) override;
|
||||
void FailFrameBuffer(unsigned frameIndex) override;
|
||||
void CompleteFrameBuffer(
|
||||
unsigned frameIndex, bool succeeded) override;
|
||||
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) override;
|
||||
void WriteFrameBuffer(unsigned frameIndex, void * src, size_t offset,
|
||||
size_t len, bool setWritePos) const override;
|
||||
void WriteFrameBufferRows(unsigned frameIndex, void * src,
|
||||
size_t offset, size_t rowBytes, size_t pitch,
|
||||
unsigned rows) const override;
|
||||
void FinalizeFrameBuffer(unsigned frameIndex) const override;
|
||||
|
||||
void ObserveFrame(uint64_t now) override;
|
||||
void ForceFrame() override;
|
||||
bool GetPublishTarget(uint64_t now, uint64_t& target,
|
||||
CFrameScheduler::Schedule& schedule, bool& periodic,
|
||||
bool& republish) override;
|
||||
void FrameMissed(const CFrameScheduler::Schedule& schedule,
|
||||
uint64_t now, bool periodic) override;
|
||||
void FrameSuperseded() override;
|
||||
HANDLE GetFrameScheduleEvent() const override
|
||||
{
|
||||
return m_frameScheduler.GetWakeEvent();
|
||||
}
|
||||
void TryRecordFrameTiming(uint64_t duration) override;
|
||||
};
|
||||
165
idd/LGIdd/transport/lgmp/CLGMPHost.cpp
Normal file
165
idd/LGIdd/transport/lgmp/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/lgmp/CLGMPHost.h"
|
||||
|
||||
#include "transport/lgmp/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/lgmp/CLGMPHost.h
Normal file
59
idd/LGIdd/transport/lgmp/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;
|
||||
};
|
||||
183
idd/LGIdd/transport/lgmp/CLGMPTransport.cpp
Normal file
183
idd/LGIdd/transport/lgmp/CLGMPTransport.cpp
Normal file
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "transport/lgmp/CLGMPTransport.h"
|
||||
|
||||
#include "CDebug.h"
|
||||
#include "common/KVMFR.h"
|
||||
|
||||
static bool TranslateFrameScheduleFlags(
|
||||
uint32_t source, uint32_t& destination)
|
||||
{
|
||||
static const uint32_t validFlags =
|
||||
KVMFR_FRAME_SCHEDULE_ACTIVE |
|
||||
KVMFR_FRAME_SCHEDULE_RELEASE |
|
||||
KVMFR_FRAME_SCHEDULE_RESET |
|
||||
KVMFR_FRAME_SCHEDULE_IMMEDIATE;
|
||||
|
||||
if (source & ~validFlags)
|
||||
return false;
|
||||
|
||||
destination = 0;
|
||||
if (source & KVMFR_FRAME_SCHEDULE_ACTIVE)
|
||||
destination |= FRAME_SCHEDULE_ACTIVE;
|
||||
if (source & KVMFR_FRAME_SCHEDULE_RELEASE)
|
||||
destination |= FRAME_SCHEDULE_RELEASE;
|
||||
if (source & KVMFR_FRAME_SCHEDULE_RESET)
|
||||
destination |= FRAME_SCHEDULE_RESET;
|
||||
if (source & KVMFR_FRAME_SCHEDULE_IMMEDIATE)
|
||||
destination |= FRAME_SCHEDULE_IMMEDIATE;
|
||||
return true;
|
||||
}
|
||||
|
||||
CLGMPTransport::CLGMPTransport() :
|
||||
m_control(m_host),
|
||||
m_frames(m_host, m_ivshmem)
|
||||
{
|
||||
}
|
||||
|
||||
ITransport::OpenResult CLGMPTransport::Open()
|
||||
{
|
||||
if (m_ivshmem.GetMem())
|
||||
return OpenResult::SUCCESS;
|
||||
|
||||
if (!m_ivshmem.Init() || !m_ivshmem.Open())
|
||||
return OpenResult::RETRY;
|
||||
|
||||
return OpenResult::SUCCESS;
|
||||
}
|
||||
|
||||
bool CLGMPTransport::Initialize()
|
||||
{
|
||||
if (!m_host.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_frames.Initialize() || !m_control.Initialize())
|
||||
return false;
|
||||
|
||||
m_frames.SealMemoryLayout();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLGMPTransport::Setup(size_t alignment)
|
||||
{
|
||||
return m_frames.Setup(alignment);
|
||||
}
|
||||
|
||||
void CLGMPTransport::Process(ITransportEvents& events)
|
||||
{
|
||||
const LGMP_STATUS processStatus = m_host.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 CLGMPFrameTransport::SubscriberSnapshot subscribers =
|
||||
m_frames.SnapshotSubscribers();
|
||||
|
||||
uint8_t data[LGMP_MSGS_SIZE];
|
||||
size_t size;
|
||||
uint32_t sourceClientID;
|
||||
LGMP_STATUS status;
|
||||
while ((status = m_control.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);
|
||||
events.OnSetCursorPos(position->x, position->y);
|
||||
break;
|
||||
}
|
||||
|
||||
case KVMFR_MESSAGE_WINDOWSIZE:
|
||||
{
|
||||
KVMFRWindowSize * window =
|
||||
reinterpret_cast<KVMFRWindowSize *>(msg);
|
||||
events.OnSetResolution(window->w, window->h);
|
||||
break;
|
||||
}
|
||||
|
||||
case KVMFR_MESSAGE_FRAME_SCHEDULE:
|
||||
{
|
||||
const KVMFRFrameSchedule * schedule =
|
||||
reinterpret_cast<KVMFRFrameSchedule *>(msg);
|
||||
uint32_t translatedFlags = 0;
|
||||
bool valid = size == sizeof(*schedule) &&
|
||||
TranslateFrameScheduleFlags(schedule->flags, translatedFlags);
|
||||
if (valid)
|
||||
{
|
||||
FrameScheduleUpdate update = {};
|
||||
update.clientID = schedule->clientID;
|
||||
update.generation = schedule->generation;
|
||||
update.flags = translatedFlags;
|
||||
update.period = schedule->period;
|
||||
update.targetSlack = schedule->targetSlack;
|
||||
update.phaseError = schedule->phaseError;
|
||||
update.feedbackFrameSerial = schedule->feedbackFrameSerial;
|
||||
update.feedbackScheduleEpoch = schedule->feedbackScheduleEpoch;
|
||||
update.feedbackDeadlineSerial = schedule->feedbackDeadlineSerial;
|
||||
update.lease = schedule->lease;
|
||||
valid = m_frames.UpdateSchedule(sourceClientID, update, now);
|
||||
}
|
||||
if (!valid)
|
||||
DEBUG_WARN("Ignoring invalid KVMFR frame schedule");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_control.AckData();
|
||||
}
|
||||
|
||||
m_frames.FinalizeSubscribers(subscribers, now);
|
||||
|
||||
if (m_control.HasNewSubscribers())
|
||||
m_control.ResendState();
|
||||
}
|
||||
|
||||
FrameMemoryLimits CLGMPTransport::GetMemoryLimits() const
|
||||
{
|
||||
return m_frames.GetMemoryLimits();
|
||||
}
|
||||
|
||||
DirectFrameBufferMemory CLGMPTransport::GetDirectMemory() const
|
||||
{
|
||||
return {m_ivshmem.GetMem(), m_ivshmem.GetSize()};
|
||||
}
|
||||
56
idd/LGIdd/transport/lgmp/CLGMPTransport.h
Normal file
56
idd/LGIdd/transport/lgmp/CLGMPTransport.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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/ITransport.h"
|
||||
#include "transport/lgmp/CIVSHMEM.h"
|
||||
#include "transport/lgmp/CLGMPControl.h"
|
||||
#include "transport/lgmp/CLGMPFrameTransport.h"
|
||||
#include "transport/lgmp/CLGMPHost.h"
|
||||
|
||||
class CLGMPTransport final : public ITransport
|
||||
{
|
||||
private:
|
||||
// Keep this declaration order. Destruction must release frame and control
|
||||
// allocations before the LGMP host and its IVSHMEM mapping are destroyed.
|
||||
CIVSHMEM m_ivshmem;
|
||||
CLGMPHost m_host;
|
||||
CLGMPControl m_control;
|
||||
CLGMPFrameTransport m_frames;
|
||||
|
||||
public:
|
||||
CLGMPTransport();
|
||||
~CLGMPTransport() override = default;
|
||||
|
||||
CLGMPTransport(const CLGMPTransport&) = delete;
|
||||
CLGMPTransport& operator=(const CLGMPTransport&) = delete;
|
||||
|
||||
OpenResult Open() override;
|
||||
bool Initialize() override;
|
||||
bool Setup(size_t alignment) override;
|
||||
void Process(ITransportEvents& events) override;
|
||||
|
||||
FrameMemoryLimits GetMemoryLimits() const override;
|
||||
DirectFrameBufferMemory GetDirectMemory() const override;
|
||||
|
||||
IFrameTransport& Frames() override { return m_frames; }
|
||||
IControlTransport& Control() override { return m_control; }
|
||||
};
|
||||
Reference in New Issue
Block a user