Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix macOS memory growth caused by unbounded Metal shader cache accumulation in QtWebEngine compositor
Problem
Forum discussion from over a year ago: https://forums.ankiweb.net/t/memory-leak-over-time/50931/32
On macOS, Anki's memory footprint grows continuously while the application is idle — typically several MB per minute — and never recovers - memory could reach 20 to 30 gigs after a week or so. This behavior was observed via macOS Activity Monitor and confirmed with Instruments.
Initial investigation ruled out the obvious candidates:
tracemallocfound no growing Python allocationsleaksfound no unreachable objects — the memory is reachable, not leaked in the traditional senseQtWebEngineProcesshelper process was stable; growth was entirely in the main Python processRoot Cause (confirmed via Instruments → Allocations + Call Tree)
Every second, Qt's scene graph fires a render-sync timer, which calls into
RenderWidgetHostViewQtDelegateItem— QtWebEngine's internal QML item that composites web content using OpenGL. On each sync it allocates a newQOpenGLFramebufferObjectrather than reusing the existing one.Each new FBO has a different geometry identity, so Apple's OpenGL→Metal translation layer (
GLDContextRec::buildPipelineState) compiles a fresh Metal shader pipeline state for it viaAGXG15XFamilyDevice::newRenderPipelineStateWithDescriptor. The compiled pipeline state is inserted intoMTLCompilerFSCache::getElement— a cache that is reachable and therefore not flagged as a leak, but that grows without bound for the lifetime of the process.Full call chain:
This is a Qt/Chromium interaction bug specific to the macOS OpenGL→Metal compatibility layer; it does not affect Linux or Windows.
Fix
Set
--disable-gpu-compositinginQTWEBENGINE_CHROMIUM_FLAGSon macOS (insetupGL(),qt/aqt/__init__.py). This switches Chromium's page compositor from GPU mode (which creates FBOs on every render sync) to CPU/software mode (a plain bitmap uploaded as a texture). NoQOpenGLFramebufferObjectis created, no Metal pipeline states are compiled, andMTLCompilerFSCachenever grows.The flag is appended to any existing value of
QTWEBENGINE_CHROMIUM_FLAGSrather than overwriting it, so user-set or developer flags (e.g.--remote-allow-origins) are preserved.Confirmation of fix
Anki running for 8 hours:

Trade-off
CPU compositing means Chromium composites page layers on the CPU (Skia) rather than the GPU. Web content renders identically; CSS animations and video still work. For Anki's workload — card text, LaTeX images, simple SVG — this is indistinguishable in practice.
If visible degradation is ever reported, the fallback
--use-angle=swiftshaderprovides a software GPU path instead.