diff --git a/client/include/interface/external_frame.h b/client/include/interface/external_frame.h new file mode 100644 index 00000000..82d46dcf --- /dev/null +++ b/client/include/interface/external_frame.h @@ -0,0 +1,88 @@ +/** + * 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 + */ + +#ifndef _H_LG_CLIENT_EXTERNAL_FRAME_ +#define _H_LG_CLIENT_EXTERNAL_FRAME_ + +#include +#include + +typedef enum LG_ExternalFrameType +{ + LG_EXTERNAL_FRAME_INVALID = 0, + LG_EXTERNAL_FRAME_DMABUF = 1, +} +LG_ExternalFrameType; + +typedef struct LG_ExternalFrame LG_ExternalFrame; + +/* + * A NULL pointer means there is no external resource. External frames own + * their native resource. Their address is a stable cache identity: a new + * native export must use a new object. Query results are immutable, borrowed, + * and remain valid while any reference to the object is held. Consumers must + * never close or otherwise release a queried resource. + * + * LG_EXTERNAL_FRAME_DMABUF queries return one native file descriptor in an + * int-sized result buffer. + */ +typedef struct LG_ExternalFrameOps +{ + LG_ExternalFrameType type; + void (*retain )(LG_ExternalFrame * frame); + void (*release)(LG_ExternalFrame * frame); + bool (*query )(const LG_ExternalFrame * frame, void * result, + size_t resultSize); +} +LG_ExternalFrameOps; + +struct LG_ExternalFrame +{ + const LG_ExternalFrameOps * ops; +}; + +static inline LG_ExternalFrameType lgExternalFrameType( + const LG_ExternalFrame * frame) +{ + return frame && frame->ops ? frame->ops->type : LG_EXTERNAL_FRAME_INVALID; +} + +static inline LG_ExternalFrame * lgExternalFrameRetain( + LG_ExternalFrame * frame) +{ + if (frame) + frame->ops->retain(frame); + return frame; +} + +static inline void lgExternalFrameRelease(LG_ExternalFrame * frame) +{ + if (frame) + frame->ops->release(frame); +} + +static inline bool lgExternalFrameQuery(const LG_ExternalFrame * frame, + LG_ExternalFrameType type, void * result, size_t resultSize) +{ + return frame && frame->ops && frame->ops->type == type && + frame->ops->query(frame, result, resultSize); +} + +#endif diff --git a/client/include/interface/renderer.h b/client/include/interface/renderer.h index b6148073..4795e056 100644 --- a/client/include/interface/renderer.h +++ b/client/include/interface/renderer.h @@ -26,6 +26,7 @@ #include "app.h" #include "common/types.h" #include "common/framebuffer.h" +#include "interface/external_frame.h" #define IS_LG_RENDERER_VALID(x) \ ((x)->getName && \ @@ -304,11 +305,13 @@ typedef struct LG_RendererOps /* called when there is a new frame. frameToken must remain attached to the * update if the renderer coalesces it, and must be reported by render only - * when that update is consumed. A non-NULL releaseFn transfers ownership to - * the renderer on success and must be called exactly once when the imported - * frame can no longer be sampled. Context: frameThread */ + * when that update is consumed. On success, ownership of externalFrame and + * a non-NULL releaseFn transfers to the renderer. The external frame must be + * released and releaseFn called exactly once when the imported frame can no + * longer be sampled. Context: frameThread */ bool (*onFrame)(LG_Renderer * renderer, const KVMFRFrameBuffer * frame, - int dmaFD, const KVMFRFrameDamageRect * damage, int damageCount, + LG_ExternalFrame * externalFrame, const KVMFRFrameDamageRect * damage, + int damageCount, LG_RendererFrameToken frameToken, LG_FrameReleaseFn releaseFn, void * releaseOpaque, uint64_t releaseHandle); diff --git a/client/include/interface/transport.h b/client/include/interface/transport.h index 0cb48894..a0eb42f9 100644 --- a/client/include/interface/transport.h +++ b/client/include/interface/transport.h @@ -30,6 +30,7 @@ #include "common/types.h" #include "interface/audio.h" #include "interface/clipboard.h" +#include "interface/external_frame.h" #include "interface/input.h" #define LG_TRANSPORT_MAX_DAMAGE_RECTS LG_MAX_FRAME_DAMAGE_RECTS @@ -343,7 +344,7 @@ typedef struct LG_TransportFrame const LG_TransportFrameFormat * format; const KVMFRFrameBuffer * framebuffer; - int dmaFD; + LG_ExternalFrame * externalFrame; const KVMFRFrameDamageRect * damageRects; uint32_t damageRectsCount; diff --git a/client/renderers/EGL/desktop.c b/client/renderers/EGL/desktop.c index ef859c86..e812dd43 100644 --- a/client/renderers/EGL/desktop.c +++ b/client/renderers/EGL/desktop.c @@ -382,7 +382,7 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format) bool egl_desktopUpdate(EGL_Desktop * desktop, const KVMFRFrameBuffer * frame, - LG_RendererFrameToken frameToken, int dmaFd, + LG_RendererFrameToken frameToken, LG_ExternalFrame * externalFrame, const KVMFRFrameDamageRect * damageRects, int damageRectsCount, uint64_t * waitTimeNs, LG_FrameReleaseFn releaseFn, void * releaseOpaque, uint64_t releaseHandle) @@ -396,17 +396,19 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, if (unlikely(!framebuffer_wait_timed( frame, desktop->texture->format.dataSize, waitTimeNs))) { + lgExternalFrameRelease(externalFrame); if (releaseFn) releaseFn(releaseOpaque, releaseHandle); return true; } - if (likely(desktop->useDMA && dmaFd >= 0)) + if (likely(desktop->useDMA && externalFrame)) { if (likely(egl_textureUpdateFromDMA( - desktop->texture, frame, frameToken, dmaFd, waitTimeNs, + desktop->texture, frame, frameToken, externalFrame, waitTimeNs, releaseFn, releaseOpaque, releaseHandle))) { + lgExternalFrameRelease(externalFrame); atomic_store(&desktop->processFrame, true); return true; } @@ -449,6 +451,7 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, if (likely(egl_textureUpdateFromFrame(desktop->texture, frame, frameToken, damageRects, damageRectsCount, waitTimeNs))) { + lgExternalFrameRelease(externalFrame); if (releaseFn) releaseFn(releaseOpaque, releaseHandle); return true; diff --git a/client/renderers/EGL/desktop.h b/client/renderers/EGL/desktop.h index 8d021f8a..196b3a0f 100644 --- a/client/renderers/EGL/desktop.h +++ b/client/renderers/EGL/desktop.h @@ -49,7 +49,7 @@ void egl_desktopGetHDRMapping(EGL_Desktop * desktop, bool * enabled, float * gain, float * contentPeak); bool egl_desktopSetup (EGL_Desktop * desktop, const LG_RendererFormat format); bool egl_desktopUpdate(EGL_Desktop * desktop, const KVMFRFrameBuffer * frame, - LG_RendererFrameToken frameToken, int dmaFd, + LG_RendererFrameToken frameToken, LG_ExternalFrame * externalFrame, const KVMFRFrameDamageRect * damageRects, int damageRectsCount, uint64_t * waitTimeNs, LG_FrameReleaseFn releaseFn, void * releaseOpaque, uint64_t releaseHandle); diff --git a/client/renderers/EGL/egl.c b/client/renderers/EGL/egl.c index 9afd1dc2..fe0814b3 100644 --- a/client/renderers/EGL/egl.c +++ b/client/renderers/EGL/egl.c @@ -833,7 +833,7 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo } static bool egl_onFrame(LG_Renderer * renderer, - const KVMFRFrameBuffer * frame, int dmaFd, + const KVMFRFrameBuffer * frame, LG_ExternalFrame * externalFrame, const KVMFRFrameDamageRect * damageRects, int damageRectsCount, LG_RendererFrameToken frameToken, LG_FrameReleaseFn releaseFn, void * releaseOpaque, uint64_t releaseHandle) @@ -844,7 +844,7 @@ static bool egl_onFrame(LG_Renderer * renderer, const uint64_t start = nanotime(); uint64_t waitTimeNs = 0; if (unlikely(!egl_desktopUpdate( - this->desktop, frame, frameToken, dmaFd, + this->desktop, frame, frameToken, externalFrame, damageRects, damageRectsCount, &waitTimeNs, releaseFn, releaseOpaque, releaseHandle))) { diff --git a/client/renderers/EGL/texture.c b/client/renderers/EGL/texture.c index 7ed6a3ce..16ac79e5 100644 --- a/client/renderers/EGL/texture.c +++ b/client/renderers/EGL/texture.c @@ -185,23 +185,24 @@ bool egl_textureUpdateFromFrame(EGL_Texture * this, bool egl_textureUpdateFromDMA(EGL_Texture * this, const KVMFRFrameBuffer * frame, LG_RendererFrameToken frameToken, - const int dmaFd, uint64_t * waitTimeNs, LG_FrameReleaseFn releaseFn, - void * releaseOpaque, uint64_t releaseHandle) + LG_ExternalFrame * externalFrame, uint64_t * waitTimeNs, + LG_FrameReleaseFn releaseFn, void * releaseOpaque, + uint64_t releaseHandle) { const struct EGL_TexUpdate update = { .type = EGL_TEXTYPE_DMABUF, .frameToken = frameToken, + .externalFrame = externalFrame, + .releaseFn = releaseFn, + .releaseOpaque = releaseOpaque, + .releaseHandle = releaseHandle, .x = 0, .y = 0, .width = this->format.width, .height = this->format.height, .pitch = this->format.pitch, .stride = this->format.stride, - .dmaFD = dmaFd, - .releaseFn = releaseFn, - .releaseOpaque = releaseOpaque, - .releaseHandle = releaseHandle, }; /* wait for completion */ diff --git a/client/renderers/EGL/texture.h b/client/renderers/EGL/texture.h index cace4c0c..a63daad4 100644 --- a/client/renderers/EGL/texture.h +++ b/client/renderers/EGL/texture.h @@ -44,6 +44,7 @@ typedef struct EGL_TexUpdate EGL_TexType type; LG_RendererFrameToken frameToken; uint64_t * waitTimeNs; + LG_ExternalFrame * externalFrame; LG_FrameReleaseFn releaseFn; void * releaseOpaque; uint64_t releaseHandle; @@ -72,8 +73,6 @@ typedef struct EGL_TexUpdate int rectCount; }; - /* EGL_TEXTYPE_DMABUF */ - int dmaFD; }; } EGL_TexUpdate; @@ -147,8 +146,9 @@ bool egl_textureUpdateFromFrame(EGL_Texture * texture, bool egl_textureUpdateFromDMA(EGL_Texture * texture, const KVMFRFrameBuffer * frame, LG_RendererFrameToken frameToken, - const int dmaFd, uint64_t * waitTimeNs, LG_FrameReleaseFn releaseFn, - void * releaseOpaque, uint64_t releaseHandle); + LG_ExternalFrame * externalFrame, uint64_t * waitTimeNs, + LG_FrameReleaseFn releaseFn, void * releaseOpaque, + uint64_t releaseHandle); void egl_textureReset(EGL_Texture * texture); void egl_texturePoll(EGL_Texture * texture); diff --git a/client/renderers/EGL/texture_dmabuf.c b/client/renderers/EGL/texture_dmabuf.c index e3c33871..e6f99eb7 100644 --- a/client/renderers/EGL/texture_dmabuf.c +++ b/client/renderers/EGL/texture_dmabuf.c @@ -36,11 +36,11 @@ #define EGL_DMABUF_SNAPSHOT_COUNT (LGMP_Q_FRAME_BUFFER_LEN + 1) -struct FdImage +struct ExternalImage { - int fd; - EGLImage image; - GLuint texture; + LG_ExternalFrame * externalFrame; + EGLImage image; + GLuint texture; }; struct Snapshot @@ -50,6 +50,7 @@ struct Snapshot GLsync copySync; GLsync useSync; LG_RendererFrameToken frameToken; + LG_ExternalFrame * externalFrame; LG_FrameReleaseFn releaseFn; void * releaseOpaque; uint64_t releaseHandle; @@ -61,9 +62,9 @@ typedef struct TexDMABUF EGLDisplay display; - struct FdImage images[EGL_TEX_BUFFER_MAX]; - struct Snapshot snapshots[EGL_DMABUF_SNAPSHOT_COUNT]; - int renderIndex; + struct ExternalImage images[EGL_TEX_BUFFER_MAX]; + struct Snapshot snapshots[EGL_DMABUF_SNAPSHOT_COUNT]; + int renderIndex; EGL_Shader * copyShader; EGL_Model * copyModel; @@ -79,9 +80,10 @@ TexDMABUF; struct SnapshotRelease { - LG_FrameReleaseFn fn; - void * opaque; - uint64_t handle; + LG_ExternalFrame * externalFrame; + LG_FrameReleaseFn fn; + void * opaque; + uint64_t handle; }; EGL_TextureOps EGL_TextureDMABUF; @@ -98,10 +100,12 @@ static void egl_texDMABUFPoll(EGL_Texture * texture); static void snapshotRelease(struct Snapshot * snapshot, struct SnapshotRelease * release) { - release->fn = snapshot->releaseFn; - release->opaque = snapshot->releaseOpaque; - release->handle = snapshot->releaseHandle; + release->externalFrame = snapshot->externalFrame; + release->fn = snapshot->releaseFn; + release->opaque = snapshot->releaseOpaque; + release->handle = snapshot->releaseHandle; + snapshot->externalFrame = NULL; snapshot->releaseFn = NULL; snapshot->releaseOpaque = NULL; snapshot->releaseHandle = 0; @@ -111,7 +115,11 @@ static void snapshotInvokeReleases(struct SnapshotRelease * releases, int count) { for (int i = 0; i < count; ++i) - releases[i].fn(releases[i].opaque, releases[i].handle); + { + lgExternalFrameRelease(releases[i].externalFrame); + if (releases[i].fn) + releases[i].fn(releases[i].opaque, releases[i].handle); + } } static void waitSync(GLsync * sync) @@ -132,7 +140,7 @@ static void egl_texDMABUFCleanup(EGL_Texture * texture) TexDMABUF * this = UPCAST(TexDMABUF , parent); struct SnapshotRelease releases[EGL_DMABUF_SNAPSHOT_COUNT]; - int releaseCount = 0; + int releaseCount = 0; LG_LOCK(parent->copyLock); @@ -141,7 +149,7 @@ static void egl_texDMABUFCleanup(EGL_Texture * texture) struct Snapshot * snapshot = &this->snapshots[i]; waitSync(&snapshot->copySync); waitSync(&snapshot->useSync); - if (snapshot->releaseFn) + if (snapshot->externalFrame || snapshot->releaseFn) snapshotRelease(snapshot, &releases[releaseCount++]); if (snapshot->framebuffer) @@ -151,7 +159,7 @@ static void egl_texDMABUFCleanup(EGL_Texture * texture) memset(snapshot, 0, sizeof(*snapshot)); } - for(int i = 0; i < ARRAY_LENGTH(this->images); ++i) + for (int i = 0; i < ARRAY_LENGTH(this->images); ++i) { if (this->images[i].image != EGL_NO_IMAGE) { @@ -160,8 +168,9 @@ static void egl_texDMABUFCleanup(EGL_Texture * texture) } if (this->images[i].texture) glDeleteTextures(1, &this->images[i].texture); - this->images[i].fd = -1; - this->images[i].texture = 0; + lgExternalFrameRelease(this->images[i].externalFrame); + this->images[i].externalFrame = NULL; + this->images[i].texture = 0; } this->renderIndex = -1; @@ -179,13 +188,10 @@ static bool egl_texDMABUFInit(EGL_Texture ** texture, EGL_TexType type, EGLDisplay * display) { TexDMABUF * this = calloc(1, sizeof(*this)); - *texture = &this->base.base; + *texture = &this->base.base; - for(int i = 0; i < ARRAY_LENGTH(this->images); ++i) - { - this->images[i].fd = -1; + for (int i = 0; i < ARRAY_LENGTH(this->images); ++i) this->images[i].image = EGL_NO_IMAGE; - } this->renderIndex = -1; EGL_Texture * parent = &this->base.base; @@ -338,11 +344,20 @@ static bool egl_texDMABUFSetup(EGL_Texture * texture, const EGL_TexSetup * setup return texDMABUFSetup(texture); } -static EGLImage createImage(EGL_Texture * texture, int fd) +static EGLImage createImage(EGL_Texture * texture, + const LG_ExternalFrame * externalFrame) { TextureBuffer * parent = UPCAST(TextureBuffer, texture); TexDMABUF * this = UPCAST(TexDMABUF , parent); + int fd; + if (!lgExternalFrameQuery(externalFrame, LG_EXTERNAL_FRAME_DMABUF, + &fd, sizeof(fd))) + { + DEBUG_ERROR("Failed to query the DMABUF file descriptor"); + return EGL_NO_IMAGE; + } + const uint64_t modifier = DRM_FORMAT_MOD_LINEAR; EGLAttrib attribs[] = { @@ -376,40 +391,41 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture, TexDMABUF * this = UPCAST(TexDMABUF , parent); DEBUG_ASSERT(update->type == EGL_TEXTYPE_DMABUF); + DEBUG_ASSERT(update->externalFrame); /* Retire completed copies before deciding whether a snapshot slot must be * waited on and coalesced. */ egl_texDMABUFPoll(texture); - struct FdImage *fdImage = NULL; + struct ExternalImage * externalImage = NULL; for (int i = 0; i < ARRAY_LENGTH(this->images); ++i) - if (this->images[i].fd == update->dmaFD) + if (this->images[i].externalFrame == update->externalFrame) { - fdImage = &this->images[i]; + externalImage = &this->images[i]; break; } - if (!fdImage) + if (!externalImage) for (int i = 0; i < ARRAY_LENGTH(this->images); ++i) - if (this->images[i].fd == -1) + if (!this->images[i].externalFrame) { - fdImage = &this->images[i]; + externalImage = &this->images[i]; break; } - if (!fdImage) + if (!externalImage) { DEBUG_ERROR("No free DMABUF image slot"); return false; } - if (unlikely(fdImage->image == EGL_NO_IMAGE)) + if (unlikely(externalImage->image == EGL_NO_IMAGE)) { bool setup = false; if (texture->format.pixFmt == EGL_PF_RGB_24 && has24BitSupport) { - fdImage->image = createImage(texture, update->dmaFD); - if (fdImage->image == EGL_NO_IMAGE) + externalImage->image = createImage(texture, update->externalFrame); + if (externalImage->image == EGL_NO_IMAGE) { DEBUG_INFO("Using 24-bit in 32-bit for DMA"); has24BitSupport = false; @@ -421,21 +437,22 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture, { if (!texDMABUFSetup(texture)) return false; - fdImage = &this->images[0]; + externalImage = &this->images[0]; } - if (fdImage->image == EGL_NO_IMAGE) - fdImage->image = createImage(texture, update->dmaFD); + if (externalImage->image == EGL_NO_IMAGE) + externalImage->image = createImage(texture, update->externalFrame); - if (unlikely(fdImage->image == EGL_NO_IMAGE)) + if (unlikely(externalImage->image == EGL_NO_IMAGE)) { DEBUG_EGL_ERROR("Failed to create EGLImage for DMA transfer"); return false; } - fdImage->fd = update->dmaFD; - glGenTextures(1, &fdImage->texture); - egl_stateBindTexture(0, GL_TEXTURE_EXTERNAL_OES, fdImage->texture); + externalImage->externalFrame = + lgExternalFrameRetain(update->externalFrame); + glGenTextures(1, &externalImage->texture); + egl_stateBindTexture(0, GL_TEXTURE_EXTERNAL_OES, externalImage->texture); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_EXTERNAL_OES, @@ -445,11 +462,11 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture, glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); g_egl_dynProcs.glEGLImageTargetTexture2DOES( - GL_TEXTURE_EXTERNAL_OES, fdImage->image); + GL_TEXTURE_EXTERNAL_OES, externalImage->image); } struct SnapshotRelease releases[2]; - int releaseCount = 0; + int releaseCount = 0; LG_LOCK(parent->copyLock); int index = -1; @@ -480,7 +497,7 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture, struct Snapshot * old = &this->snapshots[index]; waitSync(&old->copySync); waitSync(&old->useSync); - if (old->releaseFn) + if (old->externalFrame || old->releaseFn) snapshotRelease(old, &releases[releaseCount++]); old->frameToken = LG_RENDERER_FRAME_TOKEN_NONE; } @@ -493,7 +510,7 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture, glDisable(GL_DITHER); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); egl_stateBindSampler(0, this->copySampler); - egl_stateBindTexture(0, GL_TEXTURE_EXTERNAL_OES, fdImage->texture); + egl_stateBindTexture(0, GL_TEXTURE_EXTERNAL_OES, externalImage->texture); egl_shaderUse(this->copyShader); egl_modelRender(this->copyModel); @@ -504,11 +521,13 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture, glFinish(); } snapshot->frameToken = update->frameToken; + snapshot->externalFrame = lgExternalFrameRetain(update->externalFrame); snapshot->releaseFn = update->releaseFn; snapshot->releaseOpaque = update->releaseOpaque; snapshot->releaseHandle = update->releaseHandle; - if (!snapshot->copySync && snapshot->releaseFn) + if (!snapshot->copySync && + (snapshot->externalFrame || snapshot->releaseFn)) snapshotRelease(snapshot, &releases[releaseCount++]); glFlush(); @@ -525,7 +544,7 @@ static void egl_texDMABUFPoll(EGL_Texture * texture) TexDMABUF * this = UPCAST(TexDMABUF , parent); struct SnapshotRelease releases[EGL_DMABUF_SNAPSHOT_COUNT]; - int releaseCount = 0; + int releaseCount = 0; LG_LOCK(parent->copyLock); for (int i = 0; i < ARRAY_LENGTH(this->snapshots); ++i) @@ -539,7 +558,7 @@ static void egl_texDMABUFPoll(EGL_Texture * texture) { glDeleteSync(snapshot->copySync); snapshot->copySync = 0; - if (snapshot->releaseFn) + if (snapshot->externalFrame || snapshot->releaseFn) snapshotRelease(snapshot, &releases[releaseCount++]); } else if (result == GL_WAIT_FAILED) diff --git a/client/renderers/OpenGL/opengl.c b/client/renderers/OpenGL/opengl.c index e75652b9..588658f0 100644 --- a/client/renderers/OpenGL/opengl.c +++ b/client/renderers/OpenGL/opengl.c @@ -483,14 +483,15 @@ bool opengl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat format } bool opengl_onFrame(LG_Renderer * renderer, const KVMFRFrameBuffer * frame, - int dmaFd, const KVMFRFrameDamageRect * damage, int damageCount, + LG_ExternalFrame * externalFrame, const KVMFRFrameDamageRect * damage, + int damageCount, LG_RendererFrameToken frameToken, LG_FrameReleaseFn releaseFn, void * releaseOpaque, uint64_t releaseHandle) { struct Inst * this = UPCAST(struct Inst, renderer); /* The legacy renderer supports only deferred CPU reads. */ - if (!frame || dmaFd >= 0) + if (!frame || externalFrame) return false; LG_LOCK(this->frameLock); diff --git a/client/src/main.c b/client/src/main.c index 31d7fc74..8511cc4a 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -1619,7 +1619,7 @@ int main_frameThread(void * unused) g_state.frameImportWaitTime = 0; const bool rendererOwnsFrame = frame.releaseFn != NULL; - if (!RENDERER(onFrame, frame.framebuffer, frame.dmaFD, + if (!RENDERER(onFrame, frame.framebuffer, frame.externalFrame, frame.damageRects, damageCount, frameToken, rendererOwnsFrame ? frame.releaseFn : NULL, rendererOwnsFrame ? frame.releaseOpaque : NULL, @@ -1634,6 +1634,7 @@ int main_frameThread(void * unused) primaryWorkerFailed(); break; } + frame.externalFrame = NULL; /* A leased frame can complete on the render thread immediately after it * is signalled below, so sample producer timing while this lease is still * unambiguously owned by the frame thread. */ diff --git a/client/tests/CMakeLists.txt b/client/tests/CMakeLists.txt index f161b430..f41d18d4 100644 --- a/client/tests/CMakeLists.txt +++ b/client/tests/CMakeLists.txt @@ -35,6 +35,20 @@ if(LG_CLIENT_PLATFORM_LINUX) set_tests_properties(config-platform-tests PROPERTIES TIMEOUT 10 ) + + add_executable(lgmp-platform-tests + lgmp_platform_test.c + ) + target_link_libraries(lgmp-platform-tests + ${EXE_FLAGS} + lg_common + ) + add_test(NAME lgmp-platform-tests + COMMAND lgmp-platform-tests + ) + set_tests_properties(lgmp-platform-tests PROPERTIES + TIMEOUT 10 + ) endif() if(LG_CLIENT_PLATFORM_WINDOWS) @@ -999,6 +1013,7 @@ if(ENABLE_EGL) poll saturation release + identity ) foreach(name IN LISTS TEXTURE_DMABUF_CASES) add_test(NAME texture-dmabuf-${name} diff --git a/client/tests/lgmp_frame_test.c b/client/tests/lgmp_frame_test.c index 98630379..ecaf16a2 100644 --- a/client/tests/lgmp_frame_test.c +++ b/client/tests/lgmp_frame_test.c @@ -527,7 +527,7 @@ static bool testDMAUnavailable(TestState * state) CHECK(postShared(state, &frame)); CHECK(state->frameOps->nextFrame(state->transport, false, &result) == LG_TRANSPORT_OK); - CHECK(result.dmaFD == -1); + CHECK(result.externalFrame == NULL); CHECK(releaseFrame(state, &result)); return true; } diff --git a/client/tests/lgmp_platform_test.c b/client/tests/lgmp_platform_test.c new file mode 100644 index 00000000..80d3743d --- /dev/null +++ b/client/tests/lgmp_platform_test.c @@ -0,0 +1,97 @@ +/** + * 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 "test.h" + +#include +#include +#include +#include +#include +#include + +#define ivshmemGetDMABuf testIVSHMEMGetDMABuf +#include "../transports/LGMP/platform/linux.c" +#undef ivshmemGetDMABuf + +static struct IVSHMEM * expectedDevice; +static uint64_t expectedOffset; +static uint64_t expectedSize; +static int sourceFD; +static unsigned int exportCalls; +static bool exportFails; + +int testIVSHMEMGetDMABuf(struct IVSHMEM * device, uint64_t offset, + uint64_t size) +{ + ++exportCalls; + CHECK(device == expectedDevice); + CHECK(offset == expectedOffset); + CHECK(size == expectedSize); + return exportFails ? -1 : dup(sourceFD); +} + +int main(void) +{ + struct IVSHMEM device = { 0 }; + + expectedDevice = &device; + expectedOffset = 4096; + expectedSize = 8192; + sourceFD = open("/dev/null", O_RDONLY); + CHECK(sourceFD >= 0); + + LG_ExternalFrame * frame = lgmpPlatformCreateExternalFrame( + &device, expectedOffset, expectedSize); + CHECK(frame); + CHECK(exportCalls == 1); + CHECK(lgExternalFrameType(frame) == LG_EXTERNAL_FRAME_DMABUF); + + int frameFD = -1; + CHECK(lgExternalFrameQuery(frame, LG_EXTERNAL_FRAME_DMABUF, + &frameFD, sizeof(frameFD))); + CHECK(frameFD >= 0); + CHECK(frameFD != sourceFD); + CHECK(fcntl(frameFD, F_GETFD) != -1); + CHECK(!lgExternalFrameQuery(frame, LG_EXTERNAL_FRAME_INVALID, + &frameFD, sizeof(frameFD))); + CHECK(!lgExternalFrameQuery(frame, LG_EXTERNAL_FRAME_DMABUF, + NULL, sizeof(frameFD))); + CHECK(!lgExternalFrameQuery(frame, LG_EXTERNAL_FRAME_DMABUF, + &frameFD, sizeof(frameFD) - 1)); + + CHECK(lgExternalFrameRetain(frame) == frame); + lgExternalFrameRelease(frame); + CHECK(fcntl(frameFD, F_GETFD) != -1); + + lgExternalFrameRelease(frame); + errno = 0; + CHECK(fcntl(frameFD, F_GETFD) == -1); + CHECK(errno == EBADF); + CHECK(fcntl(sourceFD, F_GETFD) != -1); + + exportFails = true; + CHECK(!lgmpPlatformCreateExternalFrame( + &device, expectedOffset, expectedSize)); + CHECK(exportCalls == 2); + + CHECK(close(sourceFD) == 0); + return EXIT_SUCCESS; +} diff --git a/client/tests/texture_dmabuf_test.c b/client/tests/texture_dmabuf_test.c index 7804e8d7..90603d13 100644 --- a/client/tests/texture_dmabuf_test.c +++ b/client/tests/texture_dmabuf_test.c @@ -53,14 +53,66 @@ struct Trace unsigned int failFence; }; +struct FakeExternalFrame +{ + LG_ExternalFrame base; + unsigned int refs; + int fd; +}; + struct Releases { - unsigned int count; - uint64_t handle[MAX_REL]; + struct FakeExternalFrame externalFrame; + unsigned int count; + uint64_t handle[MAX_REL]; }; static struct Trace t; +static void externalFrameRetain(LG_ExternalFrame * frame) +{ + struct FakeExternalFrame * external = (struct FakeExternalFrame *)frame; + CHECK(external->refs > 0); + ++external->refs; +} + +static void externalFrameRelease(LG_ExternalFrame * frame) +{ + struct FakeExternalFrame * external = (struct FakeExternalFrame *)frame; + CHECK(external->refs > 0); + --external->refs; +} + +static bool externalFrameQuery(const LG_ExternalFrame * frame, + void * result, size_t resultSize) +{ + if (!result || resultSize != sizeof(int)) + return false; + + const struct FakeExternalFrame * external = + (const struct FakeExternalFrame *)frame; + *(int *)result = external->fd; + return true; +} + +static const LG_ExternalFrameOps externalFrameOps = +{ + .type = LG_EXTERNAL_FRAME_DMABUF, + .retain = externalFrameRetain, + .release = externalFrameRelease, + .query = externalFrameQuery, +}; + +static void externalFrameInit(struct FakeExternalFrame * external) +{ + if (external->base.ops) + return; + + external->base.ops = &externalFrameOps; + external->refs = 1; + external->fd = 7; +} + struct EGLDynProcs g_egl_dynProcs; _Thread_local EGL_StateCache g_eglState; @@ -380,10 +432,7 @@ static TexDMABUF * newTex(void) LG_LOCK_INIT(texture->base.copyLock); for (int i = 0; i < ARRAY_LENGTH(texture->images); ++i) - { - texture->images[i].fd = -1; texture->images[i].image = EGL_NO_IMAGE; - } for (int i = 0; i < ARRAY_LENGTH(texture->snapshots); ++i) { texture->snapshots[i].texture = 200 + i; @@ -400,16 +449,21 @@ static TexDMABUF * newTex(void) static bool update(TexDMABUF * texture, LG_RendererFrameToken token, struct Releases * releases, uint64_t handle) { + externalFrameInit(&releases->externalFrame); + LG_ExternalFrame * externalFrame = + lgExternalFrameRetain(&releases->externalFrame.base); const EGL_TexUpdate value = { .type = EGL_TEXTYPE_DMABUF, .frameToken = token, + .externalFrame = externalFrame, .releaseFn = onRelease, .releaseOpaque = releases, .releaseHandle = handle, - .dmaFD = 7, }; - return egl_texDMABUFUpdate(&texture->base.base, &value); + const bool result = egl_texDMABUFUpdate(&texture->base.base, &value); + lgExternalFrameRelease(externalFrame); + return result; } static void setSlot(TexDMABUF * texture, int index, @@ -500,6 +554,7 @@ static void testPoll(void) CHECK(releases.count == 2); egl_texDMABUFFree(&texture->base.base); CHECK(releases.count == 2); + CHECK(releases.externalFrame.refs == 1); } static void testSaturation(void) @@ -533,6 +588,7 @@ static void testSaturation(void) egl_texDMABUFFree(&texture->base.base); CHECK(releases.count == 6); + CHECK(releases.externalFrame.refs == 1); for (uint64_t handle = 10; handle <= 60; handle += 10) CHECK(relCount(&releases, handle) == 1); } @@ -554,6 +610,24 @@ static void testRelease(void) egl_texDMABUFFree(&second->base.base); CHECK(relCount(&releases, 202) == 1); CHECK(releases.count == 2); + CHECK(releases.externalFrame.refs == 1); +} + +static void testIdentity(void) +{ + struct Releases first = { 0 }; + struct Releases second = { 0 }; + TexDMABUF * texture = newTex(); + + CHECK(update(texture, 1, &first, 1)); + CHECK(update(texture, 2, &second, 2)); + CHECK(t.imageNew == 2); + + egl_texDMABUFFree(&texture->base.base); + CHECK(first.count == 1); + CHECK(second.count == 1); + CHECK(first.externalFrame.refs == 1); + CHECK(second.externalFrame.refs == 1); } struct Test @@ -568,6 +642,7 @@ static const struct Test tests[] = { "poll" , testPoll }, { "saturation", testSaturation }, { "release" , testRelease }, + { "identity" , testIdentity }, }; int main(int argc, char ** argv) diff --git a/client/transports/LGMP/CMakeLists.txt b/client/transports/LGMP/CMakeLists.txt index d6801994..bb773ce6 100644 --- a/client/transports/LGMP/CMakeLists.txt +++ b/client/transports/LGMP/CMakeLists.txt @@ -7,6 +7,14 @@ set(TRANSPORT_LGMP_SOURCES lgmp.c ) +if(WIN32) + list(APPEND TRANSPORT_LGMP_SOURCES platform/windows.c) +elseif(UNIX) + list(APPEND TRANSPORT_LGMP_SOURCES platform/linux.c) +else() + message(FATAL_ERROR "Unsupported LGMP transport platform") +endif() + if(ENABLE_AUDIO) list(APPEND TRANSPORT_LGMP_SOURCES audio.c) endif() diff --git a/client/transports/LGMP/lgmp.c b/client/transports/LGMP/lgmp.c index 73527cf3..39154c5e 100644 --- a/client/transports/LGMP/lgmp.c +++ b/client/transports/LGMP/lgmp.c @@ -22,6 +22,7 @@ #include "clipboard.h" #include "input.h" +#include "platform.h" #ifdef ENABLE_AUDIO #include "audio.h" #endif @@ -39,11 +40,8 @@ #include -#include #include #include -#include -#include #define LGMP_TIMING_SPIN_COUNT 4096 #define LGMP_RECOVERY_PROBE_INTERVAL_US 10000U @@ -64,7 +62,7 @@ struct DMAFrameInfo const KVMFRFrame * frame; size_t offset; size_t dataSize; - int fd; + LG_ExternalFrame * externalFrame; }; #define LGMP_FRAME_LEASE_COUNT (LGMP_Q_FRAME_LEN + 1) @@ -420,65 +418,10 @@ static LG_VideoStatus lgmp_publishVideoStatus(LG_Transport * this, return published; } -static bool lgmp_deviceValidator(struct Option * opt, const char ** error) -{ - const char * transport = option_get_string("app", "transport"); - if (!transport || strcmp(transport, "lgmp") != 0) - return true; - - if (strlen(opt->value.x_string) > 3 && - memcmp(opt->value.x_string, "kvmfr", 5) != 0) - { - struct stat st; - if (stat(opt->value.x_string, &st) != 0) - { - *error = "Invalid path to the shared memory file"; - return false; - } - } - return true; -} - -static StringList lgmp_deviceValues(struct Option * option) -{ - StringList values = stringlist_new(true); - DIR * dir = opendir("/sys/class/kvmfr"); - if (!dir) - return values; - - struct dirent * entry; - while ((entry = readdir(dir))) - { - if (entry->d_name[0] == '.') - continue; - char * name; - alloc_sprintf(&name, "/dev/%s", entry->d_name); - stringlist_push(values, name); - } - closedir(dir); - return values; -} - static void lgmp_setup(void) { - struct stat st; - const char * defaultDevice = stat("/dev/kvmfr0", &st) == 0 ? - "/dev/kvmfr0" : "/dev/shm/looking-glass"; - static struct Option options[] = { - { - .module = "lgmp", - .name = "shmDevice", - .old_module = "app", - .old_name = "shmFile", - .shortopt = 'f', - .description = "Shared memory file or KVMFR device path", - .type = OPTION_TYPE_STRING, - .value.x_string = NULL, - .validator = lgmp_deviceValidator, - .getValues = lgmp_deviceValues, - }, { .module = "lgmp", .name = "allowDMA", @@ -509,7 +452,7 @@ static void lgmp_setup(void) {0} }; - options[0].value.x_string = (char *)defaultDevice; + lgmpPlatformOptionsInit(); option_register(options); } @@ -601,7 +544,7 @@ static bool lgmp_refreshRecoveryLockedCancellable( if (!wait || now >= deadline) break; - usleep(LGMP_RECOVERY_PROBE_INTERVAL_US); + nsleep((uint64_t)LGMP_RECOVERY_PROBE_INTERVAL_US * 1000U); } while (true); @@ -697,9 +640,6 @@ static bool lgmp_create(LG_Transport ** result) return false; } - for (unsigned i = 0; i < LGMP_Q_FRAME_BUFFER_LEN; ++i) - this->dma[i].fd = -1; - LG_LOCK_INIT(this->frameLock); LG_LOCK_INIT(this->pointerLock); LG_RWLOCK_INIT(this->videoStatusLock); @@ -711,8 +651,7 @@ static bool lgmp_create(LG_Transport ** result) for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i) this->frameLease[i + 1].subscription = &this->ownerFrameQueue[i]; - if (!ivshmemOpenDev(&this->shm, - option_get_string("lgmp", "shmDevice"))) + if (!lgmpPlatformOpenIVSHMEM(&this->shm)) { LG_LOCK_FREE(this->frameLock); LG_LOCK_FREE(this->pointerLock); @@ -753,6 +692,7 @@ static bool lgmp_create(LG_Transport ** result) DEBUG_ERROR("lgmpClientInit failed: %s", lgmpStatusString(status)); ivshmemClose(&this->shm); + ivshmemFree(&this->shm); LG_LOCK_FREE(this->frameLock); LG_LOCK_FREE(this->pointerLock); LG_RWLOCK_FREE(this->videoStatusLock); @@ -883,12 +823,11 @@ static void lgmp_closeDMA(struct LG_Transport * this) { for (unsigned i = 0; i < LGMP_Q_FRAME_BUFFER_LEN; ++i) { - if (this->dma[i].fd >= 0) - close(this->dma[i].fd); - this->dma[i].fd = -1; - this->dma[i].frame = NULL; - this->dma[i].offset = 0; - this->dma[i].dataSize = 0; + lgExternalFrameRelease(this->dma[i].externalFrame); + this->dma[i].externalFrame = NULL; + this->dma[i].frame = NULL; + this->dma[i].offset = 0; + this->dma[i].dataSize = 0; } } @@ -908,6 +847,7 @@ static void lgmp_destroyNow(LG_Transport * this) lgmp_closeDMA(this); free(this->pointerData); ivshmemClose(&this->shm); + ivshmemFree(&this->shm); LG_LOCK_FREE(this->frameLock); LG_LOCK_FREE(this->pointerLock); LG_RWLOCK_FREE(this->videoStatusLock); @@ -1570,7 +1510,8 @@ static LG_TransportStatus lgmp_requestRecovery(LG_Transport * this, static bool lgmp_supportsDMA(LG_Transport * this) { - return this->client && this->allowDMA && ivshmemHasDMA(&this->shm); + return this->client && this->allowDMA && + lgmpPlatformSupportsExternalFrame(&this->shm); } static bool lgmp_attachRenderer(LG_Transport * this, @@ -1909,33 +1850,34 @@ static void lgmp_selectNewestFrameMessage( *selected = candidate; } -static int lgmp_getDMA(struct LG_Transport * this, +static LG_ExternalFrame * lgmp_getDMA(struct LG_Transport * this, const KVMFRFrame * sharedFrame, size_t frameOffset, size_t dataSize) { const uintptr_t base = (uintptr_t)this->shm.mem; const uintptr_t address = (uintptr_t)sharedFrame; if (address < base) - return -1; + return NULL; const size_t position = address - base; if (position > this->lgmpSize || frameOffset > this->lgmpSize - position || sizeof(KVMFRFrameBuffer) > this->lgmpSize - position - frameOffset) - return -1; + return NULL; const size_t offset = position + frameOffset + sizeof(KVMFRFrameBuffer); if (dataSize > this->lgmpSize - offset) - return -1; + return NULL; struct DMAFrameInfo * dma = NULL; for (unsigned i = 0; i < LGMP_Q_FRAME_BUFFER_LEN; ++i) if (this->dma[i].frame == sharedFrame) { dma = &this->dma[i]; - if ((dma->offset != offset || dma->dataSize < dataSize) && dma->fd >= 0) + if ((dma->offset != offset || dma->dataSize < dataSize) && + dma->externalFrame) { - close(dma->fd); - dma->fd = -1; + lgExternalFrameRelease(dma->externalFrame); + dma->externalFrame = NULL; } break; } @@ -1950,14 +1892,15 @@ static int lgmp_getDMA(struct LG_Transport * this, } if (!dma) - return -1; - if (dma->fd >= 0) - return dma->fd; + return NULL; + if (dma->externalFrame) + return dma->externalFrame; - dma->offset = offset; - dma->dataSize = dataSize; - dma->fd = ivshmemGetDMABuf(&this->shm, offset, dataSize); - return dma->fd; + dma->offset = offset; + dma->dataSize = dataSize; + dma->externalFrame = lgmpPlatformCreateExternalFrame( + &this->shm, offset, dataSize); + return dma->externalFrame; } static void lgmp_releaseFrameLease(void * opaque, uint64_t handle); @@ -2128,18 +2071,18 @@ static LG_TransportStatus lgmp_nextFrameLocked(LG_Transport * this, result->framebuffer = (const KVMFRFrameBuffer *) ((const uint8_t *)selected->sharedFrame + frame->offset); - result->dmaFD = -1; if (useDMA) { - result->dmaFD = lgmp_getDMA(this, selected->sharedFrame, + LG_ExternalFrame * externalFrame = lgmp_getDMA(this, selected->sharedFrame, frame->offset, selected->dataSize); - if (result->dmaFD < 0) + if (!externalFrame) { const LG_TransportStatus done = lgmp_doneFrameMessage(selected); - DEBUG_ERROR("Failed to obtain a DMA buffer for the frame"); + DEBUG_ERROR("Failed to obtain an external frame handle"); return done == LG_TRANSPORT_DISCONNECTED ? done : LG_TRANSPORT_ERROR; } + result->externalFrame = lgExternalFrameRetain(externalFrame); } if (!fullDamage && frame->damageRectsCount <= KVMFR_MAX_DAMAGE_RECTS) @@ -2308,6 +2251,9 @@ static void lgmp_releaseFrameLease(void * opaque, uint64_t handle) static void lgmp_releaseFrame(LG_Transport * this, LG_TransportFrame * frame) { + lgExternalFrameRelease(frame->externalFrame); + frame->externalFrame = NULL; + if (frame->releaseFn) { LG_LOCK(this->frameLock); diff --git a/client/transports/LGMP/platform.h b/client/transports/LGMP/platform.h new file mode 100644 index 00000000..f7ce2336 --- /dev/null +++ b/client/transports/LGMP/platform.h @@ -0,0 +1,36 @@ +/** + * 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 + */ + +#ifndef _H_LG_CLIENT_TRANSPORT_LGMP_PLATFORM_ +#define _H_LG_CLIENT_TRANSPORT_LGMP_PLATFORM_ + +#include "common/ivshmem.h" +#include "interface/external_frame.h" + +#include +#include + +void lgmpPlatformOptionsInit(void); +bool lgmpPlatformOpenIVSHMEM(struct IVSHMEM * shm); +bool lgmpPlatformSupportsExternalFrame(struct IVSHMEM * shm); +LG_ExternalFrame * lgmpPlatformCreateExternalFrame(struct IVSHMEM * shm, + size_t offset, size_t size); + +#endif diff --git a/client/transports/LGMP/platform/linux.c b/client/transports/LGMP/platform/linux.c new file mode 100644 index 00000000..f75b9f23 --- /dev/null +++ b/client/transports/LGMP/platform/linux.c @@ -0,0 +1,189 @@ +/** + * 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 "../platform.h" + +#include "common/debug.h" +#include "common/ivshmem_dmabuf.h" +#include "common/option.h" +#include "common/stringutils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct LGMPExternalFrame +{ + LG_ExternalFrame base; + atomic_uint refs; + int fd; +} +LGMPExternalFrame; + +static void lgmpExternalFrameRetain(LG_ExternalFrame * frame) +{ + LGMPExternalFrame * this = (LGMPExternalFrame *)frame; + const unsigned int refs = atomic_fetch_add_explicit( + &this->refs, 1, memory_order_relaxed); + DEBUG_ASSERT(refs > 0 && refs < UINT_MAX); +} + +static void lgmpExternalFrameRelease(LG_ExternalFrame * frame) +{ + LGMPExternalFrame * this = (LGMPExternalFrame *)frame; + const unsigned int refs = atomic_fetch_sub_explicit( + &this->refs, 1, memory_order_acq_rel); + DEBUG_ASSERT(refs > 0); + if (refs != 1) + return; + + close(this->fd); + free(this); +} + +static bool lgmpExternalFrameQuery(const LG_ExternalFrame * frame, + void * result, size_t resultSize) +{ + if (!result || resultSize != sizeof(int)) + return false; + + const LGMPExternalFrame * this = (const LGMPExternalFrame *)frame; + *(int *)result = this->fd; + return true; +} + +static const LG_ExternalFrameOps lgmpExternalFrameOps = +{ + .type = LG_EXTERNAL_FRAME_DMABUF, + .retain = lgmpExternalFrameRetain, + .release = lgmpExternalFrameRelease, + .query = lgmpExternalFrameQuery, +}; + +static bool lgmpDeviceValidator(struct Option * opt, const char ** error) +{ + const char * transport = option_get_string("app", "transport"); + if (!transport || strcmp(transport, "lgmp") != 0) + return true; + + if (strlen(opt->value.x_string) > 3 && + strncmp(opt->value.x_string, "kvmfr", 5) != 0) + { + struct stat st; + if (stat(opt->value.x_string, &st) != 0) + { + *error = "Invalid path to the shared memory file"; + return false; + } + } + return true; +} + +static StringList lgmpDeviceValues(struct Option * option) +{ + StringList values = stringlist_new(true); + DIR * dir = opendir("/sys/class/kvmfr"); + if (!dir) + return values; + + struct dirent * entry; + while ((entry = readdir(dir))) + { + if (entry->d_name[0] == '.') + continue; + + char * name; + alloc_sprintf(&name, "/dev/%s", entry->d_name); + stringlist_push(values, name); + } + + closedir(dir); + return values; +} + +void lgmpPlatformOptionsInit(void) +{ + struct stat st; + const char * defaultDevice = stat("/dev/kvmfr0", &st) == 0 ? + "/dev/kvmfr0" : "/dev/shm/looking-glass"; + + static struct Option options[] = + { + { + .module = "lgmp", + .name = "shmDevice", + .old_module = "app", + .old_name = "shmFile", + .shortopt = 'f', + .description = "Shared memory file or KVMFR device path", + .type = OPTION_TYPE_STRING, + .value.x_string = NULL, + .validator = lgmpDeviceValidator, + .getValues = lgmpDeviceValues, + }, + {0} + }; + + options[0].value.x_string = (char *)defaultDevice; + option_register(options); +} + +bool lgmpPlatformOpenIVSHMEM(struct IVSHMEM * shm) +{ + if (!ivshmemInit(shm)) + return false; + + if (ivshmemOpenDev(shm, option_get_string("lgmp", "shmDevice"), + IVSHMEM_CACHE_MODE_CACHED)) + return true; + + ivshmemFree(shm); + return false; +} + +bool lgmpPlatformSupportsExternalFrame(struct IVSHMEM * shm) +{ + return ivshmemHasDMA(shm); +} + +LG_ExternalFrame * lgmpPlatformCreateExternalFrame(struct IVSHMEM * shm, + size_t offset, size_t size) +{ + const int fd = ivshmemGetDMABuf(shm, offset, size); + if (fd < 0) + return NULL; + + LGMPExternalFrame * frame = malloc(sizeof(*frame)); + if (!frame) + { + close(fd); + return NULL; + } + + frame->base.ops = &lgmpExternalFrameOps; + frame->fd = fd; + atomic_init(&frame->refs, 1); + return &frame->base; +} diff --git a/client/transports/LGMP/platform/windows.c b/client/transports/LGMP/platform/windows.c new file mode 100644 index 00000000..a95c8274 --- /dev/null +++ b/client/transports/LGMP/platform/windows.c @@ -0,0 +1,53 @@ +/** + * 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 "../platform.h" + +void lgmpPlatformOptionsInit(void) +{ + ivshmemOptionsInit(); +} + +bool lgmpPlatformOpenIVSHMEM(struct IVSHMEM * shm) +{ + if (!ivshmemInit(shm)) + return false; + + if (ivshmemOpen(shm, IVSHMEM_CACHE_MODE_CACHED)) + return true; + + ivshmemFree(shm); + return false; +} + +bool lgmpPlatformSupportsExternalFrame(struct IVSHMEM * shm) +{ + (void)shm; + return false; +} + +LG_ExternalFrame * lgmpPlatformCreateExternalFrame(struct IVSHMEM * shm, + size_t offset, size_t size) +{ + (void)shm; + (void)offset; + (void)size; + return NULL; +} diff --git a/client/transports/Test/test.c b/client/transports/Test/test.c index 38396cf9..56fc2625 100644 --- a/client/transports/Test/test.c +++ b/client/transports/Test/test.c @@ -640,7 +640,6 @@ static LG_TransportStatus test_nextFrame(LG_Transport * this, bool useDMA, this->serial * (1000000000ULL / this->frameRate); frame->format = &this->format; frame->framebuffer = buffer->framebuffer; - frame->dmaFD = -1; if (this->damageMode == TEST_DAMAGE_FULL || this->serial == 1 || formatChanged) diff --git a/common/include/common/ivshmem.h b/common/include/common/ivshmem.h index 6320b391..4508aa3d 100644 --- a/common/include/common/ivshmem.h +++ b/common/include/common/ivshmem.h @@ -22,26 +22,34 @@ #define _H_LG_COMMON_IVSHMEM_ #include +#include #include +typedef enum IVSHMEMCacheMode +{ + /* Producers normally use write-combined mappings; consumers use cached. + * Platforms without selectable cache modes ignore this policy. */ + IVSHMEM_CACHE_MODE_NONCACHED = 0, + IVSHMEM_CACHE_MODE_CACHED = 1, + IVSHMEM_CACHE_MODE_WRITECOMBINED = 2, +} +IVSHMEMCacheMode; + struct IVSHMEM { - unsigned int size; - void * mem; + size_t size; + void * mem; // internal use - void * opaque; + void * opaque; }; void ivshmemOptionsInit(void); bool ivshmemInit(struct IVSHMEM * dev); -bool ivshmemOpen(struct IVSHMEM * dev); -bool ivshmemOpenDev(struct IVSHMEM * dev, const char * shmDev); +bool ivshmemOpen(struct IVSHMEM * dev, IVSHMEMCacheMode cacheMode); +bool ivshmemOpenDev(struct IVSHMEM * dev, const char * shmDev, + IVSHMEMCacheMode cacheMode); void ivshmemClose(struct IVSHMEM * dev); void ivshmemFree(struct IVSHMEM * dev); -/* Linux KVMFR support only for now (VM->VM) */ -bool ivshmemHasDMA (struct IVSHMEM * dev); -int ivshmemGetDMABuf(struct IVSHMEM * dev, uint64_t offset, uint64_t size); - #endif diff --git a/common/include/common/ivshmem_dmabuf.h b/common/include/common/ivshmem_dmabuf.h new file mode 100644 index 00000000..47349f66 --- /dev/null +++ b/common/include/common/ivshmem_dmabuf.h @@ -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 + */ + +#ifndef _H_LG_COMMON_IVSHMEM_DMABUF_ +#define _H_LG_COMMON_IVSHMEM_DMABUF_ + +#include "ivshmem.h" + +#include + +bool ivshmemHasDMA (struct IVSHMEM * dev); +int ivshmemGetDMABuf(struct IVSHMEM * dev, uint64_t offset, uint64_t size); + +#endif diff --git a/common/src/platform/linux/ivshmem.c b/common/src/platform/linux/ivshmem.c index 392585c4..c6c3df0a 100644 --- a/common/src/platform/linux/ivshmem.c +++ b/common/src/platform/linux/ivshmem.c @@ -18,7 +18,7 @@ * Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "common/ivshmem.h" +#include "common/ivshmem_dmabuf.h" #include #include @@ -39,9 +39,9 @@ struct IVSHMEMInfo { - int devFd; - int size; - bool hasDMA; + int devFd; + size_t size; + bool hasDMA; }; static bool ivshmemDeviceValidator(struct Option * opt, const char ** error) @@ -115,23 +115,34 @@ void ivshmemOptionsInit(void) bool ivshmemInit(struct IVSHMEM * dev) { - // FIXME: split code from ivshmemOpen + DEBUG_ASSERT(dev && !dev->opaque); + + dev->size = 0; + dev->mem = NULL; + dev->opaque = NULL; return true; } -bool ivshmemOpen(struct IVSHMEM * dev) +bool ivshmemOpen(struct IVSHMEM * dev, IVSHMEMCacheMode cacheMode) { - return ivshmemOpenDev(dev, option_get_string("app", "shmFile")); + return ivshmemOpenDev(dev, option_get_string("app", "shmFile"), + cacheMode); } -bool ivshmemOpenDev(struct IVSHMEM * dev, const char * shmDevice) +bool ivshmemOpenDev(struct IVSHMEM * dev, const char * shmDevice, + IVSHMEMCacheMode cacheMode) { - DEBUG_ASSERT(dev); + DEBUG_ASSERT(dev && !dev->opaque && !dev->mem); - int devSize; - int devFd; - bool hasDMA; + size_t devSize; + int devFd; + bool hasDMA; + /* POSIX mmap does not expose the Windows IVSHMEM driver's cache modes. */ + (void)cacheMode; + + dev->size = 0; + dev->mem = NULL; dev->opaque = NULL; DEBUG_INFO("KVMFR Device : %s", shmDevice); @@ -147,14 +158,15 @@ bool ivshmemOpenDev(struct IVSHMEM * dev, const char * shmDevice) } // get the device size - devSize = ioctl(devFd, KVMFR_DMABUF_GETSIZE, 0); - if (devSize < 0) + const int size = ioctl(devFd, KVMFR_DMABUF_GETSIZE, 0); + if (size <= 0) { DEBUG_ERROR("Failed to get the device size"); close(devFd); return false; } - hasDMA = true; + devSize = (size_t)size; + hasDMA = true; } else { @@ -175,7 +187,14 @@ bool ivshmemOpenDev(struct IVSHMEM * dev, const char * shmDevice) return false; } - devSize = st.st_size; + if (st.st_size <= 0 || (uintmax_t)st.st_size > SIZE_MAX) + { + DEBUG_ERROR("Invalid shared memory size: %s", shmDevice); + close(devFd); + return false; + } + + devSize = (size_t)st.st_size; hasDMA = false; } @@ -189,6 +208,14 @@ bool ivshmemOpenDev(struct IVSHMEM * dev, const char * shmDevice) } struct IVSHMEMInfo * info = malloc(sizeof(*info)); + if (!info) + { + DEBUG_ERROR("Failed to allocate IVSHMEM device state"); + munmap(map, devSize); + close(devFd); + return false; + } + info->size = devSize; info->devFd = devFd; info->hasDMA = hasDMA; @@ -220,7 +247,10 @@ void ivshmemClose(struct IVSHMEM * dev) void ivshmemFree(struct IVSHMEM * dev) { - // FIXME: split code from ivshmemClose + DEBUG_ASSERT(dev); + + if (dev->opaque) + ivshmemClose(dev); } bool ivshmemHasDMA(struct IVSHMEM * dev) @@ -237,7 +267,7 @@ int ivshmemGetDMABuf(struct IVSHMEM * dev, uint64_t offset, uint64_t size) { DEBUG_ASSERT(ivshmemHasDMA(dev)); DEBUG_ASSERT(dev && dev->opaque); - DEBUG_ASSERT(offset + size <= dev->size); + DEBUG_ASSERT(offset <= dev->size && size <= dev->size - offset); static long pageSize = 0; diff --git a/common/src/platform/windows/ivshmem.c b/common/src/platform/windows/ivshmem.c index c99fb2a8..81b4845f 100644 --- a/common/src/platform/windows/ivshmem.c +++ b/common/src/platform/windows/ivshmem.c @@ -27,7 +27,8 @@ #include "ivshmem.h" #include -#include +#include +#include struct IVSHMEMInfo { @@ -70,24 +71,57 @@ static int ivshmemComparator(const void * a_, const void * b_) return 0; } +static bool ivshmemCacheModeToDriver(IVSHMEMCacheMode cacheMode, + UINT8 * driverMode) +{ + switch (cacheMode) + { + case IVSHMEM_CACHE_MODE_NONCACHED: + *driverMode = IVSHMEM_CACHE_NONCACHED; + return true; + + case IVSHMEM_CACHE_MODE_CACHED: + *driverMode = IVSHMEM_CACHE_CACHED; + return true; + + case IVSHMEM_CACHE_MODE_WRITECOMBINED: + *driverMode = IVSHMEM_CACHE_WRITECOMBINED; + return true; + } + + DEBUG_ERROR("Invalid IVSHMEM cache mode: %d", (int)cacheMode); + return false; +} + bool ivshmemInit(struct IVSHMEM * dev) { - DEBUG_ASSERT(dev && !dev->opaque); + DEBUG_ASSERT(dev && !dev->opaque && !dev->mem); - HANDLE handle; - HDEVINFO devInfoSet; - PSP_DEVICE_INTERFACE_DETAIL_DATA infData = NULL; - SP_DEVINFO_DATA devInfoData = {0}; + dev->size = 0; + dev->mem = NULL; + dev->opaque = NULL; + + HANDLE handle = INVALID_HANDLE_VALUE; + HDEVINFO devInfoSet = INVALID_HANDLE_VALUE; + PSP_DEVICE_INTERFACE_DETAIL_DATA infData = NULL; + SP_DEVINFO_DATA devInfoData = {0}; SP_DEVICE_INTERFACE_DATA devInterfaceData = {0}; Vector devices; devInfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IVSHMEM, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (devInfoSet == INVALID_HANDLE_VALUE) + { + DEBUG_WINERROR("SetupDiGetClassDevs failed", GetLastError()); + return false; + } + devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); if (!vector_create(&devices, sizeof(struct IVSHMEMData), 1)) { + SetupDiDestroyDeviceInfoList(devInfoSet); DEBUG_ERROR("Failed to allocate memory"); return false; } @@ -95,8 +129,15 @@ bool ivshmemInit(struct IVSHMEM * dev) for (int i = 0; SetupDiEnumDeviceInfo(devInfoSet, i, &devInfoData); ++i) { struct IVSHMEMData * device = vector_push(&devices, NULL); + if (!device) + { + vector_destroy(&devices); + SetupDiDestroyDeviceInfoList(devInfoSet); + return false; + } - DWORD bus, addr; + DWORD bus; + DWORD addr; if (!SetupDiGetDeviceRegistryProperty(devInfoSet, &devInfoData, SPDRP_BUSNUMBER, NULL, (void*) &bus, sizeof(bus), NULL)) { @@ -117,16 +158,19 @@ bool ivshmemInit(struct IVSHMEM * dev) memcpy(&device->devInfoData, &devInfoData, sizeof(SP_DEVINFO_DATA)); } - if (GetLastError() != ERROR_NO_MORE_ITEMS) + const DWORD enumError = GetLastError(); + if (enumError != ERROR_NO_MORE_ITEMS) { vector_destroy(&devices); - DEBUG_WINERROR("SetupDiEnumDeviceInfo failed", GetLastError()); + SetupDiDestroyDeviceInfoList(devInfoSet); + DEBUG_WINERROR("SetupDiEnumDeviceInfo failed", enumError); return false; } if (vector_size(&devices) == 0) { vector_destroy(&devices); + SetupDiDestroyDeviceInfoList(devInfoSet); DEBUG_ERROR("Failed to find any IVSHMEM devices, unable to continue"); DEBUG_ERROR("Did you remember to add the device to your VM " "and is the driver installed?"); @@ -145,15 +189,16 @@ bool ivshmemInit(struct IVSHMEM * dev) DEBUG_INFO( "IVSHMEM %" PRIuPTR "%c on bus 0x%lx, device 0x%lx, function 0x%lx", i, - i == shmDevice ? '*' : ' ', + i == (uintptr_t)shmDevice ? '*' : ' ', bus, addr >> 16, addr & 0xFFFF); } - if (shmDevice >= vector_size(&devices)) + if (shmDevice < 0 || (uintptr_t)shmDevice >= vector_size(&devices)) { vector_destroy(&devices); + SetupDiDestroyDeviceInfoList(devInfoSet); DEBUG_ERROR("os:shmDevice %d does not exist", shmDevice); return false; } @@ -165,7 +210,9 @@ bool ivshmemInit(struct IVSHMEM * dev) if (SetupDiEnumDeviceInterfaces(devInfoSet, &devInfoData, &GUID_DEVINTERFACE_IVSHMEM, 0, &devInterfaceData) == FALSE) { - DEBUG_WINERROR("SetupDiEnumDeviceInterfaces failed", GetLastError()); + const DWORD error = GetLastError(); + SetupDiDestroyDeviceInfoList(devInfoSet); + DEBUG_WINERROR("SetupDiEnumDeviceInterfaces failed", error); return false; } @@ -174,25 +221,37 @@ bool ivshmemInit(struct IVSHMEM * dev) NULL, 0, &reqSize, NULL); if (!reqSize) { - DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError()); + const DWORD error = GetLastError(); + SetupDiDestroyDeviceInfoList(devInfoSet); + DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", error); return false; } infData = calloc(1, reqSize); + if (!infData) + { + SetupDiDestroyDeviceInfoList(devInfoSet); + DEBUG_ERROR("Failed to allocate IVSHMEM interface data"); + return false; + } infData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); if (!SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, infData, reqSize, NULL, NULL)) { + const DWORD error = GetLastError(); free(infData); - DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError()); + SetupDiDestroyDeviceInfoList(devInfoSet); + DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", error); return false; } handle = CreateFile(infData->DevicePath, 0, 0, NULL, OPEN_EXISTING, 0, 0); if (handle == INVALID_HANDLE_VALUE) { + const DWORD error = GetLastError(); + free(infData); SetupDiDestroyDeviceInfoList(devInfoSet); - DEBUG_WINERROR("CreateFile returned INVALID_HANDLE_VALUE", GetLastError()); + DEBUG_WINERROR("CreateFile returned INVALID_HANDLE_VALUE", error); return false; } @@ -200,6 +259,12 @@ bool ivshmemInit(struct IVSHMEM * dev) SetupDiDestroyDeviceInfoList(devInfoSet); struct IVSHMEMInfo * info = malloc(sizeof(*info)); + if (!info) + { + CloseHandle(handle); + DEBUG_ERROR("Failed to allocate IVSHMEM device state"); + return false; + } info->handle = handle; dev->opaque = info; @@ -209,12 +274,16 @@ bool ivshmemInit(struct IVSHMEM * dev) return true; } -bool ivshmemOpen(struct IVSHMEM * dev) +bool ivshmemOpen(struct IVSHMEM * dev, IVSHMEMCacheMode cacheMode) { DEBUG_ASSERT(dev && dev->opaque && !dev->mem); struct IVSHMEMInfo * info = (struct IVSHMEMInfo *)dev->opaque; + UINT8 driverMode; + if (!ivshmemCacheModeToDriver(cacheMode, &driverMode)) + return false; + IVSHMEM_SIZE size; if (!DeviceIoControl(info->handle, IOCTL_IVSHMEM_REQUEST_SIZE, NULL, 0, &size, sizeof(IVSHMEM_SIZE), NULL, NULL)) @@ -223,8 +292,15 @@ bool ivshmemOpen(struct IVSHMEM * dev) return false; } - IVSHMEM_MMAP_CONFIG config = { .cacheMode = IVSHMEM_CACHE_WRITECOMBINED }; - IVSHMEM_MMAP map = { 0 }; + const size_t mapSize = (size_t)size; + if (!mapSize || (IVSHMEM_SIZE)mapSize != size) + { + DEBUG_ERROR("Invalid IVSHMEM size: %" PRIu64, (uint64_t)size); + return false; + } + + IVSHMEM_MMAP_CONFIG config = { .cacheMode = driverMode }; + IVSHMEM_MMAP map = { 0 }; if (!DeviceIoControl( info->handle, IOCTL_IVSHMEM_REQUEST_MMAP, @@ -236,14 +312,36 @@ bool ivshmemOpen(struct IVSHMEM * dev) return false; } - dev->size = (unsigned int)size; - dev->mem = map.ptr; + if (!map.ptr || map.size != size) + { + DEBUG_ERROR("IVSHMEM driver returned an invalid mapping"); + DeviceIoControl(info->handle, IOCTL_IVSHMEM_RELEASE_MMAP, NULL, 0, NULL, + 0, NULL, NULL); + return false; + } + + dev->size = mapSize; + dev->mem = map.ptr; return true; } +bool ivshmemOpenDev(struct IVSHMEM * dev, const char * shmDev, + IVSHMEMCacheMode cacheMode) +{ + DEBUG_ASSERT(dev); + (void)shmDev; + (void)cacheMode; + + DEBUG_ERROR("Opening an IVSHMEM device by path is not supported on Windows"); + return false; +} + void ivshmemClose(struct IVSHMEM * dev) { - DEBUG_ASSERT(dev && dev->opaque && dev->mem); + DEBUG_ASSERT(dev); + + if (!dev->opaque || !dev->mem) + return; struct IVSHMEMInfo * info = (struct IVSHMEMInfo *)dev->opaque; @@ -257,10 +355,17 @@ void ivshmemClose(struct IVSHMEM * dev) void ivshmemFree(struct IVSHMEM * dev) { - DEBUG_ASSERT(dev && dev->opaque && !dev->mem); + DEBUG_ASSERT(dev); + + if (!dev->opaque) + return; + + if (dev->mem) + ivshmemClose(dev); struct IVSHMEMInfo * info = (struct IVSHMEMInfo *)dev->opaque; + CloseHandle(info->handle); free(info); dev->opaque = NULL; } diff --git a/host/src/app.c b/host/src/app.c index 69bbeb1e..38af87da 100644 --- a/host/src/app.c +++ b/host/src/app.c @@ -834,12 +834,18 @@ static bool newKVMFRData(KVMFRUserData * dst) static bool lgmpSetup(struct IVSHMEM * shmDev) { + if (shmDev->size > UINT32_MAX) + { + DEBUG_ERROR("IVSHMEM size exceeds LGMP's 32-bit host limit"); + return false; + } + KVMFRUserData udata = { 0 }; if (!newKVMFRData(&udata)) goto fail_init; LGMP_STATUS status; - if ((status = lgmpHostInit(shmDev->mem, shmDev->size, &app.lgmp, + if ((status = lgmpHostInit(shmDev->mem, (uint32_t)shmDev->size, &app.lgmp, udata.used, udata.data)) != LGMP_OK) { DEBUG_ERROR("lgmpHostInit Failed: %s", lgmpStatusString(status)); @@ -1011,15 +1017,17 @@ int app_main(int argc, char * argv[]) return LG_HOST_EXIT_FATAL; } - if (!ivshmemOpen(&shmDev)) + if (!ivshmemOpen(&shmDev, IVSHMEM_CACHE_MODE_WRITECOMBINED)) { DEBUG_ERROR("Failed to open the IVSHMEM device"); + ivshmemFree(&shmDev); return LG_HOST_EXIT_FATAL; } app.ivshmemBase = shmDev.mem; int exitcode = 0; - DEBUG_INFO("IVSHMEM Size : %u MiB", shmDev.size / 1048576); + DEBUG_INFO("IVSHMEM Size : %" PRIu64 " MiB", + (uint64_t)shmDev.size / 1048576); DEBUG_INFO("IVSHMEM Address : 0x%" PRIXPTR, (uintptr_t)shmDev.mem); DEBUG_INFO("Max Pointer Size : %u KiB", (unsigned int)MAX_POINTER_SIZE / 1024); DEBUG_INFO("KVMFR Version : %u", KVMFR_VERSION); diff --git a/obs/lg.c b/obs/lg.c index 1668f3bf..be60a7da 100644 --- a/obs/lg.c +++ b/obs/lg.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -970,7 +971,8 @@ static void lgUpdate(void * data, obs_data_t * settings) lgObsAudioSetEnabled(this->audio, obs_data_get_bool(settings, "captureAudio")); this->shmFile = bstrdup(obs_data_get_string(settings, "shmFile")); - if (!ivshmemOpenDev(&this->shmDev, this->shmFile)) + if (!ivshmemOpenDev(&this->shmDev, this->shmFile, + IVSHMEM_CACHE_MODE_CACHED)) return; this->hideMouse = obs_data_get_bool(settings, "hideMouse") ? 1 : 0; diff --git a/profile/client/src/main.c b/profile/client/src/main.c index 0cd7ee39..f5e5f806 100644 --- a/profile/client/src/main.c +++ b/profile/client/src/main.c @@ -229,7 +229,7 @@ int main(int argc, char * argv[]) state.running = true; int ret = -1; - if (ivshmemOpen(&state.shmDev)) + if (ivshmemOpen(&state.shmDev, IVSHMEM_CACHE_MODE_CACHED)) ret = run(); ivshmemClose(&state.shmDev);