mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-12-15 13:52:28 +00:00
Use EGL fences to reduce video latency
This commit is contained in:
parent
81d5e7f014
commit
e3a7b54f90
3 changed files with 93 additions and 9 deletions
|
@ -76,11 +76,16 @@ EGLRenderer::EGLRenderer(IFFmpegRenderer *backendRenderer)
|
||||||
m_Backend(backendRenderer),
|
m_Backend(backendRenderer),
|
||||||
m_VAO(0),
|
m_VAO(0),
|
||||||
m_BlockingSwapBuffers(false),
|
m_BlockingSwapBuffers(false),
|
||||||
|
m_LastRenderSync(EGL_NO_SYNC),
|
||||||
m_LastFrame(av_frame_alloc()),
|
m_LastFrame(av_frame_alloc()),
|
||||||
m_glEGLImageTargetTexture2DOES(nullptr),
|
m_glEGLImageTargetTexture2DOES(nullptr),
|
||||||
m_glGenVertexArraysOES(nullptr),
|
m_glGenVertexArraysOES(nullptr),
|
||||||
m_glBindVertexArrayOES(nullptr),
|
m_glBindVertexArrayOES(nullptr),
|
||||||
m_glDeleteVertexArraysOES(nullptr),
|
m_glDeleteVertexArraysOES(nullptr),
|
||||||
|
m_eglCreateSync(nullptr),
|
||||||
|
m_eglCreateSyncKHR(nullptr),
|
||||||
|
m_eglDestroySync(nullptr),
|
||||||
|
m_eglClientWaitSync(nullptr),
|
||||||
m_GlesMajorVersion(0),
|
m_GlesMajorVersion(0),
|
||||||
m_GlesMinorVersion(0),
|
m_GlesMinorVersion(0),
|
||||||
m_HasExtUnpackSubimage(false),
|
m_HasExtUnpackSubimage(false),
|
||||||
|
@ -100,6 +105,10 @@ EGLRenderer::~EGLRenderer()
|
||||||
if (m_Context) {
|
if (m_Context) {
|
||||||
// Reattach the GL context to the main thread for destruction
|
// Reattach the GL context to the main thread for destruction
|
||||||
SDL_GL_MakeCurrent(m_Window, m_Context);
|
SDL_GL_MakeCurrent(m_Window, m_Context);
|
||||||
|
if (m_LastRenderSync != EGL_NO_SYNC) {
|
||||||
|
SDL_assert(m_eglDestroySync != nullptr);
|
||||||
|
m_eglDestroySync(m_EGLDisplay, m_LastRenderSync);
|
||||||
|
}
|
||||||
if (m_ShaderProgram) {
|
if (m_ShaderProgram) {
|
||||||
glDeleteProgram(m_ShaderProgram);
|
glDeleteProgram(m_ShaderProgram);
|
||||||
}
|
}
|
||||||
|
@ -615,6 +624,30 @@ bool EGLRenderer::initialize(PDECODER_PARAMETERS params)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EGL_KHR_fence_sync is an extension for EGL 1.1+
|
||||||
|
if (eglExtensions.isSupported("EGL_KHR_fence_sync")) {
|
||||||
|
// eglCreateSyncKHR() has a slightly different prototype to eglCreateSync()
|
||||||
|
m_eglCreateSyncKHR = (typeof(m_eglCreateSyncKHR))eglGetProcAddress("eglCreateSyncKHR");
|
||||||
|
m_eglDestroySync = (typeof(m_eglDestroySync))eglGetProcAddress("eglDestroySyncKHR");
|
||||||
|
m_eglClientWaitSync = (typeof(m_eglClientWaitSync))eglGetProcAddress("eglClientWaitSyncKHR");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// EGL 1.5 introduced sync support to the core specification
|
||||||
|
m_eglCreateSync = (typeof(m_eglCreateSync))eglGetProcAddress("eglCreateSync");
|
||||||
|
m_eglDestroySync = (typeof(m_eglDestroySync))eglGetProcAddress("eglDestroySync");
|
||||||
|
m_eglClientWaitSync = (typeof(m_eglClientWaitSync))eglGetProcAddress("eglClientWaitSync");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(m_eglCreateSync || m_eglCreateSyncKHR) || !m_eglDestroySync || !m_eglClientWaitSync) {
|
||||||
|
EGL_LOG(Warn, "Failed to find sync functions");
|
||||||
|
|
||||||
|
// Sub-optimal, but not fatal
|
||||||
|
m_eglCreateSync = nullptr;
|
||||||
|
m_eglCreateSyncKHR = nullptr;
|
||||||
|
m_eglDestroySync = nullptr;
|
||||||
|
m_eglClientWaitSync = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
/* Compute the video region size in order to keep the aspect ratio of the
|
/* Compute the video region size in order to keep the aspect ratio of the
|
||||||
* video stream.
|
* video stream.
|
||||||
*/
|
*/
|
||||||
|
@ -820,6 +853,18 @@ void EGLRenderer::cleanupRenderContext()
|
||||||
SDL_GL_MakeCurrent(m_Window, nullptr);
|
SDL_GL_MakeCurrent(m_Window, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EGLRenderer::waitToRender()
|
||||||
|
{
|
||||||
|
// Ensure our GL context is active on this thread
|
||||||
|
// See comment in renderFrame() for more details.
|
||||||
|
SDL_GL_MakeCurrent(m_Window, m_Context);
|
||||||
|
|
||||||
|
if (m_LastRenderSync != 0) {
|
||||||
|
SDL_assert(m_eglClientWaitSync != nullptr);
|
||||||
|
m_eglClientWaitSync(m_EGLDisplay, m_LastRenderSync, EGL_SYNC_FLUSH_COMMANDS_BIT, EGL_FOREVER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EGLRenderer::renderFrame(AVFrame* frame)
|
void EGLRenderer::renderFrame(AVFrame* frame)
|
||||||
{
|
{
|
||||||
EGLImage imgs[EGL_MAX_PLANES];
|
EGLImage imgs[EGL_MAX_PLANES];
|
||||||
|
@ -878,15 +923,34 @@ void EGLRenderer::renderFrame(AVFrame* frame)
|
||||||
SDL_GL_SwapWindow(m_Window);
|
SDL_GL_SwapWindow(m_Window);
|
||||||
|
|
||||||
if (m_BlockingSwapBuffers) {
|
if (m_BlockingSwapBuffers) {
|
||||||
// This glClear() forces us to block until the buffer swap is
|
// If we this EGL implementation supports fences, use those to delay
|
||||||
// complete to continue rendering. Mesa won't actually wait
|
// rendering the next frame until this one is completed.
|
||||||
// for the swap with just glFinish() alone. Waiting here keeps us
|
if (m_eglClientWaitSync != nullptr) {
|
||||||
// in lock step with the display refresh rate. If we don't wait
|
// Delete the sync object from last render
|
||||||
// here, we'll stall on the first GL call next frame. Doing the
|
if (m_LastRenderSync != EGL_NO_SYNC) {
|
||||||
// wait here instead allows more time for a newer frame to arrive
|
m_eglDestroySync(m_EGLDisplay, m_LastRenderSync);
|
||||||
// for next renderFrame() call.
|
}
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
glFinish();
|
// Create a new sync object that will be signalled when the buffer swap is completed
|
||||||
|
if (m_eglCreateSync != nullptr) {
|
||||||
|
m_LastRenderSync = m_eglCreateSync(m_EGLDisplay, EGL_SYNC_FENCE, nullptr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SDL_assert(m_eglCreateSyncKHR != nullptr);
|
||||||
|
m_LastRenderSync = m_eglCreateSyncKHR(m_EGLDisplay, EGL_SYNC_FENCE, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// This glClear() forces us to block until the buffer swap is
|
||||||
|
// complete to continue rendering. Mesa won't actually wait
|
||||||
|
// for the swap with just glFinish() alone. Waiting here keeps us
|
||||||
|
// in lock step with the display refresh rate. If we don't wait
|
||||||
|
// here, we'll stall on the first GL call next frame. Doing the
|
||||||
|
// wait here instead allows more time for a newer frame to arrive
|
||||||
|
// for next renderFrame() call.
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glFinish();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Backend->freeEGLImages(m_EGLDisplay, imgs);
|
m_Backend->freeEGLImages(m_EGLDisplay, imgs);
|
||||||
|
|
|
@ -12,6 +12,7 @@ public:
|
||||||
virtual bool initialize(PDECODER_PARAMETERS params) override;
|
virtual bool initialize(PDECODER_PARAMETERS params) override;
|
||||||
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
|
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
|
||||||
virtual void cleanupRenderContext() override;
|
virtual void cleanupRenderContext() override;
|
||||||
|
virtual void waitToRender() override;
|
||||||
virtual void renderFrame(AVFrame* frame) override;
|
virtual void renderFrame(AVFrame* frame) override;
|
||||||
virtual bool testRenderFrame(AVFrame* frame) override;
|
virtual bool testRenderFrame(AVFrame* frame) override;
|
||||||
virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
|
virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
|
||||||
|
@ -45,11 +46,16 @@ private:
|
||||||
IFFmpegRenderer *m_Backend;
|
IFFmpegRenderer *m_Backend;
|
||||||
unsigned int m_VAO;
|
unsigned int m_VAO;
|
||||||
bool m_BlockingSwapBuffers;
|
bool m_BlockingSwapBuffers;
|
||||||
|
EGLSync m_LastRenderSync;
|
||||||
AVFrame* m_LastFrame;
|
AVFrame* m_LastFrame;
|
||||||
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_glEGLImageTargetTexture2DOES;
|
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC m_glEGLImageTargetTexture2DOES;
|
||||||
PFNGLGENVERTEXARRAYSOESPROC m_glGenVertexArraysOES;
|
PFNGLGENVERTEXARRAYSOESPROC m_glGenVertexArraysOES;
|
||||||
PFNGLBINDVERTEXARRAYOESPROC m_glBindVertexArrayOES;
|
PFNGLBINDVERTEXARRAYOESPROC m_glBindVertexArrayOES;
|
||||||
PFNGLDELETEVERTEXARRAYSOESPROC m_glDeleteVertexArraysOES;
|
PFNGLDELETEVERTEXARRAYSOESPROC m_glDeleteVertexArraysOES;
|
||||||
|
PFNEGLCREATESYNCPROC m_eglCreateSync;
|
||||||
|
PFNEGLCREATESYNCKHRPROC m_eglCreateSyncKHR;
|
||||||
|
PFNEGLDESTROYSYNCPROC m_eglDestroySync;
|
||||||
|
PFNEGLCLIENTWAITSYNCPROC m_eglClientWaitSync;
|
||||||
int m_GlesMajorVersion;
|
int m_GlesMajorVersion;
|
||||||
int m_GlesMinorVersion;
|
int m_GlesMinorVersion;
|
||||||
bool m_HasExtUnpackSubimage;
|
bool m_HasExtUnpackSubimage;
|
||||||
|
|
|
@ -21,9 +21,19 @@ extern "C" {
|
||||||
#ifndef EGL_VERSION_1_5
|
#ifndef EGL_VERSION_1_5
|
||||||
typedef intptr_t EGLAttrib;
|
typedef intptr_t EGLAttrib;
|
||||||
typedef void *EGLImage;
|
typedef void *EGLImage;
|
||||||
|
|
||||||
|
typedef void *EGLSync;
|
||||||
|
#define EGL_NO_SYNC ((EGLSync)0)
|
||||||
|
#define EGL_SYNC_FENCE 0x30F9
|
||||||
|
#define EGL_FOREVER 0xFFFFFFFFFFFFFFFFull
|
||||||
|
#define EGL_SYNC_FLUSH_COMMANDS_BIT 0x0001
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(EGL_VERSION_1_5) || !defined(EGL_EGL_PROTOTYPES)
|
#if !defined(EGL_VERSION_1_5) || !defined(EGL_EGL_PROTOTYPES)
|
||||||
|
typedef EGLSync (EGLAPIENTRYP PFNEGLCREATESYNCPROC) (EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
|
||||||
|
typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYSYNCPROC) (EGLDisplay dpy, EGLSync sync);
|
||||||
|
typedef EGLint (EGLAPIENTRYP PFNEGLCLIENTWAITSYNCPROC) (EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
|
||||||
|
|
||||||
typedef EGLImage (EGLAPIENTRYP PFNEGLCREATEIMAGEPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);
|
typedef EGLImage (EGLAPIENTRYP PFNEGLCREATEIMAGEPROC) (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);
|
||||||
typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEPROC) (EGLDisplay dpy, EGLImage image);
|
typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEPROC) (EGLDisplay dpy, EGLImage image);
|
||||||
typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYPROC) (EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
|
typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYPROC) (EGLenum platform, void *native_display, const EGLAttrib *attrib_list);
|
||||||
|
@ -40,6 +50,10 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGL
|
||||||
typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
|
typedef EGLDisplay (EGLAPIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC) (EGLenum platform, void *native_display, const EGLint *attrib_list);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(EGL_KHR_fence_sync) || !defined(EGL_EGLEXT_PROTOTYPES)
|
||||||
|
typedef EGLSyncKHR (EGLAPIENTRYP PFNEGLCREATESYNCKHRPROC) (EGLDisplay dpy, EGLenum type, const EGLint *attrib_list);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef EGL_EXT_image_dma_buf_import
|
#ifndef EGL_EXT_image_dma_buf_import
|
||||||
#define EGL_LINUX_DMA_BUF_EXT 0x3270
|
#define EGL_LINUX_DMA_BUF_EXT 0x3270
|
||||||
#define EGL_LINUX_DRM_FOURCC_EXT 0x3271
|
#define EGL_LINUX_DRM_FOURCC_EXT 0x3271
|
||||||
|
|
Loading…
Reference in a new issue