mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[client] egl: snapshot DMA frames before release
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.
This commit is contained in:
@@ -72,7 +72,7 @@ struct EGL_Desktop
|
||||
EGLDisplay * display;
|
||||
|
||||
EGL_Texture * texture;
|
||||
struct DesktopShader dmaShader, shader;
|
||||
struct DesktopShader shader;
|
||||
EGL_DesktopRects * mesh;
|
||||
GLfloat matrix[6];
|
||||
|
||||
@@ -194,17 +194,6 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (useDMA)
|
||||
if (!egl_initDesktopShader(
|
||||
&desktop->dmaShader,
|
||||
b_shader_desktop_vert , b_shader_desktop_vert_size,
|
||||
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size,
|
||||
true))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the desktop DMA shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
app_registerKeybind(KEY_N, toggleNV, desktop,
|
||||
"Toggle night vision mode");
|
||||
|
||||
@@ -264,7 +253,6 @@ void egl_desktopFree(EGL_Desktop ** desktop)
|
||||
egl_textureFree (&(*desktop)->texture );
|
||||
egl_textureFree (&(*desktop)->spiceTexture );
|
||||
egl_shaderFree (&(*desktop)->shader .shader);
|
||||
egl_shaderFree (&(*desktop)->dmaShader.shader);
|
||||
egl_desktopRectsFree(&(*desktop)->mesh );
|
||||
egl_postProcessFree(&(*desktop)->pp);
|
||||
|
||||
@@ -394,12 +382,14 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
|
||||
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame,
|
||||
LG_RendererFrameToken frameToken, int dmaFd,
|
||||
const FrameDamageRect * damageRects, int damageRectsCount,
|
||||
uint64_t * waitTimeNs)
|
||||
uint64_t * waitTimeNs, LG_FrameReleaseFn releaseFn,
|
||||
void * releaseOpaque, uint64_t releaseHandle)
|
||||
{
|
||||
if (likely(desktop->useDMA && dmaFd >= 0))
|
||||
{
|
||||
if (likely(egl_textureUpdateFromDMA(
|
||||
desktop->texture, frame, frameToken, dmaFd, waitTimeNs)))
|
||||
desktop->texture, frame, frameToken, dmaFd, waitTimeNs,
|
||||
releaseFn, releaseOpaque, releaseHandle)))
|
||||
{
|
||||
atomic_store(&desktop->processFrame, true);
|
||||
return true;
|
||||
@@ -442,11 +432,26 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame,
|
||||
*/
|
||||
if (likely(egl_textureUpdateFromFrame(desktop->texture, frame, frameToken,
|
||||
damageRects, damageRectsCount, waitTimeNs)))
|
||||
{
|
||||
if (releaseFn)
|
||||
releaseFn(releaseOpaque, releaseHandle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void egl_desktopRestart(EGL_Desktop * desktop)
|
||||
{
|
||||
egl_textureReset(desktop->texture);
|
||||
atomic_store(&desktop->processFrame, false);
|
||||
}
|
||||
|
||||
void egl_desktopPoll(EGL_Desktop * desktop)
|
||||
{
|
||||
egl_texturePoll(desktop->texture);
|
||||
}
|
||||
|
||||
void egl_desktopResize(EGL_Desktop * desktop, int width, int height)
|
||||
{
|
||||
atomic_store(&desktop->processFrame, true);
|
||||
@@ -464,21 +469,21 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
{
|
||||
EGL_Texture * tex;
|
||||
int width, height;
|
||||
bool dma;
|
||||
|
||||
/* This also retires completed snapshots while the SPICE display is shown. */
|
||||
egl_texturePoll(desktop->texture);
|
||||
|
||||
if (unlikely(desktop->useSpice))
|
||||
{
|
||||
tex = desktop->spiceTexture;
|
||||
width = desktop->spiceWidth;
|
||||
height = desktop->spiceHeight;
|
||||
dma = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
tex = desktop->texture;
|
||||
width = desktop->width;
|
||||
height = desktop->height;
|
||||
dma = desktop->useDMA;
|
||||
}
|
||||
|
||||
if (unlikely(outputWidth == 0 || outputHeight == 0))
|
||||
@@ -535,7 +540,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
const uint64_t effectsStart = nanotime();
|
||||
const bool postProcessed = egl_postProcessRun(
|
||||
desktop->pp, tex, desktop->mesh,
|
||||
width, height, outputWidth, outputHeight, dma,
|
||||
width, height, outputWidth, outputHeight, false,
|
||||
hdr && desktop->hdrPQ, (float)hdrPeak);
|
||||
*effectsTime = nanotime() - effectsStart;
|
||||
|
||||
@@ -574,6 +579,8 @@ 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->useSpice && desktop->useDMA && processFrame)
|
||||
egl_textureMarkUsed(desktop->texture);
|
||||
return false;
|
||||
}
|
||||
*renderedFrameToken = tex->frameToken;
|
||||
@@ -601,9 +608,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
scaleAlgo = desktop->scaleAlgo;
|
||||
}
|
||||
|
||||
const struct DesktopShader * shader =
|
||||
desktop->useDMA && texture == desktop->texture ?
|
||||
&desktop->dmaShader : &desktop->shader;
|
||||
const struct DesktopShader * shader = &desktop->shader;
|
||||
|
||||
/* PQ is normalized to 10000 nits, while scRGB defines 1.0 as 80 nits.
|
||||
* Convert either encoding into values relative to the selected SDR peak. */
|
||||
@@ -628,6 +633,9 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
desktop->linearComposition);
|
||||
egl_shaderUse(shader->shader);
|
||||
egl_desktopRectsRender(desktop->mesh);
|
||||
if (!desktop->useSpice && desktop->useDMA &&
|
||||
(processFrame || texture == tex))
|
||||
egl_textureMarkUsed(desktop->texture);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,10 @@ bool egl_desktopSetup (EGL_Desktop * desktop, const LG_RendererFormat format);
|
||||
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame,
|
||||
LG_RendererFrameToken frameToken, int dmaFd,
|
||||
const FrameDamageRect * damageRects, int damageRectsCount,
|
||||
uint64_t * waitTimeNs);
|
||||
uint64_t * waitTimeNs, LG_FrameReleaseFn releaseFn,
|
||||
void * releaseOpaque, uint64_t releaseHandle);
|
||||
void egl_desktopRestart(EGL_Desktop * desktop);
|
||||
void egl_desktopPoll(EGL_Desktop * desktop);
|
||||
void egl_desktopResize(EGL_Desktop * desktop, int width, int height);
|
||||
bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
unsigned int outputHeight, const float x, const float y,
|
||||
|
||||
@@ -374,8 +374,13 @@ static void egl_onRestart(LG_Renderer * renderer)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
|
||||
eglDestroyContext(this->display, this->frameContext);
|
||||
this->frameContext = NULL;
|
||||
if (this->frameContext)
|
||||
{
|
||||
egl_stateCheckShared();
|
||||
egl_desktopRestart(this->desktop);
|
||||
eglDestroyContext(this->display, this->frameContext);
|
||||
this->frameContext = NULL;
|
||||
}
|
||||
|
||||
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
||||
this->desktopDamage[this->desktopDamageIdx].frameToken =
|
||||
@@ -746,7 +751,8 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
|
||||
|
||||
static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame,
|
||||
int dmaFd, const FrameDamageRect * damageRects, int damageRectsCount,
|
||||
LG_RendererFrameToken frameToken)
|
||||
LG_RendererFrameToken frameToken, LG_FrameReleaseFn releaseFn,
|
||||
void * releaseOpaque, uint64_t releaseHandle)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
egl_stateCheckShared();
|
||||
@@ -755,7 +761,8 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame,
|
||||
uint64_t waitTimeNs = 0;
|
||||
if (unlikely(!egl_desktopUpdate(
|
||||
this->desktop, frame, frameToken, dmaFd,
|
||||
damageRects, damageRectsCount, &waitTimeNs)))
|
||||
damageRects, damageRectsCount, &waitTimeNs,
|
||||
releaseFn, releaseOpaque, releaseHandle)))
|
||||
{
|
||||
DEBUG_INFO("Failed to to update the desktop");
|
||||
return false;
|
||||
@@ -790,6 +797,16 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void egl_onFramePoll(LG_Renderer * renderer)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
if (!this->frameContext)
|
||||
return;
|
||||
|
||||
egl_stateCheckShared();
|
||||
egl_desktopPoll(this->desktop);
|
||||
}
|
||||
|
||||
static void debugCallback(GLenum source, GLenum type, GLuint id,
|
||||
GLenum severity, GLsizei length, const GLchar * message,
|
||||
const void * userParam)
|
||||
@@ -1835,6 +1852,7 @@ struct LG_RendererOps LGR_EGL =
|
||||
.onMouseEvent = egl_onMouseEvent,
|
||||
.onFrameFormat = egl_onFrameFormat,
|
||||
.onFrame = egl_onFrame,
|
||||
.onFramePoll = egl_onFramePoll,
|
||||
.renderStartup = egl_renderStartup,
|
||||
.render = egl_render,
|
||||
.capture = egl_capture,
|
||||
|
||||
@@ -185,19 +185,23 @@ bool egl_textureUpdateFromFrame(EGL_Texture * this,
|
||||
|
||||
bool egl_textureUpdateFromDMA(EGL_Texture * this,
|
||||
const FrameBuffer * frame, LG_RendererFrameToken frameToken,
|
||||
const int dmaFd, uint64_t * waitTimeNs)
|
||||
const int dmaFd, uint64_t * waitTimeNs, LG_FrameReleaseFn releaseFn,
|
||||
void * releaseOpaque, uint64_t releaseHandle)
|
||||
{
|
||||
const struct EGL_TexUpdate update =
|
||||
{
|
||||
.type = EGL_TEXTYPE_DMABUF,
|
||||
.frameToken = frameToken,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = this->format.width,
|
||||
.height = this->format.height,
|
||||
.pitch = this->format.pitch,
|
||||
.stride = this->format.stride,
|
||||
.dmaFD = dmaFd
|
||||
.type = EGL_TEXTYPE_DMABUF,
|
||||
.frameToken = frameToken,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = this->format.width,
|
||||
.height = this->format.height,
|
||||
.pitch = this->format.pitch,
|
||||
.stride = this->format.stride,
|
||||
.dmaFD = dmaFd,
|
||||
.releaseFn = releaseFn,
|
||||
.releaseOpaque = releaseOpaque,
|
||||
.releaseHandle = releaseHandle,
|
||||
};
|
||||
|
||||
/* wait for completion */
|
||||
@@ -208,6 +212,24 @@ bool egl_textureUpdateFromDMA(EGL_Texture * this,
|
||||
return this->ops.update(this, &update);
|
||||
}
|
||||
|
||||
void egl_textureReset(EGL_Texture * this)
|
||||
{
|
||||
if (this->ops.reset)
|
||||
this->ops.reset(this);
|
||||
}
|
||||
|
||||
void egl_texturePoll(EGL_Texture * this)
|
||||
{
|
||||
if (this->ops.poll)
|
||||
this->ops.poll(this);
|
||||
}
|
||||
|
||||
void egl_textureMarkUsed(EGL_Texture * this)
|
||||
{
|
||||
if (this->ops.markUsed)
|
||||
this->ops.markUsed(this);
|
||||
}
|
||||
|
||||
enum EGL_TexStatus egl_textureProcess(EGL_Texture * this,
|
||||
LG_RendererFrameToken frameTokenLimit,
|
||||
LG_RendererFrameToken * consumedFrameToken)
|
||||
|
||||
@@ -41,9 +41,12 @@ typedef struct EGL_Model EGL_Model;
|
||||
typedef struct EGL_TexUpdate
|
||||
{
|
||||
/* the type of this update */
|
||||
EGL_TexType type;
|
||||
LG_RendererFrameToken frameToken;
|
||||
uint64_t * waitTimeNs;
|
||||
EGL_TexType type;
|
||||
LG_RendererFrameToken frameToken;
|
||||
uint64_t * waitTimeNs;
|
||||
LG_FrameReleaseFn releaseFn;
|
||||
void * releaseOpaque;
|
||||
uint64_t releaseHandle;
|
||||
|
||||
int x, y, width, height;
|
||||
|
||||
@@ -101,6 +104,15 @@ typedef struct EGL_TextureOps
|
||||
|
||||
/* bind the texture for use */
|
||||
enum EGL_TexStatus (*bind)(EGL_Texture * texture, GLuint unit);
|
||||
|
||||
/* release queued updates and reset stream state */
|
||||
void (*reset)(EGL_Texture * texture);
|
||||
|
||||
/* poll asynchronous work without changing the selected frame */
|
||||
void (*poll)(EGL_Texture * texture);
|
||||
|
||||
/* record that the selected texture has been sampled */
|
||||
void (*markUsed)(EGL_Texture * texture);
|
||||
}
|
||||
EGL_TextureOps;
|
||||
|
||||
@@ -135,7 +147,12 @@ bool egl_textureUpdateFromFrame(EGL_Texture * texture,
|
||||
|
||||
bool egl_textureUpdateFromDMA(EGL_Texture * texture,
|
||||
const FrameBuffer * frame, LG_RendererFrameToken frameToken,
|
||||
const int dmaFd, uint64_t * waitTimeNs);
|
||||
const int dmaFd, uint64_t * waitTimeNs, LG_FrameReleaseFn releaseFn,
|
||||
void * releaseOpaque, uint64_t releaseHandle);
|
||||
|
||||
void egl_textureReset(EGL_Texture * texture);
|
||||
void egl_texturePoll(EGL_Texture * texture);
|
||||
void egl_textureMarkUsed(EGL_Texture * texture);
|
||||
|
||||
enum EGL_TexStatus egl_textureProcess(EGL_Texture * texture,
|
||||
LG_RendererFrameToken frameTokenLimit,
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "texture.h"
|
||||
#include "texture_buffer.h"
|
||||
#include "model.h"
|
||||
#include "shader.h"
|
||||
#include "state.h"
|
||||
#include "util.h"
|
||||
|
||||
@@ -27,13 +29,30 @@
|
||||
#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;
|
||||
GLsync sync;
|
||||
int texIndex;
|
||||
LG_RendererFrameToken frameToken;
|
||||
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
|
||||
@@ -42,17 +61,29 @@ typedef struct TexDMABUF
|
||||
|
||||
EGLDisplay display;
|
||||
|
||||
struct FdImage images[EGL_TEX_BUFFER_MAX];
|
||||
int lastIndex;
|
||||
int renderIndex;
|
||||
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;
|
||||
GLuint format;
|
||||
GLenum intFormat;
|
||||
GLenum dataType;
|
||||
}
|
||||
TexDMABUF;
|
||||
|
||||
struct SnapshotRelease
|
||||
{
|
||||
LG_FrameReleaseFn fn;
|
||||
void * opaque;
|
||||
uint64_t handle;
|
||||
};
|
||||
|
||||
EGL_TextureOps EGL_TextureDMABUF;
|
||||
|
||||
static bool initDone = false;
|
||||
@@ -61,11 +92,65 @@ 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)
|
||||
@@ -73,28 +158,19 @@ static void egl_texDMABUFCleanup(EGL_Texture * texture)
|
||||
g_egl_dynProcs.eglDestroyImage(this->display, this->images[i].image);
|
||||
this->images[i].image = EGL_NO_IMAGE;
|
||||
}
|
||||
if (this->images[i].sync)
|
||||
{
|
||||
glDeleteSync(this->images[i].sync);
|
||||
this->images[i].sync = 0;
|
||||
}
|
||||
this->images[i].fd = -1;
|
||||
this->images[i].texIndex = -1;
|
||||
this->images[i].frameToken = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
if (this->images[i].texture)
|
||||
glDeleteTextures(1, &this->images[i].texture);
|
||||
this->images[i].fd = -1;
|
||||
this->images[i].texture = 0;
|
||||
}
|
||||
|
||||
this->lastIndex = -1;
|
||||
this->renderIndex = -1;
|
||||
parent->rIndex = -1;
|
||||
texture->frameToken = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
this->renderIndex = -1;
|
||||
parent->rIndex = -1;
|
||||
texture->frameToken = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
egl_stateInvalidateShared();
|
||||
|
||||
egl_texUtilFreeBuffers(parent->buf, parent->texCount);
|
||||
|
||||
if (parent->tex[0])
|
||||
{
|
||||
glDeleteTextures(parent->texCount, parent->tex);
|
||||
egl_stateInvalidateShared();
|
||||
}
|
||||
LG_UNLOCK(parent->copyLock);
|
||||
snapshotInvokeReleases(releases, releaseCount);
|
||||
}
|
||||
|
||||
// dmabuf functions
|
||||
@@ -107,13 +183,9 @@ static bool egl_texDMABUFInit(EGL_Texture ** texture, EGL_TexType type,
|
||||
|
||||
for(int i = 0; i < ARRAY_LENGTH(this->images); ++i)
|
||||
{
|
||||
this->images[i].fd = -1;
|
||||
this->images[i].image = EGL_NO_IMAGE;
|
||||
this->images[i].sync = 0;
|
||||
this->images[i].texIndex = -1;
|
||||
this->images[i].frameToken = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
this->images[i].fd = -1;
|
||||
this->images[i].image = EGL_NO_IMAGE;
|
||||
}
|
||||
this->lastIndex = -1;
|
||||
this->renderIndex = -1;
|
||||
|
||||
EGL_Texture * parent = &this->base.base;
|
||||
@@ -126,6 +198,30 @@ static bool egl_texDMABUFInit(EGL_Texture ** texture, EGL_TexType type,
|
||||
|
||||
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);
|
||||
@@ -143,6 +239,10 @@ static void egl_texDMABUFFree(EGL_Texture * 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);
|
||||
}
|
||||
@@ -157,12 +257,47 @@ static bool texDMABUFSetup(EGL_Texture * texture)
|
||||
this->pixFmt = EGL_PF_RGB_24_32;
|
||||
this->width = texture->format.pitch / 4;
|
||||
this->fourcc = DRM_FORMAT_ARGB8888;
|
||||
this->format = GL_BGRA_EXT;
|
||||
}
|
||||
|
||||
egl_texDMABUFCleanup(texture);
|
||||
|
||||
glGenTextures(parent->texCount, parent->tex);
|
||||
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;
|
||||
@@ -173,10 +308,32 @@ 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->format = texture->format.format;
|
||||
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);
|
||||
}
|
||||
@@ -220,6 +377,10 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture,
|
||||
|
||||
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)
|
||||
@@ -242,14 +403,13 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture,
|
||||
return false;
|
||||
}
|
||||
|
||||
EGLImage image = fdImage->image;
|
||||
if (unlikely(image == EGL_NO_IMAGE))
|
||||
if (unlikely(fdImage->image == EGL_NO_IMAGE))
|
||||
{
|
||||
bool setup = false;
|
||||
if (texture->format.pixFmt == EGL_PF_RGB_24 && has24BitSupport)
|
||||
{
|
||||
image = createImage(texture, update->dmaFD);
|
||||
if (image == EGL_NO_IMAGE)
|
||||
fdImage->image = createImage(texture, update->dmaFD);
|
||||
if (fdImage->image == EGL_NO_IMAGE)
|
||||
{
|
||||
DEBUG_INFO("Using 24-bit in 32-bit for DMA");
|
||||
has24BitSupport = false;
|
||||
@@ -258,80 +418,184 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture,
|
||||
}
|
||||
|
||||
if (!has24BitSupport && setup)
|
||||
texDMABUFSetup(texture);
|
||||
{
|
||||
if (!texDMABUFSetup(texture))
|
||||
return false;
|
||||
fdImage = &this->images[0];
|
||||
}
|
||||
|
||||
if (image == EGL_NO_IMAGE)
|
||||
image = createImage(texture, update->dmaFD);
|
||||
if (fdImage->image == EGL_NO_IMAGE)
|
||||
fdImage->image = createImage(texture, update->dmaFD);
|
||||
|
||||
if (unlikely(image == EGL_NO_IMAGE))
|
||||
if (unlikely(fdImage->image == EGL_NO_IMAGE))
|
||||
{
|
||||
DEBUG_EGL_ERROR("Failed to create EGLImage for DMA transfer");
|
||||
return false;
|
||||
}
|
||||
|
||||
fdImage->fd = update->dmaFD;
|
||||
fdImage->image = image;
|
||||
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);
|
||||
}
|
||||
|
||||
const int slot = (int)(fdImage - this->images);
|
||||
fdImage->texIndex = slot;
|
||||
GLsync sync = 0;
|
||||
INTERLOCKED_SECTION(parent->copyLock,
|
||||
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)
|
||||
{
|
||||
egl_stateBindTexture(0, GL_TEXTURE_EXTERNAL_OES, parent->tex[slot]);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
g_egl_dynProcs.glEGLImageTargetTexture2DOES(
|
||||
GL_TEXTURE_EXTERNAL_OES, image);
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
fdImage->sync = sync;
|
||||
});
|
||||
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(!sync))
|
||||
if (unlikely(index < 0))
|
||||
{
|
||||
DEBUG_GL_ERROR("Failed to create DMABUF sync");
|
||||
LG_UNLOCK(parent->copyLock);
|
||||
DEBUG_ERROR("No reusable DMABUF snapshot slot");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Publish the texture attachment to the shared render context once. The
|
||||
* backing memory is synchronized separately by framebuffer_wait(), so
|
||||
* subsequent frames do not add any GL commands to this context. */
|
||||
glFlush();
|
||||
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;
|
||||
}
|
||||
|
||||
INTERLOCKED_SECTION(parent->copyLock,
|
||||
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))
|
||||
{
|
||||
fdImage->frameToken = update->frameToken;
|
||||
this->lastIndex = (int)(fdImage - this->images);
|
||||
});
|
||||
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,
|
||||
{
|
||||
index = this->lastIndex;
|
||||
haveImage = this->renderIndex >= 0;
|
||||
if (index >= 0 && egl_textureFrameAllowed(
|
||||
this->images[index].frameToken, frameTokenLimit))
|
||||
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 FdImage * pending = &this->images[index];
|
||||
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 = pending->texIndex;
|
||||
parent->rIndex = index;
|
||||
texture->frameToken = pending->frameToken;
|
||||
updated = true;
|
||||
haveImage = true;
|
||||
@@ -350,34 +614,17 @@ static EGL_TexStatus egl_texDMABUFGet(EGL_Texture * texture, GLuint * tex,
|
||||
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
||||
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
||||
|
||||
int index = -1;
|
||||
int texIndex = -1;
|
||||
GLsync sync = 0;
|
||||
int index = -1;
|
||||
|
||||
INTERLOCKED_SECTION(parent->copyLock,
|
||||
{
|
||||
index = this->renderIndex;
|
||||
if (index >= 0)
|
||||
{
|
||||
struct FdImage * cur = &this->images[index];
|
||||
texIndex = cur->texIndex;
|
||||
sync = cur->sync;
|
||||
cur->sync = 0;
|
||||
}
|
||||
});
|
||||
|
||||
if (unlikely(index < 0))
|
||||
return EGL_TEX_STATUS_NOTREADY;
|
||||
|
||||
if (sync)
|
||||
{
|
||||
/* Keep the cross-context dependency on the GPU timeline. Subsequent
|
||||
* sampling waits for the import without delaying render submission. */
|
||||
glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync(sync);
|
||||
}
|
||||
|
||||
*tex = parent->tex[texIndex];
|
||||
*tex = this->snapshots[index].texture;
|
||||
|
||||
if (fmt)
|
||||
*fmt = this->pixFmt;
|
||||
@@ -393,10 +640,42 @@ static EGL_TexStatus egl_texDMABUFBind(EGL_Texture * texture, GLuint unit)
|
||||
if ((status = egl_texDMABUFGet(texture, &tex, NULL)) != EGL_TEX_STATUS_OK)
|
||||
return status;
|
||||
|
||||
egl_stateBindTexture(unit, GL_TEXTURE_EXTERNAL_OES, tex);
|
||||
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,
|
||||
@@ -405,5 +684,8 @@ EGL_TextureOps EGL_TextureDMABUF =
|
||||
.update = egl_texDMABUFUpdate,
|
||||
.process = egl_texDMABUFProcess,
|
||||
.get = egl_texDMABUFGet,
|
||||
.bind = egl_texDMABUFBind
|
||||
.bind = egl_texDMABUFBind,
|
||||
.reset = egl_texDMABUFReset,
|
||||
.poll = egl_texDMABUFPoll,
|
||||
.markUsed = egl_texDMABUFMarkUsed,
|
||||
};
|
||||
|
||||
@@ -396,10 +396,19 @@ bool opengl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat format
|
||||
|
||||
bool opengl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int dmaFd,
|
||||
const FrameDamageRect * damage, int damageCount,
|
||||
LG_RendererFrameToken frameToken)
|
||||
LG_RendererFrameToken frameToken, LG_FrameReleaseFn releaseFn,
|
||||
void * releaseOpaque, uint64_t releaseHandle)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
|
||||
/* The legacy renderer retains the shared framebuffer until render. It does
|
||||
* not support asynchronous DMA ownership. */
|
||||
if (releaseFn)
|
||||
return false;
|
||||
|
||||
(void)releaseOpaque;
|
||||
(void)releaseHandle;
|
||||
|
||||
LG_LOCK(this->frameLock);
|
||||
this->frame = frame;
|
||||
this->pendingFrameToken = frameToken;
|
||||
|
||||
Reference in New Issue
Block a user