namespace AcDream.App.Rendering.Gpu; /// /// Campaign V (V0): the PINNED binding model shared by the CPU renderers, the /// GLSL sources, and both RHI backends. Every number here appears in a shader /// file; changing one is a contract change that must land in the same commit as /// the matching shader edit. /// /// The model is deliberately Vulkan-shaped and dual-legal: /// /// set 0 — storage buffers, bindings 0..9. GLSL omits the set qualifier, and /// GL_KHR_vulkan_glsl defines an omitted set as set 0, so one source /// file compiles for both backends. /// set 1 — uniform buffers. Vulkan has ONE binding namespace per set, while /// GL keeps GL_SHADER_STORAGE_BUFFER and GL_UNIFORM_BUFFER tables /// separate. Today mesh_modern.vert exploits that: the BatchBuffer /// SSBO and the SceneLighting UBO both sit at binding=1. Moving UBOs /// to their own set preserves both numbers and removes the collision. /// set 2 — the global sampled-texture table that replaces ARB_bindless_texture. /// Vulkan binds it as one variable-count, partially-bound, /// update-after-bind descriptor array. GL used to emulate it with a /// storage buffer of uvec2 handles at set 0 binding 9 /// (StorageTextureTable); Campaign V slice V11 deleted that /// binding along with the rest of the raw-GL arm, so set 2 is the /// only texture table left. /// /// A batch no longer carries a 64-bit bindless handle; it carries a /// index into the table. That single change is what /// makes the CPU-side data model backend-neutral (Campaign V slice V2), and it /// lands on GL — pixel-gated — long before any Vulkan code exists. /// internal static class GpuBindingModel { // ---- set 0: storage buffers (identical numbering to today's SSBO bindings) ---- /// Per-instance transforms. std430 InstanceData { mat4 transform; }. public const uint StorageInstances = 0; /// Per-draw batch metadata. std430 BatchData (see ). public const uint StorageBatches = 1; /// Phase U.3 shared per-frame clip regions (CellClip, 144 B/slot). Slot 0 = no-clip. public const uint StorageClipRegions = 2; /// Phase U.3 per-instance clip-slot index, parallel to . public const uint StorageClipSlots = 3; /// A7 Fix B global point/spot light array. public const uint StorageGlobalLights = 4; /// A7 Fix B per-instance light set: 8 indices into the global light array, -1 = unused. public const uint StorageInstanceLightSets = 5; /// #142 per-instance indoor flag (1 = parented to an EnvCell, skip the sun). public const uint StorageInstanceIndoor = 6; /// #188 per-instance opacity multiplier for TransparentPartHook fades. public const uint StorageInstanceAlpha = 7; /// Retail SmartBox selection lighting: one vec2 (luminosity, diffuse) per instance. public const uint StorageInstanceSelectionLighting = 8; // Campaign V slice V11 deleted StorageTextureTable (binding 9): the GL-only // emulation of the Vulkan texture table via a storage buffer of uvec2 // bindless handles indexed by GpuTextureSlot.Index. The Vulkan backend // always bound TextureTableSet instead and never used this binding — every // Vulkan descriptor set layout declared it anyway (seeded with a dummy // buffer, like every other unused-by-a-given-shader binding), purely // because it counted toward StorageBindingCount. /// One past the highest storage binding — the count the backend must support. public const uint StorageBindingCount = 9; // ---- set 1: uniform buffers ---- /// /// SceneLighting std140 block. Keeps binding=1 so the existing shader source /// and SceneLightingUboBinding layout are untouched; the set index is /// what disambiguates it from under Vulkan. /// public const uint UniformSceneLighting = 1; /// /// Terrain per-layer texture tiling factors — 36 floats. /// /// Added at slice V4d. These live in a uniform float[36] today, which /// is 144 bytes: too large for the 96-byte push-constant block (and for /// Vulkan's guaranteed 128-byte ceiling), and there is no RHI verb for setting /// a uniform array. A small uniform buffer is the Vulkan-legal home. Binding 2 /// is taken by the terrain clip block, so this is 3. /// public const uint UniformTerrainTiling = 3; /// /// Sky per-draw parameters — the sky/model transforms, the keyframe's /// ambient/sun colours and sun direction, the UV scroll, and the /// per-surface emissive/diffuse/opacity/fog scalars. /// /// Added at slice V6e for the same reason as /// : Vulkan GLSL has no default uniform /// block, so a loose uniform mat4 uSkyView; is unspellable, and this /// set is 256 bytes in std140 — three matrices alone are twice the entire /// 96-byte push-constant block. A uniform buffer is the only legal home. /// Bindings 1, 2 and 3 are taken by SceneLighting, the terrain clip block /// and terrain tiling, so this is 4. /// public const uint UniformSkyParams = 4; /// Set index carrying every uniform buffer. public const uint UniformSet = 1; // ---- set 2: the global texture table ---- /// Set index of the sampled-texture descriptor array (Vulkan) / logical table (GL). public const uint TextureTableSet = 2; /// Binding of the descriptor array within . public const uint TextureTableBinding = 0; /// /// Upper bound on simultaneously registered textures. Vulkan requires /// maxDescriptorSetUpdateAfterBindSampledImages to reach this; the /// capability probe asserts it rather than discovering it at draw time. /// public const uint TextureTableCapacity = 16384; // ---- push constants ---- /// /// Bytes actually written by . Vulkan guarantees /// at least , so 32 bytes of headroom remain /// for later slices; any growth updates this constant and the shader block in /// the same commit. /// public const int PushConstantBytes = 96; /// The Vulkan-guaranteed minimum push-constant budget. A hard ceiling for us. public const int MaxPushConstantBytes = 128; // ---- shared layout facts the CPU writers and the shaders must agree on ---- /// /// std430 stride of BatchData. The bindless uvec2 textureHandle /// becomes uint textureIndex plus one pad word at slice V2, so the /// stride is unchanged and every existing CPU writer keeps its offsets. /// public const int GpuBatchDataStrideBytes = 16; /// Clip planes per CellClip slot; also the required gl_ClipDistance size. public const int ClipPlanesPerSlot = 8; /// std430 stride of one CellClip slot: 16 B header + 8 × vec4. public const int ClipRegionStrideBytes = 16 + (ClipPlanesPerSlot * 16); /// Lights selected per object by retail's minimize_object_lighting. public const int MaxLightsPerObject = 8; }