[client] egl: import shared GPU textures

This commit is contained in:
Geoffrey McRae
2026-09-30 15:33:36 +10:00
parent adf9ac3035
commit ed3b269919
9 changed files with 563 additions and 22 deletions

View File

@@ -23,14 +23,39 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
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
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 <stdlib.h>
#include <string.h>
#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,
};