mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-04 06:12:04 +00:00
[client] expose EGL stages in frame latency
Correlate received frames with the texture update actually consumed by the renderer and retain damage until its token becomes renderable. Split client latency into dispatch, import, queue, preparation, setup, effects, desktop, composition, and swap stages in FRAME LATENCY. Exclude diagnostic overlay work from composition and preserve client wait time when producer timing is unavailable. Publish joined samples through a bounded token queue, avoid sleeping while producer timing is finalized, and correct Wayland photon units.
This commit is contained in:
@@ -27,8 +27,9 @@
|
||||
|
||||
struct DesktopDamage
|
||||
{
|
||||
int count;
|
||||
FrameDamageRect rects[LG_MAX_FRAME_DAMAGE_RECTS];
|
||||
LG_RendererFrameToken frameToken;
|
||||
int count;
|
||||
FrameDamageRect rects[LG_MAX_FRAME_DAMAGE_RECTS];
|
||||
};
|
||||
|
||||
typedef struct EGL_Damage EGL_Damage;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
#include "common/locking.h"
|
||||
#include "common/time.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "texture.h"
|
||||
@@ -390,14 +391,15 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
|
||||
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame,
|
||||
LG_RendererFrameToken frameToken, int dmaFd,
|
||||
const FrameDamageRect * damageRects, int damageRectsCount,
|
||||
uint64_t * waitTimeNs)
|
||||
{
|
||||
if (likely(desktop->useDMA && dmaFd >= 0))
|
||||
{
|
||||
if (likely(egl_textureUpdateFromDMA(
|
||||
desktop->texture, frame, dmaFd, waitTimeNs)))
|
||||
desktop->texture, frame, frameToken, dmaFd, waitTimeNs)))
|
||||
{
|
||||
atomic_store(&desktop->processFrame, true);
|
||||
return true;
|
||||
@@ -438,7 +440,7 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dma
|
||||
* the upload. Signalling here can race with that submission and process the
|
||||
* previous texture contents instead.
|
||||
*/
|
||||
if (likely(egl_textureUpdateFromFrame(desktop->texture, frame,
|
||||
if (likely(egl_textureUpdateFromFrame(desktop->texture, frame, frameToken,
|
||||
damageRects, damageRectsCount, waitTimeNs)))
|
||||
return true;
|
||||
|
||||
@@ -454,7 +456,10 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
unsigned int outputHeight, const float x, const float y,
|
||||
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
|
||||
LG_RendererRotate rotate, const struct DamageRects * rects,
|
||||
bool * fullFrame, EGL_Framebuffer * target)
|
||||
LG_RendererFrameToken damageFrameToken,
|
||||
LG_RendererFrameToken frameTokenLimit, bool * fullFrame,
|
||||
LG_RendererFrameToken * consumedFrameToken,
|
||||
uint64_t * effectsTime, EGL_Framebuffer * target)
|
||||
{
|
||||
EGL_Texture * tex;
|
||||
int width, height;
|
||||
@@ -478,21 +483,33 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
if (unlikely(outputWidth == 0 || outputHeight == 0))
|
||||
DEBUG_FATAL("outputWidth || outputHeight == 0");
|
||||
|
||||
const enum EGL_TexStatus status = egl_textureProcess(tex);
|
||||
const bool textureUpdated = status == EGL_TEX_STATUS_UPDATED;
|
||||
LG_RendererFrameToken frameToken;
|
||||
const enum EGL_TexStatus status =
|
||||
egl_textureProcess(tex, frameTokenLimit, &frameToken);
|
||||
const bool textureUpdated = status == EGL_TEX_STATUS_UPDATED;
|
||||
|
||||
*consumedFrameToken = frameToken;
|
||||
*effectsTime = 0;
|
||||
if (unlikely(status != EGL_TEX_STATUS_OK && !textureUpdated))
|
||||
{
|
||||
if (status != EGL_TEX_STATUS_NOTREADY)
|
||||
DEBUG_ERROR("Failed to process the desktop texture");
|
||||
}
|
||||
|
||||
*fullFrame = false;
|
||||
if (frameToken != LG_RENDERER_FRAME_TOKEN_NONE &&
|
||||
frameToken != damageFrameToken)
|
||||
{
|
||||
rects = NULL;
|
||||
*fullFrame = true;
|
||||
}
|
||||
|
||||
int scaleAlgo = EGL_SCALE_NEAREST;
|
||||
|
||||
egl_desktopRectsMatrix(desktop->matrix,
|
||||
width, height, x, y, scaleX, scaleY, rotate);
|
||||
egl_desktopRectsUpdate(desktop->mesh, rects, width, height);
|
||||
|
||||
*fullFrame = false;
|
||||
const bool hdr = desktop->hdr && !desktop->useSpice;
|
||||
uint32_t hdrPeak = 0;
|
||||
if (hdr)
|
||||
@@ -509,16 +526,22 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
bool processFrame = textureUpdated;
|
||||
processFrame |= atomic_exchange(&desktop->processFrame, false);
|
||||
processFrame |= egl_postProcessConfigModified(desktop->pp);
|
||||
if (processFrame &&
|
||||
egl_postProcessRun(desktop->pp, tex, desktop->mesh,
|
||||
width, height, outputWidth, outputHeight, dma,
|
||||
hdr && desktop->hdrPQ, (float)hdrPeak) &&
|
||||
egl_postProcessNeedsFullFrame(desktop->pp))
|
||||
if (processFrame)
|
||||
{
|
||||
/* The filter output may have changed everywhere, but this only applies to
|
||||
* the render that actually evaluated the filter. */
|
||||
egl_desktopRectsUpdate(desktop->mesh, NULL, width, height);
|
||||
*fullFrame = true;
|
||||
const uint64_t effectsStart = nanotime();
|
||||
const bool postProcessed = egl_postProcessRun(
|
||||
desktop->pp, tex, desktop->mesh,
|
||||
width, height, outputWidth, outputHeight, dma,
|
||||
hdr && desktop->hdrPQ, (float)hdrPeak);
|
||||
*effectsTime = nanotime() - effectsStart;
|
||||
|
||||
if (postProcessed && egl_postProcessNeedsFullFrame(desktop->pp))
|
||||
{
|
||||
/* The filter output may have changed everywhere, but this only applies
|
||||
* to the render that actually evaluated the filter. */
|
||||
egl_desktopRectsUpdate(desktop->mesh, NULL, width, height);
|
||||
*fullFrame = true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int finalSizeX, finalSizeY;
|
||||
|
||||
@@ -48,7 +48,8 @@ void egl_desktopSetNativeHDR(EGL_Desktop * desktop, bool nativeHDR,
|
||||
void egl_desktopGetHDRMapping(EGL_Desktop * desktop, bool * enabled,
|
||||
float * gain, float * contentPeak);
|
||||
bool egl_desktopSetup (EGL_Desktop * desktop, const LG_RendererFormat format);
|
||||
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
|
||||
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame,
|
||||
LG_RendererFrameToken frameToken, int dmaFd,
|
||||
const FrameDamageRect * damageRects, int damageRectsCount,
|
||||
uint64_t * waitTimeNs);
|
||||
void egl_desktopResize(EGL_Desktop * desktop, int width, int height);
|
||||
@@ -56,7 +57,10 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
unsigned int outputHeight, const float x, const float y,
|
||||
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
|
||||
LG_RendererRotate rotate, const struct DamageRects * rects,
|
||||
bool * fullFrame, EGL_Framebuffer * target);
|
||||
LG_RendererFrameToken damageFrameToken,
|
||||
LG_RendererFrameToken frameTokenLimit, bool * fullFrame,
|
||||
LG_RendererFrameToken * consumedFrameToken,
|
||||
uint64_t * effectsTime, EGL_Framebuffer * target);
|
||||
|
||||
void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height);
|
||||
void egl_desktopSpiceDrawFill(EGL_Desktop * desktop, int x, int y, int width,
|
||||
|
||||
@@ -378,6 +378,8 @@ static void egl_onRestart(LG_Renderer * renderer)
|
||||
this->frameContext = NULL;
|
||||
|
||||
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
||||
this->desktopDamage[this->desktopDamageIdx].frameToken =
|
||||
LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
this->desktopDamage[this->desktopDamageIdx].count = -1;
|
||||
});
|
||||
}
|
||||
@@ -742,8 +744,9 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
|
||||
return egl_desktopSetup(this->desktop, format);
|
||||
}
|
||||
|
||||
static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int dmaFd,
|
||||
const FrameDamageRect * damageRects, int damageRectsCount)
|
||||
static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame,
|
||||
int dmaFd, const FrameDamageRect * damageRects, int damageRectsCount,
|
||||
LG_RendererFrameToken frameToken)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
egl_stateCheckShared();
|
||||
@@ -751,21 +754,23 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int d
|
||||
const uint64_t start = nanotime();
|
||||
uint64_t waitTimeNs = 0;
|
||||
if (unlikely(!egl_desktopUpdate(
|
||||
this->desktop, frame, dmaFd, damageRects, damageRectsCount,
|
||||
&waitTimeNs)))
|
||||
this->desktop, frame, frameToken, dmaFd,
|
||||
damageRects, damageRectsCount, &waitTimeNs)))
|
||||
{
|
||||
DEBUG_INFO("Failed to to update the desktop");
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t elapsed = nanotime() - start;
|
||||
|
||||
/* Producer Copy already covers the interval before FrameBuffer::wp becomes
|
||||
* ready. Exclude that overlapping wait from the client Import stage. */
|
||||
app_setFrameImportTime(
|
||||
elapsed > waitTimeNs ? elapsed - waitTimeNs : 0);
|
||||
app_setFrameImportTiming(
|
||||
elapsed > waitTimeNs ? elapsed - waitTimeNs : 0, waitTimeNs);
|
||||
|
||||
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
||||
struct DesktopDamage * damage = this->desktopDamage + this->desktopDamageIdx;
|
||||
struct DesktopDamage * damage =
|
||||
this->desktopDamage + this->desktopDamageIdx;
|
||||
if (unlikely(
|
||||
damage->count == -1 ||
|
||||
damageRectsCount == 0 ||
|
||||
@@ -779,6 +784,7 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int d
|
||||
damageRectsCount * sizeof(FrameDamageRect));
|
||||
damage->count += damageRectsCount;
|
||||
}
|
||||
damage->frameToken = frameToken;
|
||||
});
|
||||
|
||||
return true;
|
||||
@@ -1334,10 +1340,14 @@ inline static void renderLetterBox(struct Inst * this)
|
||||
}
|
||||
|
||||
static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
const bool newFrame, const bool invalidateWindow,
|
||||
void (*preSwap)(void * udata), void * udata)
|
||||
LG_RendererFrameToken frameTokenLimit, const bool invalidateWindow,
|
||||
void (*preSwap)(void * udata), void * udata,
|
||||
LG_RendererFrameTiming * timing)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
*timing = (LG_RendererFrameTiming) {};
|
||||
|
||||
const uint64_t setupStart = nanotime();
|
||||
egl_stateCheckShared();
|
||||
EGLint bufferAge = egl_bufferAge(this);
|
||||
const bool hdrStateChanged = egl_updateHDRState(this, false);
|
||||
@@ -1354,9 +1364,10 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
bufferAge <= 0 || bufferAge > MAX_BUFFER_AGE ||
|
||||
this->showSpice;
|
||||
|
||||
bool hasOverlay = false;
|
||||
struct CursorState cursorState = { .visible = false };
|
||||
struct DesktopDamage * desktopDamage;
|
||||
bool hasOverlay = false;
|
||||
struct CursorState cursorState = { .visible = false };
|
||||
struct DesktopDamage noDesktopDamage = {};
|
||||
struct DesktopDamage * desktopDamage = NULL;
|
||||
|
||||
struct DamageRects * accumulated = (struct DamageRects *)alloca(
|
||||
sizeof(struct DamageRects) +
|
||||
@@ -1365,12 +1376,20 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
accumulated->count = 0;
|
||||
|
||||
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
||||
struct DesktopDamage * pendingDamage =
|
||||
this->desktopDamage + this->desktopDamageIdx;
|
||||
const bool damageQueued =
|
||||
pendingDamage->frameToken == LG_RENDERER_FRAME_TOKEN_NONE ||
|
||||
pendingDamage->frameToken <= frameTokenLimit;
|
||||
const int historyOffset = damageQueued ? 0 : 1;
|
||||
|
||||
if (likely(!renderAll))
|
||||
{
|
||||
for (int i = 0; i < bufferAge; ++i)
|
||||
{
|
||||
struct DesktopDamage * damage = this->desktopDamage +
|
||||
IDX_AGO(this->desktopDamageIdx, i, DESKTOP_DAMAGE_COUNT);
|
||||
IDX_AGO(this->desktopDamageIdx, i + historyOffset,
|
||||
DESKTOP_DAMAGE_COUNT);
|
||||
|
||||
if (unlikely(damage->count < 0))
|
||||
{
|
||||
@@ -1387,9 +1406,18 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
}
|
||||
}
|
||||
}
|
||||
desktopDamage = this->desktopDamage + this->desktopDamageIdx;
|
||||
this->desktopDamageIdx = (this->desktopDamageIdx + 1) % DESKTOP_DAMAGE_COUNT;
|
||||
this->desktopDamage[this->desktopDamageIdx].count = 0;
|
||||
|
||||
if (damageQueued)
|
||||
{
|
||||
desktopDamage = pendingDamage;
|
||||
this->desktopDamageIdx =
|
||||
(this->desktopDamageIdx + 1) % DESKTOP_DAMAGE_COUNT;
|
||||
this->desktopDamage[this->desktopDamageIdx].frameToken =
|
||||
LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
this->desktopDamage[this->desktopDamageIdx].count = 0;
|
||||
}
|
||||
else
|
||||
desktopDamage = &noDesktopDamage;
|
||||
});
|
||||
|
||||
if (hdrStateChanged)
|
||||
@@ -1427,17 +1455,46 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
}
|
||||
++this->overlayHistoryIdx;
|
||||
|
||||
bool fullFrame = false;
|
||||
const bool linearHDRComposition = egl_hdrComposeBegin(this->hdrCompose);
|
||||
bool deferredLogicalCursor = false;
|
||||
if (likely(this->destRect.w > 0 && this->destRect.h > 0))
|
||||
bool fullFrame = false;
|
||||
const bool linearHDRComposition = egl_hdrComposeBegin(
|
||||
this->hdrCompose);
|
||||
bool deferredLogicalCursor = false;
|
||||
const bool haveDesktop = likely(
|
||||
this->destRect.w > 0 && this->destRect.h > 0);
|
||||
bool desktopRendered = false;
|
||||
const uint64_t desktopStart = nanotime();
|
||||
uint64_t effectsTime = 0;
|
||||
LG_RendererFrameToken frameToken = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
|
||||
timing->setupTime = desktopStart - setupStart;
|
||||
if (haveDesktop)
|
||||
{
|
||||
if (egl_desktopRender(this->desktop,
|
||||
desktopRendered = egl_desktopRender(this->desktop,
|
||||
this->destRect.w, this->destRect.h,
|
||||
this->translateX, this->translateY,
|
||||
this->scaleX , this->scaleY ,
|
||||
this->scaleType , rotate, renderAll ? NULL : accumulated,
|
||||
&fullFrame, egl_hdrComposeGetFramebuffer(this->hdrCompose)))
|
||||
desktopDamage->frameToken, frameTokenLimit, &fullFrame, &frameToken,
|
||||
&effectsTime,
|
||||
egl_hdrComposeGetFramebuffer(this->hdrCompose));
|
||||
}
|
||||
const uint64_t composeStart = nanotime();
|
||||
if (haveDesktop)
|
||||
{
|
||||
const uint64_t totalDesktopTime = composeStart - desktopStart;
|
||||
timing->effectsTime = effectsTime;
|
||||
timing->desktopTime = totalDesktopTime > effectsTime ?
|
||||
totalDesktopTime - effectsTime : 0;
|
||||
}
|
||||
else
|
||||
timing->setupTime += composeStart - desktopStart;
|
||||
|
||||
timing->frameToken = frameToken;
|
||||
const bool frameConsumed = frameToken != LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
|
||||
if (haveDesktop)
|
||||
{
|
||||
if (desktopRendered)
|
||||
{
|
||||
cursorState = egl_cursorRender(this->cursor,
|
||||
(this->format.rotate + rotate) % LG_ROTATE_MAX,
|
||||
@@ -1456,9 +1513,13 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
renderLetterBox(this);
|
||||
|
||||
hasOverlay |=
|
||||
egl_damageRender(this->damage, rotate, newFrame ? desktopDamage : NULL) |
|
||||
egl_damageRender(
|
||||
this->damage, rotate, frameConsumed ? desktopDamage : NULL) |
|
||||
invalidateWindow;
|
||||
|
||||
/* The diagnostics being displayed must not contribute to Compose. */
|
||||
timing->composeTime = nanotime() - composeStart;
|
||||
|
||||
struct Rect damage[LG_MAX_FRAME_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2];
|
||||
int damageIdx = app_renderOverlay(damage, MAX_OVERLAY_RECTS);
|
||||
if (unlikely(damageIdx != 0))
|
||||
@@ -1482,6 +1543,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
for (int i = 0; i < damageIdx; ++i)
|
||||
damage[i].y = this->height - damage[i].y - damage[i].h;
|
||||
}
|
||||
const uint64_t postOverlayStart = nanotime();
|
||||
|
||||
if (likely(damageIdx >= 0 && cursorState.visible))
|
||||
damage[damageIdx++] = cursorState.rect;
|
||||
@@ -1559,13 +1621,19 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
egl_cursorRender(this->cursor,
|
||||
(this->format.rotate + rotate) % LG_ROTATE_MAX,
|
||||
this->width, this->height, false, NULL);
|
||||
|
||||
this->hadOverlay = hasOverlay;
|
||||
this->cursorLast = cursorState;
|
||||
|
||||
preSwap(udata);
|
||||
|
||||
const uint64_t composeEnd = nanotime();
|
||||
timing->composeTime += composeEnd - postOverlayStart;
|
||||
|
||||
const uint64_t swapStart = nanotime();
|
||||
app_eglSwapBuffers(this->display, this->surface, damage,
|
||||
this->noSwapDamage ? 0 : damageIdx);
|
||||
const uint64_t swapEnd = nanotime();
|
||||
timing->swapTime = swapEnd - swapStart;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -160,12 +160,14 @@ bool egl_textureUpdateRect(EGL_Texture * this,
|
||||
}
|
||||
|
||||
bool egl_textureUpdateFromFrame(EGL_Texture * this,
|
||||
const FrameBuffer * frame, const FrameDamageRect * damageRects,
|
||||
int damageRectsCount, uint64_t * waitTimeNs)
|
||||
const FrameBuffer * frame, LG_RendererFrameToken frameToken,
|
||||
const FrameDamageRect * damageRects, int damageRectsCount,
|
||||
uint64_t * waitTimeNs)
|
||||
{
|
||||
const struct EGL_TexUpdate update =
|
||||
{
|
||||
.type = EGL_TEXTYPE_FRAMEBUFFER,
|
||||
.frameToken = frameToken,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = this->format.width,
|
||||
@@ -182,18 +184,20 @@ bool egl_textureUpdateFromFrame(EGL_Texture * this,
|
||||
}
|
||||
|
||||
bool egl_textureUpdateFromDMA(EGL_Texture * this,
|
||||
const FrameBuffer * frame, const int dmaFd, uint64_t * waitTimeNs)
|
||||
const FrameBuffer * frame, LG_RendererFrameToken frameToken,
|
||||
const int dmaFd, uint64_t * waitTimeNs)
|
||||
{
|
||||
const struct EGL_TexUpdate update =
|
||||
{
|
||||
.type = EGL_TEXTYPE_DMABUF,
|
||||
.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
|
||||
};
|
||||
|
||||
/* wait for completion */
|
||||
@@ -204,9 +208,18 @@ bool egl_textureUpdateFromDMA(EGL_Texture * this,
|
||||
return this->ops.update(this, &update);
|
||||
}
|
||||
|
||||
enum EGL_TexStatus egl_textureProcess(EGL_Texture * this)
|
||||
enum EGL_TexStatus egl_textureProcess(EGL_Texture * this,
|
||||
LG_RendererFrameToken frameTokenLimit,
|
||||
LG_RendererFrameToken * consumedFrameToken)
|
||||
{
|
||||
return this->ops.process(this);
|
||||
if (consumedFrameToken)
|
||||
*consumedFrameToken = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
|
||||
const enum EGL_TexStatus status =
|
||||
this->ops.process(this, frameTokenLimit);
|
||||
if (status == EGL_TEX_STATUS_UPDATED && consumedFrameToken)
|
||||
*consumedFrameToken = this->frameToken;
|
||||
return status;
|
||||
}
|
||||
|
||||
enum EGL_TexStatus egl_textureBind(EGL_Texture * this)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "model.h"
|
||||
#include "common/framebuffer.h"
|
||||
#include "common/types.h"
|
||||
#include "interface/renderer.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
@@ -40,8 +41,9 @@ typedef struct EGL_Model EGL_Model;
|
||||
typedef struct EGL_TexUpdate
|
||||
{
|
||||
/* the type of this update */
|
||||
EGL_TexType type;
|
||||
uint64_t * waitTimeNs;
|
||||
EGL_TexType type;
|
||||
LG_RendererFrameToken frameToken;
|
||||
uint64_t * waitTimeNs;
|
||||
|
||||
int x, y, width, height;
|
||||
|
||||
@@ -90,7 +92,8 @@ typedef struct EGL_TextureOps
|
||||
bool (*update)(EGL_Texture * texture, const EGL_TexUpdate * update);
|
||||
|
||||
/* called from a background job to prepare the texture for use before bind */
|
||||
enum EGL_TexStatus (*process)(EGL_Texture * texture);
|
||||
enum EGL_TexStatus (*process)(EGL_Texture * texture,
|
||||
LG_RendererFrameToken frameTokenLimit);
|
||||
|
||||
/* get the texture for use */
|
||||
enum EGL_TexStatus (*get)(EGL_Texture * texture, GLuint * tex,
|
||||
@@ -104,10 +107,11 @@ EGL_TextureOps;
|
||||
struct EGL_Texture
|
||||
{
|
||||
struct EGL_TextureOps ops;
|
||||
EGL_TexType type;
|
||||
GLuint sampler;
|
||||
EGL_TexType type;
|
||||
GLuint sampler;
|
||||
LG_RendererFrameToken frameToken;
|
||||
|
||||
EGL_TexFormat format;
|
||||
EGL_TexFormat format;
|
||||
};
|
||||
|
||||
bool egl_textureInit(EGL_Texture ** texture, EGLDisplay * display,
|
||||
@@ -125,13 +129,24 @@ bool egl_textureUpdateRect(EGL_Texture * texture,
|
||||
const uint8_t * buffer, bool topDown);
|
||||
|
||||
bool egl_textureUpdateFromFrame(EGL_Texture * texture,
|
||||
const FrameBuffer * frame, const FrameDamageRect * damageRects,
|
||||
int damageRectsCount, uint64_t * waitTimeNs);
|
||||
const FrameBuffer * frame, LG_RendererFrameToken frameToken,
|
||||
const FrameDamageRect * damageRects, int damageRectsCount,
|
||||
uint64_t * waitTimeNs);
|
||||
|
||||
bool egl_textureUpdateFromDMA(EGL_Texture * texture,
|
||||
const FrameBuffer * frame, const int dmaFd, uint64_t * waitTimeNs);
|
||||
const FrameBuffer * frame, LG_RendererFrameToken frameToken,
|
||||
const int dmaFd, uint64_t * waitTimeNs);
|
||||
|
||||
enum EGL_TexStatus egl_textureProcess(EGL_Texture * texture);
|
||||
enum EGL_TexStatus egl_textureProcess(EGL_Texture * texture,
|
||||
LG_RendererFrameToken frameTokenLimit,
|
||||
LG_RendererFrameToken * consumedFrameToken);
|
||||
|
||||
static inline bool egl_textureFrameAllowed(LG_RendererFrameToken frameToken,
|
||||
LG_RendererFrameToken frameTokenLimit)
|
||||
{
|
||||
return frameToken == LG_RENDERER_FRAME_TOKEN_NONE ||
|
||||
frameToken <= frameTokenLimit;
|
||||
}
|
||||
|
||||
static inline EGL_TexStatus egl_textureGet(EGL_Texture * texture, GLuint * tex,
|
||||
unsigned int * sizeX, unsigned int * sizeY, EGL_PixelFormat * fmt)
|
||||
|
||||
@@ -108,11 +108,15 @@ bool egl_texBufferSetup(EGL_Texture * texture, const EGL_TexSetup * setup)
|
||||
NULL);
|
||||
}
|
||||
|
||||
this->bufIndex = 0;
|
||||
this->rIndex = -1;
|
||||
this->bufIndex = 0;
|
||||
this->rIndex = -1;
|
||||
texture->frameToken = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
|
||||
for (int i = 0; i < this->texCount; ++i)
|
||||
{
|
||||
this->buf[i].updated = false;
|
||||
this->slotToken[i] = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -138,8 +142,10 @@ static bool egl_texBufferUpdate(EGL_Texture * texture, const EGL_TexUpdate * upd
|
||||
return true;
|
||||
}
|
||||
|
||||
EGL_TexStatus egl_texBufferProcess(EGL_Texture * texture)
|
||||
EGL_TexStatus egl_texBufferProcess(EGL_Texture * texture,
|
||||
LG_RendererFrameToken frameTokenLimit)
|
||||
{
|
||||
(void)frameTokenLimit;
|
||||
return EGL_TEX_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -260,13 +266,15 @@ static bool egl_texBufferStreamUpdate(EGL_Texture * texture,
|
||||
}
|
||||
}
|
||||
|
||||
this->slotToken[this->bufIndex] = update->frameToken;
|
||||
this->buf[this->bufIndex].updated = true;
|
||||
LG_UNLOCK(this->copyLock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture)
|
||||
EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture,
|
||||
LG_RendererFrameToken frameTokenLimit)
|
||||
{
|
||||
TextureBuffer * this = UPCAST(TextureBuffer, texture);
|
||||
|
||||
@@ -276,7 +284,8 @@ EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture)
|
||||
GLuint tex = this->tex[index];
|
||||
EGL_TexBuffer * buffer = &this->buf[index];
|
||||
|
||||
if (!buffer->updated)
|
||||
if (!buffer->updated ||
|
||||
!egl_textureFrameAllowed(this->slotToken[index], frameTokenLimit))
|
||||
{
|
||||
LG_UNLOCK(this->copyLock);
|
||||
return EGL_TEX_STATUS_OK;
|
||||
@@ -284,7 +293,8 @@ EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture)
|
||||
|
||||
DEBUG_ASSERT(!this->sync[index]);
|
||||
|
||||
this->rIndex = index;
|
||||
this->rIndex = index;
|
||||
texture->frameToken = this->slotToken[index];
|
||||
if (++this->bufIndex == this->texCount)
|
||||
this->bufIndex = 0;
|
||||
buffer->updated = false;
|
||||
|
||||
@@ -31,14 +31,15 @@ typedef struct TextureBuffer
|
||||
EGL_Texture base;
|
||||
bool free;
|
||||
|
||||
int texCount;
|
||||
GLuint tex[EGL_TEX_BUFFER_MAX];
|
||||
EGL_TexBuffer buf[EGL_TEX_BUFFER_MAX];
|
||||
int bufFree;
|
||||
GLsync sync[EGL_TEX_BUFFER_MAX];
|
||||
LG_Lock copyLock;
|
||||
int bufIndex;
|
||||
int rIndex;
|
||||
int texCount;
|
||||
GLuint tex[EGL_TEX_BUFFER_MAX];
|
||||
EGL_TexBuffer buf[EGL_TEX_BUFFER_MAX];
|
||||
int bufFree;
|
||||
GLsync sync[EGL_TEX_BUFFER_MAX];
|
||||
LG_RendererFrameToken slotToken[EGL_TEX_BUFFER_MAX];
|
||||
LG_Lock copyLock;
|
||||
int bufIndex;
|
||||
int rIndex;
|
||||
}
|
||||
TextureBuffer;
|
||||
|
||||
@@ -46,7 +47,8 @@ bool egl_texBufferInit(EGL_Texture ** texture_, EGL_TexType type,
|
||||
EGLDisplay * display);
|
||||
void egl_texBufferFree(EGL_Texture * texture_);
|
||||
bool egl_texBufferSetup(EGL_Texture * texture_, const EGL_TexSetup * setup);
|
||||
EGL_TexStatus egl_texBufferProcess(EGL_Texture * texture_);
|
||||
EGL_TexStatus egl_texBufferProcess(EGL_Texture * texture_,
|
||||
LG_RendererFrameToken frameTokenLimit);
|
||||
EGL_TexStatus egl_texBufferGet(EGL_Texture * texture_, GLuint * tex,
|
||||
EGL_PixelFormat * fmt);
|
||||
EGL_TexStatus egl_texBufferBind(EGL_Texture * texture_, GLuint unit);
|
||||
@@ -57,6 +59,7 @@ bool egl_texBufferStreamSetup(EGL_Texture * texture_,
|
||||
const EGL_TexSetup * setup);
|
||||
/* Returns with copyLock held when the current upload buffer is safe to write. */
|
||||
bool egl_texBufferStreamLock(TextureBuffer * texture);
|
||||
EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture_);
|
||||
EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture_,
|
||||
LG_RendererFrameToken frameTokenLimit);
|
||||
EGL_TexStatus egl_texBufferStreamGet(EGL_Texture * texture_, GLuint * tex,
|
||||
EGL_PixelFormat * fmt);
|
||||
|
||||
@@ -29,10 +29,11 @@
|
||||
|
||||
struct FdImage
|
||||
{
|
||||
int fd;
|
||||
EGLImage image;
|
||||
GLsync sync;
|
||||
int texIndex;
|
||||
int fd;
|
||||
EGLImage image;
|
||||
GLsync sync;
|
||||
int texIndex;
|
||||
LG_RendererFrameToken frameToken;
|
||||
};
|
||||
|
||||
typedef struct TexDMABUF
|
||||
@@ -43,6 +44,7 @@ typedef struct TexDMABUF
|
||||
|
||||
struct FdImage images[2];
|
||||
int lastIndex;
|
||||
int renderIndex;
|
||||
|
||||
EGL_PixelFormat pixFmt;
|
||||
unsigned fourcc;
|
||||
@@ -76,10 +78,16 @@ static void egl_texDMABUFCleanup(EGL_Texture * texture)
|
||||
glDeleteSync(this->images[i].sync);
|
||||
this->images[i].sync = 0;
|
||||
}
|
||||
this->images[i].fd = -1;
|
||||
this->images[i].texIndex = -1;
|
||||
this->images[i].fd = -1;
|
||||
this->images[i].texIndex = -1;
|
||||
this->images[i].frameToken = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
}
|
||||
|
||||
this->lastIndex = -1;
|
||||
this->renderIndex = -1;
|
||||
parent->rIndex = -1;
|
||||
texture->frameToken = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
|
||||
egl_texUtilFreeBuffers(parent->buf, parent->texCount);
|
||||
|
||||
if (parent->tex[0])
|
||||
@@ -99,12 +107,14 @@ 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].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->lastIndex = -1;
|
||||
this->lastIndex = -1;
|
||||
this->renderIndex = -1;
|
||||
|
||||
EGL_Texture * parent = &this->base.base;
|
||||
if (!egl_texBufferStreamInit(&parent, type, display))
|
||||
@@ -276,15 +286,45 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture,
|
||||
|
||||
INTERLOCKED_SECTION(parent->copyLock,
|
||||
{
|
||||
this->lastIndex = (fdImage == &this->images[0]) ? 0 : 1;
|
||||
fdImage->frameToken = update->frameToken;
|
||||
this->lastIndex = (fdImage == &this->images[0]) ? 0 : 1;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static EGL_TexStatus egl_texDMABUFProcess(EGL_Texture * texture)
|
||||
static EGL_TexStatus egl_texDMABUFProcess(EGL_Texture * texture,
|
||||
LG_RendererFrameToken frameTokenLimit)
|
||||
{
|
||||
return EGL_TEX_STATUS_OK;
|
||||
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
|
||||
TexDMABUF * this = UPCAST(TexDMABUF , parent);
|
||||
|
||||
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))
|
||||
{
|
||||
const struct FdImage * pending = &this->images[index];
|
||||
if (this->renderIndex != index ||
|
||||
texture->frameToken != pending->frameToken)
|
||||
{
|
||||
this->renderIndex = index;
|
||||
parent->rIndex = pending->texIndex;
|
||||
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,
|
||||
@@ -299,7 +339,7 @@ static EGL_TexStatus egl_texDMABUFGet(EGL_Texture * texture, GLuint * tex,
|
||||
|
||||
INTERLOCKED_SECTION(parent->copyLock,
|
||||
{
|
||||
index = this->lastIndex;
|
||||
index = this->renderIndex;
|
||||
if (index >= 0)
|
||||
{
|
||||
struct FdImage * cur = &this->images[index];
|
||||
|
||||
@@ -156,12 +156,14 @@ static bool egl_texFBUpdate(EGL_Texture * texture, const EGL_TexUpdate * update)
|
||||
{
|
||||
/* The mapped PBO may contain a partial copy. Force a full refresh if the
|
||||
* caller recovers, and never expose this slot to the render context. */
|
||||
damage->count = -1;
|
||||
damage->count = -1;
|
||||
parent->buf[parent->bufIndex].updated = false;
|
||||
parent->slotToken[parent->bufIndex] = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
LG_UNLOCK(parent->copyLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
parent->slotToken[parent->bufIndex] = update->frameToken;
|
||||
parent->buf[parent->bufIndex].updated = true;
|
||||
|
||||
for (int i = 0; i < EGL_TEX_BUFFER_MAX; ++i)
|
||||
|
||||
@@ -107,44 +107,46 @@ struct Inst
|
||||
LG_RendererParams params;
|
||||
struct OpenGL_Options opt;
|
||||
|
||||
bool amdPinnedMemSupport;
|
||||
bool renderStarted;
|
||||
bool configured;
|
||||
bool reconfigure;
|
||||
LG_DSGLContext glContext;
|
||||
bool amdPinnedMemSupport;
|
||||
bool renderStarted;
|
||||
bool configured;
|
||||
bool reconfigure;
|
||||
LG_DSGLContext glContext;
|
||||
|
||||
struct IntPoint window;
|
||||
float uiScale;
|
||||
_Atomic(bool) frameUpdate;
|
||||
struct IntPoint window;
|
||||
float uiScale;
|
||||
_Atomic(bool) frameUpdate;
|
||||
|
||||
LG_Lock formatLock;
|
||||
LG_RendererFormat format;
|
||||
GLuint intFormat;
|
||||
GLuint vboFormat;
|
||||
GLuint dataFormat;
|
||||
size_t texSize;
|
||||
size_t texPos;
|
||||
float scaleX, scaleY;
|
||||
const FrameBuffer * frame;
|
||||
LG_Lock formatLock;
|
||||
LG_RendererFormat format;
|
||||
GLuint intFormat;
|
||||
GLuint vboFormat;
|
||||
GLuint dataFormat;
|
||||
size_t texSize;
|
||||
size_t texPos;
|
||||
float scaleX, scaleY;
|
||||
const FrameBuffer * frame;
|
||||
LG_RendererFrameToken pendingFrameToken;
|
||||
|
||||
uint64_t drawStart;
|
||||
bool hasBuffers;
|
||||
GLuint vboID[BUFFER_COUNT];
|
||||
uint8_t * texPixels[BUFFER_COUNT];
|
||||
LG_Lock frameLock;
|
||||
bool texReady;
|
||||
int texWIndex, texRIndex;
|
||||
int texList;
|
||||
int mouseList;
|
||||
int spiceList;
|
||||
LG_RendererRect destRect;
|
||||
struct IntPoint spiceSize;
|
||||
bool spiceShow;
|
||||
uint64_t drawStart;
|
||||
bool hasBuffers;
|
||||
GLuint vboID[BUFFER_COUNT];
|
||||
uint8_t * texPixels[BUFFER_COUNT];
|
||||
LG_Lock frameLock;
|
||||
bool texReady;
|
||||
int texWIndex, texRIndex;
|
||||
int texList;
|
||||
int mouseList;
|
||||
int spiceList;
|
||||
LG_RendererRect destRect;
|
||||
struct IntPoint spiceSize;
|
||||
bool spiceShow;
|
||||
|
||||
bool hasTextures, hasFrames;
|
||||
GLuint frames[BUFFER_COUNT];
|
||||
GLsync fences[BUFFER_COUNT];
|
||||
GLuint textures[TEXTURE_COUNT];
|
||||
bool hasTextures, hasFrames;
|
||||
GLuint frames[BUFFER_COUNT];
|
||||
GLsync fences[BUFFER_COUNT];
|
||||
LG_RendererFrameToken frameToken[BUFFER_COUNT];
|
||||
GLuint textures[TEXTURE_COUNT];
|
||||
|
||||
LG_Lock mouseLock;
|
||||
LG_RendererCursor mouseCursor;
|
||||
@@ -174,7 +176,9 @@ enum ConfigStatus
|
||||
static void deconfigure(struct Inst * this);
|
||||
static enum ConfigStatus configure(struct Inst * this);
|
||||
static void updateMouseShape(struct Inst * this);
|
||||
static bool drawFrame(struct Inst * this);
|
||||
static bool drawFrame(struct Inst * this,
|
||||
LG_RendererFrameToken frameTokenLimit,
|
||||
LG_RendererFrameToken * consumedFrameToken);
|
||||
static void drawMouse(struct Inst * this);
|
||||
|
||||
const char * opengl_getName(void)
|
||||
@@ -391,12 +395,14 @@ 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)
|
||||
const FrameDamageRect * damage, int damageCount,
|
||||
LG_RendererFrameToken frameToken)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
|
||||
LG_LOCK(this->frameLock);
|
||||
this->frame = frame;
|
||||
this->frame = frame;
|
||||
this->pendingFrameToken = frameToken;
|
||||
atomic_store_explicit(&this->frameUpdate, true, memory_order_release);
|
||||
LG_UNLOCK(this->frameLock);
|
||||
|
||||
@@ -486,10 +492,13 @@ bool opengl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool opengl_render(LG_Renderer * renderer, LG_RendererRotate rotate, const bool newFrame,
|
||||
const bool invalidateWindow, void (*preSwap)(void * udata), void * udata)
|
||||
bool opengl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
LG_RendererFrameToken frameTokenLimit, const bool invalidateWindow,
|
||||
void (*preSwap)(void * udata), void * udata,
|
||||
LG_RendererFrameTiming * timing)
|
||||
{
|
||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||
*timing = (LG_RendererFrameTiming) {};
|
||||
|
||||
setupModelView(this);
|
||||
|
||||
@@ -501,7 +510,7 @@ bool opengl_render(LG_Renderer * renderer, LG_RendererRotate rotate, const bool
|
||||
|
||||
case CONFIG_STATUS_NOOP :
|
||||
case CONFIG_STATUS_OK :
|
||||
if (!drawFrame(this))
|
||||
if (!drawFrame(this, frameTokenLimit, &timing->frameToken))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1024,16 +1033,17 @@ static void deconfigure(struct Inst * this)
|
||||
this->hasBuffers = false;
|
||||
}
|
||||
|
||||
if (this->amdPinnedMemSupport)
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
{
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
if (this->fences[i])
|
||||
{
|
||||
if (this->fences[i])
|
||||
{
|
||||
g_gl_dynProcs.glDeleteSync(this->fences[i]);
|
||||
this->fences[i] = NULL;
|
||||
}
|
||||
g_gl_dynProcs.glDeleteSync(this->fences[i]);
|
||||
this->fences[i] = NULL;
|
||||
}
|
||||
this->frameToken[i] = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
|
||||
if (this->amdPinnedMemSupport)
|
||||
{
|
||||
if (this->texPixels[i])
|
||||
{
|
||||
free(this->texPixels[i]);
|
||||
@@ -1208,8 +1218,12 @@ static bool opengl_bufferFn(void * opaque, const void * data, size_t size)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool drawFrame(struct Inst * this)
|
||||
static bool drawFrame(struct Inst * this,
|
||||
LG_RendererFrameToken frameTokenLimit,
|
||||
LG_RendererFrameToken * consumedFrameToken)
|
||||
{
|
||||
*consumedFrameToken = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
|
||||
if (g_gl_dynProcs.glIsSync(this->fences[this->texWIndex]))
|
||||
{
|
||||
switch(g_gl_dynProcs.glClientWaitSync(this->fences[this->texWIndex], 0, GL_TIMEOUT_IGNORED))
|
||||
@@ -1234,16 +1248,21 @@ static bool drawFrame(struct Inst * this)
|
||||
this->fences[this->texWIndex] = NULL;
|
||||
|
||||
this->texRIndex = this->texWIndex;
|
||||
*consumedFrameToken = this->frameToken[this->texRIndex];
|
||||
this->frameToken[this->texRIndex] = LG_RENDERER_FRAME_TOKEN_NONE;
|
||||
if (++this->texWIndex == BUFFER_COUNT)
|
||||
this->texWIndex = 0;
|
||||
}
|
||||
|
||||
LG_LOCK(this->frameLock);
|
||||
if (!atomic_exchange_explicit(&this->frameUpdate, false, memory_order_acquire))
|
||||
if (!atomic_load_explicit(&this->frameUpdate, memory_order_acquire) ||
|
||||
this->pendingFrameToken > frameTokenLimit)
|
||||
{
|
||||
LG_UNLOCK(this->frameLock);
|
||||
return true;
|
||||
}
|
||||
atomic_store_explicit(&this->frameUpdate, false, memory_order_release);
|
||||
const LG_RendererFrameToken pendingFrameToken = this->pendingFrameToken;
|
||||
|
||||
LG_LOCK(this->formatLock);
|
||||
glBindTexture(GL_TEXTURE_2D, this->frames[this->texWIndex]);
|
||||
@@ -1317,6 +1336,7 @@ static bool drawFrame(struct Inst * this)
|
||||
// set a fence so we don't overwrite a buffer in use
|
||||
this->fences[this->texWIndex] =
|
||||
g_gl_dynProcs.glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
this->frameToken[this->texWIndex] = pendingFrameToken;
|
||||
glFlush();
|
||||
|
||||
LG_UNLOCK(this->formatLock);
|
||||
|
||||
Reference in New Issue
Block a user