Skip to main content

SHADOW

Constant SHADOW 

Source
pub const SHADOW: &str = "// Cascaded shadow pass: single source for every backend.\n//\n// Depth-only. The scene is rendered from the directional light\'s perspective\n// into one slice of a Depth32Float texture array, one slice per cascade, and\n// the host loops the pass once per cascade pushing the slot to project through.\n// There is no fragment stage on any backend -- depth writes are automatic --\n// so these entries have no varying interface to match.\n//\n// One entry per compile, selected by a define, so each variant declares exactly\n// the resources it binds and nothing reserves a slot a host never writes:\n//\n//   SHADOW_STATIC   - per-draw casters, model matrix from the host\n//   SHADOW_SKINNED  - per-draw skinned casters, one joint palette\n//   SHADOW_BINDLESS - GPU-driven: the model comes from the per-frame\n//                     GpuObjectData record the cull baked into each indirect\n//                     command\'s first-instance value. The deformed skinned tail\n//                     rides this same entry: its vertices are already\n//                     model-space, so `light_vp * model * deformed_pos` matches\n//                     the static case.\n//\n// METAL_BINDINGS selects the Metal *host\'s* constant shape, a host difference\n// rather than a target one: Vulkan carries the model matrix and the cascade\n// index in one push constant block (a pipeline layout may declare only one),\n// while the Metal encoder writes the model to buffer(2) and the cascade to\n// buffer(7) -- the split the spot-shadow pass shares.\n//\n// DXIL_ABI pins every register to the shadow root signatures in\n// directx/init/pipelines.rs and directx/resources.rs. DirectX reuses Vulkan\'s\n// constant *shape* (the per-draw entries take one 20-DWORD block holding the\n// model and the cascade; the GPU-driven one takes the cascade alone) but hands\n// it over as root constants at b0 / b2, which pushes the shadow CBV to b1 on\n// every variant and starts the structured buffers at t0. The object id comes\n// from that b0 root constant the indirect command writes, exactly as\n// main_bindless.slang does, rather than from SV_StartInstanceLocation.\n\n{OBJECT_COMMON}\n\n// Layout matches `ShadowUniforms` in render_types.rs.\nstruct ShadowUniforms\n{\n    float4x4 light_vps[4];\n    float4 cascade_splits;\n};\n\n#ifdef DXIL_ABI\n// b0 carries the per-draw root constants on every variant, so the shadow CBV\n// follows at b1.\nConstantBuffer<ShadowUniforms> shadow_cb : register(b1);\n#else\n[[vk::binding(0, 0)]] ConstantBuffer<ShadowUniforms> shadow_cb : register(b0);\n#endif\n\n#ifdef METAL_BINDINGS\n\nstruct ModelUniforms { float4x4 model; };\n// Layout matches `ShadowPassPush` in render_types.rs (16 B).\nstruct ShadowPassPush { uint cascade_idx; uint _pad0; uint _pad1; uint _pad2; };\n\n#if defined(SHADOW_STATIC) || defined(SHADOW_SKINNED)\nConstantBuffer<ModelUniforms> model_cb : register(b2);\n#define SHADOW_MODEL model_cb.model\n#endif\nConstantBuffer<ShadowPassPush> cascade_cb : register(b7);\n#define SHADOW_CASCADE cascade_cb.cascade_idx\n\n#else\n\n// The per-draw block: model matrix plus the cascade slot, padded to 80 B.\nstruct ShadowPush\n{\n    float4x4 model;\n    uint cascade_idx;\n    uint _pad0;\n    uint _pad1;\n    uint _pad2;\n};\n\n// The GPU-driven pass pushes only the cascade slot: its model comes from the\n// object record.\nstruct CascadePush { uint cascade_idx; };\n\n#ifdef SHADOW_BINDLESS\n#ifdef DXIL_ABI\n// One root constant per cascade\'s ExecuteIndirect; b0 is the object id.\nConstantBuffer<CascadePush> push : register(b2);\n#else\n[[vk::push_constant]] ConstantBuffer<CascadePush> push;\n#endif\n#else\n#ifdef DXIL_ABI\nConstantBuffer<ShadowPush> push : register(b0);\n#else\n[[vk::push_constant]] ConstantBuffer<ShadowPush> push;\n#endif\n#define SHADOW_MODEL push.model\n#endif\n#define SHADOW_CASCADE push.cascade_idx\n\n#endif\n\n#ifdef SHADOW_BINDLESS\n#ifdef DXIL_ABI\nstruct ObjectId { uint value; };\nConstantBuffer<ObjectId> objid_cb : register(b0);\nStructuredBuffer<GpuObjectData> objects : register(t0);\n#else\n[[vk::binding(0, 1)]] StructuredBuffer<GpuObjectData> objects : register(t9);\n#endif\n#endif\n\n// The full static layout is declared so the pipeline\'s vertex descriptor is\n// consumed exactly as the other passes declare it, even though only the\n// position is read.\nstruct ShadowVertexIn\n{\n    [[vk::location(0)]] float3 pos     : POSITION;\n    [[vk::location(1)]] float3 normal  : NORMAL;\n    [[vk::location(2)]] float3 tangent : TANGENT;\n    [[vk::location(3)]] float3 color   : COLOR0;\n    [[vk::location(4)]] float2 uv      : TEXCOORD0;\n};\n\n#ifdef SHADOW_STATIC\n\n[shader(\"vertex\")]\nfloat4 shadow_vertex_main(ShadowVertexIn v) : SV_Position\n{\n    return mul(shadow_cb.light_vps[SHADOW_CASCADE], mul(SHADOW_MODEL, float4(v.pos, 1.0)));\n}\n\n#endif\n\n#ifdef SHADOW_SKINNED\n\n// Only the attributes the depth-only skinned caster consumes are declared, so\n// the pipeline is validation-clean (no \"attribute not consumed\" findings).\nstruct SkinnedShadowVertexIn\n{\n    [[vk::location(0)]] float3 pos     : POSITION;\n    [[vk::location(5)]] uint4  joints  : BLENDINDICES;\n    [[vk::location(6)]] float4 weights : BLENDWEIGHT;\n};\n\n#ifdef DXIL_ABI\nStructuredBuffer<float4x4> joints : register(t0);\n#else\n[[vk::binding(0, 1)]] StructuredBuffer<float4x4> joints : register(t8);\n#endif\n\n[shader(\"vertex\")]\nfloat4 shadow_vertex_main_skinned(SkinnedShadowVertexIn v) : SV_Position\n{\n    float4x4 skin = v.weights.x * joints[v.joints.x]\n                  + v.weights.y * joints[v.joints.y]\n                  + v.weights.z * joints[v.joints.z]\n                  + v.weights.w * joints[v.joints.w];\n    float4 skinned_pos = mul(skin, float4(v.pos, 1.0));\n    return mul(shadow_cb.light_vps[SHADOW_CASCADE], mul(SHADOW_MODEL, skinned_pos));\n}\n\n#endif\n\n#ifdef SHADOW_BINDLESS\n\n[shader(\"vertex\")]\nfloat4 shadow_vertex_bindless(\n    ShadowVertexIn v\n#ifdef DXIL_ABI\n    ) : SV_Position\n{\n    uint oid = objid_cb.value;\n#else\n    ,\n    uint instance_id : SV_InstanceID,\n    uint first_instance : SV_StartInstanceLocation) : SV_Position\n{\n    uint oid = object_instance_index(instance_id, first_instance);\n#endif\n    float4x4 model = objects[oid].model;\n    return mul(shadow_cb.light_vps[SHADOW_CASCADE], mul(model, float4(v.pos, 1.0)));\n}\n\n#endif\n";
Expand description

shadow.slang.