diff --git a/client/include/interface/external_frame.h b/client/include/interface/external_frame.h index 82d46dcf..b1d34120 100644 --- a/client/include/interface/external_frame.h +++ b/client/include/interface/external_frame.h @@ -23,14 +23,39 @@ #include #include +#include typedef enum LG_ExternalFrameType { - LG_EXTERNAL_FRAME_INVALID = 0, - LG_EXTERNAL_FRAME_DMABUF = 1, + LG_EXTERNAL_FRAME_INVALID = 0, + LG_EXTERNAL_FRAME_DMABUF = 1, + LG_EXTERNAL_FRAME_EGL_TEXTURE = 2, } LG_ExternalFrameType; +#define LG_EXTERNAL_FRAME_EGL_TEXTURE_VERSION 1U + +/* + * A texture created in a context which shares objects with the renderer's EGL + * context. The producer must perform all decode and colour conversion on the + * GPU and expose a completed RGB(A) GL_TEXTURE_2D; this interface deliberately + * provides no CPU pixel or YUV conversion path. + * + * readySync is an optional GLsync encoded as an integer-sized handle. The + * consumer waits on it before sampling, but does not delete it. Both the + * texture and readySync remain owned by the external frame and remain valid + * while the consumer holds a reference to that frame. + */ +typedef struct LG_ExternalFrameEGLTexture +{ + uint32_t version; + uint32_t size; + uint32_t texture; + uint32_t target; + uintptr_t readySync; +} +LG_ExternalFrameEGLTexture; + typedef struct LG_ExternalFrame LG_ExternalFrame; /* @@ -41,7 +66,8 @@ typedef struct LG_ExternalFrame LG_ExternalFrame; * never close or otherwise release a queried resource. * * LG_EXTERNAL_FRAME_DMABUF queries return one native file descriptor in an - * int-sized result buffer. + * int-sized result buffer. LG_EXTERNAL_FRAME_EGL_TEXTURE queries return an + * LG_ExternalFrameEGLTexture. */ typedef struct LG_ExternalFrameOps { diff --git a/client/include/interface/renderer.h b/client/include/interface/renderer.h index 4795e056..cab98768 100644 --- a/client/include/interface/renderer.h +++ b/client/include/interface/renderer.h @@ -216,19 +216,21 @@ typedef enum LG_RendererInteropType } LG_RendererInteropType; -#define LG_RENDERER_INTEROP_VERSION 1 +#define LG_RENDERER_INTEROP_EGL_TEXTURE_VERSION 2 +#define LG_RENDERER_INTEROP_VERSION 2 typedef struct LG_RendererInterop { - uint32_t version; - uint32_t size; + uint32_t version; + uint32_t size; LG_RendererInteropType type; struct { EGLDisplay display; - EGLConfig config; + EGLConfig config; EGLContext shareContext; - bool dmaBufImport; + bool dmaBufImport; + bool sharedTextureImport; } egl; } diff --git a/client/renderers/EGL/CMakeLists.txt b/client/renderers/EGL/CMakeLists.txt index 716dd89b..307a3de3 100644 --- a/client/renderers/EGL/CMakeLists.txt +++ b/client/renderers/EGL/CMakeLists.txt @@ -73,6 +73,7 @@ set(RENDERER_EGL_SOURCES texture_util.c texture.c texture_buffer.c + texture_external.c texture_framebuffer.c model.c desktop.c diff --git a/client/renderers/EGL/desktop.c b/client/renderers/EGL/desktop.c index 2f1e41bd..aa727810 100644 --- a/client/renderers/EGL/desktop.c +++ b/client/renderers/EGL/desktop.c @@ -73,6 +73,7 @@ struct EGL_Desktop EGLDisplay * display; EGL_Texture * texture; + EGL_Texture * externalTexture; struct DesktopShader shader; EGL_DesktopRects * mesh; GLfloat matrix[6]; @@ -97,7 +98,7 @@ struct EGL_Desktop // colorblind mode int cbMode; - bool useDMA; + bool useDMA; LG_RendererFormat format; // map HDR content to SDR @@ -109,6 +110,7 @@ struct EGL_Desktop EGL_PostProcess * pp; _Atomic(bool) processFrame; + _Atomic(bool) externalActive; }; // forwards @@ -179,6 +181,13 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display, return false; } + if (!egl_textureInit( + &desktop->externalTexture, display, EGL_TEXTYPE_EXTERNAL)) + { + DEBUG_ERROR("Failed to initialize the external desktop texture"); + return false; + } + if (!egl_desktopRectsInit(&desktop->mesh, maxRects)) { DEBUG_ERROR("Failed to initialize the desktop mesh"); @@ -252,6 +261,7 @@ void egl_desktopFree(EGL_Desktop ** desktop) return; egl_textureFree (&(*desktop)->texture ); + egl_textureFree (&(*desktop)->externalTexture ); egl_textureFree (&(*desktop)->swSurfaceTexture); egl_shaderFree (&(*desktop)->shader .shader); egl_desktopRectsFree(&(*desktop)->mesh ); @@ -377,6 +387,19 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format) return false; } + if (!egl_textureSetup( + desktop->externalTexture, + pixFmt, + desktop->format.dataWidth, + desktop->format.dataHeight, + desktop->format.stride, + desktop->format.pitch + )) + { + DEBUG_ERROR("Failed to setup the external desktop texture"); + return false; + } + return true; } @@ -387,6 +410,21 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, uint64_t * waitTimeNs, LG_FrameReleaseFn releaseFn, void * releaseOpaque, uint64_t releaseHandle) { + const LG_ExternalFrameType externalType = + lgExternalFrameType(externalFrame); + + if (externalType == LG_EXTERNAL_FRAME_EGL_TEXTURE) + { + if (!egl_textureUpdateFromExternal(desktop->externalTexture, frameToken, + externalFrame, releaseFn, releaseOpaque, releaseHandle)) + return false; + + lgExternalFrameRelease(externalFrame); + atomic_store(&desktop->externalActive, true); + atomic_store(&desktop->processFrame, true); + return true; + } + /* * Both the DMA and staged upload paths need a complete frame. Do this * before selecting either path so a producer that is still filling an @@ -402,13 +440,16 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, return true; } - if (likely(desktop->useDMA && externalFrame)) + if (likely(desktop->useDMA && + externalType == LG_EXTERNAL_FRAME_DMABUF)) { if (likely(egl_textureUpdateFromDMA( desktop->texture, frame, frameToken, externalFrame, waitTimeNs, releaseFn, releaseOpaque, releaseHandle))) { lgExternalFrameRelease(externalFrame); + atomic_store(&desktop->externalActive, false); + egl_textureDeactivate(desktop->externalTexture); atomic_store(&desktop->processFrame, true); return true; } @@ -447,6 +488,8 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, lgExternalFrameRelease(externalFrame); if (releaseFn) releaseFn(releaseOpaque, releaseHandle); + atomic_store(&desktop->externalActive, false); + egl_textureDeactivate(desktop->externalTexture); return true; } @@ -456,12 +499,15 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, void egl_desktopRestart(EGL_Desktop * desktop) { egl_textureReset(desktop->texture); + egl_textureReset(desktop->externalTexture); atomic_store(&desktop->processFrame, false); + atomic_store(&desktop->externalActive, false); } void egl_desktopPoll(EGL_Desktop * desktop) { egl_texturePoll(desktop->texture); + egl_texturePoll(desktop->externalTexture); } void egl_desktopResize(EGL_Desktop * desktop, int width, int height) @@ -485,6 +531,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, /* This also retires completed snapshots while the software surface is * shown. */ egl_texturePoll(desktop->texture); + egl_texturePoll(desktop->externalTexture); if (unlikely(desktop->useSwSurface)) { @@ -494,7 +541,8 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, } else { - tex = desktop->texture; + tex = atomic_load(&desktop->externalActive) ? + desktop->externalTexture : desktop->texture; width = desktop->width; height = desktop->height; } @@ -592,8 +640,9 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, { if (bindStatus != EGL_TEX_STATUS_NOTREADY) DEBUG_ERROR("Failed to bind the desktop texture"); - if (!desktop->useSwSurface && desktop->useDMA && processFrame) - egl_textureMarkUsed(desktop->texture); + if (!desktop->useSwSurface && + (tex == desktop->externalTexture || desktop->useDMA) && processFrame) + egl_textureMarkUsed(tex); return false; } *renderedFrameToken = tex->frameToken; @@ -646,9 +695,10 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth, desktop->linearComposition); egl_shaderUse(shader->shader); egl_desktopRectsRender(desktop->mesh); - if (!desktop->useSwSurface && desktop->useDMA && + if (!desktop->useSwSurface && + (tex == desktop->externalTexture || desktop->useDMA) && (processFrame || texture == tex)) - egl_textureMarkUsed(desktop->texture); + egl_textureMarkUsed(tex); return true; } diff --git a/client/renderers/EGL/egl.c b/client/renderers/EGL/egl.c index 6bd29662..ba40e2ce 100644 --- a/client/renderers/EGL/egl.c +++ b/client/renderers/EGL/egl.c @@ -366,12 +366,13 @@ static bool egl_getInterop(LG_Renderer * renderer, interop->size < sizeof(*interop)) return false; - interop->version = LG_RENDERER_INTEROP_VERSION; - interop->type = LG_RENDERER_INTEROP_EGL; - interop->egl.display = this->display; - interop->egl.config = this->configs; - interop->egl.shareContext = this->context; - interop->egl.dmaBufImport = this->dmaSupport; + interop->version = LG_RENDERER_INTEROP_VERSION; + interop->type = LG_RENDERER_INTEROP_EGL; + interop->egl.display = this->display; + interop->egl.config = this->configs; + interop->egl.shareContext = this->context; + interop->egl.dmaBufImport = this->dmaSupport; + interop->egl.sharedTextureImport = true; return true; } diff --git a/client/renderers/EGL/egltypes.h b/client/renderers/EGL/egltypes.h index 1967ecef..96684d0d 100644 --- a/client/renderers/EGL/egltypes.h +++ b/client/renderers/EGL/egltypes.h @@ -28,7 +28,8 @@ typedef enum EGL_TexType EGL_TEXTYPE_BUFFER_MAP, EGL_TEXTYPE_BUFFER_STREAM, EGL_TEXTYPE_FRAMEBUFFER, - EGL_TEXTYPE_DMABUF + EGL_TEXTYPE_DMABUF, + EGL_TEXTYPE_EXTERNAL } EGL_TexType; diff --git a/client/renderers/EGL/texture.c b/client/renderers/EGL/texture.c index 9b5f416f..10ac0475 100644 --- a/client/renderers/EGL/texture.c +++ b/client/renderers/EGL/texture.c @@ -36,6 +36,7 @@ extern const EGL_TextureOps EGL_TextureBuffer; extern const EGL_TextureOps EGL_TextureBufferStream; extern const EGL_TextureOps EGL_TextureFrameBuffer; +extern const EGL_TextureOps EGL_TextureExternal; bool egl_textureInit(EGL_Texture ** texture_, EGLDisplay * display, EGL_TexType type) @@ -61,6 +62,10 @@ bool egl_textureInit(EGL_Texture ** texture_, EGLDisplay * display, ops = &EGL_TextureDMABUF; break; + case EGL_TEXTYPE_EXTERNAL: + ops = &EGL_TextureExternal; + break; + default: return false; } @@ -212,12 +217,42 @@ bool egl_textureUpdateFromDMA(EGL_Texture * this, return this->ops.update(this, &update); } +bool egl_textureUpdateFromExternal(EGL_Texture * this, + LG_RendererFrameToken frameToken, LG_ExternalFrame * externalFrame, + LG_FrameReleaseFn releaseFn, void * releaseOpaque, + uint64_t releaseHandle) +{ + const struct EGL_TexUpdate update = + { + .type = EGL_TEXTYPE_EXTERNAL, + .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, + }; + + return this->ops.update(this, &update); +} + void egl_textureReset(EGL_Texture * this) { if (this->ops.reset) this->ops.reset(this); } +void egl_textureDeactivate(EGL_Texture * this) +{ + if (this->ops.deactivate) + this->ops.deactivate(this); +} + void egl_texturePoll(EGL_Texture * this) { if (this->ops.poll) diff --git a/client/renderers/EGL/texture.h b/client/renderers/EGL/texture.h index 1bdc95b9..f00606b9 100644 --- a/client/renderers/EGL/texture.h +++ b/client/renderers/EGL/texture.h @@ -107,6 +107,9 @@ typedef struct EGL_TextureOps /* release queued updates and reset stream state */ void (*reset)(EGL_Texture * texture); + /* retire the selected frame when another texture source takes over */ + void (*deactivate)(EGL_Texture * texture); + /* poll asynchronous work without changing the selected frame */ void (*poll)(EGL_Texture * texture); @@ -153,7 +156,13 @@ bool egl_textureUpdateFromDMA(EGL_Texture * texture, LG_FrameReleaseFn releaseFn, void * releaseOpaque, uint64_t releaseHandle); +bool egl_textureUpdateFromExternal(EGL_Texture * texture, + LG_RendererFrameToken frameToken, LG_ExternalFrame * externalFrame, + LG_FrameReleaseFn releaseFn, void * releaseOpaque, + uint64_t releaseHandle); + void egl_textureReset(EGL_Texture * texture); +void egl_textureDeactivate(EGL_Texture * texture); void egl_texturePoll(EGL_Texture * texture); void egl_textureMarkUsed(EGL_Texture * texture); diff --git a/client/renderers/EGL/texture_external.c b/client/renderers/EGL/texture_external.c new file mode 100644 index 00000000..31c34930 --- /dev/null +++ b/client/renderers/EGL/texture_external.c @@ -0,0 +1,416 @@ +/** + * 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 "texture.h" +#include "state.h" + +#include "egldebug.h" + +#include "common/array.h" +#include "common/debug.h" +#include "common/locking.h" + +#include +#include + +#define EGL_EXTERNAL_RETIRED_COUNT 4 + +struct ExternalTextureFrame +{ + GLuint texture; + GLsync readySync; + GLsync useSync; + LG_RendererFrameToken frameToken; + LG_ExternalFrame * externalFrame; + LG_FrameReleaseFn releaseFn; + void * releaseOpaque; + uint64_t releaseHandle; +}; + +struct ExternalTextureRelease +{ + LG_ExternalFrame * externalFrame; + LG_FrameReleaseFn releaseFn; + void * releaseOpaque; + uint64_t releaseHandle; +}; + +typedef struct TexExternal +{ + EGL_Texture base; + LG_Lock lock; + struct ExternalTextureFrame pending; + struct ExternalTextureFrame current; + struct ExternalTextureFrame retired[EGL_EXTERNAL_RETIRED_COUNT]; + bool deactivateRequested; +} +TexExternal; + +static void externalFrameTakeRelease(struct ExternalTextureFrame * frame, + struct ExternalTextureRelease * release) +{ + release->externalFrame = frame->externalFrame; + release->releaseFn = frame->releaseFn; + release->releaseOpaque = frame->releaseOpaque; + release->releaseHandle = frame->releaseHandle; + + frame->externalFrame = NULL; + frame->releaseFn = NULL; + frame->releaseOpaque = NULL; + frame->releaseHandle = 0; +} + +static void externalFrameInvokeReleases( + struct ExternalTextureRelease * releases, int count) +{ + for (int i = 0; i < count; ++i) + { + lgExternalFrameRelease(releases[i].externalFrame); + if (releases[i].releaseFn) + releases[i].releaseFn( + releases[i].releaseOpaque, releases[i].releaseHandle); + } +} + +static void externalFrameWait(GLsync * sync) +{ + if (!*sync) + return; + + const GLenum result = glClientWaitSync(*sync, 0, GL_TIMEOUT_IGNORED); + if (result == GL_WAIT_FAILED) + DEBUG_FATAL("Failed to wait for an external texture fence"); + + glDeleteSync(*sync); + *sync = 0; +} + +static bool externalFramePending(const struct ExternalTextureFrame * frame) +{ + return frame->externalFrame || frame->releaseFn; +} + +static int externalFrameCleanupLocked(TexExternal * this, + struct ExternalTextureRelease * releases) +{ + int releaseCount = 0; + + if (externalFramePending(&this->pending)) + externalFrameTakeRelease( + &this->pending, &releases[releaseCount++]); + + externalFrameWait(&this->current.useSync); + if (externalFramePending(&this->current)) + externalFrameTakeRelease( + &this->current, &releases[releaseCount++]); + + for (int i = 0; i < ARRAY_LENGTH(this->retired); ++i) + { + externalFrameWait(&this->retired[i].useSync); + if (externalFramePending(&this->retired[i])) + externalFrameTakeRelease( + &this->retired[i], &releases[releaseCount++]); + } + + memset(&this->pending, 0, sizeof(this->pending)); + memset(&this->current, 0, sizeof(this->current)); + memset(this->retired, 0, sizeof(this->retired)); + this->base.frameToken = LG_RENDERER_FRAME_TOKEN_NONE; + this->deactivateRequested = false; + + return releaseCount; +} + +static void egl_texExternalCleanup(EGL_Texture * texture) +{ + TexExternal * this = UPCAST(TexExternal, texture); + struct ExternalTextureRelease releases[EGL_EXTERNAL_RETIRED_COUNT + 2]; + int releaseCount; + + LG_LOCK(this->lock); + releaseCount = externalFrameCleanupLocked(this, releases); + LG_UNLOCK(this->lock); + externalFrameInvokeReleases(releases, releaseCount); +} + +static bool egl_texExternalInit(EGL_Texture ** texture, EGL_TexType type, + EGLDisplay * display) +{ + (void)display; + + TexExternal * this = calloc(1, sizeof(*this)); + if (!this) + return false; + + LG_LOCK_INIT(this->lock); + this->base.type = type; + *texture = &this->base; + return true; +} + +static void egl_texExternalFree(EGL_Texture * texture) +{ + TexExternal * this = UPCAST(TexExternal, texture); + + egl_texExternalCleanup(texture); + LG_LOCK_FREE(this->lock); + free(this); +} + +static bool egl_texExternalSetup(EGL_Texture * texture, + const EGL_TexSetup * setup) +{ + (void)setup; + egl_texExternalCleanup(texture); + return true; +} + +static bool egl_texExternalUpdate(EGL_Texture * texture, + const EGL_TexUpdate * update) +{ + TexExternal * this = UPCAST(TexExternal, texture); + + DEBUG_ASSERT(update->type == EGL_TEXTYPE_EXTERNAL); + DEBUG_ASSERT(update->externalFrame); + + LG_ExternalFrameEGLTexture descriptor = + { + .version = LG_EXTERNAL_FRAME_EGL_TEXTURE_VERSION, + .size = sizeof(descriptor), + }; + if (!lgExternalFrameQuery(update->externalFrame, + LG_EXTERNAL_FRAME_EGL_TEXTURE, &descriptor, sizeof(descriptor))) + { + DEBUG_ERROR("Failed to query the external EGL texture"); + return false; + } + + if (descriptor.version != LG_EXTERNAL_FRAME_EGL_TEXTURE_VERSION || + descriptor.size < sizeof(descriptor) || + !descriptor.texture || descriptor.target != (uint32_t)GL_TEXTURE_2D) + { + DEBUG_ERROR("Invalid external EGL texture descriptor"); + return false; + } + + struct ExternalTextureFrame pending = + { + .texture = (GLuint)descriptor.texture, + .readySync = (GLsync)descriptor.readySync, + .frameToken = update->frameToken, + .externalFrame = lgExternalFrameRetain(update->externalFrame), + .releaseFn = update->releaseFn, + .releaseOpaque = update->releaseOpaque, + .releaseHandle = update->releaseHandle, + }; + struct ExternalTextureRelease release = {0}; + bool haveRelease; + + LG_LOCK(this->lock); + this->deactivateRequested = false; + haveRelease = externalFramePending(&this->pending); + if (haveRelease) + externalFrameTakeRelease(&this->pending, &release); + this->pending = pending; + LG_UNLOCK(this->lock); + + if (haveRelease) + externalFrameInvokeReleases(&release, 1); + return true; +} + +static void egl_texExternalPoll(EGL_Texture * texture) +{ + TexExternal * this = UPCAST(TexExternal, texture); + struct ExternalTextureRelease releases[EGL_EXTERNAL_RETIRED_COUNT + 2]; + int releaseCount = 0; + + LG_LOCK(this->lock); + if (this->deactivateRequested) + { + releaseCount = externalFrameCleanupLocked(this, releases); + LG_UNLOCK(this->lock); + externalFrameInvokeReleases(releases, releaseCount); + return; + } + + for (int i = 0; i < ARRAY_LENGTH(this->retired); ++i) + { + struct ExternalTextureFrame * retired = &this->retired[i]; + if (!retired->useSync) + continue; + + const GLenum result = glClientWaitSync(retired->useSync, 0, 0); + if (result == GL_ALREADY_SIGNALED || result == GL_CONDITION_SATISFIED) + { + glDeleteSync(retired->useSync); + retired->useSync = 0; + externalFrameTakeRelease(retired, &releases[releaseCount++]); + memset(retired, 0, sizeof(*retired)); + } + else if (result == GL_WAIT_FAILED) + DEBUG_FATAL("Failed to poll an external texture fence"); + } + LG_UNLOCK(this->lock); + + externalFrameInvokeReleases(releases, releaseCount); +} + +static EGL_TexStatus egl_texExternalProcess(EGL_Texture * texture, + LG_RendererFrameToken frameTokenLimit) +{ + TexExternal * this = UPCAST(TexExternal, texture); + struct ExternalTextureRelease releases[2]; + int releaseCount = 0; + bool haveTexture; + bool updated = false; + + egl_texExternalPoll(texture); + + LG_LOCK(this->lock); + if (externalFramePending(&this->pending) && + egl_textureFrameAllowed(this->pending.frameToken, frameTokenLimit)) + { + if (externalFramePending(&this->current)) + { + if (this->current.useSync) + { + int index = -1; + for (int i = 0; i < ARRAY_LENGTH(this->retired); ++i) + if (!externalFramePending(&this->retired[i])) + { + index = i; + break; + } + + if (index < 0) + { + index = 0; + externalFrameWait(&this->retired[index].useSync); + externalFrameTakeRelease( + &this->retired[index], &releases[releaseCount++]); + } + + this->retired[index] = this->current; + } + else + externalFrameTakeRelease( + &this->current, &releases[releaseCount++]); + } + + if (this->pending.readySync) + glWaitSync(this->pending.readySync, 0, GL_TIMEOUT_IGNORED); + this->pending.readySync = 0; + + this->current = this->pending; + memset(&this->pending, 0, sizeof(this->pending)); + texture->frameToken = this->current.frameToken; + updated = true; + } + + haveTexture = externalFramePending(&this->current); + LG_UNLOCK(this->lock); + + externalFrameInvokeReleases(releases, releaseCount); + + if (!haveTexture) + return EGL_TEX_STATUS_NOTREADY; + return updated ? EGL_TEX_STATUS_UPDATED : EGL_TEX_STATUS_OK; +} + +static EGL_TexStatus egl_texExternalGet(EGL_Texture * texture, GLuint * tex, + EGL_PixelFormat * format) +{ + TexExternal * this = UPCAST(TexExternal, texture); + GLuint selected; + + LG_LOCK(this->lock); + selected = this->current.texture; + LG_UNLOCK(this->lock); + + if (!selected) + return EGL_TEX_STATUS_NOTREADY; + + *tex = selected; + if (format) + *format = texture->format.pixFmt; + return EGL_TEX_STATUS_OK; +} + +static EGL_TexStatus egl_texExternalBind(EGL_Texture * texture, GLuint unit) +{ + GLuint selected; + EGL_TexStatus status = egl_texExternalGet(texture, &selected, NULL); + if (status != EGL_TEX_STATUS_OK) + return status; + + egl_stateBindTexture(unit, GL_TEXTURE_2D, selected); + return EGL_TEX_STATUS_OK; +} + +static void egl_texExternalReset(EGL_Texture * texture) +{ + egl_texExternalCleanup(texture); +} + +static void egl_texExternalDeactivate(EGL_Texture * texture) +{ + TexExternal * this = UPCAST(TexExternal, texture); + + LG_LOCK(this->lock); + this->deactivateRequested = true; + LG_UNLOCK(this->lock); +} + +static void egl_texExternalMarkUsed(EGL_Texture * texture) +{ + TexExternal * this = UPCAST(TexExternal, texture); + + LG_LOCK(this->lock); + if (externalFramePending(&this->current)) + { + if (this->current.useSync) + glDeleteSync(this->current.useSync); + this->current.useSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + if (!this->current.useSync) + { + DEBUG_GL_ERROR("Failed to fence external texture use"); + glFinish(); + } + else + glFlush(); + } + LG_UNLOCK(this->lock); +} + +const EGL_TextureOps EGL_TextureExternal = +{ + .init = egl_texExternalInit, + .free = egl_texExternalFree, + .setup = egl_texExternalSetup, + .update = egl_texExternalUpdate, + .process = egl_texExternalProcess, + .get = egl_texExternalGet, + .bind = egl_texExternalBind, + .reset = egl_texExternalReset, + .deactivate = egl_texExternalDeactivate, + .poll = egl_texExternalPoll, + .markUsed = egl_texExternalMarkUsed, +};