Skip to content

Add a global mutex to serialize PAGDecoder frame rendering across shared EGL contexts.#3565

Open
leiyue123 wants to merge 1 commit into
mainfrom
bugfix/thunderllei_decoder_render
Open

Add a global mutex to serialize PAGDecoder frame rendering across shared EGL contexts.#3565
leiyue123 wants to merge 1 commit into
mainfrom
bugfix/thunderllei_decoder_render

Conversation

@leiyue123

@leiyue123 leiyue123 commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

问题

使用多个 PAGImageView 同时播放时,首次渲染(无缓存)会出现帧错乱。例如视图 A 渲染出视图 B 的某一帧,且错误帧被写入磁盘缓存后重启也无法恢复。

低端设备(小米 Note8 Pro、华为 P20)偶现,详见 #3540

根因

多个 PAGDecoder 在创建时通过 GLDevice::CurrentNativeHandle() 获取到同一个 EGLContext 作为 sharedContext。各自的 BitmapDrawable 又通过 GLDevice::Make(sharedContext) 创建了共享同一 EGL display 和 GL 对象命名空间的新 EGLContext。

在 PAGAnimator 异步渲染模式下(_isSync = false),tgfx::Task::Run() 将不同 PAGImageView 的 onAnimationUpdate 回调分发到线程池的不同线程上执行,导致多个 Decoder 的 renderFrame() 在不同线程上并发执行 GL 命令。低端设备的 GPU 驱动无法正确处理共享 EGL 上下文的并发访问,导致渲染目标混乱。

修复

PAGDecoder::readFrameInternal() 中新增静态全局互斥锁 renderLocker,包裹 renderFrame()writeFrame() 调用。缓存命中时不进入锁区,仅首次 GPU 渲染时串行化,确保不同线程的 GL 命令不会交错执行。

改动量:3 行代码,无公开 API 变更。

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.32%. Comparing base (c7eee2d) to head (9280d56).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3565   +/-   ##
=======================================
  Coverage   81.32%   81.32%           
=======================================
  Files         653      653           
  Lines       77180    77181    +1     
  Branches    21913    21914    +1     
=======================================
+ Hits        62763    62765    +2     
+ Misses       9983     9980    -3     
- Partials     4434     4436    +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.


namespace pag {

static std::mutex renderLocker = {};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

建议从根本上解决而非打补丁规避。真正的根因在于渲染引擎(tgfx)层:多个 PAGDecoder 各自 GLDevice::Make(sharedContext) 创建的 EGLContext 共享同一命名空间,缺乏对并发 GL 访问的保护,低端驱动才会错乱。

在 PAGDecoder 层加一把全局锁只是把并发压平为串行来规避问题,存在两个固有缺陷:

  1. 进程内所有 decoder 的首帧渲染被完全串行化,即使分属不相干的 context/业务,多视图首屏耗时线性叠加;
  2. 无法覆盖其他同样使用 GLDevice::Make(currentContext) 的并发 GL 路径(如 StillImage 等),本 PR 也未保护 reader 销毁(reader = nullptr)时的 GL 对象释放——它在锁作用域之外,仍与其他 decoder 的 renderFrame 并发。

根治方向:在 tgfx GLDevice 层对共享命名空间的并发访问做 per-context 锁 + makeCurrent 纪律,从渲染引擎层根除,让所有上层调用方自动受益,而非在每个调用点各自打补丁。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants