Example fixture only: This package is not a real or production-ready water renderer. Its shaders and visual behavior are intentionally minimal placeholders used to demonstrate and test how a third-party Rendering Extension is packaged, registered, validated, and distributed with Swift Package Manager.
For a clean-room, step-by-step authoring workflow, start with Creating a Rendering Extension Plugin. This README documents the concrete fixture and its consumer integration.
This nested package is the acceptance fixture for distributing an Untold Engine rendering extension independently from the engine package. It contains:
- a
RenderExtensionPluginmanifest and public registration entry point; - compute, model-surface, and procedural custom-geometry implementations;
- declared texture resources and compute/render pipelines;
- a model-surface argument-buffer layout;
- Metal source plus a bundled, precompiled macOS
.metallib; - consumer-side tests that import only public engine and plugin APIs.
The procedural pass is the acceptance fixture for general scene drawing. Its
vertex shader generates a triangle from vertex_id, so it does not use an
engine mesh or model-surface helper. The pass obtains the current eye's
view-projection matrix from context.camera, looks up its package-owned pipeline
through context.renderPipelines, creates an encoder through
context.sceneRenderTargets, binds the pipeline depth state, and issues a direct
draw into the working scene color and depth targets.
This directory contains its own Package.swift, so Xcode treats it as a separate
package boundary and may hide it from the parent UntoldEngine package navigator.
Open this directory's Package.swift separately, add the directory as a local
package dependency, or include both packages in an Xcode workspace. Do not add
the package's source files directly to the UntoldEngine target.
The fixture's Package.swift uses:
.package(path: "../../..")That path is relative to this package directory and works only while the fixture is nested inside the UntoldEngine repository. It is not resolved relative to the consuming application. If you copy the package elsewhere, replace it with the path to your local engine checkout:
.package(path: "/path/to/UntoldEngine")For distribution, use the canonical UntoldEngine repository URL and a compatible version requirement instead. The consuming application and plugin should resolve the same UntoldEngine package source and version.
Add the WaterRenderPlugin library product to the application target, import the
module, and install it before renderer creation:
import WaterRenderPlugin
let result = registerWaterRenderPlugin()registerWaterRenderPlugin() installs every extension in the plugin atomically.
The package remains a compile-time SwiftPM dependency; this is not runtime module
discovery or dynamic loading from an arbitrary folder.
Install the plugin once during application startup, before renderer creation, and handle the installation result:
import UntoldEngine
import WaterRenderPlugin
func installWaterRendering() -> Bool {
switch registerWaterRenderPlugin() {
case .installed:
return true
case .replaced:
// A plugin with the same manifest ID was already installed and was
// replaced atomically by this version.
return true
case let .rejected(failure):
print("Water plugin validation errors:", failure.validationErrors)
print("Water plugin artifact conflicts:", failure.artifactConflicts)
print("Water plugin graph errors:", failure.graphValidationErrors)
return false
}
}After installation, create a model entity and attach the public component exported by the package. The plugin's surface pass selects only entities carrying this component:
if installWaterRendering() {
let water = createEntity()
setEntityMeshDirect(
entityId: water,
meshes: BasicPrimitives.createPlane(),
assetName: "water"
)
registerComponent(entityId: water, componentType: WaterSurfaceComponent.self)
if let surface = getEntityComponent(
entityId: water,
componentType: WaterSurfaceComponent.self
) {
surface.tint = SIMD4<Float>(0.08, 0.38, 0.62, 1.0)
surface.roughness = 0.08
surface.waveStrength = 0.18
}
}Do not also register WaterSurfaceRenderExtension through setRendering. The
plugin entry point already creates it with the package-only Bundle.module and
registers it under plugin lifecycle management.
The procedural geometry is installed automatically with the same extension; no entity or component is required. Its pipeline uses the engine-resolved scene attachment formats and remains compatible with reverse-Z and per-eye XR graph execution.
This package and the
ApplicationLocal
sample use the same RenderExtension hooks and argument-buffer APIs. The tint
sample shows the smallest application-local implementation. This fixture adds
the pieces required for third-party distribution:
- a SwiftPM library product and
Bundle.moduleresource access; - a bundled precompiled metallib;
- a versioned
RenderExtensionPluginManifest; - a factory that can supply one or more extensions;
- one public function that installs or rolls back the complete plugin.
Applications can use both forms simultaneously. Register application-local
extensions with setRendering(.extensions(.register(...))) and install package
plugins through RenderExtensionPluginRegistry or their public entry points.
Every extension and artifact ID must remain globally unique. Extension-owned
resources remain private and cannot be accessed by another provider.
Run the fixture independently from the engine repository root:
swift test --package-path Examples/RenderingExtensions/SwiftPackagePluginRebuild the bundled macOS shader library after changing the Metal source:
Examples/RenderingExtensions/SwiftPackagePlugin/Scripts/build-metallib.shA production package should build and bundle a metallib for every platform and SDK it supports. This fixture is macOS-only so the checked-in binary has one unambiguous target.