feat(render): shader ABI v2 - AtmosphericFrame gains clock/wind blocks; caster pass binds it (Campaign VM VM6a)
AtmosphericFrame (set 3/binding 5) grows additively from 160 to 192 bytes: two appended vec4 members, uAtmosphereClockWind and uAtmosphereWindAmplitude, carry the foliage-wind clock/weather and amplitude inputs VM6b's shader displacement will read. RenderPackShaderAbi renames the old constant to AtmosphericFrameSizeBytesV1 (160), adds AtmosphericFrameSizeBytesV2 (192), keeps AtmosphericFrameSizeBytes pointing at the current (v2) size, and adds ShaderAbiVersion = 2. RenderPackSpirvValidator.ValidateAtmosphericFrame accepts either the v1 (seven-member, 160-byte) or v2 (nine-member, 192-byte) shape and rejects anything else naming both — this is why the frozen external sample packs under samples/*/Shaders/*.spv, whose GLSL sources are not in this tree, need no rebuild: a v1 shader bound to the 192-byte buffer still reads correctly, since a bound range only needs to be >= the block's own declared size. DirectionalSunShadowRenderer's caster pass now binds AtmosphericFrame too (both the multiview and per-cascade sites), through a new AtmosphericFrameBufferBinding the graph owns and supplies via DirectionalSunShadowRenderInput. AtmosphericPostProcessGraph.RenderDirectionalShadows builds its own 192-byte ring allocation for this, separate from the world receiver's frame block, because the caster pass runs before RenderPostProcess constructs that block within the same frame. The four world caster pipeline variants (opaque/cutout, base/multiview) are now allowed to declare binding 5 in the validator; terrain casters are untouched. This commit is plumbing only: the two new members are always written but never read by any shader yet (zero placeholders), so pack-on and pack-off output are both pixel-identical to before. VM6b wires the real weather-driven values and the shader-side displacement. App hermetic filter: 5972/5974 (2 pre-existing failures unrelated to this change, confirmed against the unmodified baseline). Core.Tests hermetic: 4697/4697. RenderPackValidator.Tests: 30/30. VulkanShaderManifestTests (retail oracle set): 7/7, byte-identical. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
a0157693ec
commit
0930c35d1d
17 changed files with 340 additions and 32 deletions
|
|
@ -323,6 +323,8 @@ internal sealed class AtmosphericPostProcessGraph :
|
|||
* _shadowStrength,
|
||||
0f,
|
||||
1f));
|
||||
AtmosphericFrameBufferBinding shadowAtmosphericFrame =
|
||||
BuildShadowAtmosphericFrameBinding(frame);
|
||||
var input = new DirectionalSunShadowRenderInput(
|
||||
environment,
|
||||
world.Camera.Camera.View,
|
||||
|
|
@ -333,7 +335,8 @@ internal sealed class AtmosphericPostProcessGraph :
|
|||
MeasureGpuTimers: AtmosphericGpuTimerSampling.ShouldMeasure(
|
||||
Preset.Semantic,
|
||||
frame.Serial),
|
||||
MeasureCpuStages: measureCpuStages);
|
||||
MeasureCpuStages: measureCpuStages,
|
||||
AtmosphericFrame: shadowAtmosphericFrame);
|
||||
long environmentFinished = measureCpuStages ? Stopwatch.GetTimestamp() : 0L;
|
||||
_lastShadowCasterCount = _shadowCasters.Stats.Accepted;
|
||||
_lastShadowClassificationCalls = _shadowCasters.Stats.TopologyRebuilt ? 1 : 0;
|
||||
|
|
@ -368,6 +371,43 @@ internal sealed class AtmosphericPostProcessGraph :
|
|||
return _lastShadowDiagnostics;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Campaign VM VM6: the caster pass runs before <see cref="RenderPostProcess"/>
|
||||
/// builds the receiver's frame block (shadows render during the prepared-
|
||||
/// world phase; post-process runs after), so it cannot share that
|
||||
/// allocation. This graph owns a second, independent 192-byte ABI v2
|
||||
/// ring slice for the caster instead — see the D2 binding sites in
|
||||
/// <see cref="DirectionalSunShadowRenderer.RenderPrepared"/>. Only the
|
||||
/// two appended VM6 members carry real content; the caster shaders never
|
||||
/// read the other seven (sun/weather/policy/inverse-view-projection are
|
||||
/// receiver-only concerns), so this method leaves them zeroed rather
|
||||
/// than duplicating <see cref="RenderPostProcess"/>'s computation.
|
||||
/// </summary>
|
||||
private AtmosphericFrameBufferBinding BuildShadowAtmosphericFrameBinding(
|
||||
IGpuFrame frame)
|
||||
{
|
||||
GpuRingAllocation allocation = frame.AllocateRing(
|
||||
AtmosphericFrameUniforms.SizeInBytes,
|
||||
GpuRingUsage.Uniform);
|
||||
var uniforms = new AtmosphericFrameUniforms(
|
||||
Vector4.Zero,
|
||||
Vector4.Zero,
|
||||
Vector4.Zero,
|
||||
Vector4.Zero,
|
||||
Vector4.Zero,
|
||||
Vector4.Zero,
|
||||
Matrix4x4.Identity,
|
||||
// VM6a: ABI v2 plumbing only. VM6b fills the real weather-driven
|
||||
// wind block here; no caster shader reads it yet.
|
||||
Vector4.Zero,
|
||||
Vector4.Zero);
|
||||
MemoryMarshal.Write(allocation.Data, in uniforms);
|
||||
return new AtmosphericFrameBufferBinding(
|
||||
allocation.Buffer,
|
||||
allocation.OffsetBytes,
|
||||
(uint)AtmosphericFrameUniforms.SizeInBytes);
|
||||
}
|
||||
|
||||
public IGpuRenderTarget PrepareWorldTarget(
|
||||
int width,
|
||||
int height,
|
||||
|
|
@ -485,7 +525,13 @@ internal sealed class AtmosphericPostProcessGraph :
|
|||
dayGroupPolicy,
|
||||
shadowElevationPolicy,
|
||||
volumetricElevationPolicy),
|
||||
inputs.InverseViewProjection);
|
||||
inputs.InverseViewProjection,
|
||||
// Campaign VM VM6a: ABI v2 plumbing only — written here so the
|
||||
// 192-byte block is always fully populated, but mesh_atmospheric.vert
|
||||
// does not read these two members yet (VM6b wires the real
|
||||
// weather-driven values and the shader read together).
|
||||
Vector4.Zero,
|
||||
Vector4.Zero);
|
||||
GpuRingAllocation frameBlock;
|
||||
GpuRingAllocation settingsBlock;
|
||||
GpuRingAllocation fusedSunPassBlock = default;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue