[common/client] ivshmem: add portable external frames

This commit is contained in:
Geoffrey McRae
2026-09-12 09:49:24 +10:00
parent 6d367d4ab2
commit 382f35e503
28 changed files with 946 additions and 227 deletions

View File

@@ -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}

View File

@@ -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;
}

View File

@@ -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 <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#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;
}

View File

@@ -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)