mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 16:51:31 +00:00
Copy imported IVSHMEM frames into renderer-owned textures before acknowledging their LGMP messages. Hold each lease until its copy fence signals, and protect selected snapshots until rendering completes. Track independent delivery lanes, drain acknowledgements on the frame thread, and recover stale queue subscriptions after host timeouts.
692 lines
19 KiB
C
692 lines
19 KiB
C
/**
|
|
* 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 "texture_buffer.h"
|
|
#include "model.h"
|
|
#include "shader.h"
|
|
#include "state.h"
|
|
#include "util.h"
|
|
|
|
#include "common/array.h"
|
|
#include "egl_dynprocs.h"
|
|
#include "egldebug.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include "hdr_compose.vert.h"
|
|
#include "downscale_linear.frag.h"
|
|
|
|
#define EGL_DMABUF_SNAPSHOT_COUNT (LGMP_Q_FRAME_BUFFER_LEN + 1)
|
|
|
|
struct FdImage
|
|
{
|
|
int fd;
|
|
EGLImage image;
|
|
GLuint texture;
|
|
};
|
|
|
|
struct Snapshot
|
|
{
|
|
GLuint texture;
|
|
GLuint framebuffer;
|
|
GLsync copySync;
|
|
GLsync useSync;
|
|
LG_RendererFrameToken frameToken;
|
|
LG_FrameReleaseFn releaseFn;
|
|
void * releaseOpaque;
|
|
uint64_t releaseHandle;
|
|
};
|
|
|
|
typedef struct TexDMABUF
|
|
{
|
|
TextureBuffer base;
|
|
|
|
EGLDisplay display;
|
|
|
|
struct FdImage images[EGL_TEX_BUFFER_MAX];
|
|
struct Snapshot snapshots[EGL_DMABUF_SNAPSHOT_COUNT];
|
|
int renderIndex;
|
|
|
|
EGL_Shader * copyShader;
|
|
EGL_Model * copyModel;
|
|
GLuint copySampler;
|
|
|
|
EGL_PixelFormat pixFmt;
|
|
unsigned fourcc;
|
|
unsigned width;
|
|
GLenum intFormat;
|
|
GLenum dataType;
|
|
}
|
|
TexDMABUF;
|
|
|
|
struct SnapshotRelease
|
|
{
|
|
LG_FrameReleaseFn fn;
|
|
void * opaque;
|
|
uint64_t handle;
|
|
};
|
|
|
|
EGL_TextureOps EGL_TextureDMABUF;
|
|
|
|
static bool initDone = false;
|
|
static bool has24BitSupport = true;
|
|
static bool hasImportModifiers = true;
|
|
|
|
// internal functions
|
|
|
|
static void egl_texDMABUFFree(EGL_Texture * texture);
|
|
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;
|
|
|
|
snapshot->releaseFn = NULL;
|
|
snapshot->releaseOpaque = NULL;
|
|
snapshot->releaseHandle = 0;
|
|
}
|
|
|
|
static void snapshotInvokeReleases(struct SnapshotRelease * releases,
|
|
int count)
|
|
{
|
|
for (int i = 0; i < count; ++i)
|
|
releases[i].fn(releases[i].opaque, releases[i].handle);
|
|
}
|
|
|
|
static void waitSync(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 DMABUF snapshot fence");
|
|
glDeleteSync(*sync);
|
|
*sync = 0;
|
|
}
|
|
|
|
static void egl_texDMABUFCleanup(EGL_Texture * texture)
|
|
{
|
|
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
|
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
|
|
|
struct SnapshotRelease releases[EGL_DMABUF_SNAPSHOT_COUNT];
|
|
int releaseCount = 0;
|
|
|
|
LG_LOCK(parent->copyLock);
|
|
|
|
for (int i = 0; i < ARRAY_LENGTH(this->snapshots); ++i)
|
|
{
|
|
struct Snapshot * snapshot = &this->snapshots[i];
|
|
waitSync(&snapshot->copySync);
|
|
waitSync(&snapshot->useSync);
|
|
if (snapshot->releaseFn)
|
|
snapshotRelease(snapshot, &releases[releaseCount++]);
|
|
|
|
if (snapshot->framebuffer)
|
|
glDeleteFramebuffers(1, &snapshot->framebuffer);
|
|
if (snapshot->texture)
|
|
glDeleteTextures(1, &snapshot->texture);
|
|
memset(snapshot, 0, sizeof(*snapshot));
|
|
}
|
|
|
|
for(int i = 0; i < ARRAY_LENGTH(this->images); ++i)
|
|
{
|
|
if (this->images[i].image != EGL_NO_IMAGE)
|
|
{
|
|
g_egl_dynProcs.eglDestroyImage(this->display, this->images[i].image);
|
|
this->images[i].image = EGL_NO_IMAGE;
|
|
}
|
|
if (this->images[i].texture)
|
|
glDeleteTextures(1, &this->images[i].texture);
|
|
this->images[i].fd = -1;
|
|
this->images[i].texture = 0;
|
|
}
|
|
|
|
this->renderIndex = -1;
|
|
parent->rIndex = -1;
|
|
texture->frameToken = LG_RENDERER_FRAME_TOKEN_NONE;
|
|
egl_stateInvalidateShared();
|
|
|
|
LG_UNLOCK(parent->copyLock);
|
|
snapshotInvokeReleases(releases, releaseCount);
|
|
}
|
|
|
|
// dmabuf functions
|
|
|
|
static bool egl_texDMABUFInit(EGL_Texture ** texture, EGL_TexType type,
|
|
EGLDisplay * display)
|
|
{
|
|
TexDMABUF * this = calloc(1, sizeof(*this));
|
|
*texture = &this->base.base;
|
|
|
|
for(int i = 0; i < ARRAY_LENGTH(this->images); ++i)
|
|
{
|
|
this->images[i].fd = -1;
|
|
this->images[i].image = EGL_NO_IMAGE;
|
|
}
|
|
this->renderIndex = -1;
|
|
|
|
EGL_Texture * parent = &this->base.base;
|
|
if (!egl_texBufferStreamInit(&parent, type, display))
|
|
{
|
|
free(this);
|
|
*texture = NULL;
|
|
return false;
|
|
}
|
|
|
|
this->display = display;
|
|
|
|
if (!egl_shaderInit(&this->copyShader) ||
|
|
!egl_shaderCompile(this->copyShader,
|
|
b_shader_hdr_compose_vert, b_shader_hdr_compose_vert_size,
|
|
b_shader_downscale_linear_frag, b_shader_downscale_linear_frag_size,
|
|
true, NULL))
|
|
{
|
|
DEBUG_ERROR("Failed to initialize the DMABUF snapshot pass");
|
|
egl_texDMABUFFree(*texture);
|
|
*texture = NULL;
|
|
return false;
|
|
}
|
|
|
|
egl_shaderAssocTextures(this->copyShader, 1);
|
|
|
|
glGenSamplers(1, &this->copySampler);
|
|
glSamplerParameteri(this->copySampler,
|
|
GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glSamplerParameteri(this->copySampler,
|
|
GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glSamplerParameteri(this->copySampler,
|
|
GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glSamplerParameteri(this->copySampler,
|
|
GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
if (!initDone)
|
|
{
|
|
const char * client_exts = eglQueryString(this->display, EGL_EXTENSIONS);
|
|
hasImportModifiers =
|
|
util_hasGLExt(client_exts, "EGL_EXT_image_dma_buf_import_modifiers");
|
|
initDone = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void egl_texDMABUFFree(EGL_Texture * texture)
|
|
{
|
|
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
|
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
|
|
|
egl_texDMABUFCleanup(texture);
|
|
egl_modelFree(&this->copyModel);
|
|
egl_shaderFree(&this->copyShader);
|
|
if (this->copySampler)
|
|
glDeleteSamplers(1, &this->copySampler);
|
|
egl_texBufferFree(&parent->base);
|
|
free(this);
|
|
}
|
|
|
|
static bool texDMABUFSetup(EGL_Texture * texture)
|
|
{
|
|
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
|
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
|
|
|
if (texture->format.pixFmt == EGL_PF_RGB_24 && !has24BitSupport)
|
|
{
|
|
this->pixFmt = EGL_PF_RGB_24_32;
|
|
this->width = texture->format.pitch / 4;
|
|
this->fourcc = DRM_FORMAT_ARGB8888;
|
|
}
|
|
|
|
egl_texDMABUFCleanup(texture);
|
|
|
|
if (!this->copyModel)
|
|
{
|
|
if (!egl_modelInit(&this->copyModel))
|
|
return false;
|
|
egl_modelSetDefault(this->copyModel, false);
|
|
egl_modelSetShader(this->copyModel, this->copyShader);
|
|
}
|
|
|
|
for (int i = 0; i < ARRAY_LENGTH(this->snapshots); ++i)
|
|
{
|
|
struct Snapshot * snapshot = &this->snapshots[i];
|
|
|
|
glGenTextures(1, &snapshot->texture);
|
|
egl_stateBindTexture(0, GL_TEXTURE_2D, snapshot->texture);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, this->intFormat,
|
|
this->width, texture->format.height, 0,
|
|
GL_RGBA, this->dataType, NULL);
|
|
|
|
glGenFramebuffers(1, &snapshot->framebuffer);
|
|
egl_stateBindFramebuffer(snapshot->framebuffer);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
GL_TEXTURE_2D, snapshot->texture, 0);
|
|
glDrawBuffers(1, &(GLenum){ GL_COLOR_ATTACHMENT0 });
|
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
|
{
|
|
DEBUG_ERROR("Failed to create DMABUF snapshot framebuffer");
|
|
egl_stateBindFramebuffer(0);
|
|
egl_texDMABUFCleanup(texture);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
egl_stateBindFramebuffer(0);
|
|
parent->rIndex = -1;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool egl_texDMABUFSetup(EGL_Texture * texture, const EGL_TexSetup * setup)
|
|
{
|
|
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
|
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
|
|
|
this->pixFmt = texture->format.pixFmt;
|
|
this->width = texture->format.width;
|
|
this->fourcc = texture->format.fourcc;
|
|
this->dataType = GL_UNSIGNED_BYTE;
|
|
this->intFormat = GL_RGBA8;
|
|
|
|
switch (texture->format.pixFmt)
|
|
{
|
|
case EGL_PF_RGBA10:
|
|
this->intFormat = GL_RGB10_A2;
|
|
this->dataType = GL_UNSIGNED_INT_2_10_10_10_REV;
|
|
break;
|
|
|
|
case EGL_PF_RGBA16F:
|
|
this->intFormat = GL_RGBA16F;
|
|
this->dataType = GL_HALF_FLOAT;
|
|
break;
|
|
|
|
case EGL_PF_RGB_24:
|
|
/* A native RGB888 import is expanded by the snapshot shader. */
|
|
this->pixFmt = EGL_PF_RGBA;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return texDMABUFSetup(texture);
|
|
}
|
|
|
|
static EGLImage createImage(EGL_Texture * texture, int fd)
|
|
{
|
|
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
|
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
|
|
|
const uint64_t modifier = DRM_FORMAT_MOD_LINEAR;
|
|
EGLAttrib attribs[] =
|
|
{
|
|
EGL_WIDTH , this->width ,
|
|
EGL_HEIGHT , texture->format.height,
|
|
EGL_LINUX_DRM_FOURCC_EXT , this->fourcc,
|
|
EGL_DMA_BUF_PLANE0_FD_EXT , fd,
|
|
EGL_DMA_BUF_PLANE0_OFFSET_EXT , 0,
|
|
EGL_DMA_BUF_PLANE0_PITCH_EXT , texture->format.pitch,
|
|
EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, (modifier & 0xffffffff),
|
|
EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, (modifier >> 32),
|
|
EGL_NONE , EGL_NONE
|
|
};
|
|
|
|
if (!hasImportModifiers)
|
|
attribs[12] = attribs[13] =
|
|
attribs[14] = attribs[15] = EGL_NONE;
|
|
|
|
return g_egl_dynProcs.eglCreateImage(
|
|
this->display,
|
|
EGL_NO_CONTEXT,
|
|
EGL_LINUX_DMA_BUF_EXT,
|
|
(EGLClientBuffer)NULL,
|
|
attribs);
|
|
}
|
|
|
|
static bool egl_texDMABUFUpdate(EGL_Texture * texture,
|
|
const EGL_TexUpdate * update)
|
|
{
|
|
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
|
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
|
|
|
DEBUG_ASSERT(update->type == EGL_TEXTYPE_DMABUF);
|
|
|
|
/* Retire completed copies before deciding whether a snapshot slot must be
|
|
* waited on and coalesced. */
|
|
egl_texDMABUFPoll(texture);
|
|
|
|
struct FdImage *fdImage = NULL;
|
|
for (int i = 0; i < ARRAY_LENGTH(this->images); ++i)
|
|
if (this->images[i].fd == update->dmaFD)
|
|
{
|
|
fdImage = &this->images[i];
|
|
break;
|
|
}
|
|
|
|
if (!fdImage)
|
|
for (int i = 0; i < ARRAY_LENGTH(this->images); ++i)
|
|
if (this->images[i].fd == -1)
|
|
{
|
|
fdImage = &this->images[i];
|
|
break;
|
|
}
|
|
|
|
if (!fdImage)
|
|
{
|
|
DEBUG_ERROR("No free DMABUF image slot");
|
|
return false;
|
|
}
|
|
|
|
if (unlikely(fdImage->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)
|
|
{
|
|
DEBUG_INFO("Using 24-bit in 32-bit for DMA");
|
|
has24BitSupport = false;
|
|
setup = true;
|
|
}
|
|
}
|
|
|
|
if (!has24BitSupport && setup)
|
|
{
|
|
if (!texDMABUFSetup(texture))
|
|
return false;
|
|
fdImage = &this->images[0];
|
|
}
|
|
|
|
if (fdImage->image == EGL_NO_IMAGE)
|
|
fdImage->image = createImage(texture, update->dmaFD);
|
|
|
|
if (unlikely(fdImage->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);
|
|
glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
|
|
GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
|
|
GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
|
|
GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_EXTERNAL_OES,
|
|
GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
g_egl_dynProcs.glEGLImageTargetTexture2DOES(
|
|
GL_TEXTURE_EXTERNAL_OES, fdImage->image);
|
|
}
|
|
|
|
struct SnapshotRelease releases[2];
|
|
int releaseCount = 0;
|
|
LG_LOCK(parent->copyLock);
|
|
|
|
int index = -1;
|
|
for (int i = 0; i < ARRAY_LENGTH(this->snapshots); ++i)
|
|
if (this->snapshots[i].frameToken == LG_RENDERER_FRAME_TOKEN_NONE)
|
|
{
|
|
index = i;
|
|
break;
|
|
}
|
|
|
|
if (index < 0)
|
|
{
|
|
/* Drop the oldest snapshot that is no longer selected. If the GPU is
|
|
* still finishing it, wait only when all snapshot slots are saturated. */
|
|
for (int i = 0; i < ARRAY_LENGTH(this->snapshots); ++i)
|
|
if (i != this->renderIndex &&
|
|
(index < 0 || this->snapshots[i].frameToken <
|
|
this->snapshots[index].frameToken))
|
|
index = i;
|
|
|
|
if (unlikely(index < 0))
|
|
{
|
|
LG_UNLOCK(parent->copyLock);
|
|
DEBUG_ERROR("No reusable DMABUF snapshot slot");
|
|
return false;
|
|
}
|
|
|
|
struct Snapshot * old = &this->snapshots[index];
|
|
waitSync(&old->copySync);
|
|
waitSync(&old->useSync);
|
|
if (old->releaseFn)
|
|
snapshotRelease(old, &releases[releaseCount++]);
|
|
old->frameToken = LG_RENDERER_FRAME_TOKEN_NONE;
|
|
}
|
|
|
|
struct Snapshot * snapshot = &this->snapshots[index];
|
|
egl_stateBindFramebuffer(snapshot->framebuffer);
|
|
egl_stateViewport(0, 0, this->width, texture->format.height);
|
|
egl_stateBlend(false);
|
|
egl_stateScissor(false);
|
|
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_shaderUse(this->copyShader);
|
|
egl_modelRender(this->copyModel);
|
|
|
|
snapshot->copySync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
if (unlikely(!snapshot->copySync))
|
|
{
|
|
DEBUG_GL_ERROR("Failed to fence DMABUF snapshot copy");
|
|
glFinish();
|
|
}
|
|
snapshot->frameToken = update->frameToken;
|
|
snapshot->releaseFn = update->releaseFn;
|
|
snapshot->releaseOpaque = update->releaseOpaque;
|
|
snapshot->releaseHandle = update->releaseHandle;
|
|
|
|
if (!snapshot->copySync && snapshot->releaseFn)
|
|
snapshotRelease(snapshot, &releases[releaseCount++]);
|
|
|
|
glFlush();
|
|
LG_UNLOCK(parent->copyLock);
|
|
|
|
snapshotInvokeReleases(releases, releaseCount);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void egl_texDMABUFPoll(EGL_Texture * texture)
|
|
{
|
|
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
|
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
|
|
|
struct SnapshotRelease releases[EGL_DMABUF_SNAPSHOT_COUNT];
|
|
int releaseCount = 0;
|
|
|
|
LG_LOCK(parent->copyLock);
|
|
for (int i = 0; i < ARRAY_LENGTH(this->snapshots); ++i)
|
|
{
|
|
struct Snapshot * snapshot = &this->snapshots[i];
|
|
if (snapshot->copySync)
|
|
{
|
|
const GLenum result = glClientWaitSync(snapshot->copySync, 0, 0);
|
|
if (result == GL_ALREADY_SIGNALED ||
|
|
result == GL_CONDITION_SATISFIED)
|
|
{
|
|
glDeleteSync(snapshot->copySync);
|
|
snapshot->copySync = 0;
|
|
if (snapshot->releaseFn)
|
|
snapshotRelease(snapshot, &releases[releaseCount++]);
|
|
}
|
|
else if (result == GL_WAIT_FAILED)
|
|
DEBUG_FATAL("Failed to poll DMABUF snapshot copy fence");
|
|
}
|
|
|
|
if (snapshot->useSync)
|
|
{
|
|
const GLenum result = glClientWaitSync(snapshot->useSync, 0, 0);
|
|
if (result == GL_ALREADY_SIGNALED ||
|
|
result == GL_CONDITION_SATISFIED)
|
|
{
|
|
glDeleteSync(snapshot->useSync);
|
|
snapshot->useSync = 0;
|
|
}
|
|
else if (result == GL_WAIT_FAILED)
|
|
DEBUG_FATAL("Failed to poll DMABUF snapshot use fence");
|
|
}
|
|
}
|
|
LG_UNLOCK(parent->copyLock);
|
|
|
|
snapshotInvokeReleases(releases, releaseCount);
|
|
}
|
|
|
|
static EGL_TexStatus egl_texDMABUFProcess(EGL_Texture * texture,
|
|
LG_RendererFrameToken frameTokenLimit)
|
|
{
|
|
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
|
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
|
|
|
egl_texDMABUFPoll(texture);
|
|
|
|
int index = -1;
|
|
bool haveImage = false;
|
|
bool updated = false;
|
|
INTERLOCKED_SECTION(parent->copyLock,
|
|
{
|
|
haveImage = this->renderIndex >= 0;
|
|
for (int i = 0; i < ARRAY_LENGTH(this->snapshots); ++i)
|
|
if (this->snapshots[i].frameToken != LG_RENDERER_FRAME_TOKEN_NONE &&
|
|
egl_textureFrameAllowed(
|
|
this->snapshots[i].frameToken, frameTokenLimit) &&
|
|
(index < 0 || this->snapshots[i].frameToken >
|
|
this->snapshots[index].frameToken))
|
|
index = i;
|
|
|
|
if (index >= 0)
|
|
{
|
|
const struct Snapshot * pending = &this->snapshots[index];
|
|
if (this->renderIndex != index ||
|
|
texture->frameToken != pending->frameToken)
|
|
{
|
|
if (pending->copySync)
|
|
glWaitSync(pending->copySync, 0, GL_TIMEOUT_IGNORED);
|
|
this->renderIndex = index;
|
|
parent->rIndex = index;
|
|
texture->frameToken = pending->frameToken;
|
|
updated = true;
|
|
haveImage = true;
|
|
}
|
|
}
|
|
});
|
|
|
|
if (unlikely(!haveImage))
|
|
return EGL_TEX_STATUS_NOTREADY;
|
|
return updated ? EGL_TEX_STATUS_UPDATED : EGL_TEX_STATUS_OK;
|
|
}
|
|
|
|
static EGL_TexStatus egl_texDMABUFGet(EGL_Texture * texture, GLuint * tex,
|
|
EGL_PixelFormat * fmt)
|
|
{
|
|
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
|
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
|
|
|
int index = -1;
|
|
|
|
INTERLOCKED_SECTION(parent->copyLock,
|
|
{
|
|
index = this->renderIndex;
|
|
});
|
|
|
|
if (unlikely(index < 0))
|
|
return EGL_TEX_STATUS_NOTREADY;
|
|
|
|
*tex = this->snapshots[index].texture;
|
|
|
|
if (fmt)
|
|
*fmt = this->pixFmt;
|
|
|
|
return EGL_TEX_STATUS_OK;
|
|
}
|
|
|
|
static EGL_TexStatus egl_texDMABUFBind(EGL_Texture * texture, GLuint unit)
|
|
{
|
|
GLuint tex;
|
|
EGL_TexStatus status;
|
|
|
|
if ((status = egl_texDMABUFGet(texture, &tex, NULL)) != EGL_TEX_STATUS_OK)
|
|
return status;
|
|
|
|
egl_stateBindTexture(unit, GL_TEXTURE_2D, tex);
|
|
return EGL_TEX_STATUS_OK;
|
|
}
|
|
|
|
static void egl_texDMABUFReset(EGL_Texture * texture)
|
|
{
|
|
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
|
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
|
|
|
egl_texDMABUFCleanup(texture);
|
|
egl_modelFree(&this->copyModel);
|
|
}
|
|
|
|
static void egl_texDMABUFMarkUsed(EGL_Texture * texture)
|
|
{
|
|
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
|
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
|
|
|
LG_LOCK(parent->copyLock);
|
|
if (this->renderIndex >= 0)
|
|
{
|
|
struct Snapshot * snapshot = &this->snapshots[this->renderIndex];
|
|
if (snapshot->useSync)
|
|
glDeleteSync(snapshot->useSync);
|
|
snapshot->useSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
|
if (unlikely(!snapshot->useSync))
|
|
{
|
|
DEBUG_GL_ERROR("Failed to fence DMABUF snapshot use");
|
|
glFinish();
|
|
}
|
|
else
|
|
glFlush();
|
|
}
|
|
LG_UNLOCK(parent->copyLock);
|
|
}
|
|
|
|
EGL_TextureOps EGL_TextureDMABUF =
|
|
{
|
|
.init = egl_texDMABUFInit,
|
|
.free = egl_texDMABUFFree,
|
|
.setup = egl_texDMABUFSetup,
|
|
.update = egl_texDMABUFUpdate,
|
|
.process = egl_texDMABUFProcess,
|
|
.get = egl_texDMABUFGet,
|
|
.bind = egl_texDMABUFBind,
|
|
.reset = egl_texDMABUFReset,
|
|
.poll = egl_texDMABUFPoll,
|
|
.markUsed = egl_texDMABUFMarkUsed,
|
|
};
|