FoliageWindByDayGroup / FoliageWindDayGroupPoint(int ActiveDayGroup, ...) becomes FoliageWindByWeather / FoliageWindWeatherPoint(string WeatherKind, ...) in AtmospherePolicyDeclaration (Plugin.Abstractions is BCL-only, so the key is the exact member name of AcDream.Core.World.WeatherKind rather than the enum itself). The raw activeDayGroup index carries no weather meaning by itself; WeatherState.cs already classifies each day group's authored DAT name into one of five real weather kinds, and that fact was already threaded through AtmosphericFrameInputs.Weather / uAtmosphereWeather.x — this reuses it instead of guessing an index-to-category mapping. Built-in table (BuiltInAtmosphericRenderPack.AtmospherePolicy()): Clear 0.25/0.15, Overcast 0.60/0.35, Rain 0.85/0.60, Snow 0.35/0.20, Storm 1.00/0.75 — all five WeatherKind members declared, the invented "Cloudy" row dropped. RenderPackAtmospherePolicyEvaluation.FoliageWind now takes a WeatherKind and matches by weather.ToString() (ordinal) against each declared point's name; a kind absent from the table falls back to the declared Clear row, then to (0,0) if Clear itself is undeclared. The delta-seconds EMA interpolation (EaseTowardTarget) is unchanged. AtmosphericPostProcessGraph.ResolveFoliageWind and its two callers (RenderPostProcess via inputs.Weather; RenderDirectionalShadows via foundation.Atmosphere.Kind) now pass WeatherKind instead of the day-group int. RenderPackValidation.ValidateAtmosphere (runs for every pack declaring an AtmospherePolicy, not gated to Tier2/shadow packs) now rejects an unknown or non-exact-case weather-kind name and a repeated kind, mirroring the existing ActiveDayGroupMultiplier duplicate-key check. Tests: RenderPackAtmospherePolicyEvaluationTests rewritten for the kind-keyed API (all five kinds resolve to their declared row, an unlisted kind falls back to Clear, ordinal exact-case matching, null-table handling); RenderPackSpirvValidatorTests gains four descriptor-validation cases (unknown name, wrong case, duplicate kind, the five-kind table accepted); AtmosphericPostProcessGraphTests' three foliage-wind cases now select WeatherKind.Storm via `with` instead of an assumed day-group index. Spot-check (per the coordinator's ask, not changed here): yes — ActiveDayGroupMultiplier / EvaluateDayGroupPolicy (pre-existing, Campaign AR/VM3-era — BuiltInAtmosphericRenderPack.AtmospherePolicy()'s three rows `new ActiveDayGroupMultiplier(0, 1.0), (1, 0.35), (2, 0.20)`) key the sun-ray/shadow/volumetric day-group strength multiplier by the same raw activeDayGroup index with an undocumented assumed meaning (0=brightest ... 2=dimmest), the identical class of issue this commit fixes for foliage wind. Left unchanged per instruction; flagging for the coordinator to file. Full solution Debug and Release builds green. App hermetic filter 6024/6026 — the same 2 pre-existing failures as VM6a/VM6b. Both were re-run in isolation per the verification ask: both still fail alone (not a load-flake in this environment) — confirmed via git stash earlier this session that both already fail on the unmodified pre-VM6 baseline, so they are pre-existing and unrelated to this change. Core.Tests hermetic 4697/4697. RenderPackValidator.Tests 30/30. No shader/spv changes in this commit (pure C#/docs fix). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
489 lines
16 KiB
C#
489 lines
16 KiB
C#
namespace AcDream.Plugin.Abstractions.Rendering;
|
|
|
|
/// <summary>Prerequisite tier reached by a pack.</summary>
|
|
public enum RenderPackTier
|
|
{
|
|
Tier1 = 1,
|
|
Tier2 = 2,
|
|
Tier2Plus = 3,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renderer-owned facilities a pack may require or use opportunistically.
|
|
/// These are semantic capabilities, not Vulkan extension or feature names.
|
|
/// </summary>
|
|
public enum RenderCapability
|
|
{
|
|
MainWorldColorIntermediate,
|
|
FullscreenPasses,
|
|
SceneDepthSampling,
|
|
SceneNormalSampling,
|
|
AuthoredSunDirection,
|
|
AuthoredSunScreenPosition,
|
|
AuthoredWeather,
|
|
DirectionalShadowMaps,
|
|
OutdoorDirectionalShadowCasterReplay,
|
|
AnimatedCasterTransforms,
|
|
AlphaCutoutShadowCasters,
|
|
GpuTimestampQueries,
|
|
/// <summary>One layered directional-depth pass may address multiple cascade views.</summary>
|
|
MultiviewDirectionalShadowCascades,
|
|
/// <summary>One renderer-selected authored sun-or-moon shadow direction.</summary>
|
|
AuthoredCelestialDirectionalLight,
|
|
}
|
|
|
|
/// <summary>Fixed renderer-owned positions at which a declared pass may run.</summary>
|
|
public enum RenderPassHook
|
|
{
|
|
ShadowDepthBeforeWorld,
|
|
AtmosphereBeforeToneMap,
|
|
ToneMap,
|
|
AfterToneMapBeforePrivateViewports,
|
|
}
|
|
|
|
/// <summary>Immutable frame facts the renderer may bind for a pack.</summary>
|
|
public enum RenderSemanticInput
|
|
{
|
|
WorldColor,
|
|
SceneDepth,
|
|
SceneNormals,
|
|
SunDirection,
|
|
SunScreenPosition,
|
|
ActiveDayGroup,
|
|
Weather,
|
|
CameraMatrices,
|
|
ShadowCasterTransforms,
|
|
DirectionalShadowMaps,
|
|
FrameTime,
|
|
/// <summary>Selected surface-to-sun-or-moon direction for shadow work.</summary>
|
|
SelectedCelestialDirectionalLight,
|
|
}
|
|
|
|
/// <summary>Renderer-owned replay operations available to a declaration.</summary>
|
|
public enum RenderSceneReplaySemantic
|
|
{
|
|
OutdoorDirectionalShadowCasters,
|
|
}
|
|
|
|
/// <summary>Existing retained-scene classes eligible for a scene replay.</summary>
|
|
[Flags]
|
|
public enum RenderCasterClass
|
|
{
|
|
None = 0,
|
|
Terrain = 1 << 0,
|
|
OpaqueWorld = 1 << 1,
|
|
AlphaCutoutWorld = 1 << 2,
|
|
AnimatedOpaque = 1 << 3,
|
|
AnimatedAlphaCutout = 1 << 4,
|
|
}
|
|
|
|
/// <summary>Base renderer pipeline a pack may specialize.</summary>
|
|
public enum RenderPipelineBaseSemantic
|
|
{
|
|
Terrain,
|
|
WorldMesh,
|
|
EnvCell,
|
|
}
|
|
|
|
/// <summary>Material classifications accepted by a pipeline variant.</summary>
|
|
[Flags]
|
|
public enum RenderMaterialClass
|
|
{
|
|
None = 0,
|
|
Opaque = 1 << 0,
|
|
AlphaCutout = 1 << 1,
|
|
AnimatedOpaque = 1 << 2,
|
|
AnimatedAlphaCutout = 1 << 3,
|
|
}
|
|
|
|
/// <summary>Kind of renderer-owned intermediate resource.</summary>
|
|
public enum RenderResourceKind
|
|
{
|
|
Image2D,
|
|
Image2DArray,
|
|
Buffer,
|
|
}
|
|
|
|
/// <summary>Portable format families resolved by the renderer.</summary>
|
|
public enum RenderFormatClass
|
|
{
|
|
LdrColor,
|
|
HdrColor,
|
|
SingleChannel,
|
|
DirectionalDepth,
|
|
StructuredData,
|
|
}
|
|
|
|
/// <summary>How declared image dimensions are interpreted.</summary>
|
|
public enum RenderExtentMode
|
|
{
|
|
AbsolutePixels,
|
|
RelativeToMainWorld,
|
|
RelativeToOutput,
|
|
}
|
|
|
|
/// <summary>Permitted uses of a declared resource.</summary>
|
|
[Flags]
|
|
public enum RenderResourceUsage
|
|
{
|
|
None = 0,
|
|
Sampled = 1 << 0,
|
|
ColorAttachment = 1 << 1,
|
|
DepthAttachment = 1 << 2,
|
|
Storage = 1 << 3,
|
|
TransferSource = 1 << 4,
|
|
TransferDestination = 1 << 5,
|
|
}
|
|
|
|
/// <summary>Lifetime class used by the renderer's frame-flight allocator.</summary>
|
|
public enum RenderResourceLifetime
|
|
{
|
|
TransientPass,
|
|
FrameFlight,
|
|
ActivePack,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renderer-owned meaning of a declared resource. <see cref="Custom"/> is
|
|
/// available to ordinary declarative fullscreen graphs; the remaining values
|
|
/// let a pack request host executors without relying on magic resource IDs.
|
|
/// </summary>
|
|
public enum RenderResourceSemantic
|
|
{
|
|
Custom,
|
|
MainWorldHdr,
|
|
BloomPing,
|
|
BloomPong,
|
|
SunOcclusionMask,
|
|
SunRays,
|
|
DirectionalShadowDepth,
|
|
VolumetricShafts,
|
|
}
|
|
|
|
/// <summary>Shape of one image declaration.</summary>
|
|
/// <param name="Mode">Absolute pixels or a scale relative to a renderer surface.</param>
|
|
/// <param name="Width">Pixel width for absolute mode; horizontal scale otherwise.</param>
|
|
/// <param name="Height">Pixel height for absolute mode; vertical scale otherwise.</param>
|
|
/// <param name="Layers">Array layers; one for an ordinary 2-D image.</param>
|
|
public sealed record RenderExtentDeclaration(
|
|
RenderExtentMode Mode,
|
|
double Width,
|
|
double Height,
|
|
int Layers = 1);
|
|
|
|
/// <summary>One renderer-owned intermediate image or buffer.</summary>
|
|
public sealed record RenderResourceDeclaration(
|
|
string Id,
|
|
RenderResourceKind Kind,
|
|
RenderFormatClass Format,
|
|
RenderExtentDeclaration? Extent,
|
|
long SizeBytes,
|
|
RenderResourceUsage Usage,
|
|
RenderResourceLifetime Lifetime,
|
|
long EstimatedResidentBytes)
|
|
{
|
|
public RenderResourceSemantic Semantic { get; init; } = RenderResourceSemantic.Custom;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renderer-owned execution meaning of a pass. IDs remain pack-owned stable
|
|
/// identifiers; semantic execution never depends on a particular ID string.
|
|
/// </summary>
|
|
public enum RenderPassSemantic
|
|
{
|
|
CustomFullscreen,
|
|
DirectionalShadowDepth,
|
|
BloomDownsample,
|
|
BloomBlurHorizontal,
|
|
BloomBlurVertical,
|
|
SunOcclusion,
|
|
SunRays,
|
|
VolumetricShafts,
|
|
FilmicComposite,
|
|
}
|
|
|
|
/// <summary>One declarative full-screen, atmosphere, or tone-map pass.</summary>
|
|
public sealed record RenderPassDeclaration(
|
|
string Id,
|
|
RenderPassHook Hook,
|
|
string VertexShaderAsset,
|
|
string FragmentShaderAsset,
|
|
IReadOnlyList<RenderSemanticInput> SemanticInputs,
|
|
IReadOnlyList<string> ResourceReads,
|
|
IReadOnlyList<string> ResourceWrites)
|
|
{
|
|
public RenderPassSemantic Semantic { get; init; } = RenderPassSemantic.CustomFullscreen;
|
|
}
|
|
|
|
/// <summary>A renderer-owned replay of retained scene geometry.</summary>
|
|
public sealed record SceneReplayDeclaration(
|
|
string Id,
|
|
RenderSceneReplaySemantic Semantic,
|
|
RenderCasterClass CasterClasses,
|
|
int ViewCount);
|
|
|
|
/// <summary>A shader specialization of an existing renderer pipeline.</summary>
|
|
public sealed record PipelineVariantDeclaration(
|
|
string Id,
|
|
RenderPipelineBaseSemantic BaseSemantic,
|
|
string VertexShaderAsset,
|
|
string FragmentShaderAsset,
|
|
RenderMaterialClass CompatibleMaterials,
|
|
IReadOnlyList<RenderSemanticInput> SemanticInputs)
|
|
{
|
|
public RenderPipelineVariantSemantic Semantic { get; init; } =
|
|
RenderPipelineVariantSemantic.Custom;
|
|
}
|
|
|
|
/// <summary>Renderer-owned role of a fixed retained-scene pipeline variant.</summary>
|
|
public enum RenderPipelineVariantSemantic
|
|
{
|
|
Custom,
|
|
TerrainDirectionalShadowCaster,
|
|
WorldOpaqueDirectionalShadowCaster,
|
|
WorldAlphaCutoutDirectionalShadowCaster,
|
|
TerrainDirectionalShadowReceiver,
|
|
WorldDirectionalShadowReceiver,
|
|
TerrainMultiviewDirectionalShadowCaster,
|
|
WorldOpaqueMultiviewDirectionalShadowCaster,
|
|
WorldAlphaCutoutMultiviewDirectionalShadowCaster,
|
|
}
|
|
|
|
/// <summary>Per-preset replacement for one resource's size.</summary>
|
|
public sealed record RenderQualityResourceOverride(
|
|
string ResourceId,
|
|
RenderExtentDeclaration? Extent,
|
|
long SizeBytes,
|
|
long EstimatedResidentBytes);
|
|
|
|
/// <summary>Per-preset value for a declared user setting.</summary>
|
|
public sealed record RenderQualitySettingOverride(
|
|
string SettingId,
|
|
string Value);
|
|
|
|
/// <summary>One user-selectable, independently capability-gated preset.</summary>
|
|
public sealed record RenderQualityPreset(
|
|
string Id,
|
|
string DisplayName,
|
|
IReadOnlyList<RenderCapability> RequiredCapabilities,
|
|
IReadOnlyList<RenderQualityResourceOverride> ResourceOverrides,
|
|
IReadOnlyList<RenderQualitySettingOverride> SettingOverrides,
|
|
long MaxResidentGpuBytes,
|
|
double MaxIncrementalGpuMillisecondsP50,
|
|
double MaxIncrementalGpuMillisecondsP99,
|
|
double MaxIncrementalCpuMillisecondsP50,
|
|
double MaxIncrementalCpuMillisecondsP99,
|
|
bool AutoEligible = true)
|
|
{
|
|
public RenderQualitySemantic Semantic { get; init; } = RenderQualitySemantic.Custom;
|
|
|
|
/// <summary>
|
|
/// Optional renderer-owned execution optimizations whose shader ABI the
|
|
/// pack explicitly implements. The host never infers these from a pack ID.
|
|
/// </summary>
|
|
public RenderQualityExecutionHints ExecutionHints { get; init; } =
|
|
RenderQualityExecutionHints.None;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opt-in execution forms for renderer-owned atmospheric work. These hints
|
|
/// may fuse passes or compatible submissions; they do not remove declared
|
|
/// effects or caster classes from the final image.
|
|
/// </summary>
|
|
[Flags]
|
|
public enum RenderQualityExecutionHints
|
|
{
|
|
None = 0,
|
|
|
|
/// <summary>
|
|
/// The sun-rays shader accepts scene depth directly and filmic composite
|
|
/// evaluates the declared bloom extraction/filter while composing the
|
|
/// image. See the standard PackPass ABI flags.
|
|
/// </summary>
|
|
FusedAtmosphericPostProcess = 1 << 0,
|
|
|
|
/// <summary>
|
|
/// The three multiview caster variants select the exact cascade matrix with
|
|
/// the renderer-owned view index and render every declared Low cascade in
|
|
/// one layered depth pass.
|
|
/// </summary>
|
|
MultiviewDirectionalShadowCascades = 1 << 1,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Optional host quality role. Pack-owned IDs remain persisted; this semantic
|
|
/// is used only when a pack opts into the host's automatic-quality controller.
|
|
/// </summary>
|
|
public enum RenderQualitySemantic
|
|
{
|
|
Custom,
|
|
Low,
|
|
Medium,
|
|
High,
|
|
Automatic,
|
|
}
|
|
|
|
/// <summary>Storage and presentation kind for a pack-defined setting.</summary>
|
|
public enum RenderSettingKind
|
|
{
|
|
Boolean,
|
|
Integer,
|
|
Float,
|
|
Choice,
|
|
}
|
|
|
|
/// <summary>A bounded, user-visible pack setting.</summary>
|
|
public sealed record RenderSettingDeclaration(
|
|
string Id,
|
|
string DisplayName,
|
|
RenderSettingKind Kind,
|
|
string DefaultValue,
|
|
double? Minimum,
|
|
double? Maximum,
|
|
double? Step,
|
|
IReadOnlyList<string> Choices)
|
|
{
|
|
public RenderSettingSemantic Semantic { get; init; } = RenderSettingSemantic.Custom;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Optional host meaning for settings consumed by a renderer-owned atmospheric
|
|
/// executor. Ordinary pack settings use <see cref="Custom"/>.
|
|
/// </summary>
|
|
public enum RenderSettingSemantic
|
|
{
|
|
Custom,
|
|
BloomStrength,
|
|
FilmicStrength,
|
|
Exposure,
|
|
GradeSaturation,
|
|
GradeContrast,
|
|
VignetteStrength,
|
|
SunRayStrength,
|
|
DirectionalShadowStrength,
|
|
DirectionalShadowReachMetres,
|
|
DirectionalShadowPcfTaps,
|
|
VolumetricStrength,
|
|
VolumetricRayMarchSteps,
|
|
AutomaticQuality,
|
|
// Campaign VM VM6: foliage wind.
|
|
WindEnabled,
|
|
WindStrength,
|
|
WindDirectionDegrees,
|
|
WindLeanMetres,
|
|
WindBranchMetres,
|
|
WindFlutterMetres,
|
|
WindCanopyHeightMetres,
|
|
}
|
|
|
|
/// <summary>One point on a declared sun-elevation response curve.</summary>
|
|
public sealed record SunElevationResponsePoint(
|
|
double ElevationDegrees,
|
|
double Multiplier);
|
|
|
|
/// <summary>Explicit mapping from an authored AC day group to an effect multiplier.</summary>
|
|
public sealed record ActiveDayGroupMultiplier(
|
|
int ActiveDayGroup,
|
|
double Multiplier);
|
|
|
|
/// <summary>
|
|
/// Explicit mapping from a DAT-classified weather kind to the foliage-wind
|
|
/// mean and gust strength for that weather (both in the declared [0,1] range
|
|
/// before the pack's global wind-strength setting scales them). Campaign VM
|
|
/// VM6, corrected in the fix round: the day-group's raw <c>activeDayGroup</c>
|
|
/// INDEX carries no weather meaning by itself — <c>WeatherState.cs</c>'s
|
|
/// existing name classification is what turns a day group's DAT name into
|
|
/// one of the five real weather kinds, and that is what the host already
|
|
/// threads through <c>AtmosphericFrameInputs.Weather</c> / <c>uAtmosphereWeather.x</c>.
|
|
/// This is the same fact, keyed correctly instead of by index.
|
|
/// </summary>
|
|
/// <param name="WeatherKind">
|
|
/// The exact member name of <c>AcDream.Core.World.WeatherKind</c> — one of
|
|
/// <c>"Clear"</c>, <c>"Overcast"</c>, <c>"Rain"</c>, <c>"Snow"</c>,
|
|
/// <c>"Storm"</c>. A plain string because this project (Plugin.Abstractions)
|
|
/// is BCL-only and cannot reference the Core enum; the host converts with
|
|
/// <c>WeatherKind.ToString()</c> (ordinal) when resolving.
|
|
/// </param>
|
|
public sealed record FoliageWindWeatherPoint(
|
|
string WeatherKind,
|
|
double Mean,
|
|
double Gust);
|
|
|
|
/// <summary>Visible authored-atmosphere interpretation owned by the pack.</summary>
|
|
public sealed record AtmospherePolicyDeclaration(
|
|
IReadOnlyList<SunElevationResponsePoint> SunElevationResponse,
|
|
IReadOnlyList<ActiveDayGroupMultiplier> ActiveDayGroupMultipliers)
|
|
{
|
|
/// <summary>
|
|
/// Optional selected-light elevation curve for directional shadows. The host
|
|
/// linearly interpolates adjacent points in sine-of-elevation space and
|
|
/// clamps beyond the endpoints.
|
|
/// The resolved multiplier must be zero at and below the authored
|
|
/// 0-degree horizon: every non-positive control point must be zero and,
|
|
/// when no exact 0-degree point is declared, the first positive control
|
|
/// point must also be zero.
|
|
/// A pack using the directional-shadow semantic must declare this curve.
|
|
/// </summary>
|
|
public IReadOnlyList<SunElevationResponsePoint> DirectionalShadowLightElevationResponse
|
|
{ get; init; } = [];
|
|
|
|
/// <summary>
|
|
/// Optional moving-sun strength curve for volumetric shafts. The host
|
|
/// smoothstep-interpolates adjacent points in elevation-degree space and
|
|
/// clamps beyond the endpoints.
|
|
/// A pack using the volumetric-shaft semantic must declare this curve.
|
|
/// </summary>
|
|
public IReadOnlyList<SunElevationResponsePoint> VolumetricShaftSunElevationResponse
|
|
{ get; init; } = [];
|
|
|
|
/// <summary>
|
|
/// Campaign VM VM6: exact per-weather-kind foliage-wind mean/gust
|
|
/// targets, looked up by the DAT-classified
|
|
/// <c>AcDream.Core.World.WeatherKind</c> (an exact match, not an
|
|
/// interpolation — the five kinds are not ordered by "how windy"). The
|
|
/// host smooths the resolved (mean, gust) target toward whatever this
|
|
/// table returns over time using the existing weather delta-seconds
|
|
/// clock, so a weather change never snaps. A kind absent from this table
|
|
/// falls back to the declared Clear row, then to (0, 0) if Clear itself
|
|
/// is undeclared. Each declared <see cref="FoliageWindWeatherPoint.WeatherKind"/>
|
|
/// must be one of the five real kind names and must appear at most once.
|
|
/// </summary>
|
|
public IReadOnlyList<FoliageWindWeatherPoint> FoliageWindByWeather
|
|
{ get; init; } = [];
|
|
|
|
/// <summary>
|
|
/// Campaign VM VM6: object/GfxObj ids excluded from foliage-wind sway
|
|
/// even though their entity id falls in the procedural-scenery
|
|
/// namespace and their subset classification would otherwise qualify —
|
|
/// the rare scenery object that is cutout-material but not actually
|
|
/// foliage (e.g. a cutout rock or fence prop drawn from the scenery
|
|
/// table). There is no include list: outside this exclusion set, the
|
|
/// classification rule in <c>WbDrawDispatcher</c> is the rule.
|
|
/// </summary>
|
|
public IReadOnlyList<uint> FoliageExclusions { get; init; } = [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Complete immutable declaration for one render pack. Packs describe what
|
|
/// they need; the renderer validates and owns every concrete resource, pass,
|
|
/// pipeline, barrier, and scene replay.
|
|
/// </summary>
|
|
public sealed record RenderPackDescriptor(
|
|
string Id,
|
|
string DisplayName,
|
|
Version PackVersion,
|
|
int PackApiVersion,
|
|
RenderPackTier HighestTier,
|
|
IReadOnlyList<RenderCapability> RequiredCapabilities,
|
|
IReadOnlyList<RenderCapability> OptionalCapabilities,
|
|
IReadOnlyList<RenderResourceDeclaration> Resources,
|
|
IReadOnlyList<RenderPassDeclaration> Passes,
|
|
IReadOnlyList<SceneReplayDeclaration> SceneReplays,
|
|
IReadOnlyList<PipelineVariantDeclaration> PipelineVariants,
|
|
IReadOnlyList<RenderQualityPreset> QualityPresets,
|
|
IReadOnlyList<RenderSettingDeclaration> Settings,
|
|
AtmospherePolicyDeclaration? AtmospherePolicy)
|
|
{
|
|
/// <summary>Short user-facing description shown beside compatibility and cost.</summary>
|
|
public string FeatureSummary { get; init; } = string.Empty;
|
|
}
|