mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-13 15:27:21 +00:00
731 lines
21 KiB
C
731 lines
21 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 ExternalImage
|
|
{
|
|
LG_ExternalFrame * externalFrame;
|
|
EGLImage image;
|
|
GLuint texture;
|
|
};
|
|
|
|
struct Snapshot
|
|
{
|
|
GLuint texture;
|
|
GLuint framebuffer;
|
|
GLsync copySync;
|
|
GLsync useSync;
|
|
LG_RendererFrameToken frameToken;
|
|
LG_ExternalFrame * externalFrame;
|
|
LG_FrameReleaseFn releaseFn;
|
|
void * releaseOpaque;
|
|
uint64_t releaseHandle;
|
|
};
|
|
|
|
typedef struct TexDMABUF
|
|
{
|
|
TextureBuffer base;
|
|
|
|
EGLDisplay display;
|
|
|
|
struct ExternalImage 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_ExternalFrame * externalFrame;
|
|
LG_FrameReleaseFn fn;
|
|
void * opaque;
|
|
uint64_t handle;
|
|
};
|
|
|
|
const EGL_TextureOps EGL_TextureDMABUF;
|
|
|
|
static bool initDone = false;
|
|
static bool has24BitSupport = true;
|
|
static bool hasImportModifiers = true;
|
|
|
|
bool egl_texDMABUFSupported(EGLDisplay display, EGLint major, EGLint minor)
|
|
{
|
|
const char * extensions = eglQueryString(display, EGL_EXTENSIONS);
|
|
|
|
if (!g_egl_dynProcs.glEGLImageTargetTexture2DOES)
|
|
DEBUG_INFO("glEGLImageTargetTexture2DOES unavailable, DMA support disabled");
|
|
else if (!g_egl_dynProcs.eglCreateImage || !g_egl_dynProcs.eglDestroyImage)
|
|
DEBUG_INFO("eglCreateImage or eglDestroyImage unavailable, DMA support disabled");
|
|
else if (!extensions ||
|
|
!util_hasGLExt(extensions, "EGL_EXT_image_dma_buf_import"))
|
|
DEBUG_INFO("EGL_EXT_image_dma_buf_import unavailable, DMA support disabled");
|
|
else if ((major < 1 || (major == 1 && minor < 5)) &&
|
|
!util_hasGLExt(extensions, "EGL_KHR_image_base"))
|
|
DEBUG_INFO("Need EGL 1.5+ or EGL_KHR_image_base for eglCreateImage(KHR)");
|
|
else
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// 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->externalFrame = snapshot->externalFrame;
|
|
release->fn = snapshot->releaseFn;
|
|
release->opaque = snapshot->releaseOpaque;
|
|
release->handle = snapshot->releaseHandle;
|
|
|
|
snapshot->externalFrame = NULL;
|
|
snapshot->releaseFn = NULL;
|
|
snapshot->releaseOpaque = NULL;
|
|
snapshot->releaseHandle = 0;
|
|
}
|
|
|
|
static void snapshotInvokeReleases(struct SnapshotRelease * releases,
|
|
int count)
|
|
{
|
|
for (int i = 0; i < count; ++i)
|
|
{
|
|
lgExternalFrameRelease(releases[i].externalFrame);
|
|
if (releases[i].fn)
|
|
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->externalFrame || 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);
|
|
lgExternalFrameRelease(this->images[i].externalFrame);
|
|
this->images[i].externalFrame = NULL;
|
|
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].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,
|
|
const LG_ExternalFrame * externalFrame)
|
|
{
|
|
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
|
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
|
|
|
int fd;
|
|
if (!lgExternalFrameQuery(externalFrame, LG_EXTERNAL_FRAME_DMABUF,
|
|
&fd, sizeof(fd)))
|
|
{
|
|
DEBUG_ERROR("Failed to query the DMABUF file descriptor");
|
|
return EGL_NO_IMAGE;
|
|
}
|
|
|
|
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);
|
|
DEBUG_ASSERT(update->externalFrame);
|
|
|
|
/* Retire completed copies before deciding whether a snapshot slot must be
|
|
* waited on and coalesced. */
|
|
egl_texDMABUFPoll(texture);
|
|
|
|
struct ExternalImage * externalImage = NULL;
|
|
for (int i = 0; i < ARRAY_LENGTH(this->images); ++i)
|
|
if (this->images[i].externalFrame == update->externalFrame)
|
|
{
|
|
externalImage = &this->images[i];
|
|
break;
|
|
}
|
|
|
|
if (!externalImage)
|
|
for (int i = 0; i < ARRAY_LENGTH(this->images); ++i)
|
|
if (!this->images[i].externalFrame)
|
|
{
|
|
externalImage = &this->images[i];
|
|
break;
|
|
}
|
|
|
|
if (!externalImage)
|
|
{
|
|
DEBUG_ERROR("No free DMABUF image slot");
|
|
return false;
|
|
}
|
|
|
|
if (unlikely(externalImage->image == EGL_NO_IMAGE))
|
|
{
|
|
bool setup = false;
|
|
if (texture->format.pixFmt == EGL_PF_RGB_24 && has24BitSupport)
|
|
{
|
|
externalImage->image = createImage(texture, update->externalFrame);
|
|
if (externalImage->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;
|
|
externalImage = &this->images[0];
|
|
}
|
|
|
|
if (externalImage->image == EGL_NO_IMAGE)
|
|
externalImage->image = createImage(texture, update->externalFrame);
|
|
|
|
if (unlikely(externalImage->image == EGL_NO_IMAGE))
|
|
{
|
|
DEBUG_EGL_ERROR("Failed to create EGLImage for DMA transfer");
|
|
return false;
|
|
}
|
|
|
|
externalImage->externalFrame =
|
|
lgExternalFrameRetain(update->externalFrame);
|
|
glGenTextures(1, &externalImage->texture);
|
|
egl_stateBindTexture(0, GL_TEXTURE_EXTERNAL_OES, externalImage->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, externalImage->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->externalFrame || 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, externalImage->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->externalFrame = lgExternalFrameRetain(update->externalFrame);
|
|
snapshot->releaseFn = update->releaseFn;
|
|
snapshot->releaseOpaque = update->releaseOpaque;
|
|
snapshot->releaseHandle = update->releaseHandle;
|
|
|
|
if (!snapshot->copySync &&
|
|
(snapshot->externalFrame || 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->externalFrame || 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);
|
|
}
|
|
|
|
const 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,
|
|
};
|