227 lines
14 KiB
Markdown
227 lines
14 KiB
Markdown
# Render-pack SDK v1
|
||
|
||
**Campaign:** Atmospheric Rendering / Shader Packs
|
||
**Phase id:** **Campaign AR**
|
||
**Contract version:** `RenderPackApi.Current == 1`
|
||
|
||
Render packs are opt-in, declarative graphics extensions. acdream's current
|
||
retail-faithful renderer is always installed, remains the default and
|
||
authoritative comparison path, and is restored as one complete transaction
|
||
when a selected pack cannot run. A
|
||
pack cannot access Vulkan, renderer internals, gameplay state, world streaming,
|
||
or physics.
|
||
|
||
The public dependency is only
|
||
`AcDream.Plugin.Abstractions`. Do not reference `AcDream.App`, Silk.NET, or a
|
||
Vulkan binding. Three buildable external samples cover the API:
|
||
|
||
- [`AcDream.RenderPacks.NoOp`](../../samples/AcDream.RenderPacks.NoOp/) is the
|
||
smallest discovery and activation conformance pack.
|
||
- [`AcDream.RenderPacks.AtmosphericTier2`](../../samples/AcDream.RenderPacks.AtmosphericTier2/)
|
||
declares the complete semantic atmospheric executor with deliberately
|
||
renamed pack-owned IDs, embeds all referenced SPIR-V, and demonstrates
|
||
moving authored sun-and-moon shadows for terrain, trees, buildings, players, and monsters.
|
||
- [`AcDream.RenderPacks.ShadowsOnlyTier2`](../../samples/AcDream.RenderPacks.ShadowsOnlyTier2/)
|
||
demonstrates that Tier 2 is composable: it requests the same selected-celestial
|
||
caster/receiver semantics without Tier-1 post-processing or volumetric
|
||
shafts.
|
||
|
||
## Quick start
|
||
|
||
1. Target `.NET 10` and reference `AcDream.Plugin.Abstractions` with runtime
|
||
copy disabled. The acdream host supplies that assembly.
|
||
2. Add [`plugin.json`](plugin-manifest-v1.schema.json), include
|
||
`"kinds": ["renderPack"]`, and copy it beside the built entry DLL.
|
||
3. Expose exactly one public, parameterless `IRenderPackPlugin` entry point.
|
||
4. Construct immutable `RenderPackDescriptor` values and register them from
|
||
`Register`. Registration must only publish declarations; do not open assets,
|
||
compile shaders, start threads, or allocate native/GPU resources.
|
||
5. Supply shader bytes lazily through `IRenderPackAssets.OpenRead`. Asset keys
|
||
are forward-slash relative logical paths: never rooted, backslash-based, or
|
||
`.`/`..` traversals.
|
||
6. Build and run the SDK validator:
|
||
|
||
```powershell
|
||
dotnet build samples/AcDream.RenderPacks.NoOp/AcDream.RenderPacks.NoOp.csproj -c Release
|
||
dotnet run --project tools/RenderPackValidator/AcDream.Tools.RenderPackValidator.csproj -c Release -- samples/AcDream.RenderPacks.NoOp/bin/Release/net10.0
|
||
```
|
||
|
||
Substitute `AcDream.RenderPacks.AtmosphericTier2` in both paths to validate
|
||
the complete Tier 2/Tier 2+ example and its embedded shader interfaces.
|
||
|
||
The validator executes the managed registration entry point. Use it only on a
|
||
pack you trust. It loads no App, RHI, or Vulkan assembly and creates no GPU
|
||
objects. It validates the manifest, v1 declarations, referenced asset keys,
|
||
SPIR-V stage/entry point and complete v1 binary interface, managed
|
||
registration, and duplicate pack IDs. Hardware and driver compatibility remain
|
||
client-side activation checks.
|
||
|
||
To launch a visible offline preview of acdream with the built-in Atmospheric
|
||
pack and a disposable settings profile:
|
||
|
||
```powershell
|
||
.\tools\launch-atmospheric-preview.ps1 -Preset High
|
||
```
|
||
|
||
The preview starts `AcDream.App` with audio disabled, clears inherited
|
||
`ACDREAM_*` live/automation/diagnostic state for that child, and leaves the
|
||
user's normal acdream settings untouched. It records the binary identity,
|
||
selected audio mode, and separate stdout/stderr logs beside the disposable
|
||
profile. To exercise OpenAL explicitly, add `-EnableAudio`.
|
||
|
||
Vertex and fragment asset keys are independent opaque keys; they do not need
|
||
matching basenames or a host shader-directory stem. On explicit selection the
|
||
client opens each declared stream, validates it, copies the bytes into the
|
||
isolated candidate, and creates shader modules from those immutable blobs.
|
||
The pipeline retains neither the stream nor a path into the plugin directory.
|
||
Each stage must be little-endian, word-aligned SPIR-V no larger than 16 MiB.
|
||
|
||
Shader-visible pack settings are deliberately capped at 64 declarations. The
|
||
public `PackSettings` binding and value encoding are documented in the
|
||
[`semantic binding table`](semantic-bindings-v1.md#packsettings-set-3-binding-8-256-bytes).
|
||
A persisted user override wins the selected preset override, which wins the
|
||
declaration default. Overrides are stored by stable pack ID plus setting ID;
|
||
the host validates the selected descriptor's kind, invariant numeric grammar,
|
||
range, step, and choice list before supplying the resolved scalars. No renderer
|
||
object is exposed to managed code.
|
||
|
||
When a pack is selected, the retained Config page appends its declared
|
||
Boolean, bounded Float/Integer, and Choice controls under **Graphics
|
||
Enhancements**. Changing packs replaces only that optional tail; retail's 39
|
||
authored Config rows remain unchanged. Numeric controls snap to declared bounds
|
||
and steps, preset changes retain explicit user overrides, and a pack change
|
||
starts with an empty valid override map for the new stable pack identity.
|
||
|
||
## Manifest
|
||
|
||
The authoritative machine-readable schema is
|
||
[`plugin-manifest-v1.schema.json`](plugin-manifest-v1.schema.json).
|
||
|
||
| Field | Meaning |
|
||
|---|---|
|
||
| `id` | Stable lowercase logical plugin ID. It is persisted and must not be localized or reused. |
|
||
| `displayName` | User-visible plugin name. |
|
||
| `version` | Dotted `System.Version`-compatible package version. |
|
||
| `entryDll` | Safe path beneath the plugin directory to the managed entry DLL. |
|
||
| `apiVersion` | General `PluginApi` version. v1 is `1`; this is distinct from `RenderPackApi`. |
|
||
| `dependencies` | Optional plugin IDs that must load first. |
|
||
| `kinds` | Entry-point kinds. Include `renderPack`; omission means legacy `gameplay` only. A hybrid lists both. |
|
||
|
||
Install one plugin directory containing this manifest, the entry DLL, its
|
||
private managed dependencies, and declared shader assets. Do not redistribute
|
||
`AcDream.Plugin.Abstractions.dll` in that directory: type identity is shared
|
||
from the host.
|
||
|
||
## Declaration schema
|
||
|
||
The C# records in `AcDream.Plugin.Abstractions.Rendering` are the public v1
|
||
declaration schema. `RenderPackShaderAbi` publishes the corresponding numeric
|
||
SPIR-V set, binding, block-size, and capacity constants. They are intentionally
|
||
BCL-only and expose no Vulkan handle.
|
||
|
||
| Declaration | What the pack supplies | What the host owns |
|
||
|---|---|---|
|
||
| `RenderPackDescriptor` | Identity/version, highest tier, capabilities, resources, passes, replays, variants, presets, settings, atmosphere policy | Validation, candidate creation, activation and fallback |
|
||
| `RenderResourceDeclaration` | Logical ID, portable format, extent, usage, lifetime, estimated bytes | Images/buffers, allocation, barriers, frame-flight retirement |
|
||
| `RenderPassDeclaration` | Fixed hook, shader asset keys, semantic inputs, logical resource reads/writes | Render graph order, descriptor layout, pipeline, command recording |
|
||
| `SceneReplayDeclaration` | One supported replay semantic, caster flags, 1–4 views | Resident caster selection and existing batched submissions |
|
||
| `PipelineVariantDeclaration` | Base pipeline semantic, shader assets, compatible material flags, inputs | Visibility, mesh/material ownership, fixed renderer state |
|
||
| `RenderQualityPreset` | Capability requirements, resource/setting overrides, optional execution hints, and p50/p99 CPU/GPU/VRAM ceilings | Availability, explicit selection and stable-boundary swaps |
|
||
| `RenderSettingDeclaration` | Stable ID, kind, default, bounds/choices | Persistence and conditional Display UI |
|
||
| `AtmospherePolicyDeclaration` | Ordered sun and selected-light elevation curves plus explicit `activeDayGroup` multipliers | Authored Dereth clock, celestial source, day group, weather and indoor state |
|
||
|
||
IDs use `^[a-z][a-z0-9._-]*$`, are case-insensitively unique within each
|
||
declaration kind, and remain stable across updates. A pack must declare at
|
||
least one quality preset and at most 64 settings. The SDK ceiling is 256 MiB pack-owned resident GPU
|
||
memory, 16 MiB per SPIR-V asset, 16,384 pixels per absolute image dimension,
|
||
256 image layers, and four scene-replay views. A physical device may expose a
|
||
lower ceiling or reject a preset whose mandatory capabilities are absent.
|
||
For API v1 the host admits optional pack memory from one eighth of the selected
|
||
adapter's probed device-local heaps, capped at 256 MiB resident and 512 MiB
|
||
transient multisample storage. Presets remain listed with exact limit reasons.
|
||
Auto requires asynchronous timestamps and uses Low when Medium cannot fit. At
|
||
runtime, Auto alone watches the selected preset's declared inclusive-GPU p99,
|
||
pack-added CPU p99, and resident-GPU budgets. If Low remains over any of those
|
||
budgets for 180 stable samples, the whole pack fails safely to acdream's
|
||
default renderer with the measured and declared limits in the failure reason.
|
||
Explicit Low remains selectable and is not silently disabled by the Auto
|
||
performance policy.
|
||
|
||
A Tier-2 directional-shadow elevation curve must resolve to exactly zero at
|
||
and below the authored 0-degree horizon. Every declared non-positive point
|
||
must therefore have multiplier `0`; if the curve omits an exact 0-degree
|
||
point, its first positive point must also be `0` so endpoint clamping or
|
||
interpolation cannot manufacture a below-horizon directional shadow. The host still
|
||
owns the independent no-selected-light-energy and indoor gates.
|
||
|
||
The built-in Atmospheric Low preset preserves the complete directional-shadow
|
||
caster set (terrain, opaque and alpha-cutout world geometry, and both animated
|
||
classes). It reduces cost with two 768 x 768 shadow maps and the ordinary
|
||
six-pass, quarter-resolution separable post chain: sun occlusion, sun rays,
|
||
bloom downsample, horizontal blur, vertical blur, and filmic composition. It
|
||
does not remove a caster class or use the fused post-process hint.
|
||
|
||
`FusedAtmosphericPostProcess` is an optional external-pack Low-preset execution
|
||
hint for the standard atmospheric graph; it is not built-in Low behavior. An
|
||
opting-in shader pack implements the PackPass ABI below: the host feeds scene
|
||
depth directly to sun rays and asks filmic to evaluate the declared bloom
|
||
extraction and separable filter while composing the final image. This reduces
|
||
command recording without disabling rays, bloom, or filmic composition. The
|
||
host never infers the hint from pack identity; unknown hints, non-Low use, and
|
||
incomplete standard graphs fail validation.
|
||
|
||
`MultiviewDirectionalShadowCascades` is a separate explicit Low-preset
|
||
execution hint. The opting-in pack must implement three multiview caster
|
||
variants. The host records one layered directional-depth
|
||
pass with view mask `0b11`; `gl_ViewIndex` selects the exact two declared Low
|
||
cascade matrices. Terrain, opaque, and alpha-cutout commands retain their
|
||
ordinary pipeline, transform, cull, and cutout semantics. The preset must require
|
||
`MultiviewDirectionalShadowCascades`; unsupported hardware makes that Low preset
|
||
unavailable before allocation. A zero hint retains ordinary per-cascade passes.
|
||
|
||
Resources are declared in execution order: a pass cannot read a pack resource
|
||
before an earlier pass writes it, and one pass cannot read and write the same
|
||
resource. `WorldColor`, `SceneDepth`, and other renderer semantics are not pack
|
||
resources and are named in `SemanticInputs` instead. API v1 exposes four
|
||
sampled pass-input slots; buffers, `StructuredData`, and storage resources are
|
||
reserved enum values and are rejected until a public binding contract exists.
|
||
Colour image arrays are likewise reserved; v1 arrays are directional-depth
|
||
maps. One declared pass writes at most one attachment. Only `ToneMap` and
|
||
`AfterToneMapBeforePrivateViewports` may write directly to the host surface
|
||
without naming a pack resource.
|
||
|
||
Every semantic input implies its capability and the descriptor must list that
|
||
capability as required: world colour, scene depth/normals, authored sun/selected-
|
||
celestial/day/weather facts, animation transforms, and directional maps cannot
|
||
be treated as
|
||
optional after a pass unconditionally declares them.
|
||
|
||
## Lifecycle and versioning
|
||
|
||
- Discovery calls `Register` but does not open assets or allocate GPU objects.
|
||
- Installing a pack never selects it. The user selects a pack ID, version, and
|
||
preset; `acdream default (retail-faithful)` is always available.
|
||
- The client validates every declaration and selected asset, builds the full
|
||
candidate beside the active retail graph, then swaps at a frame boundary.
|
||
- Dispose the registration handle to withdraw the descriptor. The host also
|
||
withdraws every handle before unloading its collectible plugin context.
|
||
- `PluginApi` versions the general managed plugin ABI. `RenderPackApi` versions
|
||
these graphics declarations. Additive enum/record support stays compatible;
|
||
a breaking contract requires a new render-pack API version and explicit
|
||
compatibility path.
|
||
- Persisted identity is pack ID + pack version + preset ID, never list index.
|
||
User-authored setting strings are keyed by the same stable pack identity and
|
||
stable setting ID, never declaration or menu index.
|
||
|
||
"Device recreation" in the v1 SDK means full teardown of the old renderer,
|
||
graphics context, and device, followed by construction and capability probing
|
||
of a fresh context/device. Retail is authoritative until a fresh pack candidate
|
||
validates and activates. The SDK does not promise live recovery of a pack or
|
||
renderer after `VK_ERROR_DEVICE_LOST`; that error is terminal to the old device
|
||
lifetime.
|
||
|
||
The complete campaign contract, budgets, and non-goals remain in
|
||
[`2026-08-21-atmospheric-rendering.md`](../plans/2026-08-21-atmospheric-rendering.md).
|
||
The public shader-facing contracts are the
|
||
[`semantic binding table`](semantic-bindings-v1.md) and
|
||
[`compatibility/failure guide`](compatibility-and-failure-v1.md).
|