mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-12 14:57:20 +00:00
[client] egl: import shared GPU textures
This commit is contained in:
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
416
client/renderers/EGL/texture_external.c
Normal file
416
client/renderers/EGL/texture_external.c
Normal 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,
|
||||
};
|
||||
Reference in New Issue
Block a user