You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
can you checkout fix/434-gpu-display-texture-uaf branch to verify if it fixes it? I don't currently have macbook on hand, only linux and there there is no issue with threading
I can't reproduce the bug in the 0.35.0 release build, so I'm not sure I can effectively test the fix. Am I not understanding the repro steps? I launched NegPy, then drug a tiff file (also tried a nef) from the finder into the left sidebar of NegPy. On my machine running macOS 26.5.1, those files loaded without issue.
Happy to try testing the fix if someone can confirm how to reproduce the issue in the first place…
I poked around a bit more and I got a tiff to open, but it crashed with a similar segfault when I started dragging around the Analysis Buffer slider.
I downgraded to 0.34 and things seemed to work much better. I edited an image and then clicked the export button and got an identical crash...
Thread 0x000000016ede7000 (most recent call first):
File "threading.py", line 359 in wait
File "threading.py", line 660 in wait
File "wgpu/backends/wgpu_native/_poller.py", line 101 in run
File "threading.py", line 1044 in _bootstrap_inner
File "threading.py", line 1015 in _bootstrap
Current thread 0x00000001f9d7de80 (most recent call first):
File "negpy/desktop/session.py", line 225 in refresh
File "negpy/desktop/controller.py", line 325 in _on_thumbnails_finished
File "negpy/desktop/main.py", line 115 in main
File "desktop.py", line 29 in <module>
Extension modules: PyQt6.QtCore, PyQt6.QtGui, PyQt6.QtWidgets, numpy._core._multiarray_umath, numpy.linalg._umath_linalg, PIL._imaging, PIL._imagingcms, rawpy._rawpy, numba.core.typeconv._typeconv, numpy.random._common, numpy.random.bit_generator, numpy.random._bounded_integers, numpy.random._pcg64, numpy.random._generator, numpy.random._mt19937, numpy.random._philox, numpy.random._sfc64, numpy.random.mtrand, numba._helperlib, numba._dynfunc, numba._dispatcher, numba.core.runtime._nrt_python, numba.np.ufunc._internal, numba.experimental.jitclass._box, markupsafe._speedups, _cffi_backend, PyQt6.QtOpenGL, PyQt6.QtOpenGLWidgets, PIL._imagingmath (total: 29)
Segmentation fault: 11 /Applications/NegPy.app/Contents/MacOS/NegPy
Looks like a crash on GPU rendering. Turn off GPU rendering (the lightning bolt icon at bottom toolbar) and switch to CPU rendering does the crash still happen when doing the same action?
Recommended action from Gemini based on console error:
Your script crashed due to a Segmentation fault in the WebGPU polling thread, caused by a conflict between Python 3.13, your wgpu-native library, and the Apple M2 Max GPU driver.
Update your backend dependencies. Install the latest version of the wgpu PyPI Package to ensure compatibility with Apple silicon.
RenderWorker.cleanup() (negpy/desktop/workers/render.py:116-118) unconditionally calls ImageProcessor.cleanup(), which unconditionally calls self.engine_gpu.cleanup(collect=collect) whenever self.engine_gpu exists (negpy/services/rendering/image_processor.py:779-780) — it never checks prefer_gpu / the user's GPU toggle. That toggle only gates which backend processes pixels for a given call; it doesn't tear down or bypass GPUEngine, the underlying wgpu.GPUDevice singleton (negpy/infrastructure/gpu/device.py), or wgpu-native's own background poller thread — all of which stay alive for the app's lifetime once initialized, regardless of the toggle.
That poller thread shows up in every crash trace collected so far, always blocked in threading.Event.wait() inside wgpu/backends/wgpu_native/_poller.py:101. It's a native background thread that polls the device for GPU work completion.
So on every file load/switch, GPUEngine.cleanup() runs tex.destroy() on the pooled wgpu/Metal textures from a worker thread — while wgpu-native's own poller thread is concurrently alive and touching the same device from a different thread, with nothing synchronizing the two. That's consistent with genuine cross-thread Metal/wgpu-native resource-destruction corruption, which would explain:
why the crash trace stays identical across totally different user actions (load, reselect, export) — they all trigger a file/task switch, which all hit this same unsynchronized cleanup path;
why downgrading wgpu (0.35 → 0.34) didn't fix it, just shifted when enough concurrent activity piled up to trigger the corruption — consistent with a timing-sensitive race rather than a version-specific regression;
why it still happens with GPU rendering disabled — the toggle never stops the underlying device/cleanup machinery from running.
wgpu-py contributor here,
For multithreading I am not sure what wgpu guarantees, I am sorta surprised it works at all. Regarding the whole instance cleanup - that's definitely not intended so it will likely break in the future. If you file an issue and explain the requirement perhaps I find some time to look into it, as I also have a usecase for trying shaders in parallel and letting them crash gracefully.
Possible new data point for this: I hit what looks like the same underlying race, but escalating further than the segfault reported above.
Environment: NegPy 0.51.0 (packaged .app), macOS 15.7.9 (24G830), MacBook Pro 18,1 / M1 Pro / 16 GB unified memory, Metal backend.
On a normal render session (switching between a couple of loaded TIFFs), NegPy's own logging went completely silent mid-session — no exception, no traceback, nothing — while macOS's runningboardd kept reporting the process as running-active for another ~15 minutes. No .crash/.ips report was ever generated for NegPy. The machine then required a hardware-forced restart (power button held) to recover — confirmed by a macOS kernel panic log with panic string btn_rst, timestamped seconds before the app relaunched. System-wide unified logging (kernel, WiFi, Bluetooth, daemons) kept running normally the whole time, which rules out a full kernel lockup and points at a wedged GPU/display command queue specifically — WindowServer shares the GPU with app submissions, so one app jamming that queue can freeze the whole screen while background kernel work continues underneath.
Interesting overlap: I'd downloaded and loaded lillies.tif (the repro file from this thread) on this same machine three weeks earlier, and loaded it again right after recovering from this freeze — so this is the same repro case, on a machine that (per the fix branch's own module docstring) has the not-yet-verified-safe cpu_parallel setting available, though I've since confirmed that setting wasn't the cause here (a Numba abort kills a process in under a second and wouldn't need a hardware reset to recover from).
Full evidence/timeline write-up (log excerpts, panic log, ruled-out alternate theory) is here if useful: thetalkingdrum#4
Given Vipitis's point above that wgpu-native doesn't guarantee safety for concurrent device access from the cleanup path and the poller thread — on unlucky timing that same unsynchronized destroy could plausibly corrupt enough Metal/driver state to wedge the whole GPU queue rather than just segfault the Python process. Flagging in case it's useful signal for prioritizing/verifying fix/434-gpu-display-texture-uaf.
Full write-up behind my data point above (moving it here instead of keeping it on a fork issue, since that's not visible from this thread):
Summary
On 2026-08-15 around 23:18–23:35 local time, NegPy (packaged .app, v0.51.0) was open and being used to compare two loaded negatives. The whole machine — not just NegPy — stopped responding to any input and had to be recovered with a forced hardware restart (power button held). Confirmed by a macOS kernel panic log with panic string btn_rst (the panic macOS logs specifically for a user-forced restart), timestamped 9 seconds before the next NegPy session booted.
Environment
NegPy 0.51.0, packaged /Applications/NegPy.app (ad-hoc signed), not a dev run
macOS 15.7.9 (24G830)
MacBook Pro 18,1 (M1 Pro, 16 GB unified memory)
GPU backend: Apple M1 Pro (IntegratedGPU) via Metal (rendering.backend = "metal" in override.toml)
override.toml had [performance] cpu_parallel = true — see "Ruled out" below
Timeline (from ~/Documents/NegPy/negpy.log and macOS's unified log, cross-referenced by PID)
Time
Event
23:18:23
New NegPy instance launches (app<...>:PID 8155). CPU parallel kernels: enabled (override=True, platform=darwin)
23:18:32–23:18:36
Loads/renders two files fine: c2025-04-13-0002.tif and Kodak Gold 200_Frame028_R_linear.tiff (last successful first_render logged at 23:18:36)
23:18:36
Last line NegPy's Python logger ever writes for this session. No exception, no traceback, nothing.
23:18:36 – 23:33:55
negpy.log: total silence. macOS's runningboardd, however, keeps reporting PID 8155 as running-active the whole time — the process is alive at the OS level, just producing no application-level activity. Last such state update: 23:33:55.610.
23:25:00 – 23:34:59
System-wide unified log stays busy the entire time — thousands of lines/minute from kernel, WiFi, Bluetooth, mDNSResponder, etc. (checked explicitly to rule out a full kernel lockup — see "Ruled out" below).
23:35:14
Kernel panic, panic(cpu 1 caller ...): btn_rst — logged to /Library/Logs/DiagnosticReports/forceReset-full-2026-08-15-233514.0002.diag. This is macOS's panic string for a user-forced hardware restart (power button held), not an automatic fault/watchdog panic.
23:35:18
New NegPy launch request (likely auto-relaunch via macOS's "reopen windows on login" / PersistentAppsSupport, seen in loginwindow logs)
23:35:23
Fresh NegPy session boots, negpy.log resumes
23:35:57
WARNING desktop.main: CPU parallel kernels disabled after an unclean shutdown — the app's own self-check (main.py:_offer_to_disable_cpu_parallel) correctly detected the prior session never called clean_shutdown, and I accepted the dialog offering to turn cpu_parallel off
No .ips/.crash report for NegPy itself exists anywhere in ~/Library/Logs/DiagnosticReports or /Library/Logs/DiagnosticReports for this window — only the system-level forceReset-full / ResetCounter pair from the forced restart.
What was loaded
The file most likely in view when things went silent: Kodak Gold 200_Frame028_R_linear.tiff, 216 MB, 7968×5320, 16-bit RGB, Adobe-deflate compressed, unusually small strips (RowsPerStrip=5, 1064 strips), source SONY ILCE-7RM2. It had loaded successfully many times before and after this incident (both in this same session and the recovery session right after), so the file by itself isn't reliably reproducing a hang — something about the sequence of actions around 23:18:36 is implicated, not just "open this file."
Ruled out: Numba CPU-parallel abort
negpy/kernel/system/parallel.py documents a known failure mode: Numba's workqueue threading layer hard-aborts the process with no Python exception and nothing written to the log if two threads enter a parallel=True kernel concurrently. override.toml had cpu_parallel = true forced on macOS, despite the code's own comment that this is unverified there ("Defaults to true except on macOS, where it defaults to false while crash reports are investigated"). The silent-log symptom initially looked like a strong match.
Ruled out because:
That abort kills a single process almost immediately — it would not explain 15 minutes of the process still showing as running-active to runningboardd, and would not require a hardware forced restart to recover from.
The system-wide unified log (kernel, WiFi, Bluetooth, daemons) kept logging normally through the entire silent window, which is inconsistent with a process-fatal abort but consistent with "one process's screen output froze while the rest of the OS kept running."
I've since turned cpu_parallel off in override.toml (real, separate, documented risk per the module's own comments), but it's very unlikely to be the cause of this incident.
Leading theory: GPU/display-pipeline wedge (unconfirmed)
The pattern — one process's window stops updating/responding while background kernel and daemon activity continues normally, ending in a state only a forced hardware restart recovers from, with no automatic GPU-watchdog panic ever firing on its own — matches a wedged Metal/AGX GPU command queue. macOS's WindowServer shares the GPU with every app's Metal/WebGPU submissions; if one app's GPU work jams that shared queue, the whole screen can lock solid (no cursor movement, Force Quit unreachable) while unrelated kernel subsystems (which don't touch the GPU) keep running fine underneath — exactly what the per-minute unified-log activity shows.
NegPy's GPUEngine (negpy/services/rendering/gpu_engine.py, WGSL via wgpu) was the last thing doing GPU work before the silence. Candidate contributing factors, none confirmed:
The image is ~127 megapixels; the pipeline runs a dozen-plus GPU compute stages per render, each potentially allocating a full-resolution intermediate buffer.
The machine has 16 GB unified memory, shared between CPU and GPU — a large enough working set across pipeline stages could hit real memory pressure, which is one known trigger class for Apple Silicon GPU driver wedges.
The file's unusual strip layout (RowsPerStrip=5, 1064 strips) is not obviously GPU-relevant (that's a CPU-side TIFF decode detail), but is flagged here in case it correlates with something on the decode → upload path.
No automatic GPU-watchdog panic preceded the forced one, so macOS's own driver-level recovery never engaged — meaning either the wedge was too deep for that mechanism, or the freeze wasn't purely a self-contained GPU-driver bug at all. I don't have a way to distinguish those from the outside.
Given the cross-thread cleanup/poller race discussed above in this thread (confirmed by Vipitis as unsupported by wgpu-native), that same unsynchronized tex.destroy() racing the poller thread is my best guess for what's actually behind this — just landing as driver-state corruption severe enough to wedge the GPU queue instead of a clean segfault.
Open questions
Can this be reproduced deliberately with a large (>100 MP) source and heavy GPU-stage usage (e.g. defect repair / textural analysis, which the log shows was active — textural=True — on this file shortly before other loads that session)?
Does GPUEngine's per-stage buffer lifecycle release intermediate textures between stages, or could VRAM/unified-memory usage accumulate across repeated loads of a huge file within one session?
Is there any bound today on max_texture_size for a file this large, or does "auto" let a 7968×5320 image go through uncapped?
Worth adding a client-side watchdog (e.g. a heartbeat timer that detects the GPU submission queue not returning within N seconds) so a wedge surfaces as a recoverable error dialog instead of a silent full-system freeze?
Follow-up on my report above: I dug into the raw kernel panic snapshot and it captured NegPy's full process/thread state at the exact moment of the panic. Worth sharing since it sharpens the theory:
All 31 of NegPy's threads were parked in TH_WAIT — none running, none runnable. That's a deadlock signature, not a busy spin loop or a stuck-but-progressing compute shader. One thread (tid 459933) had accumulated far more CPU time (23.1s user + 5.7s system) than any other before going idle, consistent with it being the render/GPU-submission thread that was doing the actual work right before everything stopped. Every thread's lastRunTime clusters in the same ~15-17s-before-panic window — the whole process went quiet at essentially the same moment, not a slow cascade.
That fits the cross-thread GPUEngine.cleanup() / wgpu-native poller race discussed above better than a pure driver-corruption theory: if the destroy path and the poller end up mutually blocked on some internal wgpu-native/Metal lock, every thread touching that device (all of NegPy's QThread workers) would end up parked exactly like this.
Redacted raw evidence, if useful for cross-checking: https://gist.github.com/thetalkingdrum/63400a69a93bec3e3d7989669c31fb8c (log excerpt, the config at crash time, and a curated excerpt of the panic report — full raw .diag available on request, left out here since it's 2.6MB of mostly boilerplate kext-version listing).
Small caveat on my reports above, before it misleads anyone: I think I jumped to the wgpu race too fast.
Checked macOS's disk-space telemetry and my boot volume was down to ~300MB free at 11:26 and ~3.8GB at 23:33 — both right before my two forced restarts that day (turns out there were two, same btn_rst panic, not just the one I originally reported — found the second one, at 11:31, while digging further). A near-full disk can freeze a whole Mac solid on its own, no GPU bug needed — swap can't grow, background daemons can't write temp files, everything backs up.
That also undercuts my "all 31 threads in TH_WAIT" finding — a disk/swap stall looks exactly the same (everything blocked, nothing running), so it doesn't actually point at the wgpu race specifically. My bad for reading it that way.
Not saying the race isn't real — Vipitis confirmed it independently, and it's presumably still what's causing the clean segfaults others are hitting. Just that my particular freeze might be a "my disk was basically full" problem rather than this one. Clearing space and keeping an eye on it before I assume otherwise.
Activity
marcinz606 commentedon Jul 10, 2026
I think it might be macos-specific regression from multithreading changes in 0.35.0
marcinz606 commentedon Jul 10, 2026
can you checkout
fix/434-gpu-display-texture-uafbranch to verify if it fixes it? I don't currently have macbook on hand, only linux and there there is no issue with threadingseanharding commentedon Jul 10, 2026
I can't reproduce the bug in the 0.35.0 release build, so I'm not sure I can effectively test the fix. Am I not understanding the repro steps? I launched NegPy, then drug a tiff file (also tried a nef) from the finder into the left sidebar of NegPy. On my machine running macOS 26.5.1, those files loaded without issue.
Happy to try testing the fix if someone can confirm how to reproduce the issue in the first place…
InvisibleFunction commentedon Jul 11, 2026
I poked around a bit more and I got a tiff to open, but it crashed with a similar segfault when I started dragging around the Analysis Buffer slider.
I downgraded to 0.34 and things seemed to work much better. I edited an image and then clicked the export button and got an identical crash...
InvisibleFunction commentedon Jul 11, 2026
I tried running from your branch, let me know if this is correct:
linkmodo commentedon Jul 11, 2026
Looks like a crash on GPU rendering. Turn off GPU rendering (the lightning bolt icon at bottom toolbar) and switch to CPU rendering does the crash still happen when doing the same action?
Recommended action from Gemini based on console error:
InvisibleFunction commentedon Jul 11, 2026
I unclicked the lightning bolt icon and I still got the same crash. I'll try to update to the latest mac point release.
Looks like the latest.
marcinz606 commentedon Jul 11, 2026
If it also happens on 0.34.0 then 0.35.0 multithreading changes are not the culprit, interesting
InvisibleFunction commentedon Jul 11, 2026
Here's what claude thinks:
Vipitis commentedon Jul 12, 2026
wgpu-py contributor here,
For multithreading I am not sure what wgpu guarantees, I am sorta surprised it works at all. Regarding the whole instance cleanup - that's definitely not intended so it will likely break in the future. If you file an issue and explain the requirement perhaps I find some time to look into it, as I also have a usecase for trying shaders in parallel and letting them crash gracefully.
InvisibleFunction commentedon Jul 12, 2026
I just tested with the same tiffs on windows and it worked fine if that’s an interesting datapoint.
marcinz606 commentedon Jul 12, 2026
Can you upload that tiff here? I cannot replicate the crash on linux or macos using my files
InvisibleFunction commentedon Jul 12, 2026
https://have-some-files.s3.amazonaws.com/lillies.tif
I tried it on a Windows machine and it seemed to work fine.
thetalkingdrum commentedon Aug 15, 2026
Possible new data point for this: I hit what looks like the same underlying race, but escalating further than the segfault reported above.
Environment: NegPy 0.51.0 (packaged .app), macOS 15.7.9 (24G830), MacBook Pro 18,1 / M1 Pro / 16 GB unified memory, Metal backend.
On a normal render session (switching between a couple of loaded TIFFs), NegPy's own logging went completely silent mid-session — no exception, no traceback, nothing — while macOS's
runningboarddkept reporting the process asrunning-activefor another ~15 minutes. No.crash/.ipsreport was ever generated for NegPy. The machine then required a hardware-forced restart (power button held) to recover — confirmed by a macOS kernel panic log with panic stringbtn_rst, timestamped seconds before the app relaunched. System-wide unified logging (kernel, WiFi, Bluetooth, daemons) kept running normally the whole time, which rules out a full kernel lockup and points at a wedged GPU/display command queue specifically — WindowServer shares the GPU with app submissions, so one app jamming that queue can freeze the whole screen while background kernel work continues underneath.Interesting overlap: I'd downloaded and loaded
lillies.tif(the repro file from this thread) on this same machine three weeks earlier, and loaded it again right after recovering from this freeze — so this is the same repro case, on a machine that (per the fix branch's own module docstring) has the not-yet-verified-safecpu_parallelsetting available, though I've since confirmed that setting wasn't the cause here (a Numba abort kills a process in under a second and wouldn't need a hardware reset to recover from).Full evidence/timeline write-up (log excerpts, panic log, ruled-out alternate theory) is here if useful: thetalkingdrum#4
Given
Vipitis's point above that wgpu-native doesn't guarantee safety for concurrent device access from the cleanup path and the poller thread — on unlucky timing that same unsynchronized destroy could plausibly corrupt enough Metal/driver state to wedge the whole GPU queue rather than just segfault the Python process. Flagging in case it's useful signal for prioritizing/verifyingfix/434-gpu-display-texture-uaf.thetalkingdrum commentedon Aug 16, 2026
Full write-up behind my data point above (moving it here instead of keeping it on a fork issue, since that's not visible from this thread):
Summary
On 2026-08-15 around 23:18–23:35 local time, NegPy (packaged
.app, v0.51.0) was open and being used to compare two loaded negatives. The whole machine — not just NegPy — stopped responding to any input and had to be recovered with a forced hardware restart (power button held). Confirmed by a macOS kernel panic log with panic stringbtn_rst(the panic macOS logs specifically for a user-forced restart), timestamped 9 seconds before the next NegPy session booted.Environment
/Applications/NegPy.app(ad-hoc signed), not a dev runrendering.backend = "metal"in override.toml)override.tomlhad[performance] cpu_parallel = true— see "Ruled out" belowTimeline (from
~/Documents/NegPy/negpy.logand macOS's unified log, cross-referenced by PID)CPU parallel kernels: enabled (override=True, platform=darwin)c2025-04-13-0002.tifandKodak Gold 200_Frame028_R_linear.tiff(last successfulfirst_renderlogged at 23:18:36)negpy.log: total silence. macOS'srunningboardd, however, keeps reporting PID 8155 asrunning-activethe whole time — the process is alive at the OS level, just producing no application-level activity. Last such state update: 23:33:55.610.panic(cpu 1 caller ...): btn_rst— logged to/Library/Logs/DiagnosticReports/forceReset-full-2026-08-15-233514.0002.diag. This is macOS's panic string for a user-forced hardware restart (power button held), not an automatic fault/watchdog panic.PersistentAppsSupport, seen inloginwindowlogs)negpy.logresumesWARNING desktop.main: CPU parallel kernels disabled after an unclean shutdown— the app's own self-check (main.py:_offer_to_disable_cpu_parallel) correctly detected the prior session never calledclean_shutdown, and I accepted the dialog offering to turncpu_paralleloffNo
.ips/.crashreport for NegPy itself exists anywhere in~/Library/Logs/DiagnosticReportsor/Library/Logs/DiagnosticReportsfor this window — only the system-levelforceReset-full/ResetCounterpair from the forced restart.What was loaded
The file most likely in view when things went silent:
Kodak Gold 200_Frame028_R_linear.tiff, 216 MB, 7968×5320, 16-bit RGB, Adobe-deflate compressed, unusually small strips (RowsPerStrip=5, 1064 strips), source SONY ILCE-7RM2. It had loaded successfully many times before and after this incident (both in this same session and the recovery session right after), so the file by itself isn't reliably reproducing a hang — something about the sequence of actions around 23:18:36 is implicated, not just "open this file."Ruled out: Numba CPU-parallel abort
negpy/kernel/system/parallel.pydocuments a known failure mode: Numba'sworkqueuethreading layer hard-aborts the process with no Python exception and nothing written to the log if two threads enter aparallel=Truekernel concurrently.override.tomlhadcpu_parallel = trueforced on macOS, despite the code's own comment that this is unverified there ("Defaults to true except on macOS, where it defaults to false while crash reports are investigated"). The silent-log symptom initially looked like a strong match.Ruled out because:
running-activetorunningboardd, and would not require a hardware forced restart to recover from.I've since turned
cpu_paralleloff inoverride.toml(real, separate, documented risk per the module's own comments), but it's very unlikely to be the cause of this incident.Leading theory: GPU/display-pipeline wedge (unconfirmed)
The pattern — one process's window stops updating/responding while background kernel and daemon activity continues normally, ending in a state only a forced hardware restart recovers from, with no automatic GPU-watchdog panic ever firing on its own — matches a wedged Metal/AGX GPU command queue. macOS's WindowServer shares the GPU with every app's Metal/WebGPU submissions; if one app's GPU work jams that shared queue, the whole screen can lock solid (no cursor movement, Force Quit unreachable) while unrelated kernel subsystems (which don't touch the GPU) keep running fine underneath — exactly what the per-minute unified-log activity shows.
NegPy's
GPUEngine(negpy/services/rendering/gpu_engine.py, WGSL viawgpu) was the last thing doing GPU work before the silence. Candidate contributing factors, none confirmed:RowsPerStrip=5, 1064 strips) is not obviously GPU-relevant (that's a CPU-side TIFF decode detail), but is flagged here in case it correlates with something on the decode → upload path.No automatic GPU-watchdog panic preceded the forced one, so macOS's own driver-level recovery never engaged — meaning either the wedge was too deep for that mechanism, or the freeze wasn't purely a self-contained GPU-driver bug at all. I don't have a way to distinguish those from the outside.
Given the cross-thread cleanup/poller race discussed above in this thread (confirmed by
Vipitisas unsupported by wgpu-native), that same unsynchronizedtex.destroy()racing the poller thread is my best guess for what's actually behind this — just landing as driver-state corruption severe enough to wedge the GPU queue instead of a clean segfault.Open questions
textural=True— on this file shortly before other loads that session)?GPUEngine's per-stage buffer lifecycle release intermediate textures between stages, or could VRAM/unified-memory usage accumulate across repeated loads of a huge file within one session?max_texture_sizefor a file this large, or does"auto"let a 7968×5320 image go through uncapped?Evidence sources
~/Documents/NegPy/negpy.log(session logging, self-diagnostic messages)~/Documents/NegPy/override.toml(thecpu_parallel = truesetting)log show), filtered by PID and by system-wide per-minute activity/Library/Logs/DiagnosticReports/forceReset-full-2026-08-15-233514.0002.diagand the pairedResetCounter-2026-08-15-233521.diagtifffileinspection of the source TIFFthetalkingdrum commentedon Aug 16, 2026
Follow-up on my report above: I dug into the raw kernel panic snapshot and it captured NegPy's full process/thread state at the exact moment of the panic. Worth sharing since it sharpens the theory:
All 31 of NegPy's threads were parked in
TH_WAIT— none running, none runnable. That's a deadlock signature, not a busy spin loop or a stuck-but-progressing compute shader. One thread (tid 459933) had accumulated far more CPU time (23.1s user + 5.7s system) than any other before going idle, consistent with it being the render/GPU-submission thread that was doing the actual work right before everything stopped. Every thread'slastRunTimeclusters in the same ~15-17s-before-panic window — the whole process went quiet at essentially the same moment, not a slow cascade.That fits the cross-thread
GPUEngine.cleanup()/ wgpu-native poller race discussed above better than a pure driver-corruption theory: if the destroy path and the poller end up mutually blocked on some internal wgpu-native/Metal lock, every thread touching that device (all of NegPy's QThread workers) would end up parked exactly like this.Redacted raw evidence, if useful for cross-checking: https://gist.github.com/thetalkingdrum/63400a69a93bec3e3d7989669c31fb8c (log excerpt, the config at crash time, and a curated excerpt of the panic report — full raw
.diagavailable on request, left out here since it's 2.6MB of mostly boilerplate kext-version listing).thetalkingdrum commentedon Aug 16, 2026
Small caveat on my reports above, before it misleads anyone: I think I jumped to the wgpu race too fast.
Checked macOS's disk-space telemetry and my boot volume was down to ~300MB free at 11:26 and ~3.8GB at 23:33 — both right before my two forced restarts that day (turns out there were two, same
btn_rstpanic, not just the one I originally reported — found the second one, at 11:31, while digging further). A near-full disk can freeze a whole Mac solid on its own, no GPU bug needed — swap can't grow, background daemons can't write temp files, everything backs up.That also undercuts my "all 31 threads in TH_WAIT" finding — a disk/swap stall looks exactly the same (everything blocked, nothing running), so it doesn't actually point at the wgpu race specifically. My bad for reading it that way.
Not saying the race isn't real —
Vipitisconfirmed it independently, and it's presumably still what's causing the clean segfaults others are hitting. Just that my particular freeze might be a "my disk was basically full" problem rather than this one. Clearing space and keeping an eye on it before I assume otherwise.InvisibleFunction commentedon Oct 12, 2026
Just as a note I tried the 0.63.0 release today and was able to do much more before it eventually crashed with the same shaped output:
I was able to edit several photos and even export some.