Files
emacs-gpu/README.md
T
andros 6f8f953c0c gpu.el: fix gpu-set-cursor not applying; default cursor to block
The gpu-cursor-animation defcustom used :set #'set-default, so changing
the cursor mode at runtime (via gpu-set-cursor / setopt) only updated the
Lisp variable and never reached the live Metal driver. Wire :set to call
gpu-cursor-mode so the change propagates immediately.

Also set the default cursor to 'block (static, no effect); cursor effects
are now opt-in via gpu-set-cursor. Bump version to 0.1.1.
2026-06-05 16:12:15 +02:00

166 lines
5.8 KiB
Markdown

# emacs-gpu
GNU Emacs with a GPU-accelerated display backend.
On **macOS** it renders with **native Apple Metal**: text goes through a
GPU glyph atlas, images and inline video are textures, and the whole
frame is composited by the GPU instead of CoreGraphics. The output is
pixel-accurate against the stock Cocoa backend.
**OpenGL support for GNU/Linux and Windows is planned but not implemented
yet.** The drawing logic is already platform-neutral (`src/gfxterm.c`)
behind a small driver interface (`src/gfxdrv.h`); an OpenGL driver only
needs to implement that interface (`src/glterm.c` is the documented
skeleton). Contributions welcome.
## Why a GPU backend?
Beyond raw rendering, it enables things the stock backend cannot do:
- **Inline video playback**: AVFoundation decodes straight into Metal
textures (zero copies) and the frames are composited inside the
buffer, following scrolling and clipped to the window. No xwidgets,
no embedded browser.
- **GPU cursor effects** (opt-in): expanding rings, comet trails and
friends are drawn as a compositor overlay, without ever touching the
buffer content underneath.
- **A path to cheap visual effects**: buffer transitions, smooth
scrolling or any future eye candy is one more shader pass over the
composited frame, not a rewrite of the display engine.
Text is rasterized once into a GPU glyph atlas and drawn as textured
quads; scrolling moves already-rendered pixels with a texture blit.
## Performance
Measured on an Apple M1 Pro (Emacs 32 development build, 120x45 frame,
font-locked `xdisp.c`, same binary with and without the GPU backend;
`/usr/bin/time -l` over scripted workloads):
| Workload | Stock (Cocoa) | GPU, vsync on (default) | GPU, vsync off |
|---|---|---|---|
| Sustained scroll, redisplays/s | 481 | 324 | 475 |
| CPU for 15 s of that scroll | 16.0 s | **10.5 s** | 15.5 s |
| Typing throughput (chars/s, machine-paced) | 108 | 52 | 106 |
| Idle (8 s) CPU | 1.21 s | 1.19 s | same |
| Peak RSS | ~140 MB | ~144 MB | same |
Honest reading:
- **Machine-paced throughput and CPU cost match the stock backend**
(vsync off). There is no GPU tax.
- With vsync on (the default), presents wait for the display refresh:
the screen shows the same 60 fps either way, but Emacs burns ~35%
less CPU under flat-out scrolling because it stops rendering frames
nobody can see. Human-paced input is unaffected (the cap is ~52
machine-paced updates/s; keyboard auto-repeat tops out well below
that). `(gpu-vsync nil)` switches to uncapped, stock-like behavior.
- Idle cost is identical and the GPU resources add ~4 MB of RSS.
> Status: experimental, under active development.
>
> **Note:** I am not answering issues for now. Feel free to open them as
> a public record (they will be read eventually), but do not expect a
> reply at this stage.
## Demos
Inline video playing inside a buffer, decoded by AVFoundation straight
into Metal textures (`gpu-video-insert`):
![Inline video](.github/assets/inline-video.gif)
An animated GIF playing next to font-locked code scrolling, all
composited by the GPU:
![Animated GIF and code](.github/assets/gif-and-code.gif)
GPU cursor effects (`gpu-animations`), here the *sonicboom* mode:
![Sonicboom cursor](.github/assets/cursor-sonicboom.gif)
Buffer switches cross-fade on the GPU (on by default, configurable):
![Buffer cross-fade](.github/assets/buffer-crossfade.gif)
## Installing
With Homebrew (Apple Silicon, macOS 26+):
```sh
brew install --cask tanrax/tap/emacs-gpu
```
Or grab the signed, self-contained `Emacs.app` from the
[releases](https://github.com/tanrax/emacs-gpu/releases).
## Building on macOS
Requires Xcode (or the Command Line Tools) and the usual Emacs build
dependencies (`brew install autoconf automake gnutls texinfo pkg-config`).
```sh
./autogen.sh
SDK=$(xcrun --sdk macosx --show-sdk-path)
CC="xcrun clang" OBJC="xcrun clang" \
CFLAGS="-isysroot $SDK" CPPFLAGS="-isysroot $SDK" OBJCFLAGS="-isysroot $SDK" \
./configure --with-ns --with-mtl
make -j$(sysctl -n hw.ncpu)
```
The binary is `src/emacs` (or install the app bundle from `nextstep/`).
## Enabling the GPU backend
The release app bundle enables it automatically (set the environment
variable `EMACS_GPU_DISABLE=1` to start with the stock Cocoa backend
instead). In a source build, switch a frame to Metal with:
```elisp
(add-to-list 'load-path "/path/to/emacs-gpu/lisp")
(require 'gpu)
(gpu-enable)
```
## Commands and options
| Command | What it does |
|---|---|
| `M-x gpu-status` | Show backend state: GPU device, animations, cursor mode |
| `M-: (gpu-draw-stats)` | Renderer counters; `glyphs-drawn` growing proves the GPU is painting |
| `M-x gpu-toggle-animations` | Toggle the GPU compositor overlay used by cursor effects |
| `M-x gpu-set-cursor` | Pick the cursor effect: `block` (static, default, no effect), `sonicboom` (ring), `torpedo` (comet trail), `spring`, `ripple`, `pixiedust`, `hollow`, `beam` |
| `M-: (gpu-vsync nil)` | Uncap presents from the display refresh (lower latency, more power) |
| `M-: (gpu-video-insert "clip.mp4" 480 270 t)` | Play a video inline at point; follows scrolling |
| `M-x gpu-video-stop` | Stop the inline video |
Cursor effects trigger on cursor jumps (`M-<`, `M->`, isearch hits),
not on single-character movement.
Buffer switches cross-fade by default; tune or disable with:
```elisp
(setq gpu-buffer-transition-duration 0.15) ; seconds
(setq gpu-buffer-transitions nil) ; turn it off
```
## How it works
```
redisplay engine (xdisp.c, untouched)
src/gfxterm.c platform-neutral drawing policy
src/gfxdrv.h driver interface (~25 ops)
src/mtlterm.m Metal driver: glyph atlas (CoreText → R8 texture),
render cycle on a persistent texture, AVFoundation
video through CVMetalTextureCache
```
## License
GNU General Public License v3 or later, same as GNU Emacs.