Skip to content

Debugging Renderers Standalone

Mirage’s three renderers are all independent executables. The main app drives them by forking child processes and feeding line-based JSON over standard input. This design lets a renderer run on its own, separate from the main app, which makes it easier to pin down “whether the problem is in the renderer itself or in the main app’s scheduling.”

The main app looks up renderers in the order “prefer the app’s bundled copy, and fall back to the source build directory if not found”:

Type Source build output path (dev fallback)
Scene SceneRenderer/build/<preset>/Tools/SceneWallpaper/SceneWallpaper
Web WebRenderer/build/release/Tools/WebWallpaper/WebWallpaper
Video VideoRenderer/build/release/Tools/VideoWallpaper/VideoWallpaper

A properly packaged app copies them inside Mirage.app; during development, if there is no bundled copy, the main app derives the repository root from the source file’s compile-time path and falls back to the build directories above. This means as long as you’ve built the renderers in the repository, running the main app directly from Xcode loads the latest renderers without repackaging each time.

Renderers receive line-based JSON: each line is a complete JSON object terminated by a newline \n. The main app writes these commands into the child process’s standard input; renderers also send log and status lines back over standard output/standard error.

Because the channel is standard input, you can drive a renderer manually in a terminal — just feed it the JSON commands one line at a time.

Using the video renderer as an example, build it first and then run it directly:

Terminal window
# Build (release)
./VideoRenderer/scripts/build.sh release
# Run directly and send JSON control messages line by line from the terminal
./VideoRenderer/build/release/Tools/VideoWallpaper/VideoWallpaper

Once the process starts, it waits for line-based JSON on standard input. You can:

  • Type JSON commands one line at a time and watch the renderer’s output and window behavior; or
  • Use a script to feed a sequence of commands in line by line:
Terminal window
printf '%s\n' '<first JSON command>' '<second JSON command>' \
| ./VideoRenderer/build/release/Tools/VideoWallpaper/VideoWallpaper

The scene renderer (SceneWallpaper) also depends on the runtime resource directory assets/ (shaders, materials, fonts, etc.). The main app prefers the app’s bundled assets and falls back to the assets at the repository root. When running the scene renderer manually, make sure it can find this resource directory.

Watching How the Main App Drives Renderers

Section titled “Watching How the Main App Drives Renderers”

If you want to see which commands the main app actually sends, rather than constructing them yourself, you can:

  1. Raise the log level in “Settings → About / Developer” (see Settings Overview).
  2. Run the main app from Xcode and watch the console for logs like [Mirage] 启动渲染器: … to confirm which binary is loaded and which screen it acts on.
  3. The renderer’s own standard output/standard error is forwarded by the main app and appears in the same log stream.
  • Crash isolation: when a single renderer exits abnormally, it doesn’t directly bring down the main app’s state; the main app can restart it per policy or fall back to a placeholder image.
  • Per-screen independence: with multiple displays, each screen can run its own renderer instance, without interfering with the others.
  • Easier debugging: you can reproduce a problem outside the main app, decoupling the “rendering” and “scheduling” layers.

For the overall process model, see Rendering Architecture.