Simplify EGLImageFactory and remove caching logic for now

The platforms that would most benefit (embedded V4L2 decoders)
either don't use frame pooling or don't synchronize with
modified DMA-BUFs unless eglCreateImage() is called each time.
This commit is contained in:
Cameron Gutman 2025-12-28 17:54:31 -06:00
commit 3e5aa9b127
8 changed files with 29 additions and 98 deletions

View file

@ -1497,8 +1497,8 @@ ssize_t DrmRenderer::exportEGLImages(AVFrame *frame, EGLDisplay dpy,
return m_EglImageFactory.exportDRMImages(frame, dpy, images); return m_EglImageFactory.exportDRMImages(frame, dpy, images);
} }
void DrmRenderer::freeEGLImages(EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) { void DrmRenderer::freeEGLImages() {
m_EglImageFactory.freeEGLImages(dpy, images); m_EglImageFactory.freeEGLImages();
} }
#endif #endif

View file

@ -69,7 +69,7 @@ public:
virtual AVPixelFormat getEGLImagePixelFormat() override; virtual AVPixelFormat getEGLImagePixelFormat() override;
virtual bool initializeEGL(EGLDisplay dpy, const EGLExtensions &ext) override; virtual bool initializeEGL(EGLDisplay dpy, const EGLExtensions &ext) override;
virtual ssize_t exportEGLImages(AVFrame *frame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) override; virtual ssize_t exportEGLImages(AVFrame *frame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) override;
virtual void freeEGLImages(EGLDisplay dpy, EGLImage[EGL_MAX_PLANES]) override; virtual void freeEGLImages() override;
#endif #endif
private: private:

View file

@ -26,7 +26,6 @@
EglImageFactory::EglImageFactory(IFFmpegRenderer* renderer) : EglImageFactory::EglImageFactory(IFFmpegRenderer* renderer) :
m_Renderer(renderer), m_Renderer(renderer),
m_CacheDisabled(false),
m_EGLExtDmaBuf(false), m_EGLExtDmaBuf(false),
m_eglCreateImage(nullptr), m_eglCreateImage(nullptr),
m_eglDestroyImage(nullptr), m_eglDestroyImage(nullptr),
@ -70,72 +69,18 @@ bool EglImageFactory::initializeEGL(EGLDisplay,
void EglImageFactory::resetCache() void EglImageFactory::resetCache()
{ {
m_CachedImages.clear(); // Cannot reset cache while in the middle of rendering
} SDL_assert(!m_LastImageCtx.has_value());
ssize_t EglImageFactory::queryImageCache(AVFrame *frame, EGLImage images[EGL_MAX_PLANES])
{
if (m_CacheDisabled) {
return -1;
}
else if (!frame->hw_frames_ctx) {
// If we don't have a AVHWFramesContext, we won't have a frame pool
// which means our caching logic of checking AVBuffer pointers won't
// work properly.
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"EGLImage caching disabled due to missing AVHWFramesContext");
m_CacheDisabled = true;
return -1;
}
else if (!frame->buf[0]) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"EGLImage caching disabled due to missing AVBufferRef");
m_CacheDisabled = true;
return -1;
}
else if (!frame->buf[0]->buffer) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"EGLImage caching disabled due to missing AVBuffer");
m_CacheDisabled = true;
return -1;
}
else if (m_CachedImages.size() >= 20) {
// This is a final fail-safe for the case where the buffers aren't
// actually being reused to avoid the cache size growing forever.
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"EGLImage caching disabled due to excessive cached image count");
m_CacheDisabled = true;
return -1;
}
auto imgCtx = m_CachedImages.find(frame->buf[0]->buffer);
if (imgCtx == m_CachedImages.end()) {
return -1;
}
memcpy(images, imgCtx->second.images, sizeof(EGLImage) * imgCtx->second.count);
return imgCtx->second.count;
}
void EglImageFactory::populateImageCache(AVFrame *frame, EglImageContext &&imgCtx)
{
AVBuffer* cacheKey = m_CacheDisabled ? nullptr : frame->buf[0]->buffer;
// Move this entry into the cache
m_CachedImages.emplace(cacheKey, std::move(imgCtx));
} }
#ifdef HAVE_DRM #ifdef HAVE_DRM
ssize_t EglImageFactory::exportDRMImages(AVFrame* frame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) ssize_t EglImageFactory::exportDRMImages(AVFrame* frame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES])
{ {
memset(images, 0, sizeof(EGLImage) * EGL_MAX_PLANES); // freeEGLImages() must be called before exporting again
SDL_assert(!m_LastImageCtx.has_value());
// Check the cache first
if (ssize_t count = queryImageCache(frame, images); count > 0) {
return count;
}
SDL_assert(frame->format == AV_PIX_FMT_DRM_PRIME);
AVDRMFrameDescriptor* drmFrame = (AVDRMFrameDescriptor*)frame->data[0]; AVDRMFrameDescriptor* drmFrame = (AVDRMFrameDescriptor*)frame->data[0];
// DRM requires composed layers rather than separate layers per plane // DRM requires composed layers rather than separate layers per plane
@ -312,8 +257,8 @@ ssize_t EglImageFactory::exportDRMImages(AVFrame* frame, EGLDisplay dpy, EGLImag
// Copy the output from the image context before we move it // Copy the output from the image context before we move it
images[0] = imgCtx.images[0]; images[0] = imgCtx.images[0];
// Move this image context into the cache // Store this image context
populateImageCache(frame, std::move(imgCtx)); m_LastImageCtx.emplace(std::move(imgCtx));
return 1; return 1;
} }
@ -324,8 +269,10 @@ ssize_t EglImageFactory::exportDRMImages(AVFrame* frame, EGLDisplay dpy, EGLImag
ssize_t EglImageFactory::exportVAImages(AVFrame *frame, uint32_t exportFlags, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) ssize_t EglImageFactory::exportVAImages(AVFrame *frame, uint32_t exportFlags, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES])
{ {
memset(images, 0, sizeof(EGLImage) * EGL_MAX_PLANES); // freeEGLImages() must be called before exporting again
SDL_assert(!m_LastImageCtx.has_value());
SDL_assert(frame->format == AV_PIX_FMT_VAAPI);
auto hwFrameCtx = (AVHWFramesContext*)frame->hw_frames_ctx->data; auto hwFrameCtx = (AVHWFramesContext*)frame->hw_frames_ctx->data;
AVVAAPIDeviceContext* vaDeviceContext = (AVVAAPIDeviceContext*)hwFrameCtx->device_ctx->hwctx; AVVAAPIDeviceContext* vaDeviceContext = (AVVAAPIDeviceContext*)hwFrameCtx->device_ctx->hwctx;
VASurfaceID surface_id = (VASurfaceID)(uintptr_t)frame->data[3]; VASurfaceID surface_id = (VASurfaceID)(uintptr_t)frame->data[3];
@ -338,11 +285,6 @@ ssize_t EglImageFactory::exportVAImages(AVFrame *frame, uint32_t exportFlags, EG
return -1; return -1;
} }
// Check the cache first
if (ssize_t count = queryImageCache(frame, images); count > 0) {
return count;
}
EglImageContext imgCtx(dpy, m_eglDestroyImage, m_eglDestroyImageKHR); EglImageContext imgCtx(dpy, m_eglDestroyImage, m_eglDestroyImageKHR);
VADRMPRIMESurfaceDescriptor vaFrame; VADRMPRIMESurfaceDescriptor vaFrame;
@ -544,8 +486,8 @@ ssize_t EglImageFactory::exportVAImages(AVFrame *frame, uint32_t exportFlags, EG
ssize_t count = imgCtx.count; ssize_t count = imgCtx.count;
memcpy(images, imgCtx.images, sizeof(EGLImage) * imgCtx.count); memcpy(images, imgCtx.images, sizeof(EGLImage) * imgCtx.count);
// Move this image context into the cache // Store this image context
populateImageCache(frame, std::move(imgCtx)); m_LastImageCtx.emplace(std::move(imgCtx));
return count; return count;
} }
@ -633,13 +575,7 @@ bool EglImageFactory::supportsImportingModifier(EGLDisplay dpy, EGLint format, E
#endif #endif
void EglImageFactory::freeEGLImages(EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) { void EglImageFactory::freeEGLImages() {
Q_UNUSED(dpy); SDL_assert(m_LastImageCtx.has_value());
Q_UNUSED(images); m_LastImageCtx.reset();
// When the cache is disabled, we just insert one element at a time
// with key of nullptr and clear it after every frame.
if (m_CacheDisabled) {
m_CachedImages.clear();
}
} }

View file

@ -2,12 +2,12 @@
#include "renderer.h" #include "renderer.h"
#include <unordered_map>
#ifdef HAVE_LIBVA #ifdef HAVE_LIBVA
#include <va/va_drmcommon.h> #include <va/va_drmcommon.h>
#endif #endif
#include <optional>
class EglImageFactory class EglImageFactory
{ {
class EglImageContext { class EglImageContext {
@ -64,19 +64,14 @@ public:
ssize_t exportVAImages(AVFrame* frame, uint32_t exportFlags, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]); ssize_t exportVAImages(AVFrame* frame, uint32_t exportFlags, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]);
#endif #endif
void freeEGLImages();
bool supportsImportingFormat(EGLDisplay dpy, EGLint format); bool supportsImportingFormat(EGLDisplay dpy, EGLint format);
bool supportsImportingModifier(EGLDisplay dpy, EGLint format, EGLuint64KHR modifier); bool supportsImportingModifier(EGLDisplay dpy, EGLint format, EGLuint64KHR modifier);
void freeEGLImages(EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]);
private:
ssize_t queryImageCache(AVFrame* frame, EGLImage images[EGL_MAX_PLANES]);
void populateImageCache(AVFrame* frame, EglImageContext &&imgCtx);
private: private:
IFFmpegRenderer* m_Renderer; IFFmpegRenderer* m_Renderer;
bool m_CacheDisabled; std::optional<EglImageContext> m_LastImageCtx;
std::unordered_map<AVBuffer*, EglImageContext> m_CachedImages;
bool m_EGLExtDmaBuf; bool m_EGLExtDmaBuf;
PFNEGLCREATEIMAGEPROC m_eglCreateImage; PFNEGLCREATEIMAGEPROC m_eglCreateImage;
PFNEGLDESTROYIMAGEPROC m_eglDestroyImage; PFNEGLDESTROYIMAGEPROC m_eglDestroyImage;

View file

@ -874,7 +874,7 @@ void EGLRenderer::renderFrame(AVFrame* frame)
} }
} }
m_Backend->freeEGLImages(m_EGLDisplay, imgs); m_Backend->freeEGLImages();
// Free the DMA-BUF backing the last frame now that it is definitely // Free the DMA-BUF backing the last frame now that it is definitely
// no longer being used anymore. While the PRIME FD stays around until // no longer being used anymore. While the PRIME FD stays around until
@ -899,6 +899,6 @@ bool EGLRenderer::testRenderFrame(AVFrame* frame)
return false; return false;
} }
m_Backend->freeEGLImages(m_EGLDisplay, imgs); m_Backend->freeEGLImages();
return true; return true;
} }

View file

@ -502,7 +502,7 @@ public:
} }
// Free the resources allocated during the last `exportEGLImages` call // Free the resources allocated during the last `exportEGLImages` call
virtual void freeEGLImages(EGLDisplay, EGLImage[EGL_MAX_PLANES]) {} virtual void freeEGLImages() {}
#endif #endif
#ifdef HAVE_DRM #ifdef HAVE_DRM

View file

@ -1162,8 +1162,8 @@ VAAPIRenderer::exportEGLImages(AVFrame *frame, EGLDisplay dpy,
} }
void void
VAAPIRenderer::freeEGLImages(EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) { VAAPIRenderer::freeEGLImages() {
m_EglImageFactory.freeEGLImages(dpy, images); m_EglImageFactory.freeEGLImages();
} }
#endif #endif

View file

@ -73,7 +73,7 @@ public:
virtual AVPixelFormat getEGLImagePixelFormat() override; virtual AVPixelFormat getEGLImagePixelFormat() override;
virtual bool initializeEGL(EGLDisplay dpy, const EGLExtensions &ext) override; virtual bool initializeEGL(EGLDisplay dpy, const EGLExtensions &ext) override;
virtual ssize_t exportEGLImages(AVFrame *frame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) override; virtual ssize_t exportEGLImages(AVFrame *frame, EGLDisplay dpy, EGLImage images[EGL_MAX_PLANES]) override;
virtual void freeEGLImages(EGLDisplay dpy, EGLImage[EGL_MAX_PLANES]) override; virtual void freeEGLImages() override;
#endif #endif
#ifdef HAVE_DRM #ifdef HAVE_DRM