// Campaign V slice V2 shared preamble (docs/plans/2026-07-27-vulkan-campaign.md // §3.4, §5.2). GL has no #include, so AcDream.App.Rendering.Shader // concatenates this file's text into every shader source that opts in // (Shader(gl, vertPath, fragPath, includeCommonPreamble: true)), inserted // right after the leading #version / #extension block so it can declare new // layout bindings and macros before the rest of the shader body runs. // // --- set 1 (uniform buffers) ------------------------------------------------ // GL keeps the SSBO and UBO binding-number namespaces separate, so today's // SceneLighting UBO (binding=1) never collides with BatchBuffer's SSBO // (also binding=1). Vulkan has ONE binding namespace per set, so the Vulkan // backend (from V6 on) moves every uniform buffer to its own set (1) to keep // both binding numbers. ACDREAM_UBO_SET is a no-op under GL today and is // redefined to `set = 1,` when the same source is compiled for Vulkan, so // applying it to every UBO layout now costs nothing and needs no source edit // at the call sites later. #define ACDREAM_UBO_SET // --- set 0 binding 9 / set 2 (the global texture table) -------------------- // GL emulates Vulkan's set 2 variable-count sampled-texture descriptor array // (GpuBindingModel.TextureTableSet) with a plain storage buffer of packed // GL_ARB_bindless_texture handles at set 0 binding 9 // (GpuBindingModel.StorageTextureTable). A batch/pass no longer carries a // 64-bit bindless handle directly into its GPU-visible struct; it carries a // small integer slot index into this table instead, which is what makes the // CPU-side data model backend-neutral (Campaign V slice V2). The table itself // — and every per-renderer GL-side handle-slot allocator that fills it // (WbDrawDispatcher, EnvCellRenderer, TerrainModernRenderer, ParticleRenderer) // — is deleted once each renderer moves onto IGpuDevice's own retirement-gated // table at V4c/V4d/V4e; see the campaign doc's §5.2 for why V2 cannot reach // that table yet. layout(std430, binding = 9) readonly buffer TextureTableBuf { uvec2 gTextureTable[]; }; // Looks up the packed bindless handle for table slot `idx`. Callers still // wrap the result in `sampler2DArray(...)` themselves at the use site (kept // explicit rather than folded into one sampler-returning macro) because every // existing call site already follows that exact pattern and a function cannot // return an opaque sampler type built from a runtime value in GLSL. #define ACDREAM_TEXTURE_HANDLE(idx) gTextureTable[idx] // Campaign V slice V6d: samples a table slot that holds a plain 2-D texture. // // The two backends disagree about what a 2-D table entry IS, and this macro is // the one place that difference lives. Under GL a bindless handle carries its // own texture type, so a GL_TEXTURE_2D entry is reconstructed as a sampler2D // and read with a 2-component UV. Under Vulkan the table is one descriptor // array whose element type is fixed at sampler2DArray, so the same entry is a // one-layer array read at layer 0 (see tools/ShaderCompiler/VulkanGlslPreamble.cs). // // That asymmetry is deliberate and is what keeps the retained UI's textures // exactly as they are on GL — including the paperdoll/appraisal FBO colour // texture, which is an externally-owned GL_TEXTURE_2D registered by the §7.1 // transitional seam and cannot be made an array before V4g moves its renderer. #define ACDREAM_SAMPLE_2D(idx, uv) texture(sampler2D(gTextureTable[idx]), uv)