From 9de604505151f7fcca61cf85b14db4dd09565b10 Mon Sep 17 00:00:00 2001 From: Sky <223640096+skyblanket@users.noreply.github.com> Date: Wed, 10 Jun 2026 23:38:00 +0530 Subject: [PATCH 1/2] feat: SwiftPM build plugin compiles Metal shaders automatically - Plugins/MetalCompilerPlugin: buildCommand globs Sources/SwiftRender/Shaders, compiles via xcrun metal, output metallib auto-embeds in the module bundle - checked-in Resources/default.metallib removed; Shaders/ excluded from resources (plugin owns them); incremental: editing a .metal recompiles - kills the silently-stale-shader footgun for good Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 3 ++ CONTRIBUTING.md | 12 ++------ Package.swift | 9 ++++-- Plugins/MetalCompilerPlugin/plugin.swift | 27 ++++++++++++++++++ README.md | 2 +- .../SwiftRender/Resources/default.metallib | Bin 178377 -> 0 bytes 6 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 Plugins/MetalCompilerPlugin/plugin.swift delete mode 100644 Sources/SwiftRender/Resources/default.metallib diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f7bbf0..2406cb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- MetalCompilerPlugin: .metal files now compile automatically at build time — + checked-in metallib and manual tools/build_shaders.sh step are gone + - StyleReel + StyleReelVertical (9:16) — 12-aesthetic showcase scenes with card-zoom transitions + tools/make_reel_audio.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4fb8b1a..0528426 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,17 +31,11 @@ open out/test.mp4 ## Adding a shader -1. Add a `[[ stitchable ]]` function to `Sources/SwiftRender/Shaders/Cookbook.metal` (or create a new `.metal` file in `Shaders/`). -2. Rebuild the metallib: - ```bash - cd Sources/SwiftRender/Shaders - xcrun -sdk macosx metal -c *.metal -o /tmp/all.air - xcrun -sdk macosx metallib /tmp/*.air -o ../Resources/default.metallib - ``` +1. Add a `[[ stitchable ]]` function to a `.metal` file in `Sources/SwiftRender/Shaders/`. +2. `swift build` — the MetalCompilerPlugin compiles all shaders into the + metallib automatically (requires full Xcode for the metal toolchain). 3. Use it in a scene via `ShaderLibrary.bundle(.module).yourShader(...)`. -> If you can automate step 2 via a SwiftPM build tool plugin, please open a PR — that's high on the wishlist. - ## Style - Keep scenes pure functions of `t`. No `@State`, no `Timer`, no `withAnimation(.repeatForever)`. diff --git a/Package.swift b/Package.swift index fa6c344..53f9e2e 100644 --- a/Package.swift +++ b/Package.swift @@ -12,15 +12,20 @@ let package = Package( .target( name: "SwiftRender", dependencies: [], + exclude: ["Shaders"], resources: [ .process("Resources"), - .process("Shaders"), - ] + ], + plugins: ["MetalCompilerPlugin"] ), .executableTarget( name: "SwiftRenderCLI", dependencies: ["SwiftRender"] ), + .plugin( + name: "MetalCompilerPlugin", + capability: .buildTool() + ), .testTarget( name: "SwiftRenderTests", dependencies: ["SwiftRender"] diff --git a/Plugins/MetalCompilerPlugin/plugin.swift b/Plugins/MetalCompilerPlugin/plugin.swift new file mode 100644 index 0000000..eab1f46 --- /dev/null +++ b/Plugins/MetalCompilerPlugin/plugin.swift @@ -0,0 +1,27 @@ +import Foundation +import PackagePlugin + +/// Compiles Shaders/*.metal into default.metallib at build time, replacing +/// the checked-in metallib + tools/build_shaders.sh manual step. +@main +struct MetalCompilerPlugin: BuildToolPlugin { + func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] { + guard let target = target as? SourceModuleTarget else { return [] } + let shaderDir = target.directory.appending("Shaders") + let fm = FileManager.default + guard let entries = try? fm.contentsOfDirectory(atPath: shaderDir.string) else { return [] } + let metalFiles = entries.filter { $0.hasSuffix(".metal") }.sorted() + .map { shaderDir.appending($0) } + guard !metalFiles.isEmpty else { return [] } + + let out = context.pluginWorkDirectory.appending("default.metallib") + return [.buildCommand( + displayName: "Compiling \(metalFiles.count) Metal shaders → default.metallib", + executable: Path("/usr/bin/xcrun"), + arguments: ["-sdk", "macosx", "metal"] + metalFiles.map(\.string) + + ["-o", out.string], + inputFiles: metalFiles, + outputFiles: [out] + )] + } +} diff --git a/README.md b/README.md index 4bcdfce..2e7b3e1 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ Pipe in JSON per record and render a thousand personalized variants — the AI/d ## Real Metal shaders -Drop a `.metal` file in `Sources/SwiftRender/Shaders/`, run `tools/build_shaders.sh`, call it on any view: +Drop a `.metal` file in `Sources/SwiftRender/Shaders/` and `swift build` — shaders compile automatically (SwiftPM build plugin). Call them on any view: ```swift Rectangle().fill(.black).colorEffect( diff --git a/Sources/SwiftRender/Resources/default.metallib b/Sources/SwiftRender/Resources/default.metallib deleted file mode 100644 index 8dc2baa28d9c568cc7e24ae07c87bf0b40d432a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 178377 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 Date: Wed, 10 Jun 2026 23:45:01 +0530 Subject: [PATCH 2/2] fix(plugin): redirect metal clang module cache into pluginWorkDirectory (sandbox-safe on cold builds) Co-Authored-By: Claude Fable 5 --- Plugins/MetalCompilerPlugin/plugin.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Plugins/MetalCompilerPlugin/plugin.swift b/Plugins/MetalCompilerPlugin/plugin.swift index eab1f46..7a66f15 100644 --- a/Plugins/MetalCompilerPlugin/plugin.swift +++ b/Plugins/MetalCompilerPlugin/plugin.swift @@ -15,11 +15,15 @@ struct MetalCompilerPlugin: BuildToolPlugin { guard !metalFiles.isEmpty else { return [] } let out = context.pluginWorkDirectory.appending("default.metallib") + // the plugin sandbox only permits writes inside pluginWorkDirectory — + // metal's clang module cache must live there too or cold builds fail + let moduleCache = context.pluginWorkDirectory.appending("ModuleCache") return [.buildCommand( displayName: "Compiling \(metalFiles.count) Metal shaders → default.metallib", executable: Path("/usr/bin/xcrun"), - arguments: ["-sdk", "macosx", "metal"] + metalFiles.map(\.string) - + ["-o", out.string], + arguments: ["-sdk", "macosx", "metal", + "-fmodules-cache-path=\(moduleCache.string)"] + + metalFiles.map(\.string) + ["-o", out.string], inputFiles: metalFiles, outputFiles: [out] )]